Compare commits

...

311 Commits

Author SHA1 Message Date
Zarko Milosevic
4accdb5f59 Example of client send task and monitor task 2018-04-19 12:34:42 +02:00
Anton Kaliaev
ece3f678da [docs/spec] update msg type and Tendermint behavior (#1468)
Refs #1422
2018-04-17 19:38:10 +02:00
Ethan Buchman
45a05b4726 Merge pull request #1461 from tendermint/update-changelog
update changelog
2018-04-17 09:56:53 +02:00
Anton Kaliaev
1706ce6f7f update changelog for 0.19.0 release 2018-04-13 10:50:34 +02:00
Jae Kwon
d0beaba7e8 Bump version to 0.19.0 2018-04-13 01:32:47 -07:00
Anton Kaliaev
c28784de5e Merge pull request #1453 from tendermint/fix-localnet
Fix permissions and folder structure for localnet
2018-04-12 17:50:08 +02:00
Anton Kaliaev
d06390638d [localnet] use routable IPs 2018-04-12 16:02:31 +02:00
Anton Kaliaev
3a0edc561d log error from AddrBook#AddAddress in DialPeersAsync
Refs #1434
2018-04-12 15:51:17 +02:00
Anton Kaliaev
f8ed578325 [localnet] execute cmd from root
not secure, but we don't care because it's local tooling
2018-04-12 15:51:32 +02:00
Anton Kaliaev
5babaf9a88 [localnet] fix folder permissions errors 2018-04-12 15:51:17 +02:00
Greg Szabo
c0610b2c32 Greg/localnet (#1450)
* Added new Makefile targets for local testnet running using docker

* Added localnode docker image description, some documentation and refactored to use the tendermint testnet command

* Fixes for the new tendermint testnet command

* More fixes on tendermint testnet and docker-compose

* Changed logging

* Added missing targets to phony
2018-04-12 13:15:16 +02:00
Anton Kaliaev
1db2224241 do not use mask in testnet cmd (#1451)
fix node ids
2018-04-11 20:53:33 +02:00
Anton Kaliaev
0323b03daf improve testnet cmd (#1449)
* improve testnet cmd

* allow non-validators
* configurable prefix
* populating of persistent peers

* relax permissions

* cleanup output dir every time

* do not remove dir

* remove panic comments
2018-04-11 19:40:53 +02:00
Anton Kaliaev
379f9f875b update docs for latest develop (#1448)
* update docs for latest develop

* latest_app_hash, not app_hash
2018-04-11 17:36:14 +02:00
Thomas Corbière
ab00bf7c8b standardize PRNG access (#1411)
* replace math/rand with tmlibs equivalent.

* update tmlibs dependency
2018-04-11 11:38:30 +02:00
Bric3d
64879c1e6a 1417 status response format (#1424)
* Reformated the ResultStatus

* fix misuse of ResultStatus.

* updated changelog

* Fixed tests

* fixed rpc helper tests

* fixed rpc_tests

* fixed mock/status_test

* fixed typo

* fixed ommitempty on validatorstatus and the changelog

* fixed extra line in changelog

* Updated usage of the /status json response in tests after breaking changes

* Updated remaining tests with changes after searching the codebase for usage

* Reformated the ResultStatus

* fix misuse of ResultStatus.

* updated changelog

* Fixed tests

* fixed rpc helper tests

* fixed rpc_tests

* fixed mock/status_test

* fixed typo

* fixed ommitempty on validatorstatus and the changelog

* Updated usage of the /status json response in tests after breaking changes

* Updated remaining tests with changes after searching the codebase for usage

* rebased against develop
2018-04-11 10:38:34 +02:00
Vladislav Dmitriyev
7c22e47629 Replaced NodeInfo's pubkey to ID (#1443)
* Replaced NodeInfo PubKey to NodeID

* Fixed tests and replaced NodeID with ID

* Removed unnecessary method ID()

* Fixed codec_test.go

* Fixed codec_test.go

* Removed unnecessary bracket

* Fixed all tests

* Fixed peer_set_test.go

* Fixed peer_test.go

* Fixed common_test.go

* Fixed common_test.go

* Renamed node_id to id

* Removed peer.ID() from RPC net.go

* Replaced NodeInfo pubKey to ID

* Fixed codec_test.go

* Fixed peer_set_test.go

* Fix pex_reactor_test.go

* Refactored code for privateKey initiali

* Fixed peer_set_test.go

* Fixed test.proto and removed orphan string in codec_test.go

* Fixed pointer to a string

* generate node_key when running tendermint init

* [docs] prefix IPs with node IDs

Refs #1429

* gen_node_key cmd

* [docs/specification/secure-p2p] add a note about config

* fix data race

Closes #1442

```
WARNING: DATA RACE
Write at 0x00c4209de7c8 by goroutine 23:
  github.com/tendermint/tendermint/types.(*Block).fillHeader()
      /home/vagrant/go/src/github.com/tendermint/tendermint/types/block.go:88 +0x157
  github.com/tendermint/tendermint/types.(*Block).Hash()
      /home/vagrant/go/src/github.com/tendermint/tendermint/types/block.go:104 +0x121
  github.com/tendermint/tendermint/types.(*Block).HashesTo()
      /home/vagrant/go/src/github.com/tendermint/tendermint/types/block.go:135 +0x4f
  github.com/tendermint/tendermint/consensus.(*ConsensusState).enterPrecommit()
      /home/vagrant/go/src/github.com/tendermint/tendermint/consensus/state.go:1037 +0x182d
  github.com/tendermint/tendermint/consensus.(*ConsensusState).addVote()
      /home/vagrant/go/src/github.com/tendermint/tendermint/consensus/state.go:1425 +0x1a6c
  github.com/tendermint/tendermint/consensus.(*ConsensusState).tryAddVote()
      /home/vagrant/go/src/github.com/tendermint/tendermint/consensus/state.go:1318 +0x77
  github.com/tendermint/tendermint/consensus.(*ConsensusState).handleMsg()
      /home/vagrant/go/src/github.com/tendermint/tendermint/consensus/state.go:581 +0x7a9
  github.com/tendermint/tendermint/consensus.(*ConsensusState).receiveRoutine()
      /home/vagrant/go/src/github.com/tendermint/tendermint/consensus/state.go:539 +0x6c3

Previous read at 0x00c4209de7c8 by goroutine 47:
  github.com/tendermint/tendermint/vendor/github.com/tendermint/tmlibs/common.(*HexBytes).MarshalJSON()
      <autogenerated>:1 +0x52
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.invokeMarshalJSON()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/json-encode.go:433 +0x88
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.(*Codec)._encodeReflectJSON()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/json-encode.go:82 +0x8d2
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.(*Codec).encodeReflectJSON()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/json-encode.go:50 +0x10e
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.(*Codec).encodeReflectJSONStruct()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/json-encode.go:348 +0x539
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.(*Codec)._encodeReflectJSON()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/json-encode.go:119 +0x83f
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.(*Codec).encodeReflectJSON()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/json-encode.go:50 +0x10e
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.(*Codec).encodeReflectJSONStruct()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/json-encode.go:348 +0x539
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.(*Codec)._encodeReflectJSON()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/json-encode.go:119 +0x83f
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.(*Codec).encodeReflectJSON()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/json-encode.go:50 +0x10e
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.(*Codec).encodeReflectJSONStruct()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/json-encode.go:348 +0x539
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.(*Codec)._encodeReflectJSON()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/json-encode.go:119 +0x83f
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.(*Codec).encodeReflectJSON()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/json-encode.go:50 +0x10e
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.(*Codec).encodeReflectJSONStruct()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/json-encode.go:348 +0x539
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.(*Codec)._encodeReflectJSON()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/json-encode.go:119 +0x83f
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.(*Codec).encodeReflectJSON()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/json-encode.go:50 +0x10e
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.(*Codec).MarshalJSON()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/amino.go:296 +0x182
  github.com/tendermint/tendermint/rpc/lib/types.NewRPCSuccessResponse()
      /home/vagrant/go/src/github.com/tendermint/tendermint/rpc/lib/types/types.go:100 +0x12c
  github.com/tendermint/tendermint/rpc/lib/server.makeJSONRPCHandler.func1()
      /home/vagrant/go/src/github.com/tendermint/tendermint/rpc/lib/server/handlers.go:152 +0xab7
  net/http.HandlerFunc.ServeHTTP()
      /usr/lib/go-1.9/src/net/http/server.go:1918 +0x51
  net/http.(*ServeMux).ServeHTTP()
      /usr/lib/go-1.9/src/net/http/server.go:2254 +0xa2
  github.com/tendermint/tendermint/rpc/lib/server.RecoverAndLogHandler.func1()
      /home/vagrant/go/src/github.com/tendermint/tendermint/rpc/lib/server/http_server.go:138 +0x4fa
  net/http.HandlerFunc.ServeHTTP()
      /usr/lib/go-1.9/src/net/http/server.go:1918 +0x51
  net/http.serverHandler.ServeHTTP()
      /usr/lib/go-1.9/src/net/http/server.go:2619 +0xbc
  net/http.(*conn).serve()
      /usr/lib/go-1.9/src/net/http/server.go:1801 +0x83b

Goroutine 23 (running) created at:
  github.com/tendermint/tendermint/consensus.(*ConsensusState).OnStart()
      /home/vagrant/go/src/github.com/tendermint/tendermint/consensus/state.go:250 +0x35b
  github.com/tendermint/tendermint/vendor/github.com/tendermint/tmlibs/common.(*BaseService).Start()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/tmlibs/common/service.go:130 +0x5fc
  github.com/tendermint/tendermint/consensus.(*ConsensusReactor).OnStart()
      /home/vagrant/go/src/github.com/tendermint/tendermint/consensus/reactor.go:69 +0x1b4
  github.com/tendermint/tendermint/vendor/github.com/tendermint/tmlibs/common.(*BaseService).Start()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/tmlibs/common/service.go:130 +0x5fc
  github.com/tendermint/tendermint/consensus.(*ConsensusReactor).Start()
      <autogenerated>:1 +0x43
  github.com/tendermint/tendermint/p2p.(*Switch).OnStart()
      /home/vagrant/go/src/github.com/tendermint/tendermint/p2p/switch.go:177 +0x124
  github.com/tendermint/tendermint/vendor/github.com/tendermint/tmlibs/common.(*BaseService).Start()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/tmlibs/common/service.go:130 +0x5fc
  github.com/tendermint/tendermint/node.(*Node).OnStart()
      /home/vagrant/go/src/github.com/tendermint/tendermint/node/node.go:416 +0xa1b
  github.com/tendermint/tendermint/vendor/github.com/tendermint/tmlibs/common.(*BaseService).Start()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/tmlibs/common/service.go:130 +0x5fc
  github.com/tendermint/tendermint/rpc/test.StartTendermint()
      /home/vagrant/go/src/github.com/tendermint/tendermint/rpc/test/helpers.go:100 +0x5b
  github.com/tendermint/tendermint/rpc/client_test.TestMain()
      /home/vagrant/go/src/github.com/tendermint/tendermint/rpc/client/main_test.go:17 +0x4c
  main.main()
      github.com/tendermint/tendermint/rpc/client/_test/_testmain.go:76 +0x1cd

Goroutine 47 (running) created at:
  net/http.(*Server).Serve()
      /usr/lib/go-1.9/src/net/http/server.go:2720 +0x37c
  net/http.Serve()
      /usr/lib/go-1.9/src/net/http/server.go:2323 +0xe2
  github.com/tendermint/tendermint/rpc/lib/server.StartHTTPServer.func1()
      /home/vagrant/go/src/github.com/tendermint/tendermint/rpc/lib/server/http_server.go:35 +0xb3
```

* removed excessive comment

Refs https://github.com/tendermint/tendermint/pull/1446#discussion_r180353446

* use the tag interface for pubsub. (#1438)

* use the tag interface for pubsub.

* update tmlibs.

* Fix unresolved conflict.

* improve `show_node_id` (#1433)

* fix show_node_id

* make LoadNodeKey public

* make LoadNodeKey public

* remove if

* remove if
2018-04-11 10:11:11 +02:00
suyuhuang
384b3ea065 improve show_node_id (#1433)
* fix show_node_id

* make LoadNodeKey public

* make LoadNodeKey public

* remove if

* remove if
2018-04-10 16:03:51 +02:00
Thomas Corbière
6a48bd0c88 use the tag interface for pubsub. (#1438)
* use the tag interface for pubsub.

* update tmlibs.

* Fix unresolved conflict.
2018-04-10 16:03:03 +02:00
Ethan Buchman
d93e177a69 Merge pull request #1446 from tendermint/1442-data-race-fix-attempt
fix data race
2018-04-10 16:49:36 +03:00
Anton Kaliaev
cef053386b Merge pull request #1439 from tendermint/1429-add-docs-for-node-ids
docs: update docs to include IDs or set auth_enc to false
2018-04-10 11:46:48 +02:00
Anton Kaliaev
cca1dd8e3e removed excessive comment
Refs https://github.com/tendermint/tendermint/pull/1446#discussion_r180353446
2018-04-10 11:36:31 +02:00
Anton Kaliaev
26c38e770e fix data race
Closes #1442

```
WARNING: DATA RACE
Write at 0x00c4209de7c8 by goroutine 23:
  github.com/tendermint/tendermint/types.(*Block).fillHeader()
      /home/vagrant/go/src/github.com/tendermint/tendermint/types/block.go:88 +0x157
  github.com/tendermint/tendermint/types.(*Block).Hash()
      /home/vagrant/go/src/github.com/tendermint/tendermint/types/block.go:104 +0x121
  github.com/tendermint/tendermint/types.(*Block).HashesTo()
      /home/vagrant/go/src/github.com/tendermint/tendermint/types/block.go:135 +0x4f
  github.com/tendermint/tendermint/consensus.(*ConsensusState).enterPrecommit()
      /home/vagrant/go/src/github.com/tendermint/tendermint/consensus/state.go:1037 +0x182d
  github.com/tendermint/tendermint/consensus.(*ConsensusState).addVote()
      /home/vagrant/go/src/github.com/tendermint/tendermint/consensus/state.go:1425 +0x1a6c
  github.com/tendermint/tendermint/consensus.(*ConsensusState).tryAddVote()
      /home/vagrant/go/src/github.com/tendermint/tendermint/consensus/state.go:1318 +0x77
  github.com/tendermint/tendermint/consensus.(*ConsensusState).handleMsg()
      /home/vagrant/go/src/github.com/tendermint/tendermint/consensus/state.go:581 +0x7a9
  github.com/tendermint/tendermint/consensus.(*ConsensusState).receiveRoutine()
      /home/vagrant/go/src/github.com/tendermint/tendermint/consensus/state.go:539 +0x6c3

Previous read at 0x00c4209de7c8 by goroutine 47:
  github.com/tendermint/tendermint/vendor/github.com/tendermint/tmlibs/common.(*HexBytes).MarshalJSON()
      <autogenerated>:1 +0x52
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.invokeMarshalJSON()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/json-encode.go:433 +0x88
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.(*Codec)._encodeReflectJSON()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/json-encode.go:82 +0x8d2
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.(*Codec).encodeReflectJSON()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/json-encode.go:50 +0x10e
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.(*Codec).encodeReflectJSONStruct()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/json-encode.go:348 +0x539
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.(*Codec)._encodeReflectJSON()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/json-encode.go:119 +0x83f
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.(*Codec).encodeReflectJSON()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/json-encode.go:50 +0x10e
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.(*Codec).encodeReflectJSONStruct()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/json-encode.go:348 +0x539
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.(*Codec)._encodeReflectJSON()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/json-encode.go:119 +0x83f
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.(*Codec).encodeReflectJSON()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/json-encode.go:50 +0x10e
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.(*Codec).encodeReflectJSONStruct()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/json-encode.go:348 +0x539
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.(*Codec)._encodeReflectJSON()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/json-encode.go:119 +0x83f
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.(*Codec).encodeReflectJSON()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/json-encode.go:50 +0x10e
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.(*Codec).encodeReflectJSONStruct()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/json-encode.go:348 +0x539
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.(*Codec)._encodeReflectJSON()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/json-encode.go:119 +0x83f
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.(*Codec).encodeReflectJSON()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/json-encode.go:50 +0x10e
  github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino.(*Codec).MarshalJSON()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-amino/amino.go:296 +0x182
  github.com/tendermint/tendermint/rpc/lib/types.NewRPCSuccessResponse()
      /home/vagrant/go/src/github.com/tendermint/tendermint/rpc/lib/types/types.go:100 +0x12c
  github.com/tendermint/tendermint/rpc/lib/server.makeJSONRPCHandler.func1()
      /home/vagrant/go/src/github.com/tendermint/tendermint/rpc/lib/server/handlers.go:152 +0xab7
  net/http.HandlerFunc.ServeHTTP()
      /usr/lib/go-1.9/src/net/http/server.go:1918 +0x51
  net/http.(*ServeMux).ServeHTTP()
      /usr/lib/go-1.9/src/net/http/server.go:2254 +0xa2
  github.com/tendermint/tendermint/rpc/lib/server.RecoverAndLogHandler.func1()
      /home/vagrant/go/src/github.com/tendermint/tendermint/rpc/lib/server/http_server.go:138 +0x4fa
  net/http.HandlerFunc.ServeHTTP()
      /usr/lib/go-1.9/src/net/http/server.go:1918 +0x51
  net/http.serverHandler.ServeHTTP()
      /usr/lib/go-1.9/src/net/http/server.go:2619 +0xbc
  net/http.(*conn).serve()
      /usr/lib/go-1.9/src/net/http/server.go:1801 +0x83b

Goroutine 23 (running) created at:
  github.com/tendermint/tendermint/consensus.(*ConsensusState).OnStart()
      /home/vagrant/go/src/github.com/tendermint/tendermint/consensus/state.go:250 +0x35b
  github.com/tendermint/tendermint/vendor/github.com/tendermint/tmlibs/common.(*BaseService).Start()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/tmlibs/common/service.go:130 +0x5fc
  github.com/tendermint/tendermint/consensus.(*ConsensusReactor).OnStart()
      /home/vagrant/go/src/github.com/tendermint/tendermint/consensus/reactor.go:69 +0x1b4
  github.com/tendermint/tendermint/vendor/github.com/tendermint/tmlibs/common.(*BaseService).Start()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/tmlibs/common/service.go:130 +0x5fc
  github.com/tendermint/tendermint/consensus.(*ConsensusReactor).Start()
      <autogenerated>:1 +0x43
  github.com/tendermint/tendermint/p2p.(*Switch).OnStart()
      /home/vagrant/go/src/github.com/tendermint/tendermint/p2p/switch.go:177 +0x124
  github.com/tendermint/tendermint/vendor/github.com/tendermint/tmlibs/common.(*BaseService).Start()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/tmlibs/common/service.go:130 +0x5fc
  github.com/tendermint/tendermint/node.(*Node).OnStart()
      /home/vagrant/go/src/github.com/tendermint/tendermint/node/node.go:416 +0xa1b
  github.com/tendermint/tendermint/vendor/github.com/tendermint/tmlibs/common.(*BaseService).Start()
      /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/tmlibs/common/service.go:130 +0x5fc
  github.com/tendermint/tendermint/rpc/test.StartTendermint()
      /home/vagrant/go/src/github.com/tendermint/tendermint/rpc/test/helpers.go:100 +0x5b
  github.com/tendermint/tendermint/rpc/client_test.TestMain()
      /home/vagrant/go/src/github.com/tendermint/tendermint/rpc/client/main_test.go:17 +0x4c
  main.main()
      github.com/tendermint/tendermint/rpc/client/_test/_testmain.go:76 +0x1cd

Goroutine 47 (running) created at:
  net/http.(*Server).Serve()
      /usr/lib/go-1.9/src/net/http/server.go:2720 +0x37c
  net/http.Serve()
      /usr/lib/go-1.9/src/net/http/server.go:2323 +0xe2
  github.com/tendermint/tendermint/rpc/lib/server.StartHTTPServer.func1()
      /home/vagrant/go/src/github.com/tendermint/tendermint/rpc/lib/server/http_server.go:35 +0xb3
```
2018-04-10 11:15:16 +02:00
Anton Kaliaev
609452958c [docs/specification/secure-p2p] add a note about config 2018-04-09 17:02:48 +02:00
Anton Kaliaev
c954fca376 gen_node_key cmd 2018-04-09 17:02:47 +02:00
Anton Kaliaev
9be16d56ba [docs] prefix IPs with node IDs
Refs #1429
2018-04-09 17:02:47 +02:00
Anton Kaliaev
2b732bc11a generate node_key when running tendermint init 2018-04-09 17:02:47 +02:00
Ethan Buchman
dcd00b0e68 update deps and changelog 2018-04-09 16:36:42 +03:00
Ethan Buchman
ff3f35c5f4 Merge pull request #1347 from tendermint/jae/aminoify
Convert Tendermint to use GoAmino
2018-04-09 16:24:38 +03:00
Ethan Buchman
93c4312cdd Merge pull request #1432 from tendermint/bucky/aminoify
Bucky/aminoify
2018-04-09 15:20:36 +03:00
Ethan Buchman
1a1e4e767b check max msg size in DecodeMessage 2018-04-09 15:18:47 +03:00
Ethan Buchman
bb1b249e8a types: lock block on MakePartSet 2018-04-09 15:04:59 +03:00
Ethan Buchman
c778d7f5d1 fix addresses 2018-04-07 23:13:41 +03:00
Ethan Buchman
bb9b12d67a add scripts/wire2amino.go 2018-04-07 22:04:28 +03:00
Ethan Buchman
767521ac52 update test/p2p/data for amino 2018-04-07 22:03:48 +03:00
Ethan Buchman
df9bf60b05 forgot Gopkg.lock 2018-04-07 20:59:13 +03:00
Ethan Buchman
466c3ab1c7 forgot node/wire.go 2018-04-07 19:53:29 +03:00
Ethan Buchman
c68d406195 fix tests 2018-04-07 19:47:19 +03:00
Ethan Buchman
02c0835e9b fixes post merge 2018-04-07 16:25:10 +03:00
Ethan Buchman
c170800fbd Merge branch 'develop' into jae/aminoify 2018-04-07 16:16:53 +03:00
Jae Kwon
7afe74a963 Update go-crypto to 0.6.1 and change config/toml.go privval address 2018-04-07 02:01:45 -07:00
Jae Kwon
02531ca5a3 Fix race testing (cont;) Bump version to 0.19.0 2018-04-06 17:06:46 -07:00
Jae Kwon
d24e4cb821 Fix race testing 2018-04-06 17:02:29 -07:00
Jae Kwon
fb64314d1c Review from Anton 2018-04-06 13:46:40 -07:00
Ethan Buchman
4930b61a38 Merge pull request #1431 from tendermint/release/v0.18.0
Release/v0.18.0
2018-04-06 23:19:09 +03:00
Ethan Buchman
9cc2cf362f changelog and version 2018-04-06 23:03:27 +03:00
Ethan Buchman
ed93fb34ab Merge pull request #1350 from tendermint/1275-p2p-loopbacks
p2p: loopbacks should be detected and ignored instead of dialling self infinitely
2018-04-06 18:59:05 +03:00
Anton Kaliaev
3d32474da8 make linter happy 2018-04-06 13:26:05 +02:00
Anton Kaliaev
3233c318ea only log errors, dial correct addresses
"this means if there are lookup errors or typos in the persistent_peers,
tendermint will fail to start ? didn't some one ask for us not to do
this previously ?"
2018-04-06 12:35:48 +02:00
Jae Kwon
32e1d195a0 Fix cmd and lite 2018-04-05 22:05:30 -07:00
Jae Kwon
3ca5292dc9 Fix rpc tests 2018-04-05 21:19:14 -07:00
Jae Kwon
c541d58d2f WIP: fix rpc/core 2018-04-05 16:07:29 -07:00
Jae Kwon
3037b5b7ca Fix rpc/lib/... 2018-04-05 15:45:11 -07:00
Ethan Buchman
c9a263c589 Merge pull request #1389 from tendermint/1380-trim-whitespaces
trim whitespace from elements of lists (like `persistent_peers`)
2018-04-05 18:22:46 +03:00
Jae Kwon
e4492afbad Merge 2018-04-05 08:17:10 -07:00
Ethan Buchman
799beebd36 fix consensus tests 2018-04-05 17:54:26 +03:00
Jae Kwon
45ec5fd170 WIP consensus 2018-04-05 07:05:45 -07:00
Anton Kaliaev
6e39ec6e26 do not even try to dial ourselves
also, remove address from the book (plus mark it as our address)
and return an error if we fail to parse peers list
2018-04-05 15:45:52 +02:00
Anton Kaliaev
d38a6cc7ea trim whitespace from elements of lists (like persistent_peers)
Refs #1380
2018-04-05 16:42:26 +03:00
Anton Kaliaev
7f6ee7a46b add a comment for NewSwitch 2018-04-05 15:27:47 +02:00
Anton Kaliaev
34b77fcad4 log error when we fail to add new address 2018-04-05 15:27:47 +02:00
Anton Kaliaev
3b3f45d49b use addrbook#AddOurAddress to store our address 2018-04-05 15:27:47 +02:00
Anton Kaliaev
3284a13fee add test
Refs #1275
2018-04-05 15:27:47 +02:00
Anton Kaliaev
fc9ffee2e3 remove unused tracking because it leads to memory leaks in tests
see https://blog.cosmos.network/debugging-the-memory-leak-in-tendermint-210186711420
2018-04-05 15:27:47 +02:00
Anton Kaliaev
3a672cb2a9 update changelog [ci skip] 2018-04-05 15:27:46 +02:00
Anton Kaliaev
4b8e342309 fix panic: lookup testing on 10.0.2.3:53: no such host 2018-04-05 15:27:46 +02:00
Anton Kaliaev
5a2fa71b03 use combination of IP and port, not just IP 2018-04-05 15:27:46 +02:00
Anton Kaliaev
9a57ef9cbf do not dial ourselves (ok, maybe just once)
Refs #1275
2018-04-05 15:27:46 +02:00
Ethan Buchman
59ca9bf480 update to tmlibs v0.8.1 2018-04-05 16:16:36 +03:00
Ethan Buchman
7cce07bc99 Merge pull request #1352 from tendermint/1228-require-id
p2p: require all addresses come with an ID no matter what
2018-04-05 15:55:41 +03:00
Ethan Buchman
0ae66f75ce Merge pull request #1420 from tendermint/1414-data-race
protect Record* peerStateStats functions by mutex
2018-04-05 15:53:15 +03:00
Jae Kwon
5d1c758730 Fix evidence 2018-04-05 05:43:23 -07:00
Jae Kwon
1b9323f105 Fix blockchain tests 2018-04-05 05:17:43 -07:00
Anton Kaliaev
cee7b5cb54 GetSelectionWithBias
Refs #1130
2018-04-05 12:00:16 +02:00
Anton Kaliaev
1585152341 https://github.com/tendermint/tendermint/pull/1128#discussion_r162799294
Refs #1130
2018-04-05 12:00:16 +02:00
Anton Kaliaev
8e699c2bfd defaultSeedDisconnectWaitPeriod should be at least as long as we expect
it to take for a peer to become MarkGood

Refs #1130
2018-04-05 12:00:16 +02:00
Anton Kaliaev
904a3115a6 require addresses to have an ID by default
Refs #1228
2018-04-05 11:55:29 +02:00
Anton Kaliaev
a506cf47ad protect Record* peerStateStats functions by mutex
Fixes #1414

DATA RACE:
```
Read at 0x00c4214ee940 by goroutine 146:
  github.com/tendermint/tendermint/consensus.(*peerStateStats).String()
      <autogenerated>:1 +0x57
  fmt.(*pp).handleMethods()
      /usr/local/go/src/fmt/print.go:596 +0x3f4
  fmt.(*pp).printArg()
      /usr/local/go/src/fmt/print.go:679 +0x11f
  fmt.(*pp).doPrintf()
      /usr/local/go/src/fmt/print.go:996 +0x319
  fmt.Sprintf()
      /usr/local/go/src/fmt/print.go:196 +0x73
  github.com/tendermint/tendermint/consensus.(*PeerState).StringIndented()
      github.com/tendermint/tendermint/consensus/_test/_obj_test/reactor.go:1426 +0x573
  github.com/tendermint/tendermint/consensus.(*PeerState).String()
      github.com/tendermint/tendermint/consensus/_test/_obj_test/reactor.go:1419 +0x66
  github.com/go-logfmt/logfmt.safeString()
      /home/ubuntu/go/src/github.com/go-logfmt/logfmt/encode.go:299 +0x9d
  github.com/go-logfmt/logfmt.writeValue()
      /home/ubuntu/go/src/github.com/go-logfmt/logfmt/encode.go:217 +0x5a0
  github.com/go-logfmt/logfmt.(*Encoder).EncodeKeyval()
      /home/ubuntu/go/src/github.com/go-logfmt/logfmt/encode.go:61 +0x1dd
  github.com/tendermint/tmlibs/log.tmfmtLogger.Log()
      /home/ubuntu/go/src/github.com/tendermint/tmlibs/log/tmfmt_logger.go:107 +0x1001
  github.com/tendermint/tmlibs/log.(*tmfmtLogger).Log()
      <autogenerated>:1 +0x93
  github.com/go-kit/kit/log.(*context).Log()
      /home/ubuntu/go/src/github.com/go-kit/kit/log/log.go:124 +0x248
  github.com/tendermint/tmlibs/log.(*tmLogger).Debug()
      /home/ubuntu/go/src/github.com/tendermint/tmlibs/log/tm_logger.go:64 +0x1d0
  github.com/tendermint/tendermint/consensus.(*PeerState).PickSendVote()
      github.com/tendermint/tendermint/consensus/_test/_obj_test/reactor.go:1059 +0x242
  github.com/tendermint/tendermint/consensus.(*ConsensusReactor).gossipVotesForHeight()
      github.com/tendermint/tendermint/consensus/_test/_obj_test/reactor.go:789 +0x6ef
  github.com/tendermint/tendermint/consensus.(*ConsensusReactor).gossipVotesRoutine()
      github.com/tendermint/tendermint/consensus/_test/_obj_test/reactor.go:723 +0x1039

Previous write at 0x00c4214ee940 by goroutine 21:
  github.com/tendermint/tendermint/consensus.(*PeerState).RecordVote()
      github.com/tendermint/tendermint/consensus/_test/_obj_test/reactor.go:1242 +0x15a
  github.com/tendermint/tendermint/consensus.(*ConsensusReactor).Receive()
      github.com/tendermint/tendermint/consensus/_test/_obj_test/reactor.go:309 +0x32e6
  github.com/tendermint/tendermint/p2p.createMConnection.func1()
      /home/ubuntu/go/src/github.com/tendermint/tendermint/p2p/peer.go:365 +0xea
  github.com/tendermint/tendermint/p2p/conn.(*MConnection).recvRoutine()
      /home/ubuntu/go/src/github.com/tendermint/tendermint/p2p/conn/connection.go:531 +0x779
```
2018-04-05 11:42:45 +02:00
Anton Kaliaev
7689c15413 Merge pull request #1378 from tendermint/bucky/disable-test-libs
comment out test_libs because of gcc dep in tmlibs
2018-04-05 11:06:30 +02:00
Zaki Manian
f907113c19 Net_info should print the ID of peers (#1312) 2018-04-05 11:02:23 +02:00
Anton Kaliaev
140f962201 Merge pull request #1406 from tendermint/docker
Update dockerfile and readme
2018-04-05 10:57:19 +02:00
Ethan Buchman
c23d907f12 Merge pull request #1391 from tendermint/581-include-validator-power
Include validator power in /status
2018-04-05 11:18:45 +03:00
Anton Kaliaev
ed782e7508 include validator's voting power in /status
Refs #581
2018-04-04 11:34:59 +02:00
Anton Kaliaev
0732526465 use more relaxing < and >= ops instead of !=
an example of Search from godocs:

```
package main

import (
	"fmt"
	"sort"
)

func main() {
	a := []int{1, 3, 6, 10, 15, 21, 28, 36, 45, 55}
	x := 6

	i := sort.Search(len(a), func(i int) bool { return a[i] >= x })
	if i < len(a) && a[i] == x {
		fmt.Printf("found %d at index %d in %v\n", x, i, a)
	} else {
		fmt.Printf("%d not found in %v\n", x, a)
	}
}
```
2018-04-04 10:42:35 +02:00
Anton Kaliaev
39a4963782 document funcs in validator_set.go 2018-04-04 10:42:35 +02:00
Anton Kaliaev
37ce6b195a ValidatorSet#GetByAddress: return -1 if no validator was found 2018-04-04 10:42:34 +02:00
Ethan Buchman
7aa6d36258 Merge pull request #1412 from tendermint/bucky/exit-conR-subscribe-routine
consensus: check for closed subscription channels and exit routine
2018-04-03 23:53:48 +03:00
Ethan Buchman
991017fc41 Merge pull request #1336 from tendermint/zarko/1308-add-light-client-spec
Add light client spec
2018-04-03 23:02:32 +03:00
Ethan Buchman
5f548c7679 consensus: close pubsub channels. fixes #1372 2018-04-03 22:57:32 +03:00
Ethan Buchman
d14aacf03e Merge pull request #1300 from tendermint/lite-proxy-hardening-and-tests
lite/proxy: Validation* tests and hardening for nil dereferences
2018-04-03 22:43:38 +03:00
Ethan Buchman
39ff4d22e9 minor cleanup 2018-04-03 22:34:18 +03:00
Jae Kwon
196f8410ba WIP commit; Fix types/results_test 2018-04-03 07:03:08 -07:00
Anton Kaliaev
8462493cbf [rpc] fix subscribing using an abci.ResponseDeliverTx tag
Refs #1369
2018-04-03 15:53:13 +02:00
Anton Kaliaev
47b8bd1728 wrote a test for EventBus#PublishEventTx
Refs #1369
2018-04-03 15:53:13 +02:00
Jae Kwon
89cdde7f1e Fix state tests 2018-04-03 06:50:53 -07:00
Ethan Buchman
657fd671ea Merge pull request #1409 from tendermint/zach/docs/tm-monitor
docs: build updates
2018-04-03 15:24:55 +03:00
Zach Ramsay
315c475b79 docs: build updates
ref: https://github.com/tendermint/tools/pull/79
2018-04-03 04:48:40 -07:00
Anton Kaliaev
b800b4ec1d update docker readme 2018-04-02 16:57:25 +02:00
Anton Kaliaev
208ac32fa2 update Dockerfile to point to 0.17.1 release 2018-04-02 16:56:07 +02:00
Anton Kaliaev
641476d40f update docker to use alpine 3.7 2018-04-02 16:55:43 +02:00
Anton Kaliaev
491c8ab4c1 [rpc/lib] log cert and key files in StartHTTPAndTLSServer 2018-04-02 15:21:05 +02:00
Anton Kaliaev
5ef8a6e887 deprecate not fully formed addresses 2018-04-02 15:21:05 +02:00
Anton Kaliaev
d694d47d22 [rpc/lib] rename vars according to Go conventions 2018-04-02 15:21:05 +02:00
Zaki Manian
ecdc1b9bb0 Add a method for creating an https server (#1403) 2018-04-02 11:36:09 +02:00
Anton Kaliaev
9c757108ca [test] remove test_libs
Reasons:
1) all deps we're using should be passing tests (including external)
2) deps can require complicated setup for testing
3) the person responsible for releasing Tendermint should be cautious
when updating a dep
2018-04-02 11:29:03 +02:00
Anton Kaliaev
5243e54641 [codecov] ignore docs, scripts and DOCKER dirs 2018-04-02 11:23:56 +02:00
Ethan Buchman
70e7454c21 comment out test_libs because of gcc dep in tmlibs 2018-04-02 11:23:56 +02:00
Thomas Corbière
2644a529f0 Fix lint errors (#1390)
* use increment and decrement operators.

* remove unnecessary else branches.

* fix package comment with leading space.

* fix receiver names.

* fix error strings.

* remove omittable code.

* remove redundant return statement.

* Revert changes (code is generated.)

* use cfg as receiver name for all config-related types.

* use lsi as the receiver name for the LastSignedInfo type.
2018-04-02 10:21:17 +02:00
Greg Szabo
eaee98ee1f CGO_ENABLED=0 added for static linking (#1396) 2018-04-01 19:54:48 +02:00
Jae Kwon
35a1d747b0 Fix mempool 2018-03-31 11:51:32 +02:00
Jae Kwon
34974e3932 Make types use Amino; Refactor PrivValidator* to FilePV/SocketPV 2018-03-31 00:18:43 +02:00
Alex Hernandez
575a46d9d4 fix typo on block header (#1387) 2018-03-29 11:28:29 +02:00
Ethan Buchman
bcadbd1b10 Merge pull request #1376 from tendermint/1368-unsubscribe-does-not-work
[rpc] unsubscribe does not work
2018-03-28 15:13:40 -04:00
Tomoya Ishizaki
ead9daf1ba Fix code style (#1362)
* cfg: Uniform style for method args and var names
2018-03-28 13:40:47 -04:00
Anton Kaliaev
22949e6dfd new tmlibs Parallel implementation 2018-03-28 19:13:08 +02:00
Anton Kaliaev
49986b05bc update tmlibs
Refs #1376
2018-03-28 19:12:52 +02:00
Vladislav Dmitriyev
2fa7af4614 [lite] fixed listen address (#1384) 2018-03-28 15:59:09 +02:00
Anton Kaliaev
2d857c4b1b add hash field to ResultTx (/tx and /tx_search endpoints) (#1374)
Refs #1367
2018-03-28 15:44:58 +02:00
Anton Kaliaev
2b63f57b4c fix tx_indexer's matchRange
before we're using IteratePrefix, which is wrong because we want full
range, not just "account.number=1".
2018-03-28 15:02:54 +02:00
Anton Kaliaev
4085c72496 sort /tx_search results by height by default
Refs #1366
2018-03-28 15:02:54 +02:00
Ethan Buchman
6f9956990c Merge pull request #1377 from tendermint/release/0.17.1
Release/0.17.1
2018-03-27 11:24:48 -04:00
Ethan Buchman
9bf5862def types: fix genesis.AppStateJSON 2018-03-27 11:20:09 -04:00
Ethan Buchman
e1d98bb7f6 forgot bug fix in changelog 2018-03-27 10:06:30 -04:00
Ethan Buchman
e5cd006bce Merge pull request #1373 from tendermint/release/0.17.0
Release/0.17.0
2018-03-27 09:57:10 -04:00
Anton Kaliaev
58242e1b63 bump version one more time 2018-03-27 09:07:29 +02:00
Anton Kaliaev
4e86835163 update changelog for 0.17.0 release 2018-03-27 09:06:32 +02:00
Anton Kaliaev
ab4ac04c88 bump up the version 2018-03-26 22:07:07 +02:00
Anton Kaliaev
2c1887a635 update changelog 2018-03-26 22:06:58 +02:00
Anton Kaliaev
1c82281b77 make app_options -> app_state backwards compatible 2018-03-26 21:51:07 +02:00
Tomoya Ishizaki
43ac92b615 Changed to make line break easier to read (#1363) 2018-03-26 16:27:20 +02:00
Jae Kwon
901b456151 P2P now works with Amino 2018-03-26 06:40:02 +02:00
Emmanuel T Odeke
8813684040 lite/proxy: consolidate some common test headers into a variable
Addressing some feedback from @ebuchman in regards to
consolidating some common test headers into a variable.

I've added that for simple cases, trying to meet in the middle
instead of creating helpers that obscure readibility and easy
comparison of test cases.
2018-03-25 00:27:42 -06:00
Emmanuel T Odeke
58f36bb321 Review feedback from @melekes
* Fix typo on naming s/deabBeef/deadBeef/g
* Use `assert.*(t,` instead of `assert.New(t);...;assert.*(`
2018-03-24 23:54:01 -06:00
Emmanuel T Odeke
4c2f56626a lite/proxy: Validation* tests and hardening for nil dereferences
Updates https://github.com/tendermint/tendermint/issues/1017

Ensure that the Validate* functions in proxy are tests
and cover the case of sneakish bugs that have been encountered
a few times from nil dereferences. The lite package should
theoretically never panic with a nil dereference. It is meant
to contain the certifiers hence it should never panic with such.

Requires the following bugs to be fixed first;
* https://github.com/tendermint/tendermint/issues/1298
* https://github.com/tendermint/tendermint/issues/1299
2018-03-24 23:54:01 -06:00
Ethan Buchman
e3337d764a Merge pull request #1354 from tendermint/bucky/dep
update dep
2018-03-24 12:14:56 -04:00
Anton Kaliaev
214817ed17 do not add peer to switch if it fails to start 2018-03-23 13:31:48 +01:00
Anton Kaliaev
116a4ec705 temporary fix
I assume there is a deeper issue with how UnmarshalBinary works in
go-amino (i.e., when loading array of some objects, the empty array
becomes []object{nil}). Note when Marshaling, the object is nil.
2018-03-23 12:47:02 +01:00
Ethan Buchman
bbaad22982 update dep 2018-03-23 10:27:00 +01:00
Anton Kaliaev
a7250af303 Exponential backoff follow up (#1349)
* document new functionality [ci skip]

Refs #1304

* add fixme [ci skip]

Refs #1304

* ensure that we dial peer after backoff duration

Refs #1304
2018-03-23 09:48:27 +01:00
Zach
6545a21369 docs/examples: update quick start guide (#1351) 2018-03-22 08:58:02 +01:00
Zarko Milosevic
416f03c05b Add light client spec 2018-03-21 10:00:18 +01:00
Jae Kwon
ced74251e9 maxPacketMsg -> packetMsgMax... 2018-03-21 02:47:38 +01:00
Jae Kwon
6c345f9fa2 First stab: p2p/conn 2018-03-21 02:27:10 +01:00
Ethan Buchman
8c0c8e8e01 Merge pull request #1301 from tendermint/types-data+header+non-nil-panics
types: Hash invoked for nil Data and Header should not panic
2018-03-20 23:38:55 +01:00
Alexander Simmerl
79315efd1f Merge pull request #1341 from EugeneChung/develop
Remove unnecessary bytes.Compare() call
2018-03-20 16:27:06 +01:00
Eugene Chung
a61130aebb Remove unnecessary bytes.Compare() call 2018-03-20 23:43:18 +09:00
Alexander Simmerl
5a51a0ba06 Merge pull request #1337 from tendermint/1296-follow-up
Follow up for /health endpoint
2018-03-20 10:36:47 +01:00
Ethan Buchman
0d0b56739d Merge pull request #1335 from tendermint/zarko/1146-improve-bft-time-spec
Improve BFT time spec
2018-03-20 01:00:34 +01:00
Ethan Buchman
eb1816c9ff Merge pull request #1338 from tendermint/1266/xla-fix-flaky-testswitchreconnectstopersistentpeer
p2p: Keep reference to connections in test peer
2018-03-20 00:14:38 +01:00
Alexander Simmerl
50ae892d5e p2p: Keep reference to connections in test peer
We observed non-deterministic test failures in one of our switch tests,
which would happen if the GC would run between iterations of the accept
loop. As we don't hold any reference to the connection the setup
finalizer might get triggered and therefore the file handle closed. For
the curious check the references on finalizers and the variable scoping
in the spec:

https://groups.google.com/forum/#!topic/golang-nuts/xWkhGJ5PY6c
https://groups.google.com/forum/#!topic/golang-nuts/d8aF4rAob7U/discussion
https://golang.org/ref/spec#Declarations_and_scope

Fixes #1266
2018-03-19 20:35:12 +01:00
Zarko Milosevic
5a79b3d74a Improve the spec to make explicit median computation based on voting power 2018-03-19 19:10:02 +01:00
Anton Kaliaev
460599ef75 fix comment 2018-03-19 20:01:43 +03:00
Anton Kaliaev
830bb72d6f add Health method to clients
Refs #1296
2018-03-19 20:01:43 +03:00
Anton Kaliaev
b11c26cc1c update CHANGELOG 2018-03-19 19:53:28 +03:00
Constantine
152290db7e Add \health rpc endpoint (#1306)
* Init `\health` rpc endpoint

* remove additional info from `\health` rpc endpoint

* Cleanup imports

* Added time threshold for health check

* Update rpc doc

* Remove unnecessary checks for blocktime creation lag

* Clean up of unnecessary config usage
2018-03-19 19:39:37 +03:00
Ethan Buchman
20b198681b Merge pull request #1328 from tendermint/bucky/add-vote-readability
addVote readability
2018-03-19 12:24:28 +01:00
Ethan Buchman
2bf106a1b3 Merge pull request #1333 from tendermint/1244-follow-up
consensus: fix tracking for MarkGood
2018-03-19 12:19:16 +01:00
Anton Kaliaev
2c445059f2 mark peer as good every blocksToContributeToBecomeGoodPeer blocks
if enough peers are marked good eventually some will become unmarked, so
good to have a force that will continue to cycle them back into good
territory!

Refs #1317
2018-03-19 14:10:25 +03:00
Anton Kaliaev
d8b08cd943 return back panic in peer#onReceive
Refs #1317
2018-03-19 13:19:05 +03:00
Anton Kaliaev
ab59f64f57 test we record votes and block parts
Refs #1317
2018-03-19 13:17:11 +03:00
Anton Kaliaev
42e3457884 fix tracking of votes and blockparts to not allow old information
also remove mutex
Refs #1317
2018-03-19 13:17:06 +03:00
Anton Kaliaev
31f3dd42e7 mark peer as good only once
or should we do it every N blocks?
Refs #1317
2018-03-19 13:17:00 +03:00
Anton Kaliaev
5fab8e404d replace magic number with blocksToContributeToBecomeGoodPeer const
Refs #1317
2018-03-19 13:16:56 +03:00
Anton Kaliaev
701df09971 do not use keywords
Refs #1317
2018-03-19 13:16:02 +03:00
Ethan Buchman
d350da3135 config: fix private_peer_ids 2018-03-18 23:55:44 +01:00
Ethan Buchman
ab7dea4f20 consensus: return from errors sooner in addVote 2018-03-18 23:09:04 +01:00
Ethan Buchman
b297efb532 consensus: return from go-routine in test 2018-03-18 23:05:04 +01:00
Ethan Buchman
eaabdb5cac Merge pull request #1282 from tendermint/1126-private-peers
private peers
2018-03-18 22:53:57 +01:00
racin
066aee3045 Documentation: The character for 1/3 fraction could not be rendered in PDF on readthedocs. (#1326) 2018-03-18 22:44:38 +03:00
Ethan Buchman
ff1ec0260e Merge pull request #1318 from tendermint/bucky/testnet-cmd-fix
testnet cmd: ensure config dir exists. closes #1290
2018-03-17 00:05:30 +01:00
Emmanuel T Odeke
7cb3188fbc testnet cmd: ensure config dir exists. closes #1290 2018-03-16 14:26:43 +01:00
Alexander Simmerl
9b9022f8df privVal: Improve SocketClient network code (#1315)
Follow-up to feedback from #1286, this change simplifies the connection
handling in the SocketClient and makes the communication via TCP more
robust. It introduces the tcpTimeoutListener to encapsulate accept and
i/o timeout handling as well as connection keep-alive, this type could
likely be upgraded to handle more fine-grained tuning of the tcp stack
(linger, nodelay, etc.) according to the properties we desire. The same
methods should be applied to the RemoteSigner which will be overhauled
when the priv_val_server is fleshed out.

* require private key
* simplify connect logic
* break out conn upgrades to tcpTimeoutListener
* extend test coverage and simplify component setup
2018-03-16 16:32:17 +04:00
Ethan Buchman
68e049d3af Merge pull request #1244 from tendermint/1147-using-mark-good-and-stop-peer-for-error
Using MarkGood and StopPeerForError
2018-03-15 18:58:29 +01:00
Anton Kaliaev
86ddf17db0 add a todo
Refs #1281
2018-03-15 11:58:20 +04:00
Anton Kaliaev
a655500047 fix copy-pasted comment [ci skip] 2018-03-15 11:58:20 +04:00
Anton Kaliaev
714f885dac mark peer as good if it contributed enough votes or block parts
Refs #1147
2018-03-15 11:58:20 +04:00
Anton Kaliaev
b0d8f552c5 return err if peer has sent a vote that does not match our round 2018-03-15 11:58:20 +04:00
Anton Kaliaev
63cb69cb96 comment out ErrAddingVote because it breaks byzantine_test 2018-03-15 11:58:20 +04:00
Anton Kaliaev
266974cb59 stop peer if it sends invalid vote 2018-03-15 11:58:20 +04:00
Anton Kaliaev
bcf54b0aa3 PanicSanity is deprecated 2018-03-15 11:58:20 +04:00
Anton Kaliaev
d86855ad7a stop peer if it sends us msg with unknown channel 2018-03-15 11:58:20 +04:00
Anton Kaliaev
d0c67bbe16 stop peer if evidence is not valid 2018-03-15 11:58:20 +04:00
Anton Kaliaev
4242352852 stop peer on decoding error 2018-03-15 11:58:19 +04:00
Anton Kaliaev
f299689573 return back defaultChannelCapacity 2018-03-15 11:58:19 +04:00
Anton Kaliaev
baf457e6d4 return error if peer sent us a block we didn't expect with a height too far ahead/behind 2018-03-15 11:58:19 +04:00
Anton Kaliaev
0c7e871ef0 [blockchain] replace timeoutsCh with more abstract errorsCh 2018-03-15 11:58:19 +04:00
Anton Kaliaev
87ce804b4a cmn.PanicSanity is deprecated 2018-03-15 11:58:19 +04:00
Anton Kaliaev
2a258a2c3f revert removing private peers from persistent 2018-03-15 11:55:30 +04:00
Anton Kaliaev
a40518c7da revert adding dial_peers's private flag 2018-03-15 11:55:30 +04:00
Anton Kaliaev
31deaa4a79 fix broken merge 2018-03-15 11:55:30 +04:00
Anton Kaliaev
736ea055a8 add a test for pex reactor 2018-03-15 11:55:30 +04:00
Anton Kaliaev
a39aec0bae rename private_peers to private_peer_ids to distinguish from peers 2018-03-15 11:55:30 +04:00
Anton Kaliaev
8bef3eb1f4 private peers
Refs #1126
2018-03-15 11:55:29 +04:00
Ethan Buchman
244d88dfda Merge pull request #1314 from tendermint/add-go-amino-as-source-to-wire
Add go amino as source to wire
2018-03-15 01:10:38 +01:00
Anton Kaliaev
76e1dd41e4 fix Makefile's .PHONY 2018-03-14 21:42:35 +04:00
Anton Kaliaev
e39187a063 add go-amino as source for go-wire 2018-03-14 21:42:17 +04:00
Ethan Buchman
cd2ba4aa7f Merge pull request #1286 from tendermint/feature/xla-priv-val-invert-dial
Invert privVal socket communication
2018-03-12 16:49:32 +01:00
Emmanuel T Odeke
0de19420f6 cmd/tendermint/commands/lite: add tcp scheme to address URLs (#1297)
Noticed while investigating
https://github.com/tendermint/tendermint/issues/970

As reported by @zramsay, we'd get the warning
from tendermint/rpc/lib because we were passing in
scheme-less addresses, so by default use "tcp".

Also by default, "node" (nodeAddr) has been set to:
  "tcp://localhost:46657"
instead of the bare:
  "localhost:46657"

This change is just to clean up such warnings as
they spuriously would spook users for a package "lite"
that claims to be secure.
2018-03-12 10:03:11 +04:00
Ethan Buchman
f3000d0c84 Merge pull request #1292 from tendermint/1125-exp-backoff-for-ensure-peers
exponential backoff for addrs in the address book
2018-03-11 19:57:49 +01:00
Anton Kaliaev
fc5b0471d9 use time.Since 2018-03-11 14:13:34 +04:00
Anton Kaliaev
264bce4ddd skip dialing based on last time dialed 2018-03-11 14:00:49 +04:00
Anton Kaliaev
0f41570c80 fixes from bucky's review 2018-03-11 13:22:37 +04:00
Emmanuel T Odeke
8723c91db9 types: Hash invoked for nil Data and Header should not panic
Fixes https://github.com/tendermint/tendermint/issues/1298
Fixes https://github.com/tendermint/tendermint/issues/1299

Found while writing tests in https://github.com/tendermint/tendermint/pull/1300
2018-03-10 21:44:08 -08:00
Anton Kaliaev
f85c8896d9 test pex_reactor's dialPeer 2018-03-09 16:23:52 +04:00
Anton Kaliaev
f0d4f56327 refactor pex_reactor tests 2018-03-09 16:02:24 +04:00
Anton Kaliaev
3d5c05e4e6 Merge pull request #1293 from tendermint/update-template
add 2 more points to ISSUE_TEMPLATE
2018-03-09 15:15:34 +04:00
Anton Kaliaev
018da09f14 do not run complete test suite on make
bad dev experience
2018-03-08 18:55:14 +04:00
Anton Kaliaev
60a64af28d add 2 more points to ISSUE_TEMPLATE
Refs #1291
2018-03-08 18:53:11 +04:00
Zach
13a2013229 Testing refactor for Jenkins (#1098)
* de-mystify tests & run them in parallel (#1031)

* test optimization for jenkins (#1093)

* makefile cleanup

* tests: split fast and slow go tests, closes #1055

* pr comments

* restore circle conditions

* fix need_abci

* ...

* docker run: no :Z for circle?

* Remove cmd breaking comment
2018-03-08 18:52:38 +04:00
Anton Kaliaev
1941b5c769 fixes from @xla's review 2018-03-08 16:31:44 +04:00
Anton Kaliaev
21e2c41c6b exponential backoff for addrs in the address book
Refs #1125
2018-03-08 14:04:26 +04:00
Alexander Simmerl
589781721a Invert privVal socket communication
Follow-up to #1255 aligning with the expectation that the external
signing process connects to the node. The SocketClient will block on
start until one connection has been established, support for multiple
signers connected simultaneously is a planned future extension.

* SocketClient accepts connection
* PrivValSocketServer renamed to RemoteSigner
* extend tests
2018-03-07 12:37:05 +01:00
Ethan Buchman
2ce57a65ff Merge pull request #1284 from tendermint/feature/xla-follow-priv-val
Follow-ups to PrivValidator
2018-03-06 23:37:10 +01:00
Alexander Simmerl
2aa77025c3 Fix typo 2018-03-06 19:56:31 +01:00
Alexander Simmerl
8e1856a90a Use builtin panic 2018-03-06 19:56:31 +01:00
Alexander Simmerl
ca619c80b6 Stop privVal socket client on node shutdown 2018-03-06 19:56:30 +01:00
Alexander Simmerl
25ff699425 Improve method docs 2018-03-06 19:55:26 +01:00
Alexander Simmerl
879b4c0a2c Use common method to determine file existence 2018-03-06 19:55:26 +01:00
Ethan Buchman
45d07a3d0b Merge pull request #1283 from tendermint/feature/xla-run-integration-release
Speed up CircleCI builds
2018-03-06 19:38:08 +01:00
Admir Sabanovic
788354d81e fix typo (#1285) 2018-03-06 20:44:13 +04:00
Alexander Simmerl
b7ce89e568 Speed up CircleCI builds
To achieve faster feedback cycles for our feature PRs this change
reduces the average buildtime from 35 to ~6min by utilising their new
2.0 offering based on docker and nomad. We make use of parallel build
steps wherever possible so that the duration is determined by the
slowest test suite (p2p).

This is an intermediate step until we move our CI/CD completely
on-premise for more control and added security.
2018-03-06 17:36:44 +01:00
Anton Kaliaev
8d81a259c7 Merge pull request #1280 from tendermint/zach/explain-determinism
docs: add 'On Determinism'
2018-03-06 13:44:00 +04:00
Zach Ramsay
3019761204 docs: add document 'On Determinism'
closes https://github.com/tendermint/abci/issues/56
2018-03-06 15:19:59 +08:00
Anton Kaliaev
6120a4c5e4 Merge pull request #1256 from tendermint/feature/more-priv-val
PrivValidatorAddr -> PrivValidatorListenAddr. Update ADR008
2018-03-05 21:48:16 +04:00
Alexander Simmerl
533ed2a876 adr: Amend decisions for PrivValidator 2018-03-05 17:38:05 +01:00
Ethan Buchman
d4e4055d57 PrivValidatorAddr -> PrivValidatorListenAddr. Update ADR008 2018-03-05 17:11:43 +01:00
Alexander Simmerl
ee51ad8e29 Make RPC handler protocol agnostic (#1276) 2018-03-05 19:59:04 +04:00
Zach
bdd50c5f37 fix docs links & stuff (#1273)
* fix links in docs/spec etc, closes #1261

* spec: remove ref to non-existant repo

* codecov you weirdo
2018-03-05 16:30:36 +04:00
Anton Kaliaev
3d88612690 Merge pull request #1274 from tendermint/codecov
Codecov
2018-03-05 15:54:33 +04:00
Anton Kaliaev
630d54c95a return back threshold and ignore sections 2018-03-05 15:16:24 +04:00
Ethan Buchman
3cedd8cf07 Merge pull request #1265 from tendermint/bucky/new-wire-api
Bucky/new wire api
2018-03-02 10:56:24 -05:00
Ethan Buchman
929f326dd2 update dep 2018-03-02 10:59:10 -05:00
Ethan Buchman
ff8c648c23 types: uncomment some tests 2018-03-02 09:26:37 -05:00
Ethan Buchman
8bceb5ce36 Merge pull request #1233 from tendermint/feature/xla-dial-seed-without-timeout
p2p: if we have no peers we should dial seeds right away
2018-03-02 09:07:01 -05:00
Alexander Simmerl
8f2703e8b2 Dial seeds directly without potential peers
In order to improve the operator experience we want the node to dial
seeds immediately if there are no peers to connect to. Until now the
routine responsible for ensuring peers are connected to would wait
a random amount of time up to 30s (if not configured otherwise).
2018-03-02 12:55:01 +01:00
Ethan Buchman
c394eef7b8 types: TestValidatorSetVerifyCommit 2018-03-02 04:21:23 -05:00
Ethan Buchman
f9921ae362 types/validator_set_test: move funcs around 2018-03-02 03:52:44 -05:00
Ethan Frey
fff0c6cd8e Add app_state from genesis file in InitChain message 2018-03-02 03:46:04 -05:00
Ethan Buchman
59872bf335 update dep for new go-wire API 2018-03-02 02:28:53 -05:00
Ethan Buchman
656854186c state: fix txResult issue with UnmarshalBinary into ptr 2018-03-02 02:28:17 -05:00
Ethan Buchman
6596bff8ec types: bring back json.Marshal/Unmarshal for genesis/priv_val 2018-03-02 02:09:28 -05:00
Ethan Buchman
eaafd9d61c state: builds 2018-03-02 01:51:27 -05:00
Ethan Buchman
5378bfc5c7 types.SignBytes -> o.SignBytes 2018-03-02 01:50:17 -05:00
Ethan Buchman
abeeeeb611 types: fix validator_set_test issue with UnmarshalBinary into ptr 2018-03-02 01:49:59 -05:00
Ethan Buchman
ca3655a409 types: p2pID -> P2PID 2018-03-02 01:42:56 -05:00
Ethan Buchman
6cf5100645 types: working on tests... 2018-03-02 01:34:23 -05:00
Ethan Buchman
51628aea08 types: revert to old wire. builds 2018-03-02 01:33:38 -05:00
Ethan Buchman
3395f5fb0e types: builds 2018-03-02 01:28:38 -05:00
Ethan Buchman
d2cd079541 types: tests build 2018-03-02 01:28:21 -05:00
Ethan Buchman
fc35e3b8c5 wire: no codec yet 2018-03-02 01:27:52 -05:00
Ethan Buchman
085b4f5f2e add wire pkg with global codec 2018-03-02 01:26:38 -05:00
Ethan Buchman
fd58645dd2 types: remove dep on p2p 2018-03-02 01:26:03 -05:00
Ethan Buchman
200787ede2 types: update for new go-wire. WriteSignBytes -> SignBytes 2018-03-02 01:25:54 -05:00
Ethan Buchman
9cdba04fe9 Merge pull request #1142 from tendermint/add_valid_value_mechanism
Add support for ValidBlock mechanism for the simplest case
2018-03-01 23:33:46 -05:00
Anton Kaliaev
e92c87630d Merge pull request #1258 from tendermint/return-dummy-app
return back dummy & persistent_dummy as options for proxy_app
2018-03-01 15:14:07 +04:00
Zarko Milosevic
d4e93a6de3 Separate ValidBlock rule from unlocking rule 2018-03-01 11:42:22 +01:00
Zarko Milosevic
4670857c15 Add support for ValidBlock mechanism for the simplest case 2018-03-01 11:42:22 +01:00
Anton Kaliaev
e8d8aedd1f update changelog 2018-03-01 12:00:09 +04:00
Anton Kaliaev
87372da730 return back dummy & persistent_dummy as options for proxy_app 2018-03-01 11:54:08 +04:00
Anton Kaliaev
3b40b62d04 Merge pull request #1198 from tendermint/feature/genesisrawjson
SDK: AppOptions -> AppState
2018-03-01 00:55:13 +04:00
Anton Kaliaev
c41cbf2a07 add missing golang.org/x/net/netutil package 2018-02-28 23:44:18 +04:00
Anton Kaliaev
1a3faa8db1 add app_state field to docs 2018-02-28 23:44:10 +04:00
Anton Kaliaev
4ce79baac7 rename app_options to app_state in genesis_test 2018-02-28 23:44:10 +04:00
Anton Kaliaev
056b70b4ce update changelog 2018-02-28 23:44:10 +04:00
rigelrozanski
4806b3b9bf AppOptions -> AppStateJSON 2018-02-28 23:44:10 +04:00
Ethan Buchman
2a8f0000b2 Merge pull request #1250 from tendermint/ditch-glide
Ditch glide
2018-02-28 09:52:12 -05:00
Ethan Buchman
dd2d846c02 Merge pull request #1203 from tendermint/feature/priv_val
types/priv_validator package
2018-02-28 09:27:03 -05:00
Anton Kaliaev
2ae87eee4e style fixes from @xla 2018-02-28 12:24:26 +04:00
use-n-delete
4be23027ed adding recipe for minimalistic deps analysis (#1218) 2018-02-28 11:23:31 +04:00
Anton Kaliaev
c19bbb2403 switch back to parsing .lock file 2018-02-28 11:15:40 +04:00
Ethan Buchman
edb871f514 Merge pull request #1237 from tendermint/feature/priv_val_socket_client
privVal: Integrate socket client
2018-02-28 00:57:51 -05:00
Ethan Buchman
9c5937df96 Merge pull request #1247 from tendermint/feature/xla-integrate-codecov
Integrate CodeCov as change acceptance stage
2018-02-28 00:38:37 -05:00
Ethan Buchman
be6082df8e Merge pull request #1043 from tendermint/lite-binary-vs-linear-search+optimize-insertions
lite: memStoreProvider GetHeightBinarySearch method + fix ValKeys.signHeaders
2018-02-28 00:33:59 -05:00
Anton Kaliaev
66354de219 cd into tendermint before calling dep status 2018-02-27 18:47:53 +04:00
Alexander Simmerl
458a40f74e Integrate CodeCov as change acceptance stage
As a rough measure to keep quality up we want to integrate our code
coverage tooling to the point where it is required for changes to be
merged.

Example taken from https://github.com/cosmos/voyager/blob/develop/codecov.yaml
2018-02-27 15:39:28 +01:00
Anton Kaliaev
0821384ac6 update abci version 2018-02-27 18:34:32 +04:00
Anton Kaliaev
e01650f21d fix Dockerfile.develop 2018-02-27 18:02:40 +04:00
Anton Kaliaev
8dd06cf197 ditch glide 2018-02-27 18:02:40 +04:00
Anton Kaliaev
93732b4c1e remove old network tests
Refs #1249
2018-02-27 18:02:25 +04:00
Zach
2cc63069c6 rename dummy to kvstore (#1223)
* remove accidental binary

* docs: s/Dummy&dummy/KVStore&kvstore/g

* glide update to abci

* update abci import paths

* dummy begone, hello kvstore

* RequestInitChain needs genesisBytes

* glide update
2018-02-27 18:01:10 +04:00
Zaki Manian
6270ecef8c Switch to dep from glide for dependency management (#1243)
* Switch to dep from glide for dependency management

* Update CI dockerfile to use dep instead of glide

* Wrong file extension

* Run 'dep ensure' after copying code

* Install glide to handle abci dependencies in testing

* Use `dep ensure -vendor-only` to setup vendor directory before installing source code on ci
2018-02-27 15:59:50 +04:00
Ethan Buchman
9293ae76bf p2p: introduce peerConn to simplify peer creation (#1226)
* expose AuthEnc in the P2P config

if AuthEnc is true, dialed peers must have a node ID in the address and
it must match the persistent pubkey from the secret handshake.

Refs #1157

* fixes after my own review

* fix docs

* fix build failure

```
p2p/pex/pex_reactor_test.go:288:88: cannot use seed.NodeInfo().NetAddress() (type *p2p.NetAddress) as type string in array or slice literal
```

* p2p: introduce peerConn to simplify peer creation

* Introduce `peerConn` containing the known fields of `peer`
* `peer` only created in `sw.addPeer` once handshake is complete and NodeInfo is checked
* Eliminates some mutable variables and makes the code flow better
* Simplifies the `newXxxPeer` funcs
* Use ID instead of PubKey where possible.
        * SetPubKeyFilter -> SetIDFilter
        * nodeInfo.Validate takes ID
        * remove peer.PubKey()

* persistent node ids

* fixes from review

* test: use ip_plus_id.sh more

* fix invalid memory panic during fast_sync test

```
2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: panic: runtime error: invalid memory address or nil pointer dereference
2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: [signal SIGSEGV: segmentation violation code=0x1 addr=0x20 pc=0x98dd3e]
2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]:
2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: goroutine 3432 [running]:
2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: github.com/tendermint/tendermint/p2p.newOutboundPeerConn(0xc423fd1380, 0xc420933e00, 0x1, 0x1239a60, 0
xc420128c40, 0x2, 0x42caf6, 0xc42001f300, 0xc422831d98, 0xc4227951c0, ...)
2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: #011/go/src/github.com/tendermint/tendermint/p2p/peer.go:123 +0x31e
2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: github.com/tendermint/tendermint/p2p.(*Switch).addOutboundPeerWithConfig(0xc4200ad040, 0xc423fd1380, 0
xc420933e00, 0xc423f48801, 0x28, 0x2)
2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: #011/go/src/github.com/tendermint/tendermint/p2p/switch.go:455 +0x12b
2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: github.com/tendermint/tendermint/p2p.(*Switch).DialPeerWithAddress(0xc4200ad040, 0xc423fd1380, 0x1, 0x
0, 0x0)
2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: #011/go/src/github.com/tendermint/tendermint/p2p/switch.go:371 +0xdc
2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: github.com/tendermint/tendermint/p2p.(*Switch).reconnectToPeer(0xc4200ad040, 0x123e000, 0xc42007bb00)
2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: #011/go/src/github.com/tendermint/tendermint/p2p/switch.go:290 +0x25f
2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: created by github.com/tendermint/tendermint/p2p.(*Switch).StopPeerForError
2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: #011/go/src/github.com/tendermint/tendermint/p2p/switch.go:256 +0x1b7
```
2018-02-27 15:54:40 +04:00
Alexander Simmerl
74d3f7e1fd Integrate private validator socket client
Following ADDR 008 the node will connect to an external
process to handle signing requests. Operation of the external process is
left to the user.

* introduce alias for PrivValidator interface on socket client
* integrate socket client in node
* structure tests
* remove unnecessary flag
2018-02-23 13:58:22 +01:00
Ethan Buchman
2fd023a239 remove accidental binary 2018-02-21 00:04:53 -05:00
Ethan Buchman
ffd2483e67 Merge pull request #1204 from tendermint/feature/priv_val_sockets
Feature/priv val sockets
2018-02-19 16:06:07 -05:00
Alexander Simmerl
a14aab67de Integrate PrivValidator socket server 2018-02-19 19:20:01 +01:00
Alexander Simmerl
106d804357 Correct config description 2018-02-14 02:51:05 +01:00
Alexander Simmerl
a1020307a0 Clean up flags 2018-02-14 02:41:16 +01:00
Alexander Simmerl
6c70b4ce05 Apply connection deadline consistently 2018-02-14 02:25:17 +01:00
Alexander Simmerl
2a292efb56 Return error for all PrivValidator methoods
As calls to the private validator can involve side-effects like network
communication it is desirable for all methods returning an error to not
break the control flow of the caller.

* adjust PrivValidator interface
2018-02-13 19:34:50 +01:00
Alexander Simmerl
82b1a34a36 Separate connect logic
* break out connect functionality out of OnStart
* introduce max retries
2018-02-13 19:08:21 +01:00
Ethan Buchman
8da2a6a147 types/priv_validator: fixes for latest p2p and cmn 2018-02-09 17:24:30 -05:00
Alexander Simmerl
7d71e702d8 Integrate privVal client with node secret 2018-02-09 16:54:43 -05:00
Alexander Simmerl
38d18ca11a Harden tests 2018-02-09 16:53:17 -05:00
Alexander Simmerl
32d9563a15 Format and consolidate 2018-02-09 16:53:17 -05:00
Alexander Simmerl
18f7e52562 Use secret connection 2018-02-09 16:53:17 -05:00
Alexander Simmerl
fec541373d Correct server protocol 2018-02-09 16:53:17 -05:00
Alexander Simmerl
ff600e9aa0 wip: check error of wire read 2018-02-09 16:53:17 -05:00
Alexander Simmerl
a49357b19e wip: Avoid underscore in var name 2018-02-09 16:53:17 -05:00
Alexander Simmerl
4b997c29ee wip: fix nil pointer deference 2018-02-09 16:53:17 -05:00
Alexander Simmerl
d321839669 wip: fix code block in ADR 2018-02-09 16:53:17 -05:00
Alexander Simmerl
c27fda09dd wip: Comment types
* add comments to all public types
* fix comments to adhere to comment standards
2018-02-09 16:53:15 -05:00
Ethan Buchman
23eb84db35 wip: priv val via sockets 2018-02-09 16:52:58 -05:00
Ethan Buchman
bef91ea7fe adr-008-priv-validator 2018-02-09 16:52:05 -05:00
Ethan Buchman
459633fb4c types/priv_validator 2018-02-09 16:38:23 -05:00
Emmanuel Odeke
9ed296ae71 GetByHeight switches between linear & binary search on >=50 items
* GetByHeight will now switch to using binary search once
we have  >=50 items.
* Feedback from @ebuchman to catch a missed spot where
we forgot about lazy sorting that the original code
assumed would always have sorted commits by height.
Added a lazy sorting routine here too.
A test as well to ensure that we always get the properly
sorted and last value.
2018-01-31 20:53:03 -07:00
Zach Ramsay
e8d0960cef nolint 2018-01-31 20:52:12 -07:00
Emmanuel Odeke
2023115ff8 lite: TestCacheGetsBestHeight with GetByHeight and GetByHeightBinarySearch
Addressing PR review requests from @melekes and @ebuchman to
add a test that checks that the heights returned from both are
the same thus providing a perceptible equivalence of the code
linear range search vs binary range search code.
2018-01-31 20:52:12 -07:00
Adrian Brink
7790ae9e6f Fix spelling mistake 2018-01-31 20:52:12 -07:00
Emmanuel Odeke
206da7a1b8 lite: < len(v) in for loop check, as per @melekes' recommendation
Also lazily load the commits to only be run once when
the benchmarks are activated, lest it slows down all the tests
2018-01-31 20:52:11 -07:00
Emmanuel Odeke
14eaba9ec3 lite: memStoreProvider GetHeightBinarySearch method + fix ValKeys.signHeaders
Updates #1021

* Implement a GetHeightBinarySearch method that looks for
the height using the binary search algorithm guaranteeing
worst case iteration time of O(log2(n))
whereas
worst case iteration time of O(n) for the current linear search

So if n we had 500 commits stored by height and sorted, to
trigger the worst case scenario for each, pass in
the most negative height you can find e.g. -1
Linear search: 500 iterations
Binary search: 9 iterations

with n=1000, qHeight = -1
Linear search: 1000 iterations
Binary search: 10 iterations

with n=1e6, qHeight = -1
Linear search: 1e6 iterations
Binary search: 20 iterations

Of course there are realistic expectations e.g. a max of
commits that may be saved so linear search might be useful
for very small size set because it has less preparing overhead
and only ~2 types of comparisons, but nonetheless binary search
shines as soon as we start to hit say 50 commits to search from
as you can see below:

```shell
$ go test -v -run=^$ -bench=MemStore
goos: darwin
goarch: amd64
pkg: github.com/tendermint/tendermint/lite
BenchmarkMemStoreProviderGetByHeightLinearSearch5-4     300000        6491 ns/op      1600 B/op       15 allocs/op
BenchmarkMemStoreProviderGetByHeightLinearSearch50-4      200000       12064 ns/op      1600 B/op       15 allocs/op
BenchmarkMemStoreProviderGetByHeightLinearSearch100-4      50000       32987 ns/op      1600 B/op       15 allocs/op
BenchmarkMemStoreProviderGetByHeightLinearSearch500-4       5000      395521 ns/op      1600 B/op       15 allocs/op
BenchmarkMemStoreProviderGetByHeightLinearSearch1000-4	       500     2940724 ns/op      1600 B/op       15 allocs/op
BenchmarkMemStoreProviderGetByHeightBinarySearch5-4     300000        6281 ns/op      1600 B/op       15 allocs/op
BenchmarkMemStoreProviderGetByHeightBinarySearch50-4      200000       10117 ns/op      1600 B/op       15 allocs/op
BenchmarkMemStoreProviderGetByHeightBinarySearch100-4     100000       18447 ns/op      1600 B/op       15 allocs/op
BenchmarkMemStoreProviderGetByHeightBinarySearch500-4      20000       89029 ns/op      1600 B/op       15 allocs/op
BenchmarkMemStoreProviderGetByHeightBinarySearch1000-4	      5000      265719 ns/op      1600 B/op       15 allocs/op
PASS
ok    github.com/tendermint/tendermint/lite 86.614s
$ go test -v -run=^$ -bench=MemStore
goos: darwin
goarch: amd64
pkg: github.com/tendermint/tendermint/lite
BenchmarkMemStoreProviderGetByHeightLinearSearch5-4     300000        6779 ns/op      1600 B/op       15 allocs/op
BenchmarkMemStoreProviderGetByHeightLinearSearch50-4      100000       12980 ns/op      1600 B/op       15 allocs/op
BenchmarkMemStoreProviderGetByHeightLinearSearch100-4      30000       43598 ns/op      1600 B/op       15 allocs/op
BenchmarkMemStoreProviderGetByHeightLinearSearch500-4       5000      377462 ns/op      1600 B/op       15 allocs/op
BenchmarkMemStoreProviderGetByHeightLinearSearch1000-4	       500     3278122 ns/op      1600 B/op       15 allocs/op
BenchmarkMemStoreProviderGetByHeightBinarySearch5-4     300000        7084 ns/op      1600 B/op       15 allocs/op
BenchmarkMemStoreProviderGetByHeightBinarySearch50-4      200000        9852 ns/op      1600 B/op       15 allocs/op
BenchmarkMemStoreProviderGetByHeightBinarySearch100-4     100000       19020 ns/op      1600 B/op       15 allocs/op
BenchmarkMemStoreProviderGetByHeightBinarySearch500-4      20000       99463 ns/op      1600 B/op       15 allocs/op
BenchmarkMemStoreProviderGetByHeightBinarySearch1000-4	      5000      259293 ns/op      1600 B/op       15 allocs/op
PASS
ok    github.com/tendermint/tendermint/lite 86.204s
```

which gives
```shell
$ benchstat old.txt new.txt
name                               old time/op    new time/op    delta
MemStoreProviderGetByHeight5-4       6.63µs ± 2%    6.68µs ± 6%   ~             (p=1.000 n=2+2)
MemStoreProviderGetByHeight50-4      12.5µs ± 4%    10.0µs ± 1%   ~             (p=0.333 n=2+2)
MemStoreProviderGetByHeight100-4     38.3µs ±14%    18.7µs ± 2%   ~             (p=0.333 n=2+2)
MemStoreProviderGetByHeight500-4      386µs ± 2%      94µs ± 6%   ~             (p=0.333 n=2+2)
MemStoreProviderGetByHeight1000-4    3.11ms ± 5%    0.26ms ± 1%   ~             (p=0.333 n=2+2)
```

If need be we can make a hybrid algorithm that switches between the
linear and binary search depending on the number of items.
This is reminiscent of Python's TimSort algorithm.
2018-01-31 20:52:11 -07:00
292 changed files with 9472 additions and 5245 deletions

201
.circleci/config.yml Normal file
View File

@@ -0,0 +1,201 @@
version: 2
defaults: &defaults
working_directory: /go/src/github.com/tendermint/tendermint
docker:
- image: circleci/golang:1.10.0
environment:
GOBIN: /tmp/workspace/bin
jobs:
setup_dependencies:
<<: *defaults
steps:
- run: mkdir -p /tmp/workspace/bin
- run: mkdir -p /tmp/workspace/profiles
- checkout
- restore_cache:
keys:
- v1-pkg-cache
- run:
name: tools
command: |
export PATH="$GOBIN:$PATH"
make get_tools
- run:
name: dependencies
command: |
export PATH="$GOBIN:$PATH"
make get_vendor_deps
- run:
name: binaries
command: |
export PATH="$GOBIN:$PATH"
make install
- persist_to_workspace:
root: /tmp/workspace
paths:
- bin
- profiles
- save_cache:
key: v1-pkg-cache
paths:
- /go/pkg
- save_cache:
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
paths:
- /go/src/github.com/tendermint/tendermint
setup_abci:
<<: *defaults
steps:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v1-pkg-cache
- restore_cache:
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
- run:
name: Checkout abci
command: |
commit=$(bash scripts/dep_utils/parse.sh abci)
go get -v -u -d github.com/tendermint/abci/...
cd /go/src/github.com/tendermint/abci
git checkout "$commit"
- run:
working_directory: /go/src/github.com/tendermint/abci
name: Install abci
command: |
set -ex
export PATH="$GOBIN:$PATH"
make get_tools
make get_vendor_deps
make install
- run: ls -lah /tmp/workspace/bin
- persist_to_workspace:
root: /tmp/workspace
paths:
- "bin/abci*"
lint:
<<: *defaults
steps:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v1-pkg-cache
- restore_cache:
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
- run:
name: metalinter
command: |
set -ex
export PATH="$GOBIN:$PATH"
make metalinter
test_apps:
<<: *defaults
steps:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v1-pkg-cache
- restore_cache:
key: v1-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
test_cover:
<<: *defaults
parallelism: 4
steps:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v1-pkg-cache
- restore_cache:
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
- run:
name: Run tests
command: |
for pkg in $(go list github.com/tendermint/tendermint/... | grep -v /vendor/ | circleci tests split --split-by=timings); do
id=$(basename "$pkg")
go test -timeout 5m -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg"
done
- persist_to_workspace:
root: /tmp/workspace
paths:
- "profiles/*"
test_persistence:
<<: *defaults
steps:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v1-pkg-cache
- restore_cache:
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
- run:
name: Run tests
command: bash test/persist/test_failure_indices.sh
test_p2p:
environment:
GOBIN: /home/circleci/.go_workspace/bin
GOPATH: /home/circleci/.go_workspace
machine:
image: circleci/classic:latest
steps:
- checkout
- run: mkdir -p $GOPATH/src/github.com/tendermint
- run: ln -sf /home/circleci/project $GOPATH/src/github.com/tendermint/tendermint
- run: bash test/circleci/p2p.sh
upload_coverage:
<<: *defaults
steps:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
- 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
done
- run:
name: upload
command: bash <(curl -s https://codecov.io/bash) -f coverage.txt
workflows:
version: 2
test-suite:
jobs:
- setup_dependencies
- setup_abci:
requires:
- setup_dependencies
- lint:
requires:
- setup_dependencies
- test_apps:
requires:
- setup_abci
- test_cover:
requires:
- setup_dependencies
- test_persistence:
requires:
- setup_abci
- test_p2p
- upload_coverage:
requires:
- test_cover

View File

@@ -1,26 +0,0 @@
#
# This codecov.yml is the default configuration for
# all repositories on Codecov. You may adjust the settings
# below in your own codecov.yml in your repository.
#
coverage:
precision: 2
round: down
range: 70...100
status:
# Learn more at https://codecov.io/docs#yaml_default_commit_status
project:
default:
threshold: 1% # allow this much decrease on project
changes: false
comment:
layout: "header, diff"
behavior: default # update if exists else create new
ignore:
- "docs"
- "*.md"
- "*.rst"

View File

@@ -37,5 +37,8 @@ in a case of bug.
**How to reproduce it** (as minimally and precisely as possible): **How to reproduce it** (as minimally and precisely as possible):
**Logs (you can paste a part showing an error or attach the whole file)**:
**`/dump_consensus_state` output for consensus bugs**
**Anything else do we need to know**: **Anything else do we need to know**:

4
.gitignore vendored
View File

@@ -17,7 +17,11 @@ test/logs
coverage.txt coverage.txt
docs/_build docs/_build
docs/tools docs/tools
docs/abci-spec.rst
*.log *.log
scripts/wal2json/wal2json scripts/wal2json/wal2json
scripts/cutWALUntil/cutWALUntil scripts/cutWALUntil/cutWALUntil
.idea/
*.iml

View File

@@ -7,7 +7,6 @@ BREAKING CHANGES:
- Upgrade consensus for more real-time use of evidence - Upgrade consensus for more real-time use of evidence
FEATURES: FEATURES:
- Peer reputation management
- Use the chain as its own CA for nodes and validators - Use the chain as its own CA for nodes and validators
- Tooling to run multiple blockchains/apps, possibly in a single process - Tooling to run multiple blockchains/apps, possibly in a single process
- State syncing (without transaction replay) - State syncing (without transaction replay)
@@ -25,7 +24,84 @@ BUG FIXES:
- Graceful handling/recovery for apps that have non-determinism or fail to halt - Graceful handling/recovery for apps that have non-determinism or fail to halt
- Graceful handling/recovery for violations of safety, or liveness - Graceful handling/recovery for violations of safety, or liveness
## 0.16.0 (February 20th, 2017) ## 0.19.0 (April 13th, 2018)
BREAKING:
- [cmd] improved `testnet` command; now it can fill in `persistent_peers` for you in the config file and much more (see `tendermint testnet --help` for details)
- [cmd] `show_node_id` now returns an error if there is no node key
- [rpc]: changed the output format for the `/status` endpoint (see https://godoc.org/github.com/tendermint/tendermint/rpc/core#Status)
Upgrade from go-wire to go-amino. This is a sweeping change that breaks everything that is
serialized to disk or over the network.
See github.com/tendermint/go-amino for details on the new format.
See `scripts/wire2amino.go` for a tool to upgrade
genesis/priv_validator/node_key JSON files.
FEATURES:
- [cmd] added `gen_node_key` command
## 0.18.0 (April 6th, 2018)
BREAKING:
- [types] Merkle tree uses different encoding for varints (see tmlibs v0.8.0)
- [types] ValidtorSet.GetByAddress returns -1 if no validator found
- [p2p] require all addresses come with an ID no matter what
- [rpc] Listening address must contain tcp:// or unix:// prefix
FEATURES:
- [rpc] StartHTTPAndTLSServer (not used yet)
- [rpc] Include validator's voting power in `/status`
- [rpc] `/tx` and `/tx_search` responses now include the transaction hash
- [rpc] Include peer NodeIDs in `/net_info`
IMPROVEMENTS:
- [config] trim whitespace from elements of lists (like `persistent_peers`)
- [rpc] `/tx_search` results are sorted by height
- [p2p] do not try to connect to ourselves (ok, maybe only once)
- [p2p] seeds respond with a bias towards good peers
BUG FIXES:
- [rpc] fix subscribing using an abci.ResponseDeliverTx tag
- [rpc] fix tx_indexers matchRange
- [rpc] fix unsubscribing (see tmlibs v0.8.0)
## 0.17.1 (March 27th, 2018)
BUG FIXES:
- [types] Actually support `app_state` in genesis as `AppStateJSON`
## 0.17.0 (March 27th, 2018)
BREAKING:
- [types] WriteSignBytes -> SignBytes
IMPROVEMENTS:
- [all] renamed `dummy` (`persistent_dummy`) to `kvstore` (`persistent_kvstore`) (name "dummy" is deprecated and will not work in the next breaking release)
- [docs] note on determinism (docs/determinism.rst)
- [genesis] `app_options` field is deprecated. please rename it to `app_state` in your genesis file(s). `app_options` will not work in the next breaking release
- [p2p] dial seeds directly without potential peers
- [p2p] exponential backoff for addrs in the address book
- [p2p] mark peer as good if it contributed enough votes or block parts
- [p2p] stop peer if it sends incorrect data, msg to unknown channel, msg we did not expect
- [p2p] when `auth_enc` is true, all dialed peers must have a node ID in their address
- [spec] various improvements
- switched from glide to dep internally for package management
- [wire] prep work for upgrading to new go-wire (which is now called go-amino)
FEATURES:
- [config] exposed `auth_enc` flag to enable/disable encryption
- [config] added the `--p2p.private_peer_ids` flag and `PrivatePeerIDs` config variable (see config for description)
- [rpc] added `/health` endpoint, which returns empty result for now
- [types/priv_validator] new format and socket client, allowing for remote signing
BUG FIXES:
- [consensus] fix liveness bug by introducing ValidBlock mechanism
## 0.16.0 (February 20th, 2018)
BREAKING CHANGES: BREAKING CHANGES:
- [config] use $TMHOME/config for all config and json files - [config] use $TMHOME/config for all config and json files

View File

@@ -34,15 +34,26 @@ Please don't make Pull Requests to `master`.
## Dependencies ## Dependencies
We use [glide](https://github.com/masterminds/glide) to manage dependencies. We use [dep](https://github.com/golang/dep) 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 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 `glide install`. Even for dependencies under our control, glide helps us keeps multiple repos in sync as they evolve. Anything with an executable, such as apps, tools, and the core, should use glide.
Run `bash scripts/glide/status.sh` to get a list of vendored dependencies that may not be up-to-date. 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
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
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 vendored dependencies that may not be
up-to-date.
## Vagrant ## Vagrant
If you are a [Vagrant](https://www.vagrantup.com/) user, you can get started hacking Tendermint with the commands below. If you are a [Vagrant](https://www.vagrantup.com/) user, you can get started
hacking Tendermint with the commands below.
NOTE: In case you installed Vagrant in 2017, you might need to run NOTE: In case you installed Vagrant in 2017, you might need to run
`vagrant box update` to upgrade to the latest `ubuntu/xenial64`. `vagrant box update` to upgrade to the latest `ubuntu/xenial64`.
@@ -53,11 +64,14 @@ vagrant ssh
make test make test
``` ```
## Testing ## Testing
All repos should be hooked up to circle. All repos should be hooked up to [CircleCI](https://circleci.com/).
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`.
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`.
## Branching Model and Release ## Branching Model and Release

View File

@@ -1,8 +1,8 @@
FROM alpine:3.6 FROM alpine:3.7
# This is the release of tendermint to pull in. # This is the release of tendermint to pull in.
ENV TM_VERSION 0.15.0 ENV TM_VERSION 0.17.1
ENV TM_SHA256SUM 71cc271c67eca506ca492c8b90b090132f104bf5dbfe0af2702a50886e88de17 ENV TM_SHA256SUM d57008c63d2d9176861137e38ed203da486febf20ae7d388fb810a75afff8f24
# Tendermint will be looking for genesis file in /tendermint (unless you change # Tendermint will be looking for genesis file in /tendermint (unless you change
# `genesis_file` in config.toml). You can put your config.toml and private # `genesis_file` in config.toml). You can put your config.toml and private
@@ -26,7 +26,7 @@ RUN mkdir -p $DATA_ROOT && \
RUN apk add --no-cache bash curl jq RUN apk add --no-cache bash curl jq
RUN apk add --no-cache openssl && \ RUN apk add --no-cache openssl && \
wget https://s3-us-west-2.amazonaws.com/tendermint/binaries/tendermint/v${TM_VERSION}/tendermint_${TM_VERSION}_linux_amd64.zip && \ wget https://github.com/tendermint/tendermint/releases/download/v${TM_VERSION}/tendermint_${TM_VERSION}_linux_amd64.zip && \
echo "${TM_SHA256SUM} tendermint_${TM_VERSION}_linux_amd64.zip" | sha256sum -c && \ echo "${TM_SHA256SUM} tendermint_${TM_VERSION}_linux_amd64.zip" | sha256sum -c && \
unzip -d /bin tendermint_${TM_VERSION}_linux_amd64.zip && \ unzip -d /bin tendermint_${TM_VERSION}_linux_amd64.zip && \
apk del openssl && \ apk del openssl && \

View File

@@ -1,4 +1,4 @@
FROM alpine:3.6 FROM alpine:3.7
ENV DATA_ROOT /tendermint ENV DATA_ROOT /tendermint
ENV TMHOME $DATA_ROOT ENV TMHOME $DATA_ROOT
@@ -18,9 +18,9 @@ RUN mkdir -p /go/src/github.com/tendermint/tendermint && \
cd /go/src/github.com/tendermint/tendermint && \ cd /go/src/github.com/tendermint/tendermint && \
git clone https://github.com/tendermint/tendermint . && \ git clone https://github.com/tendermint/tendermint . && \
git checkout develop && \ git checkout develop && \
make get_tools && \
make get_vendor_deps && \ make get_vendor_deps && \
make install && \ make install && \
glide cc && \
cd - && \ cd - && \
rm -rf /go/src/github.com/tendermint/tendermint && \ rm -rf /go/src/github.com/tendermint/tendermint && \
apk del go build-base git apk del go build-base git
@@ -32,4 +32,4 @@ EXPOSE 46657
ENTRYPOINT ["tendermint"] ENTRYPOINT ["tendermint"]
CMD ["node", "--moniker=`hostname`", "--proxy_app=dummy"] CMD ["node", "--moniker=`hostname`", "--proxy_app=kvstore"]

View File

@@ -1,6 +1,7 @@
# Supported tags and respective `Dockerfile` links # Supported tags and respective `Dockerfile` links
- `0.15.0`, `latest` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/170777300ea92dc21a8aec1abc16cb51812513a4/DOCKER/Dockerfile) - `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.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.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.12.0` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/70d8afa6e952e24c573ece345560a5971bf2cc0e/DOCKER/Dockerfile)
@@ -34,13 +35,13 @@ To get started developing applications, see the [application developers guide](h
# How to use this image # How to use this image
## Start one instance of the Tendermint core with the `dummy` app ## Start one instance of the Tendermint core with the `kvstore` app
A very simple example of a built-in app and Tendermint core in one container. A very simple example of a built-in app and Tendermint core in one container.
``` ```
docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint init docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint init
docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint node --proxy_app=dummy docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint node --proxy_app=kvstore
``` ```
## mintnet-kubernetes ## mintnet-kubernetes

388
Gopkg.lock generated Normal file
View File

@@ -0,0 +1,388 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "master"
name = "github.com/btcsuite/btcd"
packages = ["btcec"]
revision = "2be2f12b358dc57d70b8f501b00be450192efbc3"
[[projects]]
name = "github.com/davecgh/go-spew"
packages = ["spew"]
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
version = "v1.1.0"
[[projects]]
branch = "master"
name = "github.com/ebuchman/fail-test"
packages = ["."]
revision = "95f809107225be108efcf10a3509e4ea6ceef3c4"
[[projects]]
name = "github.com/fortytw2/leaktest"
packages = ["."]
revision = "a5ef70473c97b71626b9abeda80ee92ba2a7de9e"
version = "v1.2.0"
[[projects]]
name = "github.com/fsnotify/fsnotify"
packages = ["."]
revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9"
version = "v1.4.7"
[[projects]]
name = "github.com/go-kit/kit"
packages = [
"log",
"log/level",
"log/term"
]
revision = "4dc7be5d2d12881735283bcab7352178e190fc71"
version = "v0.6.0"
[[projects]]
name = "github.com/go-logfmt/logfmt"
packages = ["."]
revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5"
version = "v0.3.0"
[[projects]]
name = "github.com/go-stack/stack"
packages = ["."]
revision = "259ab82a6cad3992b4e21ff5cac294ccb06474bc"
version = "v1.7.0"
[[projects]]
name = "github.com/gogo/protobuf"
packages = [
"gogoproto",
"jsonpb",
"proto",
"protoc-gen-gogo/descriptor",
"sortkeys",
"types"
]
revision = "1adfc126b41513cc696b209667c8656ea7aac67c"
version = "v1.0.0"
[[projects]]
name = "github.com/golang/protobuf"
packages = [
"proto",
"ptypes",
"ptypes/any",
"ptypes/duration",
"ptypes/timestamp"
]
revision = "925541529c1fa6821df4e44ce2723319eb2be768"
version = "v1.0.0"
[[projects]]
branch = "master"
name = "github.com/golang/snappy"
packages = ["."]
revision = "553a641470496b2327abcac10b36396bd98e45c9"
[[projects]]
name = "github.com/gorilla/websocket"
packages = ["."]
revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b"
version = "v1.2.0"
[[projects]]
branch = "master"
name = "github.com/hashicorp/hcl"
packages = [
".",
"hcl/ast",
"hcl/parser",
"hcl/printer",
"hcl/scanner",
"hcl/strconv",
"hcl/token",
"json/parser",
"json/scanner",
"json/token"
]
revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168"
[[projects]]
name = "github.com/inconshreveable/mousetrap"
packages = ["."]
revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
version = "v1.0"
[[projects]]
branch = "master"
name = "github.com/jmhodges/levigo"
packages = ["."]
revision = "c42d9e0ca023e2198120196f842701bb4c55d7b9"
[[projects]]
branch = "master"
name = "github.com/kr/logfmt"
packages = ["."]
revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0"
[[projects]]
name = "github.com/magiconair/properties"
packages = ["."]
revision = "c3beff4c2358b44d0493c7dda585e7db7ff28ae6"
version = "v1.7.6"
[[projects]]
branch = "master"
name = "github.com/mitchellh/mapstructure"
packages = ["."]
revision = "00c29f56e2386353d58c599509e8dc3801b0d716"
[[projects]]
name = "github.com/pelletier/go-toml"
packages = ["."]
revision = "acdc4509485b587f5e675510c4f2c63e90ff68a8"
version = "v1.1.0"
[[projects]]
name = "github.com/pkg/errors"
packages = ["."]
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
version = "v0.8.0"
[[projects]]
name = "github.com/pmezard/go-difflib"
packages = ["difflib"]
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0"
[[projects]]
branch = "master"
name = "github.com/rcrowley/go-metrics"
packages = ["."]
revision = "d932a24a8ccb8fcadc993e5c6c58f93dac168294"
[[projects]]
name = "github.com/spf13/afero"
packages = [
".",
"mem"
]
revision = "63644898a8da0bc22138abf860edaf5277b6102e"
version = "v1.1.0"
[[projects]]
name = "github.com/spf13/cast"
packages = ["."]
revision = "8965335b8c7107321228e3e3702cab9832751bac"
version = "v1.2.0"
[[projects]]
name = "github.com/spf13/cobra"
packages = ["."]
revision = "a1f051bc3eba734da4772d60e2d677f47cf93ef4"
version = "v0.0.2"
[[projects]]
branch = "master"
name = "github.com/spf13/jwalterweatherman"
packages = ["."]
revision = "7c0cea34c8ece3fbeb2b27ab9b59511d360fb394"
[[projects]]
name = "github.com/spf13/pflag"
packages = ["."]
revision = "583c0c0531f06d5278b7d917446061adc344b5cd"
version = "v1.0.1"
[[projects]]
name = "github.com/spf13/viper"
packages = ["."]
revision = "b5e8006cbee93ec955a89ab31e0e3ce3204f3736"
version = "v1.0.2"
[[projects]]
name = "github.com/stretchr/testify"
packages = [
"assert",
"require"
]
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
version = "v1.2.1"
[[projects]]
branch = "master"
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"
]
revision = "714f901b98fdb3aa954b4193d8cbd64a28d80cad"
[[projects]]
name = "github.com/tendermint/abci"
packages = [
"client",
"example/code",
"example/counter",
"example/kvstore",
"server",
"types"
]
revision = "78a8905690ef54f9d57e3b2b0ee7ad3a04ef3f1f"
version = "v0.10.3"
[[projects]]
branch = "master"
name = "github.com/tendermint/ed25519"
packages = [
".",
"edwards25519",
"extra25519"
]
revision = "d8387025d2b9d158cf4efb07e7ebf814bcce2057"
[[projects]]
name = "github.com/tendermint/go-amino"
packages = ["."]
revision = "42246108ff925a457fb709475070a03dfd3e2b5c"
version = "0.9.6"
[[projects]]
name = "github.com/tendermint/go-crypto"
packages = ["."]
revision = "915416979bf70efa4bcbf1c6cd5d64c5fff9fc19"
version = "v0.6.2"
[[projects]]
name = "github.com/tendermint/go-wire"
packages = ["."]
revision = "fa721242b042ecd4c6ed1a934ee740db4f74e45c"
version = "v0.7.3"
[[projects]]
name = "github.com/tendermint/tmlibs"
packages = [
"autofile",
"cli",
"cli/flags",
"clist",
"common",
"db",
"flowrate",
"log",
"merkle",
"pubsub",
"pubsub/query",
"test"
]
revision = "97e1f1ad3f510048929a51475811a18686c894df"
version = "0.8.2-rc0"
[[projects]]
branch = "master"
name = "golang.org/x/crypto"
packages = [
"curve25519",
"nacl/box",
"nacl/secretbox",
"openpgp/armor",
"openpgp/errors",
"poly1305",
"ripemd160",
"salsa20/salsa"
]
revision = "d6449816ce06963d9d136eee5a56fca5b0616e7e"
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = [
"context",
"http2",
"http2/hpack",
"idna",
"internal/timeseries",
"lex/httplex",
"trace"
]
revision = "61147c48b25b599e5b561d2e9c4f3e1ef489ca41"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["unix"]
revision = "3b87a42e500a6dc65dae1a55d0b641295971163e"
[[projects]]
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"
]
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
version = "v0.3.0"
[[projects]]
branch = "master"
name = "google.golang.org/genproto"
packages = ["googleapis/rpc/status"]
revision = "51d0944304c3cbce4afe9e5247e21100037bff78"
[[projects]]
name = "google.golang.org/grpc"
packages = [
".",
"balancer",
"codes",
"connectivity",
"credentials",
"grpclb/grpc_lb_v1/messages",
"grpclog",
"internal",
"keepalive",
"metadata",
"naming",
"peer",
"resolver",
"stats",
"status",
"tap",
"transport"
]
revision = "5b3c4e850e90a4cf6a20ebd46c8b32a0a3afcb9e"
version = "v1.7.5"
[[projects]]
name = "gopkg.in/yaml.v2"
packages = ["."]
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
version = "v2.2.1"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "e70f8692c825e80ae8510546e297840b9560d00e11b2272749a55cc2ffd147f0"
solver-name = "gps-cdcl"
solver-version = 1

94
Gopkg.toml Normal file
View File

@@ -0,0 +1,94 @@
# 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"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true
[[constraint]]
name = "github.com/ebuchman/fail-test"
branch = "master"
[[constraint]]
name = "github.com/fortytw2/leaktest"
branch = "master"
[[constraint]]
name = "github.com/go-kit/kit"
version = "~0.6.0"
[[constraint]]
name = "github.com/gogo/protobuf"
version = "~1.0.0"
[[constraint]]
name = "github.com/golang/protobuf"
version = "~1.0.0"
[[constraint]]
name = "github.com/gorilla/websocket"
version = "~1.2.0"
[[constraint]]
name = "github.com/pkg/errors"
version = "~0.8.0"
[[constraint]]
name = "github.com/rcrowley/go-metrics"
branch = "master"
[[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 = "github.com/tendermint/abci"
version = "~0.10.3"
[[constraint]]
name = "github.com/tendermint/go-crypto"
version = "~0.6.2"
[[constraint]]
name = "github.com/tendermint/go-amino"
version = "~0.9.6"
[[constraint]]
name = "github.com/tendermint/tmlibs"
version = "~0.8.2-rc0"
[[constraint]]
name = "google.golang.org/grpc"
version = "~1.7.3"
[prune]
go-tests = true
unused-packages = true

112
Makefile
View File

@@ -1,26 +1,26 @@
GOTOOLS = \ GOTOOLS = \
github.com/tendermint/glide \ github.com/golang/dep/cmd/dep \
# gopkg.in/alecthomas/gometalinter.v2 gopkg.in/alecthomas/gometalinter.v2
PACKAGES=$(shell go list ./... | grep -v '/vendor/') PACKAGES=$(shell go list ./... | grep -v '/vendor/')
BUILD_TAGS?=tendermint BUILD_TAGS?=tendermint
BUILD_FLAGS = -ldflags "-X github.com/tendermint/tendermint/version.GitCommit=`git rev-parse --short=8 HEAD`" BUILD_FLAGS = -ldflags "-X github.com/tendermint/tendermint/version.GitCommit=`git rev-parse --short=8 HEAD`"
all: check build test install all: check build test install
check: check_tools get_vendor_deps check: check_tools ensure_deps
######################################## ########################################
### Build ### Build
build: build:
go build $(BUILD_FLAGS) -tags '$(BUILD_TAGS)' -o build/tendermint ./cmd/tendermint/ CGO_ENABLED=0 go build $(BUILD_FLAGS) -tags '$(BUILD_TAGS)' -o build/tendermint ./cmd/tendermint/
build_race: build_race:
go build -race $(BUILD_FLAGS) -tags '$(BUILD_TAGS)' -o build/tendermint ./cmd/tendermint CGO_ENABLED=0 go build -race $(BUILD_FLAGS) -tags '$(BUILD_TAGS)' -o build/tendermint ./cmd/tendermint
install: install:
go install $(BUILD_FLAGS) -tags '$(BUILD_TAGS)' ./cmd/tendermint CGO_ENABLED=0 go install $(BUILD_FLAGS) -tags '$(BUILD_TAGS)' ./cmd/tendermint
######################################## ########################################
### Distribution ### Distribution
@@ -40,36 +40,84 @@ check_tools:
get_tools: get_tools:
@echo "--> Installing tools" @echo "--> Installing tools"
go get -u -v $(GOTOOLS) go get -u -v $(GOTOOLS)
# @gometalinter.v2 --install @gometalinter.v2 --install
update_tools: update_tools:
@echo "--> Updating tools" @echo "--> Updating tools"
@go get -u $(GOTOOLS) @go get -u $(GOTOOLS)
#Run this from CI
get_vendor_deps: get_vendor_deps:
@rm -rf vendor/ @rm -rf vendor/
@echo "--> Running glide install" @echo "--> Running dep"
@glide install @dep ensure -vendor-only
#Run this locally.
ensure_deps:
@rm -rf vendor/
@echo "--> Running dep"
@dep ensure
draw_deps: draw_deps:
@# requires brew install graphviz or apt-get install graphviz @# requires brew install graphviz or apt-get install graphviz
go get github.com/RobotsAndPencils/goviz go get github.com/RobotsAndPencils/goviz
@goviz -i github.com/tendermint/tendermint/cmd/tendermint -d 3 | dot -Tpng -o dependency-graph.png @goviz -i github.com/tendermint/tendermint/cmd/tendermint -d 3 | dot -Tpng -o dependency-graph.png
get_deps_bin_size:
@# Copy of build recipe with additional flags to perform binary size analysis
$(eval $(shell go build -work -a $(BUILD_FLAGS) -tags '$(BUILD_TAGS)' -o build/tendermint ./cmd/tendermint/ 2>&1))
@find $(WORK) -type f -name "*.a" | xargs -I{} du -hxs "{}" | sort -rh | sed -e s:${WORK}/::g > deps_bin_size.log
@echo "Results can be found here: $(CURDIR)/deps_bin_size.log"
######################################## ########################################
### Testing ### Testing
test: ## required to be run first by most tests
@echo "--> Running go test" build_docker_test_image:
@go test $(PACKAGES) docker build -t tester -f ./test/docker/Dockerfile .
test_race: ### coverage, app, persistence, and libs tests
@echo "--> Running go test --race" test_cover:
@go test -v -race $(PACKAGES) # 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_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
need_abci:
bash scripts/install_abci_apps.sh
test_integrations: test_integrations:
@bash ./test/test.sh make build_docker_test_image
make get_tools
make get_vendor_deps
make install
make need_abci
make test_cover
make test_apps
make test_persistence
make test_p2p
test_release: test_release:
@go test -tags release $(PACKAGES) @go test -tags release $(PACKAGES)
@@ -79,10 +127,17 @@ test100:
vagrant_test: vagrant_test:
vagrant up vagrant up
vagrant ssh -c 'make install'
vagrant ssh -c 'make test_race'
vagrant ssh -c 'make test_integrations' vagrant ssh -c 'make test_integrations'
### go tests
test:
@echo "--> Running go test"
@go test $(PACKAGES)
test_race:
@echo "--> Running go test --race"
@go test -v -race $(PACKAGES)
######################################## ########################################
### Formatting, linting, and vetting ### Formatting, linting, and vetting
@@ -92,7 +147,7 @@ fmt:
metalinter: metalinter:
@echo "--> Running linter" @echo "--> Running linter"
gometalinter.v2 --vendor --deadline=600s --disable-all \ @gometalinter.v2 --vendor --deadline=600s --disable-all \
--enable=deadcode \ --enable=deadcode \
--enable=gosimple \ --enable=gosimple \
--enable=misspell \ --enable=misspell \
@@ -123,8 +178,25 @@ metalinter_all:
@echo "--> Running linter (all)" @echo "--> Running linter (all)"
gometalinter.v2 --vendor --deadline=600s --enable-all --disable=lll ./... gometalinter.v2 --vendor --deadline=600s --enable-all --disable=lll ./...
###########################################################
### Local testnet using docker
# Build linux binary on other platforms
build-linux:
GOOS=linux GOARCH=amd64 $(MAKE) build
# Run a 4-node testnet locally
docker-start:
@echo "Wait until 'Attaching to node0, node1, node2, node3' message appears"
@if ! [ -f build/node0/config/genesis.json ]; then docker run --rm -v `pwd`/build:/tendermint:Z tendermint/localnode testnet --v 4 --o . --populate-persistent-peers --starting-ip-address 192.167.10.2 ; fi
docker-compose up
# Stop testnet
docker-stop:
docker-compose down
# To avoid unintended conflicts with file names, always add to .PHONY # To avoid unintended conflicts with file names, always add to .PHONY
# unless there is a reason not to. # unless there is a reason not to.
# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html # https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
.PHONY: check build build_race dist install check_tools get_tools update_tools get_vendor_deps draw_deps test test_race test_integrations test_release test100 vagrant_test fmt metalinter metalinter_all .PHONY: check build build_race dist install check_tools get_tools update_tools get_vendor_deps draw_deps test_cover test_apps test_persistence test_p2p test test_race test_integrations test_release test100 vagrant_test fmt build-linux docker-start docker-stop

View File

@@ -14,7 +14,7 @@ if [ ! -d $DATA ]; then
echo "starting node" echo "starting node"
tendermint node \ tendermint node \
--home $DATA \ --home $DATA \
--proxy_app dummy \ --proxy_app kvstore \
--p2p.laddr tcp://127.0.0.1:56656 \ --p2p.laddr tcp://127.0.0.1:56656 \
--rpc.laddr tcp://127.0.0.1:56657 \ --rpc.laddr tcp://127.0.0.1:56657 \
--log_level error & --log_level error &
@@ -35,7 +35,7 @@ cp -R $DATA $HOME1
echo "starting validator node" echo "starting validator node"
tendermint node \ tendermint node \
--home $HOME1 \ --home $HOME1 \
--proxy_app dummy \ --proxy_app kvstore \
--p2p.laddr tcp://127.0.0.1:56656 \ --p2p.laddr tcp://127.0.0.1:56656 \
--rpc.laddr tcp://127.0.0.1:56657 \ --rpc.laddr tcp://127.0.0.1:56657 \
--log_level error & --log_level error &
@@ -48,7 +48,7 @@ cp $HOME1/genesis.json $HOME2
printf "starting downloader node" printf "starting downloader node"
tendermint node \ tendermint node \
--home $HOME2 \ --home $HOME2 \
--proxy_app dummy \ --proxy_app kvstore \
--p2p.laddr tcp://127.0.0.1:56666 \ --p2p.laddr tcp://127.0.0.1:56666 \
--rpc.laddr tcp://127.0.0.1:56667 \ --rpc.laddr tcp://127.0.0.1:56667 \
--p2p.persistent_peers 127.0.0.1:56656 \ --p2p.persistent_peers 127.0.0.1:56656 \

View File

@@ -4,8 +4,8 @@ import (
"testing" "testing"
"time" "time"
amino "github.com/tendermint/go-amino"
"github.com/tendermint/go-crypto" "github.com/tendermint/go-crypto"
"github.com/tendermint/go-wire"
proto "github.com/tendermint/tendermint/benchmarks/proto" proto "github.com/tendermint/tendermint/benchmarks/proto"
"github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/p2p"
@@ -14,26 +14,35 @@ import (
func BenchmarkEncodeStatusWire(b *testing.B) { func BenchmarkEncodeStatusWire(b *testing.B) {
b.StopTimer() b.StopTimer()
pubKey := crypto.GenPrivKeyEd25519().PubKey() cdc := amino.NewCodec()
ctypes.RegisterAmino(cdc)
nodeKey := p2p.NodeKey{PrivKey: crypto.GenPrivKeyEd25519()}
status := &ctypes.ResultStatus{ status := &ctypes.ResultStatus{
NodeInfo: p2p.NodeInfo{ NodeInfo: p2p.NodeInfo{
PubKey: pubKey, ID: nodeKey.ID(),
Moniker: "SOMENAME", Moniker: "SOMENAME",
Network: "SOMENAME", Network: "SOMENAME",
ListenAddr: "SOMEADDR", ListenAddr: "SOMEADDR",
Version: "SOMEVER", Version: "SOMEVER",
Other: []string{"SOMESTRING", "OTHERSTRING"}, Other: []string{"SOMESTRING", "OTHERSTRING"},
}, },
PubKey: pubKey, SyncInfo: ctypes.SyncInfo{
LatestBlockHash: []byte("SOMEBYTES"), LatestBlockHash: []byte("SOMEBYTES"),
LatestBlockHeight: 123, LatestBlockHeight: 123,
LatestBlockTime: time.Unix(0, 1234), LatestBlockTime: time.Unix(0, 1234),
},
ValidatorInfo: ctypes.ValidatorInfo{
PubKey: nodeKey.PubKey(),
},
} }
b.StartTimer() b.StartTimer()
counter := 0 counter := 0
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
jsonBytes := wire.JSONBytes(status) jsonBytes, err := cdc.MarshalJSON(status)
if err != nil {
panic(err)
}
counter += len(jsonBytes) counter += len(jsonBytes)
} }
@@ -41,9 +50,11 @@ func BenchmarkEncodeStatusWire(b *testing.B) {
func BenchmarkEncodeNodeInfoWire(b *testing.B) { func BenchmarkEncodeNodeInfoWire(b *testing.B) {
b.StopTimer() b.StopTimer()
pubKey := crypto.GenPrivKeyEd25519().PubKey() cdc := amino.NewCodec()
ctypes.RegisterAmino(cdc)
nodeKey := p2p.NodeKey{PrivKey: crypto.GenPrivKeyEd25519()}
nodeInfo := p2p.NodeInfo{ nodeInfo := p2p.NodeInfo{
PubKey: pubKey, ID: nodeKey.ID(),
Moniker: "SOMENAME", Moniker: "SOMENAME",
Network: "SOMENAME", Network: "SOMENAME",
ListenAddr: "SOMEADDR", ListenAddr: "SOMEADDR",
@@ -54,16 +65,21 @@ func BenchmarkEncodeNodeInfoWire(b *testing.B) {
counter := 0 counter := 0
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
jsonBytes := wire.JSONBytes(nodeInfo) jsonBytes, err := cdc.MarshalJSON(nodeInfo)
if err != nil {
panic(err)
}
counter += len(jsonBytes) counter += len(jsonBytes)
} }
} }
func BenchmarkEncodeNodeInfoBinary(b *testing.B) { func BenchmarkEncodeNodeInfoBinary(b *testing.B) {
b.StopTimer() b.StopTimer()
pubKey := crypto.GenPrivKeyEd25519().PubKey() cdc := amino.NewCodec()
ctypes.RegisterAmino(cdc)
nodeKey := p2p.NodeKey{PrivKey: crypto.GenPrivKeyEd25519()}
nodeInfo := p2p.NodeInfo{ nodeInfo := p2p.NodeInfo{
PubKey: pubKey, ID: nodeKey.ID(),
Moniker: "SOMENAME", Moniker: "SOMENAME",
Network: "SOMENAME", Network: "SOMENAME",
ListenAddr: "SOMEADDR", ListenAddr: "SOMEADDR",
@@ -74,7 +90,7 @@ func BenchmarkEncodeNodeInfoBinary(b *testing.B) {
counter := 0 counter := 0
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
jsonBytes := wire.BinaryBytes(nodeInfo) jsonBytes := cdc.MustMarshalBinaryBare(nodeInfo)
counter += len(jsonBytes) counter += len(jsonBytes)
} }
@@ -82,15 +98,20 @@ func BenchmarkEncodeNodeInfoBinary(b *testing.B) {
func BenchmarkEncodeNodeInfoProto(b *testing.B) { func BenchmarkEncodeNodeInfoProto(b *testing.B) {
b.StopTimer() b.StopTimer()
pubKey := crypto.GenPrivKeyEd25519().PubKey().Unwrap().(crypto.PubKeyEd25519) nodeKey := p2p.NodeKey{PrivKey: crypto.GenPrivKeyEd25519()}
pubKey2 := &proto.PubKey{Ed25519: &proto.PubKeyEd25519{Bytes: pubKey[:]}} nodeID := string(nodeKey.ID())
someName := "SOMENAME"
someAddr := "SOMEADDR"
someVer := "SOMEVER"
someString := "SOMESTRING"
otherString := "OTHERSTRING"
nodeInfo := proto.NodeInfo{ nodeInfo := proto.NodeInfo{
PubKey: pubKey2, Id: &proto.ID{Id: &nodeID},
Moniker: "SOMENAME", Moniker: &someName,
Network: "SOMENAME", Network: &someName,
ListenAddr: "SOMEADDR", ListenAddr: &someAddr,
Version: "SOMEVER", Version: &someVer,
Other: []string{"SOMESTRING", "OTHERSTRING"}, Other: []string{someString, otherString},
} }
b.StartTimer() b.StartTimer()

View File

@@ -0,0 +1,26 @@
DIST_DIRS := find * -type d -exec
VERSION := $(shell perl -ne '/^var version.*"([^"]+)".*$$/ && print "v$$1\n"' main.go)
GOTOOLS = \
github.com/mitchellh/gox
tools:
go get $(GOTOOLS)
get_vendor_deps:
@hash glide 2>/dev/null || go get github.com/Masterminds/glide
glide install
build:
go build
install:
go install
test:
go test -race
clean:
rm -f ./experiments
rm -rf ./dist
.PHONY: tools get_vendor_deps build install test clean

View File

@@ -0,0 +1,12 @@
package: github.com/tendermint/tendermint/benchmarks/experiments
import:
- package: github.com/tendermint/tendermint
version: v0.16.0
subpackages:
- rpc/client
- rpc/lib/types
- types
- package: github.com/tendermint/tmlibs
version: v0.7.0
subpackages:
- log

View File

@@ -0,0 +1,126 @@
package main
import (
"encoding/binary"
"fmt"
"math/rand"
"os"
"sync"
"time"
"context"
"github.com/tendermint/tendermint/rpc/client"
"github.com/tendermint/tendermint/types"
"github.com/tendermint/tmlibs/log"
)
var logger = log.NewNopLogger()
var finishedTasks = 0
var mutex = &sync.Mutex{}
func main() {
var endpoint = "tcp://0.0.0.0:46657"
var httpClient = getHTTPClient(endpoint)
var res, err = httpClient.Status()
if err != nil {
logger.Info("something wrong happens", err)
}
logger.Info("received status", res)
go monitorTask(endpoint)
txCount := 10
var clientNumber = 10
for i := 0; i < clientNumber; i++ {
go clientTask(i, txCount, endpoint)
}
for finishedTasks < clientNumber+1 {
}
fmt.Printf("Done: %d\n", finishedTasks)
}
func clientTask(id, txCount int, endpoint string) {
var httpClient = getHTTPClient(endpoint)
for i := 0; i < txCount; i++ {
var _, err = httpClient.BroadcastTxSync(generateTx(id, rand.Int()))
if err != nil {
fmt.Printf("Something wrong happened: %s\n", err)
}
}
fmt.Printf("Finished client task: %d\n", id)
mutex.Lock()
finishedTasks++
mutex.Unlock()
}
func getHTTPClient(rpcAddr string) *client.HTTP {
return client.NewHTTP(rpcAddr, "/websocket")
}
func generateTx(i, valI int) []byte {
// a tx encodes the validator index, the tx number, and some random junk
tx := make([]byte, 250)
binary.PutUvarint(tx[:32], uint64(valI))
binary.PutUvarint(tx[32:64], uint64(i))
if _, err := rand.Read(tx[65:]); err != nil {
fmt.Println("err reading from crypto/rand", err)
os.Exit(1)
}
return tx
}
func monitorTask(endpoint string) {
fmt.Println("Monitor task started...")
var duration = 5 * time.Second
const subscriber = "monitor"
ctx, cancel := context.WithTimeout(context.Background(), duration)
defer cancel()
evts := make(chan interface{})
var httpClient = getHTTPClient(endpoint)
httpClient.Start()
evtTyp := types.EventNewBlockHeader
// register for the next event of this type
query := types.QueryForEvent(evtTyp)
err := httpClient.Subscribe(ctx, subscriber, query, evts)
if err != nil {
fmt.Println("error when subscribing", err)
}
// make sure to unregister after the test is over
defer httpClient.UnsubscribeAll(ctx, subscriber)
totalNumOfCommittedTxs := int64(0)
for {
fmt.Println("Starting main loop", err)
select {
case evt := <-evts:
event := evt.(types.TMEventData)
header, ok := event.Unwrap().(types.EventDataNewBlockHeader)
if ok {
fmt.Println("received header\n", header.Header.StringIndented(""))
} else {
fmt.Println("not able to unwrap header")
}
// Do some metric computation with header
totalNumOfCommittedTxs += header.Header.NumTxs
case <-ctx.Done():
fmt.Printf("Finished monitor task. Received %d transactions \n", totalNumOfCommittedTxs)
mutex.Lock()
finishedTasks++
mutex.Unlock()
return
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ message ResultStatus {
} }
message NodeInfo { message NodeInfo {
required PubKey pubKey = 1; required ID id = 1;
required string moniker = 2; required string moniker = 2;
required string network = 3; required string network = 3;
required string remoteAddr = 4; required string remoteAddr = 4;
@@ -16,6 +16,10 @@ message NodeInfo {
repeated string other = 7; repeated string other = 7;
} }
message ID {
required string id = 1;
}
message PubKey { message PubKey {
optional PubKeyEd25519 ed25519 = 1; optional PubKeyEd25519 ed25519 = 1;
} }

View File

@@ -1,6 +1,7 @@
package blockchain package blockchain
import ( import (
"errors"
"fmt" "fmt"
"math" "math"
"sync" "sync"
@@ -39,9 +40,12 @@ const (
// Assuming a DSL connection (not a good choice) 128 Kbps (upload) ~ 15 KB/s, // Assuming a DSL connection (not a good choice) 128 Kbps (upload) ~ 15 KB/s,
// sending data across atlantic ~ 7.5 KB/s. // sending data across atlantic ~ 7.5 KB/s.
minRecvRate = 7680 minRecvRate = 7680
// Maximum difference between current and new block's height.
maxDiffBetweenCurrentAndReceivedBlockHeight = 100
) )
var peerTimeoutSeconds = time.Duration(15) // not const so we can override with tests var peerTimeout = 15 * time.Second // not const so we can override with tests
/* /*
Peers self report their heights when we join the block pool. Peers self report their heights when we join the block pool.
@@ -68,10 +72,10 @@ type BlockPool struct {
maxPeerHeight int64 maxPeerHeight int64
requestsCh chan<- BlockRequest requestsCh chan<- BlockRequest
timeoutsCh chan<- p2p.ID errorsCh chan<- peerError
} }
func NewBlockPool(start int64, requestsCh chan<- BlockRequest, timeoutsCh chan<- p2p.ID) *BlockPool { func NewBlockPool(start int64, requestsCh chan<- BlockRequest, errorsCh chan<- peerError) *BlockPool {
bp := &BlockPool{ bp := &BlockPool{
peers: make(map[p2p.ID]*bpPeer), peers: make(map[p2p.ID]*bpPeer),
@@ -80,7 +84,7 @@ func NewBlockPool(start int64, requestsCh chan<- BlockRequest, timeoutsCh chan<-
numPending: 0, numPending: 0,
requestsCh: requestsCh, requestsCh: requestsCh,
timeoutsCh: timeoutsCh, errorsCh: errorsCh,
} }
bp.BaseService = *cmn.NewBaseService(nil, "BlockPool", bp) bp.BaseService = *cmn.NewBaseService(nil, "BlockPool", bp)
return bp return bp
@@ -128,9 +132,10 @@ func (pool *BlockPool) removeTimedoutPeers() {
curRate := peer.recvMonitor.Status().CurRate curRate := peer.recvMonitor.Status().CurRate
// curRate can be 0 on start // curRate can be 0 on start
if curRate != 0 && curRate < minRecvRate { if curRate != 0 && curRate < minRecvRate {
pool.sendTimeout(peer.id) err := errors.New("peer is not sending us data fast enough")
pool.sendError(err, peer.id)
pool.Logger.Error("SendTimeout", "peer", peer.id, pool.Logger.Error("SendTimeout", "peer", peer.id,
"reason", "peer is not sending us data fast enough", "reason", err,
"curRate", fmt.Sprintf("%d KB/s", curRate/1024), "curRate", fmt.Sprintf("%d KB/s", curRate/1024),
"minRate", fmt.Sprintf("%d KB/s", minRecvRate/1024)) "minRate", fmt.Sprintf("%d KB/s", minRecvRate/1024))
peer.didTimeout = true peer.didTimeout = true
@@ -199,7 +204,7 @@ func (pool *BlockPool) PopRequest() {
delete(pool.requesters, pool.height) delete(pool.requesters, pool.height)
pool.height++ pool.height++
} else { } else {
cmn.PanicSanity(cmn.Fmt("Expected requester to pop, got nothing at height %v", pool.height)) panic(fmt.Sprintf("Expected requester to pop, got nothing at height %v", pool.height))
} }
} }
@@ -213,8 +218,9 @@ func (pool *BlockPool) RedoRequest(height int64) p2p.ID {
request := pool.requesters[height] request := pool.requesters[height]
if request.block == nil { if request.block == nil {
cmn.PanicSanity("Expected block to be non-nil") panic("Expected block to be non-nil")
} }
// RemovePeer will redo all requesters associated with this peer. // RemovePeer will redo all requesters associated with this peer.
pool.removePeer(request.peerID) pool.removePeer(request.peerID)
return request.peerID return request.peerID
@@ -227,8 +233,14 @@ func (pool *BlockPool) AddBlock(peerID p2p.ID, block *types.Block, blockSize int
requester := pool.requesters[block.Height] requester := pool.requesters[block.Height]
if requester == nil { if requester == nil {
// a block we didn't expect. pool.Logger.Info("peer sent us a block we didn't expect", "peer", peerID, "curHeight", pool.height, "blockHeight", block.Height)
// TODO:if height is too far ahead, punish peer diff := pool.height - block.Height
if diff < 0 {
diff *= -1
}
if diff > maxDiffBetweenCurrentAndReceivedBlockHeight {
pool.sendError(errors.New("peer sent us a block we didn't expect with a height too far ahead/behind"), peerID)
}
return return
} }
@@ -339,11 +351,11 @@ func (pool *BlockPool) sendRequest(height int64, peerID p2p.ID) {
pool.requestsCh <- BlockRequest{height, peerID} pool.requestsCh <- BlockRequest{height, peerID}
} }
func (pool *BlockPool) sendTimeout(peerID p2p.ID) { func (pool *BlockPool) sendError(err error, peerID p2p.ID) {
if !pool.IsRunning() { if !pool.IsRunning() {
return return
} }
pool.timeoutsCh <- peerID pool.errorsCh <- peerError{err, peerID}
} }
// unused by tendermint; left for debugging purposes // unused by tendermint; left for debugging purposes
@@ -402,9 +414,9 @@ func (peer *bpPeer) resetMonitor() {
func (peer *bpPeer) resetTimeout() { func (peer *bpPeer) resetTimeout() {
if peer.timeout == nil { if peer.timeout == nil {
peer.timeout = time.AfterFunc(time.Second*peerTimeoutSeconds, peer.onTimeout) peer.timeout = time.AfterFunc(peerTimeout, peer.onTimeout)
} else { } else {
peer.timeout.Reset(time.Second * peerTimeoutSeconds) peer.timeout.Reset(peerTimeout)
} }
} }
@@ -430,8 +442,9 @@ func (peer *bpPeer) onTimeout() {
peer.pool.mtx.Lock() peer.pool.mtx.Lock()
defer peer.pool.mtx.Unlock() defer peer.pool.mtx.Unlock()
peer.pool.sendTimeout(peer.id) err := errors.New("peer did not send us anything")
peer.logger.Error("SendTimeout", "reason", "onTimeout") peer.pool.sendError(err, peer.id)
peer.logger.Error("SendTimeout", "reason", err, "timeout", peerTimeout)
peer.didTimeout = true peer.didTimeout = true
} }

View File

@@ -13,7 +13,7 @@ import (
) )
func init() { func init() {
peerTimeoutSeconds = time.Duration(2) peerTimeout = 2 * time.Second
} }
type testPeer struct { type testPeer struct {
@@ -34,9 +34,9 @@ func makePeers(numPeers int, minHeight, maxHeight int64) map[p2p.ID]testPeer {
func TestBasic(t *testing.T) { func TestBasic(t *testing.T) {
start := int64(42) start := int64(42)
peers := makePeers(10, start+1, 1000) peers := makePeers(10, start+1, 1000)
timeoutsCh := make(chan p2p.ID, 100) errorsCh := make(chan peerError, 1000)
requestsCh := make(chan BlockRequest, 100) requestsCh := make(chan BlockRequest, 1000)
pool := NewBlockPool(start, requestsCh, timeoutsCh) pool := NewBlockPool(start, requestsCh, errorsCh)
pool.SetLogger(log.TestingLogger()) pool.SetLogger(log.TestingLogger())
err := pool.Start() err := pool.Start()
@@ -71,8 +71,8 @@ func TestBasic(t *testing.T) {
// Pull from channels // Pull from channels
for { for {
select { select {
case peerID := <-timeoutsCh: case err := <-errorsCh:
t.Errorf("timeout: %v", peerID) t.Error(err)
case request := <-requestsCh: case request := <-requestsCh:
t.Logf("Pulled new BlockRequest %v", request) t.Logf("Pulled new BlockRequest %v", request)
if request.Height == 300 { if request.Height == 300 {
@@ -91,9 +91,9 @@ func TestBasic(t *testing.T) {
func TestTimeout(t *testing.T) { func TestTimeout(t *testing.T) {
start := int64(42) start := int64(42)
peers := makePeers(10, start+1, 1000) peers := makePeers(10, start+1, 1000)
timeoutsCh := make(chan p2p.ID, 100) errorsCh := make(chan peerError, 1000)
requestsCh := make(chan BlockRequest, 100) requestsCh := make(chan BlockRequest, 1000)
pool := NewBlockPool(start, requestsCh, timeoutsCh) pool := NewBlockPool(start, requestsCh, errorsCh)
pool.SetLogger(log.TestingLogger()) pool.SetLogger(log.TestingLogger())
err := pool.Start() err := pool.Start()
if err != nil { if err != nil {
@@ -132,9 +132,10 @@ func TestTimeout(t *testing.T) {
timedOut := map[p2p.ID]struct{}{} timedOut := map[p2p.ID]struct{}{}
for { for {
select { select {
case peerID := <-timeoutsCh: case err := <-errorsCh:
t.Logf("Peer %v timeouted", peerID) t.Log(err)
if _, ok := timedOut[peerID]; !ok { // consider error to be always timeout here
if _, ok := timedOut[err.peerID]; !ok {
counter++ counter++
if counter == len(peers) { if counter == len(peers) {
return // Done! return // Done!

View File

@@ -1,28 +1,22 @@
package blockchain package blockchain
import ( import (
"bytes"
"errors"
"fmt" "fmt"
"reflect" "reflect"
"sync"
"time" "time"
wire "github.com/tendermint/go-wire" amino "github.com/tendermint/go-amino"
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/log"
"github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/p2p"
sm "github.com/tendermint/tendermint/state" sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/log"
) )
const ( const (
// BlockchainChannel is a channel for blocks and status updates (`BlockStore` height) // BlockchainChannel is a channel for blocks and status updates (`BlockStore` height)
BlockchainChannel = byte(0x40) BlockchainChannel = byte(0x40)
defaultChannelCapacity = 1000
trySyncIntervalMS = 50 trySyncIntervalMS = 50
// stop syncing when last block's time is // stop syncing when last block's time is
// within this much of the system time. // within this much of the system time.
@@ -32,6 +26,13 @@ const (
statusUpdateIntervalSeconds = 10 statusUpdateIntervalSeconds = 10
// check if we should switch to consensus reactor // check if we should switch to consensus reactor
switchToConsensusIntervalSeconds = 1 switchToConsensusIntervalSeconds = 1
// NOTE: keep up to date with bcBlockResponseMessage
bcBlockResponseMessagePrefixSize = 4
bcBlockResponseMessageFieldKeySize = 1
maxMsgSize = types.MaxBlockSizeBytes +
bcBlockResponseMessagePrefixSize +
bcBlockResponseMessageFieldKeySize
) )
type consensusReactor interface { type consensusReactor interface {
@@ -40,13 +41,19 @@ type consensusReactor interface {
SwitchToConsensus(sm.State, int) SwitchToConsensus(sm.State, int)
} }
type peerError struct {
err error
peerID p2p.ID
}
func (e peerError) Error() string {
return fmt.Sprintf("error with peer %v: %s", e.peerID, e.err.Error())
}
// BlockchainReactor handles long-term catchup syncing. // BlockchainReactor handles long-term catchup syncing.
type BlockchainReactor struct { type BlockchainReactor struct {
p2p.BaseReactor p2p.BaseReactor
mtx sync.Mutex
params types.ConsensusParams
// immutable // immutable
initialState sm.State initialState sm.State
@@ -56,7 +63,7 @@ type BlockchainReactor struct {
fastSync bool fastSync bool
requestsCh <-chan BlockRequest requestsCh <-chan BlockRequest
timeoutsCh <-chan p2p.ID errorsCh <-chan peerError
} }
// NewBlockchainReactor returns new reactor instance. // NewBlockchainReactor returns new reactor instance.
@@ -64,26 +71,28 @@ func NewBlockchainReactor(state sm.State, blockExec *sm.BlockExecutor, store *Bl
fastSync bool) *BlockchainReactor { fastSync bool) *BlockchainReactor {
if state.LastBlockHeight != store.Height() { if state.LastBlockHeight != store.Height() {
cmn.PanicSanity(cmn.Fmt("state (%v) and store (%v) height mismatch", state.LastBlockHeight, panic(fmt.Sprintf("state (%v) and store (%v) height mismatch", state.LastBlockHeight,
store.Height())) store.Height()))
} }
requestsCh := make(chan BlockRequest, defaultChannelCapacity) const capacity = 1000 // must be bigger than peers count
timeoutsCh := make(chan p2p.ID, defaultChannelCapacity) requestsCh := make(chan BlockRequest, capacity)
errorsCh := make(chan peerError, capacity) // so we don't block in #Receive#pool.AddBlock
pool := NewBlockPool( pool := NewBlockPool(
store.Height()+1, store.Height()+1,
requestsCh, requestsCh,
timeoutsCh, errorsCh,
) )
bcR := &BlockchainReactor{ bcR := &BlockchainReactor{
params: state.ConsensusParams,
initialState: state, initialState: state,
blockExec: blockExec, blockExec: blockExec,
store: store, store: store,
pool: pool, pool: pool,
fastSync: fastSync, fastSync: fastSync,
requestsCh: requestsCh, requestsCh: requestsCh,
timeoutsCh: timeoutsCh, errorsCh: errorsCh,
} }
bcR.BaseReactor = *p2p.NewBaseReactor("BlockchainReactor", bcR) bcR.BaseReactor = *p2p.NewBaseReactor("BlockchainReactor", bcR)
return bcR return bcR
@@ -123,14 +132,16 @@ func (bcR *BlockchainReactor) GetChannels() []*p2p.ChannelDescriptor {
ID: BlockchainChannel, ID: BlockchainChannel,
Priority: 10, Priority: 10,
SendQueueCapacity: 1000, SendQueueCapacity: 1000,
RecvBufferCapacity: 50 * 4096,
RecvMessageCapacity: maxMsgSize,
}, },
} }
} }
// AddPeer implements Reactor by sending our state to peer. // AddPeer implements Reactor by sending our state to peer.
func (bcR *BlockchainReactor) AddPeer(peer p2p.Peer) { func (bcR *BlockchainReactor) AddPeer(peer p2p.Peer) {
if !peer.Send(BlockchainChannel, msgBytes := cdc.MustMarshalBinaryBare(&bcStatusResponseMessage{bcR.store.Height()})
struct{ BlockchainMessage }{&bcStatusResponseMessage{bcR.store.Height()}}) { if !peer.Send(BlockchainChannel, msgBytes) {
// doing nothing, will try later in `poolRoutine` // doing nothing, will try later in `poolRoutine`
} }
// peer is added to the pool once we receive the first // peer is added to the pool once we receive the first
@@ -151,22 +162,22 @@ func (bcR *BlockchainReactor) respondToPeer(msg *bcBlockRequestMessage,
block := bcR.store.LoadBlock(msg.Height) block := bcR.store.LoadBlock(msg.Height)
if block != nil { if block != nil {
msg := &bcBlockResponseMessage{Block: block} msgBytes := cdc.MustMarshalBinaryBare(&bcBlockResponseMessage{Block: block})
return src.TrySend(BlockchainChannel, struct{ BlockchainMessage }{msg}) return src.TrySend(BlockchainChannel, msgBytes)
} }
bcR.Logger.Info("Peer asking for a block we don't have", "src", src, "height", msg.Height) bcR.Logger.Info("Peer asking for a block we don't have", "src", src, "height", msg.Height)
return src.TrySend(BlockchainChannel, struct{ BlockchainMessage }{ msgBytes := cdc.MustMarshalBinaryBare(&bcNoBlockResponseMessage{Height: msg.Height})
&bcNoBlockResponseMessage{Height: msg.Height}, return src.TrySend(BlockchainChannel, msgBytes)
})
} }
// Receive implements Reactor by handling 4 types of messages (look below). // Receive implements Reactor by handling 4 types of messages (look below).
func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) { func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) {
_, msg, err := DecodeMessage(msgBytes, bcR.maxMsgSize()) msg, err := DecodeMessage(msgBytes)
if err != nil { if err != nil {
bcR.Logger.Error("Error decoding message", "err", err) bcR.Logger.Error("Error decoding message", "src", src, "chId", chID, "msg", msg, "err", err, "bytes", msgBytes)
bcR.Switch.StopPeerForError(src, err)
return return
} }
@@ -182,8 +193,8 @@ func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
bcR.pool.AddBlock(src.ID(), msg.Block, len(msgBytes)) bcR.pool.AddBlock(src.ID(), msg.Block, len(msgBytes))
case *bcStatusRequestMessage: case *bcStatusRequestMessage:
// Send peer our state. // Send peer our state.
queued := src.TrySend(BlockchainChannel, msgBytes := cdc.MustMarshalBinaryBare(&bcStatusResponseMessage{bcR.store.Height()})
struct{ BlockchainMessage }{&bcStatusResponseMessage{bcR.store.Height()}}) queued := src.TrySend(BlockchainChannel, msgBytes)
if !queued { if !queued {
// sorry // sorry
} }
@@ -195,21 +206,6 @@ func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
} }
} }
// maxMsgSize returns the maximum allowable size of a
// message on the blockchain reactor.
func (bcR *BlockchainReactor) maxMsgSize() int {
bcR.mtx.Lock()
defer bcR.mtx.Unlock()
return bcR.params.BlockSize.MaxBytes + 2
}
// updateConsensusParams updates the internal consensus params
func (bcR *BlockchainReactor) updateConsensusParams(params types.ConsensusParams) {
bcR.mtx.Lock()
defer bcR.mtx.Unlock()
bcR.params = params
}
// Handle messages from the poolReactor telling the reactor what to do. // Handle messages from the poolReactor telling the reactor what to do.
// NOTE: Don't sleep in the FOR_LOOP or otherwise slow it down! // NOTE: Don't sleep in the FOR_LOOP or otherwise slow it down!
// (Except for the SYNC_LOOP, which is the primary purpose and must be synchronous.) // (Except for the SYNC_LOOP, which is the primary purpose and must be synchronous.)
@@ -230,23 +226,22 @@ func (bcR *BlockchainReactor) poolRoutine() {
FOR_LOOP: FOR_LOOP:
for { for {
select { select {
case request := <-bcR.requestsCh: // chan BlockRequest case request := <-bcR.requestsCh:
peer := bcR.Switch.Peers().Get(request.PeerID) peer := bcR.Switch.Peers().Get(request.PeerID)
if peer == nil { if peer == nil {
continue FOR_LOOP // Peer has since been disconnected. continue FOR_LOOP // Peer has since been disconnected.
} }
msg := &bcBlockRequestMessage{request.Height} msgBytes := cdc.MustMarshalBinaryBare(&bcBlockRequestMessage{request.Height})
queued := peer.TrySend(BlockchainChannel, struct{ BlockchainMessage }{msg}) queued := peer.TrySend(BlockchainChannel, msgBytes)
if !queued { if !queued {
// We couldn't make the request, send-queue full. // We couldn't make the request, send-queue full.
// The pool handles timeouts, just let it go. // The pool handles timeouts, just let it go.
continue FOR_LOOP continue FOR_LOOP
} }
case peerID := <-bcR.timeoutsCh: // chan string case err := <-bcR.errorsCh:
// Peer timed out. peer := bcR.Switch.Peers().Get(err.peerID)
peer := bcR.Switch.Peers().Get(peerID)
if peer != nil { if peer != nil {
bcR.Switch.StopPeerForError(peer, errors.New("BlockchainReactor Timeout")) bcR.Switch.StopPeerForError(peer, err)
} }
case <-statusUpdateTicker.C: case <-statusUpdateTicker.C:
// ask for status updates // ask for status updates
@@ -310,9 +305,6 @@ FOR_LOOP:
} }
blocksSynced++ blocksSynced++
// update the consensus params
bcR.updateConsensusParams(state.ConsensusParams)
if blocksSynced%100 == 0 { if blocksSynced%100 == 0 {
lastRate = 0.9*lastRate + 0.1*(100/time.Since(lastHundred).Seconds()) lastRate = 0.9*lastRate + 0.1*(100/time.Since(lastHundred).Seconds())
bcR.Logger.Info("Fast Sync Rate", "height", bcR.pool.height, bcR.Logger.Info("Fast Sync Rate", "height", bcR.pool.height,
@@ -330,43 +322,36 @@ FOR_LOOP:
// BroadcastStatusRequest broadcasts `BlockStore` height. // BroadcastStatusRequest broadcasts `BlockStore` height.
func (bcR *BlockchainReactor) BroadcastStatusRequest() error { func (bcR *BlockchainReactor) BroadcastStatusRequest() error {
bcR.Switch.Broadcast(BlockchainChannel, msgBytes := cdc.MustMarshalBinaryBare(&bcStatusRequestMessage{bcR.store.Height()})
struct{ BlockchainMessage }{&bcStatusRequestMessage{bcR.store.Height()}}) bcR.Switch.Broadcast(BlockchainChannel, msgBytes)
return nil return nil
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Messages // Messages
const (
msgTypeBlockRequest = byte(0x10)
msgTypeBlockResponse = byte(0x11)
msgTypeNoBlockResponse = byte(0x12)
msgTypeStatusResponse = byte(0x20)
msgTypeStatusRequest = byte(0x21)
)
// BlockchainMessage is a generic message for this reactor. // BlockchainMessage is a generic message for this reactor.
type BlockchainMessage interface{} type BlockchainMessage interface{}
var _ = wire.RegisterInterface( func RegisterBlockchainMessages(cdc *amino.Codec) {
struct{ BlockchainMessage }{}, cdc.RegisterInterface((*BlockchainMessage)(nil), nil)
wire.ConcreteType{&bcBlockRequestMessage{}, msgTypeBlockRequest}, cdc.RegisterConcrete(&bcBlockRequestMessage{}, "tendermint/mempool/BlockRequest", nil)
wire.ConcreteType{&bcBlockResponseMessage{}, msgTypeBlockResponse}, cdc.RegisterConcrete(&bcBlockResponseMessage{}, "tendermint/mempool/BlockResponse", nil)
wire.ConcreteType{&bcNoBlockResponseMessage{}, msgTypeNoBlockResponse}, cdc.RegisterConcrete(&bcNoBlockResponseMessage{}, "tendermint/mempool/NoBlockResponse", nil)
wire.ConcreteType{&bcStatusResponseMessage{}, msgTypeStatusResponse}, cdc.RegisterConcrete(&bcStatusResponseMessage{}, "tendermint/mempool/StatusResponse", nil)
wire.ConcreteType{&bcStatusRequestMessage{}, msgTypeStatusRequest}, cdc.RegisterConcrete(&bcStatusRequestMessage{}, "tendermint/mempool/StatusRequest", nil)
) }
// DecodeMessage decodes BlockchainMessage. // DecodeMessage decodes BlockchainMessage.
// TODO: ensure that bz is completely read. // TODO: ensure that bz is completely read.
func DecodeMessage(bz []byte, maxSize int) (msgType byte, msg BlockchainMessage, err error) { func DecodeMessage(bz []byte) (msg BlockchainMessage, err error) {
msgType = bz[0] if len(bz) > maxMsgSize {
n := int(0) return msg, fmt.Errorf("Msg exceeds max size (%d > %d)",
r := bytes.NewReader(bz) len(bz), maxMsgSize)
msg = wire.ReadBinary(struct{ BlockchainMessage }{}, r, maxSize, &n, &err).(struct{ BlockchainMessage }).BlockchainMessage }
if err != nil && n != len(bz) { err = cdc.UnmarshalBinaryBare(bz, &msg)
err = errors.New("DecodeMessage() had bytes left over") if err != nil {
err = cmn.ErrorWrap(err, "DecodeMessage() had bytes left over")
} }
return return
} }
@@ -391,7 +376,6 @@ func (brm *bcNoBlockResponseMessage) String() string {
//------------------------------------- //-------------------------------------
// NOTE: keep up-to-date with maxBlockchainResponseSize
type bcBlockResponseMessage struct { type bcBlockResponseMessage struct {
Block *types.Block Block *types.Block
} }

View File

@@ -3,8 +3,6 @@ package blockchain
import ( import (
"testing" "testing"
wire "github.com/tendermint/go-wire"
cmn "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db" dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log" "github.com/tendermint/tmlibs/log"
@@ -18,8 +16,15 @@ import (
func makeStateAndBlockStore(logger log.Logger) (sm.State, *BlockStore) { func makeStateAndBlockStore(logger log.Logger) (sm.State, *BlockStore) {
config := cfg.ResetTestRoot("blockchain_reactor_test") config := cfg.ResetTestRoot("blockchain_reactor_test")
blockStore := NewBlockStore(dbm.NewMemDB()) // blockDB := dbm.NewDebugDB("blockDB", dbm.NewMemDB())
state, _ := sm.LoadStateFromDBOrGenesisFile(dbm.NewMemDB(), config.GenesisFile()) // stateDB := dbm.NewDebugDB("stateDB", dbm.NewMemDB())
blockDB := dbm.NewMemDB()
stateDB := dbm.NewMemDB()
blockStore := NewBlockStore(blockDB)
state, err := sm.LoadStateFromDBOrGenesisFile(stateDB, config.GenesisFile())
if err != nil {
panic(cmn.ErrorWrap(err, "error constructing state from genesis file"))
}
return state, blockStore return state, blockStore
} }
@@ -76,10 +81,9 @@ func TestNoBlockResponse(t *testing.T) {
// wait for our response to be received on the peer // wait for our response to be received on the peer
for _, tt := range tests { for _, tt := range tests {
reqBlockMsg := &bcBlockRequestMessage{tt.height} reqBlockMsg := &bcBlockRequestMessage{tt.height}
reqBlockBytes := wire.BinaryBytes(struct{ BlockchainMessage }{reqBlockMsg}) reqBlockBytes := cdc.MustMarshalBinaryBare(reqBlockMsg)
bcr.Receive(chID, peer, reqBlockBytes) bcr.Receive(chID, peer, reqBlockBytes)
value := peer.lastValue() msg := peer.lastBlockchainMessage()
msg := value.(struct{ BlockchainMessage }).BlockchainMessage
if tt.existent { if tt.existent {
if blockMsg, ok := msg.(*bcBlockResponseMessage); !ok { if blockMsg, ok := msg.(*bcBlockResponseMessage); !ok {
@@ -173,22 +177,26 @@ func newbcrTestPeer(id p2p.ID) *bcrTestPeer {
return bcr return bcr
} }
func (tp *bcrTestPeer) lastValue() interface{} { return <-tp.ch } func (tp *bcrTestPeer) lastBlockchainMessage() interface{} { return <-tp.ch }
func (tp *bcrTestPeer) TrySend(chID byte, value interface{}) bool { func (tp *bcrTestPeer) TrySend(chID byte, msgBytes []byte) bool {
if _, ok := value.(struct{ BlockchainMessage }). var msg BlockchainMessage
BlockchainMessage.(*bcStatusResponseMessage); ok { err := cdc.UnmarshalBinaryBare(msgBytes, &msg)
if err != nil {
panic(cmn.ErrorWrap(err, "Error while trying to parse a BlockchainMessage"))
}
if _, ok := msg.(*bcStatusResponseMessage); ok {
// Discard status response messages since they skew our results // Discard status response messages since they skew our results
// We only want to deal with: // We only want to deal with:
// + bcBlockResponseMessage // + bcBlockResponseMessage
// + bcNoBlockResponseMessage // + bcNoBlockResponseMessage
} else { } else {
tp.ch <- value tp.ch <- msg
} }
return true return true
} }
func (tp *bcrTestPeer) Send(chID byte, data interface{}) bool { return tp.TrySend(chID, data) } func (tp *bcrTestPeer) Send(chID byte, msgBytes []byte) bool { return tp.TrySend(chID, msgBytes) }
func (tp *bcrTestPeer) NodeInfo() p2p.NodeInfo { return p2p.NodeInfo{} } func (tp *bcrTestPeer) NodeInfo() p2p.NodeInfo { return p2p.NodeInfo{} }
func (tp *bcrTestPeer) Status() p2p.ConnectionStatus { return p2p.ConnectionStatus{} } func (tp *bcrTestPeer) Status() p2p.ConnectionStatus { return p2p.ConnectionStatus{} }
func (tp *bcrTestPeer) ID() p2p.ID { return tp.id } func (tp *bcrTestPeer) ID() p2p.ID { return tp.id }

View File

@@ -1,14 +1,9 @@
package blockchain package blockchain
import ( import (
"bytes"
"encoding/json"
"fmt" "fmt"
"io"
"sync" "sync"
wire "github.com/tendermint/go-wire"
cmn "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db" dbm "github.com/tendermint/tmlibs/db"
@@ -54,38 +49,25 @@ func (bs *BlockStore) Height() int64 {
return bs.height return bs.height
} }
// GetReader returns the value associated with the given key wrapped in an io.Reader.
// If no value is found, it returns nil.
// It's mainly for use with wire.ReadBinary.
func (bs *BlockStore) GetReader(key []byte) io.Reader {
bytez := bs.db.Get(key)
if bytez == nil {
return nil
}
return bytes.NewReader(bytez)
}
// LoadBlock returns the block with the given height. // LoadBlock returns the block with the given height.
// If no block is found for that height, it returns nil. // If no block is found for that height, it returns nil.
func (bs *BlockStore) LoadBlock(height int64) *types.Block { func (bs *BlockStore) LoadBlock(height int64) *types.Block {
var n int var blockMeta = bs.LoadBlockMeta(height)
var err error if blockMeta == nil {
r := bs.GetReader(calcBlockMetaKey(height))
if r == nil {
return nil return nil
} }
blockMeta := wire.ReadBinary(&types.BlockMeta{}, r, 0, &n, &err).(*types.BlockMeta)
if err != nil { var block = new(types.Block)
cmn.PanicCrisis(cmn.Fmt("Error reading block meta: %v", err)) buf := []byte{}
}
bytez := []byte{}
for i := 0; i < blockMeta.BlockID.PartsHeader.Total; i++ { for i := 0; i < blockMeta.BlockID.PartsHeader.Total; i++ {
part := bs.LoadBlockPart(height, i) part := bs.LoadBlockPart(height, i)
bytez = append(bytez, part.Bytes...) buf = append(buf, part.Bytes...)
} }
block := wire.ReadBinary(&types.Block{}, bytes.NewReader(bytez), 0, &n, &err).(*types.Block) err := cdc.UnmarshalBinary(buf, block)
if err != nil { if err != nil {
cmn.PanicCrisis(cmn.Fmt("Error reading block: %v", err)) // NOTE: The existence of meta should imply the existence of the
// block. So, make sure meta is only saved after blocks are saved.
panic(cmn.ErrorWrap(err, "Error reading block"))
} }
return block return block
} }
@@ -94,15 +76,14 @@ func (bs *BlockStore) LoadBlock(height int64) *types.Block {
// from the block at the given height. // from the block at the given height.
// If no part is found for the given height and index, it returns nil. // If no part is found for the given height and index, it returns nil.
func (bs *BlockStore) LoadBlockPart(height int64, index int) *types.Part { func (bs *BlockStore) LoadBlockPart(height int64, index int) *types.Part {
var n int var part = new(types.Part)
var err error bz := bs.db.Get(calcBlockPartKey(height, index))
r := bs.GetReader(calcBlockPartKey(height, index)) if len(bz) == 0 {
if r == nil {
return nil return nil
} }
part := wire.ReadBinary(&types.Part{}, r, 0, &n, &err).(*types.Part) err := cdc.UnmarshalBinaryBare(bz, part)
if err != nil { if err != nil {
cmn.PanicCrisis(cmn.Fmt("Error reading block part: %v", err)) panic(cmn.ErrorWrap(err, "Error reading block part"))
} }
return part return part
} }
@@ -110,15 +91,14 @@ func (bs *BlockStore) LoadBlockPart(height int64, index int) *types.Part {
// LoadBlockMeta returns the BlockMeta for the given height. // LoadBlockMeta returns the BlockMeta for the given height.
// If no block is found for the given height, it returns nil. // If no block is found for the given height, it returns nil.
func (bs *BlockStore) LoadBlockMeta(height int64) *types.BlockMeta { func (bs *BlockStore) LoadBlockMeta(height int64) *types.BlockMeta {
var n int var blockMeta = new(types.BlockMeta)
var err error bz := bs.db.Get(calcBlockMetaKey(height))
r := bs.GetReader(calcBlockMetaKey(height)) if len(bz) == 0 {
if r == nil {
return nil return nil
} }
blockMeta := wire.ReadBinary(&types.BlockMeta{}, r, 0, &n, &err).(*types.BlockMeta) err := cdc.UnmarshalBinaryBare(bz, blockMeta)
if err != nil { if err != nil {
cmn.PanicCrisis(cmn.Fmt("Error reading block meta: %v", err)) panic(cmn.ErrorWrap(err, "Error reading block meta"))
} }
return blockMeta return blockMeta
} }
@@ -128,15 +108,14 @@ func (bs *BlockStore) LoadBlockMeta(height int64) *types.BlockMeta {
// and it comes from the block.LastCommit for `height+1`. // and it comes from the block.LastCommit for `height+1`.
// If no commit is found for the given height, it returns nil. // If no commit is found for the given height, it returns nil.
func (bs *BlockStore) LoadBlockCommit(height int64) *types.Commit { func (bs *BlockStore) LoadBlockCommit(height int64) *types.Commit {
var n int var commit = new(types.Commit)
var err error bz := bs.db.Get(calcBlockCommitKey(height))
r := bs.GetReader(calcBlockCommitKey(height)) if len(bz) == 0 {
if r == nil {
return nil return nil
} }
commit := wire.ReadBinary(&types.Commit{}, r, 0, &n, &err).(*types.Commit) err := cdc.UnmarshalBinaryBare(bz, commit)
if err != nil { if err != nil {
cmn.PanicCrisis(cmn.Fmt("Error reading commit: %v", err)) panic(cmn.ErrorWrap(err, "Error reading block commit"))
} }
return commit return commit
} }
@@ -145,15 +124,14 @@ func (bs *BlockStore) LoadBlockCommit(height int64) *types.Commit {
// This is useful when we've seen a commit, but there has not yet been // This is useful when we've seen a commit, but there has not yet been
// a new block at `height + 1` that includes this commit in its block.LastCommit. // a new block at `height + 1` that includes this commit in its block.LastCommit.
func (bs *BlockStore) LoadSeenCommit(height int64) *types.Commit { func (bs *BlockStore) LoadSeenCommit(height int64) *types.Commit {
var n int var commit = new(types.Commit)
var err error bz := bs.db.Get(calcSeenCommitKey(height))
r := bs.GetReader(calcSeenCommitKey(height)) if len(bz) == 0 {
if r == nil {
return nil return nil
} }
commit := wire.ReadBinary(&types.Commit{}, r, 0, &n, &err).(*types.Commit) err := cdc.UnmarshalBinaryBare(bz, commit)
if err != nil { if err != nil {
cmn.PanicCrisis(cmn.Fmt("Error reading commit: %v", err)) panic(cmn.ErrorWrap(err, "Error reading block seen commit"))
} }
return commit return commit
} }
@@ -178,21 +156,22 @@ func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, s
// Save block meta // Save block meta
blockMeta := types.NewBlockMeta(block, blockParts) blockMeta := types.NewBlockMeta(block, blockParts)
metaBytes := wire.BinaryBytes(blockMeta) metaBytes := cdc.MustMarshalBinaryBare(blockMeta)
bs.db.Set(calcBlockMetaKey(height), metaBytes) bs.db.Set(calcBlockMetaKey(height), metaBytes)
// Save block parts // Save block parts
for i := 0; i < blockParts.Total(); i++ { for i := 0; i < blockParts.Total(); i++ {
bs.saveBlockPart(height, i, blockParts.GetPart(i)) part := blockParts.GetPart(i)
bs.saveBlockPart(height, i, part)
} }
// Save block commit (duplicate and separate from the Block) // Save block commit (duplicate and separate from the Block)
blockCommitBytes := wire.BinaryBytes(block.LastCommit) blockCommitBytes := cdc.MustMarshalBinaryBare(block.LastCommit)
bs.db.Set(calcBlockCommitKey(height-1), blockCommitBytes) bs.db.Set(calcBlockCommitKey(height-1), blockCommitBytes)
// Save seen commit (seen +2/3 precommits for block) // Save seen commit (seen +2/3 precommits for block)
// NOTE: we can delete this at a later height // NOTE: we can delete this at a later height
seenCommitBytes := wire.BinaryBytes(seenCommit) seenCommitBytes := cdc.MustMarshalBinaryBare(seenCommit)
bs.db.Set(calcSeenCommitKey(height), seenCommitBytes) bs.db.Set(calcSeenCommitKey(height), seenCommitBytes)
// Save new BlockStoreStateJSON descriptor // Save new BlockStoreStateJSON descriptor
@@ -211,7 +190,7 @@ func (bs *BlockStore) saveBlockPart(height int64, index int, part *types.Part) {
if height != bs.Height()+1 { if height != bs.Height()+1 {
cmn.PanicSanity(cmn.Fmt("BlockStore can only save contiguous blocks. Wanted %v, got %v", bs.Height()+1, height)) cmn.PanicSanity(cmn.Fmt("BlockStore can only save contiguous blocks. Wanted %v, got %v", bs.Height()+1, height))
} }
partBytes := wire.BinaryBytes(part) partBytes := cdc.MustMarshalBinaryBare(part)
bs.db.Set(calcBlockPartKey(height, index), partBytes) bs.db.Set(calcBlockPartKey(height, index), partBytes)
} }
@@ -238,12 +217,12 @@ func calcSeenCommitKey(height int64) []byte {
var blockStoreKey = []byte("blockStore") var blockStoreKey = []byte("blockStore")
type BlockStoreStateJSON struct { type BlockStoreStateJSON struct {
Height int64 Height int64 `json:"height"`
} }
// Save persists the blockStore state to the database as JSON. // Save persists the blockStore state to the database as JSON.
func (bsj BlockStoreStateJSON) Save(db dbm.DB) { func (bsj BlockStoreStateJSON) Save(db dbm.DB) {
bytes, err := json.Marshal(bsj) bytes, err := cdc.MarshalJSON(bsj)
if err != nil { if err != nil {
cmn.PanicSanity(cmn.Fmt("Could not marshal state bytes: %v", err)) cmn.PanicSanity(cmn.Fmt("Could not marshal state bytes: %v", err))
} }
@@ -260,9 +239,9 @@ func LoadBlockStoreStateJSON(db dbm.DB) BlockStoreStateJSON {
} }
} }
bsj := BlockStoreStateJSON{} bsj := BlockStoreStateJSON{}
err := json.Unmarshal(bytes, &bsj) err := cdc.UnmarshalJSON(bytes, &bsj)
if err != nil { if err != nil {
cmn.PanicCrisis(cmn.Fmt("Could not unmarshal bytes: %X", bytes)) panic(fmt.Sprintf("Could not unmarshal bytes: %X", bytes))
} }
return bsj return bsj
} }

View File

@@ -3,7 +3,6 @@ package blockchain
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"io/ioutil"
"runtime/debug" "runtime/debug"
"strings" "strings"
"testing" "testing"
@@ -11,9 +10,6 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
wire "github.com/tendermint/go-wire"
"github.com/tendermint/tmlibs/db" "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log" "github.com/tendermint/tmlibs/log"
@@ -35,7 +31,7 @@ func TestNewBlockStore(t *testing.T) {
db := db.NewMemDB() db := db.NewMemDB()
db.Set(blockStoreKey, []byte(`{"height": 10000}`)) db.Set(blockStoreKey, []byte(`{"height": 10000}`))
bs := NewBlockStore(db) bs := NewBlockStore(db)
assert.Equal(t, bs.Height(), int64(10000), "failed to properly parse blockstore") require.Equal(t, int64(10000), bs.Height(), "failed to properly parse blockstore")
panicCausers := []struct { panicCausers := []struct {
data []byte data []byte
@@ -61,38 +57,6 @@ func TestNewBlockStore(t *testing.T) {
assert.Equal(t, bs.Height(), int64(0), "expecting nil bytes to be unmarshaled alright") assert.Equal(t, bs.Height(), int64(0), "expecting nil bytes to be unmarshaled alright")
} }
func TestBlockStoreGetReader(t *testing.T) {
db := db.NewMemDB()
// Initial setup
db.Set([]byte("Foo"), []byte("Bar"))
db.Set([]byte("Foo1"), nil)
bs := NewBlockStore(db)
tests := [...]struct {
key []byte
want []byte
}{
0: {key: []byte("Foo"), want: []byte("Bar")},
1: {key: []byte("KnoxNonExistent"), want: nil},
2: {key: []byte("Foo1"), want: []byte{}},
}
for i, tt := range tests {
r := bs.GetReader(tt.key)
if r == nil {
assert.Nil(t, tt.want, "#%d: expected a non-nil reader", i)
continue
}
slurp, err := ioutil.ReadAll(r)
if err != nil {
t.Errorf("#%d: unexpected Read err: %v", i, err)
} else {
assert.Equal(t, slurp, tt.want, "#%d: mismatch", i)
}
}
}
func freshBlockStore() (*BlockStore, db.DB) { func freshBlockStore() (*BlockStore, db.DB) {
db := db.NewMemDB() db := db.NewMemDB()
return NewBlockStore(db), db return NewBlockStore(db), db
@@ -189,14 +153,14 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
parts: validPartSet, parts: validPartSet,
seenCommit: seenCommit1, seenCommit: seenCommit1,
corruptCommitInDB: true, // Corrupt the DB's commit entry corruptCommitInDB: true, // Corrupt the DB's commit entry
wantPanic: "rror reading commit", wantPanic: "Error reading block commit",
}, },
{ {
block: newBlock(&header1, commitAtH10), block: newBlock(&header1, commitAtH10),
parts: validPartSet, parts: validPartSet,
seenCommit: seenCommit1, seenCommit: seenCommit1,
wantPanic: "rror reading block", wantPanic: "Error reading block",
corruptBlockInDB: true, // Corrupt the DB's block entry corruptBlockInDB: true, // Corrupt the DB's block entry
}, },
@@ -215,7 +179,7 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
seenCommit: seenCommit1, seenCommit: seenCommit1,
corruptSeenCommitInDB: true, corruptSeenCommitInDB: true,
wantPanic: "rror reading commit", wantPanic: "Error reading block seen commit",
}, },
{ {
@@ -305,14 +269,6 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
} }
} }
func binarySerializeIt(v interface{}) []byte {
var n int
var err error
buf := new(bytes.Buffer)
wire.WriteBinary(v, buf, &n, &err)
return buf.Bytes()
}
func TestLoadBlockPart(t *testing.T) { func TestLoadBlockPart(t *testing.T) {
bs, db := freshBlockStore() bs, db := freshBlockStore()
height, index := int64(10), 1 height, index := int64(10), 1
@@ -334,7 +290,7 @@ func TestLoadBlockPart(t *testing.T) {
require.Contains(t, panicErr.Error(), "Error reading block part") require.Contains(t, panicErr.Error(), "Error reading block part")
// 3. A good block serialized and saved to the DB should be retrievable // 3. A good block serialized and saved to the DB should be retrievable
db.Set(calcBlockPartKey(height, index), binarySerializeIt(part1)) db.Set(calcBlockPartKey(height, index), cdc.MustMarshalBinaryBare(part1))
gotPart, _, panicErr := doFn(loadPart) gotPart, _, panicErr := doFn(loadPart)
require.Nil(t, panicErr, "an existent and proper block should not panic") require.Nil(t, panicErr, "an existent and proper block should not panic")
require.Nil(t, res, "a properly saved block should return a proper block") require.Nil(t, res, "a properly saved block should return a proper block")
@@ -364,11 +320,11 @@ func TestLoadBlockMeta(t *testing.T) {
// 3. A good blockMeta serialized and saved to the DB should be retrievable // 3. A good blockMeta serialized and saved to the DB should be retrievable
meta := &types.BlockMeta{} meta := &types.BlockMeta{}
db.Set(calcBlockMetaKey(height), binarySerializeIt(meta)) db.Set(calcBlockMetaKey(height), cdc.MustMarshalBinaryBare(meta))
gotMeta, _, panicErr := doFn(loadMeta) gotMeta, _, panicErr := doFn(loadMeta)
require.Nil(t, panicErr, "an existent and proper block should not panic") require.Nil(t, panicErr, "an existent and proper block should not panic")
require.Nil(t, res, "a properly saved blockMeta should return a proper blocMeta ") require.Nil(t, res, "a properly saved blockMeta should return a proper blocMeta ")
require.Equal(t, binarySerializeIt(meta), binarySerializeIt(gotMeta), require.Equal(t, cdc.MustMarshalBinaryBare(meta), cdc.MustMarshalBinaryBare(gotMeta),
"expecting successful retrieval of previously saved blockMeta") "expecting successful retrieval of previously saved blockMeta")
} }
@@ -385,6 +341,9 @@ func TestBlockFetchAtHeight(t *testing.T) {
require.Equal(t, bs.Height(), block.Header.Height, "expecting the new height to be changed") require.Equal(t, bs.Height(), block.Header.Height, "expecting the new height to be changed")
blockAtHeight := bs.LoadBlock(bs.Height()) blockAtHeight := bs.LoadBlock(bs.Height())
bz1 := cdc.MustMarshalBinaryBare(block)
bz2 := cdc.MustMarshalBinaryBare(blockAtHeight)
require.Equal(t, bz1, bz2)
require.Equal(t, block.Hash(), blockAtHeight.Hash(), require.Equal(t, block.Hash(), blockAtHeight.Hash(),
"expecting a successful load of the last saved block") "expecting a successful load of the last saved block")

13
blockchain/wire.go Normal file
View File

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

View File

@@ -1,35 +0,0 @@
---
machine:
environment:
MACH_PREFIX: tendermint-test-mach
DOCKER_VERSION: 1.10.0
DOCKER_MACHINE_VERSION: 0.9.0
GOPATH: "$HOME/.go_project"
PROJECT_PARENT_PATH: "$GOPATH/src/github.com/$CIRCLE_PROJECT_USERNAME"
PROJECT_PATH: "$PROJECT_PARENT_PATH/$CIRCLE_PROJECT_REPONAME"
PATH: "$HOME/.go_project/bin:${PATH}"
hosts:
localhost: 127.0.0.1
dependencies:
override:
- curl -sSL https://s3.amazonaws.com/circle-downloads/install-circleci-docker.sh | sudo bash -s -- $DOCKER_VERSION
- sudo start docker
- sudo curl -sSL -o /usr/bin/docker-machine "https://github.com/docker/machine/releases/download/v$DOCKER_MACHINE_VERSION/docker-machine-`uname -s`-`uname -m`"; sudo chmod 0755 /usr/bin/docker-machine
- mkdir -p "$PROJECT_PARENT_PATH"
- ln -sf "$HOME/$CIRCLE_PROJECT_REPONAME/" "$PROJECT_PATH"
post:
- go version
- docker version
- docker-machine version
test:
override:
- cd "$PROJECT_PATH" && set -o pipefail && make test_integrations 2>&1 | tee test_integrations.log:
timeout: 1800
post:
- cd "$PROJECT_PATH" && mv test_integrations.log "${CIRCLE_ARTIFACTS}"
- cd "$PROJECT_PATH" && bash <(curl -s https://codecov.io/bash) -f coverage.txt
- cd "$PROJECT_PATH" && mv coverage.txt "${CIRCLE_ARTIFACTS}"
- cd "$PROJECT_PATH" && cp test/logs/messages "${CIRCLE_ARTIFACTS}/docker.log"
- cd "${CIRCLE_ARTIFACTS}" && tar czf logs.tar.gz *.log

View File

@@ -0,0 +1,53 @@
package main
import (
"flag"
"os"
crypto "github.com/tendermint/go-crypto"
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/log"
priv_val "github.com/tendermint/tendermint/types/priv_validator"
)
func main() {
var (
addr = flag.String("addr", ":46659", "Address of client to connect to")
chainID = flag.String("chain-id", "mychain", "chain id")
privValPath = flag.String("priv", "", "priv val file path")
logger = log.NewTMLogger(
log.NewSyncWriter(os.Stdout),
).With("module", "priv_val")
)
flag.Parse()
logger.Info(
"Starting private validator",
"addr", *addr,
"chainID", *chainID,
"privPath", *privValPath,
)
privVal := priv_val.LoadFilePV(*privValPath)
rs := priv_val.NewRemoteSigner(
logger,
*chainID,
*addr,
privVal,
crypto.GenPrivKeyEd25519(),
)
err := rs.Start()
if err != nil {
panic(err)
}
cmn.TrapSignal(func() {
err := rs.Stop()
if err != nil {
panic(err)
}
})
}

View File

@@ -0,0 +1,32 @@
package commands
import (
"fmt"
"github.com/spf13/cobra"
"github.com/tendermint/tendermint/p2p"
cmn "github.com/tendermint/tmlibs/common"
)
// GenNodeKeyCmd allows the generation of a node key. It prints node's ID to
// the standard output.
var GenNodeKeyCmd = &cobra.Command{
Use: "gen_node_key",
Short: "Generate a node key for this node and print its ID",
RunE: genNodeKey,
}
func genNodeKey(cmd *cobra.Command, args []string) error {
nodeKeyFile := config.NodeKeyFile()
if cmn.FileExists(nodeKeyFile) {
return fmt.Errorf("node key at %s already exists", nodeKeyFile)
}
nodeKey, err := p2p.LoadOrGenNodeKey(nodeKeyFile)
if err != nil {
return err
}
fmt.Println(nodeKey.ID())
return nil
}

View File

@@ -1,12 +1,11 @@
package commands package commands
import ( import (
"encoding/json"
"fmt" "fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/tendermint/tendermint/types" pvm "github.com/tendermint/tendermint/types/priv_validator"
) )
// GenValidatorCmd allows the generation of a keypair for a // GenValidatorCmd allows the generation of a keypair for a
@@ -18,11 +17,11 @@ var GenValidatorCmd = &cobra.Command{
} }
func genValidator(cmd *cobra.Command, args []string) { func genValidator(cmd *cobra.Command, args []string) {
privValidator := types.GenPrivValidatorFS("") pv := pvm.GenFilePV("")
privValidatorJSONBytes, err := json.MarshalIndent(privValidator, "", "\t") jsbz, err := cdc.MarshalJSON(pv)
if err != nil { if err != nil {
panic(err) panic(err)
} }
fmt.Printf(`%v fmt.Printf(`%v
`, string(privValidatorJSONBytes)) `, string(jsbz))
} }

View File

@@ -3,7 +3,10 @@ package commands
import ( import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
pvm "github.com/tendermint/tendermint/types/priv_validator"
cmn "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
) )
@@ -11,20 +14,34 @@ import (
var InitFilesCmd = &cobra.Command{ var InitFilesCmd = &cobra.Command{
Use: "init", Use: "init",
Short: "Initialize Tendermint", Short: "Initialize Tendermint",
Run: initFiles, RunE: initFiles,
} }
func initFiles(cmd *cobra.Command, args []string) { func initFiles(cmd *cobra.Command, args []string) error {
return initFilesWithConfig(config)
}
func initFilesWithConfig(config *cfg.Config) error {
// private validator // private validator
privValFile := config.PrivValidatorFile() privValFile := config.PrivValidatorFile()
var privValidator *types.PrivValidatorFS var pv *pvm.FilePV
if cmn.FileExists(privValFile) { if cmn.FileExists(privValFile) {
privValidator = types.LoadPrivValidatorFS(privValFile) pv = pvm.LoadFilePV(privValFile)
logger.Info("Found private validator", "path", privValFile) logger.Info("Found private validator", "path", privValFile)
} else { } else {
privValidator = types.GenPrivValidatorFS(privValFile) pv = pvm.GenFilePV(privValFile)
privValidator.Save() pv.Save()
logger.Info("Genetated private validator", "path", privValFile) logger.Info("Generated private validator", "path", privValFile)
}
nodeKeyFile := config.NodeKeyFile()
if cmn.FileExists(nodeKeyFile) {
logger.Info("Found node key", "path", nodeKeyFile)
} else {
if _, err := p2p.LoadOrGenNodeKey(nodeKeyFile); err != nil {
return err
}
logger.Info("Generated node key", "path", nodeKeyFile)
} }
// genesis file // genesis file
@@ -36,13 +53,15 @@ func initFiles(cmd *cobra.Command, args []string) {
ChainID: cmn.Fmt("test-chain-%v", cmn.RandStr(6)), ChainID: cmn.Fmt("test-chain-%v", cmn.RandStr(6)),
} }
genDoc.Validators = []types.GenesisValidator{{ genDoc.Validators = []types.GenesisValidator{{
PubKey: privValidator.GetPubKey(), PubKey: pv.GetPubKey(),
Power: 10, Power: 10,
}} }}
if err := genDoc.SaveAs(genFile); err != nil { if err := genDoc.SaveAs(genFile); err != nil {
panic(err) return err
} }
logger.Info("Genetated genesis file", "path", genFile) logger.Info("Generated genesis file", "path", genFile)
} }
return nil
} }

View File

@@ -1,6 +1,9 @@
package commands package commands
import ( import (
"fmt"
"net/url"
"github.com/spf13/cobra" "github.com/spf13/cobra"
cmn "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
@@ -31,13 +34,37 @@ var (
) )
func init() { func init() {
LiteCmd.Flags().StringVar(&listenAddr, "laddr", ":8888", "Serve the proxy on the given port") LiteCmd.Flags().StringVar(&listenAddr, "laddr", "tcp://localhost:8888", "Serve the proxy on the given address")
LiteCmd.Flags().StringVar(&nodeAddr, "node", "localhost:46657", "Connect to a Tendermint node at this address") LiteCmd.Flags().StringVar(&nodeAddr, "node", "tcp://localhost:46657", "Connect to a Tendermint node at this address")
LiteCmd.Flags().StringVar(&chainID, "chain-id", "tendermint", "Specify the Tendermint chain ID") LiteCmd.Flags().StringVar(&chainID, "chain-id", "tendermint", "Specify the Tendermint chain ID")
LiteCmd.Flags().StringVar(&home, "home-dir", ".tendermint-lite", "Specify the home directory") LiteCmd.Flags().StringVar(&home, "home-dir", ".tendermint-lite", "Specify the home directory")
} }
func ensureAddrHasSchemeOrDefaultToTCP(addr string) (string, error) {
u, err := url.Parse(addr)
if err != nil {
return "", err
}
switch u.Scheme {
case "tcp", "unix":
case "":
u.Scheme = "tcp"
default:
return "", fmt.Errorf("unknown scheme %q, use either tcp or unix", u.Scheme)
}
return u.String(), nil
}
func runProxy(cmd *cobra.Command, args []string) error { func runProxy(cmd *cobra.Command, args []string) error {
nodeAddr, err := ensureAddrHasSchemeOrDefaultToTCP(nodeAddr)
if err != nil {
return err
}
listenAddr, err := ensureAddrHasSchemeOrDefaultToTCP(listenAddr)
if err != nil {
return err
}
// First, connect a client // First, connect a client
node := rpcclient.NewHTTP(nodeAddr, "/websocket") node := rpcclient.NewHTTP(nodeAddr, "/websocket")

View File

@@ -1,7 +1,6 @@
package commands package commands
import ( import (
"encoding/json"
"fmt" "fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -22,7 +21,7 @@ func probeUpnp(cmd *cobra.Command, args []string) error {
fmt.Println("Probe failed: ", err) fmt.Println("Probe failed: ", err)
} else { } else {
fmt.Println("Probe success!") fmt.Println("Probe success!")
jsonBytes, err := json.Marshal(capabilities) jsonBytes, err := cdc.MarshalJSON(capabilities)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -5,7 +5,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/tendermint/tendermint/types" pvm "github.com/tendermint/tendermint/types/priv_validator"
"github.com/tendermint/tmlibs/log" "github.com/tendermint/tmlibs/log"
) )
@@ -27,7 +27,7 @@ var ResetPrivValidatorCmd = &cobra.Command{
// ResetAll removes the privValidator files. // ResetAll removes the privValidator files.
// Exported so other CLI tools can use it. // Exported so other CLI tools can use it.
func ResetAll(dbDir, privValFile string, logger log.Logger) { func ResetAll(dbDir, privValFile string, logger log.Logger) {
resetPrivValidatorFS(privValFile, logger) resetFilePV(privValFile, logger)
if err := os.RemoveAll(dbDir); err != nil { if err := os.RemoveAll(dbDir); err != nil {
logger.Error("Error removing directory", "err", err) logger.Error("Error removing directory", "err", err)
return return
@@ -44,18 +44,18 @@ func resetAll(cmd *cobra.Command, args []string) {
// XXX: this is totally unsafe. // XXX: this is totally unsafe.
// it's only suitable for testnets. // it's only suitable for testnets.
func resetPrivValidator(cmd *cobra.Command, args []string) { func resetPrivValidator(cmd *cobra.Command, args []string) {
resetPrivValidatorFS(config.PrivValidatorFile(), logger) resetFilePV(config.PrivValidatorFile(), logger)
} }
func resetPrivValidatorFS(privValFile string, logger log.Logger) { func resetFilePV(privValFile string, logger log.Logger) {
// Get PrivValidator // Get PrivValidator
if _, err := os.Stat(privValFile); err == nil { if _, err := os.Stat(privValFile); err == nil {
privValidator := types.LoadPrivValidatorFS(privValFile) pv := pvm.LoadFilePV(privValFile)
privValidator.Reset() pv.Reset()
logger.Info("Reset PrivValidator", "file", privValFile) logger.Info("Reset PrivValidator", "file", privValFile)
} else { } else {
privValidator := types.GenPrivValidatorFS(privValFile) pv := pvm.GenFilePV(privValFile)
privValidator.Save() pv.Save()
logger.Info("Generated PrivValidator", "file", privValFile) logger.Info("Generated PrivValidator", "file", privValFile)
} }
} }

View File

@@ -14,11 +14,14 @@ func AddNodeFlags(cmd *cobra.Command) {
// bind flags // bind flags
cmd.Flags().String("moniker", config.Moniker, "Node Name") cmd.Flags().String("moniker", config.Moniker, "Node Name")
// priv val flags
cmd.Flags().String("priv_validator_laddr", config.PrivValidatorListenAddr, "Socket address to listen on for connections from external priv_validator process")
// node flags // node flags
cmd.Flags().Bool("fast_sync", config.FastSync, "Fast blockchain syncing") cmd.Flags().Bool("fast_sync", config.FastSync, "Fast blockchain syncing")
// abci flags // abci flags
cmd.Flags().String("proxy_app", config.ProxyApp, "Proxy app address, or 'nilapp' or 'dummy' for local testing.") cmd.Flags().String("proxy_app", config.ProxyApp, "Proxy app address, or 'nilapp' or 'kvstore' for local testing.")
cmd.Flags().String("abci", config.ABCI, "Specify abci transport (socket | grpc)") cmd.Flags().String("abci", config.ABCI, "Specify abci transport (socket | grpc)")
// rpc flags // rpc flags
@@ -28,18 +31,19 @@ func AddNodeFlags(cmd *cobra.Command) {
// p2p flags // p2p flags
cmd.Flags().String("p2p.laddr", config.P2P.ListenAddress, "Node listen address. (0.0.0.0:0 means any interface, any port)") cmd.Flags().String("p2p.laddr", config.P2P.ListenAddress, "Node listen address. (0.0.0.0:0 means any interface, any port)")
cmd.Flags().String("p2p.seeds", config.P2P.Seeds, "Comma delimited host:port seed nodes") cmd.Flags().String("p2p.seeds", config.P2P.Seeds, "Comma-delimited ID@host:port seed nodes")
cmd.Flags().String("p2p.persistent_peers", config.P2P.PersistentPeers, "Comma delimited host:port persistent peers") cmd.Flags().String("p2p.persistent_peers", config.P2P.PersistentPeers, "Comma-delimited ID@host:port persistent peers")
cmd.Flags().Bool("p2p.skip_upnp", config.P2P.SkipUPNP, "Skip UPNP configuration") cmd.Flags().Bool("p2p.skip_upnp", config.P2P.SkipUPNP, "Skip UPNP configuration")
cmd.Flags().Bool("p2p.pex", config.P2P.PexReactor, "Enable/disable Peer-Exchange") cmd.Flags().Bool("p2p.pex", config.P2P.PexReactor, "Enable/disable Peer-Exchange")
cmd.Flags().Bool("p2p.seed_mode", config.P2P.SeedMode, "Enable/disable seed mode") cmd.Flags().Bool("p2p.seed_mode", config.P2P.SeedMode, "Enable/disable seed mode")
cmd.Flags().String("p2p.private_peer_ids", config.P2P.PrivatePeerIDs, "Comma-delimited private peer IDs")
// consensus flags // consensus flags
cmd.Flags().Bool("consensus.create_empty_blocks", config.Consensus.CreateEmptyBlocks, "Set this to false to only produce blocks when there are txs or when the AppHash changes") cmd.Flags().Bool("consensus.create_empty_blocks", config.Consensus.CreateEmptyBlocks, "Set this to false to only produce blocks when there are txs or when the AppHash changes")
} }
// NewRunNodeCmd returns the command that allows the CLI to start a // NewRunNodeCmd returns the command that allows the CLI to start a node.
// node. It can be used with a custom PrivValidator and in-process ABCI application. // It can be used with a custom PrivValidator and in-process ABCI application.
func NewRunNodeCmd(nodeProvider nm.NodeProvider) *cobra.Command { func NewRunNodeCmd(nodeProvider nm.NodeProvider) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "node", Use: "node",
@@ -53,9 +57,8 @@ func NewRunNodeCmd(nodeProvider nm.NodeProvider) *cobra.Command {
if err := n.Start(); err != nil { if err := n.Start(); err != nil {
return fmt.Errorf("Failed to start node: %v", err) return fmt.Errorf("Failed to start node: %v", err)
} else {
logger.Info("Started node", "nodeInfo", n.Switch().NodeInfo())
} }
logger.Info("Started node", "nodeInfo", n.Switch().NodeInfo())
// Trap signal, run forever. // Trap signal, run forever.
n.RunForever() n.RunForever()

View File

@@ -0,0 +1,27 @@
package commands
import (
"fmt"
"github.com/spf13/cobra"
"github.com/tendermint/tendermint/p2p"
)
// ShowNodeIDCmd dumps node's ID to the standard output.
var ShowNodeIDCmd = &cobra.Command{
Use: "show_node_id",
Short: "Show this node's ID",
RunE: showNodeID,
}
func showNodeID(cmd *cobra.Command, args []string) error {
nodeKey, err := p2p.LoadNodeKey(config.NodeKeyFile())
if err != nil {
return err
}
fmt.Println(nodeKey.ID())
return nil
}

View File

@@ -5,8 +5,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/tendermint/go-wire/data" privval "github.com/tendermint/tendermint/types/priv_validator"
"github.com/tendermint/tendermint/types"
) )
// ShowValidatorCmd adds capabilities for showing the validator info. // ShowValidatorCmd adds capabilities for showing the validator info.
@@ -17,7 +16,7 @@ var ShowValidatorCmd = &cobra.Command{
} }
func showValidator(cmd *cobra.Command, args []string) { func showValidator(cmd *cobra.Command, args []string) {
privValidator := types.LoadOrGenPrivValidatorFS(config.PrivValidatorFile()) privValidator := privval.LoadOrGenFilePV(config.PrivValidatorFile())
pubKeyJSONBytes, _ := data.ToJSON(privValidator.PubKey) pubKeyJSONBytes, _ := cdc.MarshalJSON(privValidator.GetPubKey())
fmt.Println(string(pubKeyJSONBytes)) fmt.Println(string(pubKeyJSONBytes))
} }

View File

@@ -2,59 +2,103 @@ package commands
import ( import (
"fmt" "fmt"
"net"
"os"
"path/filepath" "path/filepath"
"strings"
"time" "time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
cfg "github.com/tendermint/tendermint/config" cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
pvm "github.com/tendermint/tendermint/types/priv_validator"
cmn "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
) )
//flags
var ( var (
nValidators int nValidators int
dataDir string nNonValidators int
outputDir string
nodeDirPrefix string
populatePersistentPeers bool
hostnamePrefix string
startingIPAddress string
p2pPort int
)
const (
nodeDirPerm = 0755
) )
func init() { func init() {
TestnetFilesCmd.Flags().IntVar(&nValidators, "n", 4, TestnetFilesCmd.Flags().IntVar(&nValidators, "v", 4,
"Number of validators to initialize the testnet with") "Number of validators to initialize the testnet with")
TestnetFilesCmd.Flags().StringVar(&dataDir, "dir", "mytestnet", TestnetFilesCmd.Flags().IntVar(&nNonValidators, "n", 0,
"Number of non-validators to initialize the testnet with")
TestnetFilesCmd.Flags().StringVar(&outputDir, "o", "./mytestnet",
"Directory to store initialization data for the testnet") "Directory to store initialization data for the testnet")
TestnetFilesCmd.Flags().StringVar(&nodeDirPrefix, "node-dir-prefix", "node",
"Prefix the directory name for each node with (node results in node0, node1, ...)")
TestnetFilesCmd.Flags().BoolVar(&populatePersistentPeers, "populate-persistent-peers", true,
"Update config of each node with the list of persistent peers build using either hostname-prefix or starting-ip-address")
TestnetFilesCmd.Flags().StringVar(&hostnamePrefix, "hostname-prefix", "node",
"Hostname prefix (node results in persistent peers list ID0@node0:46656, ID1@node1:46656, ...)")
TestnetFilesCmd.Flags().StringVar(&startingIPAddress, "starting-ip-address", "",
"Starting IP address (192.168.0.1 results in persistent peers list ID0@192.168.0.1:46656, ID1@192.168.0.2:46656, ...)")
TestnetFilesCmd.Flags().IntVar(&p2pPort, "p2p-port", 46656,
"P2P Port")
} }
// TestnetFilesCmd allows initialisation of files for a // TestnetFilesCmd allows initialisation of files for a Tendermint testnet.
// Tendermint testnet.
var TestnetFilesCmd = &cobra.Command{ var TestnetFilesCmd = &cobra.Command{
Use: "testnet", Use: "testnet",
Short: "Initialize files for a Tendermint testnet", Short: "Initialize files for a Tendermint testnet",
Run: testnetFiles, RunE: testnetFiles,
} }
func testnetFiles(cmd *cobra.Command, args []string) { func testnetFiles(cmd *cobra.Command, args []string) error {
config := cfg.DefaultConfig()
genVals := make([]types.GenesisValidator, nValidators) genVals := make([]types.GenesisValidator, nValidators)
defaultConfig := cfg.DefaultBaseConfig()
// Initialize core dir and priv_validator.json's
for i := 0; i < nValidators; i++ { for i := 0; i < nValidators; i++ {
mach := cmn.Fmt("mach%d", i) nodeDirName := cmn.Fmt("%s%d", nodeDirPrefix, i)
err := initMachCoreDirectory(dataDir, mach) nodeDir := filepath.Join(outputDir, nodeDirName)
config.SetRoot(nodeDir)
err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm)
if err != nil { if err != nil {
cmn.Exit(err.Error()) _ = os.RemoveAll(outputDir)
return err
} }
// Read priv_validator.json to populate vals
privValFile := filepath.Join(dataDir, mach, defaultConfig.PrivValidator) initFilesWithConfig(config)
privVal := types.LoadPrivValidatorFS(privValFile)
pvFile := filepath.Join(nodeDir, config.BaseConfig.PrivValidator)
pv := pvm.LoadFilePV(pvFile)
genVals[i] = types.GenesisValidator{ genVals[i] = types.GenesisValidator{
PubKey: privVal.GetPubKey(), PubKey: pv.GetPubKey(),
Power: 1, Power: 1,
Name: mach, Name: nodeDirName,
} }
} }
for i := 0; i < nNonValidators; i++ {
nodeDir := filepath.Join(outputDir, cmn.Fmt("%s%d", nodeDirPrefix, i+nValidators))
config.SetRoot(nodeDir)
err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm)
if err != nil {
_ = os.RemoveAll(outputDir)
return err
}
initFilesWithConfig(config)
}
// Generate genesis doc from generated validators // Generate genesis doc from generated validators
genDoc := &types.GenesisDoc{ genDoc := &types.GenesisDoc{
GenesisTime: time.Now(), GenesisTime: time.Now(),
@@ -63,35 +107,65 @@ func testnetFiles(cmd *cobra.Command, args []string) {
} }
// Write genesis file. // Write genesis file.
for i := 0; i < nValidators; i++ { for i := 0; i < nValidators+nNonValidators; i++ {
mach := cmn.Fmt("mach%d", i) nodeDir := filepath.Join(outputDir, cmn.Fmt("%s%d", nodeDirPrefix, i))
if err := genDoc.SaveAs(filepath.Join(dataDir, mach, defaultConfig.Genesis)); err != nil { if err := genDoc.SaveAs(filepath.Join(nodeDir, config.BaseConfig.Genesis)); err != nil {
panic(err) _ = os.RemoveAll(outputDir)
return err
} }
} }
fmt.Println(cmn.Fmt("Successfully initialized %v node directories", nValidators)) if populatePersistentPeers {
err := populatePersistentPeersInConfigAndWriteIt(config)
if err != nil {
_ = os.RemoveAll(outputDir)
return err
}
} }
// Initialize per-machine core directory fmt.Printf("Successfully initialized %v node directories\n", nValidators+nNonValidators)
func initMachCoreDirectory(base, mach string) error { return nil
dir := filepath.Join(base, mach) }
err := cmn.EnsureDir(dir, 0777)
func hostnameOrIP(i int) string {
if startingIPAddress != "" {
ip := net.ParseIP(startingIPAddress)
ip = ip.To4()
if ip == nil {
fmt.Printf("%v: non ipv4 address\n", startingIPAddress)
os.Exit(1)
}
for j := 0; j < i; j++ {
ip[3]++
}
return ip.String()
}
return fmt.Sprintf("%s%d", hostnamePrefix, i)
}
func populatePersistentPeersInConfigAndWriteIt(config *cfg.Config) error {
persistentPeers := make([]string, nValidators+nNonValidators)
for i := 0; i < nValidators+nNonValidators; i++ {
nodeDir := filepath.Join(outputDir, cmn.Fmt("%s%d", nodeDirPrefix, i))
config.SetRoot(nodeDir)
nodeKey, err := p2p.LoadNodeKey(config.NodeKeyFile())
if err != nil { if err != nil {
return err return err
} }
persistentPeers[i] = p2p.IDAddressString(nodeKey.ID(), fmt.Sprintf("%s:%d", hostnameOrIP(i), p2pPort))
}
persistentPeersList := strings.Join(persistentPeers, ",")
for i := 0; i < nValidators+nNonValidators; i++ {
nodeDir := filepath.Join(outputDir, cmn.Fmt("%s%d", nodeDirPrefix, i))
config.SetRoot(nodeDir)
config.P2P.PersistentPeers = persistentPeersList
// overwrite default config
cfg.WriteConfigFile(filepath.Join(nodeDir, "config", "config.toml"), config)
}
// Create priv_validator.json file if not present
defaultConfig := cfg.DefaultBaseConfig()
ensurePrivValidator(filepath.Join(dir, defaultConfig.PrivValidator))
return nil return nil
}
func ensurePrivValidator(file string) {
if cmn.FileExists(file) {
return
}
privValidator := types.GenPrivValidatorFS(file)
privValidator.Save()
} }

View File

@@ -0,0 +1,12 @@
package commands
import (
amino "github.com/tendermint/go-amino"
"github.com/tendermint/go-crypto"
)
var cdc = amino.NewCodec()
func init() {
crypto.RegisterAmino(cdc)
}

View File

@@ -24,6 +24,8 @@ func main() {
cmd.ResetPrivValidatorCmd, cmd.ResetPrivValidatorCmd,
cmd.ShowValidatorCmd, cmd.ShowValidatorCmd,
cmd.TestnetFilesCmd, cmd.TestnetFilesCmd,
cmd.ShowNodeIDCmd,
cmd.GenNodeKeyCmd,
cmd.VersionCmd) cmd.VersionCmd)
// NOTE: // NOTE:

23
codecov.yml Normal file
View File

@@ -0,0 +1,23 @@
coverage:
precision: 2
round: down
range: "70...100"
status:
project:
default:
threshold: 1%
patch: on
changes: off
comment:
layout: "diff, files"
behavior: default
require_changes: no
require_base: no
require_head: yes
ignore:
- "docs"
- "DOCKER"
- "scripts"

View File

@@ -20,6 +20,7 @@ var (
defaultConfigFileName = "config.toml" defaultConfigFileName = "config.toml"
defaultGenesisJSONName = "genesis.json" defaultGenesisJSONName = "genesis.json"
defaultPrivValName = "priv_validator.json" defaultPrivValName = "priv_validator.json"
defaultNodeKeyName = "node_key.json" defaultNodeKeyName = "node_key.json"
defaultAddrBookName = "addrbook.json" defaultAddrBookName = "addrbook.json"
@@ -103,6 +104,10 @@ type BaseConfig struct {
// A custom human readable name for this node // A custom human readable name for this node
Moniker string `mapstructure:"moniker"` Moniker string `mapstructure:"moniker"`
// TCP or UNIX socket address for Tendermint to listen on for
// connections from an external PrivValidator process
PrivValidatorListenAddr string `mapstructure:"priv_validator_laddr"`
// TCP or UNIX socket address of the ABCI application, // TCP or UNIX socket address of the ABCI application,
// or the name of an ABCI application compiled in with the Tendermint binary // or the name of an ABCI application compiled in with the Tendermint binary
ProxyApp string `mapstructure:"proxy_app"` ProxyApp string `mapstructure:"proxy_app"`
@@ -132,10 +137,6 @@ type BaseConfig struct {
DBPath string `mapstructure:"db_dir"` DBPath string `mapstructure:"db_dir"`
} }
func (c BaseConfig) ChainID() string {
return c.chainID
}
// DefaultBaseConfig returns a default base configuration for a Tendermint node // DefaultBaseConfig returns a default base configuration for a Tendermint node
func DefaultBaseConfig() BaseConfig { func DefaultBaseConfig() BaseConfig {
return BaseConfig{ return BaseConfig{
@@ -156,32 +157,36 @@ func DefaultBaseConfig() BaseConfig {
// TestBaseConfig returns a base configuration for testing a Tendermint node // TestBaseConfig returns a base configuration for testing a Tendermint node
func TestBaseConfig() BaseConfig { func TestBaseConfig() BaseConfig {
conf := DefaultBaseConfig() cfg := DefaultBaseConfig()
conf.chainID = "tendermint_test" cfg.chainID = "tendermint_test"
conf.ProxyApp = "dummy" cfg.ProxyApp = "kvstore"
conf.FastSync = false cfg.FastSync = false
conf.DBBackend = "memdb" cfg.DBBackend = "memdb"
return conf return cfg
}
func (cfg BaseConfig) ChainID() string {
return cfg.chainID
} }
// GenesisFile returns the full path to the genesis.json file // GenesisFile returns the full path to the genesis.json file
func (b BaseConfig) GenesisFile() string { func (cfg BaseConfig) GenesisFile() string {
return rootify(b.Genesis, b.RootDir) return rootify(cfg.Genesis, cfg.RootDir)
} }
// PrivValidatorFile returns the full path to the priv_validator.json file // PrivValidatorFile returns the full path to the priv_validator.json file
func (b BaseConfig) PrivValidatorFile() string { func (cfg BaseConfig) PrivValidatorFile() string {
return rootify(b.PrivValidator, b.RootDir) return rootify(cfg.PrivValidator, cfg.RootDir)
} }
// NodeKeyFile returns the full path to the node_key.json file // NodeKeyFile returns the full path to the node_key.json file
func (b BaseConfig) NodeKeyFile() string { func (cfg BaseConfig) NodeKeyFile() string {
return rootify(b.NodeKey, b.RootDir) return rootify(cfg.NodeKey, cfg.RootDir)
} }
// DBDir returns the full path to the database directory // DBDir returns the full path to the database directory
func (b BaseConfig) DBDir() string { func (cfg BaseConfig) DBDir() string {
return rootify(b.DBPath, b.RootDir) return rootify(cfg.DBPath, cfg.RootDir)
} }
// DefaultLogLevel returns a default log level of "error" // DefaultLogLevel returns a default log level of "error"
@@ -224,11 +229,11 @@ func DefaultRPCConfig() *RPCConfig {
// TestRPCConfig returns a configuration for testing the RPC server // TestRPCConfig returns a configuration for testing the RPC server
func TestRPCConfig() *RPCConfig { func TestRPCConfig() *RPCConfig {
conf := DefaultRPCConfig() cfg := DefaultRPCConfig()
conf.ListenAddress = "tcp://0.0.0.0:36657" cfg.ListenAddress = "tcp://0.0.0.0:36657"
conf.GRPCListenAddress = "tcp://0.0.0.0:36658" cfg.GRPCListenAddress = "tcp://0.0.0.0:36658"
conf.Unsafe = true cfg.Unsafe = true
return conf return cfg
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@@ -245,8 +250,8 @@ type P2PConfig struct {
// We only use these if we cant connect to peers in the addrbook // We only use these if we cant connect to peers in the addrbook
Seeds string `mapstructure:"seeds"` Seeds string `mapstructure:"seeds"`
// Comma separated list of persistent peers to connect to // Comma separated list of nodes to keep persistent connections to
// We always connect to these // Do not add private peers to this list if you don't want them advertised
PersistentPeers string `mapstructure:"persistent_peers"` PersistentPeers string `mapstructure:"persistent_peers"`
// Skip UPNP port forwarding // Skip UPNP port forwarding
@@ -265,7 +270,7 @@ type P2PConfig struct {
FlushThrottleTimeout int `mapstructure:"flush_throttle_timeout"` FlushThrottleTimeout int `mapstructure:"flush_throttle_timeout"`
// Maximum size of a message packet payload, in bytes // Maximum size of a message packet payload, in bytes
MaxMsgPacketPayloadSize int `mapstructure:"max_msg_packet_payload_size"` MaxPacketMsgPayloadSize int `mapstructure:"max_packet_msg_payload_size"`
// Rate at which packets can be sent, in bytes/second // Rate at which packets can be sent, in bytes/second
SendRate int64 `mapstructure:"send_rate"` SendRate int64 `mapstructure:"send_rate"`
@@ -281,6 +286,12 @@ type P2PConfig struct {
// //
// Does not work if the peer-exchange reactor is disabled. // Does not work if the peer-exchange reactor is disabled.
SeedMode bool `mapstructure:"seed_mode"` SeedMode bool `mapstructure:"seed_mode"`
// Authenticated encryption
AuthEnc bool `mapstructure:"auth_enc"`
// Comma separated list of peer IDs to keep private (will not be gossiped to other peers)
PrivatePeerIDs string `mapstructure:"private_peer_ids"`
} }
// DefaultP2PConfig returns a default configuration for the peer-to-peer layer // DefaultP2PConfig returns a default configuration for the peer-to-peer layer
@@ -291,26 +302,27 @@ func DefaultP2PConfig() *P2PConfig {
AddrBookStrict: true, AddrBookStrict: true,
MaxNumPeers: 50, MaxNumPeers: 50,
FlushThrottleTimeout: 100, FlushThrottleTimeout: 100,
MaxMsgPacketPayloadSize: 1024, // 1 kB MaxPacketMsgPayloadSize: 1024, // 1 kB
SendRate: 512000, // 500 kB/s SendRate: 512000, // 500 kB/s
RecvRate: 512000, // 500 kB/s RecvRate: 512000, // 500 kB/s
PexReactor: true, PexReactor: true,
SeedMode: false, SeedMode: false,
AuthEnc: true,
} }
} }
// TestP2PConfig returns a configuration for testing the peer-to-peer layer // TestP2PConfig returns a configuration for testing the peer-to-peer layer
func TestP2PConfig() *P2PConfig { func TestP2PConfig() *P2PConfig {
conf := DefaultP2PConfig() cfg := DefaultP2PConfig()
conf.ListenAddress = "tcp://0.0.0.0:36656" cfg.ListenAddress = "tcp://0.0.0.0:36656"
conf.SkipUPNP = true cfg.SkipUPNP = true
conf.FlushThrottleTimeout = 10 cfg.FlushThrottleTimeout = 10
return conf return cfg
} }
// AddrBookFile returns the full path to the address book // AddrBookFile returns the full path to the address book
func (p *P2PConfig) AddrBookFile() string { func (cfg *P2PConfig) AddrBookFile() string {
return rootify(p.AddrBook, p.RootDir) return rootify(cfg.AddrBook, cfg.RootDir)
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@@ -339,14 +351,14 @@ func DefaultMempoolConfig() *MempoolConfig {
// TestMempoolConfig returns a configuration for testing the Tendermint mempool // TestMempoolConfig returns a configuration for testing the Tendermint mempool
func TestMempoolConfig() *MempoolConfig { func TestMempoolConfig() *MempoolConfig {
config := DefaultMempoolConfig() cfg := DefaultMempoolConfig()
config.CacheSize = 1000 cfg.CacheSize = 1000
return config return cfg
} }
// WalDir returns the full path to the mempool's write-ahead log // WalDir returns the full path to the mempool's write-ahead log
func (m *MempoolConfig) WalDir() string { func (cfg *MempoolConfig) WalDir() string {
return rootify(m.WalPath, m.RootDir) return rootify(cfg.WalPath, cfg.RootDir)
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@@ -385,6 +397,44 @@ type ConsensusConfig struct {
PeerQueryMaj23SleepDuration int `mapstructure:"peer_query_maj23_sleep_duration"` PeerQueryMaj23SleepDuration int `mapstructure:"peer_query_maj23_sleep_duration"`
} }
// DefaultConsensusConfig returns a default configuration for the consensus service
func DefaultConsensusConfig() *ConsensusConfig {
return &ConsensusConfig{
WalPath: filepath.Join(defaultDataDir, "cs.wal", "wal"),
WalLight: false,
TimeoutPropose: 3000,
TimeoutProposeDelta: 500,
TimeoutPrevote: 1000,
TimeoutPrevoteDelta: 500,
TimeoutPrecommit: 1000,
TimeoutPrecommitDelta: 500,
TimeoutCommit: 1000,
SkipTimeoutCommit: false,
MaxBlockSizeTxs: 10000,
MaxBlockSizeBytes: 1, // TODO
CreateEmptyBlocks: true,
CreateEmptyBlocksInterval: 0,
PeerGossipSleepDuration: 100,
PeerQueryMaj23SleepDuration: 2000,
}
}
// TestConsensusConfig returns a configuration for testing the consensus service
func TestConsensusConfig() *ConsensusConfig {
cfg := DefaultConsensusConfig()
cfg.TimeoutPropose = 100
cfg.TimeoutProposeDelta = 1
cfg.TimeoutPrevote = 10
cfg.TimeoutPrevoteDelta = 1
cfg.TimeoutPrecommit = 10
cfg.TimeoutPrecommitDelta = 1
cfg.TimeoutCommit = 10
cfg.SkipTimeoutCommit = true
cfg.PeerGossipSleepDuration = 5
cfg.PeerQueryMaj23SleepDuration = 250
return cfg
}
// WaitForTxs returns true if the consensus should wait for transactions before entering the propose step // WaitForTxs returns true if the consensus should wait for transactions before entering the propose step
func (cfg *ConsensusConfig) WaitForTxs() bool { func (cfg *ConsensusConfig) WaitForTxs() bool {
return !cfg.CreateEmptyBlocks || cfg.CreateEmptyBlocksInterval > 0 return !cfg.CreateEmptyBlocks || cfg.CreateEmptyBlocksInterval > 0
@@ -425,55 +475,17 @@ func (cfg *ConsensusConfig) PeerQueryMaj23Sleep() time.Duration {
return time.Duration(cfg.PeerQueryMaj23SleepDuration) * time.Millisecond return time.Duration(cfg.PeerQueryMaj23SleepDuration) * time.Millisecond
} }
// DefaultConsensusConfig returns a default configuration for the consensus service
func DefaultConsensusConfig() *ConsensusConfig {
return &ConsensusConfig{
WalPath: filepath.Join(defaultDataDir, "cs.wal", "wal"),
WalLight: false,
TimeoutPropose: 3000,
TimeoutProposeDelta: 500,
TimeoutPrevote: 1000,
TimeoutPrevoteDelta: 500,
TimeoutPrecommit: 1000,
TimeoutPrecommitDelta: 500,
TimeoutCommit: 1000,
SkipTimeoutCommit: false,
MaxBlockSizeTxs: 10000,
MaxBlockSizeBytes: 1, // TODO
CreateEmptyBlocks: true,
CreateEmptyBlocksInterval: 0,
PeerGossipSleepDuration: 100,
PeerQueryMaj23SleepDuration: 2000,
}
}
// TestConsensusConfig returns a configuration for testing the consensus service
func TestConsensusConfig() *ConsensusConfig {
config := DefaultConsensusConfig()
config.TimeoutPropose = 100
config.TimeoutProposeDelta = 1
config.TimeoutPrevote = 10
config.TimeoutPrevoteDelta = 1
config.TimeoutPrecommit = 10
config.TimeoutPrecommitDelta = 1
config.TimeoutCommit = 10
config.SkipTimeoutCommit = true
config.PeerGossipSleepDuration = 5
config.PeerQueryMaj23SleepDuration = 250
return config
}
// WalFile returns the full path to the write-ahead log file // WalFile returns the full path to the write-ahead log file
func (c *ConsensusConfig) WalFile() string { func (cfg *ConsensusConfig) WalFile() string {
if c.walFile != "" { if cfg.walFile != "" {
return c.walFile return cfg.walFile
} }
return rootify(c.WalPath, c.RootDir) return rootify(cfg.WalPath, cfg.RootDir)
} }
// SetWalFile sets the path to the write-ahead log file // SetWalFile sets the path to the write-ahead log file
func (c *ConsensusConfig) SetWalFile(walFile string) { func (cfg *ConsensusConfig) SetWalFile(walFile string) {
c.walFile = walFile cfg.walFile = walFile
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@@ -37,16 +37,21 @@ func EnsureRoot(rootDir string) {
// Write default config file if missing. // Write default config file if missing.
if !cmn.FileExists(configFilePath) { if !cmn.FileExists(configFilePath) {
writeConfigFile(configFilePath) writeDefaultCondigFile(configFilePath)
} }
} }
// XXX: this func should probably be called by cmd/tendermint/commands/init.go // XXX: this func should probably be called by cmd/tendermint/commands/init.go
// alongside the writing of the genesis.json and priv_validator.json // alongside the writing of the genesis.json and priv_validator.json
func writeConfigFile(configFilePath string) { func writeDefaultCondigFile(configFilePath string) {
WriteConfigFile(configFilePath, DefaultConfig())
}
// WriteConfigFile renders config using the template and writes it to configFilePath.
func WriteConfigFile(configFilePath string, config *Config) {
var buffer bytes.Buffer var buffer bytes.Buffer
if err := configTemplate.Execute(&buffer, DefaultConfig()); err != nil { if err := configTemplate.Execute(&buffer, config); err != nil {
panic(err) panic(err)
} }
@@ -124,10 +129,11 @@ unsafe = {{ .RPC.Unsafe }}
laddr = "{{ .P2P.ListenAddress }}" laddr = "{{ .P2P.ListenAddress }}"
# Comma separated list of seed nodes to connect to # Comma separated list of seed nodes to connect to
seeds = "" seeds = "{{ .P2P.Seeds }}"
# Comma separated list of nodes to keep persistent connections to # Comma separated list of nodes to keep persistent connections to
persistent_peers = "" # Do not add private peers to this list if you don't want them advertised
persistent_peers = "{{ .P2P.PersistentPeers }}"
# Path to address book # Path to address book
addr_book_file = "{{ .P2P.AddrBook }}" addr_book_file = "{{ .P2P.AddrBook }}"
@@ -142,7 +148,7 @@ flush_throttle_timeout = {{ .P2P.FlushThrottleTimeout }}
max_num_peers = {{ .P2P.MaxNumPeers }} max_num_peers = {{ .P2P.MaxNumPeers }}
# Maximum size of a message packet payload, in bytes # Maximum size of a message packet payload, in bytes
max_msg_packet_payload_size = {{ .P2P.MaxMsgPacketPayloadSize }} max_packet_msg_payload_size = {{ .P2P.MaxPacketMsgPayloadSize }}
# Rate at which packets can be sent, in bytes/second # Rate at which packets can be sent, in bytes/second
send_rate = {{ .P2P.SendRate }} send_rate = {{ .P2P.SendRate }}
@@ -159,6 +165,12 @@ pex = {{ .P2P.PexReactor }}
# Does not work if the peer-exchange reactor is disabled. # Does not work if the peer-exchange reactor is disabled.
seed_mode = {{ .P2P.SeedMode }} seed_mode = {{ .P2P.SeedMode }}
# Authenticated encryption
auth_enc = {{ .P2P.AuthEnc }}
# Comma separated list of peer IDs to keep private (will not be gossiped to other peers)
private_peer_ids = "{{ .P2P.PrivatePeerIDs }}"
##### mempool configuration options ##### ##### mempool configuration options #####
[mempool] [mempool]
@@ -255,7 +267,7 @@ func ResetTestRoot(testName string) *Config {
// Write default config file if missing. // Write default config file if missing.
if !cmn.FileExists(configFilePath) { if !cmn.FileExists(configFilePath) {
writeConfigFile(configFilePath) writeDefaultCondigFile(configFilePath)
} }
if !cmn.FileExists(genesisFilePath) { if !cmn.FileExists(genesisFilePath) {
cmn.MustWriteFile(genesisFilePath, []byte(testGenesis), 0644) cmn.MustWriteFile(genesisFilePath, []byte(testGenesis), 0644)
@@ -273,8 +285,8 @@ var testGenesis = `{
"validators": [ "validators": [
{ {
"pub_key": { "pub_key": {
"type": "ed25519", "type": "AC26791624DE60",
"data":"3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8" "value":"AT/+aaL1eB0477Mud9JMm8Sh8BIvOYlPGC9KkIUmFaE="
}, },
"power": 10, "power": 10,
"name": "" "name": ""
@@ -284,14 +296,14 @@ var testGenesis = `{
}` }`
var testPrivValidator = `{ var testPrivValidator = `{
"address": "D028C9981F7A87F3093672BF0D5B0E2A1B3ED456", "address": "849CB2C877F87A20925F35D00AE6688342D25B47",
"pub_key": { "pub_key": {
"type": "ed25519", "type": "AC26791624DE60",
"data": "3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8" "value": "AT/+aaL1eB0477Mud9JMm8Sh8BIvOYlPGC9KkIUmFaE="
}, },
"priv_key": { "priv_key": {
"type": "ed25519", "type": "954568A3288910",
"data": "27F82582AEFAE7AB151CFB01C48BB6C1A0DA78F9BDDA979A9F70A84D074EB07D3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8" "value": "EVkqJO/jIXp3rkASXfh9YnyToYXRXhBr6g9cQVxPFnQBP/5povV4HTjvsy530kybxKHwEi85iU8YL0qQhSYVoQ=="
}, },
"last_height": 0, "last_height": 0,
"last_round": 0, "last_round": 0,

View File

@@ -7,7 +7,6 @@ import (
"time" "time"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
@@ -46,9 +45,11 @@ func TestByzantine(t *testing.T) {
eventChans := make([]chan interface{}, N) eventChans := make([]chan interface{}, N)
reactors := make([]p2p.Reactor, N) reactors := make([]p2p.Reactor, N)
for i := 0; i < N; i++ { for i := 0; i < N; i++ {
// make first val byzantine
if i == 0 { if i == 0 {
css[i].privValidator = NewByzantinePrivValidator(css[i].privValidator) // NOTE: Now, test validators are MockPV, which by default doesn't
// make byzantine // do any safety checks.
css[i].privValidator.(*types.MockPV).DisableChecks()
css[i].decideProposal = func(j int) func(int64, int) { css[i].decideProposal = func(j int) func(int64, int) {
return func(height int64, round int) { return func(height int64, round int) {
byzantineDecideProposalFunc(t, height, round, css[j], switches[j]) byzantineDecideProposalFunc(t, height, round, css[j], switches[j])
@@ -74,9 +75,11 @@ func TestByzantine(t *testing.T) {
var conRI p2p.Reactor // nolint: gotype, gosimple var conRI p2p.Reactor // nolint: gotype, gosimple
conRI = conR conRI = conR
// make first val byzantine
if i == 0 { if i == 0 {
conRI = NewByzantineReactor(conR) conRI = NewByzantineReactor(conR)
} }
reactors[i] = conRI reactors[i] = conRI
} }
@@ -115,19 +118,19 @@ func TestByzantine(t *testing.T) {
// and the other block to peers[1] and peers[2]. // and the other block to peers[1] and peers[2].
// note peers and switches order don't match. // note peers and switches order don't match.
peers := switches[0].Peers().List() peers := switches[0].Peers().List()
// partition A
ind0 := getSwitchIndex(switches, peers[0]) ind0 := getSwitchIndex(switches, peers[0])
// partition B
ind1 := getSwitchIndex(switches, peers[1]) ind1 := getSwitchIndex(switches, peers[1])
ind2 := getSwitchIndex(switches, peers[2]) ind2 := getSwitchIndex(switches, peers[2])
// connect the 2 peers in the larger partition
p2p.Connect2Switches(switches, ind1, ind2) p2p.Connect2Switches(switches, ind1, ind2)
// wait for someone in the big partition to make a block // wait for someone in the big partition (B) to make a block
<-eventChans[ind2] <-eventChans[ind2]
t.Log("A block has been committed. Healing partition") t.Log("A block has been committed. Healing partition")
// connect the partitions
p2p.Connect2Switches(switches, ind0, ind1) p2p.Connect2Switches(switches, ind0, ind1)
p2p.Connect2Switches(switches, ind0, ind2) p2p.Connect2Switches(switches, ind0, ind2)
@@ -201,7 +204,7 @@ func byzantineDecideProposalFunc(t *testing.T, height int64, round int, cs *Cons
func sendProposalAndParts(height int64, round int, cs *ConsensusState, peer p2p.Peer, proposal *types.Proposal, blockHash []byte, parts *types.PartSet) { func sendProposalAndParts(height int64, round int, cs *ConsensusState, peer p2p.Peer, proposal *types.Proposal, blockHash []byte, parts *types.PartSet) {
// proposal // proposal
msg := &ProposalMessage{Proposal: proposal} msg := &ProposalMessage{Proposal: proposal}
peer.Send(DataChannel, struct{ ConsensusMessage }{msg}) peer.Send(DataChannel, cdc.MustMarshalBinaryBare(msg))
// parts // parts
for i := 0; i < parts.Total(); i++ { for i := 0; i < parts.Total(); i++ {
@@ -211,7 +214,7 @@ func sendProposalAndParts(height int64, round int, cs *ConsensusState, peer p2p.
Round: round, // This tells peer that this part applies to us. Round: round, // This tells peer that this part applies to us.
Part: part, Part: part,
} }
peer.Send(DataChannel, struct{ ConsensusMessage }{msg}) peer.Send(DataChannel, cdc.MustMarshalBinaryBare(msg))
} }
// votes // votes
@@ -220,8 +223,8 @@ func sendProposalAndParts(height int64, round int, cs *ConsensusState, peer p2p.
precommit, _ := cs.signVote(types.VoteTypePrecommit, blockHash, parts.Header()) precommit, _ := cs.signVote(types.VoteTypePrecommit, blockHash, parts.Header())
cs.mtx.Unlock() cs.mtx.Unlock()
peer.Send(VoteChannel, struct{ ConsensusMessage }{&VoteMessage{prevote}}) peer.Send(VoteChannel, cdc.MustMarshalBinaryBare(&VoteMessage{prevote}))
peer.Send(VoteChannel, struct{ ConsensusMessage }{&VoteMessage{precommit}}) peer.Send(VoteChannel, cdc.MustMarshalBinaryBare(&VoteMessage{precommit}))
} }
//---------------------------------------- //----------------------------------------
@@ -262,47 +265,3 @@ func (br *ByzantineReactor) RemovePeer(peer p2p.Peer, reason interface{}) {
func (br *ByzantineReactor) Receive(chID byte, peer p2p.Peer, msgBytes []byte) { func (br *ByzantineReactor) Receive(chID byte, peer p2p.Peer, msgBytes []byte) {
br.reactor.Receive(chID, peer, msgBytes) br.reactor.Receive(chID, peer, msgBytes)
} }
//----------------------------------------
// byzantine privValidator
type ByzantinePrivValidator struct {
types.Signer
pv types.PrivValidator
}
// Return a priv validator that will sign anything
func NewByzantinePrivValidator(pv types.PrivValidator) *ByzantinePrivValidator {
return &ByzantinePrivValidator{
Signer: pv.(*types.PrivValidatorFS).Signer,
pv: pv,
}
}
func (privVal *ByzantinePrivValidator) GetAddress() types.Address {
return privVal.pv.GetAddress()
}
func (privVal *ByzantinePrivValidator) GetPubKey() crypto.PubKey {
return privVal.pv.GetPubKey()
}
func (privVal *ByzantinePrivValidator) SignVote(chainID string, vote *types.Vote) (err error) {
vote.Signature, err = privVal.Sign(types.SignBytes(chainID, vote))
return err
}
func (privVal *ByzantinePrivValidator) SignProposal(chainID string, proposal *types.Proposal) (err error) {
proposal.Signature, _ = privVal.Sign(types.SignBytes(chainID, proposal))
return nil
}
func (privVal *ByzantinePrivValidator) SignHeartbeat(chainID string, heartbeat *types.Heartbeat) (err error) {
heartbeat.Signature, _ = privVal.Sign(types.SignBytes(chainID, heartbeat))
return nil
}
func (privVal *ByzantinePrivValidator) String() string {
return cmn.Fmt("PrivValidator{%X}", privVal.GetAddress())
}

View File

@@ -21,12 +21,13 @@ import (
"github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/p2p"
sm "github.com/tendermint/tendermint/state" sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
pvm "github.com/tendermint/tendermint/types/priv_validator"
cmn "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db" dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log" "github.com/tendermint/tmlibs/log"
"github.com/tendermint/abci/example/counter" "github.com/tendermint/abci/example/counter"
"github.com/tendermint/abci/example/dummy" "github.com/tendermint/abci/example/kvstore"
"github.com/go-kit/kit/log/term" "github.com/go-kit/kit/log/term"
) )
@@ -50,7 +51,7 @@ func ResetConfig(name string) *cfg.Config {
} }
//------------------------------------------------------------------------------- //-------------------------------------------------------------------------------
// validator stub (a dummy consensus peer we control) // validator stub (a kvstore consensus peer we control)
type validatorStub struct { type validatorStub struct {
Index int // Validator index. NOTE: we don't assume validator set changes. Index int // Validator index. NOTE: we don't assume validator set changes.
@@ -101,13 +102,13 @@ func signVotes(voteType byte, hash []byte, header types.PartSetHeader, vss ...*v
func incrementHeight(vss ...*validatorStub) { func incrementHeight(vss ...*validatorStub) {
for _, vs := range vss { for _, vs := range vss {
vs.Height += 1 vs.Height++
} }
} }
func incrementRound(vss ...*validatorStub) { func incrementRound(vss ...*validatorStub) {
for _, vs := range vss { for _, vs := range vss {
vs.Round += 1 vs.Round++
} }
} }
@@ -222,7 +223,7 @@ func subscribeToVoter(cs *ConsensusState, addr []byte) chan interface{} {
voteCh := make(chan interface{}) voteCh := make(chan interface{})
go func() { go func() {
for v := range voteCh0 { for v := range voteCh0 {
vote := v.(types.TMEventData).Unwrap().(types.EventDataVote) vote := v.(types.EventDataVote)
// we only fire for our own votes // we only fire for our own votes
if bytes.Equal(addr, vote.Vote.ValidatorAddress) { if bytes.Equal(addr, vote.Vote.ValidatorAddress) {
voteCh <- v voteCh <- v
@@ -277,10 +278,10 @@ func newConsensusStateWithConfigAndBlockStore(thisConfig *cfg.Config, state sm.S
return cs return cs
} }
func loadPrivValidator(config *cfg.Config) *types.PrivValidatorFS { func loadPrivValidator(config *cfg.Config) *pvm.FilePV {
privValidatorFile := config.PrivValidatorFile() privValidatorFile := config.PrivValidatorFile()
ensureDir(path.Dir(privValidatorFile), 0700) ensureDir(path.Dir(privValidatorFile), 0700)
privValidator := types.LoadOrGenPrivValidatorFS(privValidatorFile) privValidator := pvm.LoadOrGenFilePV(privValidatorFile)
privValidator.Reset() privValidator.Reset()
return privValidator return privValidator
} }
@@ -378,7 +379,7 @@ func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerF
privVal = privVals[i] privVal = privVals[i]
} else { } else {
_, tempFilePath := cmn.Tempfile("priv_validator_") _, tempFilePath := cmn.Tempfile("priv_validator_")
privVal = types.GenPrivValidatorFS(tempFilePath) privVal = pvm.GenFilePV(tempFilePath)
} }
app := appFunc() app := appFunc()
@@ -394,7 +395,7 @@ func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerF
func getSwitchIndex(switches []*p2p.Switch, peer p2p.Peer) int { func getSwitchIndex(switches []*p2p.Switch, peer p2p.Peer) int {
for i, s := range switches { for i, s := range switches {
if bytes.Equal(peer.NodeInfo().PubKey.Address(), s.NodeInfo().PubKey.Address()) { if peer.NodeInfo().ID == s.NodeInfo().ID {
return i return i
} }
} }
@@ -405,9 +406,9 @@ func getSwitchIndex(switches []*p2p.Switch, peer p2p.Peer) int {
//------------------------------------------------------------------------------- //-------------------------------------------------------------------------------
// genesis // genesis
func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.GenesisDoc, []*types.PrivValidatorFS) { func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.GenesisDoc, []types.PrivValidator) {
validators := make([]types.GenesisValidator, numValidators) validators := make([]types.GenesisValidator, numValidators)
privValidators := make([]*types.PrivValidatorFS, numValidators) privValidators := make([]types.PrivValidator, numValidators)
for i := 0; i < numValidators; i++ { for i := 0; i < numValidators; i++ {
val, privVal := types.RandValidator(randPower, minPower) val, privVal := types.RandValidator(randPower, minPower)
validators[i] = types.GenesisValidator{ validators[i] = types.GenesisValidator{
@@ -425,7 +426,7 @@ func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.G
}, privValidators }, privValidators
} }
func randGenesisState(numValidators int, randPower bool, minPower int64) (sm.State, []*types.PrivValidatorFS) { func randGenesisState(numValidators int, randPower bool, minPower int64) (sm.State, []types.PrivValidator) {
genDoc, privValidators := randGenesisDoc(numValidators, randPower, minPower) genDoc, privValidators := randGenesisDoc(numValidators, randPower, minPower)
s0, _ := sm.MakeGenesisState(genDoc) s0, _ := sm.MakeGenesisState(genDoc)
db := dbm.NewMemDB() db := dbm.NewMemDB()
@@ -488,7 +489,7 @@ func newCounter() abci.Application {
return counter.NewCounterApplication(true) return counter.NewCounterApplication(true)
} }
func newPersistentDummy() abci.Application { func newPersistentKVStore() abci.Application {
dir, _ := ioutil.TempDir("/tmp", "persistent-dummy") dir, _ := ioutil.TempDir("/tmp", "persistent-kvstore")
return dummy.NewPersistentDummyApplication(dir) return kvstore.NewPersistentKVStoreApplication(dir)
} }

View File

@@ -108,7 +108,7 @@ func TestMempoolTxConcurrentWithCommit(t *testing.T) {
ticker := time.NewTicker(time.Second * 30) ticker := time.NewTicker(time.Second * 30)
select { select {
case b := <-newBlockCh: case b := <-newBlockCh:
evt := b.(types.TMEventData).Unwrap().(types.EventDataNewBlock) evt := b.(types.EventDataNewBlock)
nTxs += int(evt.Block.Header.NumTxs) nTxs += int(evt.Block.Header.NumTxs)
case <-ticker.C: case <-ticker.C:
panic("Timed out waiting to commit blocks with transactions") panic("Timed out waiting to commit blocks with transactions")
@@ -152,6 +152,7 @@ func TestMempoolRmBadTx(t *testing.T) {
txs := cs.mempool.Reap(1) txs := cs.mempool.Reap(1)
if len(txs) == 0 { if len(txs) == 0 {
emptyMempoolCh <- struct{}{} emptyMempoolCh <- struct{}{}
return
} }
time.Sleep(10 * time.Millisecond) time.Sleep(10 * time.Millisecond)
} }
@@ -199,7 +200,7 @@ func (app *CounterApplication) DeliverTx(tx []byte) abci.ResponseDeliverTx {
Code: code.CodeTypeBadNonce, Code: code.CodeTypeBadNonce,
Log: fmt.Sprintf("Invalid nonce. Expected %v, got %v", app.txCount, txValue)} Log: fmt.Sprintf("Invalid nonce. Expected %v, got %v", app.txCount, txValue)}
} }
app.txCount += 1 app.txCount++
return abci.ResponseDeliverTx{Code: code.CodeTypeOK} return abci.ResponseDeliverTx{Code: code.CodeTypeOK}
} }
@@ -210,7 +211,7 @@ func (app *CounterApplication) CheckTx(tx []byte) abci.ResponseCheckTx {
Code: code.CodeTypeBadNonce, Code: code.CodeTypeBadNonce,
Log: fmt.Sprintf("Invalid nonce. Expected %v, got %v", app.mempoolTxCount, txValue)} Log: fmt.Sprintf("Invalid nonce. Expected %v, got %v", app.mempoolTxCount, txValue)}
} }
app.mempoolTxCount += 1 app.mempoolTxCount++
return abci.ResponseCheckTx{Code: code.CodeTypeOK} return abci.ResponseCheckTx{Code: code.CodeTypeOK}
} }
@@ -224,9 +225,8 @@ func (app *CounterApplication) Commit() abci.ResponseCommit {
app.mempoolTxCount = app.txCount app.mempoolTxCount = app.txCount
if app.txCount == 0 { if app.txCount == 0 {
return abci.ResponseCommit{} return abci.ResponseCommit{}
} else { }
hash := make([]byte, 8) hash := make([]byte, 8)
binary.BigEndian.PutUint64(hash, uint64(app.txCount)) binary.BigEndian.PutUint64(hash, uint64(app.txCount))
return abci.ResponseCommit{Data: hash} return abci.ResponseCommit{Data: hash}
} }
}

View File

@@ -1,7 +1,6 @@
package consensus package consensus
import ( import (
"bytes"
"context" "context"
"fmt" "fmt"
"reflect" "reflect"
@@ -10,7 +9,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
wire "github.com/tendermint/go-wire" amino "github.com/tendermint/go-amino"
cmn "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/log" "github.com/tendermint/tmlibs/log"
@@ -26,7 +25,9 @@ const (
VoteChannel = byte(0x22) VoteChannel = byte(0x22)
VoteSetBitsChannel = byte(0x23) VoteSetBitsChannel = byte(0x23)
maxConsensusMessageSize = 1048576 // 1MB; NOTE/TODO: keep in sync with types.PartSet sizes. maxMsgSize = 1048576 // 1MB; NOTE/TODO: keep in sync with types.PartSet sizes.
blocksToContributeToBecomeGoodPeer = 10000
) )
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@@ -111,24 +112,28 @@ func (conR *ConsensusReactor) GetChannels() []*p2p.ChannelDescriptor {
ID: StateChannel, ID: StateChannel,
Priority: 5, Priority: 5,
SendQueueCapacity: 100, SendQueueCapacity: 100,
RecvMessageCapacity: maxMsgSize,
}, },
{ {
ID: DataChannel, // maybe split between gossiping current block and catchup stuff ID: DataChannel, // maybe split between gossiping current block and catchup stuff
Priority: 10, // once we gossip the whole block there's nothing left to send until next height or round Priority: 10, // once we gossip the whole block there's nothing left to send until next height or round
SendQueueCapacity: 100, SendQueueCapacity: 100,
RecvBufferCapacity: 50 * 4096, RecvBufferCapacity: 50 * 4096,
RecvMessageCapacity: maxMsgSize,
}, },
{ {
ID: VoteChannel, ID: VoteChannel,
Priority: 5, Priority: 5,
SendQueueCapacity: 100, SendQueueCapacity: 100,
RecvBufferCapacity: 100 * 100, RecvBufferCapacity: 100 * 100,
RecvMessageCapacity: maxMsgSize,
}, },
{ {
ID: VoteSetBitsChannel, ID: VoteSetBitsChannel,
Priority: 1, Priority: 1,
SendQueueCapacity: 2, SendQueueCapacity: 2,
RecvBufferCapacity: 1024, RecvBufferCapacity: 1024,
RecvMessageCapacity: maxMsgSize,
}, },
} }
} }
@@ -176,10 +181,10 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
return return
} }
_, msg, err := DecodeMessage(msgBytes) msg, err := DecodeMessage(msgBytes)
if err != nil { if err != nil {
conR.Logger.Error("Error decoding message", "src", src, "chId", chID, "msg", msg, "err", err, "bytes", msgBytes) conR.Logger.Error("Error decoding message", "src", src, "chId", chID, "msg", msg, "err", err, "bytes", msgBytes)
// TODO punish peer? conR.Switch.StopPeerForError(src, err)
return return
} }
conR.Logger.Debug("Receive", "src", src, "chId", chID, "msg", msg) conR.Logger.Debug("Receive", "src", src, "chId", chID, "msg", msg)
@@ -222,13 +227,13 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
conR.Logger.Error("Bad VoteSetBitsMessage field Type") conR.Logger.Error("Bad VoteSetBitsMessage field Type")
return return
} }
src.TrySend(VoteSetBitsChannel, struct{ ConsensusMessage }{&VoteSetBitsMessage{ src.TrySend(VoteSetBitsChannel, cdc.MustMarshalBinaryBare(&VoteSetBitsMessage{
Height: msg.Height, Height: msg.Height,
Round: msg.Round, Round: msg.Round,
Type: msg.Type, Type: msg.Type,
BlockID: msg.BlockID, BlockID: msg.BlockID,
Votes: ourVotes, Votes: ourVotes,
}}) }))
case *ProposalHeartbeatMessage: case *ProposalHeartbeatMessage:
hb := msg.Heartbeat hb := msg.Heartbeat
conR.Logger.Debug("Received proposal heartbeat message", conR.Logger.Debug("Received proposal heartbeat message",
@@ -251,6 +256,9 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
ps.ApplyProposalPOLMessage(msg) ps.ApplyProposalPOLMessage(msg)
case *BlockPartMessage: case *BlockPartMessage:
ps.SetHasProposalBlockPart(msg.Height, msg.Round, msg.Part.Index) ps.SetHasProposalBlockPart(msg.Height, msg.Round, msg.Part.Index)
if numBlocks := ps.RecordBlockPart(msg); numBlocks%blocksToContributeToBecomeGoodPeer == 0 {
conR.Switch.MarkPeerAsGood(src)
}
conR.conS.peerMsgQueue <- msgInfo{msg, src.ID()} conR.conS.peerMsgQueue <- msgInfo{msg, src.ID()}
default: default:
conR.Logger.Error(cmn.Fmt("Unknown message type %v", reflect.TypeOf(msg))) conR.Logger.Error(cmn.Fmt("Unknown message type %v", reflect.TypeOf(msg)))
@@ -270,6 +278,9 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
ps.EnsureVoteBitArrays(height, valSize) ps.EnsureVoteBitArrays(height, valSize)
ps.EnsureVoteBitArrays(height-1, lastCommitSize) ps.EnsureVoteBitArrays(height-1, lastCommitSize)
ps.SetHasVote(msg.Vote) ps.SetHasVote(msg.Vote)
if blocks := ps.RecordVote(msg.Vote); blocks%blocksToContributeToBecomeGoodPeer == 0 {
conR.Switch.MarkPeerAsGood(src)
}
cs.peerMsgQueue <- msgInfo{msg, src.ID()} cs.peerMsgQueue <- msgInfo{msg, src.ID()}
@@ -363,27 +374,33 @@ func (conR *ConsensusReactor) startBroadcastRoutine() error {
} }
go func() { go func() {
var data interface{}
var ok bool
for { for {
select { select {
case data, ok := <-stepsCh: case data, ok = <-stepsCh:
if ok { // a receive from a closed channel returns the zero value immediately if ok { // a receive from a closed channel returns the zero value immediately
edrs := data.(types.TMEventData).Unwrap().(types.EventDataRoundState) edrs := data.(types.EventDataRoundState)
conR.broadcastNewRoundStep(edrs.RoundState.(*cstypes.RoundState)) conR.broadcastNewRoundStep(edrs.RoundState.(*cstypes.RoundState))
} }
case data, ok := <-votesCh: case data, ok = <-votesCh:
if ok { if ok {
edv := data.(types.TMEventData).Unwrap().(types.EventDataVote) edv := data.(types.EventDataVote)
conR.broadcastHasVoteMessage(edv.Vote) conR.broadcastHasVoteMessage(edv.Vote)
} }
case data, ok := <-heartbeatsCh: case data, ok = <-heartbeatsCh:
if ok { if ok {
edph := data.(types.TMEventData).Unwrap().(types.EventDataProposalHeartbeat) edph := data.(types.EventDataProposalHeartbeat)
conR.broadcastProposalHeartbeatMessage(edph) conR.broadcastProposalHeartbeatMessage(edph)
} }
case <-conR.Quit(): case <-conR.Quit():
conR.eventBus.UnsubscribeAll(ctx, subscriber) conR.eventBus.UnsubscribeAll(ctx, subscriber)
return return
} }
if !ok {
conR.eventBus.UnsubscribeAll(ctx, subscriber)
return
}
} }
}() }()
@@ -395,16 +412,16 @@ func (conR *ConsensusReactor) broadcastProposalHeartbeatMessage(heartbeat types.
conR.Logger.Debug("Broadcasting proposal heartbeat message", conR.Logger.Debug("Broadcasting proposal heartbeat message",
"height", hb.Height, "round", hb.Round, "sequence", hb.Sequence) "height", hb.Height, "round", hb.Round, "sequence", hb.Sequence)
msg := &ProposalHeartbeatMessage{hb} msg := &ProposalHeartbeatMessage{hb}
conR.Switch.Broadcast(StateChannel, struct{ ConsensusMessage }{msg}) conR.Switch.Broadcast(StateChannel, cdc.MustMarshalBinaryBare(msg))
} }
func (conR *ConsensusReactor) broadcastNewRoundStep(rs *cstypes.RoundState) { func (conR *ConsensusReactor) broadcastNewRoundStep(rs *cstypes.RoundState) {
nrsMsg, csMsg := makeRoundStepMessages(rs) nrsMsg, csMsg := makeRoundStepMessages(rs)
if nrsMsg != nil { if nrsMsg != nil {
conR.Switch.Broadcast(StateChannel, struct{ ConsensusMessage }{nrsMsg}) conR.Switch.Broadcast(StateChannel, cdc.MustMarshalBinaryBare(nrsMsg))
} }
if csMsg != nil { if csMsg != nil {
conR.Switch.Broadcast(StateChannel, struct{ ConsensusMessage }{csMsg}) conR.Switch.Broadcast(StateChannel, cdc.MustMarshalBinaryBare(csMsg))
} }
} }
@@ -416,7 +433,7 @@ func (conR *ConsensusReactor) broadcastHasVoteMessage(vote *types.Vote) {
Type: vote.Type, Type: vote.Type,
Index: vote.ValidatorIndex, Index: vote.ValidatorIndex,
} }
conR.Switch.Broadcast(StateChannel, struct{ ConsensusMessage }{msg}) conR.Switch.Broadcast(StateChannel, cdc.MustMarshalBinaryBare(msg))
/* /*
// TODO: Make this broadcast more selective. // TODO: Make this broadcast more selective.
for _, peer := range conR.Switch.Peers().List() { for _, peer := range conR.Switch.Peers().List() {
@@ -456,10 +473,10 @@ func (conR *ConsensusReactor) sendNewRoundStepMessages(peer p2p.Peer) {
rs := conR.conS.GetRoundState() rs := conR.conS.GetRoundState()
nrsMsg, csMsg := makeRoundStepMessages(rs) nrsMsg, csMsg := makeRoundStepMessages(rs)
if nrsMsg != nil { if nrsMsg != nil {
peer.Send(StateChannel, struct{ ConsensusMessage }{nrsMsg}) peer.Send(StateChannel, cdc.MustMarshalBinaryBare(nrsMsg))
} }
if csMsg != nil { if csMsg != nil {
peer.Send(StateChannel, struct{ ConsensusMessage }{csMsg}) peer.Send(StateChannel, cdc.MustMarshalBinaryBare(csMsg))
} }
} }
@@ -486,7 +503,7 @@ OUTER_LOOP:
Part: part, Part: part,
} }
logger.Debug("Sending block part", "height", prs.Height, "round", prs.Round) logger.Debug("Sending block part", "height", prs.Height, "round", prs.Round)
if peer.Send(DataChannel, struct{ ConsensusMessage }{msg}) { if peer.Send(DataChannel, cdc.MustMarshalBinaryBare(msg)) {
ps.SetHasProposalBlockPart(prs.Height, prs.Round, index) ps.SetHasProposalBlockPart(prs.Height, prs.Round, index)
} }
continue OUTER_LOOP continue OUTER_LOOP
@@ -530,7 +547,7 @@ OUTER_LOOP:
{ {
msg := &ProposalMessage{Proposal: rs.Proposal} msg := &ProposalMessage{Proposal: rs.Proposal}
logger.Debug("Sending proposal", "height", prs.Height, "round", prs.Round) logger.Debug("Sending proposal", "height", prs.Height, "round", prs.Round)
if peer.Send(DataChannel, struct{ ConsensusMessage }{msg}) { if peer.Send(DataChannel, cdc.MustMarshalBinaryBare(msg)) {
ps.SetHasProposal(rs.Proposal) ps.SetHasProposal(rs.Proposal)
} }
} }
@@ -545,7 +562,7 @@ OUTER_LOOP:
ProposalPOL: rs.Votes.Prevotes(rs.Proposal.POLRound).BitArray(), ProposalPOL: rs.Votes.Prevotes(rs.Proposal.POLRound).BitArray(),
} }
logger.Debug("Sending POL", "height", prs.Height, "round", prs.Round) logger.Debug("Sending POL", "height", prs.Height, "round", prs.Round)
peer.Send(DataChannel, struct{ ConsensusMessage }{msg}) peer.Send(DataChannel, cdc.MustMarshalBinaryBare(msg))
} }
continue OUTER_LOOP continue OUTER_LOOP
} }
@@ -588,17 +605,15 @@ func (conR *ConsensusReactor) gossipDataForCatchup(logger log.Logger, rs *cstype
Part: part, Part: part,
} }
logger.Debug("Sending block part for catchup", "round", prs.Round, "index", index) logger.Debug("Sending block part for catchup", "round", prs.Round, "index", index)
if peer.Send(DataChannel, struct{ ConsensusMessage }{msg}) { if peer.Send(DataChannel, cdc.MustMarshalBinaryBare(msg)) {
ps.SetHasProposalBlockPart(prs.Height, prs.Round, index) ps.SetHasProposalBlockPart(prs.Height, prs.Round, index)
} else { } else {
logger.Debug("Sending block part for catchup failed") logger.Debug("Sending block part for catchup failed")
} }
return return
} else { }
//logger.Info("No parts to send in catch-up, sleeping") //logger.Info("No parts to send in catch-up, sleeping")
time.Sleep(conR.conS.config.PeerGossipSleep()) time.Sleep(conR.conS.config.PeerGossipSleep())
return
}
} }
func (conR *ConsensusReactor) gossipVotesRoutine(peer p2p.Peer, ps *PeerState) { func (conR *ConsensusReactor) gossipVotesRoutine(peer p2p.Peer, ps *PeerState) {
@@ -727,12 +742,12 @@ OUTER_LOOP:
prs := ps.GetRoundState() prs := ps.GetRoundState()
if rs.Height == prs.Height { if rs.Height == prs.Height {
if maj23, ok := rs.Votes.Prevotes(prs.Round).TwoThirdsMajority(); ok { if maj23, ok := rs.Votes.Prevotes(prs.Round).TwoThirdsMajority(); ok {
peer.TrySend(StateChannel, struct{ ConsensusMessage }{&VoteSetMaj23Message{ peer.TrySend(StateChannel, cdc.MustMarshalBinaryBare(&VoteSetMaj23Message{
Height: prs.Height, Height: prs.Height,
Round: prs.Round, Round: prs.Round,
Type: types.VoteTypePrevote, Type: types.VoteTypePrevote,
BlockID: maj23, BlockID: maj23,
}}) }))
time.Sleep(conR.conS.config.PeerQueryMaj23Sleep()) time.Sleep(conR.conS.config.PeerQueryMaj23Sleep())
} }
} }
@@ -744,12 +759,12 @@ OUTER_LOOP:
prs := ps.GetRoundState() prs := ps.GetRoundState()
if rs.Height == prs.Height { if rs.Height == prs.Height {
if maj23, ok := rs.Votes.Precommits(prs.Round).TwoThirdsMajority(); ok { if maj23, ok := rs.Votes.Precommits(prs.Round).TwoThirdsMajority(); ok {
peer.TrySend(StateChannel, struct{ ConsensusMessage }{&VoteSetMaj23Message{ peer.TrySend(StateChannel, cdc.MustMarshalBinaryBare(&VoteSetMaj23Message{
Height: prs.Height, Height: prs.Height,
Round: prs.Round, Round: prs.Round,
Type: types.VoteTypePrecommit, Type: types.VoteTypePrecommit,
BlockID: maj23, BlockID: maj23,
}}) }))
time.Sleep(conR.conS.config.PeerQueryMaj23Sleep()) time.Sleep(conR.conS.config.PeerQueryMaj23Sleep())
} }
} }
@@ -761,12 +776,12 @@ OUTER_LOOP:
prs := ps.GetRoundState() prs := ps.GetRoundState()
if rs.Height == prs.Height && prs.ProposalPOLRound >= 0 { if rs.Height == prs.Height && prs.ProposalPOLRound >= 0 {
if maj23, ok := rs.Votes.Prevotes(prs.ProposalPOLRound).TwoThirdsMajority(); ok { if maj23, ok := rs.Votes.Prevotes(prs.ProposalPOLRound).TwoThirdsMajority(); ok {
peer.TrySend(StateChannel, struct{ ConsensusMessage }{&VoteSetMaj23Message{ peer.TrySend(StateChannel, cdc.MustMarshalBinaryBare(&VoteSetMaj23Message{
Height: prs.Height, Height: prs.Height,
Round: prs.ProposalPOLRound, Round: prs.ProposalPOLRound,
Type: types.VoteTypePrevote, Type: types.VoteTypePrevote,
BlockID: maj23, BlockID: maj23,
}}) }))
time.Sleep(conR.conS.config.PeerQueryMaj23Sleep()) time.Sleep(conR.conS.config.PeerQueryMaj23Sleep())
} }
} }
@@ -780,12 +795,12 @@ OUTER_LOOP:
prs := ps.GetRoundState() prs := ps.GetRoundState()
if prs.CatchupCommitRound != -1 && 0 < prs.Height && prs.Height <= conR.conS.blockStore.Height() { if prs.CatchupCommitRound != -1 && 0 < prs.Height && prs.Height <= conR.conS.blockStore.Height() {
commit := conR.conS.LoadCommit(prs.Height) commit := conR.conS.LoadCommit(prs.Height)
peer.TrySend(StateChannel, struct{ ConsensusMessage }{&VoteSetMaj23Message{ peer.TrySend(StateChannel, cdc.MustMarshalBinaryBare(&VoteSetMaj23Message{
Height: prs.Height, Height: prs.Height,
Round: commit.Round(), Round: commit.Round(),
Type: types.VoteTypePrecommit, Type: types.VoteTypePrecommit,
BlockID: commit.BlockID, BlockID: commit.BlockID,
}}) }))
time.Sleep(conR.conS.config.PeerQueryMaj23Sleep()) time.Sleep(conR.conS.config.PeerQueryMaj23Sleep())
} }
} }
@@ -823,14 +838,29 @@ var (
ErrPeerStateInvalidStartTime = errors.New("Error peer state invalid startTime") ErrPeerStateInvalidStartTime = errors.New("Error peer state invalid startTime")
) )
// PeerState contains the known state of a peer, including its connection // PeerState contains the known state of a peer, including its connection and
// and threadsafe access to its PeerRoundState. // threadsafe access to its PeerRoundState.
type PeerState struct { type PeerState struct {
Peer p2p.Peer Peer p2p.Peer
logger log.Logger logger log.Logger
mtx sync.Mutex mtx sync.Mutex
cstypes.PeerRoundState cstypes.PeerRoundState
stats *peerStateStats
}
// peerStateStats holds internal statistics for a peer.
type peerStateStats struct {
lastVoteHeight int64
votes int
lastBlockPartHeight int64
blockParts int
}
func (pss peerStateStats) String() string {
return fmt.Sprintf("peerStateStats{votes: %d, blockParts: %d}", pss.votes, pss.blockParts)
} }
// NewPeerState returns a new PeerState for the given Peer // NewPeerState returns a new PeerState for the given Peer
@@ -844,15 +874,18 @@ func NewPeerState(peer p2p.Peer) *PeerState {
LastCommitRound: -1, LastCommitRound: -1,
CatchupCommitRound: -1, CatchupCommitRound: -1,
}, },
stats: &peerStateStats{},
} }
} }
// SetLogger allows to set a logger on the peer state. Returns the peer state
// itself.
func (ps *PeerState) SetLogger(logger log.Logger) *PeerState { func (ps *PeerState) SetLogger(logger log.Logger) *PeerState {
ps.logger = logger ps.logger = logger
return ps return ps
} }
// GetRoundState returns an atomic snapshot of the PeerRoundState. // GetRoundState returns an shallow copy of the PeerRoundState.
// There's no point in mutating it since it won't change PeerState. // There's no point in mutating it since it won't change PeerState.
func (ps *PeerState) GetRoundState() *cstypes.PeerRoundState { func (ps *PeerState) GetRoundState() *cstypes.PeerRoundState {
ps.mtx.Lock() ps.mtx.Lock()
@@ -862,6 +895,14 @@ func (ps *PeerState) GetRoundState() *cstypes.PeerRoundState {
return &prs return &prs
} }
// GetRoundStateJSON returns a json of PeerRoundState, marshalled using go-amino.
func (ps *PeerState) GetRoundStateJSON() ([]byte, error) {
ps.mtx.Lock()
defer ps.mtx.Unlock()
return cdc.MarshalJSON(ps.PeerRoundState)
}
// GetHeight returns an atomic snapshot of the PeerRoundState's height // GetHeight returns an atomic snapshot of the PeerRoundState's height
// used by the mempool to ensure peers are caught up before broadcasting new txs // used by the mempool to ensure peers are caught up before broadcasting new txs
func (ps *PeerState) GetHeight() int64 { func (ps *PeerState) GetHeight() int64 {
@@ -920,7 +961,7 @@ func (ps *PeerState) PickSendVote(votes types.VoteSetReader) bool {
if vote, ok := ps.PickVoteToSend(votes); ok { if vote, ok := ps.PickVoteToSend(votes); ok {
msg := &VoteMessage{vote} msg := &VoteMessage{vote}
ps.logger.Debug("Sending vote message", "ps", ps, "vote", vote) ps.logger.Debug("Sending vote message", "ps", ps, "vote", vote)
return ps.Peer.Send(VoteChannel, struct{ ConsensusMessage }{msg}) return ps.Peer.Send(VoteChannel, cdc.MustMarshalBinaryBare(msg))
} }
return false return false
} }
@@ -1024,7 +1065,7 @@ func (ps *PeerState) ensureCatchupCommitRound(height int64, round int, numValida
} }
} }
// EnsureVoteVitArrays ensures the bit-arrays have been allocated for tracking // EnsureVoteBitArrays ensures the bit-arrays have been allocated for tracking
// what votes this peer has received. // what votes this peer has received.
// NOTE: It's important to make sure that numValidators actually matches // NOTE: It's important to make sure that numValidators actually matches
// what the node sees as the number of validators for height. // what the node sees as the number of validators for height.
@@ -1055,6 +1096,56 @@ func (ps *PeerState) ensureVoteBitArrays(height int64, numValidators int) {
} }
} }
// RecordVote updates internal statistics for this peer by recording the vote.
// It returns the total number of votes (1 per block). This essentially means
// the number of blocks for which peer has been sending us votes.
func (ps *PeerState) RecordVote(vote *types.Vote) int {
ps.mtx.Lock()
defer ps.mtx.Unlock()
if ps.stats.lastVoteHeight >= vote.Height {
return ps.stats.votes
}
ps.stats.lastVoteHeight = vote.Height
ps.stats.votes++
return ps.stats.votes
}
// VotesSent returns the number of blocks for which peer has been sending us
// votes.
func (ps *PeerState) VotesSent() int {
ps.mtx.Lock()
defer ps.mtx.Unlock()
return ps.stats.votes
}
// RecordBlockPart updates internal statistics for this peer by recording the
// block part. It returns the total number of block parts (1 per block). This
// essentially means the number of blocks for which peer has been sending us
// block parts.
func (ps *PeerState) RecordBlockPart(bp *BlockPartMessage) int {
ps.mtx.Lock()
defer ps.mtx.Unlock()
if ps.stats.lastBlockPartHeight >= bp.Height {
return ps.stats.blockParts
}
ps.stats.lastBlockPartHeight = bp.Height
ps.stats.blockParts++
return ps.stats.blockParts
}
// BlockPartsSent returns the number of blocks for which peer has been sending
// us block parts.
func (ps *PeerState) BlockPartsSent() int {
ps.mtx.Lock()
defer ps.mtx.Unlock()
return ps.stats.blockParts
}
// SetHasVote sets the given vote as known by the peer // SetHasVote sets the given vote as known by the peer
func (ps *PeerState) SetHasVote(vote *types.Vote) { func (ps *PeerState) SetHasVote(vote *types.Vote) {
ps.mtx.Lock() ps.mtx.Lock()
@@ -1203,54 +1294,41 @@ func (ps *PeerState) StringIndented(indent string) string {
return fmt.Sprintf(`PeerState{ return fmt.Sprintf(`PeerState{
%s Key %v %s Key %v
%s PRS %v %s PRS %v
%s Stats %v
%s}`, %s}`,
indent, ps.Peer.ID(), indent, ps.Peer.ID(),
indent, ps.PeerRoundState.StringIndented(indent+" "), indent, ps.PeerRoundState.StringIndented(indent+" "),
indent, ps.stats,
indent) indent)
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Messages // Messages
const (
msgTypeNewRoundStep = byte(0x01)
msgTypeCommitStep = byte(0x02)
msgTypeProposal = byte(0x11)
msgTypeProposalPOL = byte(0x12)
msgTypeBlockPart = byte(0x13) // both block & POL
msgTypeVote = byte(0x14)
msgTypeHasVote = byte(0x15)
msgTypeVoteSetMaj23 = byte(0x16)
msgTypeVoteSetBits = byte(0x17)
msgTypeProposalHeartbeat = byte(0x20)
)
// ConsensusMessage is a message that can be sent and received on the ConsensusReactor // ConsensusMessage is a message that can be sent and received on the ConsensusReactor
type ConsensusMessage interface{} type ConsensusMessage interface{}
var _ = wire.RegisterInterface( func RegisterConsensusMessages(cdc *amino.Codec) {
struct{ ConsensusMessage }{}, cdc.RegisterInterface((*ConsensusMessage)(nil), nil)
wire.ConcreteType{&NewRoundStepMessage{}, msgTypeNewRoundStep}, cdc.RegisterConcrete(&NewRoundStepMessage{}, "tendermint/NewRoundStepMessage", nil)
wire.ConcreteType{&CommitStepMessage{}, msgTypeCommitStep}, cdc.RegisterConcrete(&CommitStepMessage{}, "tendermint/CommitStep", nil)
wire.ConcreteType{&ProposalMessage{}, msgTypeProposal}, cdc.RegisterConcrete(&ProposalMessage{}, "tendermint/Proposal", nil)
wire.ConcreteType{&ProposalPOLMessage{}, msgTypeProposalPOL}, cdc.RegisterConcrete(&ProposalPOLMessage{}, "tendermint/ProposalPOL", nil)
wire.ConcreteType{&BlockPartMessage{}, msgTypeBlockPart}, cdc.RegisterConcrete(&BlockPartMessage{}, "tendermint/BlockPart", nil)
wire.ConcreteType{&VoteMessage{}, msgTypeVote}, cdc.RegisterConcrete(&VoteMessage{}, "tendermint/Vote", nil)
wire.ConcreteType{&HasVoteMessage{}, msgTypeHasVote}, cdc.RegisterConcrete(&HasVoteMessage{}, "tendermint/HasVote", nil)
wire.ConcreteType{&VoteSetMaj23Message{}, msgTypeVoteSetMaj23}, cdc.RegisterConcrete(&VoteSetMaj23Message{}, "tendermint/VoteSetMaj23", nil)
wire.ConcreteType{&VoteSetBitsMessage{}, msgTypeVoteSetBits}, cdc.RegisterConcrete(&VoteSetBitsMessage{}, "tendermint/VoteSetBits", nil)
wire.ConcreteType{&ProposalHeartbeatMessage{}, msgTypeProposalHeartbeat}, cdc.RegisterConcrete(&ProposalHeartbeatMessage{}, "tendermint/ProposalHeartbeat", nil)
) }
// DecodeMessage decodes the given bytes into a ConsensusMessage. // DecodeMessage decodes the given bytes into a ConsensusMessage.
// TODO: check for unnecessary extra bytes at the end. func DecodeMessage(bz []byte) (msg ConsensusMessage, err error) {
func DecodeMessage(bz []byte) (msgType byte, msg ConsensusMessage, err error) { if len(bz) > maxMsgSize {
msgType = bz[0] return msg, fmt.Errorf("Msg exceeds max size (%d > %d)",
n := new(int) len(bz), maxMsgSize)
r := bytes.NewReader(bz) }
msgI := wire.ReadBinary(struct{ ConsensusMessage }{}, r, maxConsensusMessageSize, n, &err) err = cdc.UnmarshalBinaryBare(bz, &msg)
msg = msgI.(struct{ ConsensusMessage }).ConsensusMessage
return return
} }

View File

@@ -10,11 +10,13 @@ import (
"testing" "testing"
"time" "time"
"github.com/tendermint/abci/example/dummy" "github.com/tendermint/abci/example/kvstore"
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/log" "github.com/tendermint/tmlibs/log"
cfg "github.com/tendermint/tendermint/config" cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/p2p"
p2pdummy "github.com/tendermint/tendermint/p2p/dummy"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@@ -121,13 +123,119 @@ func TestReactorProposalHeartbeats(t *testing.T) {
}, css) }, css)
} }
// Test we record block parts from other peers
func TestReactorRecordsBlockParts(t *testing.T) {
// create dummy peer
peer := p2pdummy.NewPeer()
ps := NewPeerState(peer).SetLogger(log.TestingLogger())
peer.Set(types.PeerStateKey, ps)
// create reactor
css := randConsensusNet(1, "consensus_reactor_records_block_parts_test", newMockTickerFunc(true), newPersistentKVStore)
reactor := NewConsensusReactor(css[0], false) // so we dont start the consensus states
reactor.SetEventBus(css[0].eventBus)
reactor.SetLogger(log.TestingLogger())
sw := p2p.MakeSwitch(cfg.DefaultP2PConfig(), 1, "testing", "123.123.123", func(i int, sw *p2p.Switch) *p2p.Switch { return sw })
reactor.SetSwitch(sw)
err := reactor.Start()
require.NoError(t, err)
defer reactor.Stop()
// 1) new block part
parts := types.NewPartSetFromData(cmn.RandBytes(100), 10)
msg := &BlockPartMessage{
Height: 2,
Round: 0,
Part: parts.GetPart(0),
}
bz, err := cdc.MarshalBinaryBare(msg)
require.NoError(t, err)
reactor.Receive(DataChannel, peer, bz)
require.Equal(t, 1, ps.BlockPartsSent(), "number of block parts sent should have increased by 1")
// 2) block part with the same height, but different round
msg.Round = 1
bz, err = cdc.MarshalBinaryBare(msg)
require.NoError(t, err)
reactor.Receive(DataChannel, peer, bz)
require.Equal(t, 1, ps.BlockPartsSent(), "number of block parts sent should stay the same")
// 3) block part from earlier height
msg.Height = 1
msg.Round = 0
bz, err = cdc.MarshalBinaryBare(msg)
require.NoError(t, err)
reactor.Receive(DataChannel, peer, bz)
require.Equal(t, 1, ps.BlockPartsSent(), "number of block parts sent should stay the same")
}
// Test we record votes from other peers
func TestReactorRecordsVotes(t *testing.T) {
// create dummy peer
peer := p2pdummy.NewPeer()
ps := NewPeerState(peer).SetLogger(log.TestingLogger())
peer.Set(types.PeerStateKey, ps)
// create reactor
css := randConsensusNet(1, "consensus_reactor_records_votes_test", newMockTickerFunc(true), newPersistentKVStore)
reactor := NewConsensusReactor(css[0], false) // so we dont start the consensus states
reactor.SetEventBus(css[0].eventBus)
reactor.SetLogger(log.TestingLogger())
sw := p2p.MakeSwitch(cfg.DefaultP2PConfig(), 1, "testing", "123.123.123", func(i int, sw *p2p.Switch) *p2p.Switch { return sw })
reactor.SetSwitch(sw)
err := reactor.Start()
require.NoError(t, err)
defer reactor.Stop()
_, val := css[0].state.Validators.GetByIndex(0)
// 1) new vote
vote := &types.Vote{
ValidatorIndex: 0,
ValidatorAddress: val.Address,
Height: 2,
Round: 0,
Timestamp: time.Now().UTC(),
Type: types.VoteTypePrevote,
BlockID: types.BlockID{},
}
bz, err := cdc.MarshalBinaryBare(&VoteMessage{vote})
require.NoError(t, err)
reactor.Receive(VoteChannel, peer, bz)
assert.Equal(t, 1, ps.VotesSent(), "number of votes sent should have increased by 1")
// 2) vote with the same height, but different round
vote.Round = 1
bz, err = cdc.MarshalBinaryBare(&VoteMessage{vote})
require.NoError(t, err)
reactor.Receive(VoteChannel, peer, bz)
assert.Equal(t, 1, ps.VotesSent(), "number of votes sent should stay the same")
// 3) vote from earlier height
vote.Height = 1
vote.Round = 0
bz, err = cdc.MarshalBinaryBare(&VoteMessage{vote})
require.NoError(t, err)
reactor.Receive(VoteChannel, peer, bz)
assert.Equal(t, 1, ps.VotesSent(), "number of votes sent should stay the same")
}
//------------------------------------------------------------- //-------------------------------------------------------------
// ensure we can make blocks despite cycling a validator set // ensure we can make blocks despite cycling a validator set
func TestReactorVotingPowerChange(t *testing.T) { func TestReactorVotingPowerChange(t *testing.T) {
nVals := 4 nVals := 4
logger := log.TestingLogger() logger := log.TestingLogger()
css := randConsensusNet(nVals, "consensus_voting_power_changes_test", newMockTickerFunc(true), newPersistentDummy) css := randConsensusNet(nVals, "consensus_voting_power_changes_test", newMockTickerFunc(true), newPersistentKVStore)
reactors, eventChans, eventBuses := startConsensusNet(t, css, nVals) reactors, eventChans, eventBuses := startConsensusNet(t, css, nVals)
defer stopConsensusNet(logger, reactors, eventBuses) defer stopConsensusNet(logger, reactors, eventBuses)
@@ -146,7 +254,7 @@ func TestReactorVotingPowerChange(t *testing.T) {
logger.Debug("---------------------------- Testing changing the voting power of one validator a few times") logger.Debug("---------------------------- Testing changing the voting power of one validator a few times")
val1PubKey := css[0].privValidator.GetPubKey() val1PubKey := css[0].privValidator.GetPubKey()
updateValidatorTx := dummy.MakeValSetChangeTx(val1PubKey.Bytes(), 25) updateValidatorTx := kvstore.MakeValSetChangeTx(val1PubKey.Bytes(), 25)
previousTotalVotingPower := css[0].GetRoundState().LastValidators.TotalVotingPower() previousTotalVotingPower := css[0].GetRoundState().LastValidators.TotalVotingPower()
waitForAndValidateBlock(t, nVals, activeVals, eventChans, css, updateValidatorTx) waitForAndValidateBlock(t, nVals, activeVals, eventChans, css, updateValidatorTx)
@@ -158,7 +266,7 @@ func TestReactorVotingPowerChange(t *testing.T) {
t.Fatalf("expected voting power to change (before: %d, after: %d)", previousTotalVotingPower, css[0].GetRoundState().LastValidators.TotalVotingPower()) t.Fatalf("expected voting power to change (before: %d, after: %d)", previousTotalVotingPower, css[0].GetRoundState().LastValidators.TotalVotingPower())
} }
updateValidatorTx = dummy.MakeValSetChangeTx(val1PubKey.Bytes(), 2) updateValidatorTx = kvstore.MakeValSetChangeTx(val1PubKey.Bytes(), 2)
previousTotalVotingPower = css[0].GetRoundState().LastValidators.TotalVotingPower() previousTotalVotingPower = css[0].GetRoundState().LastValidators.TotalVotingPower()
waitForAndValidateBlock(t, nVals, activeVals, eventChans, css, updateValidatorTx) waitForAndValidateBlock(t, nVals, activeVals, eventChans, css, updateValidatorTx)
@@ -170,7 +278,7 @@ func TestReactorVotingPowerChange(t *testing.T) {
t.Fatalf("expected voting power to change (before: %d, after: %d)", previousTotalVotingPower, css[0].GetRoundState().LastValidators.TotalVotingPower()) t.Fatalf("expected voting power to change (before: %d, after: %d)", previousTotalVotingPower, css[0].GetRoundState().LastValidators.TotalVotingPower())
} }
updateValidatorTx = dummy.MakeValSetChangeTx(val1PubKey.Bytes(), 26) updateValidatorTx = kvstore.MakeValSetChangeTx(val1PubKey.Bytes(), 26)
previousTotalVotingPower = css[0].GetRoundState().LastValidators.TotalVotingPower() previousTotalVotingPower = css[0].GetRoundState().LastValidators.TotalVotingPower()
waitForAndValidateBlock(t, nVals, activeVals, eventChans, css, updateValidatorTx) waitForAndValidateBlock(t, nVals, activeVals, eventChans, css, updateValidatorTx)
@@ -186,7 +294,7 @@ func TestReactorVotingPowerChange(t *testing.T) {
func TestReactorValidatorSetChanges(t *testing.T) { func TestReactorValidatorSetChanges(t *testing.T) {
nPeers := 7 nPeers := 7
nVals := 4 nVals := 4
css := randConsensusNetWithPeers(nVals, nPeers, "consensus_val_set_changes_test", newMockTickerFunc(true), newPersistentDummy) css := randConsensusNetWithPeers(nVals, nPeers, "consensus_val_set_changes_test", newMockTickerFunc(true), newPersistentKVStore)
logger := log.TestingLogger() logger := log.TestingLogger()
@@ -208,7 +316,7 @@ func TestReactorValidatorSetChanges(t *testing.T) {
logger.Info("---------------------------- Testing adding one validator") logger.Info("---------------------------- Testing adding one validator")
newValidatorPubKey1 := css[nVals].privValidator.GetPubKey() newValidatorPubKey1 := css[nVals].privValidator.GetPubKey()
newValidatorTx1 := dummy.MakeValSetChangeTx(newValidatorPubKey1.Bytes(), testMinPower) newValidatorTx1 := kvstore.MakeValSetChangeTx(newValidatorPubKey1.Bytes(), testMinPower)
// wait till everyone makes block 2 // wait till everyone makes block 2
// ensure the commit includes all validators // ensure the commit includes all validators
@@ -234,7 +342,7 @@ func TestReactorValidatorSetChanges(t *testing.T) {
logger.Info("---------------------------- Testing changing the voting power of one validator") logger.Info("---------------------------- Testing changing the voting power of one validator")
updateValidatorPubKey1 := css[nVals].privValidator.GetPubKey() updateValidatorPubKey1 := css[nVals].privValidator.GetPubKey()
updateValidatorTx1 := dummy.MakeValSetChangeTx(updateValidatorPubKey1.Bytes(), 25) updateValidatorTx1 := kvstore.MakeValSetChangeTx(updateValidatorPubKey1.Bytes(), 25)
previousTotalVotingPower := css[nVals].GetRoundState().LastValidators.TotalVotingPower() previousTotalVotingPower := css[nVals].GetRoundState().LastValidators.TotalVotingPower()
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css, updateValidatorTx1) waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css, updateValidatorTx1)
@@ -250,10 +358,10 @@ func TestReactorValidatorSetChanges(t *testing.T) {
logger.Info("---------------------------- Testing adding two validators at once") logger.Info("---------------------------- Testing adding two validators at once")
newValidatorPubKey2 := css[nVals+1].privValidator.GetPubKey() newValidatorPubKey2 := css[nVals+1].privValidator.GetPubKey()
newValidatorTx2 := dummy.MakeValSetChangeTx(newValidatorPubKey2.Bytes(), testMinPower) newValidatorTx2 := kvstore.MakeValSetChangeTx(newValidatorPubKey2.Bytes(), testMinPower)
newValidatorPubKey3 := css[nVals+2].privValidator.GetPubKey() newValidatorPubKey3 := css[nVals+2].privValidator.GetPubKey()
newValidatorTx3 := dummy.MakeValSetChangeTx(newValidatorPubKey3.Bytes(), testMinPower) newValidatorTx3 := kvstore.MakeValSetChangeTx(newValidatorPubKey3.Bytes(), testMinPower)
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css, newValidatorTx2, newValidatorTx3) waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css, newValidatorTx2, newValidatorTx3)
waitForAndValidateBlockWithTx(t, nPeers, activeVals, eventChans, css, newValidatorTx2, newValidatorTx3) waitForAndValidateBlockWithTx(t, nPeers, activeVals, eventChans, css, newValidatorTx2, newValidatorTx3)
@@ -265,8 +373,8 @@ func TestReactorValidatorSetChanges(t *testing.T) {
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
logger.Info("---------------------------- Testing removing two validators at once") logger.Info("---------------------------- Testing removing two validators at once")
removeValidatorTx2 := dummy.MakeValSetChangeTx(newValidatorPubKey2.Bytes(), 0) removeValidatorTx2 := kvstore.MakeValSetChangeTx(newValidatorPubKey2.Bytes(), 0)
removeValidatorTx3 := dummy.MakeValSetChangeTx(newValidatorPubKey3.Bytes(), 0) removeValidatorTx3 := kvstore.MakeValSetChangeTx(newValidatorPubKey3.Bytes(), 0)
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css, removeValidatorTx2, removeValidatorTx3) waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css, removeValidatorTx2, removeValidatorTx3)
waitForAndValidateBlockWithTx(t, nPeers, activeVals, eventChans, css, removeValidatorTx2, removeValidatorTx3) waitForAndValidateBlockWithTx(t, nPeers, activeVals, eventChans, css, removeValidatorTx2, removeValidatorTx3)
@@ -301,7 +409,7 @@ func waitForAndValidateBlock(t *testing.T, n int, activeVals map[string]struct{}
if !ok { if !ok {
return return
} }
newBlock := newBlockI.(types.TMEventData).Unwrap().(types.EventDataNewBlock).Block newBlock := newBlockI.(types.EventDataNewBlock).Block
css[j].Logger.Debug("waitForAndValidateBlock: Got block", "height", newBlock.Height) css[j].Logger.Debug("waitForAndValidateBlock: Got block", "height", newBlock.Height)
err := validateBlock(newBlock, activeVals) err := validateBlock(newBlock, activeVals)
assert.Nil(t, err) assert.Nil(t, err)
@@ -322,7 +430,7 @@ func waitForAndValidateBlockWithTx(t *testing.T, n int, activeVals map[string]st
if !ok { if !ok {
return return
} }
newBlock := newBlockI.(types.TMEventData).Unwrap().(types.EventDataNewBlock).Block newBlock := newBlockI.(types.EventDataNewBlock).Block
css[j].Logger.Debug("waitForAndValidateBlockWithTx: Got block", "height", newBlock.Height) css[j].Logger.Debug("waitForAndValidateBlockWithTx: Got block", "height", newBlock.Height)
err := validateBlock(newBlock, activeVals) err := validateBlock(newBlock, activeVals)
assert.Nil(t, err) assert.Nil(t, err)
@@ -332,7 +440,7 @@ func waitForAndValidateBlockWithTx(t *testing.T, n int, activeVals map[string]st
// but they should be in order. // but they should be in order.
for _, tx := range newBlock.Data.Txs { for _, tx := range newBlock.Data.Txs {
assert.EqualValues(t, txs[ntxs], tx) assert.EqualValues(t, txs[ntxs], tx)
ntxs += 1 ntxs++
} }
if ntxs == len(txs) { if ntxs == len(txs) {
@@ -354,7 +462,7 @@ func waitForBlockWithUpdatedValsAndValidateIt(t *testing.T, n int, updatedVals m
if !ok { if !ok {
return return
} }
newBlock = newBlockI.(types.TMEventData).Unwrap().(types.EventDataNewBlock).Block newBlock = newBlockI.(types.EventDataNewBlock).Block
if newBlock.LastCommit.Size() == len(updatedVals) { if newBlock.LastCommit.Size() == len(updatedVals) {
css[j].Logger.Debug("waitForBlockWithUpdatedValsAndValidateIt: Got block", "height", newBlock.Height) css[j].Logger.Debug("waitForBlockWithUpdatedValsAndValidateIt: Got block", "height", newBlock.Height)
break LOOP break LOOP

View File

@@ -2,6 +2,7 @@ package consensus
import ( import (
"bytes" "bytes"
"encoding/json"
"fmt" "fmt"
"hash/crc32" "hash/crc32"
"io" "io"
@@ -111,7 +112,7 @@ func (cs *ConsensusState) catchupReplay(csHeight int64) error {
} }
} }
if found { if found {
return fmt.Errorf("WAL should not contain #ENDHEIGHT %d.", csHeight) return fmt.Errorf("WAL should not contain #ENDHEIGHT %d", csHeight)
} }
// Search for last height marker // Search for last height marker
@@ -124,7 +125,7 @@ func (cs *ConsensusState) catchupReplay(csHeight int64) error {
return err return err
} }
if !found { if !found {
return fmt.Errorf("Cannot replay height %d. WAL does not contain #ENDHEIGHT for %d.", csHeight, csHeight-1) return fmt.Errorf("Cannot replay height %d. WAL does not contain #ENDHEIGHT for %d", csHeight, csHeight-1)
} }
defer gr.Close() // nolint: errcheck defer gr.Close() // nolint: errcheck
@@ -190,13 +191,23 @@ type Handshaker struct {
stateDB dbm.DB stateDB dbm.DB
initialState sm.State initialState sm.State
store types.BlockStore store types.BlockStore
appState json.RawMessage
logger log.Logger logger log.Logger
nBlocks int // number of blocks applied to the state nBlocks int // number of blocks applied to the state
} }
func NewHandshaker(stateDB dbm.DB, state sm.State, store types.BlockStore) *Handshaker { func NewHandshaker(stateDB dbm.DB, state sm.State,
return &Handshaker{stateDB, state, store, log.NewNopLogger(), 0} store types.BlockStore, appState json.RawMessage) *Handshaker {
return &Handshaker{
stateDB: stateDB,
initialState: state,
store: store,
appState: appState,
logger: log.NewNopLogger(),
nBlocks: 0,
}
} }
func (h *Handshaker) SetLogger(l log.Logger) { func (h *Handshaker) SetLogger(l log.Logger) {
@@ -249,9 +260,12 @@ func (h *Handshaker) ReplayBlocks(state sm.State, appHash []byte, appBlockHeight
// If appBlockHeight == 0 it means that we are at genesis and hence should send InitChain // If appBlockHeight == 0 it means that we are at genesis and hence should send InitChain
if appBlockHeight == 0 { if appBlockHeight == 0 {
validators := types.TM2PB.Validators(state.Validators) validators := types.TM2PB.Validators(state.Validators)
// TODO: get the genesis bytes (https://github.com/tendermint/tendermint/issues/1224) req := abci.RequestInitChain{
var genesisBytes []byte Validators: validators,
if _, err := proxyApp.Consensus().InitChainSync(abci.RequestInitChain{validators, genesisBytes}); err != nil { AppStateBytes: h.appState,
}
_, err := proxyApp.Consensus().InitChainSync(req)
if err != nil {
return nil, err return nil, err
} }
} }
@@ -338,7 +352,7 @@ func (h *Handshaker) replayBlocks(state sm.State, proxyApp proxy.AppConns, appBl
var err error var err error
finalBlock := storeBlockHeight finalBlock := storeBlockHeight
if mutateState { if mutateState {
finalBlock -= 1 finalBlock--
} }
for i := appBlockHeight + 1; i <= finalBlock; i++ { for i := appBlockHeight + 1; i <= finalBlock; i++ {
h.logger.Info("Applying block", "height", i) h.logger.Info("Applying block", "height", i)
@@ -348,7 +362,7 @@ func (h *Handshaker) replayBlocks(state sm.State, proxyApp proxy.AppConns, appBl
return nil, err return nil, err
} }
h.nBlocks += 1 h.nBlocks++
} }
if mutateState { if mutateState {
@@ -376,7 +390,7 @@ func (h *Handshaker) replayBlock(state sm.State, height int64, proxyApp proxy.Ap
return sm.State{}, err return sm.State{}, err
} }
h.nBlocks += 1 h.nBlocks++
return state, nil return state, nil
} }
@@ -415,7 +429,7 @@ type mockProxyApp struct {
func (mock *mockProxyApp) DeliverTx(tx []byte) abci.ResponseDeliverTx { func (mock *mockProxyApp) DeliverTx(tx []byte) abci.ResponseDeliverTx {
r := mock.abciResponses.DeliverTx[mock.txCount] r := mock.abciResponses.DeliverTx[mock.txCount]
mock.txCount += 1 mock.txCount++
return *r return *r
} }

View File

@@ -87,9 +87,9 @@ func (cs *ConsensusState) ReplayFile(file string, console bool) error {
} }
if nextN > 0 { if nextN > 0 {
nextN -= 1 nextN--
} }
pb.count += 1 pb.count++
} }
return nil return nil
} }
@@ -153,7 +153,7 @@ func (pb *playback) replayReset(count int, newStepCh chan interface{}) error {
if err := pb.cs.readReplayMessage(msg, newStepCh); err != nil { if err := pb.cs.readReplayMessage(msg, newStepCh); err != nil {
return err return err
} }
pb.count += 1 pb.count++
} }
return nil return nil
} }
@@ -197,14 +197,13 @@ func (pb *playback) replayConsoleLoop() int {
if len(tokens) == 1 { if len(tokens) == 1 {
return 0 return 0
} else { }
i, err := strconv.Atoi(tokens[1]) i, err := strconv.Atoi(tokens[1])
if err != nil { if err != nil {
fmt.Println("next takes an integer argument") fmt.Println("next takes an integer argument")
} else { } else {
return i return i
} }
}
case "back": case "back":
// "back" -> go back one message // "back" -> go back one message
@@ -287,14 +286,19 @@ func newConsensusStateForReplay(config cfg.BaseConfig, csConfig *cfg.ConsensusCo
// Get State // Get State
stateDB := dbm.NewDB("state", dbType, config.DBDir()) stateDB := dbm.NewDB("state", dbType, config.DBDir())
state, err := sm.MakeGenesisStateFromFile(config.GenesisFile()) gdoc, err := sm.MakeGenesisDocFromFile(config.GenesisFile())
if err != nil {
cmn.Exit(err.Error())
}
state, err := sm.MakeGenesisState(gdoc)
if err != nil { if err != nil {
cmn.Exit(err.Error()) cmn.Exit(err.Error())
} }
// Create proxyAppConn connection (consensus, mempool, query) // Create proxyAppConn connection (consensus, mempool, query)
clientCreator := proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()) clientCreator := proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir())
proxyApp := proxy.NewAppConns(clientCreator, NewHandshaker(stateDB, state, blockStore)) proxyApp := proxy.NewAppConns(clientCreator,
NewHandshaker(stateDB, state, blockStore, gdoc.AppState()))
err = proxyApp.Start() err = proxyApp.Start()
if err != nil { if err != nil {
cmn.Exit(cmn.Fmt("Error starting proxy app conns: %v", err)) cmn.Exit(cmn.Fmt("Error starting proxy app conns: %v", err))

View File

@@ -15,10 +15,9 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/tendermint/abci/example/dummy" "github.com/tendermint/abci/example/kvstore"
abci "github.com/tendermint/abci/types" abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto" "github.com/tendermint/go-crypto"
wire "github.com/tendermint/go-wire"
auto "github.com/tendermint/tmlibs/autofile" auto "github.com/tendermint/tmlibs/autofile"
cmn "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db" dbm "github.com/tendermint/tmlibs/db"
@@ -27,6 +26,7 @@ import (
"github.com/tendermint/tendermint/proxy" "github.com/tendermint/tendermint/proxy"
sm "github.com/tendermint/tendermint/state" sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
pvm "github.com/tendermint/tendermint/types/priv_validator"
"github.com/tendermint/tmlibs/log" "github.com/tendermint/tmlibs/log"
) )
@@ -55,12 +55,12 @@ func startNewConsensusStateAndWaitForBlock(t *testing.T, lastBlockHeight int64,
logger := log.TestingLogger() logger := log.TestingLogger()
state, _ := sm.LoadStateFromDBOrGenesisFile(stateDB, consensusReplayConfig.GenesisFile()) state, _ := sm.LoadStateFromDBOrGenesisFile(stateDB, consensusReplayConfig.GenesisFile())
privValidator := loadPrivValidator(consensusReplayConfig) privValidator := loadPrivValidator(consensusReplayConfig)
cs := newConsensusStateWithConfigAndBlockStore(consensusReplayConfig, state, privValidator, dummy.NewDummyApplication(), blockDB) cs := newConsensusStateWithConfigAndBlockStore(consensusReplayConfig, state, privValidator, kvstore.NewKVStoreApplication(), blockDB)
cs.SetLogger(logger) cs.SetLogger(logger)
bytes, _ := ioutil.ReadFile(cs.config.WalFile()) bytes, _ := ioutil.ReadFile(cs.config.WalFile())
// fmt.Printf("====== WAL: \n\r%s\n", bytes) // fmt.Printf("====== WAL: \n\r%s\n", bytes)
t.Logf("====== WAL: \n\r%s\n", bytes) t.Logf("====== WAL: \n\r%X\n", bytes)
err := cs.Start() err := cs.Start()
require.NoError(t, err) require.NoError(t, err)
@@ -141,7 +141,7 @@ LOOP:
state, _ := sm.MakeGenesisStateFromFile(consensusReplayConfig.GenesisFile()) state, _ := sm.MakeGenesisStateFromFile(consensusReplayConfig.GenesisFile())
privValidator := loadPrivValidator(consensusReplayConfig) privValidator := loadPrivValidator(consensusReplayConfig)
blockDB := dbm.NewMemDB() blockDB := dbm.NewMemDB()
cs := newConsensusStateWithConfigAndBlockStore(consensusReplayConfig, state, privValidator, dummy.NewDummyApplication(), blockDB) cs := newConsensusStateWithConfigAndBlockStore(consensusReplayConfig, state, privValidator, kvstore.NewKVStoreApplication(), blockDB)
cs.SetLogger(logger) cs.SetLogger(logger)
// start sending transactions // start sending transactions
@@ -325,7 +325,7 @@ func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) {
walFile := tempWALWithData(walBody) walFile := tempWALWithData(walBody)
config.Consensus.SetWalFile(walFile) config.Consensus.SetWalFile(walFile)
privVal := types.LoadPrivValidatorFS(config.PrivValidatorFile()) privVal := pvm.LoadFilePV(config.PrivValidatorFile())
wal, err := NewWAL(walFile, false) wal, err := NewWAL(walFile, false)
if err != nil { if err != nil {
@@ -351,8 +351,8 @@ func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) {
latestAppHash := state.AppHash latestAppHash := state.AppHash
// make a new client creator // make a new client creator
dummyApp := dummy.NewPersistentDummyApplication(path.Join(config.DBDir(), "2")) kvstoreApp := kvstore.NewPersistentKVStoreApplication(path.Join(config.DBDir(), "2"))
clientCreator2 := proxy.NewLocalClientCreator(dummyApp) clientCreator2 := proxy.NewLocalClientCreator(kvstoreApp)
if nBlocks > 0 { if nBlocks > 0 {
// run nBlocks against a new client to build up the app state. // run nBlocks against a new client to build up the app state.
// use a throwaway tendermint state // use a throwaway tendermint state
@@ -362,7 +362,7 @@ func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) {
} }
// now start the app using the handshake - it should sync // now start the app using the handshake - it should sync
handshaker := NewHandshaker(stateDB, state, store) handshaker := NewHandshaker(stateDB, state, store, nil)
proxyApp := proxy.NewAppConns(clientCreator2, handshaker) proxyApp := proxy.NewAppConns(clientCreator2, handshaker)
if err := proxyApp.Start(); err != nil { if err := proxyApp.Start(); err != nil {
t.Fatalf("Error starting proxy app connections: %v", err) t.Fatalf("Error starting proxy app connections: %v", err)
@@ -382,9 +382,9 @@ func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) {
expectedBlocksToSync := NUM_BLOCKS - nBlocks expectedBlocksToSync := NUM_BLOCKS - nBlocks
if nBlocks == NUM_BLOCKS && mode > 0 { if nBlocks == NUM_BLOCKS && mode > 0 {
expectedBlocksToSync += 1 expectedBlocksToSync++
} else if nBlocks > 0 && mode == 1 { } else if nBlocks > 0 && mode == 1 {
expectedBlocksToSync += 1 expectedBlocksToSync++
} }
if handshaker.NBlocks() != expectedBlocksToSync { if handshaker.NBlocks() != expectedBlocksToSync {
@@ -412,9 +412,9 @@ func buildAppStateFromChain(proxyApp proxy.AppConns, stateDB dbm.DB,
} }
defer proxyApp.Stop() defer proxyApp.Stop()
validators := types.TM2PB.Validators(state.Validators)
// TODO: get the genesis bytes (https://github.com/tendermint/tendermint/issues/1224) // TODO: get the genesis bytes (https://github.com/tendermint/tendermint/issues/1224)
var genesisBytes []byte var genesisBytes []byte
validators := types.TM2PB.Validators(state.Validators)
if _, err := proxyApp.Consensus().InitChainSync(abci.RequestInitChain{validators, genesisBytes}); err != nil { if _, err := proxyApp.Consensus().InitChainSync(abci.RequestInitChain{validators, genesisBytes}); err != nil {
panic(err) panic(err)
} }
@@ -432,7 +432,7 @@ func buildAppStateFromChain(proxyApp proxy.AppConns, stateDB dbm.DB,
} }
if mode == 2 { if mode == 2 {
// update the dummy height and apphash // update the kvstore height and apphash
// as if we ran commit but not // as if we ran commit but not
state = applyBlock(stateDB, state, chain[nBlocks-1], proxyApp) state = applyBlock(stateDB, state, chain[nBlocks-1], proxyApp)
} }
@@ -442,16 +442,16 @@ func buildAppStateFromChain(proxyApp proxy.AppConns, stateDB dbm.DB,
func buildTMStateFromChain(config *cfg.Config, stateDB dbm.DB, state sm.State, chain []*types.Block, mode uint) sm.State { func buildTMStateFromChain(config *cfg.Config, stateDB dbm.DB, state sm.State, chain []*types.Block, mode uint) sm.State {
// run the whole chain against this client to build up the tendermint state // run the whole chain against this client to build up the tendermint state
clientCreator := proxy.NewLocalClientCreator(dummy.NewPersistentDummyApplication(path.Join(config.DBDir(), "1"))) clientCreator := proxy.NewLocalClientCreator(kvstore.NewPersistentKVStoreApplication(path.Join(config.DBDir(), "1")))
proxyApp := proxy.NewAppConns(clientCreator, nil) // sm.NewHandshaker(config, state, store, ReplayLastBlock)) proxyApp := proxy.NewAppConns(clientCreator, nil) // sm.NewHandshaker(config, state, store, ReplayLastBlock))
if err := proxyApp.Start(); err != nil { if err := proxyApp.Start(); err != nil {
panic(err) panic(err)
} }
defer proxyApp.Stop() defer proxyApp.Stop()
validators := types.TM2PB.Validators(state.Validators)
// TODO: get the genesis bytes (https://github.com/tendermint/tendermint/issues/1224) // TODO: get the genesis bytes (https://github.com/tendermint/tendermint/issues/1224)
var genesisBytes []byte var genesisBytes []byte
validators := types.TM2PB.Validators(state.Validators)
if _, err := proxyApp.Consensus().InitChainSync(abci.RequestInitChain{validators, genesisBytes}); err != nil { if _, err := proxyApp.Consensus().InitChainSync(abci.RequestInitChain{validators, genesisBytes}); err != nil {
panic(err) panic(err)
} }
@@ -519,8 +519,8 @@ func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) {
case EndHeightMessage: case EndHeightMessage:
// if its not the first one, we have a full block // if its not the first one, we have a full block
if thisBlockParts != nil { if thisBlockParts != nil {
var n int var block = new(types.Block)
block := wire.ReadBinary(&types.Block{}, thisBlockParts.GetReader(), 0, &n, &err).(*types.Block) _, err = cdc.UnmarshalBinaryReader(thisBlockParts.GetReader(), block, 0)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@@ -533,7 +533,7 @@ func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) {
} }
blocks = append(blocks, block) blocks = append(blocks, block)
commits = append(commits, thisBlockCommit) commits = append(commits, thisBlockCommit)
height += 1 height++
} }
case *types.PartSetHeader: case *types.PartSetHeader:
thisBlockParts = types.NewPartSetFromHeader(*p) thisBlockParts = types.NewPartSetFromHeader(*p)
@@ -552,8 +552,8 @@ func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) {
} }
} }
// grab the last block too // grab the last block too
var n int var block = new(types.Block)
block := wire.ReadBinary(&types.Block{}, thisBlockParts.GetReader(), 0, &n, &err).(*types.Block) _, err = cdc.UnmarshalBinaryReader(thisBlockParts.GetReader(), block, 0)
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@@ -10,8 +10,6 @@ import (
"time" "time"
fail "github.com/ebuchman/fail-test" fail "github.com/ebuchman/fail-test"
wire "github.com/tendermint/go-wire"
cmn "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/log" "github.com/tendermint/tmlibs/log"
@@ -170,18 +168,23 @@ func (cs *ConsensusState) GetState() sm.State {
return cs.state.Copy() return cs.state.Copy()
} }
// GetRoundState returns a copy of the internal consensus state. // GetRoundState returns a shallow copy of the internal consensus state.
func (cs *ConsensusState) GetRoundState() *cstypes.RoundState { func (cs *ConsensusState) GetRoundState() *cstypes.RoundState {
cs.mtx.Lock() cs.mtx.Lock()
defer cs.mtx.Unlock() defer cs.mtx.Unlock()
return cs.getRoundState()
}
func (cs *ConsensusState) getRoundState() *cstypes.RoundState {
rs := cs.RoundState // copy rs := cs.RoundState // copy
return &rs return &rs
} }
// GetRoundStateJSON returns a json of RoundState, marshalled using go-amino.
func (cs *ConsensusState) GetRoundStateJSON() ([]byte, error) {
cs.mtx.Lock()
defer cs.mtx.Unlock()
return cdc.MarshalJSON(cs.RoundState)
}
// GetValidators returns a copy of the current validators. // GetValidators returns a copy of the current validators.
func (cs *ConsensusState) GetValidators() (int64, []*types.Validator) { func (cs *ConsensusState) GetValidators() (int64, []*types.Validator) {
cs.mtx.Lock() cs.mtx.Lock()
@@ -477,6 +480,9 @@ func (cs *ConsensusState) updateToState(state sm.State) {
cs.LockedRound = 0 cs.LockedRound = 0
cs.LockedBlock = nil cs.LockedBlock = nil
cs.LockedBlockParts = nil cs.LockedBlockParts = nil
cs.ValidRound = 0
cs.ValidBlock = nil
cs.ValidBlockParts = nil
cs.Votes = cstypes.NewHeightVoteSet(state.ChainID, height, validators) cs.Votes = cstypes.NewHeightVoteSet(state.ChainID, height, validators)
cs.CommitRound = -1 cs.CommitRound = -1
cs.LastCommit = lastPrecommits cs.LastCommit = lastPrecommits
@@ -491,7 +497,7 @@ func (cs *ConsensusState) updateToState(state sm.State) {
func (cs *ConsensusState) newStep() { func (cs *ConsensusState) newStep() {
rs := cs.RoundStateEvent() rs := cs.RoundStateEvent()
cs.wal.Save(rs) cs.wal.Save(rs)
cs.nSteps += 1 cs.nSteps++
// newStep is called by updateToStep in NewConsensusState before the eventBus is set! // newStep is called by updateToStep in NewConsensusState before the eventBus is set!
if cs.eventBus != nil { if cs.eventBus != nil {
cs.eventBus.PublishEventNewRoundStep(rs) cs.eventBus.PublishEventNewRoundStep(rs)
@@ -580,6 +586,10 @@ func (cs *ConsensusState) handleMsg(mi msgInfo) {
err := cs.tryAddVote(msg.Vote, peerID) err := cs.tryAddVote(msg.Vote, peerID)
if err == ErrAddingVote { if err == ErrAddingVote {
// TODO: punish peer // TODO: punish peer
// We probably don't want to stop the peer here. The vote does not
// necessarily comes from a malicious peer but can be just broadcasted by
// a typical peer.
// https://github.com/tendermint/tendermint/issues/1281
} }
// NOTE: the vote is broadcast to peers by the reactor listening // NOTE: the vote is broadcast to peers by the reactor listening
@@ -713,11 +723,7 @@ func (cs *ConsensusState) needProofBlock(height int64) bool {
func (cs *ConsensusState) proposalHeartbeat(height int64, round int) { func (cs *ConsensusState) proposalHeartbeat(height int64, round int) {
counter := 0 counter := 0
addr := cs.privValidator.GetAddress() addr := cs.privValidator.GetAddress()
valIndex, v := cs.Validators.GetByAddress(addr) valIndex, _ := cs.Validators.GetByAddress(addr)
if v == nil {
// not a validator
valIndex = -1
}
chainID := cs.state.ChainID chainID := cs.state.ChainID
for { for {
rs := cs.GetRoundState() rs := cs.GetRoundState()
@@ -734,7 +740,7 @@ func (cs *ConsensusState) proposalHeartbeat(height int64, round int) {
} }
cs.privValidator.SignHeartbeat(chainID, heartbeat) cs.privValidator.SignHeartbeat(chainID, heartbeat)
cs.eventBus.PublishEventProposalHeartbeat(types.EventDataProposalHeartbeat{heartbeat}) cs.eventBus.PublishEventProposalHeartbeat(types.EventDataProposalHeartbeat{heartbeat})
counter += 1 counter++
time.Sleep(proposalHeartbeatIntervalSeconds * time.Second) time.Sleep(proposalHeartbeatIntervalSeconds * time.Second)
} }
} }
@@ -773,7 +779,7 @@ func (cs *ConsensusState) enterPropose(height int64, round int) {
// if not a validator, we're done // if not a validator, we're done
if !cs.Validators.HasAddress(cs.privValidator.GetAddress()) { if !cs.Validators.HasAddress(cs.privValidator.GetAddress()) {
cs.Logger.Debug("This node is not a validator") cs.Logger.Debug("This node is not a validator", "addr", cs.privValidator.GetAddress(), "vals", cs.Validators)
return return
} }
cs.Logger.Debug("This node is a validator") cs.Logger.Debug("This node is a validator")
@@ -798,6 +804,9 @@ func (cs *ConsensusState) defaultDecideProposal(height int64, round int) {
if cs.LockedBlock != nil { if cs.LockedBlock != nil {
// If we're locked onto a block, just choose that. // If we're locked onto a block, just choose that.
block, blockParts = cs.LockedBlock, cs.LockedBlockParts block, blockParts = cs.LockedBlock, cs.LockedBlockParts
} else if cs.ValidBlock != nil {
// If there is valid block, choose that.
block, blockParts = cs.ValidBlock, cs.ValidBlockParts
} else { } else {
// Create a new proposal block from state/txs from the mempool. // Create a new proposal block from state/txs from the mempool.
block, blockParts = cs.createProposalBlock() block, blockParts = cs.createProposalBlock()
@@ -842,10 +851,10 @@ func (cs *ConsensusState) isProposalComplete() bool {
// make sure we have the prevotes from it too // make sure we have the prevotes from it too
if cs.Proposal.POLRound < 0 { if cs.Proposal.POLRound < 0 {
return true return true
} else { }
// if this is false the proposer is lying or we haven't received the POL yet // if this is false the proposer is lying or we haven't received the POL yet
return cs.Votes.Prevotes(cs.Proposal.POLRound).HasTwoThirdsMajority() return cs.Votes.Prevotes(cs.Proposal.POLRound).HasTwoThirdsMajority()
}
} }
// Create the next block to propose and return it. // Create the next block to propose and return it.
@@ -1263,7 +1272,7 @@ func (cs *ConsensusState) defaultSetProposal(proposal *types.Proposal) error {
} }
// Verify signature // Verify signature
if !cs.Validators.GetProposer().PubKey.VerifyBytes(types.SignBytes(cs.state.ChainID, proposal), proposal.Signature) { if !cs.Validators.GetProposer().PubKey.VerifyBytes(proposal.SignBytes(cs.state.ChainID), proposal.Signature) {
return ErrInvalidProposalSignature return ErrInvalidProposalSignature
} }
@@ -1291,10 +1300,10 @@ func (cs *ConsensusState) addProposalBlockPart(height int64, part *types.Part, v
} }
if added && cs.ProposalBlockParts.IsComplete() { if added && cs.ProposalBlockParts.IsComplete() {
// Added and completed! // Added and completed!
var n int _, err = cdc.UnmarshalBinaryReader(cs.ProposalBlockParts.GetReader(), &cs.ProposalBlock, int64(cs.state.ConsensusParams.BlockSize.MaxBytes))
var err error if err != nil {
cs.ProposalBlock = wire.ReadBinary(&types.Block{}, cs.ProposalBlockParts.GetReader(), return true, err
cs.state.ConsensusParams.BlockSize.MaxBytes, &n, &err).(*types.Block) }
// NOTE: it's possible to receive complete proposal blocks for future rounds without having the proposal // NOTE: it's possible to receive complete proposal blocks for future rounds without having the proposal
cs.Logger.Info("Received complete proposal block", "height", cs.ProposalBlock.Height, "hash", cs.ProposalBlock.Hash()) cs.Logger.Info("Received complete proposal block", "height", cs.ProposalBlock.Height, "hash", cs.ProposalBlock.Hash())
if cs.Step == cstypes.RoundStepPropose && cs.isProposalComplete() { if cs.Step == cstypes.RoundStepPropose && cs.isProposalComplete() {
@@ -1304,7 +1313,7 @@ func (cs *ConsensusState) addProposalBlockPart(height int64, part *types.Part, v
// If we're waiting on the proposal block... // If we're waiting on the proposal block...
cs.tryFinalizeCommit(height) cs.tryFinalizeCommit(height)
} }
return true, err return true, nil
} }
return added, nil return added, nil
} }
@@ -1349,7 +1358,10 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
return added, ErrVoteHeightMismatch return added, ErrVoteHeightMismatch
} }
added, err = cs.LastCommit.AddVote(vote) added, err = cs.LastCommit.AddVote(vote)
if added { if !added {
return added, err
}
cs.Logger.Info(cmn.Fmt("Added to lastPrecommits: %v", cs.LastCommit.StringShort())) cs.Logger.Info(cmn.Fmt("Added to lastPrecommits: %v", cs.LastCommit.StringShort()))
cs.eventBus.PublishEventVote(types.EventDataVote{vote}) cs.eventBus.PublishEventVote(types.EventDataVote{vote})
@@ -1359,29 +1371,38 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
// cs.scheduleTimeout(time.Duration(0), cs.Height, 0, cstypes.RoundStepNewHeight) // cs.scheduleTimeout(time.Duration(0), cs.Height, 0, cstypes.RoundStepNewHeight)
cs.enterNewRound(cs.Height, 0) cs.enterNewRound(cs.Height, 0)
} }
}
return return
} }
// A prevote/precommit for this height? // Height mismatch is ignored.
if vote.Height == cs.Height { // Not necessarily a bad peer, but not favourable behaviour.
if vote.Height != cs.Height {
err = ErrVoteHeightMismatch
cs.Logger.Info("Vote ignored and not added", "voteHeight", vote.Height, "csHeight", cs.Height, "err", err)
return
}
height := cs.Height height := cs.Height
added, err = cs.Votes.AddVote(vote, peerID) added, err = cs.Votes.AddVote(vote, peerID)
if added { if !added {
// Either duplicate, or error upon cs.Votes.AddByIndex()
return
}
cs.eventBus.PublishEventVote(types.EventDataVote{vote}) cs.eventBus.PublishEventVote(types.EventDataVote{vote})
switch vote.Type { switch vote.Type {
case types.VoteTypePrevote: case types.VoteTypePrevote:
prevotes := cs.Votes.Prevotes(vote.Round) prevotes := cs.Votes.Prevotes(vote.Round)
cs.Logger.Info("Added to prevote", "vote", vote, "prevotes", prevotes.StringShort()) cs.Logger.Info("Added to prevote", "vote", vote, "prevotes", prevotes.StringShort())
blockID, ok := prevotes.TwoThirdsMajority()
// First, unlock if prevotes is a valid POL. // First, unlock if prevotes is a valid POL.
// >> lockRound < POLRound <= unlockOrChangeLockRound (see spec) // >> lockRound < POLRound <= unlockOrChangeLockRound (see spec)
// NOTE: If (lockRound < POLRound) but !(POLRound <= unlockOrChangeLockRound), // NOTE: If (lockRound < POLRound) but !(POLRound <= unlockOrChangeLockRound),
// we'll still enterNewRound(H,vote.R) and enterPrecommit(H,vote.R) to process it // we'll still enterNewRound(H,vote.R) and enterPrecommit(H,vote.R) to process it
// there. // there.
if (cs.LockedBlock != nil) && (cs.LockedRound < vote.Round) && (vote.Round <= cs.Round) { if (cs.LockedBlock != nil) && (cs.LockedRound < vote.Round) && (vote.Round <= cs.Round) {
blockID, ok := prevotes.TwoThirdsMajority()
if ok && !cs.LockedBlock.HashesTo(blockID.Hash) { if ok && !cs.LockedBlock.HashesTo(blockID.Hash) {
cs.Logger.Info("Unlocking because of POL.", "lockedRound", cs.LockedRound, "POLRound", vote.Round) cs.Logger.Info("Unlocking because of POL.", "lockedRound", cs.LockedRound, "POLRound", vote.Round)
cs.LockedRound = 0 cs.LockedRound = 0
@@ -1390,6 +1411,18 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
cs.eventBus.PublishEventUnlock(cs.RoundStateEvent()) cs.eventBus.PublishEventUnlock(cs.RoundStateEvent())
} }
} }
// Update ValidBlock
if ok && !blockID.IsZero() && !cs.ValidBlock.HashesTo(blockID.Hash) && vote.Round > cs.ValidRound {
// update valid value
if cs.ProposalBlock.HashesTo(blockID.Hash) {
cs.ValidRound = vote.Round
cs.ValidBlock = cs.ProposalBlock
cs.ValidBlockParts = cs.ProposalBlockParts
}
//TODO: We might want to update ValidBlock also in case we don't have that block yet,
// and obtain the required block using gossiping
}
if cs.Round <= vote.Round && prevotes.HasTwoThirdsAny() { if cs.Round <= vote.Round && prevotes.HasTwoThirdsAny() {
// Round-skip over to PrevoteWait or goto Precommit. // Round-skip over to PrevoteWait or goto Precommit.
cs.enterNewRound(height, vote.Round) // if the vote is ahead of us cs.enterNewRound(height, vote.Round) // if the vote is ahead of us
@@ -1431,17 +1464,9 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
cs.enterPrecommitWait(height, vote.Round) cs.enterPrecommitWait(height, vote.Round)
} }
default: default:
cmn.PanicSanity(cmn.Fmt("Unexpected vote type %X", vote.Type)) // Should not happen. panic(cmn.Fmt("Unexpected vote type %X", vote.Type)) // go-wire should prevent this.
}
}
// Either duplicate, or error upon cs.Votes.AddByIndex()
return
} else {
err = ErrVoteHeightMismatch
} }
// Height mismatch, bad peer?
cs.Logger.Info("Vote ignored and not added", "voteHeight", vote.Height, "csHeight", cs.Height, "err", err)
return return
} }
@@ -1472,13 +1497,12 @@ func (cs *ConsensusState) signAddVote(type_ byte, hash []byte, header types.Part
cs.sendInternalMessage(msgInfo{&VoteMessage{vote}, ""}) cs.sendInternalMessage(msgInfo{&VoteMessage{vote}, ""})
cs.Logger.Info("Signed and pushed vote", "height", cs.Height, "round", cs.Round, "vote", vote, "err", err) cs.Logger.Info("Signed and pushed vote", "height", cs.Height, "round", cs.Round, "vote", vote, "err", err)
return vote return vote
} else { }
//if !cs.replayMode { //if !cs.replayMode {
cs.Logger.Error("Error signing vote", "height", cs.Height, "round", cs.Round, "vote", vote, "err", err) cs.Logger.Error("Error signing vote", "height", cs.Height, "round", cs.Round, "vote", vote, "err", err)
//} //}
return nil return nil
} }
}
//--------------------------------------------------------- //---------------------------------------------------------

View File

@@ -261,7 +261,7 @@ func TestStateFullRound1(t *testing.T) {
// grab proposal // grab proposal
re := <-propCh re := <-propCh
propBlockHash := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState).ProposalBlock.Hash() propBlockHash := re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState).ProposalBlock.Hash()
<-voteCh // wait for prevote <-voteCh // wait for prevote
validatePrevote(t, cs, round, vss[0], propBlockHash) validatePrevote(t, cs, round, vss[0], propBlockHash)
@@ -356,7 +356,7 @@ func TestStateLockNoPOL(t *testing.T) {
cs1.startRoutines(0) cs1.startRoutines(0)
re := <-proposalCh re := <-proposalCh
rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState) rs := re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
theBlockHash := rs.ProposalBlock.Hash() theBlockHash := rs.ProposalBlock.Hash()
<-voteCh // prevote <-voteCh // prevote
@@ -396,7 +396,7 @@ func TestStateLockNoPOL(t *testing.T) {
// now we're on a new round and not the proposer, so wait for timeout // now we're on a new round and not the proposer, so wait for timeout
re = <-timeoutProposeCh re = <-timeoutProposeCh
rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState) rs = re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
if rs.ProposalBlock != nil { if rs.ProposalBlock != nil {
panic("Expected proposal block to be nil") panic("Expected proposal block to be nil")
@@ -409,7 +409,7 @@ func TestStateLockNoPOL(t *testing.T) {
validatePrevote(t, cs1, 1, vss[0], rs.LockedBlock.Hash()) validatePrevote(t, cs1, 1, vss[0], rs.LockedBlock.Hash())
// add a conflicting prevote from the other validator // add a conflicting prevote from the other validator
signAddVotes(cs1, types.VoteTypePrevote, hash, rs.ProposalBlock.MakePartSet(partSize).Header(), vs2) signAddVotes(cs1, types.VoteTypePrevote, hash, rs.LockedBlock.MakePartSet(partSize).Header(), vs2)
<-voteCh <-voteCh
// now we're going to enter prevote again, but with invalid args // now we're going to enter prevote again, but with invalid args
@@ -424,7 +424,7 @@ func TestStateLockNoPOL(t *testing.T) {
// add conflicting precommit from vs2 // add conflicting precommit from vs2
// NOTE: in practice we should never get to a point where there are precommits for different blocks at the same round // NOTE: in practice we should never get to a point where there are precommits for different blocks at the same round
signAddVotes(cs1, types.VoteTypePrecommit, hash, rs.ProposalBlock.MakePartSet(partSize).Header(), vs2) signAddVotes(cs1, types.VoteTypePrecommit, hash, rs.LockedBlock.MakePartSet(partSize).Header(), vs2)
<-voteCh <-voteCh
// (note we're entering precommit for a second time this round, but with invalid args // (note we're entering precommit for a second time this round, but with invalid args
@@ -440,7 +440,7 @@ func TestStateLockNoPOL(t *testing.T) {
incrementRound(vs2) incrementRound(vs2)
re = <-proposalCh re = <-proposalCh
rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState) rs = re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
// now we're on a new round and are the proposer // now we're on a new round and are the proposer
if !bytes.Equal(rs.ProposalBlock.Hash(), rs.LockedBlock.Hash()) { if !bytes.Equal(rs.ProposalBlock.Hash(), rs.LockedBlock.Hash()) {
@@ -529,7 +529,7 @@ func TestStateLockPOLRelock(t *testing.T) {
<-newRoundCh <-newRoundCh
re := <-proposalCh re := <-proposalCh
rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState) rs := re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
theBlockHash := rs.ProposalBlock.Hash() theBlockHash := rs.ProposalBlock.Hash()
<-voteCh // prevote <-voteCh // prevote
@@ -605,9 +605,9 @@ func TestStateLockPOLRelock(t *testing.T) {
discardFromChan(voteCh, 2) discardFromChan(voteCh, 2)
be := <-newBlockCh be := <-newBlockCh
b := be.(types.TMEventData).Unwrap().(types.EventDataNewBlockHeader) b := be.(types.EventDataNewBlockHeader)
re = <-newRoundCh re = <-newRoundCh
rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState) rs = re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
if rs.Height != 2 { if rs.Height != 2 {
panic("Expected height to increment") panic("Expected height to increment")
} }
@@ -643,7 +643,7 @@ func TestStateLockPOLUnlock(t *testing.T) {
startTestRound(cs1, cs1.Height, 0) startTestRound(cs1, cs1.Height, 0)
<-newRoundCh <-newRoundCh
re := <-proposalCh re := <-proposalCh
rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState) rs := re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
theBlockHash := rs.ProposalBlock.Hash() theBlockHash := rs.ProposalBlock.Hash()
<-voteCh // prevote <-voteCh // prevote
@@ -669,7 +669,7 @@ func TestStateLockPOLUnlock(t *testing.T) {
// timeout to new round // timeout to new round
re = <-timeoutWaitCh re = <-timeoutWaitCh
rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState) rs = re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
lockedBlockHash := rs.LockedBlock.Hash() lockedBlockHash := rs.LockedBlock.Hash()
//XXX: this isnt guaranteed to get there before the timeoutPropose ... //XXX: this isnt guaranteed to get there before the timeoutPropose ...
@@ -731,7 +731,7 @@ func TestStateLockPOLSafety1(t *testing.T) {
startTestRound(cs1, cs1.Height, 0) startTestRound(cs1, cs1.Height, 0)
<-newRoundCh <-newRoundCh
re := <-proposalCh re := <-proposalCh
rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState) rs := re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
propBlock := rs.ProposalBlock propBlock := rs.ProposalBlock
<-voteCh // prevote <-voteCh // prevote
@@ -781,7 +781,7 @@ func TestStateLockPOLSafety1(t *testing.T) {
re = <-proposalCh re = <-proposalCh
} }
rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState) rs = re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
if rs.LockedBlock != nil { if rs.LockedBlock != nil {
panic("we should not be locked!") panic("we should not be locked!")
@@ -1033,7 +1033,7 @@ func TestStateHalt1(t *testing.T) {
startTestRound(cs1, cs1.Height, 0) startTestRound(cs1, cs1.Height, 0)
<-newRoundCh <-newRoundCh
re := <-proposalCh re := <-proposalCh
rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState) rs := re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
propBlock := rs.ProposalBlock propBlock := rs.ProposalBlock
propBlockParts := propBlock.MakePartSet(partSize) propBlockParts := propBlock.MakePartSet(partSize)
@@ -1056,7 +1056,7 @@ func TestStateHalt1(t *testing.T) {
// timeout to new round // timeout to new round
<-timeoutWaitCh <-timeoutWaitCh
re = <-newRoundCh re = <-newRoundCh
rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState) rs = re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
t.Log("### ONTO ROUND 1") t.Log("### ONTO ROUND 1")
/*Round2 /*Round2
@@ -1074,7 +1074,7 @@ func TestStateHalt1(t *testing.T) {
// receiving that precommit should take us straight to commit // receiving that precommit should take us straight to commit
<-newBlockCh <-newBlockCh
re = <-newRoundCh re = <-newRoundCh
rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState) rs = re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
if rs.Height != 2 { if rs.Height != 2 {
panic("expected height to increment") panic("expected height to increment")

View File

@@ -1,6 +1,7 @@
package types package types
import ( import (
"errors"
"fmt" "fmt"
"strings" "strings"
"sync" "sync"
@@ -15,6 +16,10 @@ type RoundVoteSet struct {
Precommits *types.VoteSet Precommits *types.VoteSet
} }
var (
GotVoteFromUnwantedRoundError = errors.New("Peer has sent a vote that does not match our round for more than one round")
)
/* /*
Keeps track of all VoteSets from round 0 to round 'round'. Keeps track of all VoteSets from round 0 to round 'round'.
@@ -117,10 +122,8 @@ func (hvs *HeightVoteSet) AddVote(vote *types.Vote, peerID p2p.ID) (added bool,
voteSet = hvs.getVoteSet(vote.Round, vote.Type) voteSet = hvs.getVoteSet(vote.Round, vote.Type)
hvs.peerCatchupRounds[peerID] = append(rndz, vote.Round) hvs.peerCatchupRounds[peerID] = append(rndz, vote.Round)
} else { } else {
// Peer has sent a vote that does not match our round, // punish peer
// for more than one round. Bad peer! err = GotVoteFromUnwantedRoundError
// TODO punish peer.
// log.Warn("Deal with peer giving votes from unwanted rounds")
return return
} }
} }
@@ -218,5 +221,5 @@ func (hvs *HeightVoteSet) SetPeerMaj23(round int, type_ byte, peerID p2p.ID, blo
if voteSet == nil { if voteSet == nil {
return nil // something we don't know about yet return nil // something we don't know about yet
} }
return voteSet.SetPeerMaj23(peerID, blockID) return voteSet.SetPeerMaj23(types.P2PID(peerID), blockID)
} }

View File

@@ -34,8 +34,8 @@ func TestPeerCatchupRounds(t *testing.T) {
vote1001_0 := makeVoteHR(t, 1, 1001, privVals, 0) vote1001_0 := makeVoteHR(t, 1, 1001, privVals, 0)
added, err = hvs.AddVote(vote1001_0, "peer1") added, err = hvs.AddVote(vote1001_0, "peer1")
if err != nil { if err != GotVoteFromUnwantedRoundError {
t.Error("AddVote error", err) t.Errorf("Expected GotVoteFromUnwantedRoundError, but got %v", err)
} }
if added { if added {
t.Error("Expected to *not* add vote from peer, too many catchup rounds.") t.Error("Expected to *not* add vote from peer, too many catchup rounds.")
@@ -48,7 +48,7 @@ func TestPeerCatchupRounds(t *testing.T) {
} }
func makeVoteHR(t *testing.T, height int64, round int, privVals []*types.PrivValidatorFS, valIndex int) *types.Vote { func makeVoteHR(t *testing.T, height int64, round int, privVals []types.PrivValidator, valIndex int) *types.Vote {
privVal := privVals[valIndex] privVal := privVals[valIndex]
vote := &types.Vote{ vote := &types.Vote{
ValidatorAddress: privVal.GetAddress(), ValidatorAddress: privVal.GetAddress(),

View File

@@ -52,9 +52,6 @@ func (rs RoundStepType) String() string {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// RoundState defines the internal consensus state. // RoundState defines the internal consensus state.
// It is Immutable when returned from ConsensusState.GetRoundState()
// TODO: Actually, only the top pointer is copied,
// so access to field pointers is still racey
// NOTE: Not thread safe. Should only be manipulated by functions downstream // NOTE: Not thread safe. Should only be manipulated by functions downstream
// of the cs.receiveRoutine // of the cs.receiveRoutine
type RoundState struct { type RoundState struct {
@@ -70,6 +67,9 @@ type RoundState struct {
LockedRound int LockedRound int
LockedBlock *types.Block LockedBlock *types.Block
LockedBlockParts *types.PartSet LockedBlockParts *types.PartSet
ValidRound int
ValidBlock *types.Block
ValidBlockParts *types.PartSet
Votes *HeightVoteSet Votes *HeightVoteSet
CommitRound int // CommitRound int //
LastCommit *types.VoteSet // Last precommits at Height-1 LastCommit *types.VoteSet // Last precommits at Height-1
@@ -106,6 +106,8 @@ func (rs *RoundState) StringIndented(indent string) string {
%s ProposalBlock: %v %v %s ProposalBlock: %v %v
%s LockedRound: %v %s LockedRound: %v
%s LockedBlock: %v %v %s LockedBlock: %v %v
%s ValidRound: %v
%s ValidBlock: %v %v
%s Votes: %v %s Votes: %v
%s LastCommit: %v %s LastCommit: %v
%s LastValidators:%v %s LastValidators:%v
@@ -118,6 +120,8 @@ func (rs *RoundState) StringIndented(indent string) string {
indent, rs.ProposalBlockParts.StringShort(), rs.ProposalBlock.StringShort(), indent, rs.ProposalBlockParts.StringShort(), rs.ProposalBlock.StringShort(),
indent, rs.LockedRound, indent, rs.LockedRound,
indent, rs.LockedBlockParts.StringShort(), rs.LockedBlock.StringShort(), indent, rs.LockedBlockParts.StringShort(), rs.LockedBlock.StringShort(),
indent, rs.ValidRound,
indent, rs.ValidBlockParts.StringShort(), rs.ValidBlock.StringShort(),
indent, rs.Votes.StringIndented(indent+" "), indent, rs.Votes.StringIndented(indent+" "),
indent, rs.LastCommit.StringShort(), indent, rs.LastCommit.StringShort(),
indent, rs.LastValidators.StringIndented(indent+" "), indent, rs.LastValidators.StringIndented(indent+" "),

View File

@@ -1,7 +1,6 @@
package consensus package consensus
import ( import (
"bytes"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"hash/crc32" "hash/crc32"
@@ -11,7 +10,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
wire "github.com/tendermint/go-wire" amino "github.com/tendermint/go-amino"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
auto "github.com/tendermint/tmlibs/autofile" auto "github.com/tendermint/tmlibs/autofile"
cmn "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
@@ -38,13 +37,13 @@ type EndHeightMessage struct {
type WALMessage interface{} type WALMessage interface{}
var _ = wire.RegisterInterface( func RegisterWALMessages(cdc *amino.Codec) {
struct{ WALMessage }{}, cdc.RegisterInterface((*WALMessage)(nil), nil)
wire.ConcreteType{types.EventDataRoundState{}, 0x01}, cdc.RegisterConcrete(types.EventDataRoundState{}, "tendermint/wal/EventDataRoundState", nil)
wire.ConcreteType{msgInfo{}, 0x02}, cdc.RegisterConcrete(msgInfo{}, "tendermint/wal/MsgInfo", nil)
wire.ConcreteType{timeoutInfo{}, 0x03}, cdc.RegisterConcrete(timeoutInfo{}, "tendermint/wal/TimeoutInfo", nil)
wire.ConcreteType{EndHeightMessage{}, 0x04}, cdc.RegisterConcrete(EndHeightMessage{}, "tendermint/wal/EndHeightMessage", nil)
) }
//-------------------------------------------------------- //--------------------------------------------------------
// Simple write-ahead logger // Simple write-ahead logger
@@ -193,7 +192,7 @@ func (wal *baseWAL) SearchForEndHeight(height int64, options *WALSearchOptions)
// A WALEncoder writes custom-encoded WAL messages to an output stream. // A WALEncoder writes custom-encoded WAL messages to an output stream.
// //
// Format: 4 bytes CRC sum + 4 bytes length + arbitrary-length value (go-wire encoded) // Format: 4 bytes CRC sum + 4 bytes length + arbitrary-length value (go-amino encoded)
type WALEncoder struct { type WALEncoder struct {
wr io.Writer wr io.Writer
} }
@@ -205,7 +204,7 @@ func NewWALEncoder(wr io.Writer) *WALEncoder {
// Encode writes the custom encoding of v to the stream. // Encode writes the custom encoding of v to the stream.
func (enc *WALEncoder) Encode(v *TimedWALMessage) error { func (enc *WALEncoder) Encode(v *TimedWALMessage) error {
data := wire.BinaryBytes(v) data := cdc.MustMarshalBinaryBare(v)
crc := crc32.Checksum(data, crc32c) crc := crc32.Checksum(data, crc32c)
length := uint32(len(data)) length := uint32(len(data))
@@ -298,9 +297,8 @@ func (dec *WALDecoder) Decode() (*TimedWALMessage, error) {
return nil, DataCorruptionError{fmt.Errorf("checksums do not match: (read: %v, actual: %v)", crc, actualCRC)} return nil, DataCorruptionError{fmt.Errorf("checksums do not match: (read: %v, actual: %v)", crc, actualCRC)}
} }
var nn int var res = new(TimedWALMessage) // nolint: gosimple
var res *TimedWALMessage // nolint: gosimple err = cdc.UnmarshalBinaryBare(data, res)
res = wire.ReadBinary(&TimedWALMessage{}, bytes.NewBuffer(data), int(length), &nn, &err).(*TimedWALMessage)
if err != nil { if err != nil {
return nil, DataCorruptionError{fmt.Errorf("failed to decode data: %v", err)} return nil, DataCorruptionError{fmt.Errorf("failed to decode data: %v", err)}
} }

View File

@@ -4,19 +4,19 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"fmt" "fmt"
"math/rand"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"time" "time"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/tendermint/abci/example/dummy" "github.com/tendermint/abci/example/kvstore"
bc "github.com/tendermint/tendermint/blockchain" bc "github.com/tendermint/tendermint/blockchain"
cfg "github.com/tendermint/tendermint/config" cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/proxy" "github.com/tendermint/tendermint/proxy"
sm "github.com/tendermint/tendermint/state" sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
pvm "github.com/tendermint/tendermint/types/priv_validator"
auto "github.com/tendermint/tmlibs/autofile" auto "github.com/tendermint/tmlibs/autofile"
cmn "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/db" "github.com/tendermint/tmlibs/db"
@@ -25,13 +25,13 @@ import (
// WALWithNBlocks generates a consensus WAL. It does this by spining up a // WALWithNBlocks generates a consensus WAL. It does this by spining up a
// stripped down version of node (proxy app, event bus, consensus state) with a // stripped down version of node (proxy app, event bus, consensus state) with a
// persistent dummy application and special consensus wal instance // persistent kvstore application and special consensus wal instance
// (byteBufferWAL) and waits until numBlocks are created. Then it returns a WAL // (byteBufferWAL) and waits until numBlocks are created. Then it returns a WAL
// content. // content.
func WALWithNBlocks(numBlocks int) (data []byte, err error) { func WALWithNBlocks(numBlocks int) (data []byte, err error) {
config := getConfig() config := getConfig()
app := dummy.NewPersistentDummyApplication(filepath.Join(config.DBDir(), "wal_generator")) app := kvstore.NewPersistentKVStoreApplication(filepath.Join(config.DBDir(), "wal_generator"))
logger := log.TestingLogger().With("wal_generator", "wal_generator") logger := log.TestingLogger().With("wal_generator", "wal_generator")
logger.Info("generating WAL (last height msg excluded)", "numBlocks", numBlocks) logger.Info("generating WAL (last height msg excluded)", "numBlocks", numBlocks)
@@ -40,7 +40,7 @@ func WALWithNBlocks(numBlocks int) (data []byte, err error) {
// COPY PASTE FROM node.go WITH A FEW MODIFICATIONS // COPY PASTE FROM node.go WITH A FEW MODIFICATIONS
// NOTE: we can't import node package because of circular dependency // NOTE: we can't import node package because of circular dependency
privValidatorFile := config.PrivValidatorFile() privValidatorFile := config.PrivValidatorFile()
privValidator := types.LoadOrGenPrivValidatorFS(privValidatorFile) privValidator := pvm.LoadOrGenFilePV(privValidatorFile)
genDoc, err := types.GenesisDocFromFile(config.GenesisFile()) genDoc, err := types.GenesisDocFromFile(config.GenesisFile())
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to read genesis file") return nil, errors.Wrap(err, "failed to read genesis file")
@@ -52,7 +52,7 @@ func WALWithNBlocks(numBlocks int) (data []byte, err error) {
return nil, errors.Wrap(err, "failed to make genesis state") return nil, errors.Wrap(err, "failed to make genesis state")
} }
blockStore := bc.NewBlockStore(blockStoreDB) blockStore := bc.NewBlockStore(blockStoreDB)
handshaker := NewHandshaker(stateDB, state, blockStore) handshaker := NewHandshaker(stateDB, state, blockStore, genDoc.AppState())
proxyApp := proxy.NewAppConns(proxy.NewLocalClientCreator(app), handshaker) proxyApp := proxy.NewAppConns(proxy.NewLocalClientCreator(app), handshaker)
proxyApp.SetLogger(logger.With("module", "proxy")) proxyApp.SetLogger(logger.With("module", "proxy"))
if err := proxyApp.Start(); err != nil { if err := proxyApp.Start(); err != nil {
@@ -116,7 +116,7 @@ func makePathname() string {
func randPort() int { func randPort() int {
// returns between base and base + spread // returns between base and base + spread
base, spread := 20000, 20000 base, spread := 20000, 20000
return base + rand.Intn(spread) return base + cmn.RandIntn(spread)
} }
func makeAddrs() (string, string, string) { func makeAddrs() (string, string, string) {

View File

@@ -3,11 +3,10 @@ package consensus
import ( import (
"bytes" "bytes"
"crypto/rand" "crypto/rand"
"sync" // "sync"
"testing" "testing"
"time" "time"
wire "github.com/tendermint/go-wire"
"github.com/tendermint/tendermint/consensus/types" "github.com/tendermint/tendermint/consensus/types"
tmtypes "github.com/tendermint/tendermint/types" tmtypes "github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
@@ -36,7 +35,7 @@ func TestWALEncoderDecoder(t *testing.T) {
decoded, err := dec.Decode() decoded, err := dec.Decode()
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, msg.Time.Truncate(time.Millisecond), decoded.Time) assert.Equal(t, msg.Time.UTC(), decoded.Time)
assert.Equal(t, msg.Msg, decoded.Msg) assert.Equal(t, msg.Msg, decoded.Msg)
} }
} }
@@ -68,6 +67,7 @@ func TestWALSearchForEndHeight(t *testing.T) {
assert.Equal(t, rs.Height, h+1, cmn.Fmt("wrong height")) assert.Equal(t, rs.Height, h+1, cmn.Fmt("wrong height"))
} }
/*
var initOnce sync.Once var initOnce sync.Once
func registerInterfacesOnce() { func registerInterfacesOnce() {
@@ -78,6 +78,7 @@ func registerInterfacesOnce() {
) )
}) })
} }
*/
func nBytes(n int) []byte { func nBytes(n int) []byte {
buf := make([]byte, n) buf := make([]byte, n)
@@ -86,7 +87,7 @@ func nBytes(n int) []byte {
} }
func benchmarkWalDecode(b *testing.B, n int) { func benchmarkWalDecode(b *testing.B, n int) {
registerInterfacesOnce() // registerInterfacesOnce()
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
enc := NewWALEncoder(buf) enc := NewWALEncoder(buf)

14
consensus/wire.go Normal file
View File

@@ -0,0 +1,14 @@
package consensus
import (
amino "github.com/tendermint/go-amino"
"github.com/tendermint/go-crypto"
)
var cdc = amino.NewCodec()
func init() {
RegisterConsensusMessages(cdc)
RegisterWALMessages(cdc)
crypto.RegisterAmino(cdc)
}

68
docker-compose.yml Normal file
View File

@@ -0,0 +1,68 @@
version: '3'
services:
node0:
container_name: node0
image: "tendermint/localnode"
ports:
- "46656-46657:46656-46657"
environment:
- ID=0
- LOG=${LOG:-tendermint.log}
volumes:
- ${FOLDER:-./build}:/tendermint:Z
networks:
localnet:
ipv4_address: 192.167.10.2
node1:
container_name: node1
image: "tendermint/localnode"
ports:
- "46659-46660:46656-46657"
environment:
- ID=1
- LOG=${LOG:-tendermint.log}
volumes:
- ${FOLDER:-./build}:/tendermint:Z
networks:
localnet:
ipv4_address: 192.167.10.3
node2:
container_name: node2
image: "tendermint/localnode"
environment:
- ID=2
- LOG=${LOG:-tendermint.log}
ports:
- "46661-46662:46656-46657"
volumes:
- ${FOLDER:-./build}:/tendermint:Z
networks:
localnet:
ipv4_address: 192.167.10.4
node3:
container_name: node3
image: "tendermint/localnode"
environment:
- ID=3
- LOG=${LOG:-tendermint.log}
ports:
- "46663-46664:46656-46657"
volumes:
- ${FOLDER:-./build}:/tendermint:Z
networks:
localnet:
ipv4_address: 192.167.10.5
networks:
localnet:
driver: bridge
ipam:
driver: default
config:
-
subnet: 192.167.10.0/16

7
docker-compose/Makefile Normal file
View File

@@ -0,0 +1,7 @@
# Makefile for the "localnode" docker image.
all:
docker build --tag tendermint/localnode localnode
.PHONY: all

40
docker-compose/README.rst Normal file
View File

@@ -0,0 +1,40 @@
localnode
=========
It is assumed that you have already `setup docker <https://docs.docker.com/engine/installation/>`__.
Description
-----------
Image for local testnets.
Add the tendermint binary to the image by attaching it in a folder to the `/tendermint` mount point.
It assumes that the configuration was created by the `tendermint testnet` command and it is also attached to the `/tendermint` mount point.
Example:
This example builds a linux tendermint binary under the `build/` folder, creates tendermint configuration for a single-node validator and runs the node:
```
cd $GOPATH/src/github.com/tendermint/tendermint
#Build binary
make build-linux
#Create configuration
docker run -e LOG="stdout" -v `pwd`/build:/tendermint tendermint/localnode testnet --o . --v 1
#Run the node
docker run -v `pwd`/build:/tendermint tendermint/localnode
```
Logging
-------
Log is saved under the attached volume, in the `tendermint.log` file. If the `LOG` environment variable is set to `stdout` at start, the log is not saved, but printed on the screen.
Special binaries
----------------
If you have multiple binaries with different names, you can specify which one to run with the BINARY environment variable. The path of the binary is relative to the attached volume.
docker-compose.yml
==================
This file creates a 4-node network using the localnode image. The nodes of the network are exposed to the host machine on ports 46656-46657, 46659-46660, 46661-46662, 46663-46664 respectively.

View File

@@ -0,0 +1,16 @@
FROM alpine:3.7
MAINTAINER Greg Szabo <greg@tendermint.com>
RUN apk update && \
apk upgrade && \
apk --no-cache add curl jq file
VOLUME [ /tendermint ]
WORKDIR /tendermint
EXPOSE 46656 46657
ENTRYPOINT ["/usr/bin/wrapper.sh"]
CMD ["node", "--proxy_app dummy"]
STOPSIGNAL SIGTERM
COPY wrapper.sh /usr/bin/wrapper.sh

View File

@@ -0,0 +1,33 @@
#!/usr/bin/env sh
##
## Input parameters
##
BINARY=/tendermint/${BINARY:-tendermint}
ID=${ID:-0}
LOG=${LOG:-tendermint.log}
##
## Assert linux binary
##
if ! [ -f "${BINARY}" ]; then
echo "The binary `basename ${BINARY}` cannot be found. Please add the binary to the shared folder. Please use the BINARY environment variable if the name of the binary is not 'tendermint' E.g.: -e BINARY=tendermint_my_test_version"
exit 1
fi
BINARY_CHECK="`file $BINARY | grep 'ELF 64-bit LSB executable, x86-64'`"
if [ -z "${BINARY_CHECK}" ]; then
echo "Binary needs to be OS linux, ARCH amd64"
exit 1
fi
##
## Run binary with all parameters
##
export TMHOME="/tendermint/node${ID}"
if [ -d "${TMHOME}/${LOG}" ]; then
"$BINARY" $@ | tee "${TMHOME}/${LOG}"
else
"$BINARY" $@
fi

View File

@@ -16,15 +16,15 @@ Next, install the ``abci-cli`` tool and example applications:
go get -u github.com/tendermint/abci/cmd/abci-cli go get -u github.com/tendermint/abci/cmd/abci-cli
If this fails, you may need to use ``glide`` to get vendored If this fails, you may need to use `dep <https://github.com/golang/dep>`__ to get vendored
dependencies: dependencies:
:: ::
go get github.com/Masterminds/glide
cd $GOPATH/src/github.com/tendermint/abci cd $GOPATH/src/github.com/tendermint/abci
glide install make get_tools
go install ./cmd/abci-cli make get_vendor_deps
make install
Now run ``abci-cli`` to see the list of commands: Now run ``abci-cli`` to see the list of commands:
@@ -40,7 +40,7 @@ Now run ``abci-cli`` to see the list of commands:
console Start an interactive abci console for multiple commands console Start an interactive abci console for multiple commands
counter ABCI demo example counter ABCI demo example
deliver_tx Deliver a new tx to the application deliver_tx Deliver a new tx to the application
dummy ABCI demo example kvstore ABCI demo example
echo Have the application echo a message echo Have the application echo a message
help Help about any command help Help about any command
info Get some info about the application info Get some info about the application
@@ -56,8 +56,8 @@ Now run ``abci-cli`` to see the list of commands:
Use "abci-cli [command] --help" for more information about a command. Use "abci-cli [command] --help" for more information about a command.
Dummy - First Example KVStore - First Example
--------------------- -----------------------
The ``abci-cli`` tool lets us send ABCI messages to our application, to The ``abci-cli`` tool lets us send ABCI messages to our application, to
help build and debug them. help build and debug them.
@@ -66,8 +66,8 @@ The most important messages are ``deliver_tx``, ``check_tx``, and
``commit``, but there are others for convenience, configuration, and ``commit``, but there are others for convenience, configuration, and
information purposes. information purposes.
We'll start a dummy application, which was installed at the same time as We'll start a kvstore application, which was installed at the same time as
``abci-cli`` above. The dummy just stores transactions in a merkle tree. ``abci-cli`` above. The kvstore just stores transactions in a merkle tree.
Its code can be found `here <https://github.com/tendermint/abci/blob/master/cmd/abci-cli/abci-cli.go>`__ and looks like: Its code can be found `here <https://github.com/tendermint/abci/blob/master/cmd/abci-cli/abci-cli.go>`__ and looks like:
@@ -75,20 +75,20 @@ Its code can be found `here <https://github.com/tendermint/abci/blob/master/cmd/
.. container:: header .. container:: header
**Show/Hide Dummy Example** **Show/Hide KVStore Example**
.. code-block:: go .. code-block:: go
func cmdDummy(cmd *cobra.Command, args []string) error { func cmdKVStore(cmd *cobra.Command, args []string) error {
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
// Create the application - in memory or persisted to disk // Create the application - in memory or persisted to disk
var app types.Application var app types.Application
if flagPersist == "" { if flagPersist == "" {
app = dummy.NewDummyApplication() app = kvstore.NewKVStoreApplication()
} else { } else {
app = dummy.NewPersistentDummyApplication(flagPersist) app = kvstore.NewPersistentKVStoreApplication(flagPersist)
app.(*dummy.PersistentDummyApplication).SetLogger(logger.With("module", "dummy")) app.(*kvstore.PersistentKVStoreApplication).SetLogger(logger.With("module", "kvstore"))
} }
// Start the listener // Start the listener
@@ -113,7 +113,7 @@ Start by running:
:: ::
abci-cli dummy abci-cli kvstore
And in another terminal, run And in another terminal, run
@@ -229,7 +229,7 @@ Counter - Another Example
Now that we've got the hang of it, let's try another application, the Now that we've got the hang of it, let's try another application, the
"counter" app. "counter" app.
Like the dummy app, its code can be found `here <https://github.com/tendermint/abci/blob/master/cmd/abci-cli/abci-cli.go>`__ and looks like: Like the kvstore app, its code can be found `here <https://github.com/tendermint/abci/blob/master/cmd/abci-cli/abci-cli.go>`__ and looks like:
.. container:: toggle .. container:: toggle
@@ -288,7 +288,7 @@ other peers.
In this instance of the counter app, ``check_tx`` only allows In this instance of the counter app, ``check_tx`` only allows
transactions whose integer is greater than the last committed one. transactions whose integer is greater than the last committed one.
Let's kill the console and the dummy application, and start the counter Let's kill the console and the kvstore application, and start the counter
app: app:
:: ::
@@ -328,7 +328,7 @@ In another window, start the ``abci-cli console``:
-> data.hex: 0x7B22686173686573223A302C22747873223A327D -> data.hex: 0x7B22686173686573223A302C22747873223A327D
This is a very simple application, but between ``counter`` and This is a very simple application, but between ``counter`` and
``dummy``, its easy to see how you can build out arbitrary application ``kvstore``, its easy to see how you can build out arbitrary application
states on top of the ABCI. `Hyperledger's states on top of the ABCI. `Hyperledger's
Burrow <https://github.com/hyperledger/burrow>`__ also runs atop ABCI, Burrow <https://github.com/hyperledger/burrow>`__ also runs atop ABCI,
bringing with it Ethereum-like accounts, the Ethereum virtual-machine, bringing with it Ethereum-like accounts, the Ethereum virtual-machine,

View File

@@ -142,10 +142,10 @@ It is unlikely that you will need to implement a client. For details of
our client, see our client, see
`here <https://github.com/tendermint/abci/tree/master/client>`__. `here <https://github.com/tendermint/abci/tree/master/client>`__.
Most of the examples below are from `dummy application Most of the examples below are from `kvstore application
<https://github.com/tendermint/abci/blob/master/example/dummy/dummy.go>`__, <https://github.com/tendermint/abci/blob/master/example/kvstore/kvstore.go>`__,
which is a part of the abci repo. `persistent_dummy application which is a part of the abci repo. `persistent_kvstore application
<https://github.com/tendermint/abci/blob/master/example/dummy/persistent_dummy.go>`__ <https://github.com/tendermint/abci/blob/master/example/kvstore/persistent_kvstore.go>`__
is used to show ``BeginBlock``, ``EndBlock`` and ``InitChain`` is used to show ``BeginBlock``, ``EndBlock`` and ``InitChain``
example implementations. example implementations.
@@ -202,7 +202,7 @@ mempool state.
.. code-block:: go .. code-block:: go
func (app *DummyApplication) CheckTx(tx []byte) types.Result { func (app *KVStoreApplication) CheckTx(tx []byte) types.Result {
return types.OK return types.OK
} }
@@ -263,7 +263,7 @@ merkle root of the data returned by the DeliverTx requests, or both.
.. code-block:: go .. code-block:: go
// tx is either "key=value" or just arbitrary bytes // tx is either "key=value" or just arbitrary bytes
func (app *DummyApplication) DeliverTx(tx []byte) types.Result { func (app *KVStoreApplication) DeliverTx(tx []byte) types.Result {
parts := strings.Split(string(tx), "=") parts := strings.Split(string(tx), "=")
if len(parts) == 2 { if len(parts) == 2 {
app.state.Set([]byte(parts[0]), []byte(parts[1])) app.state.Set([]byte(parts[0]), []byte(parts[1]))
@@ -327,7 +327,7 @@ job of the `Handshake <#handshake>`__.
.. code-block:: go .. code-block:: go
func (app *DummyApplication) Commit() types.Result { func (app *KVStoreApplication) Commit() types.Result {
hash := app.state.Hash() hash := app.state.Hash()
return types.NewResultOK(hash, "") return types.NewResultOK(hash, "")
} }
@@ -369,7 +369,7 @@ pick up from when it restarts. See information on the Handshake, below.
.. code-block:: go .. code-block:: go
// Track the block hash and header information // Track the block hash and header information
func (app *PersistentDummyApplication) BeginBlock(params types.RequestBeginBlock) { func (app *PersistentKVStoreApplication) BeginBlock(params types.RequestBeginBlock) {
// update latest block info // update latest block info
app.blockHeader = params.Header app.blockHeader = params.Header
@@ -423,7 +423,7 @@ for details on how it tracks validators.
.. code-block:: go .. code-block:: go
// Update the validator set // Update the validator set
func (app *PersistentDummyApplication) EndBlock(req types.RequestEndBlock) types.ResponseEndBlock { func (app *PersistentKVStoreApplication) EndBlock(req types.RequestEndBlock) types.ResponseEndBlock {
return types.ResponseEndBlock{ValidatorUpdates: app.ValUpdates} return types.ResponseEndBlock{ValidatorUpdates: app.ValUpdates}
} }
@@ -477,7 +477,7 @@ Note: these query formats are subject to change!
.. code-block:: go .. code-block:: go
func (app *DummyApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) { func (app *KVStoreApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) {
if reqQuery.Prove { if reqQuery.Prove {
value, proof, exists := app.state.Proof(reqQuery.Data) value, proof, exists := app.state.Proof(reqQuery.Data)
resQuery.Index = -1 // TODO make Proof return index resQuery.Index = -1 // TODO make Proof return index
@@ -561,7 +561,7 @@ all blocks.
.. code-block:: go .. code-block:: go
func (app *DummyApplication) Info(req types.RequestInfo) (resInfo types.ResponseInfo) { func (app *KVStoreApplication) Info(req types.RequestInfo) (resInfo types.ResponseInfo) {
return types.ResponseInfo{Data: cmn.Fmt("{\"size\":%v}", app.state.Size())} return types.ResponseInfo{Data: cmn.Fmt("{\"size\":%v}", app.state.Size())}
} }
@@ -595,7 +595,7 @@ consensus params.
.. code-block:: go .. code-block:: go
// Save the validators in the merkle tree // Save the validators in the merkle tree
func (app *PersistentDummyApplication) InitChain(params types.RequestInitChain) { func (app *PersistentKVStoreApplication) InitChain(params types.RequestInitChain) {
for _, v := range params.Validators { for _, v := range params.Validators {
r := app.updateValidator(v) r := app.updateValidator(v)
if r.IsErr() { if r.IsErr() {

View File

@@ -0,0 +1,29 @@
# ADR 008: SocketPV
Tendermint node's should support only two in-process PrivValidator
implementations:
- FilePV uses an unencrypted private key in a "priv_validator.json" file - no
configuration required (just `tendermint init`).
- SocketPV uses a socket to send signing requests to another process - user is
responsible for starting that process themselves.
The SocketPV address can be provided via flags at the command line - doing so
will cause Tendermint to ignore any "priv_validator.json" file and to listen on
the given address for incoming connections from an external priv_validator
process. It will halt any operation until at least one external process
succesfully connected.
The external priv_validator process will dial the address to connect to
Tendermint, and then Tendermint will send requests on the ensuing connection to
sign votes and proposals. Thus the external process initiates the connection,
but the Tendermint process makes all requests. In a later stage we're going to
support multiple validators for fault tolerance. To prevent double signing they
need to be synced, which is deferred to an external solution (see #1185).
In addition, Tendermint will provide implementations that can be run in that
external process. These include:
- FilePV will encrypt the private key, and the user must enter password to
decrypt key when process is started.
- LedgerPV uses a Ledger Nano S to handle all signing.

View File

@@ -41,15 +41,15 @@ templates_path = ['_templates']
# The suffix(es) of source filenames. # The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string: # You can specify multiple suffix as a list of string:
# #
source_suffix = ['.rst', '.md'] #source_suffix = ['.rst', '.md']
# source_suffix = '.rst' source_suffix = '.rst'
# The master toctree document. # The master toctree document.
master_doc = 'index' master_doc = 'index'
# General information about the project. # General information about the project.
project = u'Tendermint' project = u'Tendermint'
copyright = u'2017, The Authors' copyright = u'2018, The Authors'
author = u'Tendermint' author = u'Tendermint'
# The version info for the project you're documenting, acts as replacement for # The version info for the project you're documenting, acts as replacement for
@@ -71,7 +71,7 @@ language = None
# List of patterns, relative to source directory, that match files and # List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files. # directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path # This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'architecture'] exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'architecture', 'specification/new-spec', 'examples']
# The name of the Pygments (syntax highlighting) style to use. # The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx' pygments_style = 'sphinx'
@@ -196,13 +196,12 @@ urllib.urlretrieve(tools_repo+tools_branch+'/mintnet-kubernetes/assets/statefuls
urllib.urlretrieve(tools_repo+tools_branch+'/mintnet-kubernetes/assets/t_plus_k.png', filename=assets_dir+'/t_plus_k.png') urllib.urlretrieve(tools_repo+tools_branch+'/mintnet-kubernetes/assets/t_plus_k.png', filename=assets_dir+'/t_plus_k.png')
urllib.urlretrieve(tools_repo+tools_branch+'/terraform-digitalocean/README.rst', filename=tools_dir+'/terraform-digitalocean.rst') urllib.urlretrieve(tools_repo+tools_branch+'/terraform-digitalocean/README.rst', filename=tools_dir+'/terraform-digitalocean.rst')
urllib.urlretrieve(tools_repo+tools_branch+'/tm-bench/README.rst', filename=tools_dir+'/benchmarking-and-monitoring.rst') urllib.urlretrieve(tools_repo+tools_branch+'/tm-bench/README.rst', filename=tools_dir+'/benchmarking.rst')
# the readme for below is included in tm-bench urllib.urlretrieve('https://raw.githubusercontent.com/tendermint/tools/master/tm-monitor/README.rst', filename='tools/monitoring.rst')
# urllib.urlretrieve('https://raw.githubusercontent.com/tendermint/tools/master/tm-monitor/README.rst', filename='tools/tm-monitor.rst')
#### abci spec ################################# #### abci spec #################################
abci_repo = "https://raw.githubusercontent.com/tendermint/abci/" abci_repo = "https://raw.githubusercontent.com/tendermint/abci/"
abci_branch = "spec-docs" abci_branch = "develop"
urllib.urlretrieve(abci_repo+abci_branch+'/specification.rst', filename='abci-spec.rst') urllib.urlretrieve(abci_repo+abci_branch+'/specification.rst', filename='abci-spec.rst')

View File

@@ -11,26 +11,26 @@ Manual Deployments
It's relatively easy to setup a Tendermint cluster manually. The only It's relatively easy to setup a Tendermint cluster manually. The only
requirements for a particular Tendermint node are a private key for the requirements for a particular Tendermint node are a private key for the
validator, stored as ``priv_validator.json``, and a list of the public validator, stored as ``priv_validator.json``, a node key, stored as
keys of all validators, stored as ``genesis.json``. These files should ``node_key.json`` and a list of the public keys of all validators, stored as
be stored in ``~/.tendermint/config``, or wherever the ``$TMHOME`` variable ``genesis.json``. These files should be stored in ``~/.tendermint/config``, or
might be set to. wherever the ``$TMHOME`` variable might be set to.
Here are the steps to setting up a testnet manually: Here are the steps to setting up a testnet manually:
1) Provision nodes on your cloud provider of choice 1) Provision nodes on your cloud provider of choice
2) Install Tendermint and the application of interest on all nodes 2) Install Tendermint and the application of interest on all nodes
3) Generate a private key for each validator using 3) Generate a private key and a node key for each validator using
``tendermint gen_validator`` ``tendermint init``
4) Compile a list of public keys for each validator into a 4) Compile a list of public keys for each validator into a
``genesis.json`` file. ``genesis.json`` file and replace the existing file with it.
5) Run ``tendermint node --p2p.persistent_peers=< peer addresses >`` on each node, 5) Run ``tendermint node --p2p.persistent_peers=< peer addresses >`` on each node,
where ``< peer addresses >`` is a comma separated list of the IP:PORT where ``< peer addresses >`` is a comma separated list of the IP:PORT
combination for each node. The default port for Tendermint is combination for each node. The default port for Tendermint is
``46656``. Thus, if the IP addresses of your nodes were ``46656``. Thus, if the IP addresses of your nodes were
``192.168.0.1, 192.168.0.2, 192.168.0.3, 192.168.0.4``, the command ``192.168.0.1, 192.168.0.2, 192.168.0.3, 192.168.0.4``, the command
would look like: would look like:
``tendermint node --p2p.persistent_peers=192.168.0.1:46656,192.168.0.2:46656,192.168.0.3:46656,192.168.0.4:46656``. ``tendermint node --p2p.persistent_peers=96663a3dd0d7b9d17d4c8211b191af259621c693@192.168.0.1:46656, 429fcf25974313b95673f58d77eacdd434402665@192.168.0.2:46656, 0491d373a8e0fcf1023aaf18c51d6a1d0d4f31bd@192.168.0.3:46656, f9baeaa15fedf5e1ef7448dd60f46c01f1a9e9c4@192.168.0.4:46656``.
After a few seconds, all the nodes should connect to each other and start After a few seconds, all the nodes should connect to each other and start
making blocks! For more information, see the Tendermint Networks section making blocks! For more information, see the Tendermint Networks section

8
docs/determinism.rst Normal file
View File

@@ -0,0 +1,8 @@
On Determinism
==============
Arguably, the most difficult part of blockchain programming is determinism - that is, ensuring that sources of indeterminism do not creep into the design of such systems.
See `this issue <https://github.com/tendermint/abci/issues/56>`__ for more information on the potential sources of indeterminism.

View File

@@ -5,7 +5,7 @@ The growing list of applications built using various pieces of the Tendermint st
* https://tendermint.com/ecosystem * https://tendermint.com/ecosystem
We thank the community for their contributions thus far and welcome the addition of new projects. A pull request can be submitted to `this file <https://github.com/tendermint/tendermint/blob/master/docs/ecosystem.rst>`__ to include your project. We thank the community for their contributions thus far and welcome the addition of new projects. A pull request can be submitted to `this file <https://github.com/tendermint/aib-data/blob/master/json/ecosystem.json>`__ to include your project.
Other Tools Other Tools
----------- -----------

View File

@@ -2,8 +2,9 @@
## Overview ## Overview
This is a quick start guide. If you have a vague idea about how Tendermint works This is a quick start guide. If you have a vague idea about how Tendermint
and want to get started right away, continue. Otherwise, [review the documentation](http://tendermint.readthedocs.io/en/master/) works and want to get started right away, continue. Otherwise, [review the
documentation](http://tendermint.readthedocs.io/en/master/).
## Install ## Install
@@ -12,7 +13,7 @@ and want to get started right away, continue. Otherwise, [review the documentati
On a fresh Ubuntu 16.04 machine can be done with [this script](https://git.io/vNLfY), like so: On a fresh Ubuntu 16.04 machine can be done with [this script](https://git.io/vNLfY), like so:
``` ```
curl -L https://git.io/vNLfY | bash curl -L https://git.io/vxWlX | bash
source ~/.profile source ~/.profile
``` ```
@@ -42,7 +43,7 @@ Confirm installation:
``` ```
$ tendermint version $ tendermint version
0.15.0-381fe19 0.18.0-XXXXXXX
``` ```
## Initialization ## Initialization
@@ -71,7 +72,7 @@ Configuring a cluster is covered further below.
Start tendermint with a simple in-process application: Start tendermint with a simple in-process application:
``` ```
tendermint node --proxy_app=dummy tendermint node --proxy_app=kvstore
``` ```
and blocks will start to stream in: and blocks will start to stream in:
@@ -89,7 +90,7 @@ curl -s localhost:46657/status
### Sending Transactions ### Sending Transactions
With the dummy app running, we can send transactions: With the kvstore app running, we can send transactions:
``` ```
curl -s 'localhost:46657/broadcast_tx_commit?tx="abcd"' curl -s 'localhost:46657/broadcast_tx_commit?tx="abcd"'
@@ -117,7 +118,9 @@ where the value is returned in hex.
## Cluster of Nodes ## Cluster of Nodes
First create four Ubuntu cloud machines. The following was tested on Digital Ocean Ubuntu 16.04 x64 (3GB/1CPU, 20GB SSD). We'll refer to their respective IP addresses below as IP1, IP2, IP3, IP4. First create four Ubuntu cloud machines. The following was tested on Digital
Ocean Ubuntu 16.04 x64 (3GB/1CPU, 20GB SSD). We'll refer to their respective IP
addresses below as IP1, IP2, IP3, IP4.
Then, `ssh` into each machine, and execute [this script](https://git.io/vNLfY): Then, `ssh` into each machine, and execute [this script](https://git.io/vNLfY):
@@ -131,12 +134,16 @@ This will install `go` and other dependencies, get the Tendermint source code, t
Next, `cd` into `docs/examples`. Each command below should be run from each node, in sequence: Next, `cd` into `docs/examples`. Each command below should be run from each node, in sequence:
``` ```
tendermint node --home ./node1 --proxy_app=dummy --p2p.seeds IP1:46656,IP2:46656,IP3:46656,IP4:46656 tendermint node --home ./node1 --proxy_app=kvstore --p2p.persistent_peers="3a558bd6f8c97453aa6c2372bb800e8b6ed8e6db@IP1:46656,ccf30d873fddda10a495f42687c8f33472a6569f@IP2:46656,9a4c3de5d6788a76c6ee3cd9ff41e3b45b4cfd14@IP3:46656,58e6f2ab297b3ceae107ba4c8c2898da5c009ff4@IP4:46656"
tendermint node --home ./node2 --proxy_app=dummy --p2p.seeds IP1:46656,IP2:46656,IP3:46656,IP4:46656 tendermint node --home ./node2 --proxy_app=kvstore --p2p.persistent_peers="3a558bd6f8c97453aa6c2372bb800e8b6ed8e6db@IP1:46656,ccf30d873fddda10a495f42687c8f33472a6569f@IP2:46656,9a4c3de5d6788a76c6ee3cd9ff41e3b45b4cfd14@IP3:46656,58e6f2ab297b3ceae107ba4c8c2898da5c009ff4@IP4:46656"
tendermint node --home ./node3 --proxy_app=dummy --p2p.seeds IP1:46656,IP2:46656,IP3:46656,IP4:46656 tendermint node --home ./node3 --proxy_app=kvstore --p2p.persistent_peers="3a558bd6f8c97453aa6c2372bb800e8b6ed8e6db@IP1:46656,ccf30d873fddda10a495f42687c8f33472a6569f@IP2:46656,9a4c3de5d6788a76c6ee3cd9ff41e3b45b4cfd14@IP3:46656,58e6f2ab297b3ceae107ba4c8c2898da5c009ff4@IP4:46656"
tendermint node --home ./node4 --proxy_app=dummy --p2p.seeds IP1:46656,IP2:46656,IP3:46656,IP4:46656 tendermint node --home ./node4 --proxy_app=kvstore --p2p.persistent_peers="3a558bd6f8c97453aa6c2372bb800e8b6ed8e6db@IP1:46656,ccf30d873fddda10a495f42687c8f33472a6569f@IP2:46656,9a4c3de5d6788a76c6ee3cd9ff41e3b45b4cfd14@IP3:46656,58e6f2ab297b3ceae107ba4c8c2898da5c009ff4@IP4:46656"
``` ```
Note that after the third node is started, blocks will start to stream in because >2/3 of validators (defined in the `genesis.json`) have come online. Seeds can also be specified in the `config.toml`. See [this PR](https://github.com/tendermint/tendermint/pull/792) for more information about configuration options. Note that after the third node is started, blocks will start to stream in
because >2/3 of validators (defined in the `genesis.json`) have come online.
Seeds can also be specified in the `config.toml`. See [this
PR](https://github.com/tendermint/tendermint/pull/792) for more information
about configuration options.
Transactions can then be sent as covered in the single, local node example above. Transactions can then be sent as covered in the single, local node example above.

View File

@@ -4,8 +4,8 @@
# and has only been tested on Digital Ocean # and has only been tested on Digital Ocean
# get and unpack golang # get and unpack golang
curl -O https://storage.googleapis.com/golang/go1.9.2.linux-amd64.tar.gz curl -O https://storage.googleapis.com/golang/go1.10.linux-amd64.tar.gz
tar -xvf go1.9.2.linux-amd64.tar.gz tar -xvf go1.10.linux-amd64.tar.gz
apt install make apt install make
@@ -26,7 +26,7 @@ go get $REPO
cd $GOPATH/src/$REPO cd $GOPATH/src/$REPO
## build ## build
git checkout v0.15.0 git checkout v0.18.0
make get_tools make get_tools
make get_vendor_deps make get_vendor_deps
make install make install

View File

@@ -0,0 +1,6 @@
{
"priv_key" : {
"data" : "DA9BAABEA7211A6D93D9A1986B4279EAB3021FAA1653D459D53E6AB4D1CFB4C69BF7D52E48CF00AC5779AA0A6D3C368955D5636A677F72370B8ED19989714CFC",
"type" : "ed25519"
}
}

View File

@@ -0,0 +1,6 @@
{
"priv_key" : {
"data" : "F7BCABA165DFC0DDD50AE563EFB285BAA236EA805D35612504238A36EFA105958756442B1D9F942D7ABD259F2D59671657B6378E9C7194342A7AAA47A66D1E95",
"type" : "ed25519"
}
}

View File

@@ -0,0 +1,6 @@
{
"priv_key" : {
"data" : "95136FCC97E4446B3141EDF9841078107ECE755E99925D79CCBF91085492680B3CA1034D9917DF1DED4E4AB2D9BC225919F6CB2176F210D2368697CC339DF4E7",
"type" : "ed25519"
}
}

View File

@@ -0,0 +1,6 @@
{
"priv_key" : {
"data" : "8895D6C9A1B46AB83A8E2BAE2121B8C3E245B9E9126EBD797FEAC5058285F2F64FDE2E8182C88AD5185A49D837C581465D57BD478C41865A66D7D9742D8AEF57",
"type" : "ed25519"
}
}

View File

@@ -27,38 +27,38 @@ Then run
go get -u github.com/tendermint/abci/cmd/abci-cli go get -u github.com/tendermint/abci/cmd/abci-cli
If there is an error, install and run the ``glide`` tool to pin the If there is an error, install and run the `dep <https://github.com/golang/dep>`__ tool to pin the
dependencies: dependencies:
:: ::
go get github.com/Masterminds/glide
cd $GOPATH/src/github.com/tendermint/abci cd $GOPATH/src/github.com/tendermint/abci
glide install make get_tools
go install ./cmd/abci-cli make get_vendor_deps
make install
Now you should have the ``abci-cli`` installed; you'll see Now you should have the ``abci-cli`` installed; you'll see
a couple of commands (``counter`` and ``dummy``) that are a couple of commands (``counter`` and ``kvstore``) that are
example applications written in Go. See below for an application example applications written in Go. See below for an application
written in JavaScript. written in JavaScript.
Now, let's run some apps! Now, let's run some apps!
Dummy - A First Example KVStore - A First Example
----------------------- -------------------------
The dummy app is a `Merkle The kvstore app is a `Merkle
tree <https://en.wikipedia.org/wiki/Merkle_tree>`__ that just stores all tree <https://en.wikipedia.org/wiki/Merkle_tree>`__ that just stores all
transactions. If the transaction contains an ``=``, e.g. ``key=value``, transactions. If the transaction contains an ``=``, e.g. ``key=value``,
then the ``value`` is stored under the ``key`` in the Merkle tree. then the ``value`` is stored under the ``key`` in the Merkle tree.
Otherwise, the full transaction bytes are stored as the key and the Otherwise, the full transaction bytes are stored as the key and the
value. value.
Let's start a dummy application. Let's start a kvstore application.
:: ::
abci-cli dummy abci-cli kvstore
In another terminal, we can start Tendermint. If you have never run In another terminal, we can start Tendermint. If you have never run
Tendermint before, use: Tendermint before, use:
@@ -81,11 +81,10 @@ Tendermint node as follows:
curl -s localhost:46657/status curl -s localhost:46657/status
The ``-s`` just silences ``curl``. For nicer output, pipe the result The ``-s`` just silences ``curl``. For nicer output, pipe the result into a
into a tool like `jq <https://stedolan.github.io/jq/>`__ or tool like `jq <https://stedolan.github.io/jq/>`__ or ``json_pp``.
`jsonpp <https://github.com/jmhodges/jsonpp>`__.
Now let's send some transactions to the dummy. Now let's send some transactions to the kvstore.
:: ::
@@ -104,17 +103,23 @@ like:
"id": "", "id": "",
"result": { "result": {
"check_tx": { "check_tx": {
"code": 0, "fee": {}
"data": "",
"log": ""
}, },
"deliver_tx": { "deliver_tx": {
"code": 0, "tags": [
"data": "", {
"log": "" "key": "YXBwLmNyZWF0b3I=",
"value": "amFl"
}, },
"hash": "2B8EC32BA2579B3B8606E42C06DE2F7AFA2556EF", {
"height": 154 "key": "YXBwLmtleQ==",
"value": "YWJjZA=="
}
],
"fee": {}
},
"hash": "9DF66553F98DE3C26E3C3317A3E4CED54F714E39",
"height": 14
} }
} }
@@ -134,20 +139,17 @@ The result should look like:
"id": "", "id": "",
"result": { "result": {
"response": { "response": {
"code": 0, "log": "exists",
"index": 0, "index": "-1",
"key": "", "key": "YWJjZA==",
"value": "61626364", "value": "YWJjZA=="
"proof": "",
"height": 0,
"log": "exists"
} }
} }
} }
Note the ``value`` in the result (``61626364``); this is the Note the ``value`` in the result (``YWJjZA==``); this is the
hex-encoding of the ASCII of ``abcd``. You can verify this in base64-encoding of the ASCII of ``abcd``. You can verify this in
a python 2 shell by running ``"61626364".decode('hex')`` or in python 3 shell by running ``import codecs; codecs.decode("61626364", 'hex').decode('ascii')``. Stay a python 2 shell by running ``"61626364".decode('base64')`` or in python 3 shell by running ``import codecs; codecs.decode("61626364", 'base64').decode('ascii')``. Stay
tuned for a future release that `makes this output more human-readable <https://github.com/tendermint/abci/issues/32>`__. tuned for a future release that `makes this output more human-readable <https://github.com/tendermint/abci/issues/32>`__.
Now let's try setting a different key and value: Now let's try setting a different key and value:
@@ -157,7 +159,7 @@ Now let's try setting a different key and value:
curl -s 'localhost:46657/broadcast_tx_commit?tx="name=satoshi"' curl -s 'localhost:46657/broadcast_tx_commit?tx="name=satoshi"'
Now if we query for ``name``, we should get ``satoshi``, or Now if we query for ``name``, we should get ``satoshi``, or
``7361746F736869`` in hex: ``c2F0b3NoaQ==`` in base64:
:: ::
@@ -192,7 +194,7 @@ In this instance of the counter app, with ``serial=on``, ``CheckTx``
only allows transactions whose integer is greater than the last only allows transactions whose integer is greater than the last
committed one. committed one.
Let's kill the previous instance of ``tendermint`` and the ``dummy`` Let's kill the previous instance of ``tendermint`` and the ``kvstore``
application, and start the counter app. We can enable ``serial=on`` with application, and start the counter app. We can enable ``serial=on`` with
a flag: a flag:
@@ -226,17 +228,15 @@ the number ``1``. If instead, we try to send a ``5``, we get an error:
"id": "", "id": "",
"result": { "result": {
"check_tx": { "check_tx": {
"code": 0, "fee": {}
"data": "",
"log": ""
}, },
"deliver_tx": { "deliver_tx": {
"code": 3, "code": 2,
"data": "", "log": "Invalid nonce. Expected 1, got 5",
"log": "Invalid nonce. Expected 1, got 5" "fee": {}
}, },
"hash": "33B93DFF98749B0D6996A70F64071347060DC19C", "hash": "33B93DFF98749B0D6996A70F64071347060DC19C",
"height": 38 "height": 34
} }
} }
@@ -250,17 +250,13 @@ But if we send a ``1``, it works again:
"id": "", "id": "",
"result": { "result": {
"check_tx": { "check_tx": {
"code": 0, "fee": {}
"data": "",
"log": ""
}, },
"deliver_tx": { "deliver_tx": {
"code": 0, "fee": {}
"data": "",
"log": ""
}, },
"hash": "F17854A977F6FA7EEA1BD758E296710B86F72F3D", "hash": "F17854A977F6FA7EEA1BD758E296710B86F72F3D",
"height": 87 "height": 60
} }
} }
@@ -313,7 +309,7 @@ Neat, eh?
Basecoin - A More Interesting Example Basecoin - A More Interesting Example
------------------------------------- -------------------------------------
We saved the best for last; the `Cosmos SDK <https://github.com/cosmos/cosmos-sdk>`__ is a general purpose framework for building cryptocurrencies. Unlike the ``dummy`` and ``counter``, which are strictly for example purposes. The reference implementation of Cosmos SDK is ``basecoin``, which demonstrates how to use the building blocks of the Cosmos SDK. We saved the best for last; the `Cosmos SDK <https://github.com/cosmos/cosmos-sdk>`__ is a general purpose framework for building cryptocurrencies. Unlike the ``kvstore`` and ``counter``, which are strictly for example purposes. The reference implementation of Cosmos SDK is ``basecoin``, which demonstrates how to use the building blocks of the Cosmos SDK.
The default ``basecoin`` application is a multi-asset cryptocurrency The default ``basecoin`` application is a multi-asset cryptocurrency
that supports inter-blockchain communication (IBC). For more details on how that supports inter-blockchain communication (IBC). For more details on how

View File

@@ -5,7 +5,7 @@ Walk through example
-------------------- --------------------
We first create three connections (mempool, consensus and query) to the We first create three connections (mempool, consensus and query) to the
application (locally running dummy in this case). application (running ``kvstore`` locally in this case).
:: ::
@@ -59,7 +59,7 @@ Next we replay all the messages from the WAL.
:: ::
I[10-04|13:54:30.391] Starting RPC HTTP server on tcp socket 0.0.0.0:46657 module=rpc-server I[10-04|13:54:30.391] Starting RPC HTTP server on tcp socket 0.0.0.0:46657 module=rpc-server
I[10-04|13:54:30.392] Started node module=main nodeInfo="NodeInfo{pk: PubKeyEd25519{DF22D7C92C91082324A1312F092AA1DA197FA598DBBFB6526E177003C4D6FD66}, moniker: anonymous, network: test-chain-3MNw2N [remote , listen 10.0.2.15:46656], version: 0.11.0-10f361fc ([wire_version=0.6.2 p2p_version=0.5.0 consensus_version=v1/0.2.2 rpc_version=0.7.0/3 tx_index=on rpc_addr=tcp://0.0.0.0:46657])}" I[10-04|13:54:30.392] Started node module=main nodeInfo="NodeInfo{id: DF22D7C92C91082324A1312F092AA1DA197FA598DBBFB6526E, moniker: anonymous, network: test-chain-3MNw2N [remote , listen 10.0.2.15:46656], version: 0.11.0-10f361fc ([wire_version=0.6.2 p2p_version=0.5.0 consensus_version=v1/0.2.2 rpc_version=0.7.0/3 tx_index=on rpc_addr=tcp://0.0.0.0:46657])}"
Next follows a standard block creation cycle, where we enter a new round, Next follows a standard block creation cycle, where we enter a new round,
propose a block, receive more than 2/3 of prevotes, then precommits and finally propose a block, receive more than 2/3 of prevotes, then precommits and finally

View File

@@ -44,7 +44,8 @@ Tendermint Tools
tools/docker.rst tools/docker.rst
tools/mintnet-kubernetes.rst tools/mintnet-kubernetes.rst
tools/terraform-digitalocean.rst tools/terraform-digitalocean.rst
tools/benchmarking-and-monitoring.rst tools/benchmarking.rst
tools/monitoring.rst
Tendermint 102 Tendermint 102
-------------- --------------
@@ -65,10 +66,11 @@ Tendermint 201
:maxdepth: 2 :maxdepth: 2
specification.rst specification.rst
determinism.rst
* For a deeper dive, see `this thesis <https://atrium.lib.uoguelph.ca/xmlui/handle/10214/9769>`__. * For a deeper dive, see `this thesis <https://atrium.lib.uoguelph.ca/xmlui/handle/10214/9769>`__.
* There is also the `original whitepaper <https://tendermint.com/static/docs/tendermint.pdf>`__, though it is now quite outdated. * There is also the `original whitepaper <https://tendermint.com/static/docs/tendermint.pdf>`__, though it is now quite outdated.
* Readers might also be interested in the `Cosmos Whitepaper <https://cosmos.network/whitepaper>`__ which describes Tendermint, ABCI, and how to build a scalable, heterogeneous, cryptocurrency network. * Readers might also be interested in the `Cosmos Whitepaper <https://cosmos.network/whitepaper>`__ which describes Tendermint, ABCI, and how to build a scalable, heterogeneous, cryptocurrency network.
* For example applications and related software built by the Tendermint team and other, see the `software ecosystem <https://tendermint.com/ecosystem>`__. * For example applications and related software built by the Tendermint team and other, see the `software ecosystem <https://tendermint.com/ecosystem>`__.
Join the `Cosmos and Tendermint Rocket Chat <https://cosmos.rocket.chat>`__ to ask questions and discuss projects. Join the `community <https://cosmos.network/community>`__ to ask questions and discuss projects.

View File

@@ -4,12 +4,12 @@ Install Tendermint
From Binary From Binary
----------- -----------
To download pre-built binaries, see the `Download page <https://tendermint.com/download>`__. To download pre-built binaries, see the `Download page <https://tendermint.com/downloads>`__.
From Source From Source
----------- -----------
You'll need ``go``, maybe ``glide``, and the Tendermint source code. You'll need ``go``, maybe `dep <https://github.com/golang/dep>`__, and the Tendermint source code.
Install Go Install Go
^^^^^^^^^^ ^^^^^^^^^^
@@ -31,21 +31,21 @@ installation worked.
If the installation failed, a dependency may have been updated and become If the installation failed, a dependency may have been updated and become
incompatible with the latest Tendermint master branch. We solve this incompatible with the latest Tendermint master branch. We solve this
using the ``glide`` tool for dependency management. using the ``dep`` tool for dependency management.
First, install ``glide``: First, install ``dep``:
:: ::
go get github.com/Masterminds/glide cd $GOPATH/src/github.com/tendermint/tendermint
make get_tools
Now we can fetch the correct versions of each dependency by running: Now we can fetch the correct versions of each dependency by running:
:: ::
cd $GOPATH/src/github.com/tendermint/tendermint make get_vendor_deps
glide install make install
go install ./cmd/tendermint
Note that even though ``go get`` originally failed, the repository was Note that even though ``go get`` originally failed, the repository was
still cloned to the correct location in the ``$GOPATH``. still cloned to the correct location in the ``$GOPATH``.
@@ -60,7 +60,7 @@ If you already have Tendermint installed, and you make updates, simply
:: ::
cd $GOPATH/src/github.com/tendermint/tendermint cd $GOPATH/src/github.com/tendermint/tendermint
go install ./cmd/tendermint make install
To upgrade, there are a few options: To upgrade, there are a few options:
@@ -72,18 +72,18 @@ To upgrade, there are a few options:
its dependencies its dependencies
- fetch and checkout the latest master branch in - fetch and checkout the latest master branch in
``$GOPATH/src/github.com/tendermint/tendermint``, and then run ``$GOPATH/src/github.com/tendermint/tendermint``, and then run
``glide install && go install ./cmd/tendermint`` as above. ``make get_vendor_deps && make install`` as above.
Note the first two options should usually work, but may fail. If they Note the first two options should usually work, but may fail. If they
do, use ``glide``, as above: do, use ``dep``, as above:
:: ::
cd $GOPATH/src/github.com/tendermint/tendermint cd $GOPATH/src/github.com/tendermint/tendermint
glide install make get_vendor_deps
go install ./cmd/tendermint make install
Since the third option just uses ``glide`` right away, it should always Since the third option just uses ``dep`` right away, it should always
work. work.
Troubleshooting Troubleshooting
@@ -96,8 +96,9 @@ If ``go get`` failing bothers you, fetch the code using ``git``:
mkdir -p $GOPATH/src/github.com/tendermint mkdir -p $GOPATH/src/github.com/tendermint
git clone https://github.com/tendermint/tendermint $GOPATH/src/github.com/tendermint/tendermint git clone https://github.com/tendermint/tendermint $GOPATH/src/github.com/tendermint/tendermint
cd $GOPATH/src/github.com/tendermint/tendermint cd $GOPATH/src/github.com/tendermint/tendermint
glide install make get_tools
go install ./cmd/tendermint make get_vendor_deps
make install
Run Run
^^^ ^^^
@@ -107,4 +108,4 @@ To start a one-node blockchain with a simple in-process application:
:: ::
tendermint init tendermint init
tendermint node --proxy_app=dummy tendermint node --proxy_app=kvstore

View File

@@ -2,7 +2,7 @@
Specification Specification
############# #############
Here you'll find details of the Tendermint specification. See `the spec repo <https://github.com/tendermint/spec>`__ for upcoming material. Tendermint's types are produced by `godoc <https://godoc.org/github.com/tendermint/tendermint/types>`__. Here you'll find details of the Tendermint specification. Tendermint's types are produced by `godoc <https://godoc.org/github.com/tendermint/tendermint/types>`__.
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
@@ -10,6 +10,7 @@ Here you'll find details of the Tendermint specification. See `the spec repo <ht
specification/block-structure.rst specification/block-structure.rst
specification/byzantine-consensus-algorithm.rst specification/byzantine-consensus-algorithm.rst
specification/configuration.rst specification/configuration.rst
specification/corruption.rst
specification/fast-sync.rst specification/fast-sync.rst
specification/genesis.rst specification/genesis.rst
specification/light-client-protocol.rst specification/light-client-protocol.rst

View File

@@ -329,11 +329,11 @@ collateral on all other forks. Clients should verify the signatures on
the reorg-proposal, verify any evidence, and make a judgement or prompt the reorg-proposal, verify any evidence, and make a judgement or prompt
the end-user for a decision. For example, a phone wallet app may prompt the end-user for a decision. For example, a phone wallet app may prompt
the user with a security warning, while a refrigerator may accept any the user with a security warning, while a refrigerator may accept any
reorg-proposal signed by +½ of the original validators. reorg-proposal signed by +1/2 of the original validators.
No non-synchronous Byzantine fault-tolerant algorithm can come to No non-synchronous Byzantine fault-tolerant algorithm can come to
consensus when + of validators are dishonest, yet a fork assumes that consensus when 1/3+ of validators are dishonest, yet a fork assumes that
+ of validators have already been dishonest by double-signing or 1/3+ of validators have already been dishonest by double-signing or
lock-changing without justification. So, signing the reorg-proposal is a lock-changing without justification. So, signing the reorg-proposal is a
coordination problem that cannot be solved by any non-synchronous coordination problem that cannot be solved by any non-synchronous
protocol (i.e. automatically, and without making assumptions about the protocol (i.e. automatically, and without making assumptions about the

View File

@@ -89,6 +89,7 @@ like the file below, however, double check by inspecting the
seeds = "" seeds = ""
# Comma separated list of nodes to keep persistent connections to # Comma separated list of nodes to keep persistent connections to
# Do not add private peers to this list if you don't want them advertised
persistent_peers = "" persistent_peers = ""
# Path to address book # Path to address book
@@ -121,6 +122,12 @@ like the file below, however, double check by inspecting the
# Does not work if the peer-exchange reactor is disabled. # Does not work if the peer-exchange reactor is disabled.
seed_mode = false seed_mode = false
# Authenticated encryption
auth_enc = true
# Comma separated list of peer IDs to keep private (will not be gossiped to other peers)
private_peer_ids = ""
##### mempool configuration options ##### ##### mempool configuration options #####
[mempool] [mempool]

View File

@@ -5,12 +5,6 @@ The genesis.json file in ``$TMHOME/config`` defines the initial TendermintCore
state upon genesis of the blockchain (`see state upon genesis of the blockchain (`see
definition <https://github.com/tendermint/tendermint/blob/master/types/genesis.go>`__). definition <https://github.com/tendermint/tendermint/blob/master/types/genesis.go>`__).
NOTE: This does not (yet) specify the application state (e.g. initial
distribution of tokens). Currently we leave it up to the application to
load the initial application genesis state. In the future, we may
include genesis SetOption messages that get passed from TendermintCore
to the app upon genesis.
Fields Fields
~~~~~~ ~~~~~~
@@ -24,8 +18,9 @@ Fields
- ``power``: The validator's voting power. - ``power``: The validator's voting power.
- ``name``: Name of the validator (optional). - ``name``: Name of the validator (optional).
- ``app_hash``: The expected application hash (as returned by the - ``app_hash``: The expected application hash (as returned by the
``Commit`` ABCI message) upon genesis. If the app's hash does not ``ResponseInfo`` ABCI message) upon genesis. If the app's hash does not
match, a warning message is printed. match, Tendermint will panic.
- ``app_state``: The application state (e.g. initial distribution of tokens).
Sample genesis.json Sample genesis.json
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
@@ -69,5 +64,8 @@ Sample genesis.json
"name": "mach4" "name": "mach4"
} }
], ],
"app_hash": "15005165891224E721CB664D15CB972240F5703F" "app_hash": "15005165891224E721CB664D15CB972240F5703F",
"app_state": {
{"account": "Bob", "coins": 5000}
}
} }

View File

@@ -4,11 +4,6 @@ This is a markdown specification of the Tendermint blockchain.
It defines the base data structures, how they are validated, It defines the base data structures, how they are validated,
and how they are communicated over the network. and how they are communicated over the network.
XXX: this spec is a work in progress and not yet complete - see github
[issues](https://github.com/tendermint/tendermint/issues) and
[pull requests](https://github.com/tendermint/tendermint/pulls)
for more details.
If you find discrepancies between the spec and the code that If you find discrepancies between the spec and the code that
do not have an associated issue or pull request on github, do not have an associated issue or pull request on github,
please submit them to our [bug bounty](https://tendermint.com/security)! please submit them to our [bug bounty](https://tendermint.com/security)!
@@ -24,18 +19,16 @@ please submit them to our [bug bounty](https://tendermint.com/security)!
### P2P and Network Protocols ### P2P and Network Protocols
TODO: update links - [The Base P2P Layer](p2p): multiplex the protocols ("reactors") on authenticated and encrypted TCP connections
- [Peer Exchange (PEX)](reactors/pex): gossip known peer addresses so peers can find each other
- [The Base P2P Layer](p2p/README.md): multiplex the protocols ("reactors") on authenticated and encrypted TCP connections - [Block Sync](reactors/block_sync): gossip blocks so peers can catch up quickly
- [Peer Exchange (PEX)](pex/README.md): gossip known peer addresses so peers can find each other - [Consensus](reactors/consensus): gossip votes and block parts so new blocks can be committed
- [Block Sync](block_sync/README.md): gossip blocks so peers can catch up quickly - [Mempool](reactors/mempool): gossip transactions so they get included in blocks
- [Consensus](consensus/README.md): gossip votes and block parts so new blocks can be committed - Evidence: TODO
- [Mempool](mempool/README.md): gossip transactions so they get included in blocks
- [Evidence](evidence/README.md): TODO
### More ### More
- [Light Client](light_client/README.md): TODO - Light Client: TODO
- [Persistence](persistence/README.md): TODO - Persistence: TODO
## Overview ## Overview

View File

@@ -16,7 +16,21 @@ In the context of Tendermint, time is of type int64 and denotes UNIX time in mil
corresponds to the number of milliseconds since January 1, 1970. Before defining rules that need to be enforced by the corresponds to the number of milliseconds since January 1, 1970. Before defining rules that need to be enforced by the
Tendermint consensus protocol, so the properties above holds, we introduce the following definition: Tendermint consensus protocol, so the properties above holds, we introduce the following definition:
- median of a set of `Vote` messages is equal to the median of `Vote.Time` fields of the corresponding `Vote` messages - median of a set of `Vote` messages is equal to the median of `Vote.Time` fields of the corresponding `Vote` messages,
where the value of `Vote.Time` is counted number of times proportional to the process voting power. As in Tendermint
the voting power is not uniform (one process one vote), a vote message is actually an aggregator of the same votes whose
number is equal to the voting power of the process that has casted the corresponding votes message.
Let's consider the following example:
- we have four processes p1, p2, p3 and p4, with the following voting power distribution (p1, 23), (p2, 27), (p3, 10)
and (p4, 10). The total voting power is 70 (`N = 3f+1`, where `N` is the total voting power, and `f` is the maximum voting
power of the faulty processes), so we assume that the faulty processes have at most 23 of voting power.
Furthermore, we have the following vote messages in some LastCommit field (we ignore all fields except Time field):
- (p1, 100), (p2, 98), (p3, 1000), (p4, 500). We assume that p3 and p4 are faulty processes. Let's assume that the
`block.LastCommit` message contains votes of processes p2, p3 and p4. Median is then chosen the following way:
the value 98 is counted 27 times, the value 1000 is counted 10 times and the value 500 is counted also 10 times.
So the median value will be the value 98. No matter what set of messages with at least `2f+1` voting power we
choose, the median value will always be between the values sent by correct processes.
We ensure Time Monotonicity and Time Validity properties by the following rules: We ensure Time Monotonicity and Time Validity properties by the following rules:

View File

@@ -0,0 +1,114 @@
# Light client
A light client is a process that connects to the Tendermint Full Node(s) and then tries to verify the Merkle proofs
about the blockchain application. In this document we describe mechanisms that ensures that the Tendermint light client
has the same level of security as Full Node processes (without being itself a Full Node).
To be able to validate a Merkle proof, a light client needs to validate the blockchain header that contains the root app hash.
Validating a blockchain header in Tendermint consists in verifying that the header is committed (signed) by >2/3 of the
voting power of the corresponding validator set. As the validator set is a dynamic set (it is changing), one of the
core functionality of the light client is updating the current validator set, that is then used to verify the
blockchain header, and further the corresponding Merkle proofs.
For the purpose of this light client specification, we assume that the Tendermint Full Node exposes the following functions over
Tendermint RPC:
```golang
Header(height int64) (SignedHeader, error) // returns signed header for the given height
Validators(height int64) (ResultValidators, error) // returns validator set for the given height
LastHeader(valSetNumber int64) (SignedHeader, error) // returns last header signed by the validator set with the given validator set number
type SignedHeader struct {
Header Header
Commit Commit
ValSetNumber int64
}
type ResultValidators struct {
BlockHeight int64
Validators []Validator
// time the current validator set is initialised, i.e, time of the last validator change before header BlockHeight
ValSetTime int64
}
```
We assume that Tendermint keeps track of the validator set changes and that each time a validator set is changed it is
being assigned the next sequence number. We can call this number the validator set sequence number. Tendermint also remembers
the Time from the header when the next validator set is initialised (starts to be in power), and we refer to this time
as validator set init time.
Furthermore, we assume that each validator set change is signed (committed) by the current validator set. More precisely,
given a block `H` that contains transactions that are modifying the current validator set, the Merkle root hash of the next
validator set (modified based on transactions from block H) will be in block `H+1` (and signed by the current validator
set), and then starting from the block `H+2`, it will be signed by the next validator set.
Note that the real Tendermint RPC API is slightly different (for example, response messages contain more data and function
names are slightly different); we shortened (and modified) it for the purpose of this document to make the spec more
clear and simple. Furthermore, note that in case of the third function, the returned header has `ValSetNumber` equals to
`valSetNumber+1`.
Locally, light client manages the following state:
```golang
valSet []Validator // current validator set (last known and verified validator set)
valSetNumber int64 // sequence number of the current validator set
valSetHash []byte // hash of the current validator set
valSetTime int64 // time when the current validator set is initialised
```
The light client is initialised with the trusted validator set, for example based on the known validator set hash,
validator set sequence number and the validator set init time.
The core of the light client logic is captured by the VerifyAndUpdate function that is used to 1) verify if the given header is valid,
and 2) update the validator set (when the given header is valid and it is more recent than the seen headers).
```golang
VerifyAndUpdate(signedHeader SignedHeader):
assertThat signedHeader.valSetNumber >= valSetNumber
if isValid(signedHeader) and signedHeader.Header.Time <= valSetTime + UNBONDING_PERIOD then
setValidatorSet(signedHeader)
return true
else
updateValidatorSet(signedHeader.ValSetNumber)
return VerifyAndUpdate(signedHeader)
isValid(signedHeader SignedHeader):
valSetOfTheHeader = Validators(signedHeader.Header.Height)
assertThat Hash(valSetOfTheHeader) == signedHeader.Header.ValSetHash
assertThat signedHeader is passing basic validation
if votingPower(signedHeader.Commit) > 2/3 * votingPower(valSetOfTheHeader) then return true
else
return false
setValidatorSet(signedHeader SignedHeader):
nextValSet = Validators(signedHeader.Header.Height)
assertThat Hash(nextValSet) == signedHeader.Header.ValidatorsHash
valSet = nextValSet.Validators
valSetHash = signedHeader.Header.ValidatorsHash
valSetNumber = signedHeader.ValSetNumber
valSetTime = nextValSet.ValSetTime
votingPower(commit Commit):
votingPower = 0
for each precommit in commit.Precommits do:
if precommit.ValidatorAddress is in valSet and signature of the precommit verifies then
votingPower += valSet[precommit.ValidatorAddress].VotingPower
return votingPower
votingPower(validatorSet []Validator):
for each validator in validatorSet do:
votingPower += validator.VotingPower
return votingPower
updateValidatorSet(valSetNumberOfTheHeader):
while valSetNumber != valSetNumberOfTheHeader do
signedHeader = LastHeader(valSetNumber)
if isValid(signedHeader) then
setValidatorSet(signedHeader)
else return error
return
```
Note that in the logic above we assume that the light client will always go upward with respect to header verifications,
i.e., that it will always be used to verify more recent headers. In case a light client needs to be used to verify older
headers (go backward) the same mechanisms and similar logic can be used. In case a call to the FullNode or subsequent
checks fail, a light client need to implement some recovery strategy, for example connecting to other FullNode.

View File

@@ -83,7 +83,7 @@ The Tendermint Version Handshake allows the peers to exchange their NodeInfo:
```golang ```golang
type NodeInfo struct { type NodeInfo struct {
PubKey crypto.PubKey ID p2p.ID
Moniker string Moniker string
Network string Network string
RemoteAddr string RemoteAddr string
@@ -95,7 +95,7 @@ type NodeInfo struct {
``` ```
The connection is disconnected if: The connection is disconnected if:
- `peer.NodeInfo.PubKey != peer.PubKey` - `peer.NodeInfo.ID` is not equal `peerConn.ID`
- `peer.NodeInfo.Version` is not formatted as `X.X.X` where X are integers known as Major, Minor, and Revision - `peer.NodeInfo.Version` is not formatted as `X.X.X` where X are integers known as Major, Minor, and Revision
- `peer.NodeInfo.Version` Major is not the same as ours - `peer.NodeInfo.Version` Major is not the same as ours
- `peer.NodeInfo.Version` Minor is not the same as ours - `peer.NodeInfo.Version` Minor is not the same as ours

View File

@@ -31,7 +31,7 @@ Updates (state transitions) happen on timeouts, complete proposals, and 2/3 majo
It receives messages from peers, internal validators and from Timeout Ticker It receives messages from peers, internal validators and from Timeout Ticker
and invokes the corresponding handlers, potentially updating the RoundState. and invokes the corresponding handlers, potentially updating the RoundState.
The details of the protocol (together with formal proofs of correctness) implemented by the Receive Routine are The details of the protocol (together with formal proofs of correctness) implemented by the Receive Routine are
discussed in separate document (see [spec](https://github.com/tendermint/spec)). For understanding of this document discussed in separate document. For understanding of this document
it is sufficient to understand that the Receive Routine manages and updates RoundState data structure that is it is sufficient to understand that the Receive Routine manages and updates RoundState data structure that is
then extensively used by the gossip routines to determine what information should be sent to peer processes. then extensively used by the gossip routines to determine what information should be sent to peer processes.

View File

@@ -11,7 +11,7 @@ next block should be; a validator might vote with a `VoteMessage` for a differen
round, enough number of processes vote for the same block, then this block is committed and later round, enough number of processes vote for the same block, then this block is committed and later
added to the blockchain. `ProposalMessage` and `VoteMessage` are signed by the private key of the added to the blockchain. `ProposalMessage` and `VoteMessage` are signed by the private key of the
validator. The internals of the protocol and how it ensures safety and liveness properties are validator. The internals of the protocol and how it ensures safety and liveness properties are
explained [here](https://github.com/tendermint/spec). explained in a forthcoming document.
For efficiency reasons, validators in Tendermint consensus protocol do not agree directly on the For efficiency reasons, validators in Tendermint consensus protocol do not agree directly on the
block as the block size is big, i.e., they don't embed the block inside `Proposal` and block as the block size is big, i.e., they don't embed the block inside `Proposal` and

View File

@@ -57,10 +57,17 @@ a trust metric (see below), but it's best to start with something simple.
## Select Peers to Dial ## Select Peers to Dial
When we need more peers, we pick them randomly from the addrbook with some When we need more peers, we pick them randomly from the addrbook with some
configurable bias for unvetted peers. The bias should be lower when we have fewer peers, configurable bias for unvetted peers. The bias should be lower when we have fewer peers
and can increase as we obtain more, ensuring that our first peers are more trustworthy, and can increase as we obtain more, ensuring that our first peers are more trustworthy,
but always giving us the chance to discover new good peers. but always giving us the chance to discover new good peers.
We track the last time we dialed a peer and the number of unsuccessful attempts
we've made. If too many attempts are made, we mark the peer as bad.
Connection attempts are made with exponential backoff (plus jitter). Because
the selection process happens every `ensurePeersPeriod`, we might not end up
dialing a peer for much longer than the backoff duration.
## Select Peers to Exchange ## Select Peers to Exchange
When were asked for peers, we select them as follows: When were asked for peers, we select them as follows:

View File

@@ -97,6 +97,7 @@ An HTTP Get request to the root RPC endpoint (e.g.
http://localhost:46657/genesis http://localhost:46657/genesis
http://localhost:46657/net_info http://localhost:46657/net_info
http://localhost:46657/num_unconfirmed_txs http://localhost:46657/num_unconfirmed_txs
http://localhost:46657/health
http://localhost:46657/status http://localhost:46657/status
http://localhost:46657/unconfirmed_txs http://localhost:46657/unconfirmed_txs
http://localhost:46657/unsafe_flush_mempool http://localhost:46657/unsafe_flush_mempool

View File

@@ -62,6 +62,13 @@ such as the Web-of-Trust or Certificate Authorities. In our case, we can
use the blockchain itself as a certificate authority to ensure that we use the blockchain itself as a certificate authority to ensure that we
are connected to at least one validator. are connected to at least one validator.
Config
------
Authenticated encryption is enabled by default. If you wish to use another
authentication scheme or your peers are connected via VPN, you can turn it off
by setting ``auth_enc`` to ``false`` in the config file.
Additional Reading Additional Reading
------------------ ------------------

View File

@@ -41,18 +41,18 @@ To run a Tendermint node, use
tendermint node tendermint node
By default, Tendermint will try to connect to an ABCI application on By default, Tendermint will try to connect to an ABCI application on
`127.0.0.1:46658 <127.0.0.1:46658>`__. If you have the ``dummy`` ABCI `127.0.0.1:46658 <127.0.0.1:46658>`__. If you have the ``kvstore`` ABCI
app installed, run it in another window. If you don't, kill Tendermint app installed, run it in another window. If you don't, kill Tendermint
and run an in-process version with and run an in-process version of the ``kvstore`` app:
:: ::
tendermint node --proxy_app=dummy tendermint node --proxy_app=kvstore
After a few seconds you should see blocks start streaming in. Note that After a few seconds you should see blocks start streaming in. Note that
blocks are produced regularly, even if there are no transactions. See *No Empty Blocks*, below, to modify this setting. blocks are produced regularly, even if there are no transactions. See *No Empty Blocks*, below, to modify this setting.
Tendermint supports in-process versions of the dummy, counter, and nil Tendermint supports in-process versions of the ``counter``, ``kvstore`` and ``nil``
apps that ship as examples in the `ABCI apps that ship as examples in the `ABCI
repository <https://github.com/tendermint/abci>`__. It's easy to compile repository <https://github.com/tendermint/abci>`__. It's easy to compile
your own app in-process with Tendermint if it's written in Go. If your your own app in-process with Tendermint if it's written in Go. If your
@@ -74,20 +74,17 @@ RPC server, for example:
curl http://localhost:46657/broadcast_tx_commit?tx=\"abcd\" curl http://localhost:46657/broadcast_tx_commit?tx=\"abcd\"
For handling responses, we recommend you `install the jsonpp
tool <http://jmhodges.github.io/jsonpp/>`__ to pretty print the JSON.
We can see the chain's status at the ``/status`` end-point: We can see the chain's status at the ``/status`` end-point:
:: ::
curl http://localhost:46657/status | jsonpp curl http://localhost:46657/status | json_pp
and the ``latest_app_hash`` in particular: and the ``latest_app_hash`` in particular:
:: ::
curl http://localhost:46657/status | jsonpp | grep app_hash curl http://localhost:46657/status | json_pp | grep latest_app_hash
Visit http://localhost:46657 in your browser to see the list of other Visit http://localhost:46657 in your browser to see the list of other
endpoints. Some take no arguments (like ``/status``), while others endpoints. Some take no arguments (like ``/status``), while others
@@ -260,19 +257,19 @@ When ``tendermint init`` is run, both a ``genesis.json`` and
:: ::
{ {
"app_hash": "",
"chain_id": "test-chain-HZw6TB",
"genesis_time": "0001-01-01T00:00:00.000Z",
"validators" : [ "validators" : [
{ {
"pub_key" : {
"value" : "h3hk+QE8c6QLTySp8TcfzclJw/BG79ziGB/pIA+DfPE=",
"type" : "AC26791624DE60"
},
"power" : 10, "power" : 10,
"name": "", "name" : ""
"pub_key": [
1,
"5770B4DD55B3E08B7F5711C48B516347D8C33F47C30C226315D21AA64E0DFF2E"
]
} }
] ],
"app_hash" : "",
"chain_id" : "test-chain-rDlYSN",
"genesis_time" : "0001-01-01T00:00:00Z"
} }
And the ``priv_validator.json``: And the ``priv_validator.json``:
@@ -280,20 +277,18 @@ And the ``priv_validator.json``:
:: ::
{ {
"address": "4F4D895F882A18E1D1FC608D102601DA8D3570E5",
"last_height": 0,
"last_round": 0,
"last_signature": null,
"last_signbytes": "",
"last_step" : 0, "last_step" : 0,
"priv_key": [ "last_round" : 0,
1, "address" : "B788DEDE4F50AD8BC9462DE76741CCAFF87D51E2",
"F9FA3CD435BDAE54D0BCA8F1BC289D718C23D855C6DB21E8543F5E4F457E62805770B4DD55B3E08B7F5711C48B516347D8C33F47C30C226315D21AA64E0DFF2E" "pub_key" : {
], "value" : "h3hk+QE8c6QLTySp8TcfzclJw/BG79ziGB/pIA+DfPE=",
"pub_key": [ "type" : "AC26791624DE60"
1, },
"5770B4DD55B3E08B7F5711C48B516347D8C33F47C30C226315D21AA64E0DFF2E" "last_height" : 0,
] "priv_key" : {
"value" : "JPivl82x+LfVkp8i3ztoTjY6c6GJ4pBxQexErOCyhwqHeGT5ATxzpAtPJKnxNx/NyUnD8Ebv3OIYH+kgD4N88Q==",
"type" : "954568A3288910"
}
} }
The ``priv_validator.json`` actually contains a private key, and should The ``priv_validator.json`` actually contains a private key, and should
@@ -334,14 +329,14 @@ For instance,
:: ::
tendermint node --p2p.seeds "1.2.3.4:46656,5.6.7.8:46656" tendermint node --p2p.seeds "f9baeaa15fedf5e1ef7448dd60f46c01f1a9e9c4@1.2.3.4:46656,0491d373a8e0fcf1023aaf18c51d6a1d0d4f31bd@5.6.7.8:46656"
Alternatively, you can use the ``/dial_seeds`` endpoint of the RPC to Alternatively, you can use the ``/dial_seeds`` endpoint of the RPC to
specify seeds for a running node to connect to: specify seeds for a running node to connect to:
:: ::
curl 'localhost:46657/dial_seeds?seeds=\["1.2.3.4:46656","5.6.7.8:46656"\]' curl 'localhost:46657/dial_seeds?seeds=\["f9baeaa15fedf5e1ef7448dd60f46c01f1a9e9c4@1.2.3.4:46656","0491d373a8e0fcf1023aaf18c51d6a1d0d4f31bd@5.6.7.8:46656"\]'
Note, if the peer-exchange protocol (PEX) is enabled (default), you should not Note, if the peer-exchange protocol (PEX) is enabled (default), you should not
normally need seeds after the first start. Peers will be gossipping about known normally need seeds after the first start. Peers will be gossipping about known
@@ -355,8 +350,8 @@ core instance.
:: ::
tendermint node --p2p.persistent_peers "10.11.12.13:46656,10.11.12.14:46656" tendermint node --p2p.persistent_peers "429fcf25974313b95673f58d77eacdd434402665@10.11.12.13:46656,96663a3dd0d7b9d17d4c8211b191af259621c693@10.11.12.14:46656"
curl 'localhost:46657/dial_peers?persistent=true&peers=\["1.2.3.4:46656","5.6.7.8:46656"\]' curl 'localhost:46657/dial_peers?persistent=true&peers=\["429fcf25974313b95673f58d77eacdd434402665@10.11.12.13:46656","96663a3dd0d7b9d17d4c8211b191af259621c693@10.11.12.14:46656"\]'
Adding a Non-Validator Adding a Non-Validator
~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
@@ -387,20 +382,18 @@ Now we can update our genesis file. For instance, if the new
:: ::
{ {
"address": "AC379688105901436A34A65F185C115B8BB277A1", "address" : "5AF49D2A2D4F5AD4C7C8C4CC2FB020131E9C4902",
"last_height": 0, "pub_key" : {
"last_round": 0, "value" : "l9X9+fjkeBzDfPGbUM7AMIRE6uJN78zN5+lk5OYotek=",
"last_signature": null, "type" : "AC26791624DE60"
"last_signbytes": "", },
"priv_key" : {
"value" : "EDJY9W6zlAw+su6ITgTKg2nTZcHAH1NMTW5iwlgmNDuX1f35+OR4HMN88ZtQzsAwhETq4k3vzM3n6WTk5ii16Q==",
"type" : "954568A3288910"
},
"last_step" : 0, "last_step" : 0,
"priv_key": [ "last_round" : 0,
1, "last_height" : 0
"0D2ED337D748ADF79BE28559B9E59EBE1ABBA0BAFE6D65FCB9797985329B950C8F2B5AACAACC9FCE41881349743B0CFDE190DF0177744568D4E82A18F0B7DF94"
],
"pub_key": [
1,
"8F2B5AACAACC9FCE41881349743B0CFDE190DF0177744568D4E82A18F0B7DF94"
]
} }
then the new ``genesis.json`` will be: then the new ``genesis.json`` will be:
@@ -408,27 +401,27 @@ then the new ``genesis.json`` will be:
:: ::
{ {
"app_hash": "",
"chain_id": "test-chain-HZw6TB",
"genesis_time": "0001-01-01T00:00:00.000Z",
"validators" : [ "validators" : [
{ {
"pub_key" : {
"value" : "h3hk+QE8c6QLTySp8TcfzclJw/BG79ziGB/pIA+DfPE=",
"type" : "AC26791624DE60"
},
"power" : 10, "power" : 10,
"name": "", "name" : ""
"pub_key": [
1,
"5770B4DD55B3E08B7F5711C48B516347D8C33F47C30C226315D21AA64E0DFF2E"
]
}, },
{ {
"pub_key" : {
"value" : "l9X9+fjkeBzDfPGbUM7AMIRE6uJN78zN5+lk5OYotek=",
"type" : "AC26791624DE60"
},
"power" : 10, "power" : 10,
"name": "", "name" : ""
"pub_key": [
1,
"8F2B5AACAACC9FCE41881349743B0CFDE190DF0177744568D4E82A18F0B7DF94"
]
} }
] ],
"app_hash" : "",
"chain_id" : "test-chain-rDlYSN",
"genesis_time" : "0001-01-01T00:00:00Z"
} }
Update the ``genesis.json`` in ``~/.tendermint/config``. Copy the genesis file Update the ``genesis.json`` in ``~/.tendermint/config``. Copy the genesis file

View File

@@ -1,12 +1,11 @@
package evidence package evidence
import ( import (
"bytes"
"fmt" "fmt"
"reflect" "reflect"
"time" "time"
wire "github.com/tendermint/go-wire" amino "github.com/tendermint/go-amino"
"github.com/tendermint/tmlibs/log" "github.com/tendermint/tmlibs/log"
"github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/p2p"
@@ -16,7 +15,7 @@ import (
const ( const (
EvidenceChannel = byte(0x38) EvidenceChannel = byte(0x38)
maxEvidenceMessageSize = 1048576 // 1MB TODO make it configurable maxMsgSize = 1048576 // 1MB TODO make it configurable
broadcastEvidenceIntervalS = 60 // broadcast uncommitted evidence this often broadcastEvidenceIntervalS = 60 // broadcast uncommitted evidence this often
) )
@@ -68,7 +67,7 @@ func (evR *EvidenceReactor) AddPeer(peer p2p.Peer) {
// the rest will be sent by the broadcastRoutine // the rest will be sent by the broadcastRoutine
evidences := evR.evpool.PriorityEvidence() evidences := evR.evpool.PriorityEvidence()
msg := &EvidenceListMessage{evidences} msg := &EvidenceListMessage{evidences}
success := peer.Send(EvidenceChannel, struct{ EvidenceMessage }{msg}) success := peer.Send(EvidenceChannel, cdc.MustMarshalBinaryBare(msg))
if !success { if !success {
// TODO: remove peer ? // TODO: remove peer ?
} }
@@ -82,9 +81,10 @@ func (evR *EvidenceReactor) RemovePeer(peer p2p.Peer, reason interface{}) {
// Receive implements Reactor. // Receive implements Reactor.
// It adds any received evidence to the evpool. // It adds any received evidence to the evpool.
func (evR *EvidenceReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) { func (evR *EvidenceReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) {
_, msg, err := DecodeMessage(msgBytes) msg, err := DecodeMessage(msgBytes)
if err != nil { if err != nil {
evR.Logger.Error("Error decoding message", "err", err) evR.Logger.Error("Error decoding message", "src", src, "chId", chID, "msg", msg, "err", err, "bytes", msgBytes)
evR.Switch.StopPeerForError(src, err)
return return
} }
evR.Logger.Debug("Receive", "src", src, "chId", chID, "msg", msg) evR.Logger.Debug("Receive", "src", src, "chId", chID, "msg", msg)
@@ -95,7 +95,8 @@ func (evR *EvidenceReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) {
err := evR.evpool.AddEvidence(ev) err := evR.evpool.AddEvidence(ev)
if err != nil { if err != nil {
evR.Logger.Info("Evidence is not valid", "evidence", msg.Evidence, "err", err) evR.Logger.Info("Evidence is not valid", "evidence", msg.Evidence, "err", err)
// TODO: punish peer // punish peer
evR.Switch.StopPeerForError(src, err)
} }
} }
default: default:
@@ -117,7 +118,7 @@ func (evR *EvidenceReactor) broadcastRoutine() {
case evidence := <-evR.evpool.EvidenceChan(): case evidence := <-evR.evpool.EvidenceChan():
// broadcast some new evidence // broadcast some new evidence
msg := &EvidenceListMessage{[]types.Evidence{evidence}} msg := &EvidenceListMessage{[]types.Evidence{evidence}}
evR.Switch.Broadcast(EvidenceChannel, struct{ EvidenceMessage }{msg}) evR.Switch.Broadcast(EvidenceChannel, cdc.MustMarshalBinaryBare(msg))
// TODO: Broadcast runs asynchronously, so this should wait on the successChan // TODO: Broadcast runs asynchronously, so this should wait on the successChan
// in another routine before marking to be proper. // in another routine before marking to be proper.
@@ -125,7 +126,7 @@ func (evR *EvidenceReactor) broadcastRoutine() {
case <-ticker.C: case <-ticker.C:
// broadcast all pending evidence // broadcast all pending evidence
msg := &EvidenceListMessage{evR.evpool.PendingEvidence()} msg := &EvidenceListMessage{evR.evpool.PendingEvidence()}
evR.Switch.Broadcast(EvidenceChannel, struct{ EvidenceMessage }{msg}) evR.Switch.Broadcast(EvidenceChannel, cdc.MustMarshalBinaryBare(msg))
case <-evR.Quit(): case <-evR.Quit():
return return
} }
@@ -135,24 +136,22 @@ func (evR *EvidenceReactor) broadcastRoutine() {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Messages // Messages
const (
msgTypeEvidence = byte(0x01)
)
// EvidenceMessage is a message sent or received by the EvidenceReactor. // EvidenceMessage is a message sent or received by the EvidenceReactor.
type EvidenceMessage interface{} type EvidenceMessage interface{}
var _ = wire.RegisterInterface( func RegisterEvidenceMessages(cdc *amino.Codec) {
struct{ EvidenceMessage }{}, cdc.RegisterInterface((*EvidenceMessage)(nil), nil)
wire.ConcreteType{&EvidenceListMessage{}, msgTypeEvidence}, cdc.RegisterConcrete(&EvidenceListMessage{},
) "tendermint/evidence/EvidenceListMessage", nil)
}
// DecodeMessage decodes a byte-array into a EvidenceMessage. // DecodeMessage decodes a byte-array into a EvidenceMessage.
func DecodeMessage(bz []byte) (msgType byte, msg EvidenceMessage, err error) { func DecodeMessage(bz []byte) (msg EvidenceMessage, err error) {
msgType = bz[0] if len(bz) > maxMsgSize {
n := new(int) return msg, fmt.Errorf("Msg exceeds max size (%d > %d)",
r := bytes.NewReader(bz) len(bz), maxMsgSize)
msg = wire.ReadBinary(struct{ EvidenceMessage }{}, r, maxEvidenceMessageSize, n, &err).(struct{ EvidenceMessage }).EvidenceMessage }
err = cdc.UnmarshalBinaryBare(bz, &msg)
return return
} }

View File

@@ -3,7 +3,6 @@ package evidence
import ( import (
"fmt" "fmt"
wire "github.com/tendermint/go-wire"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
dbm "github.com/tendermint/tmlibs/db" dbm "github.com/tendermint/tmlibs/db"
) )
@@ -104,7 +103,10 @@ func (store *EvidenceStore) ListEvidence(prefixKey string) (evidence []types.Evi
val := iter.Value() val := iter.Value()
var ei EvidenceInfo var ei EvidenceInfo
wire.ReadBinaryBytes(val, &ei) err := cdc.UnmarshalBinaryBare(val, &ei)
if err != nil {
panic(err)
}
evidence = append(evidence, ei.Evidence) evidence = append(evidence, ei.Evidence)
} }
return evidence return evidence
@@ -119,7 +121,10 @@ func (store *EvidenceStore) GetEvidence(height int64, hash []byte) *EvidenceInfo
return nil return nil
} }
var ei EvidenceInfo var ei EvidenceInfo
wire.ReadBinaryBytes(val, &ei) err := cdc.UnmarshalBinaryBare(val, &ei)
if err != nil {
panic(err)
}
return &ei return &ei
} }
@@ -137,7 +142,7 @@ func (store *EvidenceStore) AddNewEvidence(evidence types.Evidence, priority int
Priority: priority, Priority: priority,
Evidence: evidence, Evidence: evidence,
} }
eiBytes := wire.BinaryBytes(ei) eiBytes := cdc.MustMarshalBinaryBare(ei)
// add it to the store // add it to the store
key := keyOutqueue(evidence, priority) key := keyOutqueue(evidence, priority)
@@ -171,7 +176,7 @@ func (store *EvidenceStore) MarkEvidenceAsCommitted(evidence types.Evidence) {
ei.Committed = true ei.Committed = true
lookupKey := keyLookup(evidence) lookupKey := keyLookup(evidence)
store.db.SetSync(lookupKey, wire.BinaryBytes(ei)) store.db.SetSync(lookupKey, cdc.MustMarshalBinaryBare(ei))
} }
//--------------------------------------------------- //---------------------------------------------------
@@ -181,6 +186,9 @@ func (store *EvidenceStore) getEvidenceInfo(evidence types.Evidence) EvidenceInf
key := keyLookup(evidence) key := keyLookup(evidence)
var ei EvidenceInfo var ei EvidenceInfo
b := store.db.Get(key) b := store.db.Get(key)
wire.ReadBinaryBytes(b, &ei) err := cdc.UnmarshalBinaryBare(b, &ei)
if err != nil {
panic(err)
}
return ei return ei
} }

View File

@@ -4,7 +4,6 @@ import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
wire "github.com/tendermint/go-wire"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
dbm "github.com/tendermint/tmlibs/db" dbm "github.com/tendermint/tmlibs/db"
) )
@@ -108,15 +107,3 @@ func TestStorePriority(t *testing.T) {
assert.Equal(ev, cases[i].ev) assert.Equal(ev, cases[i].ev)
} }
} }
//-------------------------------------------
const (
evidenceTypeMockGood = byte(0x01)
evidenceTypeMockBad = byte(0x02)
)
var _ = wire.RegisterInterface(
struct{ types.Evidence }{},
wire.ConcreteType{types.MockGoodEvidence{}, evidenceTypeMockGood},
wire.ConcreteType{types.MockBadEvidence{}, evidenceTypeMockBad},
)

25
evidence/wire.go Normal file
View File

@@ -0,0 +1,25 @@
package evidence
import (
amino "github.com/tendermint/go-amino"
"github.com/tendermint/go-crypto"
"github.com/tendermint/tendermint/types"
)
var cdc = amino.NewCodec()
func init() {
RegisterEvidenceMessages(cdc)
crypto.RegisterAmino(cdc)
types.RegisterEvidences(cdc)
RegisterMockEvidences(cdc) // For testing
}
//-------------------------------------------
func RegisterMockEvidences(cdc *amino.Codec) {
cdc.RegisterConcrete(types.MockGoodEvidence{},
"tendermint/MockGoodEvidence", nil)
cdc.RegisterConcrete(types.MockBadEvidence{},
"tendermint/MockBadEvidence", nil)
}

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