Compare commits

...

224 Commits

Author SHA1 Message Date
folex
a507865905 Add multistage Build.Dockerfile to build tendermint container 2019-10-09 21:05:35 +03:00
folex
b4b6fd4de8 Regenerate abci/types/types.pb.go 2019-10-08 14:48:39 +03:00
folex
82c35bf03f Add to CHANGELOG_PENDING 2019-10-08 14:36:07 +03:00
folex
70cd985a94 Add TODO question 2019-10-08 14:35:06 +03:00
folex
5d3961d705 Fix TestBeginBlockValidators 2019-10-08 14:35:06 +03:00
folex
2a1d0542f3 Add full vote to BeginBlock 2019-10-08 14:35:06 +03:00
Marko
a13a4b8c77 remove traces of github.com/tendermint/abci (#4038)
*  Remove traces oaf `github.com/tendermint/abci`

- removed abci dockerfile as it was still referencing `github.com/tendermint/abci`

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>

* nor change to install of abci

* use abci-cli instead of tendermint node

* remove traces of Dockerfile.develop

also use latest Go in Dockerfile.testing

* update docker readme

* remove wrapping because it will look awful on docker hub
2019-10-07 10:34:24 -05:00
Marko
38359f407e docs: add previous dev sessions (#4040)
Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>
2019-10-07 09:55:09 -05:00
Marko
acea49e4ae Cleanup docs (#4037)
* Quick clean up of docs

- removed a few files that have been deprecated and/or relocated

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>

* cleanup  a few docs
2019-10-07 14:26:06 +02:00
Anton Kaliaev
e024805354 docs: any path can be absolute or relative (#4035)
Fixes #4026
2019-10-02 12:23:32 -07:00
Anton Kaliaev
b225e0e87e changelog: add v0.31.9 and v0.31.8 updates (#4034)
also replace TODO placeholder with the actual issue in v0.32.5
2019-10-02 11:46:50 -07:00
Anton Kaliaev
004ff3ac38 Merge pull request #4032 from tendermint/v0.32.5-backport
V0.32.5 backport
2019-10-02 11:11:13 -07:00
Anton Kaliaev
9dc1ca1537 update changelog 2019-10-02 11:01:15 -07:00
Zaki Manian
ba547cb780 Update CHANGELOG.md
Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com>
2019-10-02 11:01:06 -07:00
Zaki Manian
4c11bab23f Changelog update 2019-10-02 11:00:57 -07:00
Zaki Manian
1af0e83931 update version.go 2019-10-02 11:00:47 -07:00
Zaki Manian
8c9bf1dade Update p2p/conn/secret_connection.go
Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com>
2019-10-01 23:00:35 -07:00
Zaki Manian
ebfaf30705 Fix for panic in signature verification if a peer sends a nil public key. 2019-10-01 23:00:35 -07:00
Marko
797a3f6c16 Add Dogsled to linting (#4025)
* add dogsled

* add back comments
2019-09-30 23:31:51 -07:00
Phil Salant
05075ea5b7 linters: modify code to pass maligned and interfacer (#3959)
* Fix maligned structs

* Fix interfacer errors

* Revert accidental go.mod and go.sum changes

* Revert P2PConfig struct maligned reorder

* Revert PeerRoundState struct maligned reordering

* Revert RoundState struct maligned reordering

* Reorder WSClient struct

* Revert accidental type change

* Clean up type change

* Clean up type changes

* Revert to types.ABCIApplicationServer in GRPCServer struct

* Revert maligned changes to BaseConfig struct

* Fix tests in io_test.go

* Fix client_test package tests

* Fix reactor tests in consensus package

* Fix new interfacer errors
2019-09-30 17:12:51 -07:00
Marko
68f8fba7c2 .golangci: disable new linters (#4024)
* Add Version to golangci

- added version to golangci because of recent update

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>

* remove unused for 1.17

* disable new linters
2019-09-30 13:46:55 -07:00
Erik Grinaker
ac099aa272 Improved tm-monitor formatting (#4023)
* tm-monitor: tweaked formatting of start time and avg tx throughput.

* tm-monitor: update health when validator number is updated.

* Updated CHANGELOG_PENDING

* Added PR number to CHANGELOG_PENDING.

Improves `tm-monitor` formatting of start time (RFC1123 without unnecessary precision) and avg tx throughput (three decimal places). The old tx throughput display was confusing during local testing where the tx rate is low and displayed as 0.

Also updates the monitor health whenever the validator number changes. It otherwise starts with moderate health and fails to update this once it discovers the validators, leading to incorrect health reporting and invalid uptime statistics. Let me know if you would like me to submit this as a separate PR.

### Before:

```
2019-09-29 20:40:00.992834 +0200 CEST m=+0.024057059 up -92030989600.42%

Height: 2518
Avg block time: 1275.496 ms
Avg tx throughput: 0 per sec
Avg block latency: 2.464 ms
Active nodes: 4/4 (health: moderate) Validators: 4

NAME                HEIGHT     BLOCK LATENCY     ONLINE     VALIDATOR     
localhost:26657     2518       0.935 ms          true       true          
localhost:26660     2518       0.710 ms          true       true          
localhost:26662     2518       0.708 ms          true       true          
localhost:26664     2518       0.717 ms          true       true          
```

### After:

```
Sun, 29 Sep 2019 20:21:59 +0200 up 100.00%

Height: 2480
Avg block time: 1361.445 ms
Avg tx throughput: 0.735 per sec
Avg block latency: 4.232 ms
Active nodes: 4/4 (health: full) Validators: 4

NAME                HEIGHT     BLOCK LATENCY     ONLINE     VALIDATOR     
localhost:26657     2480       1.174 ms          true       true          
localhost:26660     2480       1.037 ms          true       true          
localhost:26662     2480       0.981 ms          true       true          
localhost:26664     2480       0.995 ms          true       true          
```
2019-09-29 22:30:30 -07:00
Marko
cb1a0a7333 docs: remove traces of develop branch (#4022)
* Develop -> Master

- Some places still had develop instead of master.

closes #4107

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>

* add one more
2019-09-27 15:16:19 -07:00
Ethan Buchman
b065b8a0c5 ADR 045 - ABCI Evidence (#4010)
* ADR 045: ABCI Evidnce Handling

* add link to abci evidence defn

* remove debug line
2019-09-26 23:42:22 -07:00
dependabot-preview[bot]
a81aa14b06 deps: bump google.golang.org/grpc from 1.23.1 to 1.24.0 (#4021)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.23.1 to 1.24.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.23.1...v1.24.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-09-26 17:58:30 -07:00
Anton Kaliaev
134fe28962 remove benchmarks folder (#3999)
Afaik, nobody is using it. I've never used it myself.

1) atomic, os.Write, map, codec benchmarks all seem temporary and to be
of no use in the future.
2) syncing benchmark may be useful, but we're trying to move away from
Bash. Also, the blocks are empty there.
2019-09-24 10:07:34 -07:00
Ethan Buchman
51dc810d04 Merge pull request #4009 from tendermint/release/v0.32.4
Release/v0.32.4
2019-09-21 11:12:17 -04:00
Marko
0d13736ace address pr comments in #4002 (#4004) 2019-09-19 12:24:17 -04:00
Marko
bf989eb272 fix linting (#4000) 2019-09-19 09:31:28 -04:00
Marko Baricevic
b3b30a5e70 Merge branch 'master' into rc1-v0.32.4 2019-09-19 14:11:06 +02:00
dependabot-preview[bot]
8fbc62563e build(deps): bump github.com/tendermint/tm-db from 0.1.1 to 0.2.0 (#4001)
Bumps [github.com/tendermint/tm-db](https://github.com/tendermint/tm-db) from 0.1.1 to 0.2.0.
- [Release notes](https://github.com/tendermint/tm-db/releases)
- [Changelog](https://github.com/tendermint/tm-db/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tendermint/tm-db/compare/v0.1.1...v0.2.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-09-19 14:09:42 +02:00
Marko Baricevic
049f158465 revert abci change 2019-09-19 14:08:49 +02:00
Marko Baricevic
a5a19add77 bump versions 2019-09-19 11:51:51 +02:00
Marko Baricevic
fce61cc51c pr suggestions 2019-09-19 11:03:39 +02:00
Marko Baricevic
0116c8113b RC1 - 0.32.4
- Release candidate for v0.32.4

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>
2019-09-19 10:53:15 +02:00
Anton Kaliaev
4dfbaeb0c4 regenerate protobuf files with newer gogo version (#3998)
protoc 3.7.0
gogo v1.3.0
2019-09-19 11:41:57 +03:00
Sean Braithwaite
abab49089d Merge pull request #3878 from tendermint/brapse/blockchain-v2-riri-routine
[blockchain] v2 Routines
2019-09-18 16:06:56 -04:00
Sean Braithwaite
fc77f8faf3 Merge branch 'brapse/blockchain-v2-riri-routine' of https://github.com/tendermint/tendermint into brapse/blockchain-v2-riri-routine 2019-09-18 16:00:07 -04:00
Sean Braithwaite
2ae7a300b7 merge artifact go build file 2019-09-18 15:59:51 -04:00
Sean Braithwaite
211bd64355 Merge branch 'master' into brapse/blockchain-v2-riri-routine 2019-09-18 15:46:29 -04:00
Sean Braithwaite
0cbf32de97 merge fix 2019-09-18 15:22:24 -04:00
Gustavo Chaín
2c7f42fafa fix Header misalignment in StringIndent (#3996) 2019-09-18 14:10:00 +02:00
Marko
1b54369f41 custom marshallers for proto types, which EmitDefaults (#3889)
* Remove omitempty from *pb.go

- remove omitempty from *pb.go files
- added command to makefile for everytime `make protoc_all` is run

- open question:
  - Do we want to further remove omitempty from other places
	- https://github.com/tendermint/tendermint/blob/master/rpc/lib/types/types.go#L151
	- and other places
ref #3882

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>

* bring back omitempty to *pb.go

* Update types/tx.go

* custom marshlers

* undo benchmark `omitepmty`

* golangci lint fix

* cleanup comments

* changelog_pending entry
2019-09-18 12:32:50 +03:00
Marko
98b91ce2a2 tm-bench: add deprecation warning (#3992)
- Added a deprecation warining in for deprecation of tm-bench in favor of tm-load-test

- With the merging of this pr we can close tm-bench related issues.

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>
2019-09-18 12:04:40 +03:00
Marko
9d4a480f54 makefile: minor cleanup (#3994)
- goimports is not used as a tool anymore
	- correct me if wrong
- rename devtools folder to merely tools.mk
- remove slate_header.txt

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>
2019-09-18 11:45:03 +03:00
Sean Braithwaite
ffb066777e Merge branch 'master' into brapse/blockchain-v2-riri-routine 2019-09-17 17:47:20 -04:00
Sean Braithwaite
d3d034e572 tidying 2019-09-17 15:18:15 -04:00
Marko
45ddd67bd6 Add MempoolClient to Client Interface (#3987)
* Add MempoolClient to Client Interface

closes #3984

- add mempoolclient interface to client interface

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>

* Update CHANGELOG_PENDING.md

* add golang-ci version
2019-09-16 11:26:13 +02:00
Marko
522a849ba9 get_tools = tools (#3988)
- in the makefile update `get_tools` was changed to just `tools`

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>
2019-09-16 10:46:10 +02:00
Sean Braithwaite
99b7a33f90 align buffer sizes 2019-09-14 13:01:19 -04:00
Sean Braithwaite
822942a2e4 better debugging logging 2019-09-14 12:49:10 -04:00
Sean Braithwaite
9bd2c0389f rename trySend to end 2019-09-13 18:54:25 -04:00
Sean Braithwaite
fbede85e20 changes based on feedback 2019-09-13 18:36:02 -04:00
Marko
21d46dea93 Update Makefile (#3949)
* Update makefile

- these two files were taken from the sdk with adjustments for usage in TM

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>

* .phony

* remove slate

* more tools

* circleci update 1/2

* fixed typo

* fixed bash error

* '<<' must be escaped in 2.1

* fixed invalid config

* removed golangci-lint from tools. runs as github app now

* one more issue in Circle config

* some more work on the Circle config

* minor changes

* fiddling with restore cache config

* fiddling with restore cache config

* added commands in circle config. makefile changes

* bash shenannigans

* bash shenannigans

* fighting the config demons

* fighting the config demons, v2

* fighting the config demons, v3

* updating p2p dockerfile

* updating p2p dockerfile

* updating p2p test

* updating p2p test

* testing circle config

* testing circle config

* remove dontcover command

* its the weekend, custom docker image
2019-09-13 18:52:35 +02:00
Sean Braithwaite
e7ee314c99 Subsume the demuxer into the reactor
+ Simplify the design by demuxing events directly in the reactor
2019-09-13 11:33:38 -04:00
Tess Rinearson
7e0b64e8b0 types: add test for block commits with votes for the wrong blockID (#3936)
* types: add test for block commits with votes for the wrong blockID

* remove

* respond to feedback

* verify the commits as well

* testing table

* Update types/block_test.go

Co-Authored-By: Bot from GolangCI <42910462+golangcibot@users.noreply.github.com>

* Update types/block_test.go

Co-Authored-By: Bot from GolangCI <42910462+golangcibot@users.noreply.github.com>

* gofmt

* test panic case
2019-09-12 17:59:41 -04:00
Sean Braithwaite
c62b7fbd7e feedback tweaks 2019-09-12 12:50:25 -04:00
dependabot-preview[bot]
9f80c8e1c5 deps: bump google.golang.org/grpc from 1.23.0 to 1.23.1 (#3982)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.23.0 to 1.23.1.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.23.0...v1.23.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-09-12 20:08:50 +04:00
Sean Braithwaite
5474528db1 Switch to a priority queue:
* Routines will now use a priority queue instead of channels to
    iterate over events
2019-09-12 12:06:26 -04:00
Ethan Buchman
fc1c37c4ac Update ADR-025 and mark it as Accepted (#3958)
* update adr-025, mark accepted

* minor update

* Update docs/architecture/adr-025-commit.md

Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com>
2019-09-11 16:10:07 +02:00
Thane Thomson
9c82780742 Add cleveldb build for Amazon Linux (#3976)
* Add cleveldb build for Amazon Linux

In attempting to build Tendermint binaries with cleveldb support that we
can use for load testing (see https://github.com/interchainio/got), it
became apparent that we need a bit of a simpler build process for this
kind of executable. Since we're basing our load testing infrastructure
on Amazon Linux, it makes sense to support such a build process for
Amazon Linux in a platform-independent way.

This PR allows one to simply build the Amazon Linux-compatible binary
using Docker on one's local machine. It first builds an Amazon
Linux-based build image with Go v1.12.9, and then it uses that image to
build the cleveldb version of Tendermint.

This should, in theory, be compatible with CentOS too, but that's yet to
be tested.

* Add comment describing the new Makefile target

* Add missing PHONY entry for new Makefile target

* Expand on Makefile comment
2019-09-11 11:29:27 +02:00
Phil Salant
d1d517a9b7 linters: enable scopelint (#3963)
* Pin range scope vars

* Don't disable scopelint

This PR repairs linter errors seen when running the following commands:
golangci-lint run --no-config --disable-all=true --enable=scopelint

Contributes to #3262
2019-09-11 09:15:18 +04:00
Shivani Joshi
961e1d360f docs/guides: specify a fix for badger err on Windows (#3974)
* Added badger error for Windows

* Update go-built-in.md

* Update docs/guides/go-built-in.md

Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com>

* Update docs/guides/go.md

Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com>

When restarting the app, it throws an error saying that it requires truncation of badger value log because it is corrupted. This seems to be an issue with badger DB and it is reported here. It solves the problem when you set the truncate value to true, but this obviously results in loss of data. I am not sure if this is a concern or not. However, it'd great if this could be documented for Windows users!

Fixes #3954
2019-09-10 23:19:33 +04:00
Anton Kaliaev
777ff271cb enable unconvert, goconst and nakedret linters (#3973)
This should've been a part of
https://github.com/tendermint/tendermint/pull/3960, but I forgot about
it while reviewing.

A good programmer is someone who always looks both ways before crossing
a one-way street. - Doug Linder
2019-09-10 21:58:17 +04:00
Jon
2edd89b450 mempool: fix memory loading error on 32-bit machines (#3969)
* Correct memory alignment for 32-bit machine. 

Switching the `txsBytes` and `rechecking` fields of `CListMempool` to ensure the correct memory alignment for `atomic.LoadInt64` on 32-bit machine.

* Update CHANGELOG_PENDING.md for #3968

Fixed #3968 `mempool` Memory Loading Error on 32-bit Ubuntu 16.04 Machine.

* Update CHANGELOG_PENDING.md

Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com>
2019-09-10 16:51:19 +04:00
Anton Kaliaev
c4294bd4d7 cs: check for SkipTimeoutCommit or wait timeout in handleTxsAvailable (#3928)
* cs: check for SkipTimeoutCommit or wait timeout in handleTxsAvailable

Previously, if create_empty_blocks was set to false, TM sometimes could
create 2 consecutive blocks without actually waiting for timeoutCommit
(1 sec. by default).

```
I[2019-08-29|07:32:43.874] Executed block                               module=state height=47 validTxs=10 invalidTxs=0
I[2019-08-29|07:32:43.876] Committed state                              module=state height=47 txs=10 appHash=F88C010000000000
I[2019-08-29|07:32:44.885] Executed block                               module=state height=48 validTxs=2 invalidTxs=0
I[2019-08-29|07:32:44.887] Committed state                              module=state height=48 txs=2 appHash=FC8C010000000000
I[2019-08-29|07:32:44.908] Executed block                               module=state height=49 validTxs=8 invalidTxs=0
I[2019-08-29|07:32:44.909] Committed state                              module=state height=49 txs=8 appHash=8C8D010000000000
I[2019-08-29|07:32:45.886] Executed block                               module=state height=50 validTxs=2 invalidTxs=0
I[2019-08-29|07:32:45.895] Committed state                              module=state height=50 txs=2 appHash=908D010000000000
I[2019-08-29|07:32:45.908] Executed block                               module=state height=51 validTxs=8 invalidTxs=0
I[2019-08-29|07:32:45.909] Committed state                              module=state height=51 txs=8 appHash=A08D010000000000
```

This commit fixes that by adding a check to handleTxsAvailable.

Fixes #3908

* update changelog

* schedule timeoutCommit if StartTime is in the future

or SkipTimeoutCommit=true && we DON'T have all the votes

* fix TestReactorCreatesBlockWhenEmptyBlocksFalse

by checking if we have LastCommit or not

* address Ethan's comments
2019-09-10 16:38:37 +04:00
Phil Salant
04d13d9945 fix linter errors thrown by unconvert, goconst, and nakedret (#3960)
* Remove unnecessary type conversions

* Consolidate repeated strings into consts

* Clothe return statements

* Update blockchain/v1/reactor_fsm_test.go

Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com>

This PR repairs linter errors seen when running the following commands:
golangci-lint run --no-config --disable-all=true --enable=unconvert
golangci-lint run --no-config --disable-all=true --enable=goconst
golangci-lint run --no-config --disable-all=true --enable=nakedret

Contributes to #3262
2019-09-10 11:31:44 +04:00
Mircea Colonescu
7f0c55f249 ran go mod tidy (#3967) 2019-09-10 08:56:16 +02:00
Michael Booth
22000c6a09 Zm fork accountability (#3840)
* Add fork scenarios

* Add more details about varios attack cases

* added more structure to the attack scenarios

* Update docs/spec/consensus/fork-accountability.md

Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com>

* Update docs/spec/consensus/fork-accountability.md

Co-Authored-By: Anca Zamfir <ancazamfir@users.noreply.github.com>

* Update docs/spec/consensus/fork-accountability.md

Co-Authored-By: Anca Zamfir <ancazamfir@users.noreply.github.com>

* Update docs/spec/consensus/fork-accountability.md

Co-Authored-By: Anca Zamfir <ancazamfir@users.noreply.github.com>

* Update docs/spec/consensus/fork-accountability.md

Co-Authored-By: Anca Zamfir <ancazamfir@users.noreply.github.com>

* Update docs/spec/consensus/fork-accountability.md

Co-Authored-By: Anca Zamfir <ancazamfir@users.noreply.github.com>

* Update docs/spec/consensus/fork-accountability.md

Co-Authored-By: Anca Zamfir <ancazamfir@users.noreply.github.com>

* Update docs/spec/consensus/fork-accountability.md

Co-Authored-By: Anca Zamfir <ancazamfir@users.noreply.github.com>

* Update docs/spec/consensus/fork-accountability.md

Co-Authored-By: Anca Zamfir <ancazamfir@users.noreply.github.com>

* consensus properties explained

* added question on conflicting proposals

* misbehavior

* misbehave

* more misbehavior

* table discussion

* Add few clarifications
2019-09-06 19:45:49 +02:00
gracenoah
aa721b972f rpc: allow using a custom http client in rpc client (#3779)
fixes #2010

* allow using a custom http client in rpc client

* add tests and fix bug

* fix confusion between remote and address

* parse remote inside NewJSONRPCClientWithHTTPClient

* cleanups

* add warnings

* add changelog entry

* Update CHANGELOG_PENDING.md

Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com>
2019-09-05 17:13:22 +04:00
dependabot-preview[bot]
72285115ec deps: bump github.com/go-kit/kit from 0.6.0 to 0.9.0 (#3952)
Bumps [github.com/go-kit/kit](https://github.com/go-kit/kit) from 0.6.0 to 0.9.0.
- [Release notes](https://github.com/go-kit/kit/releases)
- [Commits](https://github.com/go-kit/kit/compare/v0.6.0...v0.9.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-09-05 12:34:28 +04:00
dependabot-preview[bot]
c8ae619f1b deps: bump github.com/stretchr/testify from 1.3.0 to 1.4.0 (#3951)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.3.0 to 1.4.0.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.3.0...v1.4.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-09-05 12:25:17 +04:00
Marko
4ba49a4236 deps: bump viper to 1.4.0 and logfmt to 0.4.0 (#3950)
- depndabot couldnt update a few deps as they needed to beupdated together.
- PRs: #3946, 3938 can be closed with this PR

Signedoff-by: Marko Baricevic <marbar3778@yahoo.com>
2019-09-05 11:56:32 +04:00
dependabot-preview[bot]
af66d8c9d8 deps: bump github.com/gorilla/websocket from 1.2.0 to 1.4.1 (#3945)
Bumps [github.com/gorilla/websocket](https://github.com/gorilla/websocket) from 1.2.0 to 1.4.1.
- [Release notes](https://github.com/gorilla/websocket/releases)
- [Commits](https://github.com/gorilla/websocket/compare/v1.2.0...v1.4.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-09-05 10:42:19 +04:00
dependabot-preview[bot]
e1e4ef4abf deps: bump google.golang.org/grpc from 1.22.0 to 1.23.0 (#3942)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.22.0 to 1.23.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.22.0...v1.23.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-09-05 10:32:00 +04:00
dependabot-preview[bot]
39ff9b8ee9 deps: bump github.com/libp2p/go-buffer-pool from 0.0.1 to 0.0.2 (#3948)
Bumps [github.com/libp2p/go-buffer-pool](https://github.com/libp2p/go-buffer-pool) from 0.0.1 to 0.0.2.
- [Release notes](https://github.com/libp2p/go-buffer-pool/releases)
- [Commits](https://github.com/libp2p/go-buffer-pool/compare/v0.0.1...v0.0.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-09-05 10:13:02 +04:00
dependabot-preview[bot]
a12eeeff62 deps: bump github.com/fortytw2/leaktest from 1.2.0 to 1.3.0 (#3943)
Bumps [github.com/fortytw2/leaktest](https://github.com/fortytw2/leaktest) from 1.2.0 to 1.3.0.
- [Release notes](https://github.com/fortytw2/leaktest/releases)
- [Commits](https://github.com/fortytw2/leaktest/compare/v1.2.0...v1.3.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-09-04 21:06:48 +04:00
dependabot-preview[bot]
5a1e326577 deps: bump github.com/rs/cors from 1.6.0 to 1.7.0 (#3939)
Bumps [github.com/rs/cors](https://github.com/rs/cors) from 1.6.0 to 1.7.0.
- [Release notes](https://github.com/rs/cors/releases)
- [Commits](https://github.com/rs/cors/compare/v1.6.0...v1.7.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-09-04 20:33:29 +04:00
dependabot-preview[bot]
482cb62cf5 deps: bump github.com/magiconair/properties from 1.8.0 to 1.8.1 (#3937)
Bumps [github.com/magiconair/properties](https://github.com/magiconair/properties) from 1.8.0 to 1.8.1.
- [Release notes](https://github.com/magiconair/properties/releases)
- [Changelog](https://github.com/magiconair/properties/blob/master/CHANGELOG.md)
- [Commits](https://github.com/magiconair/properties/compare/v1.8.0...v1.8.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-09-04 20:12:26 +04:00
Anton Kaliaev
fe2dddd3fe deps: update gogo/protobuf version from v1.2.1 to v1.3.0 (#3947)
* update gogo/protobuf version from v1.2.1 to v1.3.0

Also errcheck from v1.1.0 to v1.2.0

See full diff in
- https://github.com/gogo/protobuf/compare/v1.2.1...v1.3.0
- https://github.com/kisielk/errcheck/compare/v1.1.0...v1.2.0

Changelog:

Tested versions:

go 1.12.9
protoc 3.7.1
Improvements:

    plugin/stringer - Handle repeated and/or nullable types a bit better now.
    plugin/size - Remove the loop in sovXXX by using bit twiddling.
        Thanks: https://github.com/apelisse
    plugin/marshalto - Implemented a reverse marshal strategy which allows for faster marshalling. This now avoids a recursive (and repeated) call to Size().
        Thanks: https://github.com/apelisse
    plugin/compare - Added support for for oneof types.

Bug fixes:

    protoc-gen-gogo/generator - Fix assignment to entry in nil map.
        Thanks: https://github.com/tgulacsi
    protoc-gen-gogo/generator - Allows plugins to call RecordTypeUse without panicking.
        Thanks: https://github.com/fedenusy
    proto/extensions - Fixed set extension regression. We did not clear the extensions before setting.
    io/uint32 - fix uint32reader bug that causes ReadMsg to recreate buffer when lengths are the same.
        Thanks: https://github.com/SebiSujar
    proto/table_merge: Fix merge of non-nullable slices.
        Thanks: https://github.com/euroelessar

Upstream commits:

    merged in golang/protobuf commit 318d17de72747ed1c16502681db4b2bb709a92d0 - Add UnimplementedServer for server interface
    merged in golang/protobuf commit b85cd75de734650db18a99a943fe351d41387800 - protoc-gen-go/grpc: inline errUnimplemented function
    merged in golang/protobuf commit d3c38a4eb4970272b87a425ae00ccc4548e2f9bb - protoc-gen-go/grpc: use status and code packages only if needed
    merged in golang/protobuf commit e91709a02e0e8ff8b86b7aa913fdc9ae9498e825 - fix indentation in jsonpb with Any messages
    merged in golang/protobuf commit 8d0c54c1246661d9a51ca0ba455d22116d485eaa - protoc-gen-go: generate XXX_OneofWrappers instead of XXX_OneofFuncs

Misc:

    extensions.md - Markdown update.
        Thanks: https://github.com/TennyZhuang
    Readme.md - Added user.
    go/protoc update - Updated to go1.12.x and protoc 3.7.1
    Makefile update - fix go vet shadow tool reference
    test/mixbench - Update mixbench tool. Expose runnable benchmarks via flags.

* update certstrap

* update golangci-lint from v1.13.2 to v1.17.1

* update gox as well

* test

* comment out golangci temporary

* comment out go-deadlock deps
2019-09-04 19:58:43 +04:00
Anton Kaliaev
acb874eaf6 docs: move dev sessions into docs (#3934)
* docs: move dev sessions into docs

and transform the list into a table

* fix formatting
2019-09-03 11:59:13 +04:00
Michelle-L
1802b60833 docs: add dev sessions from YouTube (#3929)
* Create development_sessions

* Rename development_sessions to videos_development_sessions

* Update videos_development_sessions

* Rename videos_development_sessions to videos_development_sessions_md

* Delete videos_development_sessions_md

* Add files via upload
2019-09-03 11:38:32 +04:00
Marko
72785a2e86 docs: switch the data in /unconfirmed_txs and num_unconfirmed_txs (#3933)
- switch the data that was represented in `/unconfirmed_txs` and `num_unconfirmed_txs`

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>
2019-09-02 22:23:02 +04:00
Anton Kaliaev
5346d8b9a2 v0.32.3 changelog and version bump (#3924) 2019-08-28 12:00:55 +04:00
josef-widder
f46e43eb38 Updated lite client spec (#3841)
* updates lite cline spec based on Zarko's and my previous specs

* updates spec incorporating comments from Aug 8 meeting

* added check of validators

* Update docs/spec/consensus/light-client.md

Co-Authored-By: Anca Zamfir <ancazamfir@users.noreply.github.com>

* Update docs/spec/consensus/light-client.md

Co-Authored-By: Anca Zamfir <ancazamfir@users.noreply.github.com>

* Update docs/spec/consensus/light-client.md

Co-Authored-By: Anca Zamfir <ancazamfir@users.noreply.github.com>

* Update docs/spec/consensus/light-client.md

Co-Authored-By: Anca Zamfir <ancazamfir@users.noreply.github.com>

* fix typos & improve formatting

Co-Authored-By: Anca Zamfir <ancazamfir@users.noreply.github.com>

* Update docs/spec/consensus/light-client.md

Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com>

* incorporated some of Anca's and Anton's comments

* addressed all current comments

* highlight assumptions/verification conditions

* added remark on checks

* Update docs/spec/consensus/light-client.md

Co-Authored-By: Ethan Buchman <ethan@coinculture.info>

* Update docs/spec/consensus/light-client.md

Co-Authored-By: Ethan Buchman <ethan@coinculture.info>

* Update docs/spec/consensus/light-client.md

Co-Authored-By: Ethan Buchman <ethan@coinculture.info>

* fixes

* addressed Ethan's comments from Aug 18

* trustlevel

* description of 3 docs fixed

* added comment on justification asked for by Anca

* bla

* removed trustlevel for adjacent headers. Added bisection order.

* added comment in bisection

* fixed comment in checksupport

* added comment on bisection timeout
2019-08-27 15:18:01 +02:00
Marko
e3a97b0981 Test nodejs image (#3921)
* Test nodejs image

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>

* minor changes

* v in version

* version in swagger.yaml
2019-08-26 14:27:33 +02:00
Ethan Buchman
c475f25786 docs: fix some typos and changelog entries (#3915)
Fixes some typos in the docs.

Fixes the classification of some changelog items re Features vs. Improvements
2019-08-23 12:11:26 +04:00
Gustavo Chaín
7b2d018f84 rpc: protect subscription access from race condition (#3910)
When using the RPC client in my test suite (with -race enabled), I do a lot of Subscribe/Unsubscribe operations, at some point (randomly) the race detector returns the following warning:

WARNING: DATA RACE
Read at 0x00c0009dbe30 by goroutine 31:
runtime.mapiterinit()
/usr/local/go/src/runtime/map.go:804 +0x0
github.com/tendermint/tendermint/rpc/client.(*WSEvents).redoSubscriptionsAfter()
/go/pkg/mod/github.com/tendermint/tendermint@v0.31.5/rpc/client/httpclient.go:364 +0xc0
github.com/tendermint/tendermint/rpc/client.(*WSEvents).eventListener()
/go/pkg/mod/github.com/tendermint/tendermint@v0.31.5/rpc/client/httpclient.go:393 +0x3c6 

Turns out that the redoSubscriptionAfter is not protecting the access to subscriptions.

The following change protects the read access to the subscription map behind the mutex
2019-08-22 13:23:07 +04:00
Sean Braithwaite
9d41770a99 Close rdy channel
+ close `rdy` channel to ensure that calls to `<-ready()` will
    always return if the routine is ready
2019-08-21 21:37:53 +02:00
Marko
eec6d33e14 remove mentions of go-wire (#3904)
*    Cleanup go-wire mentions
*    remove Roadmap.md
*    build_race in makefile was failing

ref #2790
2019-08-16 17:45:19 +04:00
Karoly Albert Szabo
ead3eefe5b [RPC] Static swagger (#3880)
* manually swagging

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* three definitions with polymorphism

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* added blockchain and block

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* low quality generation, commit, block_response and validators

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* genesis and consensus states endpoints

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* fix indentation

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* consensus parameters

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* fix indentation

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* add height to consensus parameters endpoint

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* unconfirmed_txs and num_unconfirmed_txs

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* add missing query parameter

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* add ABCI queries

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* added index document for swagger documentation

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* add missing routes

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* contract tests added on CCI

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* contract tests job should be in the test suite

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* simplify requirements to test

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* typo

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* build is a prerequisite to start localnet

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* reduce nodejs size, move goodman to get_tools, add docs, fix comments

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* Update scripts/get_tools.sh

That's cleaner, thanks!

Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com>

* xz not supported by cci image, let's keep it simple

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* REMOVE-indirect debug of CCI paths

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* dirty experiment, volume is empty but binary has been produced

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* dirty experiment, volume is empty but binary has been produced

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* dirty experiment going on

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* locally works, CCI have difficulties with second layaer containers volumes

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* restore experiment, use machine instead of docker for contract tests

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* simplify a bit

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* rollback on machine golang

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* Document the changes

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* Changelog

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* comments

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>
2019-08-16 11:29:01 +02:00
Alessio Treglia
5670a7cf7b gitian: update reproducible builds to build with Go 1.12.8 (#3902) 2019-08-16 13:01:47 +04:00
Zaki Manian
7b101ab912 [ADR -44] Lite Client with Weak Subjectivity (#3795)
* Initiat commit of lite client with
with weak subjectivity ADR

* Apply suggestions from code review

Co-Authored-By: Marko <marbar3778@yahoo.com>

* Update docs/architecture/adr-044-lite-client-with-weak-subjectivity.md

Co-Authored-By: Christopher Goes <cwgoes@pluranimity.org>

* Apply suggestions from code review

Co-Authored-By: Christopher Goes <cwgoes@pluranimity.org>

* Apply suggestions from code review

Co-Authored-By: Anca Zamfir <ancazamfir@users.noreply.github.com>

* fix typo and format the code block

* address cwgoes comments

* add Positive/Negative points

* change status to accepted

* Apply suggestions from code review

Co-Authored-By: Christopher Goes <cwgoes@pluranimity.org>

* link the spec and explain headers caching
2019-08-15 11:50:51 +02:00
Sean Braithwaite
f81c319ece Add some docs 2019-08-13 19:29:28 +02:00
Sean Braithwaite
78d4c3b88a fixes based on feedback 2019-08-13 17:57:17 +02:00
Anton Kaliaev
7fe02a04db store: register block amino, not just crypto (#3894)
* store: register block amino, not just crypto

Fixes #3893

* update changelog
2019-08-13 12:32:40 +04:00
Ivan Kushmantsev
2e39418124 docs: "Writing a Tendermint Core application in Java (gRPC)" guide (#3887)
* add abci grpc java guide

* fix grammar and spelling
2019-08-12 14:39:11 +04:00
Phil Salant
c962567814 ValidateBasic tests (#3879)
* Add test for bcBlockRequestMessage ValidateBasic method

* Add test for bcNoBlockResponseMessage ValidateBasic method

* Add test for bcStatusRequestMessage ValidateBasic method

* Add test for bcStatusResponseMessage ValidateBasic method

* Add blockchain v1 reactor ValidateBasic tests

* Add test for NewRoundStepMessage ValidateBasic method

* Add test for NewValidBlockMessage ValidateBasic method

* Test BlockParts Size

* Import cmn package

* Add test for ProposalPOLMessage ValidateBasic method

* Add test for BlockPartMessage ValidateBasic method

* Add test for HasVoteMessage ValidateBasic method

* Add test for VoteSetMaj23Message ValidateBasic method

* Add test for VoteSetBitsMessage ValidateBasic method

* Fix linter errors

* Improve readability

* Add test for BaseConfig ValidateBasic method

* Add test for RPCConfig ValidateBasic method

* Add test for P2PConfig ValidateBasic method

* Add test for MempoolConfig ValidateBasic method

* Add test for FastSyncConfig ValidateBasic method

* Add test for ConsensusConfig ValidateBasic method

* Add test for InstrumentationConfig ValidateBasic method

* Add test for BlockID ValidateBasic method

* Add test for SignedHeader ValidateBasic method

* Add test for MockGoodEvidence and MockBadEvidence ValidateBasic methods

* Remove debug logging

Co-Authored-By: Marko <marbar3778@yahoo.com>

* Update MempoolConfig field

* Test a single struct field at a time, for maintainability

Fixes #2740
2019-08-11 22:27:03 +04:00
Marko
8a282a5fee replace errors.go with github.com/pkg/errors (2/2) (#3890)
* init of (2/2) common errors

* Remove instances of cmn.Error (2/2)

- Replace usage of cmnError and errorWrap
- ref #3862

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>

* comment wording

* simplify IsErrXXX functions

* log panic along with stopping the MConnection
2019-08-11 21:03:40 +04:00
Sean Braithwaite
2c8cbfc26a linter fixes 2019-08-08 17:42:46 +02:00
Sean Braithwaite
acbfe67fb8 set logger 2019-08-08 16:56:39 +02:00
Sean Braithwaite
aeac4743cc typo fix 2019-08-08 16:54:25 +02:00
Sean Braithwaite
e826ca3c49 demuxer cleanup 2019-08-08 16:48:07 +02:00
Sean Braithwaite
5b880fbcff cleanup events 2019-08-08 15:53:02 +02:00
Sean Braithwaite
c081b60ef6 Solidify API:
+ use `trySend` the replicate peer sending
    + expose `next()` as a chan of events as output
    + expose `final()` as a chan of error, for the final error
    + add `ready()` as chan struct when routine is ready
2019-08-08 15:12:11 +02:00
Marko
8dc39b69b7 replace errors.go with github.com/pkg/errors (1/2) (#3888)
* (1/2) of replace errors.go with github.com/pkg/errors

ref #3862

- step one in removing instances of errors.go in favor of github.com/pkg/errors

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>

* gofmt

* add in /store
2019-08-08 14:31:13 +04:00
Sean Braithwaite
e4913f533a Fix race condition in shutdown:
+ ensure that we stop accepting messages once `stop` has been called
      to avoid the case in which we attempt to write to a channel which
      has already been closed
2019-08-06 18:00:14 +02:00
Sean Braithwaite
0cf9f86292 Modification based on feedback
+ `routine.send` returns false when routine is not running
    + this will prevent panics sending to channels which have been
    closed
    + Make output channels routine specific removing the risk of someone
    writting to a channel which was closed by another touine.
    + consistency changes between the routines and the demuxer
2019-08-06 13:27:15 +02:00
Marko
d70135ec71 [ADR - 42] State-sync (#3769)
* init adr 042: state sync

* link to blockchain reactor

* brapse proposal

* formatting

* response to feedback

* Update docs/architecture/adr-042-state-sync.md

Co-Authored-By: Aditya <adityasripal@gmail.com>

* Update security models and more

* clarify compression

* typo

* Update docs/architecture/adr-042-state-sync.md

Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com>
2019-08-06 10:25:24 +02:00
Jun Kimura
e179787d40 mempool: make max_tx_bytes configurable instead of max_msg_bytes (#3877)
Fix #3868 (comment)

Commits:

* mempool: make `max_tx_bytes` configurable instead of `max_msg_bytes`

* update CHANGELOG_PENDING

* apply suggestions from code review
2019-08-05 20:01:30 +04:00
Sean Braithwaite
0cf8812b17 [blockchain] v2 riri Schedule composit data structure (#3848)
* Add Schedule:

    + The schedule is a data structure used to determine the optimal
      schedule of requests to the optimal set of peers in order to perform
      fast-sync as fast and efficiently as possible.

* Add some doc strings

* fix golangci

* Add Schedule:

    + The schedule is a data structure used to determine the optimal
      schedule of requests to the optimal set of peers in order to perform
      fast-sync as fast and efficiently as possible.

* Add some doc strings

* remove globals from tests
2019-08-05 17:26:46 +02:00
Juan Leni
f7f034a8be privval: refactor Remote signers (#3370)
This PR is related to #3107 and a continuation of #3351

It is important to emphasise that in the privval original design, client/server and listening/dialing roles are inverted and do not follow a conventional interaction.

Given two hosts A and B:

    Host A is listener/client
    Host B is dialer/server (contains the secret key)
    When A requires a signature, it needs to wait for B to dial in before it can issue a request.
    A only accepts a single connection and any failure leads to dropping the connection and waiting for B to reconnect.

The original rationale behind this design was based on security.

    Host B only allows outbound connections to a list of whitelisted hosts.
    It is not possible to reach B unless B dials in. There are no listening/open ports in B.

This PR results in the following changes:

    Refactors ping/heartbeat to avoid previously existing race conditions.
    Separates transport (dialer/listener) from signing (client/server) concerns to simplify workflow.
    Unifies and abstracts away the differences between unix and tcp sockets.
    A single signer endpoint implementation unifies connection handling code (read/write/close/connection obj)
    The signer request handler (server side) is customizable to increase testability.
    Updates and extends unit tests

A high level overview of the classes is as follows:

Transport (endpoints): The following classes take care of establishing a connection

    SignerDialerEndpoint
    SignerListeningEndpoint
    SignerEndpoint groups common functionality (read/write/timeouts/etc.)

Signing (client/server): The following classes take care of exchanging request/responses

    SignerClient
    SignerServer

This PR also closes #3601

Commits:

* refactoring - work in progress

* reworking unit tests

* Encapsulating and fixing unit tests

* Improve tests

* Clean up

* Fix/improve unit tests

* clean up tests

* Improving service endpoint

* fixing unit test

* fix linter issues

* avoid invalid cache values (improve later?)

* complete implementation

* wip

* improved connection loop

* Improve reconnections + fixing unit tests

* addressing comments

* small formatting changes

* clean up

* Update node/node.go

Co-Authored-By: jleni <juan.leni@zondax.ch>

* Update privval/signer_client.go

Co-Authored-By: jleni <juan.leni@zondax.ch>

* Update privval/signer_client_test.go

Co-Authored-By: jleni <juan.leni@zondax.ch>

* check during initialization

* dropping connecting when writing fails

* removing break

* use t.log instead

* unifying and using cmn.GetFreePort()

* review fixes

* reordering and unifying drop connection

* closing instead of signalling

* refactored service loop

* removed superfluous brackets

* GetPubKey can return errors

* Revert "GetPubKey can return errors"

This reverts commit 68c06f19b4650389d7e5ab1659b318889028202c.

* adding entry to changelog

* Update CHANGELOG_PENDING.md

Co-Authored-By: jleni <juan.leni@zondax.ch>

* Update privval/signer_client.go

Co-Authored-By: jleni <juan.leni@zondax.ch>

* Update privval/signer_dialer_endpoint.go

Co-Authored-By: jleni <juan.leni@zondax.ch>

* Update privval/signer_dialer_endpoint.go

Co-Authored-By: jleni <juan.leni@zondax.ch>

* Update privval/signer_dialer_endpoint.go

Co-Authored-By: jleni <juan.leni@zondax.ch>

* Update privval/signer_dialer_endpoint.go

Co-Authored-By: jleni <juan.leni@zondax.ch>

* Update privval/signer_listener_endpoint_test.go

Co-Authored-By: jleni <juan.leni@zondax.ch>

* updating node.go

* review fixes

* fixes linter

* fixing unit test

* small fixes in comments

* addressing review comments

* addressing review comments 2

* reverting suggestion

* Update privval/signer_client_test.go

Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com>

* Update privval/signer_client_test.go

Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com>

* Update privval/signer_listener_endpoint_test.go

Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com>

* do not expose brokenSignerDialerEndpoint

* clean up logging

* unifying methods

shorten test time
signer also drops

* reenabling pings

* improving testability + unit test

* fixing go fmt + unit test

* remove unused code

* Addressing review comments

* simplifying connection workflow

* fix linter/go import issue

* using base service quit

* updating comment

* Simplifying design + adjusting names

* fixing linter issues

* refactoring test harness + fixes

* Addressing review comments

* cleaning up

* adding additional error check
2019-08-05 19:09:10 +04:00
Sean Braithwaite
d1671d6175 blockchain v2: routines
+ Include an implementaiton of the routines specified in ADR-43
    along with a demuxer and some dummy reactor code
2019-08-03 09:19:32 +02:00
Anton Kaliaev
4da3de79a7 consensus: reduce "Error attempting to add vote" message severity (Er… (#3871)
* consensus: reduce "Error attempting to add vote" message severity (Error -> Info)

Fixes #3839

* add missing changelog entry
2019-08-03 00:00:18 +04:00
Anton Kaliaev
f5b116c687 config: move max_msg_bytes into mempool section (#3869)
* config: move max_msg_bytes into mempool section

It was incorrectly placed in fastsync section during
4d7cd8055b (diff-092cdc48047eeb4c0bca311a2e1b8ae6)
merge.

Fixes #3868

* add changelog entry
2019-08-02 23:42:17 +04:00
Anton Kaliaev
be06316c84 Merge pull request #3870 from tendermint/release/v0.32.2
Merge v0.32.2 release back to master
2019-08-02 23:30:37 +04:00
Jack Zampolin
8ba8497ac8 Merge branch 'master' into release/v0.32.2 2019-08-02 08:52:21 -07:00
Marko
f9cce282da gocritic (2/2) (#3864)
Refs #3262
2019-08-02 10:53:52 +04:00
Marko
23fa2e1f1b Merge PR #3860: Update log v0.32.2
* changelog updates

* pr comments
2019-08-01 09:33:27 -07:00
Ethan Buchman
ba9cdeaed9 Merge branch 'v0.32' into release/v0.32.2 2019-07-31 17:26:40 -04:00
Marko Baricevic
8ed1400949 release for v0.32.2 2019-07-31 17:30:11 +02:00
Alexander Bezobchuk
14fa800773 txindexer: Refactor Tx Search Aggregation (#3851)
- Replace the previous intersect call, which was called at each query condition, with a map intersection.
- Replace fmt.Sprintf with string()

closes: #3076

Benchmarks

```
Old
goos: darwin
goarch: amd64
pkg: github.com/tendermint/tendermint/state/txindex/kv
BenchmarkTxSearch-4   	     200	 103641206 ns/op	 7998416 B/op	   71171 allocs/op
PASS
ok  	github.com/tendermint/tendermint/state/txindex/kv	26.019s

New
goos: darwin
goarch: amd64
pkg: github.com/tendermint/tendermint/state/txindex/kv
BenchmarkTxSearch-4   	    1000	  38615024 ns/op	13515226 B/op	  166460 allocs/op
PASS
ok  	github.com/tendermint/tendermint/state/txindex/kv	53.618s
```

~62% performance improvement

Commits:

* Refactor tx search

* Add pending changelog entry

* Add tx search benchmarking

* remove intermediate hashes list

also reset timer in BenchmarkTxSearch
and fix other benchmark

* fix import

* Add test cases

* Fix searching

* Replace fmt.Sprintf with string

* Update state/txindex/kv/kv.go

Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com>

* Rename params

* Cleanup

* Check error in benchmarks
2019-07-31 19:01:55 +04:00
Marko
f3ab967a46 version tmdb (#3854) 2019-07-31 16:17:09 +02:00
Marko
a1eb2f6c6b tm-cmn to tm-db (#3850)
* tm-cmn to tm-db

* go.mod changes

* go.mod changes

* more go.mod

* fix tm-db

* ci fix, pending change
2019-07-31 11:34:17 +02:00
Marko
41bf54a906 gocritic (1/2) (#3836)
Add gocritic as a linter

    The linting is not complete, but should i complete in this PR or in a following.

    23 files have been touched so it may be better to do in a following PR


Commits:

* Add gocritic to linting

- Added gocritic to linting

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>

* gocritic

* pr comments

* remove switch in cmdBatch
2019-07-30 18:13:35 +04:00
Anton Kaliaev
88e0973f7d node: allow replacing existing p2p.Reactor(s) (#3846)
* node: allow replacing existing p2p.Reactor(s)

using [`CustomReactors`
option](https://godoc.org/github.com/tendermint/tendermint/node#CustomReactors).
Warning: beware of accidental name clashes. Here is the list of existing
reactors: MEMPOOL, BLOCKCHAIN, CONSENSUS, EVIDENCE, PEX.

* check the absence of "CUSTOM" prefix

* merge 2 tests

* add doc.go to node package
2019-07-30 15:08:11 +02:00
Ivan Kushmantsev
1e3364a014 docs: "Writing a Tendermint Core application in Kotlin (gRPC)" guide (#3838)
* add abci grpc kotlin guide

* Update docs/guides/kotlin.md

Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com>

* Update docs/guides/kotlin.md

Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com>

* Update docs/guides/kotlin.md

Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com>

* Update kotlin.md
2019-07-29 21:48:11 +04:00
Anton Kaliaev
a4e0a46b73 docs: add a footer to guides (#3835) 2019-07-25 20:39:39 +04:00
folex
3ee7c0bfba p2p: Do not write 'Couldn't connect to any seeds' if there are no seeds (#3834)
* Do not write 'Couldn't connect to any seeds' if there are no seeds

* changelog

* remove privValUpgrade

* Fix typo in changelog

* Update CHANGELOG_PENDING.md

Co-Authored-By: Marko <marbar3778@yahoo.com>

I'm setting up all peers dynamically by calling dial_peers, so p2p.seeds in configs is empty, and I'm seeing error log a lot in logs.
2019-07-25 15:58:14 +04:00
Anton Kaliaev
af77077f3c p2p: Fix error logging for connection stop (#3824)
* p2p: fix false-positive error logging when stopping connections

This changeset fixes two types of false-positive errors occurring during
connection shutdown.

The first occurs when the process invokes FlushStop() or Stop() on a
connection. While the previous behavior did properly wait for the sendRoutine
to finish, it did not notify the recvRoutine that the connection was shutting
down. This would cause the recvRouting to receive and error when reading and
log this error. The changeset fixes this by notifying the recvRoutine that
the connection is shutting down.

The second occurs when the connection is terminated (gracefully) by the other side.
The recvRoutine would get an EOF error during the read, log it, and stop the connection
with an error. The changeset detects EOF and gracefully shuts down the connection.

* bring back the comment about flushing

* add changelog entry

* listen for quitRecvRoutine too

* we have to call stopForError

Otherwise peer won't be removed from the peer set and maybe readded
later.
2019-07-25 15:06:18 +04:00
Marko
4b9e8505cb types: move MakeVote / MakeBlock functions (#3819)
to the types package

Paritally Fixes #3584
2019-07-25 12:13:19 +04:00
Marko
98cb8c9783 add staticcheck linting (#3828)
cleanup to add linter

    grpc change:
        https://godoc.org/google.golang.org/grpc#WithContextDialer
        https://godoc.org/google.golang.org/grpc#WithDialer
        grpc/grpc-go#2627
    prometheous change:
        due to UninstrumentedHandler, being deprecated in the future
    empty branch = empty if or else statement
        didn't delete them entirely but commented
        couldn't find a reason to have them
    could not replicate the issue #3406
        but if want to keep it commented then we should comment out the if statement as well
2019-07-25 09:35:30 +04:00
Anton Kaliaev
3a1f876802 docs: add guides to docs (#3830) 2019-07-24 23:05:00 +04:00
Marko
362729c2bb Renamed wire.go to codec.go (#3827)
* Renamed wire.go to codec.go

- Wire was the previous name of amino
- Codec describes the file better than `wire` & `amino`

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>

* ide error

* rename amino.go to codec.go
2019-07-23 15:35:36 +02:00
Anca Zamfir
4d7cd8055b blockchain: Reorg reactor (#3561)
* go routines in blockchain reactor

* Added reference to the go routine diagram

* Initial commit

* cleanup

* Undo testing_logger change, committed by mistake

* Fix the test loggers

* pulled some fsm code into pool.go

* added pool tests

* changes to the design

added block requests under peer

moved the request trigger in the reactor poolRoutine, triggered now by a ticker

in general moved everything required for making block requests smarter in the poolRoutine

added a simple map of heights to keep track of what will need to be requested next

added a few more tests

* send errors to FSM in a different channel than blocks

send errors (RemovePeer) from switch on a different channel than the
one receiving blocks
renamed channels
added more pool tests

* more pool tests

* lint errors

* more tests

* more tests

* switch fast sync to new implementation

* fixed data race in tests

* cleanup

* finished fsm tests

* address golangci comments :)

* address golangci comments :)

* Added timeout on next block needed to advance

* updating docs and cleanup

* fix issue in test from previous cleanup

* cleanup

* Added termination scenarios, tests and more cleanup

* small fixes to adr, comments and cleanup

* Fix bug in sendRequest()

If we tried to send a request to a peer not present in the switch, a
missing continue statement caused the request to be blackholed in a peer
that was removed and never retried.

While this bug was manifesting, the reactor kept asking for other
blocks that would be stored and never consumed. Added the number of
unconsumed blocks in the math for requesting blocks ahead of current
processing height so eventually there will be no more blocks requested
until the already received ones are consumed.

* remove bpPeer's didTimeout field

* Use distinct err codes for peer timeout and FSM timeouts

* Don't allow peers to update with lower height

* review comments from Ethan and Zarko

* some cleanup, renaming, comments

* Move block execution in separate goroutine

* Remove pool's numPending

* review comments

* fix lint, remove old blockchain reactor and duplicates in fsm tests

* small reorg around peer after review comments

* add the reactor spec

* verify block only once

* review comments

* change to int for max number of pending requests

* cleanup and godoc

* Add configuration flag fast sync version

* golangci fixes

* fix config template

* move both reactor versions under blockchain

* cleanup, golint, renaming stuff

* updated documentation, fixed more golint warnings

* integrate with behavior package

* sync with master

* gofmt

* add changelog_pending entry

* move to improvments

* suggestion to changelog entry
2019-07-23 10:58:52 +02:00
Anton Kaliaev
756440e0a2 rpc: return err if page is incorrect (less than 0 or greater than tot… (#3825)
* rpc: return err if page is incorrect (less than 0 or greater than total pages)

Fixes #3813

* fix rpc_test
2019-07-23 12:25:59 +04:00
Jun Kimura
5398420103 mempool: make max_msg_bytes configurable (#3826)
* mempool: make max_msg_bytes configurable

* apply suggestions from code review

* update changelog pending

* apply suggestions from code review again
2019-07-22 17:17:09 +02:00
Anton Kaliaev
c6daa48368 rpc: /broadcast_evidence (#3481)
* implement broadcast_duplicate_vote endpoint

* fix test_cover

* address comments

* address comments

* Update abci/example/kvstore/persistent_kvstore.go

Co-Authored-By: mossid <torecursedivine@gmail.com>

* Update rpc/client/main_test.go

Co-Authored-By: mossid <torecursedivine@gmail.com>

* address comments in progress

* reformat the code

* make linter happy

* make tests pass

* replace BroadcastDuplicateVote with BroadcastEvidence

* fix test

* fix endpoint name

* improve doc

* fix TestBroadcastEvidenceDuplicateVote

* Update rpc/core/evidence.go

Co-Authored-By: Thane Thomson <connect@thanethomson.com>

* add changelog entry

* fix TestBroadcastEvidenceDuplicateVote
2019-07-22 12:15:29 +04:00
zjubfd
657832a95a p2p/conn: Add Bufferpool (#3664)
* use byte buffer pool to decreass allocs

* wrap to put buffer in defer

* wapper defer

* add dependency

* remove Gopkg,*

* add change log
2019-07-22 09:37:41 +02:00
Jun Kimura
51b3428f5c rpc: make max_body_bytes and max_header_bytes configurable (#3818)
* rpc: make max_body_bytes and max_header_bytes configurable

* update changelog pending
2019-07-20 09:44:42 +02:00
Marko
9e4cd19878 docs: add A TOC to the Readme.md of ADR Section (#3820)
* ADR TOC in readme.md

* Added A TOC to the Readme.md of ADR Section

- Added table of contents to the Readme of the architecture section.
	- Easier to traverse and when you know what is there.
	- If the Adr's become viewable online it would help guide the user

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>

* add tm-cmn to subprojects

* normalize word
2019-07-19 21:29:33 +04:00
Marko
816dfce8fe libs: Remove db from tendermint in favor of tendermint/tm-cmn (#3811)
* Remove db from tendemrint in favor of tendermint/tm-cmn

- remove db from `libs`
- update dependancy, there have been no breaking changes in the updated deps
	- https://github.com/grpc/grpc-go/releases
	- https://github.com/golang/protobuf/releases

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>

* changelog add

* gofmt

* more gofmt
2019-07-19 09:54:45 +04:00
Anton Kaliaev
71862b7b8d docs: "Writing a built-in Tendermint Core application in Go" guide (#3608)
* docs: go built-in guide

* fix package imports, add badger db, simplify Query

* newTendermint function

* working example

* finish the first guide

* add one more note

* add the second Golang guide - external ABCI app

* fix typos
2019-07-18 15:15:14 +04:00
Marko
1844bff613 abci/client: fix DATA RACE in gRPC client (#3798)
* Remove go func {}()

closes #357

- Remove go func(){}() that caused race condiditon

- To reproduce
	- add -race in make file to `install_abci`
	- Remove `CGO_ENABLED=0` & add -race to `install`

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>

* remove -race

* fix data race

also, reorder callbacks similarly to socket client
2019-07-17 11:49:01 +04:00
Roman Useinov
a76c503dc6 abci/server: recover from app panics in socket server (#3809)
fixes #3800
2019-07-17 08:37:27 +04:00
Alex Dupre
fee26405e8 docs: fix consensus spec formatting (#3804) 2019-07-15 23:04:06 +04:00
Ethan Buchman
dbf4062acd Merge pull request #3807 from tendermint/release/v0.32.1
Release/v0.32.1
2019-07-15 12:25:35 -04:00
Marko
06a57baa59 Release/v0.32.1 (#3801)
* Release branch for v0.32.1

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>

* add links to changelog

* wording change

* Apply suggestions from code review

Comment from PR

Co-Authored-By: Ethan Buchman <ethan@coinculture.info>

* add link bump abci Version

* include doc change for abci/app

* moved abci change to features as it doesnt break the abci

* pr comments (#3803)

* pr comments

* abci changelog change

* Marko/update release1 (#3806)

* pr comments

* remove empty space
2019-07-15 12:16:52 -04:00
Ethan Buchman
79e924b34f Merge branch 'master' into release/v0.32.1 2019-07-15 11:42:36 -04:00
Marko
0c9a284f8d Marko/update release1 (#3806)
* pr comments

* remove empty space
2019-07-15 11:42:08 -04:00
Marko
245e1c9ef7 pr comments (#3803)
* pr comments

* abci changelog change
2019-07-15 11:31:50 -04:00
Anton Kaliaev
86f7089396 add a changelog entry (#3802)
Refs https://github.com/tendermint/tendermint/pull/3758#issuecomment-510738955
2019-07-15 11:31:35 -04:00
Marko Baricevic
470f1efcc8 merge master 2019-07-15 11:34:01 +02:00
Marko
e0b9298134 libs: minor cleanup (#3794)
* more minor cleanup of libs

Remove unused `version.go`, `assert.go` and `libs/circle.yml`

* Update types/vote_set_test.go

Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com>

* spelling change
2019-07-14 18:02:48 +04:00
Marko
5d1459b584 libs/fail: clean up fail.go (#3785)
* Clean up `fail.go`

- Clean up fail.go
- Remove unneeded Func `FailRand()`

closes #2729

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>

* add in commented lines
2019-07-14 17:50:33 +04:00
Marko Baricevic
4e3b6bfb26 Merge remote-tracking branch 'origin' into marko/v0.32.1 2019-07-13 13:40:04 +02:00
Marko Baricevic
7a86e49312 moved abci change to features as it doesnt break the abci 2019-07-13 13:39:11 +02:00
Karoly Albert Szabo
78e634dd5c tm-monitor: add Context to RPC handlers (#3792)
* Fix rpc handle for tm-monitor

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* go imports file

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* go imports file

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>

* fix RPCUnmonitor too

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>
2019-07-13 10:09:27 +04:00
Karoly Albert Szabo
6d96cf4f05 tm-monitor: update build-docker Makefile target (#3790)
- build docker needs go111module on
- switch to golang:1.12 to have git available

Signed-off-by: Karoly Albert Szabo <szabo.karoly.a@gmail.com>
2019-07-13 09:57:43 +04:00
Marko Baricevic
744d65f173 Merge branch 'master' into marko/v0.32.1 2019-07-12 19:30:11 +02:00
Aditya
e7ac73177e upgrade go.mod (#3793)
upgrade levelDB to latest master
2019-07-12 19:29:36 +02:00
Marko Baricevic
1b69c6b56b include doc change for abci/app 2019-07-12 15:59:53 +02:00
Marko Baricevic
e5084a4787 Merge branch 'master' into marko/v0.32.1 2019-07-12 15:51:14 +02:00
Thane Thomson
eddb433d7c abci: Fix documentation regarding CheckTx type update (#3789)
* Update ABCI docs to reflect latest changes on PR #3744

* Add note about new Type parameter for CheckTx
2019-07-12 15:43:03 +02:00
Marko Baricevic
0787b79347 add link bump abci Version 2019-07-12 11:17:20 +02:00
Marko
823d916a11 Apply suggestions from code review
Comment from PR

Co-Authored-By: Ethan Buchman <ethan@coinculture.info>
2019-07-12 11:06:27 +02:00
Andy Nogueira
378a0e51bf docs: replace priv_validator.json with priv_validator_key.json (#3786) 2019-07-11 23:31:42 +04:00
Marko Baricevic
e8926867d8 wording change 2019-07-11 21:22:14 +02:00
Marko Baricevic
0f076e5fbe add links to changelog 2019-07-11 18:37:46 +02:00
Marko Baricevic
ac232caef3 Release branch for v0.32.1
Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>
2019-07-11 18:34:21 +02:00
Marko
e9c9c558d7 libs/common: remove unused functions (#3784)
- The removed functions are not used in Iavl, Cosmos-sdk and tendermint repos
- Code-hygenie `whoop whoop`

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>
2019-07-10 18:40:20 +04:00
Anton Kaliaev
f05c2a9558 p2p: extract ID validation into a separate func (#3754)
* p2p: extract ID validation into a separate func

- NewNetAddress panics if ID is invalid
- NetAddress#Valid returns an error
- remove ErrAddrBookInvalidAddrNoID

Fixes #2722

* p2p: remove repetitive check in ReceiveAddrs

* fix netaddress test
2019-07-10 13:36:48 +04:00
Ashley Vega
d70871f41b testnet: add consensus_params to testnet config generation (#3781)
Also, document time_iota_ms.

Closes #3723

Commits:

* config: Add ConsensusParams when generating testnet config (#3723)

* docs: Add explanation of time_iota_ms (#3723)

* Update changelog_pending (#3723)
2019-07-10 13:27:17 +04:00
Marko
fc1eb46587 libs/common: remove heap.go (#3780)
* Remove file heap.go

- cmn.Heap is not being used in cosmos-sdk, iavl nor tendermint repo.

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>

* changelog entry

* Update CHANGELOG_PENDING.md

closes #2432
2019-07-10 13:06:03 +04:00
Alessio Treglia
ddee2d641f infrastructure for reproducible builds (#3770)
* Add deterministic buildsystem

* Update CircleCI config

* Enable build on all branches for testing purposes

* Revert "Enable build on all branches for testing purposes"

This reverts commit bf5cf66da94bf9c23787b42995b0409492805f03.

* Remove develop from branch filters

* Remove dangling reference to develop

* Upload binaries too

* Build for stable branches too
2019-07-10 12:48:31 +04:00
Marko
f2ada0a604 docs: quick link fixes throughout docs and repo (#3776)
* Quick link fixes throughout docs and repo

- used markdown link tester to find broken links

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>

* minor change remove slash

* pr comments

* minor fix

* remove docker.develop

* remove master tag
2019-07-08 21:54:24 +04:00
Sean Braithwaite
1d5fcc2281 adr: [43] blockchain riri-org (#3753)
* [adr] First draft on adr-042

* fix diagram urls

* Update docs/architecture/adr-042-blockchain-riri-org.md

Co-Authored-By: Marko <marbar3778@yahoo.com>

* Update docs/architecture/adr-042-blockchain-riri-org.md

Co-Authored-By: Marko <marbar3778@yahoo.com>

* Update docs/architecture/adr-042-blockchain-riri-org.md

Co-Authored-By: Marko <marbar3778@yahoo.com>

* add go syntax highlight

* more highlighting

* consistency fixes

* Add references

* new adr number

* Fixes based on feedback

* aditional state info

* Add details on getSchedule

* replace spaces with tabs

* fixes based on feedback

* add clarity around r.msgs

* clarify block processing

* fix off by one error

* additional details on ioRoutine

* Update docs/architecture/adr-043-blockchain-riri-org.md

Co-Authored-By: Anca Zamfir <ancazamfir@users.noreply.github.com>
2019-07-08 21:45:52 +04:00
Jay Namsayin
94e0176ac2 behaviour: return correct reason in MessageOutOfOrder (#3772) 2019-07-04 11:26:13 +04:00
Anton Kaliaev
f76684a05c node: allow registration of custom reactors while creating node (#3771)
* change invocation of NewNode across

* custom reactor name are prefixed with CUSTOM_

* upgate changelog pending

* improve comments

* node: refactor NewNode to use functional options
2019-07-03 17:17:59 +04:00
Anton Kaliaev
e645442c9b abci: minor cleanups in the socket client (#3758)
Follow up from #3512

Specifically:

    cli.conn.Close() need not be under the mutex (#3512 (comment))
    call the reqRes callback after the resCb so they always happen in the same order (#3512)

Fixes #3513
2019-07-02 22:01:29 +04:00
Anton Kaliaev
d041476819 p2p: dial addrs which came from seed instead of calling ensurePeers (#3762)
Calling ensurePeers outside of ensurePeersRoutine can lead to nodes
disconnecting from us due to "sent next PEX request too soon" error.

Solution is to just dial addrs we got from src instead of calling
ensurePeers.

Refs #2093

Fixes #3338
2019-07-02 21:19:58 +04:00
Thane Thomson
62f97a69e9 abci: Refactor CheckTx to notify of recheck (#3744)
As per #2127, this refactors the RequestCheckTx ProtoBuf struct to allow for a flag indicating whether a query is a recheck or not (and allows for possible future, more nuanced states).

In order to pass this extended information through to the ABCI app, the proxy.AppConnMempool (and, for consistency, the proxy.AppConnConsensus) interface seems to need to be refactored along with abcicli.Client.

And, as per this comment, I've made the following modification to the protobuf definition for the RequestCheckTx structure:

enum CheckTxType {
  New = 0;
  Recheck = 1;
}

message RequestCheckTx {
  bytes tx = 1;
  CheckTxType type = 2;
}

* Refactor ABCI CheckTx to notify of recheck

As per #2127, this refactors the `RequestCheckTx` ProtoBuf struct to allow for:

1. a flag indicating whether a query is a recheck or not (and allows for
   possible future, more nuanced states)
2. an `additional_data` bytes array to provide information for those more
   nuanced states.

In order to pass this extended information through to the ABCI app, the
`proxy.AppConnMempool` (and, for consistency, the
`proxy.AppConnConsensus`) interface seems to need to be refactored.

Commits:

* Fix linting issue

* Add CHANGELOG_PENDING entry

* Remove extraneous explicit initialization

* Update ABCI spec doc to include new CheckTx params

* Rename method param for consistency

* Rename CheckTxType enum values and remove additional_data param
2019-07-02 18:14:53 +04:00
Ivan Kushmantsev
d9481e3648 config: make possible to set absolute paths for TLS cert and key (#3765) 2019-07-01 12:48:54 +04:00
Marko
d4cf204087 libs: remove commented and unneeded code (#3757)
- libs/errors: commented out errors.go
- libs/common: unused colors.go
- libs/db: unused debugDB

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>
2019-06-30 11:56:24 +04:00
Ivan Kushmantsev
d069eb62df docs: remove confusing statement from contributing.md (#3764) 2019-06-30 11:46:31 +04:00
Peng Zhong
b2afc65908 docs: add readme image (#3763) 2019-06-29 11:06:44 +04:00
Marko
1014f8c84b docs: update to contributing.md (#3760)
* Update to contributing.md

- Add in section for Draft PR, instead of WIP
- Add make lint as part of the commit process.

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>

* mistake

* minor word change
2019-06-28 19:32:15 +04:00
Ethan Buchman
b73cfe8786 Merge pull request #3756 from tendermint/v0.32
V0.32
2019-06-26 11:27:24 -04:00
Ethan Buchman
747f99fdc1 changelog and version (#3750)
* changelog and version

* Add section on ABCI changes

* Update ABCI upgrade section to include events examples

* update upgrading

* more upgrading and changelog

* update changelog from pending

* refer to #rpc_changes

* minor word changes
2019-06-25 07:57:50 -04:00
Mengjay Gao
eb08609de1 docs: update JS section of abci-cli.md (#3747) 2019-06-25 10:56:03 +04:00
Marko
e93e8e730d Added a disclaimer so the user is aware of binary (#3751)
* Added a disaclaimer so the user is aware of binary

- added disclaimer for `-s -w`

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>

* move up to compile
2019-06-24 14:13:25 -04:00
Marko
4c4bf0f131 Added flags '-s -w' to buildflags (#3742)
* Added flags '-s -w' to buildflags

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>

* added a condition for no strip

* minor changes

* on call discussions

* on call discussion v2
2019-06-24 10:32:24 -04:00
Thane Thomson
d88a639838 Make RPC bind to localhost by default (#3746)
* Make RPC bind to localhost by default

* Add CHANGELOG_PENDING entry

* Allow testnet command to override RPC listen address

* Update localnet test to bind RPC to 0.0.0.0

* Update p2p test to bind RPC to 0.0.0.0

* Remove rpc-laddr parameter

* Update localnet to use config template with RPC listen address override

* Use config template override method for RPC listen address

* Build config template into localnode image

* Build localnode image locally before starting localnet

* Move testnet config overrides into templates

* Revert deletion of config overrides

* Remove extraneous config parameter overrides
2019-06-24 10:32:12 -04:00
Anton Kaliaev
c37faf3ac1 docs: (rpc/broadcast_tx_*) write expectations for a client (#3749)
Refs #3322
2019-06-24 09:49:49 -04:00
Marko
a44c621d2d Remove double lint (#3748)
- Circel CI and Golangci were doing linting jobs, removed circleci

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>
2019-06-24 09:48:21 -04:00
Anton Kaliaev
3e7752c29d cs: exit if SwitchToConsensus fails (#3706)
Refs #3656
2019-06-22 11:48:01 -04:00
Anton Kaliaev
1b77bf6f20 rpc/lib: write a test for TLS server (#3703)
* rpc/lib: write a test for TLS server

Refs #3700

* do not regenerate certificates

* add nolint
2019-06-22 11:44:12 -04:00
Anton Kaliaev
8fc8368438 node: run whole func in goroutine, not just logger.Error fn (#3743)
* node: run whole func in goroutine, not just logger.Error fn

Fixes #3741

* add a changelog entry
2019-06-22 10:30:23 +04:00
Thane Thomson
228bba799d state: add more tests for block validation (#3674)
* Expose priv validators for use in testing

* Generalize block header validation test past height 1

* Remove ineffectual assignment

* Remove redundant SaveState call

* Reorder comment for clarity

* Use the block executor ApplyBlock function instead of implementing a stripped-down version of it

* Remove commented-out code

* Remove unnecessary test

The required tests already appear to be implemented (implicitly) through
the TestValidateBlockHeader test.

* Allow for catching of specific error types during TestValidateBlockCommit

* Make return error testable

* Clean up and add TestValidateBlockCommit code

* Fix formatting

* Extract function to create a new mock test app

* Update comment for clarity

* Fix comment

* Add skeleton code for evidence-related test

* Allow for addressing priv val by address

* Generalize test beyond a single validator

* Generalize TestValidateBlockEvidence past first height

* Reorder code to clearly separate tests and utility code

* Use a common constant for stop height for testing in state/validation_test.go

* Refactor errors to resemble existing conventions

* Fix formatting

* Extract common helper functions

Having the tests littered with helper functions makes them less easily
readable imho, so I've pulled them out into a separate file. This also
makes it easier to see what helper functions are available during
testing, so we minimize the chance of duplication when writing new
tests.

* Remove unused parameter

* Remove unused parameters

* Add field keys

* Remove unused height constant

* Fix typo

* Fix incorrect return error

* Add field keys

* Use separate package for tests

This refactors all of the state package's tests into a state_test
package, so as to keep any usage of the state package's internal methods
explicit.

Any internal methods/constants used by tests are now explicitly exported
in state/export_test.go

* Refactor: extract helper function to make, validate, execute and commit a block

* Rename state function to makeState

* Remove redundant constant for number of validators

* Refactor mock evidence registration into TestMain

* Remove extraneous nVals variable

* Replace function-level TODOs with file-level TODO and explanation

* Remove extraneous comment

* Fix linting issues brought up by GolangCI (pulled in from latest merge from develop)
2019-06-21 17:29:29 -04:00
Ismail Khoffi
59497c362b Prepare nuking develop (#3726)
* Update Readme and contrib. guidelines: remove traces of master

Signed-off-by: Ismail Khoffi <Ismail.Khoffi@gmail.com>

* add simple example

Signed-off-by: Ismail Khoffi <Ismail.Khoffi@gmail.com>

* update contributing.md

* add link

* Update CONTRIBUTING.md

* add a note on master and releases

Signed-off-by: Ismail Khoffi <Ismail.Khoffi@gmail.com>

* update readme
2019-06-21 16:29:16 -04:00
needkane
2e5b2a9537 abci/examples: switch from hex to base64 pubkey in kvstore (#3641)
* abci/example: use base64 for update validator set

* update kvstore/README.md

* update CHANGELOG_PENDING.md and abci/example/kvstore/README.md
2019-06-21 15:18:49 +04:00
Marko
866b343c0c Changes to files that had linting issue (#3731)
- Govet issues fixed
- 1 gosec issue solved using nolint

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>
2019-06-21 09:58:32 +04:00
Thane Thomson
9d5ba576ee abci: Refactor ABCI CheckTx and DeliverTx signatures (#3735)
* Refactor signature of Application.CheckTx

* Refactor signature of Application.DeliverTx

* Refactor example variable names for clarity and consistency

* Rename method variables for consistency

* Rename method variables for consistency

* add a changelog entry

* update docs
2019-06-21 09:56:27 +04:00
Anton Kaliaev
1b5110e91f node: fix a bug where nil is recorded as node's address (#3740)
* node: fix a bug where `nil` is recorded as node's address

Solution

  AddOurAddress when we know it
  sw.NetAddress is nil in createAddrBookAndSetOnSwitch
  it's set by n.transport.Listen function, which is called during start

Fixes #3716

* use addr instead of n.sw.NetAddress

* add both ExternalAddress and ListenAddress as our addresses
2019-06-21 09:30:32 +04:00
Hans Schoenburg
60827f7562 docs: fix some language issues and deprecated link (#3733) 2019-06-19 21:35:53 +03:00
Runchao Han
ed18ffdca3 p2p: refactor Switch#OnStop (#3729) 2019-06-17 13:30:12 +02:00
Andy Nogueira
0e1c492d3e docs: missing 'b' in python command (#3728)
In Python 3 the command outlined in this doc `import codecs; codecs.decode("YWJjZA==", 'base64').decode('ascii')` throws an error:
TypeError: decoding with 'base64' codec failed (TypeError: expected bytes-like object, not str), needs to add 'b' before the encoded string
`import codecs; codecs.decode(b"YWJjZA==", 'base64').decode('ascii')` to make it work
2019-06-17 12:51:12 +02:00
Anton Kaliaev
9010ff5f96 types: do not ignore errors returned by PublishWithEvents (#3722)
Follow up to #3643

* update changelog

* do not ignore errors returned by PublishWithEvents
2019-06-12 15:25:47 +02:00
Alexander Bezobchuk
ab0835463f abci: Refactor tagging events using list of lists (#3643)
## PR

This PR introduces a fundamental breaking change to the structure of ABCI response and tx tags and the way they're processed. Namely, the SDK can support more complex and aggregated events for distribution and slashing. In addition, block responses can include duplicate keys in events.

    Implement new Event type. An event has a type and a list of KV pairs (ie. list-of-lists). Typical events may look like:

"rewards": [{"amount": "5000uatom", "validator": "...", "recipient": "..."}]
"sender": [{"address": "...", "balance": "100uatom"}]

The events are indexed by {even.type}.{even.attribute[i].key}/.... In this case a client would subscribe or query for rewards.recipient='...'

    ABCI response types and related types now include Events []Event instead of Tags []cmn.KVPair.
    PubSub logic now publishes/matches against map[string][]string instead of map[string]string to support duplicate keys in response events (from #1385). A match is successful if the value is found in the slice of strings.

closes: #1859
closes: #2905

## Commits:

* Implement Event ABCI type and updates responses to use events

* Update messages_test.go

* Update kvstore.go

* Update event_bus.go

* Update subscription.go

* Update pubsub.go

* Update kvstore.go

* Update query logic to handle slice of strings in events

* Update Empty#Matches and unit tests

* Update pubsub logic

* Update EventBus#Publish

* Update kv tx indexer

* Update godocs

* Update ResultEvent to use slice of strings; update RPC

* Update more tests

* Update abci.md

* Check for key in validateAndStringifyEvents

* Fix KV indexer to skip empty keys

* Fix linting errors

* Update CHANGELOG_PENDING.md

* Update docs/spec/abci/abci.md

Co-Authored-By: Federico Kunze <31522760+fedekunze@users.noreply.github.com>

* Update abci/types/types.proto

Co-Authored-By: Ethan Buchman <ethan@coinculture.info>

* Update docs/spec/abci/abci.md

Co-Authored-By: Ethan Buchman <ethan@coinculture.info>

* Update libs/pubsub/query/query.go

Co-Authored-By: Ethan Buchman <ethan@coinculture.info>

* Update match function to match if ANY value matches

* Implement TestSubscribeDuplicateKeys

* Update TestMatches to include multi-key test cases

* Update events.go

* Update Query interface godoc

* Update match godoc

* Add godoc for matchValue

* DRY-up tx indexing

* Return error from PublishWithEvents in EventBus#Publish

* Update PublishEventNewBlockHeader to return an error

* Fix build

* Update events doc in ABCI

* Update ABCI events godoc

* Implement TestEventBusPublishEventTxDuplicateKeys

* Update TestSubscribeDuplicateKeys to be table-driven

* Remove mod file

* Remove markdown from events godoc

* Implement TestTxSearchDeprecatedIndexing test
2019-06-12 14:03:45 +02:00
Jack Zampolin
8b7ca8fd99 switch to go mod (#3613)
* Update to using go mod from dep

* Remove references to make get_vendor_deps

* Specify go version

* Set GO111MODULE=on and add -mod=readonly

* Fix exported env

* switch to using go1.12 everywhere

* Fix test scripts

* Typo:

* Prepend GO111MODULE=on

* remove dep cache

* Revert "remove dep cache"

This reverts commit 45117bda

Signed-off-by: Ismail Khoffi <Ismail.Khoffi@gmail.com>

* bring back the dependency cache and change it to cache modules instead
of vendored deps; also:

 - bump version for dependency cache
 - bump version on pkg-cache (includes modules directory)

Signed-off-by: Ismail Khoffi <Ismail.Khoffi@gmail.com>

* remove some more traces of dep:
 - remove Gopkg.(toml | lock)
 - update contributing guidlines
 - set global default in circleci (GO111MODULE=on)

Signed-off-by: Ismail Khoffi <Ismail.Khoffi@gmail.com>

* global var failed for `test_cover` with
`go: unknown environment setting GO111MODULE=true`
although the var was `GO111MODULE: on`

Signed-off-by: Ismail Khoffi <Ismail.Khoffi@gmail.com>

* Changelog pending entry

Signed-off-by: Ismail Khoffi <Ismail.Khoffi@gmail.com>

* Add bbolt dependency to go.mod

Signed-off-by: Ismail Khoffi <Ismail.Khoffi@gmail.com>

* move -mod=readonly to build flags
2019-06-09 16:27:48 +04:00
Ismail Khoffi
ed896f508b Merge pull request #3720 from Yawning/boltdb-fixes
Fix fundemental BoltDB backend correctness issues.
2019-06-07 10:43:32 +02:00
Yawning Angel
319ecb3005 libs/db: Fix the BoltDB Get and Iterator
BoltDB's accessors will return slices that are only valid for the
lifetime of the transaction.  This adds copies where required to prevent
hard to debug crashes (among other things).
2019-06-07 08:26:58 +00:00
Yawning Angel
f46ed4aac8 libs/db: Fix the BoltDB Batch.Delete
`Delete` should queue a delete operation that targets the entire
database, and not just keys that happen to be in the current batch.
2019-06-07 08:26:20 +00:00
Sean Braithwaite
6a7d4182b4 p2p: Peerbehaviour follow up (#3653) (#3663)
* Move peer behaviour into it's own package
* refactor wip
* Adjust API and fix tests
* remove unused test struct
* Better error message
* Restructure:
    + Now behaviour is it's own package, we don't need to include
    PeerBehaviour in every type.
    + Split up behaviours and reporters into seperate files

* doc string fixes
* Fix minor typos
* Update behaviour/reporter.go
Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com>

* Update behaviour/reporter.go
Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com>
2019-06-06 22:33:39 +02:00
Anton Kaliaev
c1f264822a p2p: remove NewNetAddressStringWithOptionalID (#3711)
Fixes #3521

The function NewNetAddressStringWithOptionalID is from a time when peer
IDs were optional. They're not anymore. So this should be renamed to
NewNetAddressString and should ensure the ID is provided.

* update changelog
* use NewNetAddress in transport tests
* use NewNetAddress in TestTransportMultiplexAcceptMultiple
2019-06-05 17:39:28 +02:00
Haochuan Guo
a7e8fbf3a7 rpc: Use Wrap instead of Errorf error (#3686)
So we can have an error code when rpc fails.

* Update CHANGELOG_PENDING.md
Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com>

* wrap correct error
* format code
2019-06-05 11:23:53 +02:00
Sean Braithwaite
c9ef824ddf p2p: Per channel metrics (#3666) (#3677)
* Add `chID` label to sent/receive byte mtrics
* add changelog pending entry
2019-06-05 11:22:00 +02:00
Anton Kaliaev
2a23eca368 docs: update /block_results RPC docs (#3708)
Fixes #3616
2019-06-05 10:36:16 +09:00
Ethan Buchman
c69ec87f67 Merge pull request #3715 from tendermint/v0.31
V0.31
2019-06-03 23:15:23 -04:00
Ethan Buchman
8fb2c2a0e8 Merge pull request #3712 from tendermint/release/v0.31.7
Release/v0.31.7
2019-06-03 23:14:37 -04:00
Anton Kaliaev
96e132b4b0 libs/db: remove deprecated LevelDBBackend const (#3632) 2019-06-04 12:12:43 +09:00
Anton Kaliaev
99c9a35982 docs: update RPC docs for /subscribe & /unsubscribe (#3705)
Fixes #3678

Co-Authored-By: Ethan Buchman <ethan@coinculture.info>
2019-06-04 10:05:22 +09:00
Ethan Buchman
65a3dfe235 changelog and version (#3709) 2019-06-03 11:34:21 -04:00
Ethan Buchman
77e711f70b Merge pull request #3695 from tendermint/release/v0.31.6
Release/v0.31.6
2019-05-30 19:15:48 -04:00
463 changed files with 25690 additions and 16245 deletions

View File

@@ -1,163 +1,126 @@
version: 2
version: 2.1
defaults: &defaults
working_directory: /go/src/github.com/tendermint/tendermint
docker:
- image: circleci/golang
environment:
GOBIN: /tmp/workspace/bin
executors:
golang:
docker:
- image: tendermintdev/docker-tendermint-build
working_directory: /go/src/github.com/tendermint/tendermint
environment:
GOBIN: /tmp/bin
release:
machine: true
docs:
docker:
- image: tendermintdev/jq_curl
environment:
AWS_REGION: us-east-1
docs_update_config: &docs_update_config
working_directory: ~/repo
docker:
- image: tendermintdev/jq_curl
environment:
AWS_REGION: us-east-1
release_management_docker: &release_management_docker
machine: true
commands:
run_test:
parameters:
script_path:
type: string
steps:
- attach_workspace:
at: /tmp/bin
- restore_cache:
name: "Restore source code cache"
keys:
- go-src-v1-{{ .Revision }}
- checkout
- restore_cache:
name: "Restore go modules cache"
keys:
- go-mod-v1-{{ checksum "go.sum" }}
- run:
name: "Running test"
command: |
bash << parameters.script_path >>
jobs:
setup_dependencies:
<<: *defaults
executor: golang
steps:
- run: mkdir -p /tmp/workspace/bin
- run: mkdir -p /tmp/workspace/profiles
- checkout
- restore_cache:
name: "Restore go modules cache"
keys:
- v3-pkg-cache
- go-mod-v1-{{ checksum "go.sum" }}
- run:
command: |
mkdir -p /tmp/bin
- run:
name: Cache go modules
command: make go-mod-cache
- run:
name: tools
command: |
export PATH="$GOBIN:$PATH"
make get_tools
command: make tools
- run:
name: dependencies
command: |
export PATH="$GOBIN:$PATH"
make get_vendor_deps
- run:
name: binaries
command: |
export PATH="$GOBIN:$PATH"
make install install_abci
name: "Build binaries"
command: make install install_abci
- save_cache:
name: "Save go modules cache"
key: go-mod-v1-{{ checksum "go.sum" }}
paths:
- "/go/pkg/mod"
- save_cache:
name: "Save source code cache"
key: go-src-v1-{{ .Revision }}
paths:
- ".git"
- persist_to_workspace:
root: /tmp/workspace
root: "/tmp/bin"
paths:
- bin
- profiles
- save_cache:
key: v3-pkg-cache
paths:
- /go/pkg
- save_cache:
key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
paths:
- /go/src/github.com/tendermint/tendermint
build_slate:
<<: *defaults
steps:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v3-pkg-cache
- restore_cache:
key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
- run:
name: slate docs
command: |
set -ex
export PATH="$GOBIN:$PATH"
make build-slate
lint:
<<: *defaults
steps:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v3-pkg-cache
- restore_cache:
key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
- run:
name: metalinter
command: |
set -ex
export PATH="$GOBIN:$PATH"
make lint
- run:
name: check_dep
command: |
set -ex
export PATH="$GOBIN:$PATH"
make check_dep
- "."
test_abci_apps:
<<: *defaults
executor: golang
steps:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v3-pkg-cache
- restore_cache:
key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
- run:
name: Run abci apps tests
command: |
export PATH="$GOBIN:$PATH"
bash abci/tests/test_app/test.sh
- run_test:
script_path: abci/tests/test_app/test.sh
# if this test fails, fix it and update the docs at:
# https://github.com/tendermint/tendermint/blob/develop/docs/abci-cli.md
# if this test fails, fix it and update the docs at:
# https://github.com/tendermint/tendermint/blob/master/docs/abci-cli.md
test_abci_cli:
<<: *defaults
executor: golang
steps:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v3-pkg-cache
- restore_cache:
key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
- run:
name: Run abci-cli tests
command: |
export PATH="$GOBIN:$PATH"
bash abci/tests/test_cli/test.sh
- run_test:
script_path: abci/tests/test_cli/test.sh
test_apps:
<<: *defaults
executor: golang
steps:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v3-pkg-cache
- restore_cache:
key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
- run: sudo apt-get update && sudo apt-get install -y --no-install-recommends bsdmainutils
- run:
name: Run tests
command: bash test/app/test.sh
- run_test:
script_path: test/app/test.sh
test_persistence:
executor: golang
steps:
- run_test:
script_path: test/persist/test_failure_indices.sh
test_cover:
<<: *defaults
executor: golang
parallelism: 4
steps:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v3-pkg-cache
name: "Restore source code cache"
keys:
- go-src-v1-{{ .Revision }}
- checkout
- restore_cache:
key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
- run: mkdir -p /tmp/logs
name: "Restore go module cache"
keys:
- go-mod-v2-{{ checksum "go.sum" }}
- run:
name: Run tests
name: "Run tests"
command: |
export VERSION="$(git describe --tags --long | sed 's/v\(.*\)/\1/')"
export GO111MODULE=on
mkdir -p /tmp/logs /tmp/workspace/profiles
for pkg in $(go list github.com/tendermint/tendermint/... | circleci tests split --split-by=timings); do
id=$(basename "$pkg")
go test -v -timeout 5m -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg" | tee "/tmp/logs/$id-$RANDOM.log"
go test -v -timeout 5m -mod=readonly -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg" | tee "/tmp/logs/$id-$RANDOM.log"
done
- persist_to_workspace:
root: /tmp/workspace
@@ -166,38 +129,25 @@ jobs:
- store_artifacts:
path: /tmp/logs
test_persistence:
<<: *defaults
steps:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v3-pkg-cache
- restore_cache:
key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
- 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
docker run --rm -v "$PWD":/go/src/github.com/tendermint/tendermint -w /go/src/github.com/tendermint/tendermint golang make build-linux
make localnet-start &
./scripts/localnet-blocks-test.sh 40 5 10 localhost
working_directory: /home/circleci/.go_workspace/src/github.com/tendermint/tendermint
machine:
image: circleci/classic:latest
environment:
GOBIN: /home/circleci/.go_workspace/bin
GOPATH: /home/circleci/.go_workspace/
GOOS: linux
GOARCH: amd64
parallelism: 1
steps:
- checkout
- run:
name: run localnet and exit on failure
command: |
set -x
docker run --rm -v "$PWD":/go/src/github.com/tendermint/tendermint -w /go/src/github.com/tendermint/tendermint golang make build-linux
make localnet-start &
./scripts/localnet-blocks-test.sh 40 5 10 localhost
test_p2p:
environment:
@@ -214,19 +164,22 @@ jobs:
path: /home/circleci/project/test/p2p/logs
upload_coverage:
<<: *defaults
executor: golang
steps:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v3-pkg-cache
name: "Restore source code cache"
keys:
- go-src-v1-{{ .Revision }}
- checkout
- restore_cache:
key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
name: "Restore go module cache"
keys:
- go-mod-v2-{{ checksum "go.sum" }}
- run:
name: gather
command: |
set -ex
echo "mode: atomic" > coverage.txt
for prof in $(ls /tmp/workspace/profiles/); do
tail -n +2 /tmp/workspace/profiles/"$prof" >> coverage.txt
@@ -236,18 +189,22 @@ jobs:
command: bash .circleci/codecov.sh -f coverage.txt
deploy_docs:
<<: *docs_update_config
executor: docs
steps:
- restore_cache:
name: "Restore source code cache"
keys:
- go-src-v1-{{ .Revision }}
- checkout
- run:
name: Trigger website build
command: |
curl --silent \
--show-error \
-X POST \
--header "Content-Type: application/json" \
-d "{\"branch\": \"$CIRCLE_BRANCH\"}" \
"https://circleci.com/api/v1.1/project/github/$CIRCLE_PROJECT_USERNAME/$WEBSITE_REPO_NAME/build?circle-token=$TENDERBOT_API_TOKEN" > response.json
--show-error \
-X POST \
--header "Content-Type: application/json" \
-d "{\"branch\": \"$CIRCLE_BRANCH\"}" \
"https://circleci.com/api/v1.1/project/github/$CIRCLE_PROJECT_USERNAME/$WEBSITE_REPO_NAME/build?circle-token=$TENDERBOT_API_TOKEN" > response.json
RESULT=`jq -r '.status' response.json`
MESSAGE=`jq -r '.message' response.json`
@@ -260,8 +217,12 @@ jobs:
fi
prepare_build:
<<: *defaults
executor: golang
steps:
- restore_cache:
name: "Restore source code cache"
keys:
- go-src-v1-{{ .Revision }}
- checkout
- run:
name: Get next release number
@@ -277,25 +238,29 @@ jobs:
echo "export CIRCLE_TAG=\"${NEXT_TAG}\"" > release-version.source
- run:
name: Build dependencies
command: |
make get_tools get_vendor_deps
command: make tools
- persist_to_workspace:
root: .
paths:
- "release-version.source"
- save_cache:
key: v1-release-deps-{{ .Branch }}-{{ .Revision }}
key: v2-release-deps-{{ checksum "go.sum" }}
paths:
- "vendor"
- "/go/pkg/mod"
build_artifacts:
<<: *defaults
executor: golang
parallelism: 4
steps:
- restore_cache:
name: "Restore source code cache"
keys:
- go-src-v1-{{ .Revision }}
- checkout
- restore_cache:
name: "Restore release dependencies cache"
keys:
- v1-release-deps-{{ .Branch }}-{{ .Revision }}
- v2-release-deps-{{ checksum "go.sum" }}
- attach_workspace:
at: /tmp/workspace
- run:
@@ -314,13 +279,17 @@ jobs:
- "tendermint_linux_amd64"
release_artifacts:
<<: *defaults
executor: golang
steps:
- restore_cache:
name: "Restore source code cache"
keys:
- go-src-v1-{{ .Revision }}
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
name: Deploy to GitHub
name: "Deploy to GitHub"
command: |
# Setting CIRCLE_TAG because we do not tag the release ourselves.
source /tmp/workspace/release-version.source
@@ -342,22 +311,88 @@ jobs:
python -u scripts/release_management/github-publish.py --id "${RELEASE_ID}"
release_docker:
<<: *release_management_docker
machine:
image: ubuntu-1604:201903-01
steps:
- restore_cache:
name: "Restore source code cache"
keys:
- go-src-v1-{{ .Revision }}
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
name: Deploy to Docker Hub
name: "Deploy to Docker Hub"
command: |
# Setting CIRCLE_TAG because we do not tag the release ourselves.
source /tmp/workspace/release-version.source
cp /tmp/workspace/tendermint_linux_amd64 DOCKER/tendermint
docker build --label="tendermint" --tag="tendermint/tendermint:${CIRCLE_TAG}" --tag="tendermint/tendermint:latest" "DOCKER"
docker login -u "${DOCKERHUB_USER}" --password-stdin <<< "${DOCKERHUB_PASS}"
docker login -u "${DOCKERHUB_USER}" --password-stdin \<<< "${DOCKERHUB_PASS}"
docker push "tendermint/tendermint"
docker logout
reproducible_builds:
executor: golang
steps:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
name: "Restore source code cache"
keys:
- go-src-v1-{{ .Revision }}
- checkout
- setup_remote_docker:
docker_layer_caching: true
- run:
name: Build tendermint
no_output_timeout: 20m
command: |
sudo apt-get install -y ruby
bash -x ./scripts/gitian-build.sh all
for os in darwin linux windows; do
cp gitian-build-${os}/result/tendermint-${os}-res.yml .
cp gitian-build-${os}/build/out/tendermint-*.tar.gz .
rm -rf gitian-build-${os}/
done
- store_artifacts:
path: /go/src/github.com/tendermint/tendermint/tendermint-darwin-res.yml
- store_artifacts:
path: /go/src/github.com/tendermint/tendermint/tendermint-linux-res.yml
- store_artifacts:
path: /go/src/github.com/tendermint/tendermint/tendermint-windows-res.yml
- store_artifacts:
path: /go/src/github.com/tendermint/tendermint/tendermint-*.tar.gz
# Test RPC implementation against the swagger documented specs
contract_tests:
working_directory: /home/circleci/.go_workspace/src/github.com/tendermint/tendermint
machine:
image: circleci/classic:latest
environment:
GOBIN: /home/circleci/.go_workspace/bin
GOPATH: /home/circleci/.go_workspace/
GOOS: linux
GOARCH: amd64
parallelism: 1
steps:
- checkout
- run:
name: Test RPC endpoints against swagger documentation
command: |
set -x
export PATH=~/.local/bin:$PATH
# install node and dredd
./scripts/get_nodejs.sh
# build the binaries with a proper version of Go
docker run --rm -v "$PWD":/go/src/github.com/tendermint/tendermint -w /go/src/github.com/tendermint/tendermint golang make build-linux build-contract-tests-hooks
# This docker image works with go 1.7, we can install here the hook handler that contract-tests is going to use
go get github.com/snikch/goodman/cmd/goodman
make contract-tests
workflows:
version: 2
test-suite:
@@ -367,11 +402,7 @@ workflows:
branches:
only:
- master
- develop
- setup_dependencies
- lint:
requires:
- setup_dependencies
- test_abci_apps:
requires:
- setup_dependencies
@@ -394,6 +425,16 @@ workflows:
- upload_coverage:
requires:
- test_cover
- reproducible_builds:
filters:
branches:
only:
- master
- /v[0-9]+\.[0-9]+/
- contract_tests:
requires:
- setup_dependencies
release:
jobs:
- prepare_build

2
.gitignore vendored
View File

@@ -43,3 +43,5 @@ terraform.tfstate.backup
terraform.tfstate.d
.vscode
profile\.out

View File

@@ -6,22 +6,19 @@ linters:
disable:
- gocyclo
- golint
- maligned
- errcheck
- staticcheck
- interfacer
- unconvert
- goconst
- unparam
- nakedret
- lll
- gochecknoglobals
- gocritic
- gochecknoinits
- scopelint
- stylecheck
- funlen
- godox
- whitespace
# linters-settings:
linters-settings:
dogsled:
max-blank-identifiers: 3
# govet:
# check-shadowing: true
# golint:
@@ -32,9 +29,6 @@ linters:
# suggest-new: true
# dupl:
# threshold: 100
# goconst:
# min-len: 2
# min-occurrences: 2
# depguard:
# list-type: blacklist
# packages:

View File

@@ -1,5 +1,280 @@
# Changelog
## v0.32.5
*October 1, 2019*
This release fixes a major security vulnerability found in the `p2p` package.
All clients are recommended to upgrade. See
[\#4030](https://github.com/tendermint/tendermint/issues/4030) for details.
Special thanks to [fudongbai](https://hackerone.com/fudongbai) for discovering
and reporting this issue.
Friendly reminder, we have a [bug bounty
program](https://hackerone.com/tendermint).
### SECURITY:
- [p2p] [\#4030](https://github.com/tendermint/tendermint/issues/4030) Fix for panic on nil public key send to a peer
## v0.32.4
*September 19, 2019*
Special thanks to external contributors on this release: @jon-certik, @gracenoah, @PSalant726, @gchaincl
Friendly reminder, we have a [bug bounty
program](https://hackerone.com/tendermint).
### BREAKING CHANGES:
- CLI/RPC/Config
- [rpc] [\#3984](https://github.com/tendermint/tendermint/issues/3984) Add `MempoolClient` interface to `Client` interface
### IMPROVEMENTS:
- [rpc] [\#2010](https://github.com/tendermint/tendermint/issues/2010) Add NewHTTPWithClient and NewJSONRPCClientWithHTTPClient (note these and NewHTTP, NewJSONRPCClient functions panic if remote is invalid) (@gracenoah)
- [rpc] [\#3882](https://github.com/tendermint/tendermint/issues/3882) Add custom marshalers to proto messages to disable `omitempty`
- [deps] [\#3952](https://github.com/tendermint/tendermint/pull/3952) bump github.com/go-kit/kit from 0.6.0 to 0.9.0
- [deps] [\#3951](https://github.com/tendermint/tendermint/pull/3951) bump github.com/stretchr/testify from 1.3.0 to 1.4.0
- [deps] [\#3945](https://github.com/tendermint/tendermint/pull/3945) bump github.com/gorilla/websocket from 1.2.0 to 1.4.1
- [deps] [\#3948](https://github.com/tendermint/tendermint/pull/3948) bump github.com/libp2p/go-buffer-pool from 0.0.1 to 0.0.2
- [deps] [\#3943](https://github.com/tendermint/tendermint/pull/3943) bump github.com/fortytw2/leaktest from 1.2.0 to 1.3.0
- [deps] [\#3939](https://github.com/tendermint/tendermint/pull/3939) bump github.com/rs/cors from 1.6.0 to 1.7.0
- [deps] [\#3937](https://github.com/tendermint/tendermint/pull/3937) bump github.com/magiconair/properties from 1.8.0 to 1.8.1
- [deps] [\#3947](https://github.com/tendermint/tendermint/pull/3947) update gogo/protobuf version from v1.2.1 to v1.3.0
- [deps] [\#4001](https://github.com/tendermint/tendermint/pull/4001) bump github.com/tendermint/tm-db from 0.1.1 to 0.2.0
### BUG FIXES:
- [consensus] [\#3908](https://github.com/tendermint/tendermint/issues/3908) Wait `timeout_commit` to pass even if `create_empty_blocks` is `false`
- [mempool] [\#3968](https://github.com/tendermint/tendermint/issues/3968) Fix memory loading error on 32-bit machines (@jon-certik)
## v0.32.3
*August 28, 2019*
@climber73 wrote the [Writing a Tendermint Core application in Java
(gRPC)](https://github.com/tendermint/tendermint/blob/master/docs/guides/java.md)
guide.
Special thanks to external contributors on this release:
@gchaincl, @bluele, @climber73
Friendly reminder, we have a [bug bounty
program](https://hackerone.com/tendermint).
### IMPROVEMENTS:
- [consensus] [\#3839](https://github.com/tendermint/tendermint/issues/3839) Reduce "Error attempting to add vote" message severity (Error -> Info)
- [mempool] [\#3877](https://github.com/tendermint/tendermint/pull/3877) Make `max_tx_bytes` configurable instead of `max_msg_bytes` (@bluele)
- [privval] [\#3370](https://github.com/tendermint/tendermint/issues/3370) Refactor and simplify validator/kms connection handling. Please refer to [this comment](https://github.com/tendermint/tendermint/pull/3370#issue-257360971) for details
- [rpc] [\#3880](https://github.com/tendermint/tendermint/issues/3880) Document endpoints with `swagger`, introduce contract tests of implementation against documentation
### BUG FIXES:
- [config] [\#3868](https://github.com/tendermint/tendermint/issues/3868) Move misplaced `max_msg_bytes` into mempool section (@bluele)
- [rpc] [\#3910](https://github.com/tendermint/tendermint/pull/3910) Fix DATA RACE in HTTP client (@gchaincl)
- [store] [\#3893](https://github.com/tendermint/tendermint/issues/3893) Fix "Unregistered interface types.Evidence" panic
## v0.32.2
*July 31, 2019*
Special thanks to external contributors on this release:
@ruseinov, @bluele, @guagualvcha
Friendly reminder, we have a [bug bounty
program](https://hackerone.com/tendermint).
### BREAKING CHANGES:
- Go API
- [libs] [\#3811](https://github.com/tendermint/tendermint/issues/3811) Remove `db` from libs in favor of `https://github.com/tendermint/tm-db`
### FEATURES:
- [blockchain] [\#3561](https://github.com/tendermint/tendermint/issues/3561) Add early version of the new blockchain reactor, which is supposed to be more modular and testable compared to the old version. To try it, you'll have to change `version` in the config file, [here](https://github.com/tendermint/tendermint/blob/master/config/toml.go#L303) NOTE: It's not ready for a production yet. For further information, see [ADR-40](https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-040-blockchain-reactor-refactor.md) & [ADR-43](https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-043-blockchain-riri-org.md)
- [mempool] [\#3826](https://github.com/tendermint/tendermint/issues/3826) Make `max_msg_bytes` configurable(@bluele)
- [node] [\#3846](https://github.com/tendermint/tendermint/pull/3846) Allow replacing existing p2p.Reactor(s) using [`CustomReactors`
option](https://godoc.org/github.com/tendermint/tendermint/node#CustomReactors).
Warning: beware of accidental name clashes. Here is the list of existing
reactors: MEMPOOL, BLOCKCHAIN, CONSENSUS, EVIDENCE, PEX.
- [rpc] [\#3818](https://github.com/tendermint/tendermint/issues/3818) Make `max_body_bytes` and `max_header_bytes` configurable(@bluele)
- [rpc] [\#2252](https://github.com/tendermint/tendermint/issues/2252) Add `/broadcast_evidence` endpoint to submit double signing and other types of evidence
### IMPROVEMENTS:
- [abci] [\#3809](https://github.com/tendermint/tendermint/issues/3809) Recover from application panics in `server/socket_server.go` to allow socket cleanup (@ruseinov)
- [p2p] [\#3664](https://github.com/tendermint/tendermint/issues/3664) p2p/conn: reuse buffer when write/read from secret connection(@guagualvcha)
- [p2p] [\#3834](https://github.com/tendermint/tendermint/issues/3834) Do not write 'Couldn't connect to any seeds' error log if there are no seeds in config file
- [rpc] [\#3076](https://github.com/tendermint/tendermint/issues/3076) Improve transaction search performance
### BUG FIXES:
- [p2p] [\#3644](https://github.com/tendermint/tendermint/issues/3644) Fix error logging for connection stop (@defunctzombie)
- [rpc] [\#3813](https://github.com/tendermint/tendermint/issues/3813) Return err if page is incorrect (less than 0 or greater than total pages)
## v0.32.1
*July 15, 2019*
Special thanks to external contributors on this release:
@ParthDesai, @climber73, @jim380, @ashleyvega
This release contains a minor enhancement to the ABCI and some breaking changes to our libs folder, namely:
- CheckTx requests include a `CheckTxType` enum that can be set to `Recheck` to indicate to the application that this transaction was already checked/validated and certain expensive operations (like checking signatures) can be skipped
- Removed various functions from `libs` pkgs
Friendly reminder, we have a [bug bounty
program](https://hackerone.com/tendermint).
### BREAKING CHANGES:
- Go API
- [abci] [\#2127](https://github.com/tendermint/tendermint/issues/2127) The CheckTx and DeliverTx methods in the ABCI `Application` interface now take structs as arguments (RequestCheckTx and RequestDeliverTx, respectively), instead of just the raw tx bytes. This allows more information to be passed to these methods, for instance, indicating whether a tx has already been checked.
- [libs] Remove unused `db/debugDB` and `common/colors.go` & `errors/errors.go` files (@marbar3778)
- [libs] [\#2432](https://github.com/tendermint/tendermint/issues/2432) Remove unused `common/heap.go` file (@marbar3778)
- [libs] Remove unused `date.go`, `io.go`. Remove `GoPath()`, `Prompt()` and `IsDirEmpty()` functions from `os.go` (@marbar3778)
- [libs] Remove unused `FailRand()` func and minor clean up to `fail.go`(@marbar3778)
### FEATURES:
- [node] Add variadic argument to `NewNode` to support functional options, allowing the Node to be more easily customized.
- [node][\#3730](https://github.com/tendermint/tendermint/pull/3730) Add `CustomReactors` option to `NewNode` allowing caller to pass
custom reactors to run inside Tendermint node (@ParthDesai)
- [abci] [\#2127](https://github.com/tendermint/tendermint/issues/2127)RequestCheckTx has a new field, `CheckTxType`, which can take values of `CheckTxType_New` and `CheckTxType_Recheck`, indicating whether this is a new tx being checked for the first time or whether this tx is being rechecked after a block commit. This allows applications to skip certain expensive operations, like signature checking, if they've already been done once. see [docs](https://github.com/tendermint/tendermint/blob/eddb433d7c082efbeaf8974413a36641519ee895/docs/spec/abci/apps.md#mempool-connection)
### IMPROVEMENTS:
- [rpc] [\#3700](https://github.com/tendermint/tendermint/issues/3700) Make possible to set absolute paths for TLS cert and key (@climber73)
- [abci] [\#3513](https://github.com/tendermint/tendermint/issues/3513) Call the reqRes callback after the resCb so they always happen in the same order
### BUG FIXES:
- [p2p] [\#3338](https://github.com/tendermint/tendermint/issues/3338) Prevent "sent next PEX request too soon" errors by not calling
ensurePeers outside of ensurePeersRoutine
- [behaviour] [\3772](https://github.com/tendermint/tendermint/pull/3772) Return correct reason in MessageOutOfOrder (@jim380)
- [config] [\#3723](https://github.com/tendermint/tendermint/issues/3723) Add consensus_params to testnet config generation; document time_iota_ms (@ashleyvega)
## v0.32.0
*June 25, 2019*
Special thanks to external contributors on this release:
@needkane, @SebastianElvis, @andynog, @Yawning, @wooparadog
This release contains breaking changes to our build and release processes, ABCI,
and the RPC, namely:
- Use Go modules instead of dep
- Bring active development to the `master` Github branch
- ABCI Tags are now Events - see
[docs](https://github.com/tendermint/tendermint/blob/60827f75623b92eff132dc0eff5b49d2025c591e/docs/spec/abci/abci.md#events)
- Bind RPC to localhost by default, not to the public interface [UPGRADING/RPC_Changes](./UPGRADING.md#rpc_changes)
Friendly reminder, we have a [bug bounty
program](https://hackerone.com/tendermint).
### BREAKING CHANGES:
* CLI/RPC/Config
- [cli] [\#3613](https://github.com/tendermint/tendermint/issues/3613) Switch from golang/dep to Go Modules to resolve dependencies:
It is recommended to switch to Go Modules if your project has tendermint as
a dependency. Read more on Modules here:
https://github.com/golang/go/wiki/Modules
- [config] [\#3632](https://github.com/tendermint/tendermint/pull/3632) Removed `leveldb` as generic
option for `db_backend`. Must be `goleveldb` or `cleveldb`.
- [rpc] [\#3616](https://github.com/tendermint/tendermint/issues/3616) Fix field names for `/block_results` response (eg. `results.DeliverTx`
-> `results.deliver_tx`). See docs for details.
- [rpc] [\#3724](https://github.com/tendermint/tendermint/issues/3724) RPC now binds to `127.0.0.1` by default instead of `0.0.0.0`
* Apps
- [abci] [\#1859](https://github.com/tendermint/tendermint/issues/1859) `ResponseCheckTx`, `ResponseDeliverTx`, `ResponseBeginBlock`,
and `ResponseEndBlock` now include `Events` instead of `Tags`. Each `Event`
contains a `type` and a list of `attributes` (list of key-value pairs)
allowing for inclusion of multiple distinct events in each response.
* Go API
- [abci] [\#3193](https://github.com/tendermint/tendermint/issues/3193) Use RequestDeliverTx and RequestCheckTx in the ABCI
Application interface
- [libs/db] [\#3632](https://github.com/tendermint/tendermint/pull/3632) Removed deprecated `LevelDBBackend` const
If you have `db_backend` set to `leveldb` in your config file, please
change it to `goleveldb` or `cleveldb`.
- [p2p] [\#3521](https://github.com/tendermint/tendermint/issues/3521) Remove NewNetAddressStringWithOptionalID
* Blockchain Protocol
* P2P Protocol
### FEATURES:
### IMPROVEMENTS:
- [abci/examples] [\#3659](https://github.com/tendermint/tendermint/issues/3659) Change validator update tx format in the `persistent_kvstore` to use base64 for pubkeys instead of hex (@needkane)
- [consensus] [\#3656](https://github.com/tendermint/tendermint/issues/3656) Exit if SwitchToConsensus fails
- [p2p] [\#3666](https://github.com/tendermint/tendermint/issues/3666) Add per channel telemetry to improve reactor observability
- [rpc] [\#3686](https://github.com/tendermint/tendermint/pull/3686) `HTTPClient#Call` returns wrapped errors, so a caller could use `errors.Cause` to retrieve an error code. (@wooparadog)
### BUG FIXES:
- [libs/db] [\#3717](https://github.com/tendermint/tendermint/issues/3717) Fixed the BoltDB backend's Batch.Delete implementation (@Yawning)
- [libs/db] [\#3718](https://github.com/tendermint/tendermint/issues/3718) Fixed the BoltDB backend's Get and Iterator implementation (@Yawning)
- [node] [\#3716](https://github.com/tendermint/tendermint/issues/3716) Fix a bug where `nil` is recorded as node's address
- [node] [\#3741](https://github.com/tendermint/tendermint/issues/3741) Fix profiler blocking the entire node
## v0.31.9
*October 1, 2019*
This release fixes a major security vulnerability found in the `p2p` package.
All clients are recommended to upgrade. See
[\#4030](https://github.com/tendermint/tendermint/issues/4030) for details.
Special thanks to [fudongbai](https://hackerone.com/fudongbai) for discovering
and reporting this issue.
Friendly reminder, we have a [bug bounty
program](https://hackerone.com/tendermint).
### SECURITY:
- [p2p] [\#4030](https://github.com/tendermint/tendermint/issues/4030) Fix for panic on nil public key send to a peer
### BUG FIXES:
- [node] [\#3716](https://github.com/tendermint/tendermint/issues/3716) Fix a bug where `nil` is recorded as node's address
- [node] [\#3741](https://github.com/tendermint/tendermint/issues/3741) Fix profiler blocking the entire node
## v0.31.8
*July 29, 2019*
This releases fixes one bug in the PEX reactor and adds a `recover` to the Go's
ABCI server, which allows it to properly cleanup.
### IMPROVEMENTS:
- [abci] [\#3809](https://github.com/tendermint/tendermint/issues/3809) Recover from application panics in `server/socket_server.go` to allow socket cleanup (@ruseinov)
### BUG FIXES:
- [p2p] [\#3338](https://github.com/tendermint/tendermint/issues/3338) Prevent "sent next PEX request too soon" errors by not calling
ensurePeers outside of ensurePeersRoutine
## v0.31.7
*June 3, 2019*
This releases fixes a regression in the mempool introduced in v0.31.6.
The regression caused the invalid committed txs to be proposed in blocks over and
over again.
### BUG FIXES:
- [mempool] [\#3699](https://github.com/tendermint/tendermint/issues/3699) Remove all committed txs from the mempool.
This reverts the change from v0.31.6 where we only remove valid txs from the mempool.
Note this means malicious proposals can cause txs to be dropped from the
mempools of other nodes by including them in blocks before they are valid.
See [\#3322](https://github.com/tendermint/tendermint/issues/3322).
## v0.31.6
*May 31st, 2019*

View File

@@ -1,23 +1,28 @@
## v0.31.7
## v0.32.6
**
\*\*
Special thanks to external contributors on this release:
@erikgrinaker
Friendly reminder, we have a [bug bounty
program](https://hackerone.com/tendermint).
### BREAKING CHANGES:
* CLI/RPC/Config
- CLI/RPC/Config
* Apps
- Apps
* Go API
* Blockchain Protocol
* P2P Protocol
- Go API
### FEATURES:
### IMPROVEMENTS:
- [tools] [\#4023](https://github.com/tendermint/tendermint/issues/4023) Improved `tm-monitor` formatting of start time and avg tx throughput (@erikgrinaker)
- [abci] Add full Vote to VoteInfo in BeginBlock (@folex from fluence labs)
### BUG FIXES:
- [mempool] \#3699 Revert the change where we only remove valid transactions
from the mempool once a block is committed.
- [tools] [\#4023](https://github.com/tendermint/tendermint/issues/4023) Refresh `tm-monitor` health when validator count is updated (@erikgrinaker)

View File

@@ -2,7 +2,8 @@
Thank you for considering making contributions to Tendermint and related repositories! Start by taking a look at the [coding repo](https://github.com/tendermint/coding) for overall information on repository workflow and standards.
Please follow standard github best practices: fork the repo, branch from the tip of develop, make some commits, and submit a pull request to develop. See the [open issues](https://github.com/tendermint/tendermint/issues) for things we need help with!
Please follow standard github best practices: fork the repo, branch from the tip of `master`, make some commits, and submit a pull request to `master`.
See the [open issues](https://github.com/tendermint/tendermint/issues) for things we need help with!
Before making a pull request, please open an issue describing the
change you would like to make. If an issue for your change already exists,
@@ -12,22 +13,24 @@ to write a more detailed design document in the form of an
Architectural Decision Record (ie. see [here](./docs/architecture/)) before submitting code
changes.
Please make sure to use `gofmt` before every commit - the easiest way to do this is have your editor run it for you upon saving a file.
Please open a [Draft PR](https://github.blog/2019-02-14-introducing-draft-pull-requests/), even if your contribution is incomplete, this inidicates to the community you're working on something and allows them to provide comments early in the development process. When the code is complete it can be marked as ready-for-review.
Please make sure to use `gofmt` before every commit - the easiest way to do this is have your editor run it for you upon saving a file. Additionally please ensure that your code is lint compliant by running `make lint`
## Forking
Please note that Go requires code to live under absolute paths, which complicates forking.
While my fork lives at `https://github.com/ebuchman/tendermint`,
the code should never exist at `$GOPATH/src/github.com/ebuchman/tendermint`.
the code should never exist at `$GOPATH/src/github.com/ebuchman/tendermint`.
Instead, we use `git remote` to add the fork as a new remote for the original repo,
`$GOPATH/src/github.com/tendermint/tendermint `, and do all the work there.
`$GOPATH/src/github.com/tendermint/tendermint`, and do all the work there.
For instance, to create a fork and work on a branch of it, I would:
* Create the fork on github, using the fork button.
* Go to the original repo checked out locally (i.e. `$GOPATH/src/github.com/tendermint/tendermint`)
* `git remote rename origin upstream`
* `git remote add origin git@github.com:ebuchman/basecoin.git`
- Create the fork on github, using the fork button.
- Go to the original repo checked out locally (i.e. `$GOPATH/src/github.com/tendermint/tendermint`)
- `git remote rename origin upstream`
- `git remote add origin git@github.com:ebuchman/basecoin.git`
Now `origin` refers to my fork and `upstream` refers to the tendermint version.
So I can `git push -u origin master` to update my fork, and make pull requests to tendermint from there.
@@ -35,14 +38,12 @@ Of course, replace `ebuchman` with your git handle.
To pull in updates from the origin repo, run
* `git fetch upstream`
* `git rebase upstream/master` (or whatever branch you want)
Please don't make Pull Requests to `master`.
- `git fetch upstream`
- `git rebase upstream/master` (or whatever branch you want)
## Dependencies
We use [dep](https://github.com/golang/dep) to manage dependencies.
We use [go modules](https://github.com/golang/go/wiki/Modules) to manage dependencies.
That said, the master branch of every Tendermint repository should just build
with `go get`, which means they should be kept up-to-date with their
@@ -50,18 +51,17 @@ dependencies so we can get away with telling people they can just `go get` our
software.
Since some dependencies are not under our control, a third party may break our
build, in which case we can fall back on `dep ensure` (or `make
get_vendor_deps`). Even for dependencies under our control, dep helps us to
build, in which case we can fall back on `go mod tidy`. Even for dependencies under our control, go helps us to
keep multiple repos in sync as they evolve. Anything with an executable, such
as apps, tools, and the core, should use dep.
Run `dep status` to get a list of vendor dependencies that may not be
Run `go list -u -m all` to get a list of dependencies that may not be
up-to-date.
When updating dependencies, please only update the particular dependencies you
need. Instead of running `dep ensure -update`, which will update anything,
need. Instead of running `go get -u=patch`, which will update anything,
specify exactly the dependency you want to update, eg.
`dep ensure -update github.com/tendermint/go-amino`.
`GO111MODULE=on go get -u github.com/tendermint/go-amino@master`.
## Vagrant
@@ -113,49 +113,66 @@ removed from the header in rpc responses as well.
## Branching Model and Release
We follow a variant of [git flow](http://nvie.com/posts/a-successful-git-branching-model/).
This means that all pull-requests should be made against develop. Any merge to
master constitutes a tagged release.
The main development branch is master.
Note all pull requests should be squash merged except for merging to master and
merging master back to develop. This keeps the commit history clean and makes it
Every release is maintained in a release branch named `vX.Y.Z`.
Note all pull requests should be squash merged except for merging to a release branch (named `vX.Y`). This keeps the commit history clean and makes it
easy to reference the pull request where a change was introduced.
### Development Procedure:
- the latest state of development is on `develop`
- `develop` must never fail `make test`
- never --force onto `develop` (except when reverting a broken commit, which should seldom happen)
### Development Procedure
- the latest state of development is on `master`
- `master` must never fail `make test`
- never --force onto `master` (except when reverting a broken commit, which should seldom happen)
- create a development branch either on github.com/tendermint/tendermint, or your fork (using `git remote add origin`)
- make changes and update the `CHANGELOG_PENDING.md` to record your change
- before submitting a pull request, run `git rebase` on top of the latest `develop`
- before submitting a pull request, run `git rebase` on top of the latest `master`
### Pull Merge Procedure:
- ensure pull branch is based on a recent develop
### Pull Merge Procedure
- ensure pull branch is based on a recent `master`
- run `make test` to ensure that all tests pass
- squash merge pull request
- the `unstable` branch may be used to aggregate pull merges before fixing tests
### Release Procedure:
- start on `develop`
- run integration tests (see `test_integrations` in Makefile)
- prepare release in a pull request against develop (to be squash merged):
- copy `CHANGELOG_PENDING.md` to top of `CHANGELOG.md`
- run `python ./scripts/linkify_changelog.py CHANGELOG.md` to add links for
all issues
- run `bash ./scripts/authors.sh` to get a list of authors since the latest
release, and add the github aliases of external contributors to the top of
the changelog. To lookup an alias from an email, try `bash
./scripts/authors.sh <email>`
- reset the `CHANGELOG_PENDING.md`
- bump versions
- push latest develop with prepared release details to release/vX.X.X to run the extended integration tests on the CI
- if necessary, make pull requests against release/vX.X.X and squash merge them
- merge to master (don't squash merge!)
- merge master back to develop (don't squash merge!)
### Release Procedure
### Hotfix Procedure:
#### Major Release
- follow the normal development and release procedure without any differences
1. start on `master`
2. run integration tests (see `test_integrations` in Makefile)
3. prepare release in a pull request against `master` (to be squash merged):
- copy `CHANGELOG_PENDING.md` to top of `CHANGELOG.md`
- run `python ./scripts/linkify_changelog.py CHANGELOG.md` to add links for
all issues
- run `bash ./scripts/authors.sh` to get a list of authors since the latest
release, and add the github aliases of external contributors to the top of
the changelog. To lookup an alias from an email, try `bash ./scripts/authors.sh <email>`
- reset the `CHANGELOG_PENDING.md`
- bump versions
4. push your changes with prepared release details to `vX.X` (this will trigger the release `vX.X.0`)
5. merge back to master (don't squash merge!)
#### Minor Release
If there were no breaking changes and you need to create a release nonetheless,
the procedure is almost exactly like with a new release above.
The only difference is that in the end you create a pull request against the existing `X.X` branch.
The branch name should match the release number you want to create.
Merging this PR will trigger the next release.
For example, if the PR is against an existing 0.34 branch which already contains a v0.34.0 release/tag,
the patch version will be incremented and the created release will be v0.34.1.
#### Backport Release
1. start from the existing release branch you want to backport changes to (e.g. v0.30)
Branch to a release/vX.X.X branch locally (e.g. release/v0.30.7)
2. cherry pick the commit(s) that contain the changes you want to backport (usually these commits are from squash-merged PRs which were already reviewed)
3. steps 2 and 3 from [Major Release](#major-release)
4. push changes to release/vX.X.X branch
5. open a PR against the existing vX.X branch
## Testing
@@ -165,3 +182,16 @@ If they have `.go` files in the root directory, they will be automatically
tested by circle using `go test -v -race ./...`. If not, they will need a
`circle.yml`. Ideally, every repo has a `Makefile` that defines `make test` and
includes its continuous integration status using a badge in the `README.md`.
### RPC Testing
If you contribute to the RPC endpoints it's important to document your changes in the [Swagger file](./docs/spec/rpc/swagger.yaml)
To test your changes you should install `nodejs` and run:
```bash
npm i -g dredd
make build-linux build-contract-tests-hooks
make contract-tests
```
This command will popup a network and check every endpoint against what has been documented

44
DOCKER/Build.Dockerfile Normal file
View File

@@ -0,0 +1,44 @@
FROM golang:alpine as build
COPY . /tendermint
WORKDIR /tendermint
RUN apk add make
RUN make tools
RUN make build
FROM alpine:3.9
LABEL maintainer="hello@tendermint.com"
# Tendermint will be looking for the genesis file in /tendermint/config/genesis.json
# (unless you change `genesis_file` in config.toml). You can put your config.toml and
# private validator file into /tendermint/config.
#
# The /tendermint/data dir is used by tendermint to store state.
ENV TMHOME /tendermint
# OS environment setup
# Set user right away for determinism, create directory for persistence and give our user ownership
# jq and curl used for extracting `pub_key` from private validator while
# deploying tendermint with Kubernetes. It is nice to have bash so the users
# could execute bash commands.
RUN apk update && \
apk upgrade && \
apk --no-cache add curl jq bash && \
addgroup tmuser && \
adduser -S -G tmuser tmuser -h "$TMHOME"
# Run the container with tmuser by default. (UID=100, GID=1000)
USER tmuser
# Expose the data directory as a volume since there's mutable state in there
VOLUME [ $TMHOME ]
WORKDIR $TMHOME
COPY --from=build /tendermint/build/tendermint /usr/bin/tendermint
# p2p and rpc port
EXPOSE 26656 26657
ENTRYPOINT ["/usr/bin/tendermint"]
CMD ["node", "--moniker=`hostname`"]
STOPSIGNAL SIGTERM

View File

@@ -30,10 +30,10 @@ WORKDIR $TMHOME
# p2p and rpc port
EXPOSE 26656 26657
ENTRYPOINT ["/usr/bin/tendermint"]
CMD ["node", "--moniker=`hostname`"]
STOPSIGNAL SIGTERM
ARG BINARY=tendermint
COPY $BINARY /usr/bin/tendermint
ENTRYPOINT ["/usr/bin/tendermint"]

View File

@@ -1,23 +0,0 @@
FROM golang:latest
RUN mkdir -p /go/src/github.com/tendermint/abci
WORKDIR /go/src/github.com/tendermint/abci
COPY Makefile /go/src/github.com/tendermint/abci/
# see make protoc for details on ldconfig
RUN make get_protoc && ldconfig
# killall is used in tests
RUN apt-get update && apt-get install -y \
psmisc \
&& rm -rf /var/lib/apt/lists/*
COPY Gopkg.toml /go/src/github.com/tendermint/abci/
COPY Gopkg.lock /go/src/github.com/tendermint/abci/
RUN make get_tools
# see https://github.com/golang/dep/issues/1312
RUN dep ensure -vendor-only
COPY . /go/src/github.com/tendermint/abci

View File

@@ -0,0 +1,28 @@
FROM amazonlinux:2
RUN yum -y update && \
yum -y install wget
RUN wget http://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm && \
rpm -ivh epel-release-latest-7.noarch.rpm
RUN yum -y groupinstall "Development Tools"
RUN yum -y install leveldb-devel which
ENV GOVERSION=1.12.9
RUN cd /tmp && \
wget https://dl.google.com/go/go${GOVERSION}.linux-amd64.tar.gz && \
tar -C /usr/local -xf go${GOVERSION}.linux-amd64.tar.gz && \
mkdir -p /go/src && \
mkdir -p /go/bin
ENV PATH=$PATH:/usr/local/go/bin:/go/bin
ENV GOBIN=/go/bin
ENV GOPATH=/go/src
RUN mkdir -p /tendermint
WORKDIR /tendermint
CMD ["/usr/bin/make", "build_c"]

View File

@@ -1,35 +0,0 @@
FROM alpine:3.7
ENV DATA_ROOT /tendermint
ENV TMHOME $DATA_ROOT
RUN addgroup tmuser && \
adduser -S -G tmuser tmuser
RUN mkdir -p $DATA_ROOT && \
chown -R tmuser:tmuser $DATA_ROOT
RUN apk add --no-cache bash curl jq
ENV GOPATH /go
ENV PATH "$PATH:/go/bin"
RUN mkdir -p /go/src/github.com/tendermint/tendermint && \
apk add --no-cache go build-base git && \
cd /go/src/github.com/tendermint/tendermint && \
git clone https://github.com/tendermint/tendermint . && \
git checkout develop && \
make get_tools && \
make get_vendor_deps && \
make install && \
cd - && \
rm -rf /go/src/github.com/tendermint/tendermint && \
apk del go build-base git
VOLUME $DATA_ROOT
EXPOSE 26656
EXPOSE 26657
ENTRYPOINT ["tendermint"]
CMD ["node", "--moniker=`hostname`", "--proxy_app=kvstore"]

View File

@@ -1,5 +1,4 @@
FROM golang:1.10.1
FROM golang:latest
# Grab deps (jq, hexdump, xxd, killall)
RUN apt-get update && \
@@ -15,4 +14,3 @@ VOLUME /go
EXPOSE 26656
EXPOSE 26657

View File

@@ -4,13 +4,10 @@ build:
push:
@sh -c "'$(CURDIR)/push.sh'"
build_develop:
docker build -t "tendermint/tendermint:develop" -f Dockerfile.develop .
build_testing:
docker build --tag tendermint/testing -f ./Dockerfile.testing .
push_develop:
docker push "tendermint/tendermint:develop"
build_amazonlinux_buildimage:
docker build -t "tendermint/tendermint:build_c-amazonlinux" -f Dockerfile.build_c-amazonlinux .
.PHONY: build build_develop push push_develop
.PHONY: build push build_testing build_amazonlinux_buildimage

View File

@@ -2,38 +2,27 @@
## Supported tags and respective `Dockerfile` links
- `0.17.1`, `latest` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/208ac32fa266657bd6c304e84ec828aa252bb0b8/DOCKER/Dockerfile)
- `0.15.0` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/170777300ea92dc21a8aec1abc16cb51812513a4/DOCKER/Dockerfile)
- `0.13.0` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/a28b3fff49dce2fb31f90abb2fc693834e0029c2/DOCKER/Dockerfile)
- `0.12.1` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/457c688346b565e90735431619ca3ca597ef9007/DOCKER/Dockerfile)
- `0.12.0` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/70d8afa6e952e24c573ece345560a5971bf2cc0e/DOCKER/Dockerfile)
- `0.11.0` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/9177cc1f64ca88a4a0243c5d1773d10fba67e201/DOCKER/Dockerfile)
- `0.10.0` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/e5342f4054ab784b2cd6150e14f01053d7c8deb2/DOCKER/Dockerfile)
- `0.9.1`, `0.9`, [(Dockerfile)](https://github.com/tendermint/tendermint/blob/809e0e8c5933604ba8b2d096803ada7c5ec4dfd3/DOCKER/Dockerfile)
- `0.9.0` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/d474baeeea6c22b289e7402449572f7c89ee21da/DOCKER/Dockerfile)
- `0.8.0`, `0.8` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/bf64dd21fdb193e54d8addaaaa2ecf7ac371de8c/DOCKER/Dockerfile)
- `develop` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/master/DOCKER/Dockerfile.develop)
DockerHub tags for official releases are [here](https://hub.docker.com/r/tendermint/tendermint/tags/). The "latest" tag will always point to the highest version number.
`develop` tag points to the [develop](https://github.com/tendermint/tendermint/tree/develop) branch.
Official releases can be found [here](https://github.com/tendermint/tendermint/releases).
The Dockerfile for tendermint is not expected to change in the near future. The master file used for all builds can be found [here](https://raw.githubusercontent.com/tendermint/tendermint/master/DOCKER/Dockerfile).
Respective versioned files can be found https://raw.githubusercontent.com/tendermint/tendermint/vX.XX.XX/DOCKER/Dockerfile (replace the Xs with the version number).
## Quick reference
* **Where to get help:**
https://cosmos.network/community
* **Where to file issues:**
https://github.com/tendermint/tendermint/issues
* **Supported Docker versions:**
[the latest release](https://github.com/moby/moby/releases) (down to 1.6 on a best-effort basis)
- **Where to get help:** https://tendermint.com/
- **Where to file issues:** https://github.com/tendermint/tendermint/issues
- **Supported Docker versions:** [the latest release](https://github.com/moby/moby/releases) (down to 1.6 on a best-effort basis)
## Tendermint
Tendermint Core is Byzantine Fault Tolerant (BFT) middleware that takes a state transition machine, written in any programming language, and securely replicates it on many machines.
For more background, see the [introduction](https://tendermint.readthedocs.io/en/master/introduction.html).
For more background, see the [the docs](https://tendermint.com/docs/introduction/#quick-start).
To get started developing applications, see the [application developers guide](https://tendermint.readthedocs.io/en/master/getting-started.html).
To get started developing applications, see the [application developers guide](https://tendermint.com/docs/introduction/quick-start.html).
## How to use this image
@@ -48,7 +37,7 @@ docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint node --proxy_app
## Local cluster
To run a 4-node network, see the `Makefile` in the root of [the repo](https://github.com/tendermint/tendermint/master/Makefile) and run:
To run a 4-node network, see the `Makefile` in the root of [the repo](https://github.com/tendermint/tendermint/blob/master/Makefile) and run:
```
make build-linux
@@ -60,7 +49,7 @@ Note that this will build and use a different image than the ones provided here.
## License
- Tendermint's license is [Apache 2.0](https://github.com/tendermint/tendermint/master/LICENSE).
- Tendermint's license is [Apache 2.0](https://github.com/tendermint/tendermint/blob/master/LICENSE).
## Contributing

561
Gopkg.lock generated
View File

@@ -1,561 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "master"
digest = "1:d6afaeed1502aa28e80a4ed0981d570ad91b2579193404256ce672ed0a609e0d"
name = "github.com/beorn7/perks"
packages = ["quantile"]
pruneopts = "UT"
revision = "3a771d992973f24aa725d07868b467d1ddfceafb"
[[projects]]
digest = "1:093bf93a65962e8191e3e8cd8fc6c363f83d43caca9739c906531ba7210a9904"
name = "github.com/btcsuite/btcd"
packages = ["btcec"]
pruneopts = "UT"
revision = "ed77733ec07dfc8a513741138419b8d9d3de9d2d"
[[projects]]
digest = "1:1d8e1cb71c33a9470bbbae09bfec09db43c6bf358dfcae13cd8807c4e2a9a2bf"
name = "github.com/btcsuite/btcutil"
packages = [
"base58",
"bech32",
]
pruneopts = "UT"
revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4"
[[projects]]
digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec"
name = "github.com/davecgh/go-spew"
packages = ["spew"]
pruneopts = "UT"
revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
version = "v1.1.1"
[[projects]]
digest = "1:5f7414cf41466d4b4dd7ec52b2cd3e481e08cfd11e7e24fef730c0e483e88bb1"
name = "github.com/etcd-io/bbolt"
packages = ["."]
pruneopts = "UT"
revision = "63597a96ec0ad9e6d43c3fc81e809909e0237461"
version = "v1.3.2"
[[projects]]
digest = "1:544229a3ca0fb2dd5ebc2896d3d2ff7ce096d9751635301e44e37e761349ee70"
name = "github.com/fortytw2/leaktest"
packages = ["."]
pruneopts = "UT"
revision = "a5ef70473c97b71626b9abeda80ee92ba2a7de9e"
version = "v1.2.0"
[[projects]]
digest = "1:abeb38ade3f32a92943e5be54f55ed6d6e3b6602761d74b4aab4c9dd45c18abd"
name = "github.com/fsnotify/fsnotify"
packages = ["."]
pruneopts = "UT"
revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9"
version = "v1.4.7"
[[projects]]
digest = "1:fdf5169073fb0ad6dc12a70c249145e30f4058647bea25f0abd48b6d9f228a11"
name = "github.com/go-kit/kit"
packages = [
"log",
"log/level",
"log/term",
"metrics",
"metrics/discard",
"metrics/internal/lv",
"metrics/prometheus",
]
pruneopts = "UT"
revision = "4dc7be5d2d12881735283bcab7352178e190fc71"
version = "v0.6.0"
[[projects]]
digest = "1:31a18dae27a29aa074515e43a443abfd2ba6deb6d69309d8d7ce789c45f34659"
name = "github.com/go-logfmt/logfmt"
packages = ["."]
pruneopts = "UT"
revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5"
version = "v0.3.0"
[[projects]]
digest = "1:586ea76dbd0374d6fb649a91d70d652b7fe0ccffb8910a77468e7702e7901f3d"
name = "github.com/go-stack/stack"
packages = ["."]
pruneopts = "UT"
revision = "2fee6af1a9795aafbe0253a0cfbdf668e1fb8a9a"
version = "v1.8.0"
[[projects]]
digest = "1:95e1006e41c641abd2f365dfa0f1213c04da294e7cd5f0bf983af234b775db64"
name = "github.com/gogo/protobuf"
packages = [
"gogoproto",
"jsonpb",
"proto",
"protoc-gen-gogo/descriptor",
"sortkeys",
"types",
]
pruneopts = "UT"
revision = "ba06b47c162d49f2af050fb4c75bcbc86a159d5c"
version = "v1.2.1"
[[projects]]
digest = "1:239c4c7fd2159585454003d9be7207167970194216193a8a210b8d29576f19c9"
name = "github.com/golang/protobuf"
packages = [
"proto",
"ptypes",
"ptypes/any",
"ptypes/duration",
"ptypes/timestamp",
]
pruneopts = "UT"
revision = "c823c79ea1570fb5ff454033735a8e68575d1d0f"
version = "v1.3.0"
[[projects]]
branch = "master"
digest = "1:4a0c6bb4805508a6287675fac876be2ac1182539ca8a32468d8128882e9d5009"
name = "github.com/golang/snappy"
packages = ["."]
pruneopts = "UT"
revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a"
[[projects]]
digest = "1:43dd08a10854b2056e615d1b1d22ac94559d822e1f8b6fcc92c1a1057e85188e"
name = "github.com/gorilla/websocket"
packages = ["."]
pruneopts = "UT"
revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b"
version = "v1.2.0"
[[projects]]
digest = "1:ea40c24cdbacd054a6ae9de03e62c5f252479b96c716375aace5c120d68647c8"
name = "github.com/hashicorp/hcl"
packages = [
".",
"hcl/ast",
"hcl/parser",
"hcl/scanner",
"hcl/strconv",
"hcl/token",
"json/parser",
"json/scanner",
"json/token",
]
pruneopts = "UT"
revision = "8cb6e5b959231cc1119e43259c4a608f9c51a241"
version = "v1.0.0"
[[projects]]
digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be"
name = "github.com/inconshreveable/mousetrap"
packages = ["."]
pruneopts = "UT"
revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
version = "v1.0"
[[projects]]
digest = "1:a74b5a8e34ee5843cd6e65f698f3e75614f812ff170c2243425d75bc091e9af2"
name = "github.com/jmhodges/levigo"
packages = ["."]
pruneopts = "UT"
revision = "853d788c5c416eaaee5b044570784a96c7a26975"
version = "v1.0.0"
[[projects]]
branch = "master"
digest = "1:a64e323dc06b73892e5bb5d040ced475c4645d456038333883f58934abbf6f72"
name = "github.com/kr/logfmt"
packages = ["."]
pruneopts = "UT"
revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0"
[[projects]]
digest = "1:53e8c5c79716437e601696140e8b1801aae4204f4ec54a504333702a49572c4f"
name = "github.com/magiconair/properties"
packages = [
".",
"assert",
]
pruneopts = "UT"
revision = "c2353362d570a7bfa228149c62842019201cfb71"
version = "v1.8.0"
[[projects]]
digest = "1:ff5ebae34cfbf047d505ee150de27e60570e8c394b3b8fdbb720ff6ac71985fc"
name = "github.com/matttproud/golang_protobuf_extensions"
packages = ["pbutil"]
pruneopts = "UT"
revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c"
version = "v1.0.1"
[[projects]]
digest = "1:53bc4cd4914cd7cd52139990d5170d6dc99067ae31c56530621b18b35fc30318"
name = "github.com/mitchellh/mapstructure"
packages = ["."]
pruneopts = "UT"
revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe"
version = "v1.1.2"
[[projects]]
digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e"
name = "github.com/pelletier/go-toml"
packages = ["."]
pruneopts = "UT"
revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194"
version = "v1.2.0"
[[projects]]
digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747"
name = "github.com/pkg/errors"
packages = ["."]
pruneopts = "UT"
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
version = "v0.8.0"
[[projects]]
digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe"
name = "github.com/pmezard/go-difflib"
packages = ["difflib"]
pruneopts = "UT"
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0"
[[projects]]
digest = "1:26663fafdea73a38075b07e8e9d82fc0056379d2be8bb4e13899e8fda7c7dd23"
name = "github.com/prometheus/client_golang"
packages = [
"prometheus",
"prometheus/internal",
"prometheus/promhttp",
]
pruneopts = "UT"
revision = "abad2d1bd44235a26707c172eab6bca5bf2dbad3"
version = "v0.9.1"
[[projects]]
branch = "master"
digest = "1:2d5cd61daa5565187e1d96bae64dbbc6080dacf741448e9629c64fd93203b0d4"
name = "github.com/prometheus/client_model"
packages = ["go"]
pruneopts = "UT"
revision = "5c3871d89910bfb32f5fcab2aa4b9ec68e65a99f"
[[projects]]
branch = "master"
digest = "1:db712fde5d12d6cdbdf14b777f0c230f4ff5ab0be8e35b239fc319953ed577a4"
name = "github.com/prometheus/common"
packages = [
"expfmt",
"internal/bitbucket.org/ww/goautoneg",
"model",
]
pruneopts = "UT"
revision = "7e9e6cabbd393fc208072eedef99188d0ce788b6"
[[projects]]
branch = "master"
digest = "1:ef74914912f99c79434d9c09658274678bc85080ebe3ab32bec3940ebce5e1fc"
name = "github.com/prometheus/procfs"
packages = [
".",
"internal/util",
"nfs",
"xfs",
]
pruneopts = "UT"
revision = "185b4288413d2a0dd0806f78c90dde719829e5ae"
[[projects]]
digest = "1:c4556a44e350b50a490544d9b06e9fba9c286c21d6c0e47f54f3a9214597298c"
name = "github.com/rcrowley/go-metrics"
packages = ["."]
pruneopts = "UT"
revision = "e2704e165165ec55d062f5919b4b29494e9fa790"
[[projects]]
digest = "1:b0c25f00bad20d783d259af2af8666969e2fc343fa0dc9efe52936bbd67fb758"
name = "github.com/rs/cors"
packages = ["."]
pruneopts = "UT"
revision = "9a47f48565a795472d43519dd49aac781f3034fb"
version = "v1.6.0"
[[projects]]
digest = "1:6a4a11ba764a56d2758899ec6f3848d24698d48442ebce85ee7a3f63284526cd"
name = "github.com/spf13/afero"
packages = [
".",
"mem",
]
pruneopts = "UT"
revision = "d40851caa0d747393da1ffb28f7f9d8b4eeffebd"
version = "v1.1.2"
[[projects]]
digest = "1:08d65904057412fc0270fc4812a1c90c594186819243160dc779a402d4b6d0bc"
name = "github.com/spf13/cast"
packages = ["."]
pruneopts = "UT"
revision = "8c9545af88b134710ab1cd196795e7f2388358d7"
version = "v1.3.0"
[[projects]]
digest = "1:7ffc0983035bc7e297da3688d9fe19d60a420e9c38bef23f845c53788ed6a05e"
name = "github.com/spf13/cobra"
packages = ["."]
pruneopts = "UT"
revision = "7b2c5ac9fc04fc5efafb60700713d4fa609b777b"
version = "v0.0.1"
[[projects]]
digest = "1:68ea4e23713989dc20b1bded5d9da2c5f9be14ff9885beef481848edd18c26cb"
name = "github.com/spf13/jwalterweatherman"
packages = ["."]
pruneopts = "UT"
revision = "4a4406e478ca629068e7768fc33f3f044173c0a6"
version = "v1.0.0"
[[projects]]
digest = "1:c1b1102241e7f645bc8e0c22ae352e8f0dc6484b6cb4d132fa9f24174e0119e2"
name = "github.com/spf13/pflag"
packages = ["."]
pruneopts = "UT"
revision = "298182f68c66c05229eb03ac171abe6e309ee79a"
version = "v1.0.3"
[[projects]]
digest = "1:f8e1a678a2571e265f4bf91a3e5e32aa6b1474a55cb0ea849750cc177b664d96"
name = "github.com/spf13/viper"
packages = ["."]
pruneopts = "UT"
revision = "25b30aa063fc18e48662b86996252eabdcf2f0c7"
version = "v1.0.0"
[[projects]]
digest = "1:7e8d267900c7fa7f35129a2a37596e38ed0f11ca746d6d9ba727980ee138f9f6"
name = "github.com/stretchr/testify"
packages = [
"assert",
"require",
]
pruneopts = "UT"
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
version = "v1.2.1"
[[projects]]
branch = "master"
digest = "1:59483b8e8183f10ab21a85ba1f4cbb4a2335d48891801f79ed7b9499f44d383c"
name = "github.com/syndtr/goleveldb"
packages = [
"leveldb",
"leveldb/cache",
"leveldb/comparer",
"leveldb/errors",
"leveldb/filter",
"leveldb/iterator",
"leveldb/journal",
"leveldb/memdb",
"leveldb/opt",
"leveldb/storage",
"leveldb/table",
"leveldb/util",
]
pruneopts = "UT"
revision = "6b91fda63f2e36186f1c9d0e48578defb69c5d43"
[[projects]]
digest = "1:ad9c4c1a4e7875330b1f62906f2830f043a23edb5db997e3a5ac5d3e6eadf80a"
name = "github.com/tendermint/go-amino"
packages = ["."]
pruneopts = "UT"
revision = "dc14acf9ef15f85828bfbc561ed9dd9d2a284885"
version = "v0.14.1"
[[projects]]
branch = "master"
digest = "1:f4edb30d5ff238e2abba10457010f74cd55ae20bbda8c54db1a07155fa020490"
name = "golang.org/x/crypto"
packages = [
"bcrypt",
"blowfish",
"chacha20poly1305",
"curve25519",
"ed25519",
"ed25519/internal/edwards25519",
"hkdf",
"internal/chacha20",
"internal/subtle",
"nacl/box",
"nacl/secretbox",
"openpgp/armor",
"openpgp/errors",
"poly1305",
"ripemd160",
"salsa20/salsa",
]
pruneopts = "UT"
revision = "8dd112bcdc25174059e45e07517d9fc663123347"
[[projects]]
digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1"
name = "golang.org/x/net"
packages = [
"context",
"http/httpguts",
"http2",
"http2/hpack",
"idna",
"internal/timeseries",
"netutil",
"trace",
]
pruneopts = "UT"
revision = "292b43bbf7cb8d35ddf40f8d5100ef3837cced3f"
[[projects]]
branch = "master"
digest = "1:6f86e2f2e2217cd4d74dec6786163cf80e4d2b99adb341ecc60a45113b844dca"
name = "golang.org/x/sys"
packages = [
"cpu",
"unix",
]
pruneopts = "UT"
revision = "7e31e0c00fa05cb5fbf4347b585621d6709e19a4"
[[projects]]
digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18"
name = "golang.org/x/text"
packages = [
"collate",
"collate/build",
"internal/colltab",
"internal/gen",
"internal/tag",
"internal/triegen",
"internal/ucd",
"language",
"secure/bidirule",
"transform",
"unicode/bidi",
"unicode/cldr",
"unicode/norm",
"unicode/rangetable",
]
pruneopts = "UT"
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
version = "v0.3.0"
[[projects]]
branch = "master"
digest = "1:56b0bca90b7e5d1facf5fbdacba23e4e0ce069d25381b8e2f70ef1e7ebfb9c1a"
name = "google.golang.org/genproto"
packages = ["googleapis/rpc/status"]
pruneopts = "UT"
revision = "b69ba1387ce2108ac9bc8e8e5e5a46e7d5c72313"
[[projects]]
digest = "1:2dab32a43451e320e49608ff4542fdfc653c95dcc35d0065ec9c6c3dd540ed74"
name = "google.golang.org/grpc"
packages = [
".",
"balancer",
"balancer/base",
"balancer/roundrobin",
"codes",
"connectivity",
"credentials",
"encoding",
"encoding/proto",
"grpclog",
"internal",
"internal/backoff",
"internal/channelz",
"internal/grpcrand",
"keepalive",
"metadata",
"naming",
"peer",
"resolver",
"resolver/dns",
"resolver/passthrough",
"stats",
"status",
"tap",
"transport",
]
pruneopts = "UT"
revision = "168a6198bcb0ef175f7dacec0b8691fc141dc9b8"
version = "v1.13.0"
[[projects]]
digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202"
name = "gopkg.in/yaml.v2"
packages = ["."]
pruneopts = "UT"
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
version = "v2.2.1"
[solve-meta]
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/etcd-io/bbolt",
"github.com/fortytw2/leaktest",
"github.com/go-kit/kit/log",
"github.com/go-kit/kit/log/level",
"github.com/go-kit/kit/log/term",
"github.com/go-kit/kit/metrics",
"github.com/go-kit/kit/metrics/discard",
"github.com/go-kit/kit/metrics/prometheus",
"github.com/go-logfmt/logfmt",
"github.com/gogo/protobuf/gogoproto",
"github.com/gogo/protobuf/jsonpb",
"github.com/gogo/protobuf/proto",
"github.com/gogo/protobuf/types",
"github.com/golang/protobuf/proto",
"github.com/golang/protobuf/ptypes/timestamp",
"github.com/gorilla/websocket",
"github.com/jmhodges/levigo",
"github.com/magiconair/properties/assert",
"github.com/pkg/errors",
"github.com/prometheus/client_golang/prometheus",
"github.com/prometheus/client_golang/prometheus/promhttp",
"github.com/rcrowley/go-metrics",
"github.com/rs/cors",
"github.com/spf13/cobra",
"github.com/spf13/viper",
"github.com/stretchr/testify/assert",
"github.com/stretchr/testify/require",
"github.com/syndtr/goleveldb/leveldb",
"github.com/syndtr/goleveldb/leveldb/errors",
"github.com/syndtr/goleveldb/leveldb/iterator",
"github.com/syndtr/goleveldb/leveldb/opt",
"github.com/tendermint/go-amino",
"golang.org/x/crypto/bcrypt",
"golang.org/x/crypto/chacha20poly1305",
"golang.org/x/crypto/curve25519",
"golang.org/x/crypto/ed25519",
"golang.org/x/crypto/hkdf",
"golang.org/x/crypto/nacl/box",
"golang.org/x/crypto/nacl/secretbox",
"golang.org/x/crypto/openpgp/armor",
"golang.org/x/crypto/ripemd160",
"golang.org/x/net/context",
"golang.org/x/net/netutil",
"google.golang.org/grpc",
"google.golang.org/grpc/credentials",
]
solver-name = "gps-cdcl"
solver-version = 1

View File

@@ -1,97 +0,0 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true
#
###########################################################
# Allow only patch releases for serialization libraries
[[constraint]]
name = "github.com/etcd-io/bbolt"
version = "v1.3.2"
[[constraint]]
name = "github.com/tendermint/go-amino"
version = "~0.14.1"
[[constraint]]
name = "github.com/gogo/protobuf"
version = "~1.2.1"
[[constraint]]
name = "github.com/golang/protobuf"
version = "~1.3.0"
# Allow only minor releases for other libraries
[[constraint]]
name = "github.com/go-kit/kit"
version = "^0.6.0"
[[constraint]]
name = "github.com/gorilla/websocket"
version = "^1.2.0"
[[constraint]]
name = "github.com/rs/cors"
version = "^1.6.0"
[[constraint]]
name = "github.com/pkg/errors"
version = "^0.8.0"
[[constraint]]
name = "github.com/spf13/cobra"
version = "^0.0.1"
[[constraint]]
name = "github.com/spf13/viper"
version = "^1.0.0"
[[constraint]]
name = "github.com/stretchr/testify"
version = "^1.2.1"
[[constraint]]
name = "google.golang.org/grpc"
version = "^1.13.0"
[[constraint]]
name = "github.com/fortytw2/leaktest"
version = "^1.2.0"
[[constraint]]
name = "github.com/prometheus/client_golang"
version = "^0.9.1"
[[constraint]]
name = "github.com/jmhodges/levigo"
version = "^1.0.0"
###################################
## Repos which don't have releases.
## - github.com/btcsuite/btcd
## - golang.org/x/crypto
## - github.com/btcsuite/btcutil
## - github.com/rcrowley/go-metrics
## - golang.org/x/net
[prune]
go-tests = true
unused-packages = true

218
Makefile
View File

@@ -1,6 +1,5 @@
GOTOOLS = \
github.com/mitchellh/gox \
github.com/golang/dep/cmd/dep \
github.com/golangci/golangci-lint/cmd/golangci-lint \
github.com/gogo/protobuf/protoc-gen-gogo \
github.com/square/certstrap
@@ -10,11 +9,14 @@ OUTPUT?=build/tendermint
INCLUDE = -I=. -I=${GOPATH}/src -I=${GOPATH}/src/github.com/gogo/protobuf/protobuf
BUILD_TAGS?='tendermint'
BUILD_FLAGS = -ldflags "-X github.com/tendermint/tendermint/version.GitCommit=`git rev-parse --short=8 HEAD`"
LD_FLAGS = -X github.com/tendermint/tendermint/version.GitCommit=`git rev-parse --short=8 HEAD` -s -w
BUILD_FLAGS = -mod=readonly -ldflags "$(LD_FLAGS)"
all: check build test install
check: check_tools get_vendor_deps
# The below include contains the tools.
include tools.mk
include tests.mk
########################################
### Build Tendermint
@@ -26,13 +28,13 @@ build_c:
CGO_ENABLED=1 go build $(BUILD_FLAGS) -tags "$(BUILD_TAGS) cleveldb" -o $(OUTPUT) ./cmd/tendermint/
build_race:
CGO_ENABLED=0 go build -race $(BUILD_FLAGS) -tags $(BUILD_TAGS) -o $(OUTPUT) ./cmd/tendermint
CGO_ENABLED=1 go build -race $(BUILD_FLAGS) -tags $(BUILD_TAGS) -o $(OUTPUT) ./cmd/tendermint
install:
CGO_ENABLED=0 go install $(BUILD_FLAGS) -tags $(BUILD_TAGS) ./cmd/tendermint
CGO_ENABLED=0 go install $(BUILD_FLAGS) -tags $(BUILD_TAGS) ./cmd/tendermint
install_c:
CGO_ENABLED=1 go install $(BUILD_FLAGS) -tags "$(BUILD_TAGS) cleveldb" ./cmd/tendermint
CGO_ENABLED=1 go install $(BUILD_FLAGS) -tags "$(BUILD_TAGS) cleveldb" ./cmd/tendermint
########################################
### Protobuf
@@ -56,10 +58,10 @@ protoc_abci: abci/types/types.pb.go
protoc_proto3types: types/proto3/block.pb.go
build_abci:
@go build -i ./abci/cmd/...
@go build -mod=readonly -i ./abci/cmd/...
install_abci:
@go install ./abci/cmd/...
@go install -mod=readonly ./abci/cmd/...
########################################
### Distribution
@@ -69,27 +71,6 @@ install_abci:
dist:
@BUILD_TAGS=$(BUILD_TAGS) sh -c "'$(CURDIR)/scripts/dist.sh'"
########################################
### Tools & dependencies
check_tools:
@# https://stackoverflow.com/a/25668869
@echo "Found tools: $(foreach tool,$(notdir $(GOTOOLS)),\
$(if $(shell which $(tool)),$(tool),$(error "No $(tool) in PATH")))"
get_tools:
@echo "--> Installing tools"
./scripts/get_tools.sh
update_tools:
@echo "--> Updating tools"
./scripts/get_tools.sh
#Update dependencies
get_vendor_deps:
@echo "--> Running dep"
@dep ensure
#For ABCI and libs
get_protoc:
@# https://github.com/google/protobuf/releases
@@ -103,6 +84,16 @@ get_protoc:
cd .. && \
rm -rf protobuf-3.6.1
go-mod-cache: go.sum
@echo "--> Download go modules to local cache"
@go mod download
.PHONY: go-mod-cache
go.sum: go.mod
@echo "--> Ensure dependencies have not been modified"
@go mod verify
@go mod tidy
draw_deps:
@# requires brew install graphviz or apt-get install graphviz
go get github.com/RobotsAndPencils/goviz
@@ -119,124 +110,36 @@ get_deps_bin_size:
protoc_libs: libs/common/types.pb.go
# generates certificates for TLS testing in remotedb and RPC server
gen_certs: clean_certs
## Generating certificates for TLS testing...
certstrap init --common-name "tendermint.com" --passphrase ""
certstrap request-cert -ip "::" --passphrase ""
certstrap sign "::" --CA "tendermint.com" --passphrase ""
mv out/::.crt out/::.key db/remotedb
clean_certs:
## Cleaning TLS testing certificates...
certstrap request-cert --common-name "remotedb" -ip "127.0.0.1" --passphrase ""
certstrap sign "remotedb" --CA "tendermint.com" --passphrase ""
mv out/remotedb.crt libs/db/remotedb/test.crt
mv out/remotedb.key libs/db/remotedb/test.key
certstrap request-cert --common-name "server" -ip "127.0.0.1" --passphrase ""
certstrap sign "server" --CA "tendermint.com" --passphrase ""
mv out/server.crt rpc/lib/server/test.crt
mv out/server.key rpc/lib/server/test.key
rm -rf out
rm -f db/remotedb/::.crt db/remotedb/::.key
test_libs: gen_certs
# deletes generated certificates
clean_certs:
rm -f libs/db/remotedb/test.crt
rm -f libs/db/remotedb/test.key
rm -f rpc/lib/server/test.crt
rm -f rpc/lib/server/test.key
test_libs:
go test -tags clevedb boltdb $(PACKAGES)
make clean_certs
grpc_dbserver:
protoc -I db/remotedb/proto/ db/remotedb/proto/defs.proto --go_out=plugins=grpc:db/remotedb/proto
protoc -I libs/db/remotedb/proto/ libs/db/remotedb/proto/defs.proto --go_out=plugins=grpc:libs/db/remotedb/proto
protoc_grpc: rpc/grpc/types.pb.go
protoc_merkle: crypto/merkle/merkle.pb.go
########################################
### Testing
## required to be run first by most tests
build_docker_test_image:
docker build -t tester -f ./test/docker/Dockerfile .
### coverage, app, persistence, and libs tests
test_cover:
# run the go unit tests with coverage
bash test/test_cover.sh
test_apps:
# run the app tests using bash
# requires `abci-cli` and `tendermint` binaries installed
bash test/app/test.sh
test_abci_apps:
bash abci/tests/test_app/test.sh
test_abci_cli:
# test the cli against the examples in the tutorial at:
# ./docs/abci-cli.md
# if test fails, update the docs ^
@ bash abci/tests/test_cli/test.sh
test_persistence:
# run the persistence tests using bash
# requires `abci-cli` installed
docker run --name run_persistence -t tester bash test/persist/test_failure_indices.sh
# TODO undockerize
# bash test/persist/test_failure_indices.sh
test_p2p:
docker rm -f rsyslog || true
rm -rf test/logs || true
mkdir test/logs
cd test/
docker run -d -v "logs:/var/log/" -p 127.0.0.1:5514:514/udp --name rsyslog voxxit/rsyslog
cd ..
# requires 'tester' the image from above
bash test/p2p/test.sh tester
# the `docker cp` takes a really long time; uncomment for debugging
#
# mkdir -p test/p2p/logs && docker cp rsyslog:/var/log test/p2p/logs
test_integrations:
make build_docker_test_image
make get_tools
make get_vendor_deps
make install
make test_cover
make test_apps
make test_abci_apps
make test_abci_cli
make test_libs
make test_persistence
make test_p2p
test_release:
@go test -tags release $(PACKAGES)
test100:
@for i in {1..100}; do make test; done
vagrant_test:
vagrant up
vagrant ssh -c 'make test_integrations'
### go tests
test:
@echo "--> Running go test"
@go test -p 1 $(PACKAGES)
test_race:
@echo "--> Running go test --race"
@go test -p 1 -v -race $(PACKAGES)
# uses https://github.com/sasha-s/go-deadlock/ to detect potential deadlocks
test_with_deadlock:
make set_with_deadlock
make test
make cleanup_after_test_with_deadlock
set_with_deadlock:
find . -name "*.go" | grep -v "vendor/" | xargs -n 1 sed -i.bak 's/sync.RWMutex/deadlock.RWMutex/'
find . -name "*.go" | grep -v "vendor/" | xargs -n 1 sed -i.bak 's/sync.Mutex/deadlock.Mutex/'
find . -name "*.go" | grep -v "vendor/" | xargs -n 1 goimports -w
# cleanes up after you ran test_with_deadlock
cleanup_after_test_with_deadlock:
find . -name "*.go" | grep -v "vendor/" | xargs -n 1 sed -i.bak 's/deadlock.RWMutex/sync.RWMutex/'
find . -name "*.go" | grep -v "vendor/" | xargs -n 1 sed -i.bak 's/deadlock.Mutex/sync.Mutex/'
find . -name "*.go" | grep -v "vendor/" | xargs -n 1 goimports -w
########################################
### Formatting, linting, and vetting
@@ -250,14 +153,6 @@ lint:
DESTINATION = ./index.html.md
rpc-docs:
cat rpc/core/slate_header.txt > $(DESTINATION)
godoc2md -template rpc/core/doc_template.txt github.com/tendermint/tendermint/rpc/core | grep -v -e "pipe.go" -e "routes.go" -e "dev.go" | sed 's,/src/target,https://github.com/tendermint/tendermint/tree/master/rpc/core,' >> $(DESTINATION)
check_dep:
dep status >> /dev/null
!(grep -n branch Gopkg.toml)
###########################################################
### Docker image
@@ -270,15 +165,22 @@ build-docker:
### Local testnet using docker
# Build linux binary on other platforms
build-linux: get_tools get_vendor_deps
build-linux: tools
GOOS=linux GOARCH=amd64 $(MAKE) build
build-docker-localnode:
@cd networks/local && make
# Runs `make build_c` from within an Amazon Linux (v2)-based Docker build
# container in order to build an Amazon Linux-compatible binary. Produces a
# compatible binary at ./build/tendermint
build_c-amazonlinux:
$(MAKE) -C ./DOCKER build_amazonlinux_buildimage
docker run --rm -it -v `pwd`:/tendermint tendermint/tendermint:build_c-amazonlinux
# Run a 4-node testnet locally
localnet-start: localnet-stop
@if ! [ -f build/node0/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/tendermint:Z tendermint/localnode testnet --v 4 --o . --populate-persistent-peers --starting-ip-address 192.167.10.2 ; fi
localnet-start: localnet-stop build-docker-localnode
@if ! [ -f build/node0/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/tendermint:Z tendermint/localnode testnet --config /etc/tendermint/config-template.toml --v 4 --o . --populate-persistent-peers --starting-ip-address 192.167.10.2; fi
docker-compose up
# Stop testnet
@@ -305,11 +207,27 @@ sentry-stop:
@if [ -z "$(DO_API_TOKEN)" ]; then echo "DO_API_TOKEN environment variable not set." ; false ; fi
cd networks/remote/terraform && terraform destroy -var DO_API_TOKEN="$(DO_API_TOKEN)" -var SSH_KEY_FILE="$(HOME)/.ssh/id_rsa.pub"
# meant for the CI, inspect script & adapt accordingly
build-slate:
bash scripts/slate.sh
# Build hooks for dredd, to skip or add information on some steps
build-contract-tests-hooks:
ifeq ($(OS),Windows_NT)
go build -mod=readonly $(BUILD_FLAGS) -o build/contract_tests.exe ./cmd/contract_tests
else
go build -mod=readonly $(BUILD_FLAGS) -o build/contract_tests ./cmd/contract_tests
endif
# Run a nodejs tool to test endpoints against a localnet
# The command takes care of starting and stopping the network
# prerequisits: build-contract-tests-hooks build-linux
# the two build commands were not added to let this command run from generic containers or machines.
# The binaries should be built beforehand
contract-tests:
dredd
# To avoid unintended conflicts with file names, always add to .PHONY
# unless there is a reason not to.
# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
.PHONY: check build build_race build_abci dist install install_abci check_dep check_tools get_tools update_tools get_vendor_deps draw_deps get_protoc protoc_abci protoc_libs gen_certs clean_certs grpc_dbserver test_cover test_apps test_persistence test_p2p test test_race test_integrations test_release test100 vagrant_test fmt rpc-docs build-linux localnet-start localnet-stop build-docker build-docker-localnode sentry-start sentry-config sentry-stop build-slate protoc_grpc protoc_all build_c install_c test_with_deadlock cleanup_after_test_with_deadlock lint
.PHONY: check build build_race build_abci dist install install_abci check_tools tools update_tools draw_deps \
get_protoc protoc_abci protoc_libs gen_certs clean_certs grpc_dbserver fmt rpc-docs build-linux localnet-start \
localnet-stop build-docker build-docker-localnode sentry-start sentry-config sentry-stop protoc_grpc protoc_all \
build_c install_c test_with_deadlock cleanup_after_test_with_deadlock lint build-contract-tests-hooks contract-tests \
build_c-amazonlinux

View File

@@ -1,23 +1,21 @@
# Tendermint
![banner](docs/tendermint-core-image.jpg)
[Byzantine-Fault Tolerant](https://en.wikipedia.org/wiki/Byzantine_fault_tolerance)
[State Machines](https://en.wikipedia.org/wiki/State_machine_replication).
Or [Blockchain](https://en.wikipedia.org/wiki/Blockchain_(database)), for short.
Or [Blockchain](<https://en.wikipedia.org/wiki/Blockchain_(database)>), for short.
[![version](https://img.shields.io/github/tag/tendermint/tendermint.svg)](https://github.com/tendermint/tendermint/releases/latest)
[![API Reference](
https://camo.githubusercontent.com/915b7be44ada53c290eb157634330494ebe3e30a/68747470733a2f2f676f646f632e6f72672f6769746875622e636f6d2f676f6c616e672f6764646f3f7374617475732e737667
)](https://godoc.org/github.com/tendermint/tendermint)
[![API Reference](https://camo.githubusercontent.com/915b7be44ada53c290eb157634330494ebe3e30a/68747470733a2f2f676f646f632e6f72672f6769746875622e636f6d2f676f6c616e672f6764646f3f7374617475732e737667)](https://godoc.org/github.com/tendermint/tendermint)
[![Go version](https://img.shields.io/badge/go-1.12.0-blue.svg)](https://github.com/moovweb/gvm)
[![riot.im](https://img.shields.io/badge/riot.im-JOIN%20CHAT-green.svg)](https://riot.im/app/#/room/#tendermint:matrix.org)
[![license](https://img.shields.io/github/license/tendermint/tendermint.svg)](https://github.com/tendermint/tendermint/blob/master/LICENSE)
[![](https://tokei.rs/b1/github/tendermint/tendermint?category=lines)](https://github.com/tendermint/tendermint)
Branch | Tests | Coverage
----------|-------|----------
master | [![CircleCI](https://circleci.com/gh/tendermint/tendermint/tree/master.svg?style=shield)](https://circleci.com/gh/tendermint/tendermint/tree/master) | [![codecov](https://codecov.io/gh/tendermint/tendermint/branch/master/graph/badge.svg)](https://codecov.io/gh/tendermint/tendermint)
develop | [![CircleCI](https://circleci.com/gh/tendermint/tendermint/tree/develop.svg?style=shield)](https://circleci.com/gh/tendermint/tendermint/tree/develop) | [![codecov](https://codecov.io/gh/tendermint/tendermint/branch/develop/graph/badge.svg)](https://codecov.io/gh/tendermint/tendermint)
| Branch | Tests | Coverage |
| ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| master | [![CircleCI](https://circleci.com/gh/tendermint/tendermint/tree/master.svg?style=shield)](https://circleci.com/gh/tendermint/tendermint/tree/master) | [![codecov](https://codecov.io/gh/tendermint/tendermint/branch/master/graph/badge.svg)](https://codecov.io/gh/tendermint/tendermint) |
Tendermint Core is Byzantine Fault Tolerant (BFT) middleware that takes a state transition machine - written in any programming language -
and securely replicates it on many machines.
@@ -27,13 +25,15 @@ For protocol details, see [the specification](/docs/spec).
For detailed analysis of the consensus protocol, including safety and liveness proofs,
see our recent paper, "[The latest gossip on BFT consensus](https://arxiv.org/abs/1807.04938)".
## A Note on Production Readiness
## Releases
While Tendermint is being used in production in private, permissioned
environments, we are still working actively to harden and audit it in preparation
for use in public blockchains, such as the [Cosmos Network](https://cosmos.network/).
We are also still making breaking changes to the protocol and the APIs.
Thus, we tag the releases as *alpha software*.
NOTE: The master branch is now an active development branch (starting with `v0.32`). Please, do not depend on it and
use [releases](https://github.com/tendermint/tendermint/releases) instead.
Tendermint is being used in production in both private and public environments,
most notably the blockchains of the [Cosmos Network](https://cosmos.network/).
However, we are still making breaking changes to the protocol and the APIs and have not yet released v1.0.
See below for more details about [versioning](#versioning).
In any case, if you intend to run Tendermint in production,
please [contact us](mailto:partners@tendermint.com) and [join the chat](https://riot.im/app/#/room/#tendermint:matrix.org).
@@ -47,9 +47,9 @@ For examples of the kinds of bugs we're looking for, see [SECURITY.md](SECURITY.
## Minimum requirements
Requirement|Notes
---|---
Go version | Go1.11.4 or higher
| Requirement | Notes |
| ----------- | ------------------ |
| Go version | Go1.11.4 or higher |
## Documentation
@@ -74,12 +74,11 @@ and the [contributing guidelines](CONTRIBUTING.md) when submitting code.
Join the larger community on the [forum](https://forum.cosmos.network/) and the [chat](https://riot.im/app/#/room/#tendermint:matrix.org).
To learn more about the structure of the software, watch the [Developer
Sessions](https://www.youtube.com/playlist?list=PLdQIb0qr3pnBbG5ZG-0gr3zM86_s8Rpqv)
and read some [Architectural
Decision Records](https://github.com/tendermint/tendermint/tree/master/docs/architecture).
Sessions](/docs/DEV_SESSIONS.md) and read some [Architectural Decision
Records](https://github.com/tendermint/tendermint/tree/master/docs/architecture).
Learn more by reading the code and comparing it to the
[specification](https://github.com/tendermint/tendermint/tree/develop/docs/spec).
[specification](https://github.com/tendermint/tendermint/tree/master/docs/spec).
## Versioning
@@ -143,20 +142,20 @@ Additional documentation is found [here](/docs/tools).
### Sub-projects
* [Amino](http://github.com/tendermint/go-amino), reflection-based proto3, with
- [Amino](http://github.com/tendermint/go-amino), reflection-based proto3, with
interfaces
* [IAVL](http://github.com/tendermint/iavl), Merkleized IAVL+ Tree implementation
- [IAVL](http://github.com/tendermint/iavl), Merkleized IAVL+ Tree implementation
- [Tm-cmn](http://github.com/tendermint/tm-cmn), Commonly used libs across Tendermint & Cosmos repos
### Applications
* [Cosmos SDK](http://github.com/cosmos/cosmos-sdk); a cryptocurrency application framework
* [Ethermint](http://github.com/cosmos/ethermint); Ethereum on Tendermint
* [Many more](https://tendermint.com/ecosystem)
- [Cosmos SDK](http://github.com/cosmos/cosmos-sdk); a cryptocurrency application framework
- [Ethermint](http://github.com/cosmos/ethermint); Ethereum on Tendermint
- [Many more](https://tendermint.com/ecosystem)
### Research
* [The latest gossip on BFT consensus](https://arxiv.org/abs/1807.04938)
* [Master's Thesis on Tendermint](https://atrium.lib.uoguelph.ca/xmlui/handle/10214/9769)
* [Original Whitepaper](https://tendermint.com/static/docs/tendermint.pdf)
* [Blog](https://blog.cosmos.network/tendermint/home)
- [The latest gossip on BFT consensus](https://arxiv.org/abs/1807.04938)
- [Master's Thesis on Tendermint](https://atrium.lib.uoguelph.ca/xmlui/handle/10214/9769)
- [Original Whitepaper](https://tendermint.com/static/docs/tendermint.pdf)
- [Blog](https://blog.cosmos.network/tendermint/home)

View File

@@ -1,23 +0,0 @@
# Roadmap
BREAKING CHANGES:
- Better support for injecting randomness
- Upgrade consensus for more real-time use of evidence
FEATURES:
- Use the chain as its own CA for nodes and validators
- Tooling to run multiple blockchains/apps, possibly in a single process
- State syncing (without transaction replay)
- Add authentication and rate-limitting to the RPC
IMPROVEMENTS:
- Improve subtleties around mempool caching and logic
- Consensus optimizations:
- cache block parts for faster agreement after round changes
- propagate block parts rarest first
- Better testing of the consensus state machine (ie. use a DSL)
- Auto compiled serialization/deserialization code instead of go-wire reflection
BUG FIXES:
- Graceful handling/recovery for apps that have non-determinism or fail to halt
- Graceful handling/recovery for violations of safety, or liveness

View File

@@ -3,6 +3,113 @@
This guide provides steps to be followed when you upgrade your applications to
a newer version of Tendermint Core.
## v0.32.0
This release is compatible with previous blockchains,
however the new ABCI Events mechanism may create some complexity
for nodes wishing to continue operation with v0.32 from a previous version.
There are some minor breaking changes to the RPC.
### Config Changes
If you have `db_backend` set to `leveldb` in your config file, please change it
to `goleveldb` or `cleveldb`.
### RPC Changes
The default listen address for the RPC is now `127.0.0.1`. If you want to expose
it publicly, you have to explicitly configure it. Note exposing the RPC to the
public internet may not be safe - endpoints which return a lot of data may
enable resource exhaustion attacks on your node, causing the process to crash.
Any consumers of `/block_results` need to be mindful of the change in all field
names from CamelCase to Snake case, eg. `results.DeliverTx` is now `results.deliver_tx`.
This is a fix, but it's breaking.
### ABCI Changes
ABCI responses which previously had a `Tags` field now have an `Events` field
instead. The original `Tags` field was simply a list of key-value pairs, where
each key effectively represented some attribute of an event occuring in the
blockchain, like `sender`, `receiver`, or `amount`. However, it was difficult to
represent the occurence of multiple events (for instance, multiple transfers) in a single list.
The new `Events` field contains a list of `Event`, where each `Event` is itself a list
of key-value pairs, allowing for more natural expression of multiple events in
eg. a single DeliverTx or EndBlock. Note each `Event` also includes a `Type`, which is meant to categorize the
event.
For transaction indexing, the index key is
prefixed with the event type: `{eventType}.{attributeKey}`.
If the same event type and attribute key appear multiple times, the values are
appended in a list.
To make queries, include the event type as a prefix. For instance if you
previously queried for `recipient = 'XYZ'`, and after the upgrade you name your event `transfer`,
the new query would be for `transfer.recipient = 'XYZ'`.
Note that transactions indexed on a node before upgrading to v0.32 will still be indexed
using the old scheme. For instance, if a node upgraded at height 100,
transactions before 100 would be queried with `recipient = 'XYZ'` and
transactions after 100 would be queried with `transfer.recipient = 'XYZ'`.
While this presents additional complexity to clients, it avoids the need to
reindex. Of course, you can reset the node and sync from scratch to re-index
entirely using the new scheme.
We illustrate further with a more complete example.
Prior to the update, suppose your `ResponseDeliverTx` look like:
```go
abci.ResponseDeliverTx{
Tags: []cmn.KVPair{
{Key: []byte("sender"), Value: []byte("foo")},
{Key: []byte("recipient"), Value: []byte("bar")},
{Key: []byte("amount"), Value: []byte("35")},
}
}
```
The following queries would match this transaction:
```go
query.MustParse("tm.event = 'Tx' AND sender = 'foo'")
query.MustParse("tm.event = 'Tx' AND recipient = 'bar'")
query.MustParse("tm.event = 'Tx' AND sender = 'foo' AND recipient = 'bar'")
```
Following the upgrade, your `ResponseDeliverTx` would look something like:
the following `Events`:
```go
abci.ResponseDeliverTx{
Events: []abci.Event{
{
Type: "transfer",
Attributes: cmn.KVPairs{
{Key: []byte("sender"), Value: []byte("foo")},
{Key: []byte("recipient"), Value: []byte("bar")},
{Key: []byte("amount"), Value: []byte("35")},
},
}
}
```
Now the following queries would match this transaction:
```go
query.MustParse("tm.event = 'Tx' AND transfer.sender = 'foo'")
query.MustParse("tm.event = 'Tx' AND transfer.recipient = 'bar'")
query.MustParse("tm.event = 'Tx' AND transfer.sender = 'foo' AND transfer.recipient = 'bar'")
```
For further documentation on `Events`, see the [docs](https://github.com/tendermint/tendermint/blob/60827f75623b92eff132dc0eff5b49d2025c591e/docs/spec/abci/abci.md#events).
### Go Applications
The ABCI Application interface changed slightly so the CheckTx and DeliverTx
methods now take Request structs. The contents of these structs are just the raw
tx bytes, which were previously passed in as the argument.
## v0.31.6
There are no breaking changes in this release except Go API of p2p and
@@ -85,13 +192,15 @@ due to changes in how various data structures are hashed.
Any implementations of Tendermint blockchain verification, including lite clients,
will need to be updated. For specific details:
- [Merkle tree](./docs/spec/blockchain/encoding.md#merkle-trees)
- [ConsensusParams](./docs/spec/blockchain/state.md#consensusparams)
There was also a small change to field ordering in the vote struct. Any
implementations of an out-of-process validator (like a Key-Management Server)
will need to be updated. For specific details:
- [Vote](https://github.com/tendermint/tendermint/blob/develop/docs/spec/consensus/signing.md#votes)
- [Vote](https://github.com/tendermint/tendermint/blob/master/docs/spec/consensus/signing.md#votes)
Finally, the proposer selection algorithm continues to evolve. See the
[work-in-progress
@@ -330,7 +439,6 @@ 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

2
Vagrantfile vendored
View File

@@ -57,6 +57,6 @@ Vagrant.configure("2") do |config|
# get all deps and tools, ready to install/test
su - vagrant -c 'source /home/vagrant/.bash_profile'
su - vagrant -c 'cd /home/vagrant/go/src/github.com/tendermint/tendermint && make get_tools && make get_vendor_deps'
su - vagrant -c 'cd /home/vagrant/go/src/github.com/tendermint/tendermint && make tools'
SHELL
end

View File

@@ -28,8 +28,8 @@ type Client interface {
EchoAsync(msg string) *ReqRes
InfoAsync(types.RequestInfo) *ReqRes
SetOptionAsync(types.RequestSetOption) *ReqRes
DeliverTxAsync(tx []byte) *ReqRes
CheckTxAsync(tx []byte) *ReqRes
DeliverTxAsync(types.RequestDeliverTx) *ReqRes
CheckTxAsync(types.RequestCheckTx) *ReqRes
QueryAsync(types.RequestQuery) *ReqRes
CommitAsync() *ReqRes
InitChainAsync(types.RequestInitChain) *ReqRes
@@ -40,8 +40,8 @@ type Client interface {
EchoSync(msg string) (*types.ResponseEcho, error)
InfoSync(types.RequestInfo) (*types.ResponseInfo, error)
SetOptionSync(types.RequestSetOption) (*types.ResponseSetOption, error)
DeliverTxSync(tx []byte) (*types.ResponseDeliverTx, error)
CheckTxSync(tx []byte) (*types.ResponseCheckTx, error)
DeliverTxSync(types.RequestDeliverTx) (*types.ResponseDeliverTx, error)
CheckTxSync(types.RequestCheckTx) (*types.ResponseCheckTx, error)
QuerySync(types.RequestQuery) (*types.ResponseQuery, error)
CommitSync() (*types.ResponseCommit, error)
InitChainSync(types.RequestInitChain) (*types.ResponseInitChain, error)

View File

@@ -6,8 +6,8 @@ import (
"sync"
"time"
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
"golang.org/x/net/context"
"google.golang.org/grpc"
"github.com/tendermint/tendermint/abci/types"
cmn "github.com/tendermint/tendermint/libs/common"
@@ -39,7 +39,7 @@ func NewGRPCClient(addr string, mustConnect bool) *grpcClient {
return cli
}
func dialerFunc(addr string, timeout time.Duration) (net.Conn, error) {
func dialerFunc(ctx context.Context, addr string) (net.Conn, error) {
return cmn.Connect(addr)
}
@@ -49,7 +49,7 @@ func (cli *grpcClient) OnStart() error {
}
RETRY_LOOP:
for {
conn, err := grpc.Dial(cli.addr, grpc.WithInsecure(), grpc.WithDialer(dialerFunc))
conn, err := grpc.Dial(cli.addr, grpc.WithInsecure(), grpc.WithContextDialer(dialerFunc))
if err != nil {
if cli.mustConnect {
return err
@@ -65,7 +65,7 @@ RETRY_LOOP:
ENSURE_CONNECTED:
for {
_, err := client.Echo(context.Background(), &types.RequestEcho{Message: "hello"}, grpc.FailFast(true))
_, err := client.Echo(context.Background(), &types.RequestEcho{Message: "hello"}, grpc.WaitForReady(true))
if err == nil {
break ENSURE_CONNECTED
}
@@ -125,7 +125,7 @@ func (cli *grpcClient) SetResponseCallback(resCb Callback) {
func (cli *grpcClient) EchoAsync(msg string) *ReqRes {
req := types.ToRequestEcho(msg)
res, err := cli.client.Echo(context.Background(), req.GetEcho(), grpc.FailFast(true))
res, err := cli.client.Echo(context.Background(), req.GetEcho(), grpc.WaitForReady(true))
if err != nil {
cli.StopForError(err)
}
@@ -134,7 +134,7 @@ func (cli *grpcClient) EchoAsync(msg string) *ReqRes {
func (cli *grpcClient) FlushAsync() *ReqRes {
req := types.ToRequestFlush()
res, err := cli.client.Flush(context.Background(), req.GetFlush(), grpc.FailFast(true))
res, err := cli.client.Flush(context.Background(), req.GetFlush(), grpc.WaitForReady(true))
if err != nil {
cli.StopForError(err)
}
@@ -143,7 +143,7 @@ func (cli *grpcClient) FlushAsync() *ReqRes {
func (cli *grpcClient) InfoAsync(params types.RequestInfo) *ReqRes {
req := types.ToRequestInfo(params)
res, err := cli.client.Info(context.Background(), req.GetInfo(), grpc.FailFast(true))
res, err := cli.client.Info(context.Background(), req.GetInfo(), grpc.WaitForReady(true))
if err != nil {
cli.StopForError(err)
}
@@ -152,25 +152,25 @@ func (cli *grpcClient) InfoAsync(params types.RequestInfo) *ReqRes {
func (cli *grpcClient) SetOptionAsync(params types.RequestSetOption) *ReqRes {
req := types.ToRequestSetOption(params)
res, err := cli.client.SetOption(context.Background(), req.GetSetOption(), grpc.FailFast(true))
res, err := cli.client.SetOption(context.Background(), req.GetSetOption(), grpc.WaitForReady(true))
if err != nil {
cli.StopForError(err)
}
return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_SetOption{SetOption: res}})
}
func (cli *grpcClient) DeliverTxAsync(tx []byte) *ReqRes {
req := types.ToRequestDeliverTx(tx)
res, err := cli.client.DeliverTx(context.Background(), req.GetDeliverTx(), grpc.FailFast(true))
func (cli *grpcClient) DeliverTxAsync(params types.RequestDeliverTx) *ReqRes {
req := types.ToRequestDeliverTx(params)
res, err := cli.client.DeliverTx(context.Background(), req.GetDeliverTx(), grpc.WaitForReady(true))
if err != nil {
cli.StopForError(err)
}
return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_DeliverTx{DeliverTx: res}})
}
func (cli *grpcClient) CheckTxAsync(tx []byte) *ReqRes {
req := types.ToRequestCheckTx(tx)
res, err := cli.client.CheckTx(context.Background(), req.GetCheckTx(), grpc.FailFast(true))
func (cli *grpcClient) CheckTxAsync(params types.RequestCheckTx) *ReqRes {
req := types.ToRequestCheckTx(params)
res, err := cli.client.CheckTx(context.Background(), req.GetCheckTx(), grpc.WaitForReady(true))
if err != nil {
cli.StopForError(err)
}
@@ -179,7 +179,7 @@ func (cli *grpcClient) CheckTxAsync(tx []byte) *ReqRes {
func (cli *grpcClient) QueryAsync(params types.RequestQuery) *ReqRes {
req := types.ToRequestQuery(params)
res, err := cli.client.Query(context.Background(), req.GetQuery(), grpc.FailFast(true))
res, err := cli.client.Query(context.Background(), req.GetQuery(), grpc.WaitForReady(true))
if err != nil {
cli.StopForError(err)
}
@@ -188,7 +188,7 @@ func (cli *grpcClient) QueryAsync(params types.RequestQuery) *ReqRes {
func (cli *grpcClient) CommitAsync() *ReqRes {
req := types.ToRequestCommit()
res, err := cli.client.Commit(context.Background(), req.GetCommit(), grpc.FailFast(true))
res, err := cli.client.Commit(context.Background(), req.GetCommit(), grpc.WaitForReady(true))
if err != nil {
cli.StopForError(err)
}
@@ -197,7 +197,7 @@ func (cli *grpcClient) CommitAsync() *ReqRes {
func (cli *grpcClient) InitChainAsync(params types.RequestInitChain) *ReqRes {
req := types.ToRequestInitChain(params)
res, err := cli.client.InitChain(context.Background(), req.GetInitChain(), grpc.FailFast(true))
res, err := cli.client.InitChain(context.Background(), req.GetInitChain(), grpc.WaitForReady(true))
if err != nil {
cli.StopForError(err)
}
@@ -206,7 +206,7 @@ func (cli *grpcClient) InitChainAsync(params types.RequestInitChain) *ReqRes {
func (cli *grpcClient) BeginBlockAsync(params types.RequestBeginBlock) *ReqRes {
req := types.ToRequestBeginBlock(params)
res, err := cli.client.BeginBlock(context.Background(), req.GetBeginBlock(), grpc.FailFast(true))
res, err := cli.client.BeginBlock(context.Background(), req.GetBeginBlock(), grpc.WaitForReady(true))
if err != nil {
cli.StopForError(err)
}
@@ -215,7 +215,7 @@ func (cli *grpcClient) BeginBlockAsync(params types.RequestBeginBlock) *ReqRes {
func (cli *grpcClient) EndBlockAsync(params types.RequestEndBlock) *ReqRes {
req := types.ToRequestEndBlock(params)
res, err := cli.client.EndBlock(context.Background(), req.GetEndBlock(), grpc.FailFast(true))
res, err := cli.client.EndBlock(context.Background(), req.GetEndBlock(), grpc.WaitForReady(true))
if err != nil {
cli.StopForError(err)
}
@@ -228,18 +228,22 @@ func (cli *grpcClient) finishAsyncCall(req *types.Request, res *types.Response)
reqres.Done() // Release waiters
reqres.SetDone() // so reqRes.SetCallback will run the callback
// go routine for callbacks
// goroutine for callbacks
go func() {
// Notify reqRes listener if set
if cb := reqres.GetCallback(); cb != nil {
cb(res)
}
cli.mtx.Lock()
defer cli.mtx.Unlock()
// Notify client listener if set
if cli.resCb != nil {
cli.resCb(reqres.Request, res)
}
// Notify reqRes listener if set
if cb := reqres.GetCallback(); cb != nil {
cb(res)
}
}()
return reqres
}
@@ -265,13 +269,13 @@ func (cli *grpcClient) SetOptionSync(req types.RequestSetOption) (*types.Respons
return reqres.Response.GetSetOption(), cli.Error()
}
func (cli *grpcClient) DeliverTxSync(tx []byte) (*types.ResponseDeliverTx, error) {
reqres := cli.DeliverTxAsync(tx)
func (cli *grpcClient) DeliverTxSync(params types.RequestDeliverTx) (*types.ResponseDeliverTx, error) {
reqres := cli.DeliverTxAsync(params)
return reqres.Response.GetDeliverTx(), cli.Error()
}
func (cli *grpcClient) CheckTxSync(tx []byte) (*types.ResponseCheckTx, error) {
reqres := cli.CheckTxAsync(tx)
func (cli *grpcClient) CheckTxSync(params types.RequestCheckTx) (*types.ResponseCheckTx, error) {
reqres := cli.CheckTxAsync(params)
return reqres.Response.GetCheckTx(), cli.Error()
}

View File

@@ -81,24 +81,24 @@ func (app *localClient) SetOptionAsync(req types.RequestSetOption) *ReqRes {
)
}
func (app *localClient) DeliverTxAsync(tx []byte) *ReqRes {
func (app *localClient) DeliverTxAsync(params types.RequestDeliverTx) *ReqRes {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.DeliverTx(tx)
res := app.Application.DeliverTx(params)
return app.callback(
types.ToRequestDeliverTx(tx),
types.ToRequestDeliverTx(params),
types.ToResponseDeliverTx(res),
)
}
func (app *localClient) CheckTxAsync(tx []byte) *ReqRes {
func (app *localClient) CheckTxAsync(req types.RequestCheckTx) *ReqRes {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.CheckTx(tx)
res := app.Application.CheckTx(req)
return app.callback(
types.ToRequestCheckTx(tx),
types.ToRequestCheckTx(req),
types.ToResponseCheckTx(res),
)
}
@@ -184,19 +184,19 @@ func (app *localClient) SetOptionSync(req types.RequestSetOption) (*types.Respon
return &res, nil
}
func (app *localClient) DeliverTxSync(tx []byte) (*types.ResponseDeliverTx, error) {
func (app *localClient) DeliverTxSync(req types.RequestDeliverTx) (*types.ResponseDeliverTx, error) {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.DeliverTx(tx)
res := app.Application.DeliverTx(req)
return &res, nil
}
func (app *localClient) CheckTxSync(tx []byte) (*types.ResponseCheckTx, error) {
func (app *localClient) CheckTxSync(req types.RequestCheckTx) (*types.ResponseCheckTx, error) {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.CheckTx(tx)
res := app.Application.CheckTx(req)
return &res, nil
}

View File

@@ -5,6 +5,7 @@ import (
"container/list"
"errors"
"fmt"
"io"
"net"
"reflect"
"sync"
@@ -55,10 +56,6 @@ func NewSocketClient(addr string, mustConnect bool) *socketClient {
}
func (cli *socketClient) OnStart() error {
if err := cli.BaseService.OnStart(); err != nil {
return err
}
var err error
var conn net.Conn
RETRY_LOOP:
@@ -82,15 +79,12 @@ RETRY_LOOP:
}
func (cli *socketClient) OnStop() {
cli.BaseService.OnStop()
cli.mtx.Lock()
defer cli.mtx.Unlock()
if cli.conn != nil {
// does this really need a mutex?
cli.conn.Close()
}
cli.mtx.Lock()
defer cli.mtx.Unlock()
cli.flushQueue()
}
@@ -126,7 +120,7 @@ func (cli *socketClient) SetResponseCallback(resCb Callback) {
//----------------------------------------
func (cli *socketClient) sendRequestsRoutine(conn net.Conn) {
func (cli *socketClient) sendRequestsRoutine(conn io.Writer) {
w := bufio.NewWriter(conn)
for {
@@ -158,7 +152,7 @@ func (cli *socketClient) sendRequestsRoutine(conn net.Conn) {
}
}
func (cli *socketClient) recvResponseRoutine(conn net.Conn) {
func (cli *socketClient) recvResponseRoutine(conn io.Reader) {
r := bufio.NewReader(conn) // Buffer reads
for {
@@ -209,19 +203,18 @@ func (cli *socketClient) didRecvResponse(res *types.Response) error {
reqres.Done() // Release waiters
cli.reqSent.Remove(next) // Pop first item from linked list
// Notify reqRes listener if set (request specific callback).
// NOTE: it is possible this callback isn't set on the reqres object.
// at this point, in which case it will be called after, when it is set.
// TODO: should we move this after the resCb call so the order is always consistent?
if cb := reqres.GetCallback(); cb != nil {
cb(res)
}
// Notify client listener if set (global callback).
if cli.resCb != nil {
cli.resCb(reqres.Request, res)
}
// Notify reqRes listener if set (request specific callback).
// NOTE: it is possible this callback isn't set on the reqres object.
// at this point, in which case it will be called after, when it is set.
if cb := reqres.GetCallback(); cb != nil {
cb(res)
}
return nil
}
@@ -243,12 +236,12 @@ func (cli *socketClient) SetOptionAsync(req types.RequestSetOption) *ReqRes {
return cli.queueRequest(types.ToRequestSetOption(req))
}
func (cli *socketClient) DeliverTxAsync(tx []byte) *ReqRes {
return cli.queueRequest(types.ToRequestDeliverTx(tx))
func (cli *socketClient) DeliverTxAsync(req types.RequestDeliverTx) *ReqRes {
return cli.queueRequest(types.ToRequestDeliverTx(req))
}
func (cli *socketClient) CheckTxAsync(tx []byte) *ReqRes {
return cli.queueRequest(types.ToRequestCheckTx(tx))
func (cli *socketClient) CheckTxAsync(req types.RequestCheckTx) *ReqRes {
return cli.queueRequest(types.ToRequestCheckTx(req))
}
func (cli *socketClient) QueryAsync(req types.RequestQuery) *ReqRes {
@@ -300,14 +293,14 @@ func (cli *socketClient) SetOptionSync(req types.RequestSetOption) (*types.Respo
return reqres.Response.GetSetOption(), cli.Error()
}
func (cli *socketClient) DeliverTxSync(tx []byte) (*types.ResponseDeliverTx, error) {
reqres := cli.queueRequest(types.ToRequestDeliverTx(tx))
func (cli *socketClient) DeliverTxSync(req types.RequestDeliverTx) (*types.ResponseDeliverTx, error) {
reqres := cli.queueRequest(types.ToRequestDeliverTx(req))
cli.FlushSync()
return reqres.Response.GetDeliverTx(), cli.Error()
}
func (cli *socketClient) CheckTxSync(tx []byte) (*types.ResponseCheckTx, error) {
reqres := cli.queueRequest(types.ToRequestCheckTx(tx))
func (cli *socketClient) CheckTxSync(req types.RequestCheckTx) (*types.ResponseCheckTx, error) {
reqres := cli.queueRequest(types.ToRequestCheckTx(req))
cli.FlushSync()
return reqres.Response.GetCheckTx(), cli.Error()
}

View File

@@ -174,9 +174,7 @@ where example.file looks something like:
info
`,
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
return cmdBatch(cmd, args)
},
RunE: cmdBatch,
}
var consoleCmd = &cobra.Command{
@@ -189,9 +187,7 @@ without opening a new connection each time
`,
Args: cobra.ExactArgs(0),
ValidArgs: []string{"echo", "info", "set_option", "deliver_tx", "check_tx", "commit", "query"},
RunE: func(cmd *cobra.Command, args []string) error {
return cmdConsole(cmd, args)
},
RunE: cmdConsole,
}
var echoCmd = &cobra.Command{
@@ -199,27 +195,21 @@ var echoCmd = &cobra.Command{
Short: "have the application echo a message",
Long: "have the application echo a message",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return cmdEcho(cmd, args)
},
RunE: cmdEcho,
}
var infoCmd = &cobra.Command{
Use: "info",
Short: "get some info about the application",
Long: "get some info about the application",
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
return cmdInfo(cmd, args)
},
RunE: cmdInfo,
}
var setOptionCmd = &cobra.Command{
Use: "set_option",
Short: "set an option on the application",
Long: "set an option on the application",
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
return cmdSetOption(cmd, args)
},
RunE: cmdSetOption,
}
var deliverTxCmd = &cobra.Command{
@@ -227,9 +217,7 @@ var deliverTxCmd = &cobra.Command{
Short: "deliver a new transaction to the application",
Long: "deliver a new transaction to the application",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return cmdDeliverTx(cmd, args)
},
RunE: cmdDeliverTx,
}
var checkTxCmd = &cobra.Command{
@@ -237,9 +225,7 @@ var checkTxCmd = &cobra.Command{
Short: "validate a transaction",
Long: "validate a transaction",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return cmdCheckTx(cmd, args)
},
RunE: cmdCheckTx,
}
var commitCmd = &cobra.Command{
@@ -247,9 +233,7 @@ var commitCmd = &cobra.Command{
Short: "commit the application state and return the Merkle root hash",
Long: "commit the application state and return the Merkle root hash",
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
return cmdCommit(cmd, args)
},
RunE: cmdCommit,
}
var versionCmd = &cobra.Command{
@@ -268,9 +252,7 @@ var queryCmd = &cobra.Command{
Short: "query the application state",
Long: "query the application state",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return cmdQuery(cmd, args)
},
RunE: cmdQuery,
}
var counterCmd = &cobra.Command{
@@ -278,9 +260,7 @@ var counterCmd = &cobra.Command{
Short: "ABCI demo example",
Long: "ABCI demo example",
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
return cmdCounter(cmd, args)
},
RunE: cmdCounter,
}
var kvstoreCmd = &cobra.Command{
@@ -288,9 +268,7 @@ var kvstoreCmd = &cobra.Command{
Short: "ABCI demo example",
Long: "ABCI demo example",
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
return cmdKVStore(cmd, args)
},
RunE: cmdKVStore,
}
var testCmd = &cobra.Command{
@@ -298,9 +276,7 @@ var testCmd = &cobra.Command{
Short: "run integration tests",
Long: "run integration tests",
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
return cmdTest(cmd, args)
},
RunE: cmdTest,
}
// Generates new Args array based off of previous call args to maintain flag persistence
@@ -356,16 +332,18 @@ func cmdTest(cmd *cobra.Command, args []string) error {
func cmdBatch(cmd *cobra.Command, args []string) error {
bufReader := bufio.NewReader(os.Stdin)
LOOP:
for {
line, more, err := bufReader.ReadLine()
if more {
switch {
case more:
return errors.New("Input line is too long")
} else if err == io.EOF {
break
} else if len(line) == 0 {
case err == io.EOF:
break LOOP
case len(line) == 0:
continue
} else if err != nil {
case err != nil:
return err
}
@@ -419,7 +397,7 @@ func muxOnCommands(cmd *cobra.Command, pArgs []string) error {
}
// otherwise, we need to skip the next one too
i += 1
i++
continue
}
@@ -546,7 +524,7 @@ func cmdDeliverTx(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
res, err := client.DeliverTxSync(txBytes)
res, err := client.DeliverTxSync(types.RequestDeliverTx{Tx: txBytes})
if err != nil {
return err
}
@@ -572,7 +550,7 @@ func cmdCheckTx(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
res, err := client.CheckTxSync(txBytes)
res, err := client.CheckTxSync(types.RequestCheckTx{Tx: txBytes})
if err != nil {
return err
}

View File

@@ -42,15 +42,15 @@ func (app *CounterApplication) SetOption(req types.RequestSetOption) types.Respo
return types.ResponseSetOption{}
}
func (app *CounterApplication) DeliverTx(tx []byte) types.ResponseDeliverTx {
func (app *CounterApplication) DeliverTx(req types.RequestDeliverTx) types.ResponseDeliverTx {
if app.serial {
if len(tx) > 8 {
if len(req.Tx) > 8 {
return types.ResponseDeliverTx{
Code: code.CodeTypeEncodingError,
Log: fmt.Sprintf("Max tx size is 8 bytes, got %d", len(tx))}
Log: fmt.Sprintf("Max tx size is 8 bytes, got %d", len(req.Tx))}
}
tx8 := make([]byte, 8)
copy(tx8[len(tx8)-len(tx):], tx)
copy(tx8[len(tx8)-len(req.Tx):], req.Tx)
txValue := binary.BigEndian.Uint64(tx8)
if txValue != uint64(app.txCount) {
return types.ResponseDeliverTx{
@@ -62,15 +62,15 @@ func (app *CounterApplication) DeliverTx(tx []byte) types.ResponseDeliverTx {
return types.ResponseDeliverTx{Code: code.CodeTypeOK}
}
func (app *CounterApplication) CheckTx(tx []byte) types.ResponseCheckTx {
func (app *CounterApplication) CheckTx(req types.RequestCheckTx) types.ResponseCheckTx {
if app.serial {
if len(tx) > 8 {
if len(req.Tx) > 8 {
return types.ResponseCheckTx{
Code: code.CodeTypeEncodingError,
Log: fmt.Sprintf("Max tx size is 8 bytes, got %d", len(tx))}
Log: fmt.Sprintf("Max tx size is 8 bytes, got %d", len(req.Tx))}
}
tx8 := make([]byte, 8)
copy(tx8[len(tx8)-len(tx):], tx)
copy(tx8[len(tx8)-len(req.Tx):], req.Tx)
txValue := binary.BigEndian.Uint64(tx8)
if txValue < uint64(app.txCount) {
return types.ResponseCheckTx{

View File

@@ -87,7 +87,7 @@ func testStream(t *testing.T, app types.Application) {
// Write requests
for counter := 0; counter < numDeliverTxs; counter++ {
// Send request
reqRes := client.DeliverTxAsync([]byte("test"))
reqRes := client.DeliverTxAsync(types.RequestDeliverTx{Tx: []byte("test")})
_ = reqRes
// check err ?
@@ -107,11 +107,11 @@ func testStream(t *testing.T, app types.Application) {
//-------------------------
// test grpc
func dialerFunc(addr string, timeout time.Duration) (net.Conn, error) {
func dialerFunc(ctx context.Context, addr string) (net.Conn, error) {
return cmn.Connect(addr)
}
func testGRPCSync(t *testing.T, app *types.GRPCApplication) {
func testGRPCSync(t *testing.T, app types.ABCIApplicationServer) {
numDeliverTxs := 2000
// Start the listener
@@ -123,7 +123,7 @@ func testGRPCSync(t *testing.T, app *types.GRPCApplication) {
defer server.Stop()
// Connect to the socket
conn, err := grpc.Dial("unix://test.sock", grpc.WithInsecure(), grpc.WithDialer(dialerFunc))
conn, err := grpc.Dial("unix://test.sock", grpc.WithInsecure(), grpc.WithContextDialer(dialerFunc))
if err != nil {
t.Fatalf("Error dialing GRPC server: %v", err.Error())
}

View File

@@ -22,10 +22,10 @@ and the Handshake allows any necessary blocks to be replayed.
Validator set changes are effected using the following transaction format:
```
val:pubkey1/power1,addr2/power2,addr3/power3"
"val:pubkey1!power1,pubkey2!power2,pubkey3!power3"
```
where `power1` is the new voting power for the validator with `pubkey1` (possibly a new one).
where `pubkeyN` is a base64-encoded 32-byte ed25519 key and `powerN` is a new voting power for the validator with `pubkeyN` (possibly a new one).
To remove a validator from the validator set, set power to `0`.
There is no sybil protection against new validators joining.
Validators can be removed by setting their power to `0`.

View File

@@ -9,8 +9,8 @@ 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/version"
dbm "github.com/tendermint/tm-db"
)
var (
@@ -76,25 +76,32 @@ func (app *KVStoreApplication) Info(req types.RequestInfo) (resInfo types.Respon
}
// tx is either "key=value" or just arbitrary bytes
func (app *KVStoreApplication) DeliverTx(tx []byte) types.ResponseDeliverTx {
func (app *KVStoreApplication) DeliverTx(req types.RequestDeliverTx) types.ResponseDeliverTx {
var key, value []byte
parts := bytes.Split(tx, []byte("="))
parts := bytes.Split(req.Tx, []byte("="))
if len(parts) == 2 {
key, value = parts[0], parts[1]
} else {
key, value = tx, tx
key, value = req.Tx, req.Tx
}
app.state.db.Set(prefixKey(key), value)
app.state.Size += 1
tags := []cmn.KVPair{
{Key: []byte("app.creator"), Value: []byte("Cosmoshi Netowoko")},
{Key: []byte("app.key"), Value: key},
events := []types.Event{
{
Type: "app",
Attributes: []cmn.KVPair{
{Key: []byte("creator"), Value: []byte("Cosmoshi Netowoko")},
{Key: []byte("key"), Value: key},
},
},
}
return types.ResponseDeliverTx{Code: code.CodeTypeOK, Tags: tags}
return types.ResponseDeliverTx{Code: code.CodeTypeOK, Events: events}
}
func (app *KVStoreApplication) CheckTx(tx []byte) types.ResponseCheckTx {
func (app *KVStoreApplication) CheckTx(req types.RequestCheckTx) types.ResponseCheckTx {
return types.ResponseCheckTx{Code: code.CodeTypeOK, GasWanted: 1}
}
@@ -108,6 +115,7 @@ func (app *KVStoreApplication) Commit() types.ResponseCommit {
return types.ResponseCommit{Data: appHash}
}
// Returns an associated value or nil if missing.
func (app *KVStoreApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) {
if reqQuery.Prove {
value := app.state.db.Get(prefixKey(reqQuery.Data))

View File

@@ -18,11 +18,17 @@ import (
"github.com/tendermint/tendermint/abci/types"
)
const (
testKey = "abc"
testValue = "def"
)
func testKVStore(t *testing.T, app types.Application, tx []byte, key, value string) {
ar := app.DeliverTx(tx)
req := types.RequestDeliverTx{Tx: tx}
ar := app.DeliverTx(req)
require.False(t, ar.IsErr(), ar)
// repeating tx doesn't raise error
ar = app.DeliverTx(tx)
ar = app.DeliverTx(req)
require.False(t, ar.IsErr(), ar)
// make sure query is fine
@@ -45,12 +51,12 @@ func testKVStore(t *testing.T, app types.Application, tx []byte, key, value stri
func TestKVStoreKV(t *testing.T) {
kvstore := NewKVStoreApplication()
key := "abc"
key := testKey
value := key
tx := []byte(key)
testKVStore(t, kvstore, tx, key, value)
value = "def"
value = testValue
tx = []byte(key + "=" + value)
testKVStore(t, kvstore, tx, key, value)
}
@@ -61,12 +67,12 @@ func TestPersistentKVStoreKV(t *testing.T) {
t.Fatal(err)
}
kvstore := NewPersistentKVStoreApplication(dir)
key := "abc"
key := testKey
value := key
tx := []byte(key)
testKVStore(t, kvstore, tx, key, value)
value = "def"
value = testValue
tx = []byte(key + "=" + value)
testKVStore(t, kvstore, tx, key, value)
}
@@ -89,7 +95,7 @@ func TestPersistentKVStoreInfo(t *testing.T) {
height = int64(1)
hash := []byte("foo")
header := types.Header{
Height: int64(height),
Height: height,
}
kvstore.BeginBlock(types.RequestBeginBlock{Hash: hash, Header: header})
kvstore.EndBlock(types.RequestEndBlock{Height: header.Height})
@@ -147,7 +153,7 @@ func TestValUpdates(t *testing.T) {
makeApplyBlock(t, kvstore, 2, diff, tx1, tx2, tx3)
vals1 = append(vals[:nInit-2], vals[nInit+1])
vals1 = append(vals[:nInit-2], vals[nInit+1]) // nolint: gocritic
vals2 = kvstore.Validators()
valsEqual(t, vals1, vals2)
@@ -179,7 +185,7 @@ func makeApplyBlock(t *testing.T, kvstore types.Application, heightInt int, diff
kvstore.BeginBlock(types.RequestBeginBlock{Hash: hash, Header: header})
for _, tx := range txs {
if r := kvstore.DeliverTx(tx); r.IsErr() {
if r := kvstore.DeliverTx(types.RequestDeliverTx{Tx: tx}); r.IsErr() {
t.Fatal(r)
}
}
@@ -271,22 +277,22 @@ func TestClientServer(t *testing.T) {
func runClientTests(t *testing.T, client abcicli.Client) {
// run some tests....
key := "abc"
key := testKey
value := key
tx := []byte(key)
testClient(t, client, tx, key, value)
value = "def"
value = testValue
tx = []byte(key + "=" + value)
testClient(t, client, tx, key, value)
}
func testClient(t *testing.T, app abcicli.Client, tx []byte, key, value string) {
ar, err := app.DeliverTxSync(tx)
ar, err := app.DeliverTxSync(types.RequestDeliverTx{Tx: tx})
require.NoError(t, err)
require.False(t, ar.IsErr(), ar)
// repeating tx doesn't raise error
ar, err = app.DeliverTxSync(tx)
ar, err = app.DeliverTxSync(types.RequestDeliverTx{Tx: tx})
require.NoError(t, err)
require.False(t, ar.IsErr(), ar)

View File

@@ -2,15 +2,17 @@ package kvstore
import (
"bytes"
"encoding/hex"
"encoding/base64"
"fmt"
"strconv"
"strings"
"github.com/tendermint/tendermint/abci/example/code"
"github.com/tendermint/tendermint/abci/types"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/libs/log"
tmtypes "github.com/tendermint/tendermint/types"
dbm "github.com/tendermint/tm-db"
)
const (
@@ -27,6 +29,8 @@ type PersistentKVStoreApplication struct {
// validator set
ValUpdates []types.ValidatorUpdate
valAddrToPubKeyMap map[string]types.PubKey
logger log.Logger
}
@@ -40,8 +44,9 @@ func NewPersistentKVStoreApplication(dbDir string) *PersistentKVStoreApplication
state := loadState(db)
return &PersistentKVStoreApplication{
app: &KVStoreApplication{state: state},
logger: log.NewNopLogger(),
app: &KVStoreApplication{state: state},
valAddrToPubKeyMap: make(map[string]types.PubKey),
logger: log.NewNopLogger(),
}
}
@@ -60,22 +65,22 @@ func (app *PersistentKVStoreApplication) SetOption(req types.RequestSetOption) t
return app.app.SetOption(req)
}
// tx is either "val:pubkey/power" or "key=value" or just arbitrary bytes
func (app *PersistentKVStoreApplication) DeliverTx(tx []byte) types.ResponseDeliverTx {
// tx is either "val:pubkey!power" or "key=value" or just arbitrary bytes
func (app *PersistentKVStoreApplication) DeliverTx(req types.RequestDeliverTx) types.ResponseDeliverTx {
// if it starts with "val:", update the validator set
// format is "val:pubkey/power"
if isValidatorTx(tx) {
// format is "val:pubkey!power"
if isValidatorTx(req.Tx) {
// update validators in the merkle tree
// and in app.ValUpdates
return app.execValidatorTx(tx)
return app.execValidatorTx(req.Tx)
}
// otherwise, update the key-value store
return app.app.DeliverTx(tx)
return app.app.DeliverTx(req)
}
func (app *PersistentKVStoreApplication) CheckTx(tx []byte) types.ResponseCheckTx {
return app.app.CheckTx(tx)
func (app *PersistentKVStoreApplication) CheckTx(req types.RequestCheckTx) types.ResponseCheckTx {
return app.app.CheckTx(req)
}
// Commit will panic if InitChain was not called
@@ -83,8 +88,20 @@ func (app *PersistentKVStoreApplication) Commit() types.ResponseCommit {
return app.app.Commit()
}
func (app *PersistentKVStoreApplication) Query(reqQuery types.RequestQuery) types.ResponseQuery {
return app.app.Query(reqQuery)
// When path=/val and data={validator address}, returns the validator update (types.ValidatorUpdate) varint encoded.
// For any other path, returns an associated value or nil if missing.
func (app *PersistentKVStoreApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) {
switch reqQuery.Path {
case "/val":
key := []byte("val:" + string(reqQuery.Data))
value := app.app.state.db.Get(key)
resQuery.Key = reqQuery.Data
resQuery.Value = value
return
default:
return app.app.Query(reqQuery)
}
}
// Save the validators in the merkle tree
@@ -102,6 +119,19 @@ func (app *PersistentKVStoreApplication) InitChain(req types.RequestInitChain) t
func (app *PersistentKVStoreApplication) BeginBlock(req types.RequestBeginBlock) types.ResponseBeginBlock {
// reset valset changes
app.ValUpdates = make([]types.ValidatorUpdate, 0)
for _, ev := range req.ByzantineValidators {
if ev.Type == tmtypes.ABCIEvidenceTypeDuplicateVote {
// decrease voting power by 1
if ev.TotalVotingPower == 0 {
continue
}
app.updateValidator(types.ValidatorUpdate{
PubKey: app.valAddrToPubKeyMap[string(ev.Validator.Address)],
Power: ev.TotalVotingPower - 1,
})
}
}
return types.ResponseBeginBlock{}
}
@@ -129,33 +159,34 @@ func (app *PersistentKVStoreApplication) Validators() (validators []types.Valida
}
func MakeValSetChangeTx(pubkey types.PubKey, power int64) []byte {
return []byte(fmt.Sprintf("val:%X/%d", pubkey.Data, power))
pubStr := base64.StdEncoding.EncodeToString(pubkey.Data)
return []byte(fmt.Sprintf("val:%s!%d", pubStr, power))
}
func isValidatorTx(tx []byte) bool {
return strings.HasPrefix(string(tx), ValidatorSetChangePrefix)
}
// format is "val:pubkey/power"
// pubkey is raw 32-byte ed25519 key
// format is "val:pubkey!power"
// pubkey is a base64-encoded 32-byte ed25519 key
func (app *PersistentKVStoreApplication) execValidatorTx(tx []byte) types.ResponseDeliverTx {
tx = tx[len(ValidatorSetChangePrefix):]
//get the pubkey and power
pubKeyAndPower := strings.Split(string(tx), "/")
pubKeyAndPower := strings.Split(string(tx), "!")
if len(pubKeyAndPower) != 2 {
return types.ResponseDeliverTx{
Code: code.CodeTypeEncodingError,
Log: fmt.Sprintf("Expected 'pubkey/power'. Got %v", pubKeyAndPower)}
Log: fmt.Sprintf("Expected 'pubkey!power'. Got %v", pubKeyAndPower)}
}
pubkeyS, powerS := pubKeyAndPower[0], pubKeyAndPower[1]
// decode the pubkey
pubkey, err := hex.DecodeString(pubkeyS)
pubkey, err := base64.StdEncoding.DecodeString(pubkeyS)
if err != nil {
return types.ResponseDeliverTx{
Code: code.CodeTypeEncodingError,
Log: fmt.Sprintf("Pubkey (%s) is invalid hex", pubkeyS)}
Log: fmt.Sprintf("Pubkey (%s) is invalid base64", pubkeyS)}
}
// decode the power
@@ -167,20 +198,26 @@ func (app *PersistentKVStoreApplication) execValidatorTx(tx []byte) types.Respon
}
// update
return app.updateValidator(types.Ed25519ValidatorUpdate(pubkey, int64(power)))
return app.updateValidator(types.Ed25519ValidatorUpdate(pubkey, power))
}
// add, update, or remove a validator
func (app *PersistentKVStoreApplication) updateValidator(v types.ValidatorUpdate) types.ResponseDeliverTx {
key := []byte("val:" + string(v.PubKey.Data))
pubkey := ed25519.PubKeyEd25519{}
copy(pubkey[:], v.PubKey.Data)
if v.Power == 0 {
// remove validator
if !app.app.state.db.Has(key) {
pubStr := base64.StdEncoding.EncodeToString(v.PubKey.Data)
return types.ResponseDeliverTx{
Code: code.CodeTypeUnauthorized,
Log: fmt.Sprintf("Cannot remove non-existent validator %X", key)}
Log: fmt.Sprintf("Cannot remove non-existent validator %s", pubStr)}
}
app.app.state.db.Delete(key)
delete(app.valAddrToPubKeyMap, string(pubkey.Address()))
} else {
// add or update validator
value := bytes.NewBuffer(make([]byte, 0))
@@ -190,6 +227,7 @@ func (app *PersistentKVStoreApplication) updateValidator(v types.ValidatorUpdate
Log: fmt.Sprintf("Error encoding validator: %v", err)}
}
app.app.state.db.Set(key, value.Bytes())
app.valAddrToPubKeyMap[string(pubkey.Address())] = v.PubKey
}
// we only update the changes array if we successfully updated the tree

View File

@@ -127,11 +127,12 @@ func (s *SocketServer) acceptConnectionsRoutine() {
func (s *SocketServer) waitForClose(closeConn chan error, connID int) {
err := <-closeConn
if err == io.EOF {
switch {
case err == io.EOF:
s.Logger.Error("Connection was closed by client")
} else if err != nil {
case err != nil:
s.Logger.Error("Connection error", "error", err)
} else {
default:
// never happens
s.Logger.Error("Connection was closed.")
}
@@ -143,9 +144,19 @@ func (s *SocketServer) waitForClose(closeConn chan error, connID int) {
}
// Read requests from conn and deal with them
func (s *SocketServer) handleRequests(closeConn chan error, conn net.Conn, responses chan<- *types.Response) {
func (s *SocketServer) handleRequests(closeConn chan error, conn io.Reader, responses chan<- *types.Response) {
var count int
var bufReader = bufio.NewReader(conn)
defer func() {
// make sure to recover from any app-related panics to allow proper socket cleanup
r := recover()
if r != nil {
closeConn <- fmt.Errorf("recovered from panic: %v", r)
s.appMtx.Unlock()
}
}()
for {
var req = &types.Request{}
@@ -154,7 +165,7 @@ func (s *SocketServer) handleRequests(closeConn chan error, conn net.Conn, respo
if err == io.EOF {
closeConn <- err
} else {
closeConn <- fmt.Errorf("Error reading message: %v", err.Error())
closeConn <- fmt.Errorf("error reading message: %v", err)
}
return
}
@@ -178,10 +189,10 @@ func (s *SocketServer) handleRequest(req *types.Request, responses chan<- *types
res := s.app.SetOption(*r.SetOption)
responses <- types.ToResponseSetOption(res)
case *types.Request_DeliverTx:
res := s.app.DeliverTx(r.DeliverTx.Tx)
res := s.app.DeliverTx(*r.DeliverTx)
responses <- types.ToResponseDeliverTx(res)
case *types.Request_CheckTx:
res := s.app.CheckTx(r.CheckTx.Tx)
res := s.app.CheckTx(*r.CheckTx)
responses <- types.ToResponseCheckTx(res)
case *types.Request_Commit:
res := s.app.Commit()
@@ -204,7 +215,7 @@ func (s *SocketServer) handleRequest(req *types.Request, responses chan<- *types
}
// Pull responses from 'responses' and write them to conn.
func (s *SocketServer) handleResponses(closeConn chan error, conn net.Conn, responses <-chan *types.Response) {
func (s *SocketServer) handleResponses(closeConn chan error, conn io.Writer, responses <-chan *types.Response) {
var count int
var bufWriter = bufio.NewWriter(conn)
for {

View File

@@ -3,8 +3,8 @@ package main
import (
"bufio"
"fmt"
"io"
"log"
"net"
"reflect"
"github.com/tendermint/tendermint/abci/types"
@@ -33,7 +33,7 @@ func main() {
}
}
func makeRequest(conn net.Conn, req *types.Request) (*types.Response, error) {
func makeRequest(conn io.ReadWriter, req *types.Request) (*types.Response, error) {
var bufWriter = bufio.NewWriter(conn)
// Write desired request

View File

@@ -58,7 +58,7 @@ func Commit(client abcicli.Client, hashExp []byte) error {
}
func DeliverTx(client abcicli.Client, txBytes []byte, codeExp uint32, dataExp []byte) error {
res, _ := client.DeliverTxSync(txBytes)
res, _ := client.DeliverTxSync(types.RequestDeliverTx{Tx: txBytes})
code, data, log := res.Code, res.Data, res.Log
if code != codeExp {
fmt.Println("Failed test: DeliverTx")
@@ -77,7 +77,7 @@ func DeliverTx(client abcicli.Client, txBytes []byte, codeExp uint32, dataExp []
}
func CheckTx(client abcicli.Client, txBytes []byte, codeExp uint32, dataExp []byte) error {
res, _ := client.CheckTxSync(txBytes)
res, _ := client.CheckTxSync(types.RequestCheckTx{Tx: txBytes})
code, data, log := res.Code, res.Data, res.Log
if code != codeExp {
fmt.Println("Failed test: CheckTx")

View File

@@ -43,7 +43,7 @@ func commit(client abcicli.Client, hashExp []byte) {
}
func deliverTx(client abcicli.Client, txBytes []byte, codeExp uint32, dataExp []byte) {
res, err := client.DeliverTxSync(txBytes)
res, err := client.DeliverTxSync(types.RequestDeliverTx{Tx: txBytes})
if err != nil {
panicf("client error: %v", err)
}

View File

@@ -4,6 +4,7 @@ set -e
# These tests spawn the counter app and server by execing the ABCI_APP command and run some simple client tests against it
# Get the directory of where this script is.
export PATH="$GOBIN:$PATH"
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
@@ -13,13 +14,13 @@ cd "$DIR"
echo "RUN COUNTER OVER SOCKET"
# test golang counter
ABCI_APP="counter" go run ./*.go
ABCI_APP="counter" go run -mod=readonly ./*.go
echo "----------------------"
echo "RUN COUNTER OVER GRPC"
# test golang counter via grpc
ABCI_APP="counter --abci=grpc" ABCI="grpc" go run ./*.go
ABCI_APP="counter --abci=grpc" ABCI="grpc" go run -mod=readonly ./*.go
echo "----------------------"
# test nodejs counter

View File

@@ -2,6 +2,7 @@
set -e
# Get the root directory.
export PATH="$GOBIN:$PATH"
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done
DIR="$( cd -P "$( dirname "$SOURCE" )/../.." && pwd )"

View File

@@ -15,12 +15,12 @@ type Application interface {
Query(RequestQuery) ResponseQuery // Query for state
// Mempool Connection
CheckTx(tx []byte) ResponseCheckTx // Validate a tx for the mempool
CheckTx(RequestCheckTx) ResponseCheckTx // Validate a tx for the mempool
// Consensus Connection
InitChain(RequestInitChain) ResponseInitChain // Initialize blockchain with validators and other info from TendermintCore
BeginBlock(RequestBeginBlock) ResponseBeginBlock // Signals the beginning of a block
DeliverTx(tx []byte) ResponseDeliverTx // Deliver a tx for full processing
DeliverTx(RequestDeliverTx) ResponseDeliverTx // Deliver a tx for full processing
EndBlock(RequestEndBlock) ResponseEndBlock // Signals the end of a block, returns changes to the validator set
Commit() ResponseCommit // Commit the state and return the application Merkle root hash
}
@@ -45,11 +45,11 @@ func (BaseApplication) SetOption(req RequestSetOption) ResponseSetOption {
return ResponseSetOption{}
}
func (BaseApplication) DeliverTx(tx []byte) ResponseDeliverTx {
func (BaseApplication) DeliverTx(req RequestDeliverTx) ResponseDeliverTx {
return ResponseDeliverTx{Code: CodeTypeOK}
}
func (BaseApplication) CheckTx(tx []byte) ResponseCheckTx {
func (BaseApplication) CheckTx(req RequestCheckTx) ResponseCheckTx {
return ResponseCheckTx{Code: CodeTypeOK}
}
@@ -103,12 +103,12 @@ func (app *GRPCApplication) SetOption(ctx context.Context, req *RequestSetOption
}
func (app *GRPCApplication) DeliverTx(ctx context.Context, req *RequestDeliverTx) (*ResponseDeliverTx, error) {
res := app.app.DeliverTx(req.Tx)
res := app.app.DeliverTx(*req)
return &res, nil
}
func (app *GRPCApplication) CheckTx(ctx context.Context, req *RequestCheckTx) (*ResponseCheckTx, error) {
res := app.app.CheckTx(req.Tx)
res := app.app.CheckTx(*req)
return &res, nil
}

View File

@@ -93,15 +93,15 @@ func ToRequestSetOption(req RequestSetOption) *Request {
}
}
func ToRequestDeliverTx(tx []byte) *Request {
func ToRequestDeliverTx(req RequestDeliverTx) *Request {
return &Request{
Value: &Request_DeliverTx{&RequestDeliverTx{Tx: tx}},
Value: &Request_DeliverTx{&req},
}
}
func ToRequestCheckTx(tx []byte) *Request {
func ToRequestCheckTx(req RequestCheckTx) *Request {
return &Request{
Value: &Request_CheckTx{&RequestCheckTx{Tx: tx}},
Value: &Request_CheckTx{&req},
}
}

View File

@@ -8,21 +8,26 @@ import (
"github.com/gogo/protobuf/proto"
"github.com/stretchr/testify/assert"
cmn "github.com/tendermint/tendermint/libs/common"
)
func TestMarshalJSON(t *testing.T) {
b, err := json.Marshal(&ResponseDeliverTx{})
assert.Nil(t, err)
// Do not include empty fields.
assert.False(t, strings.Contains(string(b), "code"))
// include empty fields.
assert.True(t, strings.Contains(string(b), "code"))
r1 := ResponseCheckTx{
Code: 1,
Data: []byte("hello"),
GasWanted: 43,
Tags: []cmn.KVPair{
{Key: []byte("pho"), Value: []byte("bo")},
Events: []Event{
{
Type: "testEvent",
Attributes: []cmn.KVPair{
{Key: []byte("pho"), Value: []byte("bo")},
},
},
},
}
b, err = json.Marshal(&r1)
@@ -82,8 +87,13 @@ func TestWriteReadMessage2(t *testing.T) {
Data: []byte(phrase),
Log: phrase,
GasWanted: 10,
Tags: []cmn.KVPair{
{Key: []byte("abc"), Value: []byte("def")},
Events: []Event{
{
Type: "testEvent",
Attributes: []cmn.KVPair{
{Key: []byte("abc"), Value: []byte("def")},
},
},
},
},
// TODO: add the rest

View File

@@ -40,7 +40,7 @@ func main() {
}
if writeImportTime && !wroteImport {
wroteImport = true
fmt.Fprintf(outFile, "import \"github.com/tendermint/go-wire/data\"\n")
fmt.Fprintf(outFile, "import \"github.com/tendermint/go-amino/data\"\n")
}
if gotPackageLine {

View File

@@ -42,14 +42,12 @@ func (r ResponseQuery) IsErr() bool {
}
//---------------------------------------------------------------------------
// override JSON marshalling so we dont emit defaults (ie. disable omitempty)
// note we need Unmarshal functions too because protobuf had the bright idea
// to marshal int64->string. cool. cool, cool, cool: https://developers.google.com/protocol-buffers/docs/proto3#json
// override JSON marshalling so we emit defaults (ie. disable omitempty)
var (
jsonpbMarshaller = jsonpb.Marshaler{
EnumsAsInts: true,
EmitDefaults: false,
EmitDefaults: true,
}
jsonpbUnmarshaller = jsonpb.Unmarshaler{}
)

File diff suppressed because it is too large Load Diff

View File

@@ -81,8 +81,14 @@ message RequestBeginBlock {
repeated Evidence byzantine_validators = 4 [(gogoproto.nullable)=false];
}
enum CheckTxType {
New = 0;
Recheck = 1;
}
message RequestCheckTx {
bytes tx = 1;
CheckTxType type = 2;
}
message RequestDeliverTx {
@@ -165,7 +171,7 @@ message ResponseQuery {
}
message ResponseBeginBlock {
repeated common.KVPair tags = 1 [(gogoproto.nullable)=false, (gogoproto.jsontag)="tags,omitempty"];
repeated Event events = 1 [(gogoproto.nullable)=false, (gogoproto.jsontag)="events,omitempty"];
}
message ResponseCheckTx {
@@ -175,7 +181,7 @@ message ResponseCheckTx {
string info = 4; // nondeterministic
int64 gas_wanted = 5;
int64 gas_used = 6;
repeated common.KVPair tags = 7 [(gogoproto.nullable)=false, (gogoproto.jsontag)="tags,omitempty"];
repeated Event events = 7 [(gogoproto.nullable)=false, (gogoproto.jsontag)="events,omitempty"];
string codespace = 8;
}
@@ -186,14 +192,14 @@ message ResponseDeliverTx {
string info = 4; // nondeterministic
int64 gas_wanted = 5;
int64 gas_used = 6;
repeated common.KVPair tags = 7 [(gogoproto.nullable)=false, (gogoproto.jsontag)="tags,omitempty"];
repeated Event events = 7 [(gogoproto.nullable)=false, (gogoproto.jsontag)="events,omitempty"];
string codespace = 8;
}
message ResponseEndBlock {
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"];
repeated Event events = 3 [(gogoproto.nullable)=false, (gogoproto.jsontag)="events,omitempty"];
}
message ResponseCommit {
@@ -236,6 +242,11 @@ message LastCommitInfo {
repeated VoteInfo votes = 2 [(gogoproto.nullable)=false];
}
message Event {
string type = 1;
repeated common.KVPair attributes = 2 [(gogoproto.nullable)=false, (gogoproto.jsontag)="attributes,omitempty"];
}
//----------------------------------------
// Blockchain Types
@@ -300,6 +311,22 @@ message ValidatorUpdate {
message VoteInfo {
Validator validator = 1 [(gogoproto.nullable)=false];
bool signed_last_block = 2;
Vote full_vote = 3;
}
// Validator's vote for a block
message Vote {
int32 type = 1;
// TODO: maybe remove height cause it could be derived from BeginBlock?
int64 height = 2;
int32 round = 3;
// TODO: maybe remove block id cause it's already in the header as last_block_id?
BlockID block_id = 4 [json_name = "block_id"];
google.protobuf.Timestamp timestamp = 5 [(gogoproto.nullable)=false, (gogoproto.stdtime)=true];
// TODO: maybe remove validator address cause it's already in VoteInfo.validator?
bytes validator_address = 6 [json_name = "validator_address"];
int32 validator_index = 7 [json_name = "validator_index"];
bytes signature = 8;
}
message PubKey {

View File

@@ -3,19 +3,21 @@
package types
import testing "testing"
import math_rand "math/rand"
import time "time"
import github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto"
import github_com_gogo_protobuf_jsonpb "github.com/gogo/protobuf/jsonpb"
import proto "github.com/gogo/protobuf/proto"
import golang_proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import _ "github.com/gogo/protobuf/gogoproto"
import _ "github.com/golang/protobuf/ptypes/timestamp"
import _ "github.com/tendermint/tendermint/crypto/merkle"
import _ "github.com/tendermint/tendermint/libs/common"
import (
fmt "fmt"
_ "github.com/gogo/protobuf/gogoproto"
github_com_gogo_protobuf_jsonpb "github.com/gogo/protobuf/jsonpb"
github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto"
proto "github.com/gogo/protobuf/proto"
golang_proto "github.com/golang/protobuf/proto"
_ "github.com/golang/protobuf/ptypes/timestamp"
_ "github.com/tendermint/tendermint/crypto/merkle"
_ "github.com/tendermint/tendermint/libs/common"
math "math"
math_rand "math/rand"
testing "testing"
time "time"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
@@ -1703,6 +1705,62 @@ func TestLastCommitInfoMarshalTo(t *testing.T) {
}
}
func TestEventProto(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedEvent(popr, false)
dAtA, err := github_com_gogo_protobuf_proto.Marshal(p)
if err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
msg := &Event{}
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 TestEventMarshalTo(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedEvent(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 := &Event{}
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 TestHeaderProto(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
@@ -2095,6 +2153,62 @@ func TestVoteInfoMarshalTo(t *testing.T) {
}
}
func TestVoteProto(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedVote(popr, false)
dAtA, err := github_com_gogo_protobuf_proto.Marshal(p)
if err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
msg := &Vote{}
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 TestVoteMarshalTo(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedVote(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 := &Vote{}
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 TestPubKeyProto(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
@@ -2747,6 +2861,24 @@ func TestLastCommitInfoJSON(t *testing.T) {
t.Fatalf("seed = %d, %#v !Json Equal %#v", seed, msg, p)
}
}
func TestEventJSON(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedEvent(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 := &Event{}
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 TestHeaderJSON(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
@@ -2873,6 +3005,24 @@ func TestVoteInfoJSON(t *testing.T) {
t.Fatalf("seed = %d, %#v !Json Equal %#v", seed, msg, p)
}
}
func TestVoteJSON(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedVote(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 := &Vote{}
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 TestPubKeyJSON(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
@@ -3749,6 +3899,34 @@ func TestLastCommitInfoProtoCompactText(t *testing.T) {
}
}
func TestEventProtoText(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedEvent(popr, true)
dAtA := github_com_gogo_protobuf_proto.MarshalTextString(p)
msg := &Event{}
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 TestEventProtoCompactText(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedEvent(popr, true)
dAtA := github_com_gogo_protobuf_proto.CompactTextString(p)
msg := &Event{}
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 TestHeaderProtoText(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
@@ -3945,6 +4123,34 @@ func TestVoteInfoProtoCompactText(t *testing.T) {
}
}
func TestVoteProtoText(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedVote(popr, true)
dAtA := github_com_gogo_protobuf_proto.MarshalTextString(p)
msg := &Vote{}
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 TestVoteProtoCompactText(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedVote(popr, true)
dAtA := github_com_gogo_protobuf_proto.CompactTextString(p)
msg := &Vote{}
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 TestPubKeyProtoText(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
@@ -4661,6 +4867,28 @@ func TestLastCommitInfoSize(t *testing.T) {
}
}
func TestEventSize(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedEvent(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 TestHeaderSize(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
@@ -4815,6 +5043,28 @@ func TestVoteInfoSize(t *testing.T) {
}
}
func TestVoteSize(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedVote(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 TestPubKeySize(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))

View File

@@ -7,7 +7,6 @@ clone_folder: c:\go\path\src\github.com\tendermint\tendermint
before_build:
- cmd: set GOPATH=%GOROOT%\path
- cmd: set PATH=%GOPATH%\bin;%PATH%
- cmd: make get_vendor_deps
build_script:
- cmd: make test
test: off

View File

@@ -0,0 +1,49 @@
package behaviour
import (
"github.com/tendermint/tendermint/p2p"
)
// PeerBehaviour is a struct describing a behaviour a peer performed.
// `peerID` identifies the peer and reason characterizes the specific
// behaviour performed by the peer.
type PeerBehaviour struct {
peerID p2p.ID
reason interface{}
}
type badMessage struct {
explanation string
}
// BadMessage returns a badMessage PeerBehaviour.
func BadMessage(peerID p2p.ID, explanation string) PeerBehaviour {
return PeerBehaviour{peerID: peerID, reason: badMessage{explanation}}
}
type messageOutOfOrder struct {
explanation string
}
// MessageOutOfOrder returns a messagOutOfOrder PeerBehaviour.
func MessageOutOfOrder(peerID p2p.ID, explanation string) PeerBehaviour {
return PeerBehaviour{peerID: peerID, reason: messageOutOfOrder{explanation}}
}
type consensusVote struct {
explanation string
}
// ConsensusVote returns a consensusVote PeerBehaviour.
func ConsensusVote(peerID p2p.ID, explanation string) PeerBehaviour {
return PeerBehaviour{peerID: peerID, reason: consensusVote{explanation}}
}
type blockPart struct {
explanation string
}
// BlockPart returns blockPart PeerBehaviour.
func BlockPart(peerID p2p.ID, explanation string) PeerBehaviour {
return PeerBehaviour{peerID: peerID, reason: blockPart{explanation}}
}

84
behaviour/reporter.go Normal file
View File

@@ -0,0 +1,84 @@
package behaviour
import (
"errors"
"sync"
"github.com/tendermint/tendermint/p2p"
)
// Reporter provides an interface for reactors to report the behaviour
// of peers synchronously to other components.
type Reporter interface {
Report(behaviour PeerBehaviour) error
}
// SwitchReporter reports peer behaviour to an internal Switch.
type SwitchReporter struct {
sw *p2p.Switch
}
// NewSwitchReporter return a new SwitchReporter instance which wraps the Switch.
func NewSwitcReporter(sw *p2p.Switch) *SwitchReporter {
return &SwitchReporter{
sw: sw,
}
}
// Report reports the behaviour of a peer to the Switch.
func (spbr *SwitchReporter) Report(behaviour PeerBehaviour) error {
peer := spbr.sw.Peers().Get(behaviour.peerID)
if peer == nil {
return errors.New("peer not found")
}
switch reason := behaviour.reason.(type) {
case consensusVote, blockPart:
spbr.sw.MarkPeerAsGood(peer)
case badMessage:
spbr.sw.StopPeerForError(peer, reason.explanation)
case messageOutOfOrder:
spbr.sw.StopPeerForError(peer, reason.explanation)
default:
return errors.New("unknown reason reported")
}
return nil
}
// MockReporter is a concrete implementation of the Reporter
// interface used in reactor tests to ensure reactors report the correct
// behaviour in manufactured scenarios.
type MockReporter struct {
mtx sync.RWMutex
pb map[p2p.ID][]PeerBehaviour
}
// NewMockReporter returns a Reporter which records all reported
// behaviours in memory.
func NewMockReporter() *MockReporter {
return &MockReporter{
pb: map[p2p.ID][]PeerBehaviour{},
}
}
// Report stores the PeerBehaviour produced by the peer identified by peerID.
func (mpbr *MockReporter) Report(behaviour PeerBehaviour) {
mpbr.mtx.Lock()
defer mpbr.mtx.Unlock()
mpbr.pb[behaviour.peerID] = append(mpbr.pb[behaviour.peerID], behaviour)
}
// GetBehaviours returns all behaviours reported on the peer identified by peerID.
func (mpbr *MockReporter) GetBehaviours(peerID p2p.ID) []PeerBehaviour {
mpbr.mtx.RLock()
defer mpbr.mtx.RUnlock()
if items, ok := mpbr.pb[peerID]; ok {
result := make([]PeerBehaviour, len(items))
copy(result, items)
return result
} else {
return []PeerBehaviour{}
}
}

186
behaviour/reporter_test.go Normal file
View File

@@ -0,0 +1,186 @@
package behaviour_test
import (
"sync"
"testing"
bh "github.com/tendermint/tendermint/behaviour"
"github.com/tendermint/tendermint/p2p"
)
// TestMockReporter tests the MockReporter's ability to store reported
// peer behaviour in memory indexed by the peerID.
func TestMockReporter(t *testing.T) {
var peerID p2p.ID = "MockPeer"
pr := bh.NewMockReporter()
behaviours := pr.GetBehaviours(peerID)
if len(behaviours) != 0 {
t.Error("Expected to have no behaviours reported")
}
badMessage := bh.BadMessage(peerID, "bad message")
pr.Report(badMessage)
behaviours = pr.GetBehaviours(peerID)
if len(behaviours) != 1 {
t.Error("Expected the peer have one reported behaviour")
}
if behaviours[0] != badMessage {
t.Error("Expected Bad Message to have been reported")
}
}
type scriptItem struct {
peerID p2p.ID
behaviour bh.PeerBehaviour
}
// equalBehaviours returns true if a and b contain the same PeerBehaviours with
// the same freequencies and otherwise false.
func equalBehaviours(a []bh.PeerBehaviour, b []bh.PeerBehaviour) bool {
aHistogram := map[bh.PeerBehaviour]int{}
bHistogram := map[bh.PeerBehaviour]int{}
for _, behaviour := range a {
aHistogram[behaviour] += 1
}
for _, behaviour := range b {
bHistogram[behaviour] += 1
}
if len(aHistogram) != len(bHistogram) {
return false
}
for _, behaviour := range a {
if aHistogram[behaviour] != bHistogram[behaviour] {
return false
}
}
for _, behaviour := range b {
if bHistogram[behaviour] != aHistogram[behaviour] {
return false
}
}
return true
}
// TestEqualPeerBehaviours tests that equalBehaviours can tell that two slices
// of peer behaviours can be compared for the behaviours they contain and the
// freequencies that those behaviours occur.
func TestEqualPeerBehaviours(t *testing.T) {
var (
peerID p2p.ID = "MockPeer"
consensusVote = bh.ConsensusVote(peerID, "voted")
blockPart = bh.BlockPart(peerID, "blocked")
equals = []struct {
left []bh.PeerBehaviour
right []bh.PeerBehaviour
}{
// Empty sets
{[]bh.PeerBehaviour{}, []bh.PeerBehaviour{}},
// Single behaviours
{[]bh.PeerBehaviour{consensusVote}, []bh.PeerBehaviour{consensusVote}},
// Equal Frequencies
{[]bh.PeerBehaviour{consensusVote, consensusVote},
[]bh.PeerBehaviour{consensusVote, consensusVote}},
// Equal frequencies different orders
{[]bh.PeerBehaviour{consensusVote, blockPart},
[]bh.PeerBehaviour{blockPart, consensusVote}},
}
unequals = []struct {
left []bh.PeerBehaviour
right []bh.PeerBehaviour
}{
// Comparing empty sets to non empty sets
{[]bh.PeerBehaviour{}, []bh.PeerBehaviour{consensusVote}},
// Different behaviours
{[]bh.PeerBehaviour{consensusVote}, []bh.PeerBehaviour{blockPart}},
// Same behaviour with different frequencies
{[]bh.PeerBehaviour{consensusVote},
[]bh.PeerBehaviour{consensusVote, consensusVote}},
}
)
for _, test := range equals {
if !equalBehaviours(test.left, test.right) {
t.Errorf("Expected %#v and %#v to be equal", test.left, test.right)
}
}
for _, test := range unequals {
if equalBehaviours(test.left, test.right) {
t.Errorf("Expected %#v and %#v to be unequal", test.left, test.right)
}
}
}
// TestPeerBehaviourConcurrency constructs a scenario in which
// multiple goroutines are using the same MockReporter instance.
// This test reproduces the conditions in which MockReporter will
// be used within a Reactor `Receive` method tests to ensure thread safety.
func TestMockPeerBehaviourReporterConcurrency(t *testing.T) {
var (
behaviourScript = []struct {
peerID p2p.ID
behaviours []bh.PeerBehaviour
}{
{"1", []bh.PeerBehaviour{bh.ConsensusVote("1", "")}},
{"2", []bh.PeerBehaviour{bh.ConsensusVote("2", ""), bh.ConsensusVote("2", ""), bh.ConsensusVote("2", "")}},
{"3", []bh.PeerBehaviour{bh.BlockPart("3", ""), bh.ConsensusVote("3", ""), bh.BlockPart("3", ""), bh.ConsensusVote("3", "")}},
{"4", []bh.PeerBehaviour{bh.ConsensusVote("4", ""), bh.ConsensusVote("4", ""), bh.ConsensusVote("4", ""), bh.ConsensusVote("4", "")}},
{"5", []bh.PeerBehaviour{bh.BlockPart("5", ""), bh.ConsensusVote("5", ""), bh.BlockPart("5", ""), bh.ConsensusVote("5", "")}},
}
)
var receiveWg sync.WaitGroup
pr := bh.NewMockReporter()
scriptItems := make(chan scriptItem)
done := make(chan int)
numConsumers := 3
for i := 0; i < numConsumers; i++ {
receiveWg.Add(1)
go func() {
defer receiveWg.Done()
for {
select {
case pb := <-scriptItems:
pr.Report(pb.behaviour)
case <-done:
return
}
}
}()
}
var sendingWg sync.WaitGroup
sendingWg.Add(1)
go func() {
defer sendingWg.Done()
for _, item := range behaviourScript {
for _, reason := range item.behaviours {
scriptItems <- scriptItem{item.peerID, reason}
}
}
}()
sendingWg.Wait()
for i := 0; i < numConsumers; i++ {
done <- 1
}
receiveWg.Wait()
for _, items := range behaviourScript {
reported := pr.GetBehaviours(items.peerID)
if !equalBehaviours(reported, items.behaviours) {
t.Errorf("Expected peer %s to have behaved \nExpected: %#v \nGot %#v \n",
items.peerID, items.behaviours, reported)
}
}
}

View File

@@ -1,29 +0,0 @@
package benchmarks
import (
"sync/atomic"
"testing"
"unsafe"
)
func BenchmarkAtomicUintPtr(b *testing.B) {
b.StopTimer()
pointers := make([]uintptr, 1000)
b.Log(unsafe.Sizeof(pointers[0]))
b.StartTimer()
for j := 0; j < b.N; j++ {
atomic.StoreUintptr(&pointers[j%1000], uintptr(j))
}
}
func BenchmarkAtomicPointer(b *testing.B) {
b.StopTimer()
pointers := make([]unsafe.Pointer, 1000)
b.Log(unsafe.Sizeof(pointers[0]))
b.StartTimer()
for j := 0; j < b.N; j++ {
atomic.StorePointer(&pointers[j%1000], unsafe.Pointer(uintptr(j)))
}
}

View File

@@ -1,2 +0,0 @@
data

View File

@@ -1,80 +0,0 @@
#!/bin/bash
DATA=$GOPATH/src/github.com/tendermint/tendermint/benchmarks/blockchain/data
if [ ! -d $DATA ]; then
echo "no data found, generating a chain... (this only has to happen once)"
tendermint init --home $DATA
cp $DATA/config.toml $DATA/config2.toml
echo "
[consensus]
timeout_commit = 0
" >> $DATA/config.toml
echo "starting node"
tendermint node \
--home $DATA \
--proxy_app kvstore \
--p2p.laddr tcp://127.0.0.1:56656 \
--rpc.laddr tcp://127.0.0.1:56657 \
--log_level error &
echo "making blocks for 60s"
sleep 60
mv $DATA/config2.toml $DATA/config.toml
kill %1
echo "done generating chain."
fi
# validator node
HOME1=$TMPDIR$RANDOM$RANDOM
cp -R $DATA $HOME1
echo "starting validator node"
tendermint node \
--home $HOME1 \
--proxy_app kvstore \
--p2p.laddr tcp://127.0.0.1:56656 \
--rpc.laddr tcp://127.0.0.1:56657 \
--log_level error &
sleep 1
# downloader node
HOME2=$TMPDIR$RANDOM$RANDOM
tendermint init --home $HOME2
cp $HOME1/genesis.json $HOME2
printf "starting downloader node"
tendermint node \
--home $HOME2 \
--proxy_app kvstore \
--p2p.laddr tcp://127.0.0.1:56666 \
--rpc.laddr tcp://127.0.0.1:56667 \
--p2p.persistent_peers 127.0.0.1:56656 \
--log_level error &
# wait for node to start up so we only count time where we are actually syncing
sleep 0.5
while curl localhost:56667/status 2> /dev/null | grep "\"latest_block_height\": 0," > /dev/null
do
printf '.'
sleep 0.2
done
echo
echo "syncing blockchain for 10s"
for i in {1..10}
do
sleep 1
HEIGHT="$(curl localhost:56667/status 2> /dev/null \
| grep 'latest_block_height' \
| grep -o ' [0-9]*' \
| xargs)"
let 'RATE = HEIGHT / i'
echo "height: $HEIGHT, blocks/sec: $RATE"
done
kill %1
kill %2
rm -rf $HOME1 $HOME2

View File

@@ -1,19 +0,0 @@
package benchmarks
import (
"testing"
)
func BenchmarkChanMakeClose(b *testing.B) {
b.StopTimer()
b.StartTimer()
for j := 0; j < b.N; j++ {
foo := make(chan struct{})
close(foo)
something, ok := <-foo
if ok {
b.Error(something, ok)
}
}
}

View File

@@ -1,123 +0,0 @@
package benchmarks
import (
"testing"
"time"
amino "github.com/tendermint/go-amino"
proto "github.com/tendermint/tendermint/benchmarks/proto"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/p2p"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
)
func testNodeInfo(id p2p.ID) p2p.DefaultNodeInfo {
return p2p.DefaultNodeInfo{
ProtocolVersion: p2p.ProtocolVersion{1, 2, 3},
ID_: id,
Moniker: "SOMENAME",
Network: "SOMENAME",
ListenAddr: "SOMEADDR",
Version: "SOMEVER",
Other: p2p.DefaultNodeInfoOther{
TxIndex: "on",
RPCAddress: "0.0.0.0:26657",
},
}
}
func BenchmarkEncodeStatusWire(b *testing.B) {
b.StopTimer()
cdc := amino.NewCodec()
ctypes.RegisterAmino(cdc)
nodeKey := p2p.NodeKey{PrivKey: ed25519.GenPrivKey()}
status := &ctypes.ResultStatus{
NodeInfo: testNodeInfo(nodeKey.ID()),
SyncInfo: ctypes.SyncInfo{
LatestBlockHash: []byte("SOMEBYTES"),
LatestBlockHeight: 123,
LatestBlockTime: time.Unix(0, 1234),
},
ValidatorInfo: ctypes.ValidatorInfo{
PubKey: nodeKey.PubKey(),
},
}
b.StartTimer()
counter := 0
for i := 0; i < b.N; i++ {
jsonBytes, err := cdc.MarshalJSON(status)
if err != nil {
panic(err)
}
counter += len(jsonBytes)
}
}
func BenchmarkEncodeNodeInfoWire(b *testing.B) {
b.StopTimer()
cdc := amino.NewCodec()
ctypes.RegisterAmino(cdc)
nodeKey := p2p.NodeKey{PrivKey: ed25519.GenPrivKey()}
nodeInfo := testNodeInfo(nodeKey.ID())
b.StartTimer()
counter := 0
for i := 0; i < b.N; i++ {
jsonBytes, err := cdc.MarshalJSON(nodeInfo)
if err != nil {
panic(err)
}
counter += len(jsonBytes)
}
}
func BenchmarkEncodeNodeInfoBinary(b *testing.B) {
b.StopTimer()
cdc := amino.NewCodec()
ctypes.RegisterAmino(cdc)
nodeKey := p2p.NodeKey{PrivKey: ed25519.GenPrivKey()}
nodeInfo := testNodeInfo(nodeKey.ID())
b.StartTimer()
counter := 0
for i := 0; i < b.N; i++ {
jsonBytes := cdc.MustMarshalBinaryBare(nodeInfo)
counter += len(jsonBytes)
}
}
func BenchmarkEncodeNodeInfoProto(b *testing.B) {
b.StopTimer()
nodeKey := p2p.NodeKey{PrivKey: ed25519.GenPrivKey()}
nodeID := string(nodeKey.ID())
someName := "SOMENAME"
someAddr := "SOMEADDR"
someVer := "SOMEVER"
someString := "SOMESTRING"
otherString := "OTHERSTRING"
nodeInfo := proto.NodeInfo{
Id: &proto.ID{Id: &nodeID},
Moniker: &someName,
Network: &someName,
ListenAddr: &someAddr,
Version: &someVer,
Other: []string{someString, otherString},
}
b.StartTimer()
counter := 0
for i := 0; i < b.N; i++ {
bytes, err := nodeInfo.Marshal()
if err != nil {
b.Fatal(err)
return
}
//jsonBytes := wire.JSONBytes(nodeInfo)
counter += len(bytes)
}
}

View File

@@ -1 +0,0 @@
package benchmarks

View File

@@ -1,35 +0,0 @@
package benchmarks
import (
"testing"
cmn "github.com/tendermint/tendermint/libs/common"
)
func BenchmarkSomething(b *testing.B) {
b.StopTimer()
numItems := 100000
numChecks := 100000
keys := make([]string, numItems)
for i := 0; i < numItems; i++ {
keys[i] = cmn.RandStr(100)
}
txs := make([]string, numChecks)
for i := 0; i < numChecks; i++ {
txs[i] = cmn.RandStr(100)
}
b.StartTimer()
counter := 0
for j := 0; j < b.N; j++ {
foo := make(map[string]string)
for _, key := range keys {
foo[key] = key
}
for _, tx := range txs {
if _, ok := foo[tx]; ok {
counter++
}
}
}
}

View File

@@ -1,33 +0,0 @@
package benchmarks
import (
"os"
"testing"
cmn "github.com/tendermint/tendermint/libs/common"
)
func BenchmarkFileWrite(b *testing.B) {
b.StopTimer()
file, err := os.OpenFile("benchmark_file_write.out",
os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
if err != nil {
b.Error(err)
}
testString := cmn.RandStr(200) + "\n"
b.StartTimer()
for i := 0; i < b.N; i++ {
_, err := file.Write([]byte(testString))
if err != nil {
b.Error(err)
}
}
if err := file.Close(); err != nil {
b.Error(err)
}
if err := os.Remove("benchmark_file_write.out"); err != nil {
b.Error(err)
}
}

View File

@@ -1,2 +0,0 @@
Doing some protobuf tests here.
Using gogoprotobuf.

File diff suppressed because it is too large Load Diff

View File

@@ -1,29 +0,0 @@
message ResultStatus {
optional NodeInfo nodeInfo = 1;
required PubKey pubKey = 2;
required bytes latestBlockHash = 3;
required int64 latestBlockHeight = 4;
required int64 latestBlocktime = 5;
}
message NodeInfo {
required ID id = 1;
required string moniker = 2;
required string network = 3;
required string remoteAddr = 4;
required string listenAddr = 5;
required string version = 6;
repeated string other = 7;
}
message ID {
required string id = 1;
}
message PubKey {
optional PubKeyEd25519 ed25519 = 1;
}
message PubKeyEd25519 {
required bytes bytes = 1;
}

View File

@@ -1,47 +0,0 @@
package main
import (
"context"
"encoding/binary"
"fmt"
"time"
cmn "github.com/tendermint/tendermint/libs/common"
rpcclient "github.com/tendermint/tendermint/rpc/lib/client"
)
func main() {
wsc := rpcclient.NewWSClient("127.0.0.1:26657", "/websocket")
err := wsc.Start()
if err != nil {
cmn.Exit(err.Error())
}
defer wsc.Stop()
// Read a bunch of responses
go func() {
for {
_, ok := <-wsc.ResponsesCh
if !ok {
break
}
//fmt.Println("Received response", string(wire.JSONBytes(res)))
}
}()
// Make a bunch of requests
buf := make([]byte, 32)
for i := 0; ; i++ {
binary.BigEndian.PutUint64(buf, uint64(i))
//txBytes := hex.EncodeToString(buf[:n])
fmt.Print(".")
err = wsc.Call(context.TODO(), "broadcast_tx", map[string]interface{}{"tx": buf[:8]})
if err != nil {
cmn.Exit(err.Error())
}
if i%1000 == 0 {
fmt.Println(i)
}
time.Sleep(time.Microsecond * 1000)
}
}

View File

@@ -1,4 +1,4 @@
package blockchain
package v0
import (
amino "github.com/tendermint/go-amino"

View File

@@ -1,4 +1,4 @@
package blockchain
package v0
import (
"errors"
@@ -59,6 +59,7 @@ var peerTimeout = 15 * time.Second // not const so we can override with tests
are not at peer limits, we can probably switch to consensus reactor
*/
// BlockPool keeps track of the fast sync peers, block requests and block responses.
type BlockPool struct {
cmn.BaseService
startTime time.Time
@@ -111,17 +112,18 @@ func (pool *BlockPool) makeRequestersRoutine() {
}
_, numPending, lenRequesters := pool.GetStatus()
if numPending >= maxPendingRequests {
switch {
case numPending >= maxPendingRequests:
// sleep for a bit.
time.Sleep(requestIntervalMS * time.Millisecond)
// check for timed out peers
pool.removeTimedoutPeers()
} else if lenRequesters >= maxTotalRequesters {
case lenRequesters >= maxTotalRequesters:
// sleep for a bit.
time.Sleep(requestIntervalMS * time.Millisecond)
// check for timed out peers
pool.removeTimedoutPeers()
} else {
default:
// request for more blocks.
pool.makeNextRequester()
}
@@ -184,6 +186,7 @@ func (pool *BlockPool) IsCaughtUp() bool {
return isCaughtUp
}
// PeekTwoBlocks returns blocks at pool.height and pool.height+1.
// We need to see the second block's Commit to validate the first block.
// So we peek two blocks at a time.
// The caller will verify the commit.
@@ -200,7 +203,7 @@ func (pool *BlockPool) PeekTwoBlocks() (first *types.Block, second *types.Block)
return
}
// Pop the first block at pool.height
// PopRequest pops the first block at pool.height.
// It must have been validated by 'second'.Commit from PeekTwoBlocks().
func (pool *BlockPool) PopRequest() {
pool.mtx.Lock()
@@ -220,7 +223,7 @@ func (pool *BlockPool) PopRequest() {
}
}
// Invalidates the block at pool.height,
// RedoRequest invalidates the block at pool.height,
// Remove the peer and redo request from others.
// Returns the ID of the removed peer.
func (pool *BlockPool) RedoRequest(height int64) p2p.ID {
@@ -236,6 +239,7 @@ func (pool *BlockPool) RedoRequest(height int64) p2p.ID {
return peerID
}
// AddBlock validates that the block comes from the peer it was expected from and calls the requester to store it.
// TODO: ensure that blocks come in order for each peer.
func (pool *BlockPool) AddBlock(peerID p2p.ID, block *types.Block, blockSize int) {
pool.mtx.Lock()
@@ -418,14 +422,14 @@ func (pool *BlockPool) debug() string {
//-------------------------------------
type bpPeer struct {
didTimeout bool
numPending int32
height int64
pool *BlockPool
id p2p.ID
recvMonitor *flow.Monitor
height int64
numPending int32
timeout *time.Timer
didTimeout bool
timeout *time.Timer
logger log.Logger
}
@@ -565,9 +569,9 @@ func (bpr *bpRequester) reset() {
// Tells bpRequester to pick another peer and try again.
// NOTE: Nonblocking, and does nothing if another redo
// was already requested.
func (bpr *bpRequester) redo(peerId p2p.ID) {
func (bpr *bpRequester) redo(peerID p2p.ID) {
select {
case bpr.redoCh <- peerId:
case bpr.redoCh <- peerID:
default:
}
}
@@ -622,8 +626,8 @@ OUTER_LOOP:
}
}
//-------------------------------------
// BlockRequest stores a block request identified by the block Height and the PeerID responsible for
// delivering the block
type BlockRequest struct {
Height int64
PeerID p2p.ID

View File

@@ -1,4 +1,4 @@
package blockchain
package v0
import (
"fmt"

View File

@@ -1,4 +1,4 @@
package blockchain
package v0
import (
"errors"
@@ -11,6 +11,7 @@ import (
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/p2p"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/store"
"github.com/tendermint/tendermint/types"
)
@@ -60,7 +61,7 @@ type BlockchainReactor struct {
initialState sm.State
blockExec *sm.BlockExecutor
store *BlockStore
store *store.BlockStore
pool *BlockPool
fastSync bool
@@ -69,7 +70,7 @@ type BlockchainReactor struct {
}
// NewBlockchainReactor returns new reactor instance.
func NewBlockchainReactor(state sm.State, blockExec *sm.BlockExecutor, store *BlockStore,
func NewBlockchainReactor(state sm.State, blockExec *sm.BlockExecutor, store *store.BlockStore,
fastSync bool) *BlockchainReactor {
if state.LastBlockHeight != store.Height() {
@@ -140,9 +141,9 @@ func (bcR *BlockchainReactor) GetChannels() []*p2p.ChannelDescriptor {
// AddPeer implements Reactor by sending our state to peer.
func (bcR *BlockchainReactor) AddPeer(peer p2p.Peer) {
msgBytes := cdc.MustMarshalBinaryBare(&bcStatusResponseMessage{bcR.store.Height()})
if !peer.Send(BlockchainChannel, msgBytes) {
// doing nothing, will try later in `poolRoutine`
}
peer.Send(BlockchainChannel, msgBytes)
// it's OK if send fails. will try later in poolRoutine
// peer is added to the pool once we receive the first
// bcStatusResponseMessage from the peer and call pool.SetPeerHeight
}
@@ -190,18 +191,13 @@ func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
switch msg := msg.(type) {
case *bcBlockRequestMessage:
if queued := bcR.respondToPeer(msg, src); !queued {
// Unfortunately not queued since the queue is full.
}
bcR.respondToPeer(msg, src)
case *bcBlockResponseMessage:
bcR.pool.AddBlock(src.ID(), msg.Block, len(msgBytes))
case *bcStatusRequestMessage:
// Send peer our state.
msgBytes := cdc.MustMarshalBinaryBare(&bcStatusResponseMessage{bcR.store.Height()})
queued := src.TrySend(BlockchainChannel, msgBytes)
if !queued {
// sorry
}
src.TrySend(BlockchainChannel, msgBytes)
case *bcStatusResponseMessage:
// Got a peer status. Unverified.
bcR.pool.SetPeerHeight(src.ID(), msg.Height)
@@ -273,9 +269,10 @@ FOR_LOOP:
conR, ok := bcR.Switch.Reactor("CONSENSUS").(consensusReactor)
if ok {
conR.SwitchToConsensus(state, blocksSynced)
} else {
// should only happen during testing
}
// else {
// should only happen during testing
// }
break FOR_LOOP
}
@@ -378,6 +375,7 @@ type BlockchainMessage interface {
ValidateBasic() error
}
// RegisterBlockchainMessages registers the fast sync messages for amino encoding.
func RegisterBlockchainMessages(cdc *amino.Codec) {
cdc.RegisterInterface((*BlockchainMessage)(nil), nil)
cdc.RegisterConcrete(&bcBlockRequestMessage{}, "tendermint/blockchain/BlockRequest", nil)
@@ -425,8 +423,8 @@ func (m *bcNoBlockResponseMessage) ValidateBasic() error {
return nil
}
func (brm *bcNoBlockResponseMessage) String() string {
return fmt.Sprintf("[bcNoBlockResponseMessage %d]", brm.Height)
func (m *bcNoBlockResponseMessage) String() string {
return fmt.Sprintf("[bcNoBlockResponseMessage %d]", m.Height)
}
//-------------------------------------

View File

@@ -1,4 +1,4 @@
package blockchain
package v0
import (
"os"
@@ -6,12 +6,13 @@ import (
"testing"
"time"
"github.com/pkg/errors"
"github.com/tendermint/tendermint/store"
"github.com/stretchr/testify/assert"
abci "github.com/tendermint/tendermint/abci/types"
cfg "github.com/tendermint/tendermint/config"
cmn "github.com/tendermint/tendermint/libs/common"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/mock"
"github.com/tendermint/tendermint/p2p"
@@ -19,6 +20,7 @@ import (
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time"
dbm "github.com/tendermint/tm-db"
)
var config *cfg.Config
@@ -43,24 +45,6 @@ func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.G
}, privValidators
}
func makeVote(header *types.Header, blockID types.BlockID, valset *types.ValidatorSet, privVal types.PrivValidator) *types.Vote {
addr := privVal.GetPubKey().Address()
idx, _ := valset.GetByAddress(addr)
vote := &types.Vote{
ValidatorAddress: addr,
ValidatorIndex: idx,
Height: header.Height,
Round: 1,
Timestamp: tmtime.Now(),
Type: types.PrecommitType,
BlockID: blockID,
}
privVal.SignVote(header.ChainID, vote)
return vote
}
type BlockchainReactorPair struct {
reactor *BlockchainReactor
app proxy.AppConns
@@ -76,16 +60,16 @@ func newBlockchainReactor(logger log.Logger, genDoc *types.GenesisDoc, privVals
proxyApp := proxy.NewAppConns(cc)
err := proxyApp.Start()
if err != nil {
panic(cmn.ErrorWrap(err, "error start app"))
panic(errors.Wrap(err, "error start app"))
}
blockDB := dbm.NewMemDB()
stateDB := dbm.NewMemDB()
blockStore := NewBlockStore(blockDB)
blockStore := store.NewBlockStore(blockDB)
state, err := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
if err != nil {
panic(cmn.ErrorWrap(err, "error constructing state from genesis file"))
panic(errors.Wrap(err, "error constructing state from genesis file"))
}
// Make the BlockchainReactor itself.
@@ -104,18 +88,22 @@ func newBlockchainReactor(logger log.Logger, genDoc *types.GenesisDoc, privVals
lastBlockMeta := blockStore.LoadBlockMeta(blockHeight - 1)
lastBlock := blockStore.LoadBlock(blockHeight - 1)
vote := makeVote(&lastBlock.Header, lastBlockMeta.BlockID, state.Validators, privVals[0]).CommitSig()
lastCommit = types.NewCommit(lastBlockMeta.BlockID, []*types.CommitSig{vote})
vote, err := types.MakeVote(lastBlock.Header.Height, lastBlockMeta.BlockID, state.Validators, privVals[0], lastBlock.Header.ChainID)
if err != nil {
panic(err)
}
voteCommitSig := vote.CommitSig()
lastCommit = types.NewCommit(lastBlockMeta.BlockID, []*types.CommitSig{voteCommitSig})
}
thisBlock := makeBlock(blockHeight, state, lastCommit)
thisParts := thisBlock.MakePartSet(types.BlockPartSizeBytes)
blockID := types.BlockID{thisBlock.Hash(), thisParts.Header()}
blockID := types.BlockID{Hash: thisBlock.Hash(), PartsHeader: thisParts.Header()}
state, err = blockExec.ApplyBlock(state, blockID, thisBlock)
if err != nil {
panic(cmn.ErrorWrap(err, "error apply block"))
panic(errors.Wrap(err, "error apply block"))
}
blockStore.SaveBlock(thisBlock, thisParts, lastCommit)
@@ -258,6 +246,86 @@ func TestBadBlockStopsPeer(t *testing.T) {
assert.True(t, lastReactorPair.reactor.Switch.Peers().Size() < len(reactorPairs)-1)
}
func TestBcBlockRequestMessageValidateBasic(t *testing.T) {
testCases := []struct {
testName string
requestHeight int64
expectErr bool
}{
{"Valid Request Message", 0, false},
{"Valid Request Message", 1, false},
{"Invalid Request Message", -1, true},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.testName, func(t *testing.T) {
request := bcBlockRequestMessage{Height: tc.requestHeight}
assert.Equal(t, tc.expectErr, request.ValidateBasic() != nil, "Validate Basic had an unexpected result")
})
}
}
func TestBcNoBlockResponseMessageValidateBasic(t *testing.T) {
testCases := []struct {
testName string
nonResponseHeight int64
expectErr bool
}{
{"Valid Non-Response Message", 0, false},
{"Valid Non-Response Message", 1, false},
{"Invalid Non-Response Message", -1, true},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.testName, func(t *testing.T) {
nonResponse := bcNoBlockResponseMessage{Height: tc.nonResponseHeight}
assert.Equal(t, tc.expectErr, nonResponse.ValidateBasic() != nil, "Validate Basic had an unexpected result")
})
}
}
func TestBcStatusRequestMessageValidateBasic(t *testing.T) {
testCases := []struct {
testName string
requestHeight int64
expectErr bool
}{
{"Valid Request Message", 0, false},
{"Valid Request Message", 1, false},
{"Invalid Request Message", -1, true},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.testName, func(t *testing.T) {
request := bcStatusRequestMessage{Height: tc.requestHeight}
assert.Equal(t, tc.expectErr, request.ValidateBasic() != nil, "Validate Basic had an unexpected result")
})
}
}
func TestBcStatusResponseMessageValidateBasic(t *testing.T) {
testCases := []struct {
testName string
responseHeight int64
expectErr bool
}{
{"Valid Response Message", 0, false},
{"Valid Response Message", 1, false},
{"Invalid Response Message", -1, true},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.testName, func(t *testing.T) {
response := bcStatusResponseMessage{Height: tc.responseHeight}
assert.Equal(t, tc.expectErr, response.ValidateBasic() != nil, "Validate Basic had an unexpected result")
})
}
}
//----------------------------------------------
// utility funcs
@@ -291,11 +359,11 @@ func (app *testApp) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock {
return abci.ResponseEndBlock{}
}
func (app *testApp) DeliverTx(tx []byte) abci.ResponseDeliverTx {
return abci.ResponseDeliverTx{Tags: []cmn.KVPair{}}
func (app *testApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx {
return abci.ResponseDeliverTx{Events: []abci.Event{}}
}
func (app *testApp) CheckTx(tx []byte) abci.ResponseCheckTx {
func (app *testApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx {
return abci.ResponseCheckTx{}
}

13
blockchain/v1/codec.go Normal file
View File

@@ -0,0 +1,13 @@
package v1
import (
amino "github.com/tendermint/go-amino"
"github.com/tendermint/tendermint/types"
)
var cdc = amino.NewCodec()
func init() {
RegisterBlockchainMessages(cdc)
types.RegisterBlockAmino(cdc)
}

209
blockchain/v1/peer.go Normal file
View File

@@ -0,0 +1,209 @@
package v1
import (
"fmt"
"math"
"time"
flow "github.com/tendermint/tendermint/libs/flowrate"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/types"
)
//--------
// Peer
// BpPeerParams stores the peer parameters that are used when creating a peer.
type BpPeerParams struct {
timeout time.Duration
minRecvRate int64
sampleRate time.Duration
windowSize time.Duration
}
// BpPeer is the datastructure associated with a fast sync peer.
type BpPeer struct {
logger log.Logger
ID p2p.ID
Height int64 // the peer reported height
NumPendingBlockRequests int // number of requests still waiting for block responses
blocks map[int64]*types.Block // blocks received or expected to be received from this peer
blockResponseTimer *time.Timer
recvMonitor *flow.Monitor
params *BpPeerParams // parameters for timer and monitor
onErr func(err error, peerID p2p.ID) // function to call on error
}
// NewBpPeer creates a new peer.
func NewBpPeer(
peerID p2p.ID, height int64, onErr func(err error, peerID p2p.ID), params *BpPeerParams) *BpPeer {
if params == nil {
params = BpPeerDefaultParams()
}
return &BpPeer{
ID: peerID,
Height: height,
blocks: make(map[int64]*types.Block, maxRequestsPerPeer),
logger: log.NewNopLogger(),
onErr: onErr,
params: params,
}
}
// String returns a string representation of a peer.
func (peer *BpPeer) String() string {
return fmt.Sprintf("peer: %v height: %v pending: %v", peer.ID, peer.Height, peer.NumPendingBlockRequests)
}
// SetLogger sets the logger of the peer.
func (peer *BpPeer) SetLogger(l log.Logger) {
peer.logger = l
}
// Cleanup performs cleanup of the peer, removes blocks, requests, stops timer and monitor.
func (peer *BpPeer) Cleanup() {
if peer.blockResponseTimer != nil {
peer.blockResponseTimer.Stop()
}
if peer.NumPendingBlockRequests != 0 {
peer.logger.Info("peer with pending requests is being cleaned", "peer", peer.ID)
}
if len(peer.blocks)-peer.NumPendingBlockRequests != 0 {
peer.logger.Info("peer with pending blocks is being cleaned", "peer", peer.ID)
}
for h := range peer.blocks {
delete(peer.blocks, h)
}
peer.NumPendingBlockRequests = 0
peer.recvMonitor = nil
}
// BlockAtHeight returns the block at a given height if available and errMissingBlock otherwise.
func (peer *BpPeer) BlockAtHeight(height int64) (*types.Block, error) {
block, ok := peer.blocks[height]
if !ok {
return nil, errMissingBlock
}
if block == nil {
return nil, errMissingBlock
}
return peer.blocks[height], nil
}
// AddBlock adds a block at peer level. Block must be non-nil and recvSize a positive integer
// The peer must have a pending request for this block.
func (peer *BpPeer) AddBlock(block *types.Block, recvSize int) error {
if block == nil || recvSize < 0 {
panic("bad parameters")
}
existingBlock, ok := peer.blocks[block.Height]
if !ok {
peer.logger.Error("unsolicited block", "blockHeight", block.Height, "peer", peer.ID)
return errMissingBlock
}
if existingBlock != nil {
peer.logger.Error("already have a block for height", "height", block.Height)
return errDuplicateBlock
}
if peer.NumPendingBlockRequests == 0 {
panic("peer does not have pending requests")
}
peer.blocks[block.Height] = block
peer.NumPendingBlockRequests--
if peer.NumPendingBlockRequests == 0 {
peer.stopMonitor()
peer.stopBlockResponseTimer()
} else {
peer.recvMonitor.Update(recvSize)
peer.resetBlockResponseTimer()
}
return nil
}
// RemoveBlock removes the block of given height
func (peer *BpPeer) RemoveBlock(height int64) {
delete(peer.blocks, height)
}
// RequestSent records that a request was sent, and starts the peer timer and monitor if needed.
func (peer *BpPeer) RequestSent(height int64) {
peer.blocks[height] = nil
if peer.NumPendingBlockRequests == 0 {
peer.startMonitor()
peer.resetBlockResponseTimer()
}
peer.NumPendingBlockRequests++
}
// CheckRate verifies that the response rate of the peer is acceptable (higher than the minimum allowed).
func (peer *BpPeer) CheckRate() error {
if peer.NumPendingBlockRequests == 0 {
return nil
}
curRate := peer.recvMonitor.Status().CurRate
// curRate can be 0 on start
if curRate != 0 && curRate < peer.params.minRecvRate {
err := errSlowPeer
peer.logger.Error("SendTimeout", "peer", peer,
"reason", err,
"curRate", fmt.Sprintf("%d KB/s", curRate/1024),
"minRate", fmt.Sprintf("%d KB/s", peer.params.minRecvRate/1024))
return err
}
return nil
}
func (peer *BpPeer) onTimeout() {
peer.onErr(errNoPeerResponse, peer.ID)
}
func (peer *BpPeer) stopMonitor() {
peer.recvMonitor.Done()
peer.recvMonitor = nil
}
func (peer *BpPeer) startMonitor() {
peer.recvMonitor = flow.New(peer.params.sampleRate, peer.params.windowSize)
initialValue := float64(peer.params.minRecvRate) * math.E
peer.recvMonitor.SetREMA(initialValue)
}
func (peer *BpPeer) resetBlockResponseTimer() {
if peer.blockResponseTimer == nil {
peer.blockResponseTimer = time.AfterFunc(peer.params.timeout, peer.onTimeout)
} else {
peer.blockResponseTimer.Reset(peer.params.timeout)
}
}
func (peer *BpPeer) stopBlockResponseTimer() bool {
if peer.blockResponseTimer == nil {
return false
}
return peer.blockResponseTimer.Stop()
}
// BpPeerDefaultParams returns the default peer parameters.
func BpPeerDefaultParams() *BpPeerParams {
return &BpPeerParams{
// Timeout for a peer to respond to a block request.
timeout: 15 * time.Second,
// Minimum recv rate to ensure we're receiving blocks from a peer fast
// enough. If a peer is not sending data at at least that rate, we
// consider them to have timedout and we disconnect.
//
// Assuming a DSL connection (not a good choice) 128 Kbps (upload) ~ 15 KB/s,
// sending data across atlantic ~ 7.5 KB/s.
minRecvRate: int64(7680),
// Monitor parameters
sampleRate: time.Second,
windowSize: 40 * time.Second,
}
}

280
blockchain/v1/peer_test.go Normal file
View File

@@ -0,0 +1,280 @@
package v1
import (
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/types"
)
func TestPeerMonitor(t *testing.T) {
peer := NewBpPeer(
p2p.ID(cmn.RandStr(12)), 10,
func(err error, _ p2p.ID) {},
nil)
peer.SetLogger(log.TestingLogger())
peer.startMonitor()
assert.NotNil(t, peer.recvMonitor)
peer.stopMonitor()
assert.Nil(t, peer.recvMonitor)
}
func TestPeerResetBlockResponseTimer(t *testing.T) {
var (
numErrFuncCalls int // number of calls to the errFunc
lastErr error // last generated error
peerTestMtx sync.Mutex // modifications of ^^ variables are also done from timer handler goroutine
)
params := &BpPeerParams{timeout: 2 * time.Millisecond}
peer := NewBpPeer(
p2p.ID(cmn.RandStr(12)), 10,
func(err error, _ p2p.ID) {
peerTestMtx.Lock()
defer peerTestMtx.Unlock()
lastErr = err
numErrFuncCalls++
},
params)
peer.SetLogger(log.TestingLogger())
checkByStoppingPeerTimer(t, peer, false)
// initial reset call with peer having a nil timer
peer.resetBlockResponseTimer()
assert.NotNil(t, peer.blockResponseTimer)
// make sure timer is running and stop it
checkByStoppingPeerTimer(t, peer, true)
// reset with running timer
peer.resetBlockResponseTimer()
time.Sleep(time.Millisecond)
peer.resetBlockResponseTimer()
assert.NotNil(t, peer.blockResponseTimer)
// let the timer expire and ...
time.Sleep(3 * time.Millisecond)
// ... check timer is not running
checkByStoppingPeerTimer(t, peer, false)
peerTestMtx.Lock()
// ... check errNoPeerResponse has been sent
assert.Equal(t, 1, numErrFuncCalls)
assert.Equal(t, lastErr, errNoPeerResponse)
peerTestMtx.Unlock()
}
func TestPeerRequestSent(t *testing.T) {
params := &BpPeerParams{timeout: 2 * time.Millisecond}
peer := NewBpPeer(
p2p.ID(cmn.RandStr(12)), 10,
func(err error, _ p2p.ID) {},
params)
peer.SetLogger(log.TestingLogger())
peer.RequestSent(1)
assert.NotNil(t, peer.recvMonitor)
assert.NotNil(t, peer.blockResponseTimer)
assert.Equal(t, 1, peer.NumPendingBlockRequests)
peer.RequestSent(1)
assert.NotNil(t, peer.recvMonitor)
assert.NotNil(t, peer.blockResponseTimer)
assert.Equal(t, 2, peer.NumPendingBlockRequests)
}
func TestPeerGetAndRemoveBlock(t *testing.T) {
peer := NewBpPeer(
p2p.ID(cmn.RandStr(12)), 100,
func(err error, _ p2p.ID) {},
nil)
// Change peer height
peer.Height = int64(10)
assert.Equal(t, int64(10), peer.Height)
// request some blocks and receive few of them
for i := 1; i <= 10; i++ {
peer.RequestSent(int64(i))
if i > 5 {
// only receive blocks 1..5
continue
}
_ = peer.AddBlock(makeSmallBlock(i), 10)
}
tests := []struct {
name string
height int64
wantErr error
blockPresent bool
}{
{"no request", 100, errMissingBlock, false},
{"no block", 6, errMissingBlock, false},
{"block 1 present", 1, nil, true},
{"block max present", 5, nil, true},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
// try to get the block
b, err := peer.BlockAtHeight(tt.height)
assert.Equal(t, tt.wantErr, err)
assert.Equal(t, tt.blockPresent, b != nil)
// remove the block
peer.RemoveBlock(tt.height)
_, err = peer.BlockAtHeight(tt.height)
assert.Equal(t, errMissingBlock, err)
})
}
}
func TestPeerAddBlock(t *testing.T) {
peer := NewBpPeer(
p2p.ID(cmn.RandStr(12)), 100,
func(err error, _ p2p.ID) {},
nil)
// request some blocks, receive one
for i := 1; i <= 10; i++ {
peer.RequestSent(int64(i))
if i == 5 {
// receive block 5
_ = peer.AddBlock(makeSmallBlock(i), 10)
}
}
tests := []struct {
name string
height int64
wantErr error
blockPresent bool
}{
{"no request", 50, errMissingBlock, false},
{"duplicate block", 5, errDuplicateBlock, true},
{"block 1 successfully received", 1, nil, true},
{"block max successfully received", 10, nil, true},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
// try to get the block
err := peer.AddBlock(makeSmallBlock(int(tt.height)), 10)
assert.Equal(t, tt.wantErr, err)
_, err = peer.BlockAtHeight(tt.height)
assert.Equal(t, tt.blockPresent, err == nil)
})
}
}
func TestPeerOnErrFuncCalledDueToExpiration(t *testing.T) {
params := &BpPeerParams{timeout: 2 * time.Millisecond}
var (
numErrFuncCalls int // number of calls to the onErr function
lastErr error // last generated error
peerTestMtx sync.Mutex // modifications of ^^ variables are also done from timer handler goroutine
)
peer := NewBpPeer(
p2p.ID(cmn.RandStr(12)), 10,
func(err error, _ p2p.ID) {
peerTestMtx.Lock()
defer peerTestMtx.Unlock()
lastErr = err
numErrFuncCalls++
},
params)
peer.SetLogger(log.TestingLogger())
peer.RequestSent(1)
time.Sleep(4 * time.Millisecond)
// timer should have expired by now, check that the on error function was called
peerTestMtx.Lock()
assert.Equal(t, 1, numErrFuncCalls)
assert.Equal(t, errNoPeerResponse, lastErr)
peerTestMtx.Unlock()
}
func TestPeerCheckRate(t *testing.T) {
params := &BpPeerParams{
timeout: time.Second,
minRecvRate: int64(100), // 100 bytes/sec exponential moving average
}
peer := NewBpPeer(
p2p.ID(cmn.RandStr(12)), 10,
func(err error, _ p2p.ID) {},
params)
peer.SetLogger(log.TestingLogger())
require.Nil(t, peer.CheckRate())
for i := 0; i < 40; i++ {
peer.RequestSent(int64(i))
}
// monitor starts with a higher rEMA (~ 2*minRecvRate), wait for it to go down
time.Sleep(900 * time.Millisecond)
// normal peer - send a bit more than 100 bytes/sec, > 10 bytes/100msec, check peer is not considered slow
for i := 0; i < 10; i++ {
_ = peer.AddBlock(makeSmallBlock(i), 11)
time.Sleep(100 * time.Millisecond)
require.Nil(t, peer.CheckRate())
}
// slow peer - send a bit less than 10 bytes/100msec
for i := 10; i < 20; i++ {
_ = peer.AddBlock(makeSmallBlock(i), 9)
time.Sleep(100 * time.Millisecond)
}
// check peer is considered slow
assert.Equal(t, errSlowPeer, peer.CheckRate())
}
func TestPeerCleanup(t *testing.T) {
params := &BpPeerParams{timeout: 2 * time.Millisecond}
peer := NewBpPeer(
p2p.ID(cmn.RandStr(12)), 10,
func(err error, _ p2p.ID) {},
params)
peer.SetLogger(log.TestingLogger())
assert.Nil(t, peer.blockResponseTimer)
peer.RequestSent(1)
assert.NotNil(t, peer.blockResponseTimer)
peer.Cleanup()
checkByStoppingPeerTimer(t, peer, false)
}
// Check if peer timer is running or not (a running timer can be successfully stopped).
// Note: stops the timer.
func checkByStoppingPeerTimer(t *testing.T, peer *BpPeer, running bool) {
assert.NotPanics(t, func() {
stopped := peer.stopBlockResponseTimer()
if running {
assert.True(t, stopped)
} else {
assert.False(t, stopped)
}
})
}
func makeSmallBlock(height int) *types.Block {
return types.MakeBlock(int64(height), []types.Tx{types.Tx("foo")}, nil, nil)
}

369
blockchain/v1/pool.go Normal file
View File

@@ -0,0 +1,369 @@
package v1
import (
"sort"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/types"
)
// BlockPool keeps track of the fast sync peers, block requests and block responses.
type BlockPool struct {
logger log.Logger
// Set of peers that have sent status responses, with height bigger than pool.Height
peers map[p2p.ID]*BpPeer
// Set of block heights and the corresponding peers from where a block response is expected or has been received.
blocks map[int64]p2p.ID
plannedRequests map[int64]struct{} // list of blocks to be assigned peers for blockRequest
nextRequestHeight int64 // next height to be added to plannedRequests
Height int64 // height of next block to execute
MaxPeerHeight int64 // maximum height of all peers
toBcR bcReactor
}
// NewBlockPool creates a new BlockPool.
func NewBlockPool(height int64, toBcR bcReactor) *BlockPool {
return &BlockPool{
Height: height,
MaxPeerHeight: 0,
peers: make(map[p2p.ID]*BpPeer),
blocks: make(map[int64]p2p.ID),
plannedRequests: make(map[int64]struct{}),
nextRequestHeight: height,
toBcR: toBcR,
}
}
// SetLogger sets the logger of the pool.
func (pool *BlockPool) SetLogger(l log.Logger) {
pool.logger = l
}
// ReachedMaxHeight check if the pool has reached the maximum peer height.
func (pool *BlockPool) ReachedMaxHeight() bool {
return pool.Height >= pool.MaxPeerHeight
}
func (pool *BlockPool) rescheduleRequest(peerID p2p.ID, height int64) {
pool.logger.Info("reschedule requests made to peer for height ", "peerID", peerID, "height", height)
pool.plannedRequests[height] = struct{}{}
delete(pool.blocks, height)
pool.peers[peerID].RemoveBlock(height)
}
// Updates the pool's max height. If no peers are left MaxPeerHeight is set to 0.
func (pool *BlockPool) updateMaxPeerHeight() {
var newMax int64
for _, peer := range pool.peers {
peerHeight := peer.Height
if peerHeight > newMax {
newMax = peerHeight
}
}
pool.MaxPeerHeight = newMax
}
// UpdatePeer adds a new peer or updates an existing peer with a new height.
// If a peer is short it is not added.
func (pool *BlockPool) UpdatePeer(peerID p2p.ID, height int64) error {
peer := pool.peers[peerID]
if peer == nil {
if height < pool.Height {
pool.logger.Info("Peer height too small",
"peer", peerID, "height", height, "fsm_height", pool.Height)
return errPeerTooShort
}
// Add new peer.
peer = NewBpPeer(peerID, height, pool.toBcR.sendPeerError, nil)
peer.SetLogger(pool.logger.With("peer", peerID))
pool.peers[peerID] = peer
pool.logger.Info("added peer", "peerID", peerID, "height", height, "num_peers", len(pool.peers))
} else {
// Check if peer is lowering its height. This is not allowed.
if height < peer.Height {
pool.RemovePeer(peerID, errPeerLowersItsHeight)
return errPeerLowersItsHeight
}
// Update existing peer.
peer.Height = height
}
// Update the pool's MaxPeerHeight if needed.
pool.updateMaxPeerHeight()
return nil
}
// Cleans and deletes the peer. Recomputes the max peer height.
func (pool *BlockPool) deletePeer(peer *BpPeer) {
if peer == nil {
return
}
peer.Cleanup()
delete(pool.peers, peer.ID)
if peer.Height == pool.MaxPeerHeight {
pool.updateMaxPeerHeight()
}
}
// RemovePeer removes the blocks and requests from the peer, reschedules them and deletes the peer.
func (pool *BlockPool) RemovePeer(peerID p2p.ID, err error) {
peer := pool.peers[peerID]
if peer == nil {
return
}
pool.logger.Info("removing peer", "peerID", peerID, "error", err)
// Reschedule the block requests made to the peer, or received and not processed yet.
// Note that some of the requests may be removed further down.
for h := range pool.peers[peerID].blocks {
pool.rescheduleRequest(peerID, h)
}
oldMaxPeerHeight := pool.MaxPeerHeight
// Delete the peer. This operation may result in the pool's MaxPeerHeight being lowered.
pool.deletePeer(peer)
// Check if the pool's MaxPeerHeight has been lowered.
// This may happen if the tallest peer has been removed.
if oldMaxPeerHeight > pool.MaxPeerHeight {
// Remove any planned requests for heights over the new MaxPeerHeight.
for h := range pool.plannedRequests {
if h > pool.MaxPeerHeight {
delete(pool.plannedRequests, h)
}
}
// Adjust the nextRequestHeight to the new max plus one.
if pool.nextRequestHeight > pool.MaxPeerHeight {
pool.nextRequestHeight = pool.MaxPeerHeight + 1
}
}
}
func (pool *BlockPool) removeShortPeers() {
for _, peer := range pool.peers {
if peer.Height < pool.Height {
pool.RemovePeer(peer.ID, nil)
}
}
}
func (pool *BlockPool) removeBadPeers() {
pool.removeShortPeers()
for _, peer := range pool.peers {
if err := peer.CheckRate(); err != nil {
pool.RemovePeer(peer.ID, err)
pool.toBcR.sendPeerError(err, peer.ID)
}
}
}
// MakeNextRequests creates more requests if the block pool is running low.
func (pool *BlockPool) MakeNextRequests(maxNumRequests int) {
heights := pool.makeRequestBatch(maxNumRequests)
if len(heights) != 0 {
pool.logger.Info("makeNextRequests will make following requests",
"number", len(heights), "heights", heights)
}
for _, height := range heights {
h := int64(height)
if !pool.sendRequest(h) {
// If a good peer was not found for sending the request at height h then return,
// as it shouldn't be possible to find a peer for h+1.
return
}
delete(pool.plannedRequests, h)
}
}
// Makes a batch of requests sorted by height such that the block pool has up to maxNumRequests entries.
func (pool *BlockPool) makeRequestBatch(maxNumRequests int) []int {
pool.removeBadPeers()
// At this point pool.requests may include heights for requests to be redone due to removal of peers:
// - peers timed out or were removed by switch
// - FSM timed out on waiting to advance the block execution due to missing blocks at h or h+1
// Determine the number of requests needed by subtracting the number of requests already made from the maximum
// allowed
numNeeded := maxNumRequests - len(pool.blocks)
for len(pool.plannedRequests) < numNeeded {
if pool.nextRequestHeight > pool.MaxPeerHeight {
break
}
pool.plannedRequests[pool.nextRequestHeight] = struct{}{}
pool.nextRequestHeight++
}
heights := make([]int, 0, len(pool.plannedRequests))
for k := range pool.plannedRequests {
heights = append(heights, int(k))
}
sort.Ints(heights)
return heights
}
func (pool *BlockPool) sendRequest(height int64) bool {
for _, peer := range pool.peers {
if peer.NumPendingBlockRequests >= maxRequestsPerPeer {
continue
}
if peer.Height < height {
continue
}
err := pool.toBcR.sendBlockRequest(peer.ID, height)
if err == errNilPeerForBlockRequest {
// Switch does not have this peer, remove it and continue to look for another peer.
pool.logger.Error("switch does not have peer..removing peer selected for height", "peer",
peer.ID, "height", height)
pool.RemovePeer(peer.ID, err)
continue
}
if err == errSendQueueFull {
pool.logger.Error("peer queue is full", "peer", peer.ID, "height", height)
continue
}
pool.logger.Info("assigned request to peer", "peer", peer.ID, "height", height)
pool.blocks[height] = peer.ID
peer.RequestSent(height)
return true
}
pool.logger.Error("could not find peer to send request for block at height", "height", height)
return false
}
// AddBlock validates that the block comes from the peer it was expected from and stores it in the 'blocks' map.
func (pool *BlockPool) AddBlock(peerID p2p.ID, block *types.Block, blockSize int) error {
peer, ok := pool.peers[peerID]
if !ok {
pool.logger.Error("block from unknown peer", "height", block.Height, "peer", peerID)
return errBadDataFromPeer
}
if wantPeerID, ok := pool.blocks[block.Height]; ok && wantPeerID != peerID {
pool.logger.Error("block received from wrong peer", "height", block.Height,
"peer", peerID, "expected_peer", wantPeerID)
return errBadDataFromPeer
}
return peer.AddBlock(block, blockSize)
}
// BlockData stores the peer responsible to deliver a block and the actual block if delivered.
type BlockData struct {
block *types.Block
peer *BpPeer
}
// BlockAndPeerAtHeight retrieves the block and delivery peer at specified height.
// Returns errMissingBlock if a block was not found
func (pool *BlockPool) BlockAndPeerAtHeight(height int64) (bData *BlockData, err error) {
peerID := pool.blocks[height]
peer := pool.peers[peerID]
if peer == nil {
return nil, errMissingBlock
}
block, err := peer.BlockAtHeight(height)
if err != nil {
return nil, err
}
return &BlockData{peer: peer, block: block}, nil
}
// FirstTwoBlocksAndPeers returns the blocks and the delivery peers at pool's height H and H+1.
func (pool *BlockPool) FirstTwoBlocksAndPeers() (first, second *BlockData, err error) {
first, err = pool.BlockAndPeerAtHeight(pool.Height)
second, err2 := pool.BlockAndPeerAtHeight(pool.Height + 1)
if err == nil {
err = err2
}
return
}
// InvalidateFirstTwoBlocks removes the peers that sent us the first two blocks, blocks are removed by RemovePeer().
func (pool *BlockPool) InvalidateFirstTwoBlocks(err error) {
first, err1 := pool.BlockAndPeerAtHeight(pool.Height)
second, err2 := pool.BlockAndPeerAtHeight(pool.Height + 1)
if err1 == nil {
pool.RemovePeer(first.peer.ID, err)
}
if err2 == nil {
pool.RemovePeer(second.peer.ID, err)
}
}
// ProcessedCurrentHeightBlock performs cleanup after a block is processed. It removes block at pool height and
// the peers that are now short.
func (pool *BlockPool) ProcessedCurrentHeightBlock() {
peerID, peerOk := pool.blocks[pool.Height]
if peerOk {
pool.peers[peerID].RemoveBlock(pool.Height)
}
delete(pool.blocks, pool.Height)
pool.logger.Debug("removed block at height", "height", pool.Height)
pool.Height++
pool.removeShortPeers()
}
// RemovePeerAtCurrentHeights checks if a block at pool's height H exists and if not, it removes the
// delivery peer and returns. If a block at height H exists then the check and peer removal is done for H+1.
// This function is called when the FSM is not able to make progress for some time.
// This happens if either the block H or H+1 have not been delivered.
func (pool *BlockPool) RemovePeerAtCurrentHeights(err error) {
peerID := pool.blocks[pool.Height]
peer, ok := pool.peers[peerID]
if ok {
if _, err := peer.BlockAtHeight(pool.Height); err != nil {
pool.logger.Info("remove peer that hasn't sent block at pool.Height",
"peer", peerID, "height", pool.Height)
pool.RemovePeer(peerID, err)
return
}
}
peerID = pool.blocks[pool.Height+1]
peer, ok = pool.peers[peerID]
if ok {
if _, err := peer.BlockAtHeight(pool.Height + 1); err != nil {
pool.logger.Info("remove peer that hasn't sent block at pool.Height+1",
"peer", peerID, "height", pool.Height+1)
pool.RemovePeer(peerID, err)
return
}
}
}
// Cleanup performs pool and peer cleanup
func (pool *BlockPool) Cleanup() {
for id, peer := range pool.peers {
peer.Cleanup()
delete(pool.peers, id)
}
pool.plannedRequests = make(map[int64]struct{})
pool.blocks = make(map[int64]p2p.ID)
pool.nextRequestHeight = 0
pool.Height = 0
pool.MaxPeerHeight = 0
}
// NumPeers returns the number of peers in the pool
func (pool *BlockPool) NumPeers() int {
return len(pool.peers)
}
// NeedsBlocks returns true if more blocks are required.
func (pool *BlockPool) NeedsBlocks() bool {
return len(pool.blocks) < maxNumRequests
}

659
blockchain/v1/pool_test.go Normal file
View File

@@ -0,0 +1,659 @@
package v1
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/types"
)
type testPeer struct {
id p2p.ID
height int64
}
type testBcR struct {
logger log.Logger
}
type testValues struct {
numRequestsSent int
}
var testResults testValues
func resetPoolTestResults() {
testResults.numRequestsSent = 0
}
func (testR *testBcR) sendPeerError(err error, peerID p2p.ID) {
}
func (testR *testBcR) sendStatusRequest() {
}
func (testR *testBcR) sendBlockRequest(peerID p2p.ID, height int64) error {
testResults.numRequestsSent++
return nil
}
func (testR *testBcR) resetStateTimer(name string, timer **time.Timer, timeout time.Duration) {
}
func (testR *testBcR) switchToConsensus() {
}
func newTestBcR() *testBcR {
testBcR := &testBcR{logger: log.TestingLogger()}
return testBcR
}
type tPBlocks struct {
id p2p.ID
create bool
}
// Makes a block pool with specified current height, list of peers, block requests and block responses
func makeBlockPool(bcr *testBcR, height int64, peers []BpPeer, blocks map[int64]tPBlocks) *BlockPool {
bPool := NewBlockPool(height, bcr)
bPool.SetLogger(bcr.logger)
txs := []types.Tx{types.Tx("foo"), types.Tx("bar")}
var maxH int64
for _, p := range peers {
if p.Height > maxH {
maxH = p.Height
}
bPool.peers[p.ID] = NewBpPeer(p.ID, p.Height, bcr.sendPeerError, nil)
bPool.peers[p.ID].SetLogger(bcr.logger)
}
bPool.MaxPeerHeight = maxH
for h, p := range blocks {
bPool.blocks[h] = p.id
bPool.peers[p.id].RequestSent(h)
if p.create {
// simulate that a block at height h has been received
_ = bPool.peers[p.id].AddBlock(types.MakeBlock(h, txs, nil, nil), 100)
}
}
return bPool
}
func assertPeerSetsEquivalent(t *testing.T, set1 map[p2p.ID]*BpPeer, set2 map[p2p.ID]*BpPeer) {
assert.Equal(t, len(set1), len(set2))
for peerID, peer1 := range set1 {
peer2 := set2[peerID]
assert.NotNil(t, peer2)
assert.Equal(t, peer1.NumPendingBlockRequests, peer2.NumPendingBlockRequests)
assert.Equal(t, peer1.Height, peer2.Height)
assert.Equal(t, len(peer1.blocks), len(peer2.blocks))
for h, block1 := range peer1.blocks {
block2 := peer2.blocks[h]
// block1 and block2 could be nil if a request was made but no block was received
assert.Equal(t, block1, block2)
}
}
}
func assertBlockPoolEquivalent(t *testing.T, poolWanted, pool *BlockPool) {
assert.Equal(t, poolWanted.blocks, pool.blocks)
assertPeerSetsEquivalent(t, poolWanted.peers, pool.peers)
assert.Equal(t, poolWanted.MaxPeerHeight, pool.MaxPeerHeight)
assert.Equal(t, poolWanted.Height, pool.Height)
}
func TestBlockPoolUpdatePeer(t *testing.T) {
testBcR := newTestBcR()
tests := []struct {
name string
pool *BlockPool
args testPeer
poolWanted *BlockPool
errWanted error
}{
{
name: "add a first short peer",
pool: makeBlockPool(testBcR, 100, []BpPeer{}, map[int64]tPBlocks{}),
args: testPeer{"P1", 50},
errWanted: errPeerTooShort,
poolWanted: makeBlockPool(testBcR, 100, []BpPeer{}, map[int64]tPBlocks{}),
},
{
name: "add a first good peer",
pool: makeBlockPool(testBcR, 100, []BpPeer{}, map[int64]tPBlocks{}),
args: testPeer{"P1", 101},
poolWanted: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 101}}, map[int64]tPBlocks{}),
},
{
name: "increase the height of P1 from 120 to 123",
pool: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 120}}, map[int64]tPBlocks{}),
args: testPeer{"P1", 123},
poolWanted: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 123}}, map[int64]tPBlocks{}),
},
{
name: "decrease the height of P1 from 120 to 110",
pool: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 120}}, map[int64]tPBlocks{}),
args: testPeer{"P1", 110},
errWanted: errPeerLowersItsHeight,
poolWanted: makeBlockPool(testBcR, 100, []BpPeer{}, map[int64]tPBlocks{}),
},
{
name: "decrease the height of P1 from 105 to 102 with blocks",
pool: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 105}},
map[int64]tPBlocks{
100: {"P1", true}, 101: {"P1", true}, 102: {"P1", true}}),
args: testPeer{"P1", 102},
errWanted: errPeerLowersItsHeight,
poolWanted: makeBlockPool(testBcR, 100, []BpPeer{},
map[int64]tPBlocks{}),
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
pool := tt.pool
err := pool.UpdatePeer(tt.args.id, tt.args.height)
assert.Equal(t, tt.errWanted, err)
assert.Equal(t, tt.poolWanted.blocks, tt.pool.blocks)
assertPeerSetsEquivalent(t, tt.poolWanted.peers, tt.pool.peers)
assert.Equal(t, tt.poolWanted.MaxPeerHeight, tt.pool.MaxPeerHeight)
})
}
}
func TestBlockPoolRemovePeer(t *testing.T) {
testBcR := newTestBcR()
type args struct {
peerID p2p.ID
err error
}
tests := []struct {
name string
pool *BlockPool
args args
poolWanted *BlockPool
}{
{
name: "attempt to delete non-existing peer",
pool: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 120}}, map[int64]tPBlocks{}),
args: args{"P99", nil},
poolWanted: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 120}}, map[int64]tPBlocks{}),
},
{
name: "delete the only peer without blocks",
pool: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 120}}, map[int64]tPBlocks{}),
args: args{"P1", nil},
poolWanted: makeBlockPool(testBcR, 100, []BpPeer{}, map[int64]tPBlocks{}),
},
{
name: "delete the shortest of two peers without blocks",
pool: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 120}}, map[int64]tPBlocks{}),
args: args{"P1", nil},
poolWanted: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P2", Height: 120}}, map[int64]tPBlocks{}),
},
{
name: "delete the tallest of two peers without blocks",
pool: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 120}}, map[int64]tPBlocks{}),
args: args{"P2", nil},
poolWanted: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 100}}, map[int64]tPBlocks{}),
},
{
name: "delete the only peer with block requests sent and blocks received",
pool: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 120}},
map[int64]tPBlocks{100: {"P1", true}, 101: {"P1", false}}),
args: args{"P1", nil},
poolWanted: makeBlockPool(testBcR, 100, []BpPeer{}, map[int64]tPBlocks{}),
},
{
name: "delete the shortest of two peers with block requests sent and blocks received",
pool: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 120}, {ID: "P2", Height: 200}},
map[int64]tPBlocks{100: {"P1", true}, 101: {"P1", false}}),
args: args{"P1", nil},
poolWanted: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P2", Height: 200}}, map[int64]tPBlocks{}),
},
{
name: "delete the tallest of two peers with block requests sent and blocks received",
pool: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 120}, {ID: "P2", Height: 110}},
map[int64]tPBlocks{100: {"P1", true}, 101: {"P1", false}}),
args: args{"P1", nil},
poolWanted: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P2", Height: 110}}, map[int64]tPBlocks{}),
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
tt.pool.RemovePeer(tt.args.peerID, tt.args.err)
assertBlockPoolEquivalent(t, tt.poolWanted, tt.pool)
})
}
}
func TestBlockPoolRemoveShortPeers(t *testing.T) {
testBcR := newTestBcR()
tests := []struct {
name string
pool *BlockPool
poolWanted *BlockPool
}{
{
name: "no short peers",
pool: makeBlockPool(testBcR, 100,
[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 110}, {ID: "P3", Height: 120}}, map[int64]tPBlocks{}),
poolWanted: makeBlockPool(testBcR, 100,
[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 110}, {ID: "P3", Height: 120}}, map[int64]tPBlocks{}),
},
{
name: "one short peer",
pool: makeBlockPool(testBcR, 100,
[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 90}, {ID: "P3", Height: 120}}, map[int64]tPBlocks{}),
poolWanted: makeBlockPool(testBcR, 100,
[]BpPeer{{ID: "P1", Height: 100}, {ID: "P3", Height: 120}}, map[int64]tPBlocks{}),
},
{
name: "all short peers",
pool: makeBlockPool(testBcR, 100,
[]BpPeer{{ID: "P1", Height: 90}, {ID: "P2", Height: 91}, {ID: "P3", Height: 92}}, map[int64]tPBlocks{}),
poolWanted: makeBlockPool(testBcR, 100, []BpPeer{}, map[int64]tPBlocks{}),
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
pool := tt.pool
pool.removeShortPeers()
assertBlockPoolEquivalent(t, tt.poolWanted, tt.pool)
})
}
}
func TestBlockPoolSendRequestBatch(t *testing.T) {
type testPeerResult struct {
id p2p.ID
numPendingBlockRequests int
}
testBcR := newTestBcR()
tests := []struct {
name string
pool *BlockPool
maxRequestsPerPeer int
expRequests map[int64]bool
expPeerResults []testPeerResult
expnumPendingBlockRequests int
}{
{
name: "one peer - send up to maxRequestsPerPeer block requests",
pool: makeBlockPool(testBcR, 10, []BpPeer{{ID: "P1", Height: 100}}, map[int64]tPBlocks{}),
maxRequestsPerPeer: 2,
expRequests: map[int64]bool{10: true, 11: true},
expPeerResults: []testPeerResult{{id: "P1", numPendingBlockRequests: 2}},
expnumPendingBlockRequests: 2,
},
{
name: "n peers - send n*maxRequestsPerPeer block requests",
pool: makeBlockPool(testBcR, 10, []BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 100}}, map[int64]tPBlocks{}),
maxRequestsPerPeer: 2,
expRequests: map[int64]bool{10: true, 11: true},
expPeerResults: []testPeerResult{
{id: "P1", numPendingBlockRequests: 2},
{id: "P2", numPendingBlockRequests: 2}},
expnumPendingBlockRequests: 4,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
resetPoolTestResults()
var pool = tt.pool
maxRequestsPerPeer = tt.maxRequestsPerPeer
pool.MakeNextRequests(10)
assert.Equal(t, testResults.numRequestsSent, maxRequestsPerPeer*len(pool.peers))
for _, tPeer := range tt.expPeerResults {
var peer = pool.peers[tPeer.id]
assert.NotNil(t, peer)
assert.Equal(t, tPeer.numPendingBlockRequests, peer.NumPendingBlockRequests)
}
assert.Equal(t, testResults.numRequestsSent, maxRequestsPerPeer*len(pool.peers))
})
}
}
func TestBlockPoolAddBlock(t *testing.T) {
testBcR := newTestBcR()
txs := []types.Tx{types.Tx("foo"), types.Tx("bar")}
type args struct {
peerID p2p.ID
block *types.Block
blockSize int
}
tests := []struct {
name string
pool *BlockPool
args args
poolWanted *BlockPool
errWanted error
}{
{name: "block from unknown peer",
pool: makeBlockPool(testBcR, 10, []BpPeer{{ID: "P1", Height: 100}}, map[int64]tPBlocks{}),
args: args{
peerID: "P2",
block: types.MakeBlock(int64(10), txs, nil, nil),
blockSize: 100,
},
poolWanted: makeBlockPool(testBcR, 10, []BpPeer{{ID: "P1", Height: 100}}, map[int64]tPBlocks{}),
errWanted: errBadDataFromPeer,
},
{name: "unexpected block 11 from known peer - waiting for 10",
pool: makeBlockPool(testBcR, 10,
[]BpPeer{{ID: "P1", Height: 100}},
map[int64]tPBlocks{10: {"P1", false}}),
args: args{
peerID: "P1",
block: types.MakeBlock(int64(11), txs, nil, nil),
blockSize: 100,
},
poolWanted: makeBlockPool(testBcR, 10,
[]BpPeer{{ID: "P1", Height: 100}},
map[int64]tPBlocks{10: {"P1", false}}),
errWanted: errMissingBlock,
},
{name: "unexpected block 10 from known peer - already have 10",
pool: makeBlockPool(testBcR, 10,
[]BpPeer{{ID: "P1", Height: 100}},
map[int64]tPBlocks{10: {"P1", true}, 11: {"P1", false}}),
args: args{
peerID: "P1",
block: types.MakeBlock(int64(10), txs, nil, nil),
blockSize: 100,
},
poolWanted: makeBlockPool(testBcR, 10,
[]BpPeer{{ID: "P1", Height: 100}},
map[int64]tPBlocks{10: {"P1", true}, 11: {"P1", false}}),
errWanted: errDuplicateBlock,
},
{name: "unexpected block 10 from known peer P2 - expected 10 to come from P1",
pool: makeBlockPool(testBcR, 10,
[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 100}},
map[int64]tPBlocks{10: {"P1", false}}),
args: args{
peerID: "P2",
block: types.MakeBlock(int64(10), txs, nil, nil),
blockSize: 100,
},
poolWanted: makeBlockPool(testBcR, 10,
[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 100}},
map[int64]tPBlocks{10: {"P1", false}}),
errWanted: errBadDataFromPeer,
},
{name: "expected block from known peer",
pool: makeBlockPool(testBcR, 10,
[]BpPeer{{ID: "P1", Height: 100}},
map[int64]tPBlocks{10: {"P1", false}}),
args: args{
peerID: "P1",
block: types.MakeBlock(int64(10), txs, nil, nil),
blockSize: 100,
},
poolWanted: makeBlockPool(testBcR, 10,
[]BpPeer{{ID: "P1", Height: 100}},
map[int64]tPBlocks{10: {"P1", true}}),
errWanted: nil,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
err := tt.pool.AddBlock(tt.args.peerID, tt.args.block, tt.args.blockSize)
assert.Equal(t, tt.errWanted, err)
assertBlockPoolEquivalent(t, tt.poolWanted, tt.pool)
})
}
}
func TestBlockPoolFirstTwoBlocksAndPeers(t *testing.T) {
testBcR := newTestBcR()
tests := []struct {
name string
pool *BlockPool
firstWanted int64
secondWanted int64
errWanted error
}{
{
name: "both blocks missing",
pool: makeBlockPool(testBcR, 10,
[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 100}},
map[int64]tPBlocks{15: {"P1", true}, 16: {"P2", true}}),
errWanted: errMissingBlock,
},
{
name: "second block missing",
pool: makeBlockPool(testBcR, 15,
[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 100}},
map[int64]tPBlocks{15: {"P1", true}, 18: {"P2", true}}),
firstWanted: 15,
errWanted: errMissingBlock,
},
{
name: "first block missing",
pool: makeBlockPool(testBcR, 15,
[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 100}},
map[int64]tPBlocks{16: {"P2", true}, 18: {"P2", true}}),
secondWanted: 16,
errWanted: errMissingBlock,
},
{
name: "both blocks present",
pool: makeBlockPool(testBcR, 10,
[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 100}},
map[int64]tPBlocks{10: {"P1", true}, 11: {"P2", true}}),
firstWanted: 10,
secondWanted: 11,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
pool := tt.pool
gotFirst, gotSecond, err := pool.FirstTwoBlocksAndPeers()
assert.Equal(t, tt.errWanted, err)
if tt.firstWanted != 0 {
peer := pool.blocks[tt.firstWanted]
block := pool.peers[peer].blocks[tt.firstWanted]
assert.Equal(t, block, gotFirst.block,
"BlockPool.FirstTwoBlocksAndPeers() gotFirst = %v, want %v",
tt.firstWanted, gotFirst.block.Height)
}
if tt.secondWanted != 0 {
peer := pool.blocks[tt.secondWanted]
block := pool.peers[peer].blocks[tt.secondWanted]
assert.Equal(t, block, gotSecond.block,
"BlockPool.FirstTwoBlocksAndPeers() gotFirst = %v, want %v",
tt.secondWanted, gotSecond.block.Height)
}
})
}
}
func TestBlockPoolInvalidateFirstTwoBlocks(t *testing.T) {
testBcR := newTestBcR()
tests := []struct {
name string
pool *BlockPool
poolWanted *BlockPool
}{
{
name: "both blocks missing",
pool: makeBlockPool(testBcR, 10,
[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 100}},
map[int64]tPBlocks{15: {"P1", true}, 16: {"P2", true}}),
poolWanted: makeBlockPool(testBcR, 10,
[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 100}},
map[int64]tPBlocks{15: {"P1", true}, 16: {"P2", true}}),
},
{
name: "second block missing",
pool: makeBlockPool(testBcR, 15,
[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 100}},
map[int64]tPBlocks{15: {"P1", true}, 18: {"P2", true}}),
poolWanted: makeBlockPool(testBcR, 15,
[]BpPeer{{ID: "P2", Height: 100}},
map[int64]tPBlocks{18: {"P2", true}}),
},
{
name: "first block missing",
pool: makeBlockPool(testBcR, 15,
[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 100}},
map[int64]tPBlocks{18: {"P1", true}, 16: {"P2", true}}),
poolWanted: makeBlockPool(testBcR, 15,
[]BpPeer{{ID: "P1", Height: 100}},
map[int64]tPBlocks{18: {"P1", true}}),
},
{
name: "both blocks present",
pool: makeBlockPool(testBcR, 10,
[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 100}},
map[int64]tPBlocks{10: {"P1", true}, 11: {"P2", true}}),
poolWanted: makeBlockPool(testBcR, 10,
[]BpPeer{},
map[int64]tPBlocks{}),
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
tt.pool.InvalidateFirstTwoBlocks(errNoPeerResponse)
assertBlockPoolEquivalent(t, tt.poolWanted, tt.pool)
})
}
}
func TestProcessedCurrentHeightBlock(t *testing.T) {
testBcR := newTestBcR()
tests := []struct {
name string
pool *BlockPool
poolWanted *BlockPool
}{
{
name: "one peer",
pool: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 120}},
map[int64]tPBlocks{100: {"P1", true}, 101: {"P1", true}}),
poolWanted: makeBlockPool(testBcR, 101, []BpPeer{{ID: "P1", Height: 120}},
map[int64]tPBlocks{101: {"P1", true}}),
},
{
name: "multiple peers",
pool: makeBlockPool(testBcR, 100,
[]BpPeer{{ID: "P1", Height: 120}, {ID: "P2", Height: 120}, {ID: "P3", Height: 130}},
map[int64]tPBlocks{
100: {"P1", true}, 104: {"P1", true}, 105: {"P1", false},
101: {"P2", true}, 103: {"P2", false},
102: {"P3", true}, 106: {"P3", true}}),
poolWanted: makeBlockPool(testBcR, 101,
[]BpPeer{{ID: "P1", Height: 120}, {ID: "P2", Height: 120}, {ID: "P3", Height: 130}},
map[int64]tPBlocks{
104: {"P1", true}, 105: {"P1", false},
101: {"P2", true}, 103: {"P2", false},
102: {"P3", true}, 106: {"P3", true}}),
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
tt.pool.ProcessedCurrentHeightBlock()
assertBlockPoolEquivalent(t, tt.poolWanted, tt.pool)
})
}
}
func TestRemovePeerAtCurrentHeight(t *testing.T) {
testBcR := newTestBcR()
tests := []struct {
name string
pool *BlockPool
poolWanted *BlockPool
}{
{
name: "one peer, remove peer for block at H",
pool: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 120}},
map[int64]tPBlocks{100: {"P1", false}, 101: {"P1", true}}),
poolWanted: makeBlockPool(testBcR, 100, []BpPeer{}, map[int64]tPBlocks{}),
},
{
name: "one peer, remove peer for block at H+1",
pool: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 120}},
map[int64]tPBlocks{100: {"P1", true}, 101: {"P1", false}}),
poolWanted: makeBlockPool(testBcR, 100, []BpPeer{}, map[int64]tPBlocks{}),
},
{
name: "multiple peers, remove peer for block at H",
pool: makeBlockPool(testBcR, 100,
[]BpPeer{{ID: "P1", Height: 120}, {ID: "P2", Height: 120}, {ID: "P3", Height: 130}},
map[int64]tPBlocks{
100: {"P1", false}, 104: {"P1", true}, 105: {"P1", false},
101: {"P2", true}, 103: {"P2", false},
102: {"P3", true}, 106: {"P3", true}}),
poolWanted: makeBlockPool(testBcR, 100,
[]BpPeer{{ID: "P2", Height: 120}, {ID: "P3", Height: 130}},
map[int64]tPBlocks{
101: {"P2", true}, 103: {"P2", false},
102: {"P3", true}, 106: {"P3", true}}),
},
{
name: "multiple peers, remove peer for block at H+1",
pool: makeBlockPool(testBcR, 100,
[]BpPeer{{ID: "P1", Height: 120}, {ID: "P2", Height: 120}, {ID: "P3", Height: 130}},
map[int64]tPBlocks{
100: {"P1", true}, 104: {"P1", true}, 105: {"P1", false},
101: {"P2", false}, 103: {"P2", false},
102: {"P3", true}, 106: {"P3", true}}),
poolWanted: makeBlockPool(testBcR, 100,
[]BpPeer{{ID: "P1", Height: 120}, {ID: "P3", Height: 130}},
map[int64]tPBlocks{
100: {"P1", true}, 104: {"P1", true}, 105: {"P1", false},
102: {"P3", true}, 106: {"P3", true}}),
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
tt.pool.RemovePeerAtCurrentHeights(errNoPeerResponse)
assertBlockPoolEquivalent(t, tt.poolWanted, tt.pool)
})
}
}

622
blockchain/v1/reactor.go Normal file
View File

@@ -0,0 +1,622 @@
package v1
import (
"errors"
"fmt"
"reflect"
"time"
amino "github.com/tendermint/go-amino"
"github.com/tendermint/tendermint/behaviour"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/p2p"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/store"
"github.com/tendermint/tendermint/types"
)
const (
// BlockchainChannel is a channel for blocks and status updates (`BlockStore` height)
BlockchainChannel = byte(0x40)
trySyncIntervalMS = 10
trySendIntervalMS = 10
// ask for best height every 10s
statusUpdateIntervalSeconds = 10
// NOTE: keep up to date with bcBlockResponseMessage
bcBlockResponseMessagePrefixSize = 4
bcBlockResponseMessageFieldKeySize = 1
maxMsgSize = types.MaxBlockSizeBytes +
bcBlockResponseMessagePrefixSize +
bcBlockResponseMessageFieldKeySize
)
var (
// Maximum number of requests that can be pending per peer, i.e. for which requests have been sent but blocks
// have not been received.
maxRequestsPerPeer = 20
// Maximum number of block requests for the reactor, pending or for which blocks have been received.
maxNumRequests = 64
)
type consensusReactor interface {
// for when we switch from blockchain reactor and fast sync to
// the consensus machine
SwitchToConsensus(sm.State, int)
}
// BlockchainReactor handles long-term catchup syncing.
type BlockchainReactor struct {
p2p.BaseReactor
initialState sm.State // immutable
state sm.State
blockExec *sm.BlockExecutor
store *store.BlockStore
fastSync bool
fsm *BcReactorFSM
blocksSynced int
// Receive goroutine forwards messages to this channel to be processed in the context of the poolRoutine.
messagesForFSMCh chan bcReactorMessage
// Switch goroutine may send RemovePeer to the blockchain reactor. This is an error message that is relayed
// to this channel to be processed in the context of the poolRoutine.
errorsForFSMCh chan bcReactorMessage
// This channel is used by the FSM and indirectly the block pool to report errors to the blockchain reactor and
// the switch.
eventsFromFSMCh chan bcFsmMessage
swReporter *behaviour.SwitchReporter
}
// NewBlockchainReactor returns new reactor instance.
func NewBlockchainReactor(state sm.State, blockExec *sm.BlockExecutor, store *store.BlockStore,
fastSync bool) *BlockchainReactor {
if state.LastBlockHeight != store.Height() {
panic(fmt.Sprintf("state (%v) and store (%v) height mismatch", state.LastBlockHeight,
store.Height()))
}
const capacity = 1000
eventsFromFSMCh := make(chan bcFsmMessage, capacity)
messagesForFSMCh := make(chan bcReactorMessage, capacity)
errorsForFSMCh := make(chan bcReactorMessage, capacity)
startHeight := store.Height() + 1
bcR := &BlockchainReactor{
initialState: state,
state: state,
blockExec: blockExec,
fastSync: fastSync,
store: store,
messagesForFSMCh: messagesForFSMCh,
eventsFromFSMCh: eventsFromFSMCh,
errorsForFSMCh: errorsForFSMCh,
}
fsm := NewFSM(startHeight, bcR)
bcR.fsm = fsm
bcR.BaseReactor = *p2p.NewBaseReactor("BlockchainReactor", bcR)
//bcR.swReporter = behaviour.NewSwitcReporter(bcR.BaseReactor.Switch)
return bcR
}
// bcReactorMessage is used by the reactor to send messages to the FSM.
type bcReactorMessage struct {
event bReactorEvent
data bReactorEventData
}
type bFsmEvent uint
const (
// message type events
peerErrorEv = iota + 1
syncFinishedEv
)
type bFsmEventData struct {
peerID p2p.ID
err error
}
// bcFsmMessage is used by the FSM to send messages to the reactor
type bcFsmMessage struct {
event bFsmEvent
data bFsmEventData
}
// SetLogger implements cmn.Service by setting the logger on reactor and pool.
func (bcR *BlockchainReactor) SetLogger(l log.Logger) {
bcR.BaseService.Logger = l
bcR.fsm.SetLogger(l)
}
// OnStart implements cmn.Service.
func (bcR *BlockchainReactor) OnStart() error {
bcR.swReporter = behaviour.NewSwitcReporter(bcR.BaseReactor.Switch)
if bcR.fastSync {
go bcR.poolRoutine()
}
return nil
}
// OnStop implements cmn.Service.
func (bcR *BlockchainReactor) OnStop() {
_ = bcR.Stop()
}
// GetChannels implements Reactor
func (bcR *BlockchainReactor) GetChannels() []*p2p.ChannelDescriptor {
return []*p2p.ChannelDescriptor{
{
ID: BlockchainChannel,
Priority: 10,
SendQueueCapacity: 2000,
RecvBufferCapacity: 50 * 4096,
RecvMessageCapacity: maxMsgSize,
},
}
}
// AddPeer implements Reactor by sending our state to peer.
func (bcR *BlockchainReactor) AddPeer(peer p2p.Peer) {
msgBytes := cdc.MustMarshalBinaryBare(&bcStatusResponseMessage{bcR.store.Height()})
peer.Send(BlockchainChannel, msgBytes)
// it's OK if send fails. will try later in poolRoutine
// peer is added to the pool once we receive the first
// bcStatusResponseMessage from the peer and call pool.updatePeer()
}
// sendBlockToPeer loads a block and sends it to the requesting peer.
// If the block doesn't exist a bcNoBlockResponseMessage is sent.
// If all nodes are honest, no node should be requesting for a block that doesn't exist.
func (bcR *BlockchainReactor) sendBlockToPeer(msg *bcBlockRequestMessage,
src p2p.Peer) (queued bool) {
block := bcR.store.LoadBlock(msg.Height)
if block != nil {
msgBytes := cdc.MustMarshalBinaryBare(&bcBlockResponseMessage{Block: block})
return src.TrySend(BlockchainChannel, msgBytes)
}
bcR.Logger.Info("peer asking for a block we don't have", "src", src, "height", msg.Height)
msgBytes := cdc.MustMarshalBinaryBare(&bcNoBlockResponseMessage{Height: msg.Height})
return src.TrySend(BlockchainChannel, msgBytes)
}
func (bcR *BlockchainReactor) sendStatusResponseToPeer(msg *bcStatusRequestMessage, src p2p.Peer) (queued bool) {
msgBytes := cdc.MustMarshalBinaryBare(&bcStatusResponseMessage{bcR.store.Height()})
return src.TrySend(BlockchainChannel, msgBytes)
}
// RemovePeer implements Reactor by removing peer from the pool.
func (bcR *BlockchainReactor) RemovePeer(peer p2p.Peer, reason interface{}) {
msgData := bcReactorMessage{
event: peerRemoveEv,
data: bReactorEventData{
peerID: peer.ID(),
err: errSwitchRemovesPeer,
},
}
bcR.errorsForFSMCh <- msgData
}
// Receive implements Reactor by handling 4 types of messages (look below).
func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) {
msg, err := decodeMsg(msgBytes)
if err != nil {
bcR.Logger.Error("error decoding message",
"src", src, "chId", chID, "msg", msg, "err", err, "bytes", msgBytes)
_ = bcR.swReporter.Report(behaviour.BadMessage(src.ID(), err.Error()))
return
}
if err = msg.ValidateBasic(); err != nil {
bcR.Logger.Error("peer sent us invalid msg", "peer", src, "msg", msg, "err", err)
_ = bcR.swReporter.Report(behaviour.BadMessage(src.ID(), err.Error()))
return
}
bcR.Logger.Debug("Receive", "src", src, "chID", chID, "msg", msg)
switch msg := msg.(type) {
case *bcBlockRequestMessage:
if queued := bcR.sendBlockToPeer(msg, src); !queued {
// Unfortunately not queued since the queue is full.
bcR.Logger.Error("Could not send block message to peer", "src", src, "height", msg.Height)
}
case *bcStatusRequestMessage:
// Send peer our state.
if queued := bcR.sendStatusResponseToPeer(msg, src); !queued {
// Unfortunately not queued since the queue is full.
bcR.Logger.Error("Could not send status message to peer", "src", src)
}
case *bcBlockResponseMessage:
msgForFSM := bcReactorMessage{
event: blockResponseEv,
data: bReactorEventData{
peerID: src.ID(),
height: msg.Block.Height,
block: msg.Block,
length: len(msgBytes),
},
}
bcR.Logger.Info("Received", "src", src, "height", msg.Block.Height)
bcR.messagesForFSMCh <- msgForFSM
case *bcStatusResponseMessage:
// Got a peer status. Unverified.
msgForFSM := bcReactorMessage{
event: statusResponseEv,
data: bReactorEventData{
peerID: src.ID(),
height: msg.Height,
length: len(msgBytes),
},
}
bcR.messagesForFSMCh <- msgForFSM
default:
bcR.Logger.Error(fmt.Sprintf("unknown message type %v", reflect.TypeOf(msg)))
}
}
// processBlocksRoutine processes blocks until signlaed to stop over the stopProcessing channel
func (bcR *BlockchainReactor) processBlocksRoutine(stopProcessing chan struct{}) {
processReceivedBlockTicker := time.NewTicker(trySyncIntervalMS * time.Millisecond)
doProcessBlockCh := make(chan struct{}, 1)
lastHundred := time.Now()
lastRate := 0.0
ForLoop:
for {
select {
case <-stopProcessing:
bcR.Logger.Info("finishing block execution")
break ForLoop
case <-processReceivedBlockTicker.C: // try to execute blocks
select {
case doProcessBlockCh <- struct{}{}:
default:
}
case <-doProcessBlockCh:
for {
err := bcR.processBlock()
if err == errMissingBlock {
break
}
// Notify FSM of block processing result.
msgForFSM := bcReactorMessage{
event: processedBlockEv,
data: bReactorEventData{
err: err,
},
}
_ = bcR.fsm.Handle(&msgForFSM)
if err != nil {
break
}
bcR.blocksSynced++
if bcR.blocksSynced%100 == 0 {
lastRate = 0.9*lastRate + 0.1*(100/time.Since(lastHundred).Seconds())
height, maxPeerHeight := bcR.fsm.Status()
bcR.Logger.Info("Fast Sync Rate", "height", height,
"max_peer_height", maxPeerHeight, "blocks/s", lastRate)
lastHundred = time.Now()
}
}
}
}
}
// poolRoutine receives and handles messages from the Receive() routine and from the FSM.
func (bcR *BlockchainReactor) poolRoutine() {
bcR.fsm.Start()
sendBlockRequestTicker := time.NewTicker(trySendIntervalMS * time.Millisecond)
statusUpdateTicker := time.NewTicker(statusUpdateIntervalSeconds * time.Second)
stopProcessing := make(chan struct{}, 1)
go bcR.processBlocksRoutine(stopProcessing)
ForLoop:
for {
select {
case <-sendBlockRequestTicker.C:
if !bcR.fsm.NeedsBlocks() {
continue
}
_ = bcR.fsm.Handle(&bcReactorMessage{
event: makeRequestsEv,
data: bReactorEventData{
maxNumRequests: maxNumRequests}})
case <-statusUpdateTicker.C:
// Ask for status updates.
go bcR.sendStatusRequest()
case msg := <-bcR.messagesForFSMCh:
// Sent from the Receive() routine when status (statusResponseEv) and
// block (blockResponseEv) response events are received
_ = bcR.fsm.Handle(&msg)
case msg := <-bcR.errorsForFSMCh:
// Sent from the switch.RemovePeer() routine (RemovePeerEv) and
// FSM state timer expiry routine (stateTimeoutEv).
_ = bcR.fsm.Handle(&msg)
case msg := <-bcR.eventsFromFSMCh:
switch msg.event {
case syncFinishedEv:
stopProcessing <- struct{}{}
// Sent from the FSM when it enters finished state.
break ForLoop
case peerErrorEv:
// Sent from the FSM when it detects peer error
bcR.reportPeerErrorToSwitch(msg.data.err, msg.data.peerID)
if msg.data.err == errNoPeerResponse {
// Sent from the peer timeout handler routine
_ = bcR.fsm.Handle(&bcReactorMessage{
event: peerRemoveEv,
data: bReactorEventData{
peerID: msg.data.peerID,
err: msg.data.err,
},
})
}
// else {
// For slow peers, or errors due to blocks received from wrong peer
// the FSM had already removed the peers
// }
default:
bcR.Logger.Error("Event from FSM not supported", "type", msg.event)
}
case <-bcR.Quit():
break ForLoop
}
}
}
func (bcR *BlockchainReactor) reportPeerErrorToSwitch(err error, peerID p2p.ID) {
peer := bcR.Switch.Peers().Get(peerID)
if peer != nil {
_ = bcR.swReporter.Report(behaviour.BadMessage(peerID, err.Error()))
}
}
func (bcR *BlockchainReactor) processBlock() error {
first, second, err := bcR.fsm.FirstTwoBlocks()
if err != nil {
// We need both to sync the first block.
return err
}
chainID := bcR.initialState.ChainID
firstParts := first.MakePartSet(types.BlockPartSizeBytes)
firstPartsHeader := firstParts.Header()
firstID := types.BlockID{Hash: first.Hash(), PartsHeader: firstPartsHeader}
// Finally, verify the first block using the second's commit
// NOTE: we can probably make this more efficient, but note that calling
// first.Hash() doesn't verify the tx contents, so MakePartSet() is
// currently necessary.
err = bcR.state.Validators.VerifyCommit(chainID, firstID, first.Height, second.LastCommit)
if err != nil {
bcR.Logger.Error("error during commit verification", "err", err,
"first", first.Height, "second", second.Height)
return errBlockVerificationFailure
}
bcR.store.SaveBlock(first, firstParts, second.LastCommit)
bcR.state, err = bcR.blockExec.ApplyBlock(bcR.state, firstID, first)
if err != nil {
panic(fmt.Sprintf("failed to process committed block (%d:%X): %v", first.Height, first.Hash(), err))
}
return nil
}
// Implements bcRNotifier
// sendStatusRequest broadcasts `BlockStore` height.
func (bcR *BlockchainReactor) sendStatusRequest() {
msgBytes := cdc.MustMarshalBinaryBare(&bcStatusRequestMessage{bcR.store.Height()})
bcR.Switch.Broadcast(BlockchainChannel, msgBytes)
}
// Implements bcRNotifier
// BlockRequest sends `BlockRequest` height.
func (bcR *BlockchainReactor) sendBlockRequest(peerID p2p.ID, height int64) error {
peer := bcR.Switch.Peers().Get(peerID)
if peer == nil {
return errNilPeerForBlockRequest
}
msgBytes := cdc.MustMarshalBinaryBare(&bcBlockRequestMessage{height})
queued := peer.TrySend(BlockchainChannel, msgBytes)
if !queued {
return errSendQueueFull
}
return nil
}
// Implements bcRNotifier
func (bcR *BlockchainReactor) switchToConsensus() {
conR, ok := bcR.Switch.Reactor("CONSENSUS").(consensusReactor)
if ok {
conR.SwitchToConsensus(bcR.state, bcR.blocksSynced)
bcR.eventsFromFSMCh <- bcFsmMessage{event: syncFinishedEv}
}
// else {
// Should only happen during testing.
// }
}
// Implements bcRNotifier
// Called by FSM and pool:
// - pool calls when it detects slow peer or when peer times out
// - FSM calls when:
// - adding a block (addBlock) fails
// - reactor processing of a block reports failure and FSM sends back the peers of first and second blocks
func (bcR *BlockchainReactor) sendPeerError(err error, peerID p2p.ID) {
bcR.Logger.Info("sendPeerError:", "peer", peerID, "error", err)
msgData := bcFsmMessage{
event: peerErrorEv,
data: bFsmEventData{
peerID: peerID,
err: err,
},
}
bcR.eventsFromFSMCh <- msgData
}
// Implements bcRNotifier
func (bcR *BlockchainReactor) resetStateTimer(name string, timer **time.Timer, timeout time.Duration) {
if timer == nil {
panic("nil timer pointer parameter")
}
if *timer == nil {
*timer = time.AfterFunc(timeout, func() {
msg := bcReactorMessage{
event: stateTimeoutEv,
data: bReactorEventData{
stateName: name,
},
}
bcR.errorsForFSMCh <- msg
})
} else {
(*timer).Reset(timeout)
}
}
//-----------------------------------------------------------------------------
// Messages
// BlockchainMessage is a generic message for this reactor.
type BlockchainMessage interface {
ValidateBasic() error
}
// RegisterBlockchainMessages registers the fast sync messages for amino encoding.
func RegisterBlockchainMessages(cdc *amino.Codec) {
cdc.RegisterInterface((*BlockchainMessage)(nil), 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) {
if len(bz) > maxMsgSize {
return msg, fmt.Errorf("msg exceeds max size (%d > %d)", len(bz), maxMsgSize)
}
err = cdc.UnmarshalBinaryBare(bz, &msg)
return
}
//-------------------------------------
type bcBlockRequestMessage struct {
Height int64
}
// ValidateBasic performs basic validation.
func (m *bcBlockRequestMessage) ValidateBasic() error {
if m.Height < 0 {
return errors.New("negative Height")
}
return nil
}
func (m *bcBlockRequestMessage) String() string {
return fmt.Sprintf("[bcBlockRequestMessage %v]", m.Height)
}
type bcNoBlockResponseMessage struct {
Height int64
}
// ValidateBasic performs basic validation.
func (m *bcNoBlockResponseMessage) ValidateBasic() error {
if m.Height < 0 {
return errors.New("negative Height")
}
return nil
}
func (m *bcNoBlockResponseMessage) String() string {
return fmt.Sprintf("[bcNoBlockResponseMessage %d]", m.Height)
}
//-------------------------------------
type bcBlockResponseMessage struct {
Block *types.Block
}
// ValidateBasic performs basic validation.
func (m *bcBlockResponseMessage) ValidateBasic() error {
return m.Block.ValidateBasic()
}
func (m *bcBlockResponseMessage) String() string {
return fmt.Sprintf("[bcBlockResponseMessage %v]", m.Block.Height)
}
//-------------------------------------
type bcStatusRequestMessage struct {
Height int64
}
// ValidateBasic performs basic validation.
func (m *bcStatusRequestMessage) ValidateBasic() error {
if m.Height < 0 {
return errors.New("negative Height")
}
return nil
}
func (m *bcStatusRequestMessage) String() string {
return fmt.Sprintf("[bcStatusRequestMessage %v]", m.Height)
}
//-------------------------------------
type bcStatusResponseMessage struct {
Height int64
}
// ValidateBasic performs basic validation.
func (m *bcStatusResponseMessage) ValidateBasic() error {
if m.Height < 0 {
return errors.New("negative Height")
}
return nil
}
func (m *bcStatusResponseMessage) String() string {
return fmt.Sprintf("[bcStatusResponseMessage %v]", m.Height)
}

View File

@@ -0,0 +1,450 @@
package v1
import (
"errors"
"fmt"
"sync"
"time"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/types"
)
// Blockchain Reactor State
type bcReactorFSMState struct {
name string
// called when transitioning out of current state
handle func(*BcReactorFSM, bReactorEvent, bReactorEventData) (next *bcReactorFSMState, err error)
// called when entering the state
enter func(fsm *BcReactorFSM)
// timeout to ensure FSM is not stuck in a state forever
// the timer is owned and run by the fsm instance
timeout time.Duration
}
func (s *bcReactorFSMState) String() string {
return s.name
}
// BcReactorFSM is the datastructure for the Blockchain Reactor State Machine
type BcReactorFSM struct {
logger log.Logger
mtx sync.Mutex
startTime time.Time
state *bcReactorFSMState
stateTimer *time.Timer
pool *BlockPool
// interface used to call the Blockchain reactor to send StatusRequest, BlockRequest, reporting errors, etc.
toBcR bcReactor
}
// NewFSM creates a new reactor FSM.
func NewFSM(height int64, toBcR bcReactor) *BcReactorFSM {
return &BcReactorFSM{
state: unknown,
startTime: time.Now(),
pool: NewBlockPool(height, toBcR),
toBcR: toBcR,
}
}
// bReactorEventData is part of the message sent by the reactor to the FSM and used by the state handlers.
type bReactorEventData struct {
peerID p2p.ID
err error // for peer error: timeout, slow; for processed block event if error occurred
height int64 // for status response; for processed block event
block *types.Block // for block response
stateName string // for state timeout events
length int // for block response event, length of received block, used to detect slow peers
maxNumRequests int // for request needed event, maximum number of pending requests
}
// Blockchain Reactor Events (the input to the state machine)
type bReactorEvent uint
const (
// message type events
startFSMEv = iota + 1
statusResponseEv
blockResponseEv
processedBlockEv
makeRequestsEv
stopFSMEv
// other events
peerRemoveEv = iota + 256
stateTimeoutEv
)
func (msg *bcReactorMessage) String() string {
var dataStr string
switch msg.event {
case startFSMEv:
dataStr = ""
case statusResponseEv:
dataStr = fmt.Sprintf("peer=%v height=%v", msg.data.peerID, msg.data.height)
case blockResponseEv:
dataStr = fmt.Sprintf("peer=%v block.height=%v length=%v",
msg.data.peerID, msg.data.block.Height, msg.data.length)
case processedBlockEv:
dataStr = fmt.Sprintf("error=%v", msg.data.err)
case makeRequestsEv:
dataStr = ""
case stopFSMEv:
dataStr = ""
case peerRemoveEv:
dataStr = fmt.Sprintf("peer: %v is being removed by the switch", msg.data.peerID)
case stateTimeoutEv:
dataStr = fmt.Sprintf("state=%v", msg.data.stateName)
default:
dataStr = fmt.Sprintf("cannot interpret message data")
}
return fmt.Sprintf("%v: %v", msg.event, dataStr)
}
func (ev bReactorEvent) String() string {
switch ev {
case startFSMEv:
return "startFSMEv"
case statusResponseEv:
return "statusResponseEv"
case blockResponseEv:
return "blockResponseEv"
case processedBlockEv:
return "processedBlockEv"
case makeRequestsEv:
return "makeRequestsEv"
case stopFSMEv:
return "stopFSMEv"
case peerRemoveEv:
return "peerRemoveEv"
case stateTimeoutEv:
return "stateTimeoutEv"
default:
return "event unknown"
}
}
// states
var (
unknown *bcReactorFSMState
waitForPeer *bcReactorFSMState
waitForBlock *bcReactorFSMState
finished *bcReactorFSMState
)
// timeouts for state timers
const (
waitForPeerTimeout = 3 * time.Second
waitForBlockAtCurrentHeightTimeout = 10 * time.Second
)
// errors
var (
// internal to the package
errNoErrorFinished = errors.New("fast sync is finished")
errInvalidEvent = errors.New("invalid event in current state")
errMissingBlock = errors.New("missing blocks")
errNilPeerForBlockRequest = errors.New("peer for block request does not exist in the switch")
errSendQueueFull = errors.New("block request not made, send-queue is full")
errPeerTooShort = errors.New("peer height too low, old peer removed/ new peer not added")
errSwitchRemovesPeer = errors.New("switch is removing peer")
errTimeoutEventWrongState = errors.New("timeout event for a state different than the current one")
errNoTallerPeer = errors.New("fast sync timed out on waiting for a peer taller than this node")
// reported eventually to the switch
errPeerLowersItsHeight = errors.New("fast sync peer reports a height lower than previous") // handle return
errNoPeerResponseForCurrentHeights = errors.New("fast sync timed out on peer block response for current heights") // handle return
errNoPeerResponse = errors.New("fast sync timed out on peer block response") // xx
errBadDataFromPeer = errors.New("fast sync received block from wrong peer or block is bad") // xx
errDuplicateBlock = errors.New("fast sync received duplicate block from peer")
errBlockVerificationFailure = errors.New("fast sync block verification failure") // xx
errSlowPeer = errors.New("fast sync peer is not sending us data fast enough") // xx
)
func init() {
unknown = &bcReactorFSMState{
name: "unknown",
handle: func(fsm *BcReactorFSM, ev bReactorEvent, data bReactorEventData) (*bcReactorFSMState, error) {
switch ev {
case startFSMEv:
// Broadcast Status message. Currently doesn't return non-nil error.
fsm.toBcR.sendStatusRequest()
return waitForPeer, nil
case stopFSMEv:
return finished, errNoErrorFinished
default:
return unknown, errInvalidEvent
}
},
}
waitForPeer = &bcReactorFSMState{
name: "waitForPeer",
timeout: waitForPeerTimeout,
enter: func(fsm *BcReactorFSM) {
// Stop when leaving the state.
fsm.resetStateTimer()
},
handle: func(fsm *BcReactorFSM, ev bReactorEvent, data bReactorEventData) (*bcReactorFSMState, error) {
switch ev {
case stateTimeoutEv:
if data.stateName != "waitForPeer" {
fsm.logger.Error("received a state timeout event for different state",
"state", data.stateName)
return waitForPeer, errTimeoutEventWrongState
}
// There was no statusResponse received from any peer.
// Should we send status request again?
return finished, errNoTallerPeer
case statusResponseEv:
if err := fsm.pool.UpdatePeer(data.peerID, data.height); err != nil {
if fsm.pool.NumPeers() == 0 {
return waitForPeer, err
}
}
if fsm.stateTimer != nil {
fsm.stateTimer.Stop()
}
return waitForBlock, nil
case stopFSMEv:
if fsm.stateTimer != nil {
fsm.stateTimer.Stop()
}
return finished, errNoErrorFinished
default:
return waitForPeer, errInvalidEvent
}
},
}
waitForBlock = &bcReactorFSMState{
name: "waitForBlock",
timeout: waitForBlockAtCurrentHeightTimeout,
enter: func(fsm *BcReactorFSM) {
// Stop when leaving the state.
fsm.resetStateTimer()
},
handle: func(fsm *BcReactorFSM, ev bReactorEvent, data bReactorEventData) (*bcReactorFSMState, error) {
switch ev {
case statusResponseEv:
err := fsm.pool.UpdatePeer(data.peerID, data.height)
if fsm.pool.NumPeers() == 0 {
return waitForPeer, err
}
if fsm.pool.ReachedMaxHeight() {
return finished, err
}
return waitForBlock, err
case blockResponseEv:
fsm.logger.Debug("blockResponseEv", "H", data.block.Height)
err := fsm.pool.AddBlock(data.peerID, data.block, data.length)
if err != nil {
// A block was received that was unsolicited, from unexpected peer, or that we already have it.
// Ignore block, remove peer and send error to switch.
fsm.pool.RemovePeer(data.peerID, err)
fsm.toBcR.sendPeerError(err, data.peerID)
}
if fsm.pool.NumPeers() == 0 {
return waitForPeer, err
}
return waitForBlock, err
case processedBlockEv:
if data.err != nil {
first, second, _ := fsm.pool.FirstTwoBlocksAndPeers()
fsm.logger.Error("error processing block", "err", data.err,
"first", first.block.Height, "second", second.block.Height)
fsm.logger.Error("send peer error for", "peer", first.peer.ID)
fsm.toBcR.sendPeerError(data.err, first.peer.ID)
fsm.logger.Error("send peer error for", "peer", second.peer.ID)
fsm.toBcR.sendPeerError(data.err, second.peer.ID)
// Remove the first two blocks. This will also remove the peers
fsm.pool.InvalidateFirstTwoBlocks(data.err)
} else {
fsm.pool.ProcessedCurrentHeightBlock()
// Since we advanced one block reset the state timer
fsm.resetStateTimer()
}
// Both cases above may result in achieving maximum height.
if fsm.pool.ReachedMaxHeight() {
return finished, nil
}
return waitForBlock, data.err
case peerRemoveEv:
// This event is sent by the switch to remove disconnected and errored peers.
fsm.pool.RemovePeer(data.peerID, data.err)
if fsm.pool.NumPeers() == 0 {
return waitForPeer, nil
}
if fsm.pool.ReachedMaxHeight() {
return finished, nil
}
return waitForBlock, nil
case makeRequestsEv:
fsm.makeNextRequests(data.maxNumRequests)
return waitForBlock, nil
case stateTimeoutEv:
if data.stateName != "waitForBlock" {
fsm.logger.Error("received a state timeout event for different state",
"state", data.stateName)
return waitForBlock, errTimeoutEventWrongState
}
// We haven't received the block at current height or height+1. Remove peer.
fsm.pool.RemovePeerAtCurrentHeights(errNoPeerResponseForCurrentHeights)
fsm.resetStateTimer()
if fsm.pool.NumPeers() == 0 {
return waitForPeer, errNoPeerResponseForCurrentHeights
}
if fsm.pool.ReachedMaxHeight() {
return finished, nil
}
return waitForBlock, errNoPeerResponseForCurrentHeights
case stopFSMEv:
if fsm.stateTimer != nil {
fsm.stateTimer.Stop()
}
return finished, errNoErrorFinished
default:
return waitForBlock, errInvalidEvent
}
},
}
finished = &bcReactorFSMState{
name: "finished",
enter: func(fsm *BcReactorFSM) {
fsm.logger.Info("Time to switch to consensus reactor!", "height", fsm.pool.Height)
fsm.toBcR.switchToConsensus()
fsm.cleanup()
},
handle: func(fsm *BcReactorFSM, ev bReactorEvent, data bReactorEventData) (*bcReactorFSMState, error) {
return finished, nil
},
}
}
// Interface used by FSM for sending Block and Status requests,
// informing of peer errors and state timeouts
// Implemented by BlockchainReactor and tests
type bcReactor interface {
sendStatusRequest()
sendBlockRequest(peerID p2p.ID, height int64) error
sendPeerError(err error, peerID p2p.ID)
resetStateTimer(name string, timer **time.Timer, timeout time.Duration)
switchToConsensus()
}
// SetLogger sets the FSM logger.
func (fsm *BcReactorFSM) SetLogger(l log.Logger) {
fsm.logger = l
fsm.pool.SetLogger(l)
}
// Start starts the FSM.
func (fsm *BcReactorFSM) Start() {
_ = fsm.Handle(&bcReactorMessage{event: startFSMEv})
}
// Handle processes messages and events sent to the FSM.
func (fsm *BcReactorFSM) Handle(msg *bcReactorMessage) error {
fsm.mtx.Lock()
defer fsm.mtx.Unlock()
fsm.logger.Debug("FSM received", "event", msg, "state", fsm.state)
if fsm.state == nil {
fsm.state = unknown
}
next, err := fsm.state.handle(fsm, msg.event, msg.data)
if err != nil {
fsm.logger.Error("FSM event handler returned", "err", err,
"state", fsm.state, "event", msg.event)
}
oldState := fsm.state.name
fsm.transition(next)
if oldState != fsm.state.name {
fsm.logger.Info("FSM changed state", "new_state", fsm.state)
}
return err
}
func (fsm *BcReactorFSM) transition(next *bcReactorFSMState) {
if next == nil {
return
}
if fsm.state != next {
fsm.state = next
if next.enter != nil {
next.enter(fsm)
}
}
}
// Called when entering an FSM state in order to detect lack of progress in the state machine.
// Note the use of the 'bcr' interface to facilitate testing without timer expiring.
func (fsm *BcReactorFSM) resetStateTimer() {
fsm.toBcR.resetStateTimer(fsm.state.name, &fsm.stateTimer, fsm.state.timeout)
}
func (fsm *BcReactorFSM) isCaughtUp() bool {
return fsm.state == finished
}
func (fsm *BcReactorFSM) makeNextRequests(maxNumRequests int) {
fsm.pool.MakeNextRequests(maxNumRequests)
}
func (fsm *BcReactorFSM) cleanup() {
fsm.pool.Cleanup()
}
// NeedsBlocks checks if more block requests are required.
func (fsm *BcReactorFSM) NeedsBlocks() bool {
fsm.mtx.Lock()
defer fsm.mtx.Unlock()
return fsm.state.name == "waitForBlock" && fsm.pool.NeedsBlocks()
}
// FirstTwoBlocks returns the two blocks at pool height and height+1
func (fsm *BcReactorFSM) FirstTwoBlocks() (first, second *types.Block, err error) {
fsm.mtx.Lock()
defer fsm.mtx.Unlock()
firstBP, secondBP, err := fsm.pool.FirstTwoBlocksAndPeers()
if err == nil {
first = firstBP.block
second = secondBP.block
}
return
}
// Status returns the pool's height and the maximum peer height.
func (fsm *BcReactorFSM) Status() (height, maxPeerHeight int64) {
fsm.mtx.Lock()
defer fsm.mtx.Unlock()
return fsm.pool.Height, fsm.pool.MaxPeerHeight
}

View File

@@ -0,0 +1,942 @@
package v1
import (
"fmt"
"testing"
"time"
"github.com/stretchr/testify/assert"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/types"
)
type lastBlockRequestT struct {
peerID p2p.ID
height int64
}
type lastPeerErrorT struct {
peerID p2p.ID
err error
}
// reactor for FSM testing
type testReactor struct {
logger log.Logger
fsm *BcReactorFSM
numStatusRequests int
numBlockRequests int
lastBlockRequest lastBlockRequestT
lastPeerError lastPeerErrorT
stateTimerStarts map[string]int
}
func sendEventToFSM(fsm *BcReactorFSM, ev bReactorEvent, data bReactorEventData) error {
return fsm.Handle(&bcReactorMessage{event: ev, data: data})
}
type fsmStepTestValues struct {
currentState string
event bReactorEvent
data bReactorEventData
wantErr error
wantState string
wantStatusReqSent bool
wantReqIncreased bool
wantNewBlocks []int64
wantRemovedPeers []p2p.ID
}
// ---------------------------------------------------------------------------
// helper test function for different FSM events, state and expected behavior
func sStopFSMEv(current, expected string) fsmStepTestValues {
return fsmStepTestValues{
currentState: current,
event: stopFSMEv,
wantState: expected,
wantErr: errNoErrorFinished}
}
func sUnknownFSMEv(current string) fsmStepTestValues {
return fsmStepTestValues{
currentState: current,
event: 1234,
wantState: current,
wantErr: errInvalidEvent}
}
func sStartFSMEv() fsmStepTestValues {
return fsmStepTestValues{
currentState: "unknown",
event: startFSMEv,
wantState: "waitForPeer",
wantStatusReqSent: true}
}
func sStateTimeoutEv(current, expected string, timedoutState string, wantErr error) fsmStepTestValues {
return fsmStepTestValues{
currentState: current,
event: stateTimeoutEv,
data: bReactorEventData{
stateName: timedoutState,
},
wantState: expected,
wantErr: wantErr,
}
}
func sProcessedBlockEv(current, expected string, reactorError error) fsmStepTestValues {
return fsmStepTestValues{
currentState: current,
event: processedBlockEv,
data: bReactorEventData{
err: reactorError,
},
wantState: expected,
wantErr: reactorError,
}
}
func sStatusEv(current, expected string, peerID p2p.ID, height int64, err error) fsmStepTestValues {
return fsmStepTestValues{
currentState: current,
event: statusResponseEv,
data: bReactorEventData{peerID: peerID, height: height},
wantState: expected,
wantErr: err}
}
func sMakeRequestsEv(current, expected string, maxPendingRequests int) fsmStepTestValues {
return fsmStepTestValues{
currentState: current,
event: makeRequestsEv,
data: bReactorEventData{maxNumRequests: maxPendingRequests},
wantState: expected,
wantReqIncreased: true,
}
}
func sMakeRequestsEvErrored(current, expected string,
maxPendingRequests int, err error, peersRemoved []p2p.ID) fsmStepTestValues {
return fsmStepTestValues{
currentState: current,
event: makeRequestsEv,
data: bReactorEventData{maxNumRequests: maxPendingRequests},
wantState: expected,
wantErr: err,
wantRemovedPeers: peersRemoved,
wantReqIncreased: true,
}
}
func sBlockRespEv(current, expected string, peerID p2p.ID, height int64, prevBlocks []int64) fsmStepTestValues {
txs := []types.Tx{types.Tx("foo"), types.Tx("bar")}
return fsmStepTestValues{
currentState: current,
event: blockResponseEv,
data: bReactorEventData{
peerID: peerID,
height: height,
block: types.MakeBlock(height, txs, nil, nil),
length: 100},
wantState: expected,
wantNewBlocks: append(prevBlocks, height),
}
}
func sBlockRespEvErrored(current, expected string,
peerID p2p.ID, height int64, prevBlocks []int64, wantErr error, peersRemoved []p2p.ID) fsmStepTestValues {
txs := []types.Tx{types.Tx("foo"), types.Tx("bar")}
return fsmStepTestValues{
currentState: current,
event: blockResponseEv,
data: bReactorEventData{
peerID: peerID,
height: height,
block: types.MakeBlock(height, txs, nil, nil),
length: 100},
wantState: expected,
wantErr: wantErr,
wantRemovedPeers: peersRemoved,
wantNewBlocks: prevBlocks,
}
}
func sPeerRemoveEv(current, expected string, peerID p2p.ID, err error, peersRemoved []p2p.ID) fsmStepTestValues {
return fsmStepTestValues{
currentState: current,
event: peerRemoveEv,
data: bReactorEventData{
peerID: peerID,
err: err,
},
wantState: expected,
wantRemovedPeers: peersRemoved,
}
}
// --------------------------------------------
func newTestReactor(height int64) *testReactor {
testBcR := &testReactor{logger: log.TestingLogger(), stateTimerStarts: make(map[string]int)}
testBcR.fsm = NewFSM(height, testBcR)
testBcR.fsm.SetLogger(testBcR.logger)
return testBcR
}
func fixBlockResponseEvStep(step *fsmStepTestValues, testBcR *testReactor) {
// There is currently no good way to know to which peer a block request was sent.
// So in some cases where it does not matter, before we simulate a block response
// we cheat and look where it is expected from.
if step.event == blockResponseEv {
height := step.data.height
peerID, ok := testBcR.fsm.pool.blocks[height]
if ok {
step.data.peerID = peerID
}
}
}
type testFields struct {
name string
startingHeight int64
maxRequestsPerPeer int
maxPendingRequests int
steps []fsmStepTestValues
}
func executeFSMTests(t *testing.T, tests []testFields, matchRespToReq bool) {
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
// Create test reactor
testBcR := newTestReactor(tt.startingHeight)
if tt.maxRequestsPerPeer != 0 {
maxRequestsPerPeer = tt.maxRequestsPerPeer
}
for _, step := range tt.steps {
step := step
assert.Equal(t, step.currentState, testBcR.fsm.state.name)
var heightBefore int64
if step.event == processedBlockEv && step.data.err == errBlockVerificationFailure {
heightBefore = testBcR.fsm.pool.Height
}
oldNumStatusRequests := testBcR.numStatusRequests
oldNumBlockRequests := testBcR.numBlockRequests
if matchRespToReq {
fixBlockResponseEvStep(&step, testBcR)
}
fsmErr := sendEventToFSM(testBcR.fsm, step.event, step.data)
assert.Equal(t, step.wantErr, fsmErr)
if step.wantStatusReqSent {
assert.Equal(t, oldNumStatusRequests+1, testBcR.numStatusRequests)
} else {
assert.Equal(t, oldNumStatusRequests, testBcR.numStatusRequests)
}
if step.wantReqIncreased {
assert.True(t, oldNumBlockRequests < testBcR.numBlockRequests)
} else {
assert.Equal(t, oldNumBlockRequests, testBcR.numBlockRequests)
}
for _, height := range step.wantNewBlocks {
_, err := testBcR.fsm.pool.BlockAndPeerAtHeight(height)
assert.Nil(t, err)
}
if step.event == processedBlockEv && step.data.err == errBlockVerificationFailure {
heightAfter := testBcR.fsm.pool.Height
assert.Equal(t, heightBefore, heightAfter)
firstAfter, err1 := testBcR.fsm.pool.BlockAndPeerAtHeight(testBcR.fsm.pool.Height)
secondAfter, err2 := testBcR.fsm.pool.BlockAndPeerAtHeight(testBcR.fsm.pool.Height + 1)
assert.NotNil(t, err1)
assert.NotNil(t, err2)
assert.Nil(t, firstAfter)
assert.Nil(t, secondAfter)
}
assert.Equal(t, step.wantState, testBcR.fsm.state.name)
if step.wantState == "finished" {
assert.True(t, testBcR.fsm.isCaughtUp())
}
}
})
}
}
func TestFSMBasic(t *testing.T) {
tests := []testFields{
{
name: "one block, one peer - TS2",
startingHeight: 1,
maxRequestsPerPeer: 2,
steps: []fsmStepTestValues{
sStartFSMEv(),
sStatusEv("waitForPeer", "waitForBlock", "P1", 2, nil),
sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
sBlockRespEv("waitForBlock", "waitForBlock", "P1", 1, []int64{}),
sBlockRespEv("waitForBlock", "waitForBlock", "P2", 2, []int64{1}),
sProcessedBlockEv("waitForBlock", "finished", nil),
},
},
{
name: "multi block, multi peer - TS2",
startingHeight: 1,
maxRequestsPerPeer: 2,
steps: []fsmStepTestValues{
sStartFSMEv(),
sStatusEv("waitForPeer", "waitForBlock", "P1", 4, nil),
sStatusEv("waitForBlock", "waitForBlock", "P2", 4, nil),
sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
sBlockRespEv("waitForBlock", "waitForBlock", "P1", 1, []int64{}),
sBlockRespEv("waitForBlock", "waitForBlock", "P1", 2, []int64{1}),
sBlockRespEv("waitForBlock", "waitForBlock", "P2", 3, []int64{1, 2}),
sBlockRespEv("waitForBlock", "waitForBlock", "P2", 4, []int64{1, 2, 3}),
sProcessedBlockEv("waitForBlock", "waitForBlock", nil),
sProcessedBlockEv("waitForBlock", "waitForBlock", nil),
sProcessedBlockEv("waitForBlock", "finished", nil),
},
},
}
executeFSMTests(t, tests, true)
}
func TestFSMBlockVerificationFailure(t *testing.T) {
tests := []testFields{
{
name: "block verification failure - TS2 variant",
startingHeight: 1,
maxRequestsPerPeer: 3,
steps: []fsmStepTestValues{
sStartFSMEv(),
// add P1 and get blocks 1-3 from it
sStatusEv("waitForPeer", "waitForBlock", "P1", 3, nil),
sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
sBlockRespEv("waitForBlock", "waitForBlock", "P1", 1, []int64{}),
sBlockRespEv("waitForBlock", "waitForBlock", "P1", 2, []int64{1}),
sBlockRespEv("waitForBlock", "waitForBlock", "P1", 3, []int64{1, 2}),
// add P2
sStatusEv("waitForBlock", "waitForBlock", "P2", 3, nil),
// process block failure, should remove P1 and all blocks
sProcessedBlockEv("waitForBlock", "waitForBlock", errBlockVerificationFailure),
// get blocks 1-3 from P2
sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
sBlockRespEv("waitForBlock", "waitForBlock", "P2", 1, []int64{}),
sBlockRespEv("waitForBlock", "waitForBlock", "P2", 2, []int64{1}),
sBlockRespEv("waitForBlock", "waitForBlock", "P2", 3, []int64{1, 2}),
// finish after processing blocks 1 and 2
sProcessedBlockEv("waitForBlock", "waitForBlock", nil),
sProcessedBlockEv("waitForBlock", "finished", nil),
},
},
}
executeFSMTests(t, tests, false)
}
func TestFSMBadBlockFromPeer(t *testing.T) {
tests := []testFields{
{
name: "block we haven't asked for",
startingHeight: 1,
maxRequestsPerPeer: 3,
steps: []fsmStepTestValues{
sStartFSMEv(),
// add P1 and ask for blocks 1-3
sStatusEv("waitForPeer", "waitForBlock", "P1", 300, nil),
sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
// blockResponseEv for height 100 should cause an error
sBlockRespEvErrored("waitForBlock", "waitForPeer",
"P1", 100, []int64{}, errMissingBlock, []p2p.ID{}),
},
},
{
name: "block we already have",
startingHeight: 1,
maxRequestsPerPeer: 3,
steps: []fsmStepTestValues{
sStartFSMEv(),
// add P1 and get block 1
sStatusEv("waitForPeer", "waitForBlock", "P1", 100, nil),
sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
sBlockRespEv("waitForBlock", "waitForBlock",
"P1", 1, []int64{}),
// Get block 1 again. Since peer is removed together with block 1,
// the blocks present in the pool should be {}
sBlockRespEvErrored("waitForBlock", "waitForPeer",
"P1", 1, []int64{}, errDuplicateBlock, []p2p.ID{"P1"}),
},
},
{
name: "block from unknown peer",
startingHeight: 1,
maxRequestsPerPeer: 3,
steps: []fsmStepTestValues{
sStartFSMEv(),
// add P1 and get block 1
sStatusEv("waitForPeer", "waitForBlock", "P1", 3, nil),
// get block 1 from unknown peer P2
sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
sBlockRespEvErrored("waitForBlock", "waitForBlock",
"P2", 1, []int64{}, errBadDataFromPeer, []p2p.ID{"P2"}),
},
},
{
name: "block from wrong peer",
startingHeight: 1,
maxRequestsPerPeer: 3,
steps: []fsmStepTestValues{
sStartFSMEv(),
// add P1, make requests for blocks 1-3 to P1
sStatusEv("waitForPeer", "waitForBlock", "P1", 3, nil),
sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
// add P2
sStatusEv("waitForBlock", "waitForBlock", "P2", 3, nil),
// receive block 1 from P2
sBlockRespEvErrored("waitForBlock", "waitForBlock",
"P2", 1, []int64{}, errBadDataFromPeer, []p2p.ID{"P2"}),
},
},
}
executeFSMTests(t, tests, false)
}
func TestFSMBlockAtCurrentHeightDoesNotArriveInTime(t *testing.T) {
tests := []testFields{
{
name: "block at current height undelivered - TS5",
startingHeight: 1,
maxRequestsPerPeer: 3,
steps: []fsmStepTestValues{
sStartFSMEv(),
// add P1, get blocks 1 and 2, process block 1
sStatusEv("waitForPeer", "waitForBlock", "P1", 3, nil),
sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
sBlockRespEv("waitForBlock", "waitForBlock",
"P1", 1, []int64{}),
sBlockRespEv("waitForBlock", "waitForBlock",
"P1", 2, []int64{1}),
sProcessedBlockEv("waitForBlock", "waitForBlock", nil),
// add P2
sStatusEv("waitForBlock", "waitForBlock", "P2", 3, nil),
// timeout on block 3, P1 should be removed
sStateTimeoutEv("waitForBlock", "waitForBlock", "waitForBlock", errNoPeerResponseForCurrentHeights),
// make requests and finish by receiving blocks 2 and 3 from P2
sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
sBlockRespEv("waitForBlock", "waitForBlock", "P2", 2, []int64{}),
sBlockRespEv("waitForBlock", "waitForBlock", "P2", 3, []int64{2}),
sProcessedBlockEv("waitForBlock", "finished", nil),
},
},
{
name: "block at current height undelivered, at maxPeerHeight after peer removal - TS3",
startingHeight: 1,
maxRequestsPerPeer: 3,
steps: []fsmStepTestValues{
sStartFSMEv(),
// add P1, request blocks 1-3 from P1
sStatusEv("waitForPeer", "waitForBlock", "P1", 3, nil),
sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
// add P2 (tallest)
sStatusEv("waitForBlock", "waitForBlock", "P2", 30, nil),
sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
// receive blocks 1-3 from P1
sBlockRespEv("waitForBlock", "waitForBlock", "P1", 1, []int64{}),
sBlockRespEv("waitForBlock", "waitForBlock", "P1", 2, []int64{1}),
sBlockRespEv("waitForBlock", "waitForBlock", "P1", 3, []int64{1, 2}),
// process blocks at heights 1 and 2
sProcessedBlockEv("waitForBlock", "waitForBlock", nil),
sProcessedBlockEv("waitForBlock", "waitForBlock", nil),
// timeout on block at height 4
sStateTimeoutEv("waitForBlock", "finished", "waitForBlock", nil),
},
},
}
executeFSMTests(t, tests, true)
}
func TestFSMPeerRelatedEvents(t *testing.T) {
tests := []testFields{
{
name: "peer remove event with no blocks",
startingHeight: 1,
steps: []fsmStepTestValues{
sStartFSMEv(),
// add P1, P2, P3
sStatusEv("waitForPeer", "waitForBlock", "P1", 3, nil),
sStatusEv("waitForBlock", "waitForBlock", "P2", 3, nil),
sStatusEv("waitForBlock", "waitForBlock", "P3", 3, nil),
// switch removes P2
sPeerRemoveEv("waitForBlock", "waitForBlock", "P2", errSwitchRemovesPeer, []p2p.ID{"P2"}),
},
},
{
name: "only peer removed while in waitForBlock state",
startingHeight: 100,
steps: []fsmStepTestValues{
sStartFSMEv(),
// add P1
sStatusEv("waitForPeer", "waitForBlock", "P1", 200, nil),
// switch removes P1
sPeerRemoveEv("waitForBlock", "waitForPeer", "P1", errSwitchRemovesPeer, []p2p.ID{"P1"}),
},
},
{
name: "highest peer removed while in waitForBlock state, node reaches maxPeerHeight - TS4 ",
startingHeight: 100,
maxRequestsPerPeer: 3,
steps: []fsmStepTestValues{
sStartFSMEv(),
// add P1 and make requests
sStatusEv("waitForPeer", "waitForBlock", "P1", 101, nil),
sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
// add P2
sStatusEv("waitForBlock", "waitForBlock", "P2", 200, nil),
// get blocks 100 and 101 from P1 and process block at height 100
sBlockRespEv("waitForBlock", "waitForBlock", "P1", 100, []int64{}),
sBlockRespEv("waitForBlock", "waitForBlock", "P1", 101, []int64{100}),
sProcessedBlockEv("waitForBlock", "waitForBlock", nil),
// switch removes peer P1, should be finished
sPeerRemoveEv("waitForBlock", "finished", "P2", errSwitchRemovesPeer, []p2p.ID{"P2"}),
},
},
{
name: "highest peer lowers its height in waitForBlock state, node reaches maxPeerHeight - TS4",
startingHeight: 100,
maxRequestsPerPeer: 3,
steps: []fsmStepTestValues{
sStartFSMEv(),
// add P1 and make requests
sStatusEv("waitForPeer", "waitForBlock", "P1", 101, nil),
sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
// add P2
sStatusEv("waitForBlock", "waitForBlock", "P2", 200, nil),
// get blocks 100 and 101 from P1
sBlockRespEv("waitForBlock", "waitForBlock", "P1", 100, []int64{}),
sBlockRespEv("waitForBlock", "waitForBlock", "P1", 101, []int64{100}),
// processed block at heights 100
sProcessedBlockEv("waitForBlock", "waitForBlock", nil),
// P2 becomes short
sStatusEv("waitForBlock", "finished", "P2", 100, errPeerLowersItsHeight),
},
},
{
name: "new short peer while in waitForPeer state",
startingHeight: 100,
steps: []fsmStepTestValues{
sStartFSMEv(),
sStatusEv("waitForPeer", "waitForPeer", "P1", 3, errPeerTooShort),
},
},
{
name: "new short peer while in waitForBlock state",
startingHeight: 100,
steps: []fsmStepTestValues{
sStartFSMEv(),
sStatusEv("waitForPeer", "waitForBlock", "P1", 200, nil),
sStatusEv("waitForBlock", "waitForBlock", "P2", 3, errPeerTooShort),
},
},
{
name: "only peer updated with low height while in waitForBlock state",
startingHeight: 100,
steps: []fsmStepTestValues{
sStartFSMEv(),
sStatusEv("waitForPeer", "waitForBlock", "P1", 200, nil),
sStatusEv("waitForBlock", "waitForPeer", "P1", 3, errPeerLowersItsHeight),
},
},
{
name: "peer does not exist in the switch",
startingHeight: 9999999,
maxRequestsPerPeer: 3,
steps: []fsmStepTestValues{
sStartFSMEv(),
// add P1
sStatusEv("waitForPeer", "waitForBlock", "P1", 20000000, nil),
// send request for block 9999999
// Note: For this block request the "switch missing the peer" error is simulated,
// see implementation of bcReactor interface, sendBlockRequest(), in this file.
sMakeRequestsEvErrored("waitForBlock", "waitForBlock",
maxNumRequests, nil, []p2p.ID{"P1"}),
},
},
}
executeFSMTests(t, tests, true)
}
func TestFSMStopFSM(t *testing.T) {
tests := []testFields{
{
name: "stopFSMEv in unknown",
steps: []fsmStepTestValues{
sStopFSMEv("unknown", "finished"),
},
},
{
name: "stopFSMEv in waitForPeer",
startingHeight: 1,
steps: []fsmStepTestValues{
sStartFSMEv(),
sStopFSMEv("waitForPeer", "finished"),
},
},
{
name: "stopFSMEv in waitForBlock",
startingHeight: 1,
steps: []fsmStepTestValues{
sStartFSMEv(),
sStatusEv("waitForPeer", "waitForBlock", "P1", 3, nil),
sStopFSMEv("waitForBlock", "finished"),
},
},
}
executeFSMTests(t, tests, false)
}
func TestFSMUnknownElements(t *testing.T) {
tests := []testFields{
{
name: "unknown event for state unknown",
steps: []fsmStepTestValues{
sUnknownFSMEv("unknown"),
},
},
{
name: "unknown event for state waitForPeer",
steps: []fsmStepTestValues{
sStartFSMEv(),
sUnknownFSMEv("waitForPeer"),
},
},
{
name: "unknown event for state waitForBlock",
startingHeight: 1,
steps: []fsmStepTestValues{
sStartFSMEv(),
sStatusEv("waitForPeer", "waitForBlock", "P1", 3, nil),
sUnknownFSMEv("waitForBlock"),
},
},
}
executeFSMTests(t, tests, false)
}
func TestFSMPeerStateTimeoutEvent(t *testing.T) {
tests := []testFields{
{
name: "timeout event for state waitForPeer while in state waitForPeer - TS1",
startingHeight: 1,
maxRequestsPerPeer: 3,
steps: []fsmStepTestValues{
sStartFSMEv(),
sStateTimeoutEv("waitForPeer", "finished", "waitForPeer", errNoTallerPeer),
},
},
{
name: "timeout event for state waitForPeer while in a state != waitForPeer",
startingHeight: 1,
maxRequestsPerPeer: 3,
steps: []fsmStepTestValues{
sStartFSMEv(),
sStateTimeoutEv("waitForPeer", "waitForPeer", "waitForBlock", errTimeoutEventWrongState),
},
},
{
name: "timeout event for state waitForBlock while in state waitForBlock ",
startingHeight: 1,
maxRequestsPerPeer: 3,
steps: []fsmStepTestValues{
sStartFSMEv(),
sStatusEv("waitForPeer", "waitForBlock", "P1", 3, nil),
sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
sStateTimeoutEv("waitForBlock", "waitForPeer", "waitForBlock", errNoPeerResponseForCurrentHeights),
},
},
{
name: "timeout event for state waitForBlock while in a state != waitForBlock",
startingHeight: 1,
maxRequestsPerPeer: 3,
steps: []fsmStepTestValues{
sStartFSMEv(),
sStatusEv("waitForPeer", "waitForBlock", "P1", 3, nil),
sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
sStateTimeoutEv("waitForBlock", "waitForBlock", "waitForPeer", errTimeoutEventWrongState),
},
},
{
name: "timeout event for state waitForBlock with multiple peers",
startingHeight: 1,
maxRequestsPerPeer: 3,
steps: []fsmStepTestValues{
sStartFSMEv(),
sStatusEv("waitForPeer", "waitForBlock", "P1", 3, nil),
sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
sStatusEv("waitForBlock", "waitForBlock", "P2", 3, nil),
sStateTimeoutEv("waitForBlock", "waitForBlock", "waitForBlock", errNoPeerResponseForCurrentHeights),
},
},
}
executeFSMTests(t, tests, false)
}
func makeCorrectTransitionSequence(startingHeight int64, numBlocks int64, numPeers int, randomPeerHeights bool,
maxRequestsPerPeer int, maxPendingRequests int) testFields {
// Generate numPeers peers with random or numBlocks heights according to the randomPeerHeights flag.
peerHeights := make([]int64, numPeers)
for i := 0; i < numPeers; i++ {
if i == 0 {
peerHeights[0] = numBlocks
continue
}
if randomPeerHeights {
peerHeights[i] = int64(cmn.MaxInt(cmn.RandIntn(int(numBlocks)), int(startingHeight)+1))
} else {
peerHeights[i] = numBlocks
}
}
// Approximate the slice capacity to save time for appends.
testSteps := make([]fsmStepTestValues, 0, 3*numBlocks+int64(numPeers))
testName := fmt.Sprintf("%v-blocks %v-startingHeight %v-peers %v-maxRequestsPerPeer %v-maxNumRequests",
numBlocks, startingHeight, numPeers, maxRequestsPerPeer, maxPendingRequests)
// Add startFSMEv step.
testSteps = append(testSteps, sStartFSMEv())
// For each peer, add statusResponseEv step.
for i := 0; i < numPeers; i++ {
peerName := fmt.Sprintf("P%d", i)
if i == 0 {
testSteps = append(
testSteps,
sStatusEv("waitForPeer", "waitForBlock", p2p.ID(peerName), peerHeights[i], nil))
} else {
testSteps = append(testSteps,
sStatusEv("waitForBlock", "waitForBlock", p2p.ID(peerName), peerHeights[i], nil))
}
}
height := startingHeight
numBlocksReceived := 0
prevBlocks := make([]int64, 0, maxPendingRequests)
forLoop:
for i := 0; i < int(numBlocks); i++ {
// Add the makeRequestEv step periodically.
if i%maxRequestsPerPeer == 0 {
testSteps = append(
testSteps,
sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
)
}
// Add the blockRespEv step
testSteps = append(
testSteps,
sBlockRespEv("waitForBlock", "waitForBlock",
"P0", height, prevBlocks))
prevBlocks = append(prevBlocks, height)
height++
numBlocksReceived++
// Add the processedBlockEv step periodically.
if numBlocksReceived >= maxRequestsPerPeer || height >= numBlocks {
for j := int(height) - numBlocksReceived; j < int(height); j++ {
if j >= int(numBlocks) {
// This is the last block that is processed, we should be in "finished" state.
testSteps = append(
testSteps,
sProcessedBlockEv("waitForBlock", "finished", nil))
break forLoop
}
testSteps = append(
testSteps,
sProcessedBlockEv("waitForBlock", "waitForBlock", nil))
}
numBlocksReceived = 0
prevBlocks = make([]int64, 0, maxPendingRequests)
}
}
return testFields{
name: testName,
startingHeight: startingHeight,
maxRequestsPerPeer: maxRequestsPerPeer,
maxPendingRequests: maxPendingRequests,
steps: testSteps,
}
}
const (
maxStartingHeightTest = 100
maxRequestsPerPeerTest = 20
maxTotalPendingRequestsTest = 600
maxNumPeersTest = 1000
maxNumBlocksInChainTest = 10000 //should be smaller than 9999999
)
func makeCorrectTransitionSequenceWithRandomParameters() testFields {
// Generate a starting height for fast sync.
startingHeight := int64(cmn.RandIntn(maxStartingHeightTest) + 1)
// Generate the number of requests per peer.
maxRequestsPerPeer := cmn.RandIntn(maxRequestsPerPeerTest) + 1
// Generate the maximum number of total pending requests, >= maxRequestsPerPeer.
maxPendingRequests := cmn.RandIntn(maxTotalPendingRequestsTest-maxRequestsPerPeer) + maxRequestsPerPeer
// Generate the number of blocks to be synced.
numBlocks := int64(cmn.RandIntn(maxNumBlocksInChainTest)) + startingHeight
// Generate a number of peers.
numPeers := cmn.RandIntn(maxNumPeersTest) + 1
return makeCorrectTransitionSequence(startingHeight, numBlocks, numPeers, true, maxRequestsPerPeer, maxPendingRequests)
}
func shouldApplyProcessedBlockEvStep(step *fsmStepTestValues, testBcR *testReactor) bool {
if step.event == processedBlockEv {
_, err := testBcR.fsm.pool.BlockAndPeerAtHeight(testBcR.fsm.pool.Height)
if err == errMissingBlock {
return false
}
_, err = testBcR.fsm.pool.BlockAndPeerAtHeight(testBcR.fsm.pool.Height + 1)
if err == errMissingBlock {
return false
}
}
return true
}
func TestFSMCorrectTransitionSequences(t *testing.T) {
tests := []testFields{
makeCorrectTransitionSequence(1, 100, 10, true, 10, 40),
makeCorrectTransitionSequenceWithRandomParameters(),
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
// Create test reactor
testBcR := newTestReactor(tt.startingHeight)
if tt.maxRequestsPerPeer != 0 {
maxRequestsPerPeer = tt.maxRequestsPerPeer
}
for _, step := range tt.steps {
step := step
assert.Equal(t, step.currentState, testBcR.fsm.state.name)
oldNumStatusRequests := testBcR.numStatusRequests
fixBlockResponseEvStep(&step, testBcR)
if !shouldApplyProcessedBlockEvStep(&step, testBcR) {
continue
}
fsmErr := sendEventToFSM(testBcR.fsm, step.event, step.data)
assert.Equal(t, step.wantErr, fsmErr)
if step.wantStatusReqSent {
assert.Equal(t, oldNumStatusRequests+1, testBcR.numStatusRequests)
} else {
assert.Equal(t, oldNumStatusRequests, testBcR.numStatusRequests)
}
assert.Equal(t, step.wantState, testBcR.fsm.state.name)
if step.wantState == "finished" {
assert.True(t, testBcR.fsm.isCaughtUp())
}
}
})
}
}
// ----------------------------------------
// implements the bcRNotifier
func (testR *testReactor) sendPeerError(err error, peerID p2p.ID) {
testR.logger.Info("Reactor received sendPeerError call from FSM", "peer", peerID, "err", err)
testR.lastPeerError.peerID = peerID
testR.lastPeerError.err = err
}
func (testR *testReactor) sendStatusRequest() {
testR.logger.Info("Reactor received sendStatusRequest call from FSM")
testR.numStatusRequests++
}
func (testR *testReactor) sendBlockRequest(peerID p2p.ID, height int64) error {
testR.logger.Info("Reactor received sendBlockRequest call from FSM", "peer", peerID, "height", height)
testR.numBlockRequests++
testR.lastBlockRequest.peerID = peerID
testR.lastBlockRequest.height = height
if height == 9999999 {
// simulate switch does not have peer
return errNilPeerForBlockRequest
}
return nil
}
func (testR *testReactor) resetStateTimer(name string, timer **time.Timer, timeout time.Duration) {
testR.logger.Info("Reactor received resetStateTimer call from FSM", "state", name, "timeout", timeout)
if _, ok := testR.stateTimerStarts[name]; !ok {
testR.stateTimerStarts[name] = 1
} else {
testR.stateTimerStarts[name]++
}
}
func (testR *testReactor) switchToConsensus() {
}
// ----------------------------------------

View File

@@ -0,0 +1,417 @@
package v1
import (
"fmt"
"os"
"sort"
"sync"
"testing"
"time"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
abci "github.com/tendermint/tendermint/abci/types"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/mock"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/proxy"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/store"
"github.com/tendermint/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time"
dbm "github.com/tendermint/tm-db"
)
var config *cfg.Config
func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.GenesisDoc, []types.PrivValidator) {
validators := make([]types.GenesisValidator, numValidators)
privValidators := make([]types.PrivValidator, numValidators)
for i := 0; i < numValidators; i++ {
val, privVal := types.RandValidator(randPower, minPower)
validators[i] = types.GenesisValidator{
PubKey: val.PubKey,
Power: val.VotingPower,
}
privValidators[i] = privVal
}
sort.Sort(types.PrivValidatorsByAddress(privValidators))
return &types.GenesisDoc{
GenesisTime: tmtime.Now(),
ChainID: config.ChainID(),
Validators: validators,
}, privValidators
}
func makeVote(header *types.Header, blockID types.BlockID, valset *types.ValidatorSet, privVal types.PrivValidator) *types.Vote {
addr := privVal.GetPubKey().Address()
idx, _ := valset.GetByAddress(addr)
vote := &types.Vote{
ValidatorAddress: addr,
ValidatorIndex: idx,
Height: header.Height,
Round: 1,
Timestamp: tmtime.Now(),
Type: types.PrecommitType,
BlockID: blockID,
}
_ = privVal.SignVote(header.ChainID, vote)
return vote
}
type BlockchainReactorPair struct {
bcR *BlockchainReactor
conR *consensusReactorTest
}
func newBlockchainReactor(logger log.Logger, genDoc *types.GenesisDoc, privVals []types.PrivValidator, maxBlockHeight int64) *BlockchainReactor {
if len(privVals) != 1 {
panic("only support one validator")
}
app := &testApp{}
cc := proxy.NewLocalClientCreator(app)
proxyApp := proxy.NewAppConns(cc)
err := proxyApp.Start()
if err != nil {
panic(errors.Wrap(err, "error start app"))
}
blockDB := dbm.NewMemDB()
stateDB := dbm.NewMemDB()
blockStore := store.NewBlockStore(blockDB)
state, err := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
if err != nil {
panic(errors.Wrap(err, "error constructing state from genesis file"))
}
// Make the BlockchainReactor itself.
// NOTE we have to create and commit the blocks first because
// pool.height is determined from the store.
fastSync := true
db := dbm.NewMemDB()
blockExec := sm.NewBlockExecutor(db, log.TestingLogger(), proxyApp.Consensus(),
mock.Mempool{}, sm.MockEvidencePool{})
sm.SaveState(db, state)
// let's add some blocks in
for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ {
lastCommit := types.NewCommit(types.BlockID{}, nil)
if blockHeight > 1 {
lastBlockMeta := blockStore.LoadBlockMeta(blockHeight - 1)
lastBlock := blockStore.LoadBlock(blockHeight - 1)
vote := makeVote(&lastBlock.Header, lastBlockMeta.BlockID, state.Validators, privVals[0]).CommitSig()
lastCommit = types.NewCommit(lastBlockMeta.BlockID, []*types.CommitSig{vote})
}
thisBlock := makeBlock(blockHeight, state, lastCommit)
thisParts := thisBlock.MakePartSet(types.BlockPartSizeBytes)
blockID := types.BlockID{Hash: thisBlock.Hash(), PartsHeader: thisParts.Header()}
state, err = blockExec.ApplyBlock(state, blockID, thisBlock)
if err != nil {
panic(errors.Wrap(err, "error apply block"))
}
blockStore.SaveBlock(thisBlock, thisParts, lastCommit)
}
bcReactor := NewBlockchainReactor(state.Copy(), blockExec, blockStore, fastSync)
bcReactor.SetLogger(logger.With("module", "blockchain"))
return bcReactor
}
func newBlockchainReactorPair(logger log.Logger, genDoc *types.GenesisDoc, privVals []types.PrivValidator, maxBlockHeight int64) BlockchainReactorPair {
consensusReactor := &consensusReactorTest{}
consensusReactor.BaseReactor = *p2p.NewBaseReactor("Consensus reactor", consensusReactor)
return BlockchainReactorPair{
newBlockchainReactor(logger, genDoc, privVals, maxBlockHeight),
consensusReactor}
}
type consensusReactorTest struct {
p2p.BaseReactor // BaseService + p2p.Switch
switchedToConsensus bool
mtx sync.Mutex
}
func (conR *consensusReactorTest) SwitchToConsensus(state sm.State, blocksSynced int) {
conR.mtx.Lock()
defer conR.mtx.Unlock()
conR.switchedToConsensus = true
}
func TestFastSyncNoBlockResponse(t *testing.T) {
config = cfg.ResetTestRoot("blockchain_new_reactor_test")
defer os.RemoveAll(config.RootDir)
genDoc, privVals := randGenesisDoc(1, false, 30)
maxBlockHeight := int64(65)
reactorPairs := make([]BlockchainReactorPair, 2)
logger := log.TestingLogger()
reactorPairs[0] = newBlockchainReactorPair(logger, genDoc, privVals, maxBlockHeight)
reactorPairs[1] = newBlockchainReactorPair(logger, genDoc, privVals, 0)
p2p.MakeConnectedSwitches(config.P2P, 2, func(i int, s *p2p.Switch) *p2p.Switch {
s.AddReactor("BLOCKCHAIN", reactorPairs[i].bcR)
s.AddReactor("CONSENSUS", reactorPairs[i].conR)
moduleName := fmt.Sprintf("blockchain-%v", i)
reactorPairs[i].bcR.SetLogger(logger.With("module", moduleName))
return s
}, p2p.Connect2Switches)
defer func() {
for _, r := range reactorPairs {
_ = r.bcR.Stop()
_ = r.conR.Stop()
}
}()
tests := []struct {
height int64
existent bool
}{
{maxBlockHeight + 2, false},
{10, true},
{1, true},
{maxBlockHeight + 100, false},
}
for {
time.Sleep(10 * time.Millisecond)
reactorPairs[1].conR.mtx.Lock()
if reactorPairs[1].conR.switchedToConsensus {
reactorPairs[1].conR.mtx.Unlock()
break
}
reactorPairs[1].conR.mtx.Unlock()
}
assert.Equal(t, maxBlockHeight, reactorPairs[0].bcR.store.Height())
for _, tt := range tests {
block := reactorPairs[1].bcR.store.LoadBlock(tt.height)
if tt.existent {
assert.True(t, block != nil)
} else {
assert.True(t, block == nil)
}
}
}
// NOTE: This is too hard to test without
// an easy way to add test peer to switch
// or without significant refactoring of the module.
// Alternatively we could actually dial a TCP conn but
// that seems extreme.
func TestFastSyncBadBlockStopsPeer(t *testing.T) {
numNodes := 4
maxBlockHeight := int64(148)
config = cfg.ResetTestRoot("blockchain_reactor_test")
defer os.RemoveAll(config.RootDir)
genDoc, privVals := randGenesisDoc(1, false, 30)
otherChain := newBlockchainReactorPair(log.TestingLogger(), genDoc, privVals, maxBlockHeight)
defer func() {
_ = otherChain.bcR.Stop()
_ = otherChain.conR.Stop()
}()
reactorPairs := make([]BlockchainReactorPair, numNodes)
logger := make([]log.Logger, numNodes)
for i := 0; i < numNodes; i++ {
logger[i] = log.TestingLogger()
height := int64(0)
if i == 0 {
height = maxBlockHeight
}
reactorPairs[i] = newBlockchainReactorPair(logger[i], genDoc, privVals, height)
}
switches := p2p.MakeConnectedSwitches(config.P2P, numNodes, func(i int, s *p2p.Switch) *p2p.Switch {
reactorPairs[i].conR.mtx.Lock()
s.AddReactor("BLOCKCHAIN", reactorPairs[i].bcR)
s.AddReactor("CONSENSUS", reactorPairs[i].conR)
moduleName := fmt.Sprintf("blockchain-%v", i)
reactorPairs[i].bcR.SetLogger(logger[i].With("module", moduleName))
reactorPairs[i].conR.mtx.Unlock()
return s
}, p2p.Connect2Switches)
defer func() {
for _, r := range reactorPairs {
_ = r.bcR.Stop()
_ = r.conR.Stop()
}
}()
outerFor:
for {
time.Sleep(10 * time.Millisecond)
for i := 0; i < numNodes; i++ {
reactorPairs[i].conR.mtx.Lock()
if !reactorPairs[i].conR.switchedToConsensus {
reactorPairs[i].conR.mtx.Unlock()
continue outerFor
}
reactorPairs[i].conR.mtx.Unlock()
}
break
}
//at this time, reactors[0-3] is the newest
assert.Equal(t, numNodes-1, reactorPairs[1].bcR.Switch.Peers().Size())
//mark last reactorPair as an invalid peer
reactorPairs[numNodes-1].bcR.store = otherChain.bcR.store
lastLogger := log.TestingLogger()
lastReactorPair := newBlockchainReactorPair(lastLogger, genDoc, privVals, 0)
reactorPairs = append(reactorPairs, lastReactorPair)
switches = append(switches, p2p.MakeConnectedSwitches(config.P2P, 1, func(i int, s *p2p.Switch) *p2p.Switch {
s.AddReactor("BLOCKCHAIN", reactorPairs[len(reactorPairs)-1].bcR)
s.AddReactor("CONSENSUS", reactorPairs[len(reactorPairs)-1].conR)
moduleName := fmt.Sprintf("blockchain-%v", len(reactorPairs)-1)
reactorPairs[len(reactorPairs)-1].bcR.SetLogger(lastLogger.With("module", moduleName))
return s
}, p2p.Connect2Switches)...)
for i := 0; i < len(reactorPairs)-1; i++ {
p2p.Connect2Switches(switches, i, len(reactorPairs)-1)
}
for {
time.Sleep(1 * time.Second)
lastReactorPair.conR.mtx.Lock()
if lastReactorPair.conR.switchedToConsensus {
lastReactorPair.conR.mtx.Unlock()
break
}
lastReactorPair.conR.mtx.Unlock()
if lastReactorPair.bcR.Switch.Peers().Size() == 0 {
break
}
}
assert.True(t, lastReactorPair.bcR.Switch.Peers().Size() < len(reactorPairs)-1)
}
func TestBcBlockRequestMessageValidateBasic(t *testing.T) {
testCases := []struct {
testName string
requestHeight int64
expectErr bool
}{
{"Valid Request Message", 0, false},
{"Valid Request Message", 1, false},
{"Invalid Request Message", -1, true},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.testName, func(t *testing.T) {
request := bcBlockRequestMessage{Height: tc.requestHeight}
assert.Equal(t, tc.expectErr, request.ValidateBasic() != nil, "Validate Basic had an unexpected result")
})
}
}
func TestBcNoBlockResponseMessageValidateBasic(t *testing.T) {
testCases := []struct {
testName string
nonResponseHeight int64
expectErr bool
}{
{"Valid Non-Response Message", 0, false},
{"Valid Non-Response Message", 1, false},
{"Invalid Non-Response Message", -1, true},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.testName, func(t *testing.T) {
nonResponse := bcNoBlockResponseMessage{Height: tc.nonResponseHeight}
assert.Equal(t, tc.expectErr, nonResponse.ValidateBasic() != nil, "Validate Basic had an unexpected result")
})
}
}
func TestBcStatusRequestMessageValidateBasic(t *testing.T) {
testCases := []struct {
testName string
requestHeight int64
expectErr bool
}{
{"Valid Request Message", 0, false},
{"Valid Request Message", 1, false},
{"Invalid Request Message", -1, true},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.testName, func(t *testing.T) {
request := bcStatusRequestMessage{Height: tc.requestHeight}
assert.Equal(t, tc.expectErr, request.ValidateBasic() != nil, "Validate Basic had an unexpected result")
})
}
}
func TestBcStatusResponseMessageValidateBasic(t *testing.T) {
testCases := []struct {
testName string
responseHeight int64
expectErr bool
}{
{"Valid Response Message", 0, false},
{"Valid Response Message", 1, false},
{"Invalid Response Message", -1, true},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.testName, func(t *testing.T) {
response := bcStatusResponseMessage{Height: tc.responseHeight}
assert.Equal(t, tc.expectErr, response.ValidateBasic() != nil, "Validate Basic had an unexpected result")
})
}
}
//----------------------------------------------
// utility funcs
func makeTxs(height int64) (txs []types.Tx) {
for i := 0; i < 10; i++ {
txs = append(txs, types.Tx([]byte{byte(height), byte(i)}))
}
return txs
}
func makeBlock(height int64, state sm.State, lastCommit *types.Commit) *types.Block {
block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, state.Validators.GetProposer().Address)
return block
}
type testApp struct {
abci.BaseApplication
}

124
blockchain/v2/metrics.go Normal file
View File

@@ -0,0 +1,124 @@
package v2
import (
"github.com/go-kit/kit/metrics"
"github.com/go-kit/kit/metrics/discard"
"github.com/go-kit/kit/metrics/prometheus"
stdprometheus "github.com/prometheus/client_golang/prometheus"
)
const (
// MetricsSubsystem is a subsystem shared by all metrics exposed by this
// package.
MetricsSubsystem = "blockchain"
)
// Metrics contains metrics exposed by this package.
type Metrics struct {
// events_in
EventsIn metrics.Counter
// events_in
EventsHandled metrics.Counter
// events_out
EventsOut metrics.Counter
// errors_in
ErrorsIn metrics.Counter
// errors_handled
ErrorsHandled metrics.Counter
// errors_out
ErrorsOut metrics.Counter
// events_shed
EventsShed metrics.Counter
// events_sent
EventsSent metrics.Counter
// errors_sent
ErrorsSent metrics.Counter
// errors_shed
ErrorsShed metrics.Counter
}
// Can we burn in the routine name here?
func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics {
labels := []string{}
for i := 0; i < len(labelsAndValues); i += 2 {
labels = append(labels, labelsAndValues[i])
}
return &Metrics{
EventsIn: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: namespace,
Subsystem: MetricsSubsystem,
Name: "events_in",
Help: "Events read from the channel.",
}, labels).With(labelsAndValues...),
EventsHandled: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: namespace,
Subsystem: MetricsSubsystem,
Name: "events_handled",
Help: "Events handled",
}, labels).With(labelsAndValues...),
EventsOut: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: namespace,
Subsystem: MetricsSubsystem,
Name: "events_out",
Help: "Events output from routine.",
}, labels).With(labelsAndValues...),
ErrorsIn: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: namespace,
Subsystem: MetricsSubsystem,
Name: "errors_in",
Help: "Errors read from the channel.",
}, labels).With(labelsAndValues...),
ErrorsHandled: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: namespace,
Subsystem: MetricsSubsystem,
Name: "errors_handled",
Help: "Errors handled.",
}, labels).With(labelsAndValues...),
ErrorsOut: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: namespace,
Subsystem: MetricsSubsystem,
Name: "errors_out",
Help: "Errors output from routine.",
}, labels).With(labelsAndValues...),
ErrorsSent: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: namespace,
Subsystem: MetricsSubsystem,
Name: "errors_sent",
Help: "Errors sent to routine.",
}, labels).With(labelsAndValues...),
ErrorsShed: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: namespace,
Subsystem: MetricsSubsystem,
Name: "errors_shed",
Help: "Errors dropped from sending.",
}, labels).With(labelsAndValues...),
EventsSent: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: namespace,
Subsystem: MetricsSubsystem,
Name: "events_sent",
Help: "Events sent to routine.",
}, labels).With(labelsAndValues...),
EventsShed: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: namespace,
Subsystem: MetricsSubsystem,
Name: "events_shed",
Help: "Events dropped from sending.",
}, labels).With(labelsAndValues...),
}
}
// NopMetrics returns no-op Metrics.
func NopMetrics() *Metrics {
return &Metrics{
EventsIn: discard.NewCounter(),
EventsHandled: discard.NewCounter(),
EventsOut: discard.NewCounter(),
ErrorsIn: discard.NewCounter(),
ErrorsHandled: discard.NewCounter(),
ErrorsOut: discard.NewCounter(),
EventsShed: discard.NewCounter(),
EventsSent: discard.NewCounter(),
ErrorsSent: discard.NewCounter(),
ErrorsShed: discard.NewCounter(),
}
}

117
blockchain/v2/reactor.go Normal file
View File

@@ -0,0 +1,117 @@
package v2
import (
"fmt"
"time"
"github.com/tendermint/tendermint/libs/log"
)
type timeCheck struct {
priorityHigh
time time.Time
}
func schedulerHandle(event Event) (Event, error) {
if _, ok := event.(timeCheck); ok {
fmt.Println("scheduler handle timeCheck")
}
return noOp, nil
}
func processorHandle(event Event) (Event, error) {
if _, ok := event.(timeCheck); ok {
fmt.Println("processor handle timeCheck")
}
return noOp, nil
}
type Reactor struct {
events chan Event
stopDemux chan struct{}
scheduler *Routine
processor *Routine
ticker *time.Ticker
logger log.Logger
}
func NewReactor(bufferSize int) *Reactor {
return &Reactor{
events: make(chan Event, bufferSize),
stopDemux: make(chan struct{}),
scheduler: newRoutine("scheduler", schedulerHandle, bufferSize),
processor: newRoutine("processor", processorHandle, bufferSize),
ticker: time.NewTicker(1 * time.Second),
logger: log.NewNopLogger(),
}
}
// nolint:unused
func (r *Reactor) setLogger(logger log.Logger) {
r.logger = logger
r.scheduler.setLogger(logger)
r.processor.setLogger(logger)
}
func (r *Reactor) Start() {
go r.scheduler.start()
go r.processor.start()
go r.demux()
<-r.scheduler.ready()
<-r.processor.ready()
go func() {
for t := range r.ticker.C {
r.events <- timeCheck{time: t}
}
}()
}
// XXX: Would it be possible here to provide some kind of type safety for the types
// of events that each routine can produce and consume?
func (r *Reactor) demux() {
for {
select {
case event := <-r.events:
// XXX: check for backpressure
r.scheduler.send(event)
r.processor.send(event)
case <-r.stopDemux:
r.logger.Info("demuxing stopped")
return
case event := <-r.scheduler.next():
r.processor.send(event)
case event := <-r.processor.next():
r.scheduler.send(event)
case err := <-r.scheduler.final():
r.logger.Info(fmt.Sprintf("scheduler final %s", err))
case err := <-r.processor.final():
r.logger.Info(fmt.Sprintf("processor final %s", err))
// XXX: switch to consensus
}
}
}
func (r *Reactor) Stop() {
r.logger.Info("reactor stopping")
r.ticker.Stop()
r.scheduler.stop()
r.processor.stop()
close(r.stopDemux)
close(r.events)
r.logger.Info("reactor stopped")
}
func (r *Reactor) Receive(event Event) {
// XXX: decode and serialize write events
// TODO: backpressure
r.events <- event
}
func (r *Reactor) AddPeer() {
// TODO: add peer event and send to demuxer
}

View File

@@ -0,0 +1,22 @@
package v2
import (
"testing"
)
func TestReactor(t *testing.T) {
var (
bufferSize = 10
reactor = NewReactor(bufferSize)
)
reactor.Start()
script := []Event{
// TODO
}
for _, event := range script {
reactor.Receive(event)
}
reactor.Stop()
}

134
blockchain/v2/routine.go Normal file
View File

@@ -0,0 +1,134 @@
package v2
import (
"fmt"
"sync/atomic"
"github.com/Workiva/go-datastructures/queue"
"github.com/tendermint/tendermint/libs/log"
)
type handleFunc = func(event Event) (Event, error)
// Routines are a structure which model a finite state machine as serialized
// stream of events processed by a handle function. This Routine structure
// handles the concurrency and messaging guarantees. Events are sent via
// `send` are handled by the `handle` function to produce an iterator
// `next()`. Calling `close()` on a routine will conclude processing of all
// sent events and produce `final()` event representing the terminal state.
type Routine struct {
name string
handle handleFunc
queue *queue.PriorityQueue
out chan Event
fin chan error
rdy chan struct{}
running *uint32
logger log.Logger
metrics *Metrics
}
func newRoutine(name string, handleFunc handleFunc, bufferSize int) *Routine {
return &Routine{
name: name,
handle: handleFunc,
queue: queue.NewPriorityQueue(bufferSize, true),
out: make(chan Event, bufferSize),
rdy: make(chan struct{}, 1),
fin: make(chan error, 1),
running: new(uint32),
logger: log.NewNopLogger(),
metrics: NopMetrics(),
}
}
// nolint: unused
func (rt *Routine) setLogger(logger log.Logger) {
rt.logger = logger
}
// nolint:unused
func (rt *Routine) setMetrics(metrics *Metrics) {
rt.metrics = metrics
}
func (rt *Routine) start() {
rt.logger.Info(fmt.Sprintf("%s: run\n", rt.name))
running := atomic.CompareAndSwapUint32(rt.running, uint32(0), uint32(1))
if !running {
panic(fmt.Sprintf("%s is already running", rt.name))
}
close(rt.rdy)
defer func() {
stopped := atomic.CompareAndSwapUint32(rt.running, uint32(1), uint32(0))
if !stopped {
panic(fmt.Sprintf("%s is failed to stop", rt.name))
}
}()
for {
events, err := rt.queue.Get(1)
if err != nil {
rt.logger.Info(fmt.Sprintf("%s: stopping\n", rt.name))
rt.terminate(fmt.Errorf("stopped"))
return
}
oEvent, err := rt.handle(events[0].(Event))
rt.metrics.EventsHandled.With("routine", rt.name).Add(1)
if err != nil {
rt.terminate(err)
return
}
rt.metrics.EventsOut.With("routine", rt.name).Add(1)
rt.logger.Debug(fmt.Sprintf("%s produced %T %+v\n", rt.name, oEvent, oEvent))
rt.out <- oEvent
}
}
// XXX: look into returning OpError in the net package
func (rt *Routine) send(event Event) bool {
rt.logger.Debug(fmt.Sprintf("%s: received %T %+v", rt.name, event, event))
if !rt.isRunning() {
return false
}
err := rt.queue.Put(event)
if err != nil {
rt.metrics.EventsShed.With("routine", rt.name).Add(1)
rt.logger.Info(fmt.Sprintf("%s: send failed, queue was full/stopped \n", rt.name))
return false
}
rt.metrics.EventsSent.With("routine", rt.name).Add(1)
return true
}
func (rt *Routine) isRunning() bool {
return atomic.LoadUint32(rt.running) == 1
}
func (rt *Routine) next() chan Event {
return rt.out
}
func (rt *Routine) ready() chan struct{} {
return rt.rdy
}
func (rt *Routine) stop() {
if !rt.isRunning() {
return
}
rt.logger.Info(fmt.Sprintf("%s: stop\n", rt.name))
rt.queue.Dispose() // this should block until all queue items are free?
}
func (rt *Routine) final() chan error {
return rt.fin
}
// XXX: Maybe get rid of this
func (rt *Routine) terminate(reason error) {
close(rt.out)
rt.fin <- reason
}

View File

@@ -0,0 +1,163 @@
package v2
import (
"fmt"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
type eventA struct {
priorityNormal
}
var done = fmt.Errorf("done")
func simpleHandler(event Event) (Event, error) {
if _, ok := event.(eventA); ok {
return noOp, done
}
return noOp, nil
}
func TestRoutineFinal(t *testing.T) {
var (
bufferSize = 10
routine = newRoutine("simpleRoutine", simpleHandler, bufferSize)
)
assert.False(t, routine.isRunning(),
"expected an initialized routine to not be running")
go routine.start()
<-routine.ready()
assert.True(t, routine.isRunning(),
"expected an started routine")
assert.True(t, routine.send(eventA{}),
"expected sending to a ready routine to succeed")
assert.Equal(t, done, <-routine.final(),
"expected the final event to be done")
assert.False(t, routine.isRunning(),
"expected an completed routine to no longer be running")
}
func TestRoutineStop(t *testing.T) {
var (
bufferSize = 10
routine = newRoutine("simpleRoutine", simpleHandler, bufferSize)
)
assert.False(t, routine.send(eventA{}),
"expected sending to an unstarted routine to fail")
go routine.start()
<-routine.ready()
assert.True(t, routine.send(eventA{}),
"expected sending to a running routine to succeed")
routine.stop()
assert.False(t, routine.send(eventA{}),
"expected sending to a stopped routine to fail")
}
type finalCount struct {
count int
}
func (f finalCount) Error() string {
return "end"
}
func genStatefulHandler(maxCount int) handleFunc {
counter := 0
return func(event Event) (Event, error) {
if _, ok := event.(eventA); ok {
counter += 1
if counter >= maxCount {
return noOp, finalCount{counter}
}
return eventA{}, nil
}
return noOp, nil
}
}
func feedback(r *Routine) {
for event := range r.next() {
r.send(event)
}
}
func TestStatefulRoutine(t *testing.T) {
var (
count = 10
handler = genStatefulHandler(count)
bufferSize = 20
routine = newRoutine("statefulRoutine", handler, bufferSize)
)
go routine.start()
go feedback(routine)
<-routine.ready()
assert.True(t, routine.send(eventA{}),
"expected sending to a started routine to succeed")
final := <-routine.final()
if fnl, ok := final.(finalCount); ok {
assert.Equal(t, count, fnl.count,
"expected the routine to count to 10")
} else {
t.Fail()
}
}
type lowPriorityEvent struct {
priorityLow
}
type highPriorityEvent struct {
priorityHigh
}
func handleWithPriority(event Event) (Event, error) {
switch event.(type) {
case lowPriorityEvent:
return noOp, nil
case highPriorityEvent:
return noOp, done
}
return noOp, nil
}
func TestPriority(t *testing.T) {
var (
bufferSize = 20
routine = newRoutine("priorityRoutine", handleWithPriority, bufferSize)
)
go routine.start()
<-routine.ready()
go func() {
for {
routine.send(lowPriorityEvent{})
time.Sleep(1 * time.Millisecond)
}
}()
time.Sleep(10 * time.Millisecond)
assert.True(t, routine.isRunning(),
"expected an started routine")
assert.True(t, routine.send(highPriorityEvent{}),
"expected send to succeed even when saturated")
assert.Equal(t, done, <-routine.final())
assert.False(t, routine.isRunning(),
"expected an started routine")
}

385
blockchain/v2/schedule.go Normal file
View File

@@ -0,0 +1,385 @@
// nolint:unused
package v2
import (
"fmt"
"math"
"math/rand"
"time"
"github.com/tendermint/tendermint/p2p"
)
type blockState int
const (
blockStateUnknown blockState = iota
blockStateNew
blockStatePending
blockStateReceived
blockStateProcessed
)
func (e blockState) String() string {
switch e {
case blockStateUnknown:
return "Unknown"
case blockStateNew:
return "New"
case blockStatePending:
return "Pending"
case blockStateReceived:
return "Received"
case blockStateProcessed:
return "Processed"
default:
return fmt.Sprintf("unknown blockState: %d", e)
}
}
type peerState int
const (
peerStateNew = iota
peerStateReady
peerStateRemoved
)
func (e peerState) String() string {
switch e {
case peerStateNew:
return "New"
case peerStateReady:
return "Ready"
case peerStateRemoved:
return "Removed"
default:
return fmt.Sprintf("unknown peerState: %d", e)
}
}
type scPeer struct {
peerID p2p.ID
state peerState
height int64
lastTouched time.Time
lastRate int64
}
func newScPeer(peerID p2p.ID) *scPeer {
return &scPeer{
peerID: peerID,
state: peerStateNew,
height: -1,
lastTouched: time.Time{},
}
}
// The schedule is a composite data structure which allows a scheduler to keep
// track of which blocks have been scheduled into which state.
type schedule struct {
initHeight int64
// a list of blocks in which blockState
blockStates map[int64]blockState
// a map of peerID to schedule specific peer struct `scPeer` used to keep
// track of peer specific state
peers map[p2p.ID]*scPeer
// a map of heights to the peer we are waiting for a response from
pendingBlocks map[int64]p2p.ID
// the time at which a block was put in blockStatePending
pendingTime map[int64]time.Time
// the peerID of the peer which put the block in blockStateReceived
receivedBlocks map[int64]p2p.ID
}
func newSchedule(initHeight int64) *schedule {
sc := schedule{
initHeight: initHeight,
blockStates: make(map[int64]blockState),
peers: make(map[p2p.ID]*scPeer),
pendingBlocks: make(map[int64]p2p.ID),
pendingTime: make(map[int64]time.Time),
receivedBlocks: make(map[int64]p2p.ID),
}
sc.setStateAtHeight(initHeight, blockStateNew)
return &sc
}
func (sc *schedule) addPeer(peerID p2p.ID) error {
if _, ok := sc.peers[peerID]; ok {
return fmt.Errorf("Cannot add duplicate peer %s", peerID)
}
sc.peers[peerID] = newScPeer(peerID)
return nil
}
func (sc *schedule) touchPeer(peerID p2p.ID, time time.Time) error {
peer, ok := sc.peers[peerID]
if !ok {
return fmt.Errorf("Couldn't find peer %s", peerID)
}
if peer.state == peerStateRemoved {
return fmt.Errorf("Tried to touch peer in peerStateRemoved")
}
peer.lastTouched = time
return nil
}
func (sc *schedule) removePeer(peerID p2p.ID) error {
peer, ok := sc.peers[peerID]
if !ok {
return fmt.Errorf("Couldn't find peer %s", peerID)
}
if peer.state == peerStateRemoved {
return fmt.Errorf("Tried to remove peer %s in peerStateRemoved", peerID)
}
for height, pendingPeerID := range sc.pendingBlocks {
if pendingPeerID == peerID {
sc.setStateAtHeight(height, blockStateNew)
delete(sc.pendingTime, height)
delete(sc.pendingBlocks, height)
}
}
for height, rcvPeerID := range sc.receivedBlocks {
if rcvPeerID == peerID {
sc.setStateAtHeight(height, blockStateNew)
delete(sc.receivedBlocks, height)
}
}
peer.state = peerStateRemoved
return nil
}
func (sc *schedule) setPeerHeight(peerID p2p.ID, height int64) error {
peer, ok := sc.peers[peerID]
if !ok {
return fmt.Errorf("Can't find peer %s", peerID)
}
if peer.state == peerStateRemoved {
return fmt.Errorf("Cannot set peer height for a peer in peerStateRemoved")
}
if height < peer.height {
return fmt.Errorf("Cannot move peer height lower. from %d to %d", peer.height, height)
}
peer.height = height
peer.state = peerStateReady
for i := sc.minHeight(); i <= height; i++ {
if sc.getStateAtHeight(i) == blockStateUnknown {
sc.setStateAtHeight(i, blockStateNew)
}
}
return nil
}
func (sc *schedule) getStateAtHeight(height int64) blockState {
if height < sc.initHeight {
return blockStateProcessed
} else if state, ok := sc.blockStates[height]; ok {
return state
} else {
return blockStateUnknown
}
}
func (sc *schedule) getPeersAtHeight(height int64) []*scPeer {
peers := []*scPeer{}
for _, peer := range sc.peers {
if peer.height >= height {
peers = append(peers, peer)
}
}
return peers
}
func (sc *schedule) peersInactiveSince(duration time.Duration, now time.Time) []p2p.ID {
peers := []p2p.ID{}
for _, peer := range sc.peers {
if now.Sub(peer.lastTouched) > duration {
peers = append(peers, peer.peerID)
}
}
return peers
}
func (sc *schedule) peersSlowerThan(minSpeed int64) []p2p.ID {
peers := []p2p.ID{}
for _, peer := range sc.peers {
if peer.lastRate < minSpeed {
peers = append(peers, peer.peerID)
}
}
return peers
}
func (sc *schedule) setStateAtHeight(height int64, state blockState) {
sc.blockStates[height] = state
}
func (sc *schedule) markReceived(peerID p2p.ID, height int64, size int64, now time.Time) error {
peer, ok := sc.peers[peerID]
if !ok {
return fmt.Errorf("Can't find peer %s", peerID)
}
if peer.state == peerStateRemoved {
return fmt.Errorf("Cannot receive blocks from removed peer %s", peerID)
}
if state := sc.getStateAtHeight(height); state != blockStatePending || sc.pendingBlocks[height] != peerID {
return fmt.Errorf("Received block %d from peer %s without being requested", height, peerID)
}
pendingTime, ok := sc.pendingTime[height]
if !ok || now.Sub(pendingTime) <= 0 {
return fmt.Errorf("Clock error. Block %d received at %s but requested at %s",
height, pendingTime, now)
}
peer.lastRate = size / int64(now.Sub(pendingTime).Seconds())
sc.setStateAtHeight(height, blockStateReceived)
delete(sc.pendingBlocks, height)
delete(sc.pendingTime, height)
sc.receivedBlocks[height] = peerID
return nil
}
func (sc *schedule) markPending(peerID p2p.ID, height int64, time time.Time) error {
peer, ok := sc.peers[peerID]
if !ok {
return fmt.Errorf("Can't find peer %s", peerID)
}
state := sc.getStateAtHeight(height)
if state != blockStateNew {
return fmt.Errorf("Block %d should be in blockStateNew but was %s", height, state)
}
if peer.state != peerStateReady {
return fmt.Errorf("Cannot schedule %d from %s in %s", height, peerID, peer.state)
}
if height > peer.height {
return fmt.Errorf("Cannot request height %d from peer %s who is at height %d",
height, peerID, peer.height)
}
sc.setStateAtHeight(height, blockStatePending)
sc.pendingBlocks[height] = peerID
// XXX: to make this more accurate we can introduce a message from
// the IO routine which indicates the time the request was put on the wire
sc.pendingTime[height] = time
return nil
}
func (sc *schedule) markProcessed(height int64) error {
state := sc.getStateAtHeight(height)
if state != blockStateReceived {
return fmt.Errorf("Can't mark height %d received from block state %s", height, state)
}
delete(sc.receivedBlocks, height)
sc.setStateAtHeight(height, blockStateProcessed)
return nil
}
// allBlockProcessed returns true if all blocks are in blockStateProcessed and
// determines if the schedule has been completed
func (sc *schedule) allBlocksProcessed() bool {
for _, state := range sc.blockStates {
if state != blockStateProcessed {
return false
}
}
return true
}
// highest block | state == blockStateNew
func (sc *schedule) maxHeight() int64 {
var max int64 = 0
for height, state := range sc.blockStates {
if state == blockStateNew && height > max {
max = height
}
}
return max
}
// lowest block | state == blockStateNew
func (sc *schedule) minHeight() int64 {
var min int64 = math.MaxInt64
for height, state := range sc.blockStates {
if state == blockStateNew && height < min {
min = height
}
}
return min
}
func (sc *schedule) pendingFrom(peerID p2p.ID) []int64 {
heights := []int64{}
for height, pendingPeerID := range sc.pendingBlocks {
if pendingPeerID == peerID {
heights = append(heights, height)
}
}
return heights
}
func (sc *schedule) selectPeer(peers []*scPeer) *scPeer {
// FIXME: properPeerSelector
s := rand.NewSource(time.Now().Unix())
r := rand.New(s)
return peers[r.Intn(len(peers))]
}
// XXX: this duplicates the logic of peersInactiveSince and peersSlowerThan
func (sc *schedule) prunablePeers(peerTimout time.Duration, minRecvRate int64, now time.Time) []p2p.ID {
prunable := []p2p.ID{}
for peerID, peer := range sc.peers {
if now.Sub(peer.lastTouched) > peerTimout || peer.lastRate < minRecvRate {
prunable = append(prunable, peerID)
}
}
return prunable
}
func (sc *schedule) numBlockInState(targetState blockState) uint32 {
var num uint32 = 0
for _, state := range sc.blockStates {
if state == targetState {
num++
}
}
return num
}

View File

@@ -0,0 +1,272 @@
package v2
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/tendermint/tendermint/p2p"
)
func TestScheduleInit(t *testing.T) {
var (
initHeight int64 = 5
sc = newSchedule(initHeight)
)
assert.Equal(t, blockStateNew, sc.getStateAtHeight(initHeight))
assert.Equal(t, blockStateProcessed, sc.getStateAtHeight(initHeight-1))
assert.Equal(t, blockStateUnknown, sc.getStateAtHeight(initHeight+1))
}
func TestAddPeer(t *testing.T) {
var (
initHeight int64 = 5
peerID p2p.ID = "1"
peerIDTwo p2p.ID = "2"
sc = newSchedule(initHeight)
)
assert.Nil(t, sc.addPeer(peerID))
assert.Nil(t, sc.addPeer(peerIDTwo))
assert.Error(t, sc.addPeer(peerID))
}
func TestTouchPeer(t *testing.T) {
var (
initHeight int64 = 5
peerID p2p.ID = "1"
sc = newSchedule(initHeight)
now = time.Now()
)
assert.Error(t, sc.touchPeer(peerID, now),
"Touching an unknown peer should return errPeerNotFound")
assert.Nil(t, sc.addPeer(peerID),
"Adding a peer should return no error")
assert.Nil(t, sc.touchPeer(peerID, now),
"Touching a peer should return no error")
threshold := 10 * time.Second
assert.Empty(t, sc.peersInactiveSince(threshold, now.Add(9*time.Second)),
"Expected no peers to have been touched over 9 seconds")
assert.Containsf(t, sc.peersInactiveSince(threshold, now.Add(11*time.Second)), peerID,
"Expected one %s to have been touched over 10 seconds ago", peerID)
}
func TestPeerHeight(t *testing.T) {
var (
initHeight int64 = 5
peerID p2p.ID = "1"
peerHeight int64 = 20
sc = newSchedule(initHeight)
)
assert.NoError(t, sc.addPeer(peerID),
"Adding a peer should return no error")
assert.NoError(t, sc.setPeerHeight(peerID, peerHeight))
for i := initHeight; i <= peerHeight; i++ {
assert.Equal(t, sc.getStateAtHeight(i), blockStateNew,
"Expected all blocks to be in blockStateNew")
peerIDs := []p2p.ID{}
for _, peer := range sc.getPeersAtHeight(i) {
peerIDs = append(peerIDs, peer.peerID)
}
assert.Containsf(t, peerIDs, peerID,
"Expected %s to have block %d", peerID, i)
}
}
func TestTransitionPending(t *testing.T) {
var (
initHeight int64 = 5
peerID p2p.ID = "1"
peerIDTwo p2p.ID = "2"
peerHeight int64 = 20
sc = newSchedule(initHeight)
now = time.Now()
)
assert.NoError(t, sc.addPeer(peerID),
"Adding a peer should return no error")
assert.Nil(t, sc.addPeer(peerIDTwo),
"Adding a peer should return no error")
assert.Error(t, sc.markPending(peerID, peerHeight, now),
"Expected scheduling a block from a peer in peerStateNew to fail")
assert.NoError(t, sc.setPeerHeight(peerID, peerHeight),
"Expected setPeerHeight to return no error")
assert.NoError(t, sc.setPeerHeight(peerIDTwo, peerHeight),
"Expected setPeerHeight to return no error")
assert.NoError(t, sc.markPending(peerID, peerHeight, now),
"Expected markingPending new block to succeed")
assert.Error(t, sc.markPending(peerIDTwo, peerHeight, now),
"Expected markingPending by a second peer to fail")
assert.Equal(t, blockStatePending, sc.getStateAtHeight(peerHeight),
"Expected the block to to be in blockStatePending")
assert.NoError(t, sc.removePeer(peerID),
"Expected removePeer to return no error")
assert.Equal(t, blockStateNew, sc.getStateAtHeight(peerHeight),
"Expected the block to to be in blockStateNew")
assert.Error(t, sc.markPending(peerID, peerHeight, now),
"Expected markingPending removed peer to fail")
assert.NoError(t, sc.markPending(peerIDTwo, peerHeight, now),
"Expected markingPending on a ready peer to succeed")
assert.Equal(t, blockStatePending, sc.getStateAtHeight(peerHeight),
"Expected the block to to be in blockStatePending")
}
func TestTransitionReceived(t *testing.T) {
var (
initHeight int64 = 5
peerID p2p.ID = "1"
peerIDTwo p2p.ID = "2"
peerHeight int64 = 20
blockSize int64 = 1024
sc = newSchedule(initHeight)
now = time.Now()
receivedAt = now.Add(1 * time.Second)
)
assert.NoError(t, sc.addPeer(peerID),
"Expected adding peer %s to succeed", peerID)
assert.NoError(t, sc.addPeer(peerIDTwo),
"Expected adding peer %s to succeed", peerIDTwo)
assert.NoError(t, sc.setPeerHeight(peerID, peerHeight),
"Expected setPeerHeight to return no error")
assert.NoErrorf(t, sc.setPeerHeight(peerIDTwo, peerHeight),
"Expected setPeerHeight on %s to %d to succeed", peerIDTwo, peerHeight)
assert.NoError(t, sc.markPending(peerID, initHeight, now),
"Expected markingPending new block to succeed")
assert.Error(t, sc.markReceived(peerIDTwo, initHeight, blockSize, receivedAt),
"Expected marking markReceived from a non requesting peer to fail")
assert.NoError(t, sc.markReceived(peerID, initHeight, blockSize, receivedAt),
"Expected marking markReceived on a pending block to succeed")
assert.Error(t, sc.markReceived(peerID, initHeight, blockSize, receivedAt),
"Expected marking markReceived on received block to fail")
assert.Equalf(t, blockStateReceived, sc.getStateAtHeight(initHeight),
"Expected block %d to be blockHeightReceived", initHeight)
assert.NoErrorf(t, sc.removePeer(peerID),
"Expected removePeer removing %s to succeed", peerID)
assert.Equalf(t, blockStateNew, sc.getStateAtHeight(initHeight),
"Expected block %d to be blockStateNew", initHeight)
assert.NoErrorf(t, sc.markPending(peerIDTwo, initHeight, now),
"Expected markingPending %d from %s to succeed", initHeight, peerIDTwo)
assert.NoErrorf(t, sc.markReceived(peerIDTwo, initHeight, blockSize, receivedAt),
"Expected marking markReceived %d from %s to succeed", initHeight, peerIDTwo)
assert.Equalf(t, blockStateReceived, sc.getStateAtHeight(initHeight),
"Expected block %d to be blockStateReceived", initHeight)
}
func TestTransitionProcessed(t *testing.T) {
var (
initHeight int64 = 5
peerID p2p.ID = "1"
peerHeight int64 = 20
blockSize int64 = 1024
sc = newSchedule(initHeight)
now = time.Now()
receivedAt = now.Add(1 * time.Second)
)
assert.NoError(t, sc.addPeer(peerID),
"Expected adding peer %s to succeed", peerID)
assert.NoErrorf(t, sc.setPeerHeight(peerID, peerHeight),
"Expected setPeerHeight on %s to %d to succeed", peerID, peerHeight)
assert.NoError(t, sc.markPending(peerID, initHeight, now),
"Expected markingPending new block to succeed")
assert.NoError(t, sc.markReceived(peerID, initHeight, blockSize, receivedAt),
"Expected marking markReceived on a pending block to succeed")
assert.Error(t, sc.markProcessed(initHeight+1),
"Expected marking %d as processed to fail", initHeight+1)
assert.NoError(t, sc.markProcessed(initHeight),
"Expected marking %d as processed to succeed", initHeight)
assert.Equalf(t, blockStateProcessed, sc.getStateAtHeight(initHeight),
"Expected block %d to be blockStateProcessed", initHeight)
assert.NoError(t, sc.removePeer(peerID),
"Expected removing peer %s to succeed", peerID)
assert.Equalf(t, blockStateProcessed, sc.getStateAtHeight(initHeight),
"Expected block %d to be blockStateProcessed", initHeight)
}
func TestMinMaxHeight(t *testing.T) {
var (
initHeight int64 = 5
peerID p2p.ID = "1"
peerHeight int64 = 20
sc = newSchedule(initHeight)
now = time.Now()
)
assert.Equal(t, initHeight, sc.minHeight(),
"Expected min height to be the initialized height")
assert.Equal(t, initHeight, sc.maxHeight(),
"Expected max height to be the initialized height")
assert.NoError(t, sc.addPeer(peerID),
"Adding a peer should return no error")
assert.NoError(t, sc.setPeerHeight(peerID, peerHeight),
"Expected setPeerHeight to return no error")
assert.Equal(t, peerHeight, sc.maxHeight(),
"Expected max height to increase to peerHeight")
assert.Nil(t, sc.markPending(peerID, initHeight, now.Add(1*time.Second)),
"Expected marking initHeight as pending to return no error")
assert.Equal(t, initHeight+1, sc.minHeight(),
"Expected marking initHeight as pending to move minHeight forward")
}
func TestPeersSlowerThan(t *testing.T) {
var (
initHeight int64 = 5
peerID p2p.ID = "1"
peerHeight int64 = 20
blockSize int64 = 1024
sc = newSchedule(initHeight)
now = time.Now()
receivedAt = now.Add(1 * time.Second)
)
assert.NoError(t, sc.addPeer(peerID),
"Adding a peer should return no error")
assert.NoError(t, sc.setPeerHeight(peerID, peerHeight),
"Expected setPeerHeight to return no error")
assert.NoError(t, sc.markPending(peerID, peerHeight, now),
"Expected markingPending on to return no error")
assert.NoError(t, sc.markReceived(peerID, peerHeight, blockSize, receivedAt),
"Expected markingPending on to return no error")
assert.Empty(t, sc.peersSlowerThan(blockSize-1),
"expected no peers to be slower than blockSize-1 bytes/sec")
assert.Containsf(t, sc.peersSlowerThan(blockSize+1), peerID,
"expected %s to be slower than blockSize+1 bytes/sec", peerID)
}

64
blockchain/v2/types.go Normal file
View File

@@ -0,0 +1,64 @@
package v2
import (
"github.com/Workiva/go-datastructures/queue"
)
type Event queue.Item
type priority interface {
Compare(other queue.Item) int
Priority() int
}
type priorityLow struct{}
type priorityNormal struct{}
type priorityHigh struct{}
func (p priorityLow) Priority() int {
return 1
}
func (p priorityNormal) Priority() int {
return 2
}
func (p priorityHigh) Priority() int {
return 3
}
func (p priorityLow) Compare(other queue.Item) int {
op := other.(priority)
if p.Priority() > op.Priority() {
return 1
} else if p.Priority() == op.Priority() {
return 0
}
return -1
}
func (p priorityNormal) Compare(other queue.Item) int {
op := other.(priority)
if p.Priority() > op.Priority() {
return 1
} else if p.Priority() == op.Priority() {
return 0
}
return -1
}
func (p priorityHigh) Compare(other queue.Item) int {
op := other.(priority)
if p.Priority() > op.Priority() {
return 1
} else if p.Priority() == op.Priority() {
return 0
}
return -1
}
type noOpEvent struct {
priorityLow
}
var noOp = noOpEvent{}

View File

@@ -0,0 +1,34 @@
package main
import (
"fmt"
"strings"
"github.com/snikch/goodman/hooks"
"github.com/snikch/goodman/transaction"
)
func main() {
// This must be compiled beforehand and given to dredd as parameter, in the meantime the server should be running
h := hooks.NewHooks()
server := hooks.NewServer(hooks.NewHooksRunner(h))
h.BeforeAll(func(t []*transaction.Transaction) {
fmt.Println(t[0].Name)
})
h.BeforeEach(func(t *transaction.Transaction) {
if strings.HasPrefix(t.Name, "Tx") ||
// We need a proper example of evidence to broadcast
strings.HasPrefix(t.Name, "Info > /broadcast_evidence") ||
// We need a proper example of path and data
strings.HasPrefix(t.Name, "ABCI > /abci_query") ||
// We need to find a way to make a transaction before starting the tests,
// that hash should replace the dummy one in hte swagger file
strings.HasPrefix(t.Name, "Info > /tx") {
t.Skip = true
fmt.Printf("%s Has been skipped\n", t.Name)
}
})
server.Serve()
defer server.Listener.Close()
fmt.Print("FINE")
}

View File

@@ -48,15 +48,17 @@ func main() {
os.Exit(1)
}
rs := privval.NewSignerServiceEndpoint(logger, *chainID, pv, dialer)
err := rs.Start()
sd := privval.NewSignerDialerEndpoint(logger, dialer)
ss := privval.NewSignerServer(sd, *chainID, pv)
err := ss.Start()
if err != nil {
panic(err)
}
// Stop upon receiving SIGTERM or CTRL-C.
cmn.TrapSignal(logger, func() {
err := rs.Stop()
err := ss.Stop()
if err != nil {
panic(err)
}

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"net/url"
"github.com/pkg/errors"
"github.com/spf13/cobra"
cmn "github.com/tendermint/tendermint/libs/common"
@@ -80,7 +81,7 @@ func runProxy(cmd *cobra.Command, args []string) error {
logger.Info("Constructing Verifier...")
cert, err := proxy.NewVerifier(chainID, home, node, logger, cacheSize)
if err != nil {
return cmn.ErrorWrap(err, "constructing Verifier")
return errors.Wrap(err, "constructing Verifier")
}
cert.SetLogger(logger)
sc := proxy.SecureClient(node, cert)
@@ -88,7 +89,7 @@ func runProxy(cmd *cobra.Command, args []string) error {
logger.Info("Starting proxy...")
err = proxy.StartProxy(sc, listenAddr, logger, maxOpenConnections)
if err != nil {
return cmn.ErrorWrap(err, "starting proxy")
return errors.Wrap(err, "starting proxy")
}
// Run forever

View File

@@ -165,7 +165,7 @@ func TestRootConfig(t *testing.T) {
func WriteConfigVals(dir string, vals map[string]string) error {
data := ""
for k, v := range vals {
data = data + fmt.Sprintf("%s = \"%s\"\n", k, v)
data += fmt.Sprintf("%s = \"%s\"\n", k, v)
}
cfile := filepath.Join(dir, "config.toml")
return ioutil.WriteFile(cfile, []byte(data), 0666)

View File

@@ -19,7 +19,7 @@ func AddNodeFlags(cmd *cobra.Command) {
cmd.Flags().String("priv_validator_laddr", config.PrivValidatorListenAddr, "Socket address to listen on for connections from external priv_validator process")
// node flags
cmd.Flags().Bool("fast_sync", config.FastSync, "Fast blockchain syncing")
cmd.Flags().Bool("fast_sync", config.FastSyncMode, "Fast blockchain syncing")
// abci flags
cmd.Flags().String("proxy_app", config.ProxyApp, "Proxy app address, or one of: 'kvstore', 'persistent_kvstore', 'counter', 'counter_serial' or 'noop' for local testing.")

View File

@@ -161,9 +161,10 @@ func testnetFiles(cmd *cobra.Command, args []string) error {
// Generate genesis doc from generated validators
genDoc := &types.GenesisDoc{
GenesisTime: tmtime.Now(),
ChainID: "chain-" + cmn.RandStr(6),
Validators: genVals,
ChainID: "chain-" + cmn.RandStr(6),
ConsensusParams: types.DefaultConsensusParams(),
GenesisTime: tmtime.Now(),
Validators: genVals,
}
// Write genesis file.

View File

@@ -2,6 +2,7 @@ package config
import (
"fmt"
"net/http"
"os"
"path/filepath"
"time"
@@ -64,6 +65,7 @@ type Config struct {
RPC *RPCConfig `mapstructure:"rpc"`
P2P *P2PConfig `mapstructure:"p2p"`
Mempool *MempoolConfig `mapstructure:"mempool"`
FastSync *FastSyncConfig `mapstructure:"fastsync"`
Consensus *ConsensusConfig `mapstructure:"consensus"`
TxIndex *TxIndexConfig `mapstructure:"tx_index"`
Instrumentation *InstrumentationConfig `mapstructure:"instrumentation"`
@@ -76,6 +78,7 @@ func DefaultConfig() *Config {
RPC: DefaultRPCConfig(),
P2P: DefaultP2PConfig(),
Mempool: DefaultMempoolConfig(),
FastSync: DefaultFastSyncConfig(),
Consensus: DefaultConsensusConfig(),
TxIndex: DefaultTxIndexConfig(),
Instrumentation: DefaultInstrumentationConfig(),
@@ -89,6 +92,7 @@ func TestConfig() *Config {
RPC: TestRPCConfig(),
P2P: TestP2PConfig(),
Mempool: TestMempoolConfig(),
FastSync: TestFastSyncConfig(),
Consensus: TestConsensusConfig(),
TxIndex: TestTxIndexConfig(),
Instrumentation: TestInstrumentationConfig(),
@@ -120,6 +124,9 @@ func (cfg *Config) ValidateBasic() error {
if err := cfg.Mempool.ValidateBasic(); err != nil {
return errors.Wrap(err, "Error in [mempool] section")
}
if err := cfg.FastSync.ValidateBasic(); err != nil {
return errors.Wrap(err, "Error in [fastsync] section")
}
if err := cfg.Consensus.ValidateBasic(); err != nil {
return errors.Wrap(err, "Error in [consensus] section")
}
@@ -151,7 +158,7 @@ type BaseConfig struct {
// 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"`
FastSyncMode bool `mapstructure:"fast_sync"`
// Database backend: goleveldb | cleveldb | boltdb
// * goleveldb (github.com/syndtr/goleveldb - most popular implementation)
@@ -216,7 +223,7 @@ func DefaultBaseConfig() BaseConfig {
LogLevel: DefaultPackageLogLevels(),
LogFormat: LogFormatPlain,
ProfListenAddress: "",
FastSync: true,
FastSyncMode: true,
FilterPeers: false,
DBBackend: "goleveldb",
DBPath: "data",
@@ -228,7 +235,7 @@ func TestBaseConfig() BaseConfig {
cfg := DefaultBaseConfig()
cfg.chainID = "tendermint_test"
cfg.ProxyApp = "kvstore"
cfg.FastSync = false
cfg.FastSyncMode = false
cfg.DBBackend = "memdb"
return cfg
}
@@ -351,7 +358,14 @@ type RPCConfig struct {
// See https://github.com/tendermint/tendermint/issues/3435
TimeoutBroadcastTxCommit time.Duration `mapstructure:"timeout_broadcast_tx_commit"`
// The name of a file containing certificate that is used to create the HTTPS server.
// Maximum size of request body, in bytes
MaxBodyBytes int64 `mapstructure:"max_body_bytes"`
// Maximum size of request header, in bytes
MaxHeaderBytes int `mapstructure:"max_header_bytes"`
// The path to a file containing certificate that is used to create the HTTPS server.
// Migth be either absolute path or path related to tendermint's config directory.
//
// If the certificate is signed by a certificate authority,
// the certFile should be the concatenation of the server's certificate, any intermediates,
@@ -360,7 +374,8 @@ type RPCConfig struct {
// NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server. Otherwise, HTTP server is run.
TLSCertFile string `mapstructure:"tls_cert_file"`
// The name of a file containing matching private key that is used to create the HTTPS server.
// The path to a file containing matching private key that is used to create the HTTPS server.
// Migth be either absolute path or path related to tendermint's config directory.
//
// NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server. Otherwise, HTTP server is run.
TLSKeyFile string `mapstructure:"tls_key_file"`
@@ -369,9 +384,9 @@ type RPCConfig struct {
// DefaultRPCConfig returns a default configuration for the RPC server
func DefaultRPCConfig() *RPCConfig {
return &RPCConfig{
ListenAddress: "tcp://0.0.0.0:26657",
ListenAddress: "tcp://127.0.0.1:26657",
CORSAllowedOrigins: []string{},
CORSAllowedMethods: []string{"HEAD", "GET", "POST"},
CORSAllowedMethods: []string{http.MethodHead, http.MethodGet, http.MethodPost},
CORSAllowedHeaders: []string{"Origin", "Accept", "Content-Type", "X-Requested-With", "X-Server-Time"},
GRPCListenAddress: "",
GRPCMaxOpenConnections: 900,
@@ -383,6 +398,9 @@ func DefaultRPCConfig() *RPCConfig {
MaxSubscriptionsPerClient: 5,
TimeoutBroadcastTxCommit: 10 * time.Second,
MaxBodyBytes: int64(1000000), // 1MB
MaxHeaderBytes: 1 << 20, // same as the net/http default
TLSCertFile: "",
TLSKeyFile: "",
}
@@ -415,6 +433,12 @@ func (cfg *RPCConfig) ValidateBasic() error {
if cfg.TimeoutBroadcastTxCommit < 0 {
return errors.New("timeout_broadcast_tx_commit can't be negative")
}
if cfg.MaxBodyBytes < 0 {
return errors.New("max_body_bytes can't be negative")
}
if cfg.MaxHeaderBytes < 0 {
return errors.New("max_header_bytes can't be negative")
}
return nil
}
@@ -424,11 +448,19 @@ func (cfg *RPCConfig) IsCorsEnabled() bool {
}
func (cfg RPCConfig) KeyFile() string {
return rootify(filepath.Join(defaultConfigDir, cfg.TLSKeyFile), cfg.RootDir)
path := cfg.TLSKeyFile
if filepath.IsAbs(path) {
return path
}
return rootify(filepath.Join(defaultConfigDir, path), cfg.RootDir)
}
func (cfg RPCConfig) CertFile() string {
return rootify(filepath.Join(defaultConfigDir, cfg.TLSCertFile), cfg.RootDir)
path := cfg.TLSCertFile
if filepath.IsAbs(path) {
return path
}
return rootify(filepath.Join(defaultConfigDir, path), cfg.RootDir)
}
func (cfg RPCConfig) IsTLSEnabled() bool {
@@ -606,6 +638,7 @@ type MempoolConfig struct {
Size int `mapstructure:"size"`
MaxTxsBytes int64 `mapstructure:"max_txs_bytes"`
CacheSize int `mapstructure:"cache_size"`
MaxTxBytes int `mapstructure:"max_tx_bytes"`
}
// DefaultMempoolConfig returns a default configuration for the Tendermint mempool
@@ -619,6 +652,7 @@ func DefaultMempoolConfig() *MempoolConfig {
Size: 5000,
MaxTxsBytes: 1024 * 1024 * 1024, // 1GB
CacheSize: 10000,
MaxTxBytes: 1024 * 1024, // 1MB
}
}
@@ -651,9 +685,44 @@ func (cfg *MempoolConfig) ValidateBasic() error {
if cfg.CacheSize < 0 {
return errors.New("cache_size can't be negative")
}
if cfg.MaxTxBytes < 0 {
return errors.New("max_tx_bytes can't be negative")
}
return nil
}
//-----------------------------------------------------------------------------
// FastSyncConfig
// FastSyncConfig defines the configuration for the Tendermint fast sync service
type FastSyncConfig struct {
Version string `mapstructure:"version"`
}
// DefaultFastSyncConfig returns a default configuration for the fast sync service
func DefaultFastSyncConfig() *FastSyncConfig {
return &FastSyncConfig{
Version: "v0",
}
}
// TestFastSyncConfig returns a default configuration for the fast sync.
func TestFastSyncConfig() *FastSyncConfig {
return DefaultFastSyncConfig()
}
// ValidateBasic performs basic validation.
func (cfg *FastSyncConfig) ValidateBasic() error {
switch cfg.Version {
case "v0":
return nil
case "v1":
return nil
default:
return fmt.Errorf("unknown fastsync version %s", cfg.Version)
}
}
//-----------------------------------------------------------------------------
// ConsensusConfig

View File

@@ -1,6 +1,7 @@
package config
import (
"reflect"
"testing"
"time"
@@ -36,3 +37,132 @@ func TestConfigValidateBasic(t *testing.T) {
cfg.Consensus.TimeoutPropose = -10 * time.Second
assert.Error(t, cfg.ValidateBasic())
}
func TestTLSConfiguration(t *testing.T) {
assert := assert.New(t)
cfg := DefaultConfig()
cfg.SetRoot("/home/user")
cfg.RPC.TLSCertFile = "file.crt"
assert.Equal("/home/user/config/file.crt", cfg.RPC.CertFile())
cfg.RPC.TLSKeyFile = "file.key"
assert.Equal("/home/user/config/file.key", cfg.RPC.KeyFile())
cfg.RPC.TLSCertFile = "/abs/path/to/file.crt"
assert.Equal("/abs/path/to/file.crt", cfg.RPC.CertFile())
cfg.RPC.TLSKeyFile = "/abs/path/to/file.key"
assert.Equal("/abs/path/to/file.key", cfg.RPC.KeyFile())
}
func TestBaseConfigValidateBasic(t *testing.T) {
cfg := TestBaseConfig()
assert.NoError(t, cfg.ValidateBasic())
// tamper with log format
cfg.LogFormat = "invalid"
assert.Error(t, cfg.ValidateBasic())
}
func TestRPCConfigValidateBasic(t *testing.T) {
cfg := TestRPCConfig()
assert.NoError(t, cfg.ValidateBasic())
fieldsToTest := []string{
"GRPCMaxOpenConnections",
"MaxOpenConnections",
"MaxSubscriptionClients",
"MaxSubscriptionsPerClient",
"TimeoutBroadcastTxCommit",
"MaxBodyBytes",
"MaxHeaderBytes",
}
for _, fieldName := range fieldsToTest {
reflect.ValueOf(cfg).Elem().FieldByName(fieldName).SetInt(-1)
assert.Error(t, cfg.ValidateBasic())
reflect.ValueOf(cfg).Elem().FieldByName(fieldName).SetInt(0)
}
}
func TestP2PConfigValidateBasic(t *testing.T) {
cfg := TestP2PConfig()
assert.NoError(t, cfg.ValidateBasic())
fieldsToTest := []string{
"MaxNumInboundPeers",
"MaxNumOutboundPeers",
"FlushThrottleTimeout",
"MaxPacketMsgPayloadSize",
"SendRate",
"RecvRate",
}
for _, fieldName := range fieldsToTest {
reflect.ValueOf(cfg).Elem().FieldByName(fieldName).SetInt(-1)
assert.Error(t, cfg.ValidateBasic())
reflect.ValueOf(cfg).Elem().FieldByName(fieldName).SetInt(0)
}
}
func TestMempoolConfigValidateBasic(t *testing.T) {
cfg := TestMempoolConfig()
assert.NoError(t, cfg.ValidateBasic())
fieldsToTest := []string{
"Size",
"MaxTxsBytes",
"CacheSize",
"MaxTxBytes",
}
for _, fieldName := range fieldsToTest {
reflect.ValueOf(cfg).Elem().FieldByName(fieldName).SetInt(-1)
assert.Error(t, cfg.ValidateBasic())
reflect.ValueOf(cfg).Elem().FieldByName(fieldName).SetInt(0)
}
}
func TestFastSyncConfigValidateBasic(t *testing.T) {
cfg := TestFastSyncConfig()
assert.NoError(t, cfg.ValidateBasic())
// tamper with version
cfg.Version = "v1"
assert.NoError(t, cfg.ValidateBasic())
cfg.Version = "invalid"
assert.Error(t, cfg.ValidateBasic())
}
func TestConsensusConfigValidateBasic(t *testing.T) {
cfg := TestConsensusConfig()
assert.NoError(t, cfg.ValidateBasic())
fieldsToTest := []string{
"TimeoutPropose",
"TimeoutProposeDelta",
"TimeoutPrevote",
"TimeoutPrevoteDelta",
"TimeoutPrecommit",
"TimeoutPrecommitDelta",
"TimeoutCommit",
"CreateEmptyBlocksInterval",
"PeerGossipSleepDuration",
"PeerQueryMaj23SleepDuration",
}
for _, fieldName := range fieldsToTest {
reflect.ValueOf(cfg).Elem().FieldByName(fieldName).SetInt(-1)
assert.Error(t, cfg.ValidateBasic())
reflect.ValueOf(cfg).Elem().FieldByName(fieldName).SetInt(0)
}
}
func TestInstrumentationConfigValidateBasic(t *testing.T) {
cfg := TestInstrumentationConfig()
assert.NoError(t, cfg.ValidateBasic())
// tamper with maximum open connections
cfg.MaxOpenConnections = -1
assert.Error(t, cfg.ValidateBasic())
}

View File

@@ -67,6 +67,11 @@ func WriteConfigFile(configFilePath string, config *Config) {
const defaultConfigTemplate = `# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml
# NOTE: Any path below can be absolute (e.g. "/var/myawesomeapp/data") or
# relative to the home directory (e.g. "data"). The home directory is
# "$HOME/.tendermint" by default, but could be changed via $TMHOME env variable
# or --home cmd flag.
##### main base config options #####
# TCP or UNIX socket address of the ABCI application,
@@ -79,7 +84,7 @@ moniker = "{{ .BaseConfig.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
fast_sync = {{ .BaseConfig.FastSync }}
fast_sync = {{ .BaseConfig.FastSyncMode }}
# Database backend: goleveldb | cleveldb | boltdb
# * goleveldb (github.com/syndtr/goleveldb - most popular implementation)
@@ -192,14 +197,22 @@ max_subscriptions_per_client = {{ .RPC.MaxSubscriptionsPerClient }}
# See https://github.com/tendermint/tendermint/issues/3435
timeout_broadcast_tx_commit = "{{ .RPC.TimeoutBroadcastTxCommit }}"
# The name of a file containing certificate that is used to create the HTTPS server.
# Maximum size of request body, in bytes
max_body_bytes = {{ .RPC.MaxBodyBytes }}
# Maximum size of request header, in bytes
max_header_bytes = {{ .RPC.MaxHeaderBytes }}
# The path to a file containing certificate that is used to create the HTTPS server.
# Migth be either absolute path or path related to tendermint's config directory.
# If the certificate is signed by a certificate authority,
# the certFile should be the concatenation of the server's certificate, any intermediates,
# and the CA's certificate.
# NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server. Otherwise, HTTP server is run.
tls_cert_file = "{{ .RPC.TLSCertFile }}"
# The name of a file containing matching private key that is used to create the HTTPS server.
# The path to a file containing matching private key that is used to create the HTTPS server.
# Migth be either absolute path or path related to tendermint's config directory.
# NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server. Otherwise, HTTP server is run.
tls_key_file = "{{ .RPC.TLSKeyFile }}"
@@ -286,6 +299,18 @@ max_txs_bytes = {{ .Mempool.MaxTxsBytes }}
# Size of the cache (used to filter transactions we saw earlier) in transactions
cache_size = {{ .Mempool.CacheSize }}
# Maximum size of a single transaction.
# NOTE: the max size of a tx transmitted over the network is {max_tx_bytes} + {amino overhead}.
max_tx_bytes = {{ .Mempool.MaxTxBytes }}
##### fast sync configuration options #####
[fastsync]
# Fast Sync version to use:
# 1) "v0" (default) - the legacy fast sync implementation
# 2) "v1" - refactor of v0 version for better testability
version = "{{ .FastSync.Version }}"
##### consensus configuration options #####
[consensus]

View File

@@ -177,7 +177,7 @@ func byzantineDecideProposalFunc(t *testing.T, height int64, round int, cs *Cons
// Create a new proposal block from state/txs from the mempool.
block1, blockParts1 := cs.createProposalBlock()
polRound, propBlockID := cs.ValidRound, types.BlockID{block1.Hash(), blockParts1.Header()}
polRound, propBlockID := cs.ValidRound, types.BlockID{Hash: block1.Hash(), PartsHeader: blockParts1.Header()}
proposal1 := types.NewProposal(height, round, polRound, propBlockID)
if err := cs.privValidator.SignProposal(cs.state.ChainID, proposal1); err != nil {
t.Error(err)
@@ -185,7 +185,7 @@ func byzantineDecideProposalFunc(t *testing.T, height int64, round int, cs *Cons
// Create a new proposal block from state/txs from the mempool.
block2, blockParts2 := cs.createProposalBlock()
polRound, propBlockID = cs.ValidRound, types.BlockID{block2.Hash(), blockParts2.Header()}
polRound, propBlockID = cs.ValidRound, types.BlockID{Hash: block2.Hash(), PartsHeader: blockParts2.Header()}
proposal2 := types.NewProposal(height, round, polRound, propBlockID)
if err := cs.privValidator.SignProposal(cs.state.ChainID, proposal2); err != nil {
t.Error(err)

View File

@@ -20,19 +20,19 @@ import (
"github.com/tendermint/tendermint/abci/example/counter"
"github.com/tendermint/tendermint/abci/example/kvstore"
abci "github.com/tendermint/tendermint/abci/types"
bc "github.com/tendermint/tendermint/blockchain"
cfg "github.com/tendermint/tendermint/config"
cstypes "github.com/tendermint/tendermint/consensus/types"
cmn "github.com/tendermint/tendermint/libs/common"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
tmpubsub "github.com/tendermint/tendermint/libs/pubsub"
mempl "github.com/tendermint/tendermint/mempool"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/privval"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/store"
"github.com/tendermint/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time"
dbm "github.com/tendermint/tm-db"
)
const (
@@ -86,7 +86,7 @@ func (vs *validatorStub) signVote(voteType types.SignedMsgType, hash []byte, hea
Round: vs.Round,
Timestamp: tmtime.Now(),
Type: voteType,
BlockID: types.BlockID{hash, header},
BlockID: types.BlockID{Hash: hash, PartsHeader: header},
}
err := vs.PrivValidator.SignVote(config.ChainID(), vote)
return vote, err
@@ -159,7 +159,7 @@ func decideProposal(cs1 *ConsensusState, vs *validatorStub, height int64, round
}
// Make proposal
polRound, propBlockID := validRound, types.BlockID{block.Hash(), blockParts.Header()}
polRound, propBlockID := validRound, types.BlockID{Hash: block.Hash(), PartsHeader: blockParts.Header()}
proposal = types.NewProposal(height, round, polRound, propBlockID)
if err := vs.SignProposal(chainID, proposal); err != nil {
panic(err)
@@ -280,7 +280,7 @@ func newConsensusStateWithConfig(thisConfig *cfg.Config, state sm.State, pv type
func newConsensusStateWithConfigAndBlockStore(thisConfig *cfg.Config, state sm.State, pv types.PrivValidator, app abci.Application, blockDB dbm.DB) *ConsensusState {
// Get BlockStore
blockStore := bc.NewBlockStore(blockDB)
blockStore := store.NewBlockStore(blockDB)
// one for mempool, one for consensus
mtx := new(sync.Mutex)

View File

@@ -11,10 +11,10 @@ import (
"github.com/tendermint/tendermint/abci/example/code"
abci "github.com/tendermint/tendermint/abci/types"
dbm "github.com/tendermint/tendermint/libs/db"
mempl "github.com/tendermint/tendermint/mempool"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
dbm "github.com/tendermint/tm-db"
)
// for testing
@@ -82,14 +82,14 @@ func TestMempoolProgressInHigherRound(t *testing.T) {
ensureNewRound(newRoundCh, height, round) // first round at first height
ensureNewEventOnChannel(newBlockCh) // first block gets committed
height = height + 1 // moving to the next height
height++ // moving to the next height
round = 0
ensureNewRound(newRoundCh, height, round) // first round at next height
deliverTxsRange(cs, 0, 1) // we deliver txs, but dont set a proposal so we get the next round
ensureNewTimeout(timeoutCh, height, round, cs.config.TimeoutPropose.Nanoseconds())
round = round + 1 // moving to the next round
round++ // moving to the next round
ensureNewRound(newRoundCh, height, round) // wait for the next round
ensureNewEventOnChannel(newBlockCh) // now we can commit the block
}
@@ -141,7 +141,7 @@ func TestMempoolRmBadTx(t *testing.T) {
txBytes := make([]byte, 8)
binary.BigEndian.PutUint64(txBytes, uint64(0))
resDeliver := app.DeliverTx(txBytes)
resDeliver := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes})
assert.False(t, resDeliver.IsErr(), fmt.Sprintf("expected no error. got %v", resDeliver))
resCommit := app.Commit()
@@ -155,12 +155,14 @@ func TestMempoolRmBadTx(t *testing.T) {
// and the tx should get removed from the pool
err := assertMempool(cs.txNotifier).CheckTx(txBytes, func(r *abci.Response) {
if r.GetCheckTx().Code != code.CodeTypeBadNonce {
t.Fatalf("expected checktx to return bad nonce, got %v", r)
t.Errorf("expected checktx to return bad nonce, got %v", r)
return
}
checkTxRespCh <- struct{}{}
})
if err != nil {
t.Fatalf("Error after CheckTx: %v", err)
t.Errorf("Error after CheckTx: %v", err)
return
}
// check for the tx
@@ -180,7 +182,8 @@ func TestMempoolRmBadTx(t *testing.T) {
case <-checkTxRespCh:
// success
case <-ticker:
t.Fatalf("Timed out waiting for tx to return")
t.Errorf("Timed out waiting for tx to return")
return
}
// Wait until the tx is removed
@@ -189,7 +192,8 @@ func TestMempoolRmBadTx(t *testing.T) {
case <-emptyMempoolCh:
// success
case <-ticker:
t.Fatalf("Timed out waiting for tx to be removed")
t.Errorf("Timed out waiting for tx to be removed")
return
}
}
@@ -209,8 +213,8 @@ func (app *CounterApplication) Info(req abci.RequestInfo) abci.ResponseInfo {
return abci.ResponseInfo{Data: fmt.Sprintf("txs:%v", app.txCount)}
}
func (app *CounterApplication) DeliverTx(tx []byte) abci.ResponseDeliverTx {
txValue := txAsUint64(tx)
func (app *CounterApplication) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx {
txValue := txAsUint64(req.Tx)
if txValue != uint64(app.txCount) {
return abci.ResponseDeliverTx{
Code: code.CodeTypeBadNonce,
@@ -220,8 +224,8 @@ func (app *CounterApplication) DeliverTx(tx []byte) abci.ResponseDeliverTx {
return abci.ResponseDeliverTx{Code: code.CodeTypeOK}
}
func (app *CounterApplication) CheckTx(tx []byte) abci.ResponseCheckTx {
txValue := txAsUint64(tx)
func (app *CounterApplication) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx {
txValue := txAsUint64(req.Tx)
if txValue != uint64(app.mempoolTxCount) {
return abci.ResponseCheckTx{
Code: code.CodeTypeBadNonce,

View File

@@ -116,8 +116,13 @@ func (conR *ConsensusReactor) SwitchToConsensus(state sm.State, blocksSynced int
}
err := conR.conS.Start()
if err != nil {
conR.Logger.Error("Error starting conS", "err", err)
return
panic(fmt.Sprintf(`Failed to start consensus state: %v
conS:
%+v
conR:
%+v`, err, conR.conS, conR))
}
}
@@ -351,10 +356,6 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
default:
conR.Logger.Error(fmt.Sprintf("Unknown chId %X", chID))
}
if err != nil {
conR.Logger.Error("Error in Receive()", "err", err)
}
}
// SetEventBus sets event bus.

View File

@@ -17,29 +17,31 @@ import (
abcicli "github.com/tendermint/tendermint/abci/client"
"github.com/tendermint/tendermint/abci/example/kvstore"
abci "github.com/tendermint/tendermint/abci/types"
bc "github.com/tendermint/tendermint/blockchain"
cfg "github.com/tendermint/tendermint/config"
dbm "github.com/tendermint/tendermint/libs/db"
cstypes "github.com/tendermint/tendermint/consensus/types"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/libs/log"
mempl "github.com/tendermint/tendermint/mempool"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/p2p/mock"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/store"
"github.com/tendermint/tendermint/types"
dbm "github.com/tendermint/tm-db"
)
//----------------------------------------------
// in-process testnets
func startConsensusNet(t *testing.T, css []*ConsensusState, N int) (
func startConsensusNet(t *testing.T, css []*ConsensusState, n int) (
[]*ConsensusReactor,
[]types.Subscription,
[]*types.EventBus,
) {
reactors := make([]*ConsensusReactor, N)
reactors := make([]*ConsensusReactor, n)
blocksSubs := make([]types.Subscription, 0)
eventBuses := make([]*types.EventBus, N)
for i := 0; i < N; i++ {
eventBuses := make([]*types.EventBus, n)
for i := 0; i < n; i++ {
/*logger, err := tmflags.ParseLogLevel("consensus:info,*:error", logger, "info")
if err != nil { t.Fatal(err)}*/
reactors[i] = NewConsensusReactor(css[i], true) // so we dont start the consensus states
@@ -58,7 +60,7 @@ func startConsensusNet(t *testing.T, css []*ConsensusState, N int) (
}
}
// make connected switches and start all reactors
p2p.MakeConnectedSwitches(config.P2P, N, func(i int, s *p2p.Switch) *p2p.Switch {
p2p.MakeConnectedSwitches(config.P2P, n, func(i int, s *p2p.Switch) *p2p.Switch {
s.AddReactor("CONSENSUS", reactors[i])
s.SetLogger(reactors[i].conS.Logger.With("module", "p2p"))
return s
@@ -68,7 +70,7 @@ func startConsensusNet(t *testing.T, css []*ConsensusState, N int) (
// If we started the state machines before everyone was connected,
// we'd block when the cs fires NewBlockEvent and the peers are trying to start their reactors
// TODO: is this still true with new pubsub?
for i := 0; i < N; i++ {
for i := 0; i < n; i++ {
s := reactors[i].conS.GetState()
reactors[i].SwitchToConsensus(s, 0)
}
@@ -133,7 +135,7 @@ func TestReactorWithEvidence(t *testing.T) {
// css[i] = newConsensusStateWithConfig(thisConfig, state, privVals[i], app)
blockDB := dbm.NewMemDB()
blockStore := bc.NewBlockStore(blockDB)
blockStore := store.NewBlockStore(blockDB)
// one for mempool, one for consensus
mtx := new(sync.Mutex)
@@ -235,7 +237,7 @@ func TestReactorCreatesBlockWhenEmptyBlocksFalse(t *testing.T) {
// send a tx
if err := assertMempool(css[3].txNotifier).CheckTx([]byte{1, 2, 3}, nil); err != nil {
//t.Fatal(err)
t.Error(err)
}
// wait till everyone makes the first new block
@@ -632,3 +634,260 @@ func capture() {
count := runtime.Stack(trace, true)
fmt.Printf("Stack of %d bytes: %s\n", count, trace)
}
//-------------------------------------------------------------
// Ensure basic validation of structs is functioning
func TestNewRoundStepMessageValidateBasic(t *testing.T) {
testCases := []struct {
expectErr bool
messageRound int
messageLastCommitRound int
messageHeight int64
testName string
messageStep cstypes.RoundStepType
}{
{false, 0, 0, 0, "Valid Message", 0x01},
{true, -1, 0, 0, "Invalid Message", 0x01},
{true, 0, 0, -1, "Invalid Message", 0x01},
{true, 0, 0, 1, "Invalid Message", 0x00},
{true, 0, 0, 1, "Invalid Message", 0x00},
{true, 0, -2, 2, "Invalid Message", 0x01},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.testName, func(t *testing.T) {
message := NewRoundStepMessage{
Height: tc.messageHeight,
Round: tc.messageRound,
Step: tc.messageStep,
LastCommitRound: tc.messageLastCommitRound,
}
assert.Equal(t, tc.expectErr, message.ValidateBasic() != nil, "Validate Basic had an unexpected result")
})
}
}
func TestNewValidBlockMessageValidateBasic(t *testing.T) {
testBitArray := cmn.NewBitArray(1)
testCases := []struct {
testName string
messageHeight int64
messageRound int
messageBlockParts *cmn.BitArray
expectErr bool
}{
{"Valid Message", 0, 0, testBitArray, false},
{"Invalid Message", -1, 0, testBitArray, true},
{"Invalid Message", 0, -1, testBitArray, true},
{"Invalid Message", 0, 0, cmn.NewBitArray(0), true},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.testName, func(t *testing.T) {
message := NewValidBlockMessage{
Height: tc.messageHeight,
Round: tc.messageRound,
BlockParts: tc.messageBlockParts,
}
message.BlockPartsHeader.Total = 1
assert.Equal(t, tc.expectErr, message.ValidateBasic() != nil, "Validate Basic had an unexpected result")
})
}
}
func TestProposalPOLMessageValidateBasic(t *testing.T) {
testBitArray := cmn.NewBitArray(1)
testCases := []struct {
testName string
messageHeight int64
messageProposalPOLRound int
messageProposalPOL *cmn.BitArray
expectErr bool
}{
{"Valid Message", 0, 0, testBitArray, false},
{"Invalid Message", -1, 0, testBitArray, true},
{"Invalid Message", 0, -1, testBitArray, true},
{"Invalid Message", 0, 0, cmn.NewBitArray(0), true},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.testName, func(t *testing.T) {
message := ProposalPOLMessage{
Height: tc.messageHeight,
ProposalPOLRound: tc.messageProposalPOLRound,
ProposalPOL: tc.messageProposalPOL,
}
assert.Equal(t, tc.expectErr, message.ValidateBasic() != nil, "Validate Basic had an unexpected result")
})
}
}
func TestBlockPartMessageValidateBasic(t *testing.T) {
testPart := new(types.Part)
testCases := []struct {
testName string
messageHeight int64
messageRound int
messagePart *types.Part
expectErr bool
}{
{"Valid Message", 0, 0, testPart, false},
{"Invalid Message", -1, 0, testPart, true},
{"Invalid Message", 0, -1, testPart, true},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.testName, func(t *testing.T) {
message := BlockPartMessage{
Height: tc.messageHeight,
Round: tc.messageRound,
Part: tc.messagePart,
}
assert.Equal(t, tc.expectErr, message.ValidateBasic() != nil, "Validate Basic had an unexpected result")
})
}
message := BlockPartMessage{Height: 0, Round: 0, Part: new(types.Part)}
message.Part.Index = -1
assert.Equal(t, true, message.ValidateBasic() != nil, "Validate Basic had an unexpected result")
}
func TestHasVoteMessageValidateBasic(t *testing.T) {
const (
validSignedMsgType types.SignedMsgType = 0x01
invalidSignedMsgType types.SignedMsgType = 0x03
)
testCases := []struct {
expectErr bool
messageRound int
messageIndex int
messageHeight int64
testName string
messageType types.SignedMsgType
}{
{false, 0, 0, 0, "Valid Message", validSignedMsgType},
{true, -1, 0, 0, "Invalid Message", validSignedMsgType},
{true, 0, -1, 0, "Invalid Message", validSignedMsgType},
{true, 0, 0, 0, "Invalid Message", invalidSignedMsgType},
{true, 0, 0, -1, "Invalid Message", validSignedMsgType},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.testName, func(t *testing.T) {
message := HasVoteMessage{
Height: tc.messageHeight,
Round: tc.messageRound,
Type: tc.messageType,
Index: tc.messageIndex,
}
assert.Equal(t, tc.expectErr, message.ValidateBasic() != nil, "Validate Basic had an unexpected result")
})
}
}
func TestVoteSetMaj23MessageValidateBasic(t *testing.T) {
const (
validSignedMsgType types.SignedMsgType = 0x01
invalidSignedMsgType types.SignedMsgType = 0x03
)
validBlockID := types.BlockID{}
invalidBlockID := types.BlockID{
Hash: cmn.HexBytes{},
PartsHeader: types.PartSetHeader{
Total: -1,
Hash: cmn.HexBytes{},
},
}
testCases := []struct {
expectErr bool
messageRound int
messageHeight int64
testName string
messageType types.SignedMsgType
messageBlockID types.BlockID
}{
{false, 0, 0, "Valid Message", validSignedMsgType, validBlockID},
{true, -1, 0, "Invalid Message", validSignedMsgType, validBlockID},
{true, 0, -1, "Invalid Message", validSignedMsgType, validBlockID},
{true, 0, 0, "Invalid Message", invalidSignedMsgType, validBlockID},
{true, 0, 0, "Invalid Message", validSignedMsgType, invalidBlockID},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.testName, func(t *testing.T) {
message := VoteSetMaj23Message{
Height: tc.messageHeight,
Round: tc.messageRound,
Type: tc.messageType,
BlockID: tc.messageBlockID,
}
assert.Equal(t, tc.expectErr, message.ValidateBasic() != nil, "Validate Basic had an unexpected result")
})
}
}
func TestVoteSetBitsMessageValidateBasic(t *testing.T) {
const (
validSignedMsgType types.SignedMsgType = 0x01
invalidSignedMsgType types.SignedMsgType = 0x03
)
validBlockID := types.BlockID{}
invalidBlockID := types.BlockID{
Hash: cmn.HexBytes{},
PartsHeader: types.PartSetHeader{
Total: -1,
Hash: cmn.HexBytes{},
},
}
testBitArray := cmn.NewBitArray(1)
testCases := []struct {
expectErr bool
messageRound int
messageHeight int64
testName string
messageType types.SignedMsgType
messageBlockID types.BlockID
messageVotes *cmn.BitArray
}{
{false, 0, 0, "Valid Message", validSignedMsgType, validBlockID, testBitArray},
{true, -1, 0, "Invalid Message", validSignedMsgType, validBlockID, testBitArray},
{true, 0, -1, "Invalid Message", validSignedMsgType, validBlockID, testBitArray},
{true, 0, 0, "Invalid Message", invalidSignedMsgType, validBlockID, testBitArray},
{true, 0, 0, "Invalid Message", validSignedMsgType, invalidBlockID, testBitArray},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.testName, func(t *testing.T) {
message := VoteSetBitsMessage{
Height: tc.messageHeight,
Round: tc.messageRound,
Type: tc.messageType,
// Votes: tc.messageVotes,
BlockID: tc.messageBlockID,
}
assert.Equal(t, tc.expectErr, message.ValidateBasic() != nil, "Validate Basic had an unexpected result")
})
}
}

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