Compare commits

...

965 Commits

Author SHA1 Message Date
Ethan Buchman
8d7640894d Merge pull request #606 from tendermint/release-v0.10.3
Release v0.10.3
2017-08-10 10:25:48 -04:00
Ethan Buchman
c5a657f540 consensus: test proposal heartbeat 2017-08-10 01:24:23 -04:00
Ethan Buchman
1ea43e513d version bump 2017-08-10 00:14:49 -04:00
Ethan Buchman
c9e11de2a7 consensus: log ProposalHeartbeat msg 2017-08-10 00:12:16 -04:00
Ethan Buchman
f644fc5725 changelog 2017-08-10 00:04:07 -04:00
Ethan Buchman
49278a7f9c Merge pull request #579 from tendermint/feature/sync_status
Add fast-sync status to Status() call
2017-08-09 23:51:25 -04:00
Ethan Buchman
b0728260e9 comments 2017-08-09 23:51:09 -04:00
Ethan Buchman
92ada55e5a make conR.FastSync() thread safe 2017-08-09 14:55:21 -04:00
Ethan Buchman
8840ae6ae2 Merge pull request #597 from zramsay/tendermint-types-links
fix dead tendermint types links in specs
2017-08-08 22:19:10 -04:00
Ethan Buchman
21dab22f17 Merge pull request #599 from zramsay/docs-getting-started
docs fixes throughout
2017-08-08 22:17:44 -04:00
Ethan Buchman
10a849c27e Merge pull request #596 from zramsay/deduplications
Deduplications
2017-08-08 22:16:25 -04:00
Ethan Buchman
7108c66e3b Merge pull request #584 from tendermint/no-empty-blocks
No empty blocks
2017-08-08 18:10:04 -04:00
Ethan Buchman
0bf66deb3c fixes from review 2017-08-08 17:09:04 -04:00
Ethan Buchman
d2f3e9faf4 test CreateEmptyBlocksInterval 2017-08-08 16:35:25 -04:00
Ethan Buchman
37f1390473 CreateEmptyBlocks and CreateEmptyBlocksInterval 2017-08-08 16:22:37 -04:00
Ethan Buchman
fb47ca6d35 fixes from review 2017-08-04 21:36:11 -04:00
Ethan Buchman
043c6018b4 Merge pull request #591 from tendermint/heartbeat
broadcast proposer heartbeat msg
2017-08-03 14:35:25 -04:00
Ethan Buchman
d0965cca05 forgot heartbeat file 2017-08-03 13:58:17 -04:00
Ethan Buchman
b8ac67e240 some fixes 2017-08-03 13:25:26 -04:00
Zach Ramsay
350d584af8 docs: tons of minor improvements
closes: https://github.com/zramsay/tendermint/issues/3
closes: https://github.com/zramsay/tendermint/issues/5
2017-08-01 15:52:16 -04:00
Zach Ramsay
e3e75376ec fix mintnet-kubernetes link 2017-08-01 14:31:38 -04:00
Ethan Buchman
ab753abfa0 Proposer->Proposal; sign heartbeats 2017-07-29 17:04:28 -04:00
Zach Ramsay
bab7877fa1 route links to godoc rather than deprecated internal implementation, closes https://github.com/zramsay/tendermint/issues/1 2017-07-29 14:33:49 -04:00
Ethan Buchman
10f8101314 fix race 2017-07-29 11:45:07 -04:00
Ethan Buchman
530626dab7 broadcast proposer heartbeat msg 2017-07-29 11:45:02 -04:00
Ethan Buchman
b96d28a42b test progress in higher round 2017-07-28 23:43:30 -04:00
Ethan Buchman
3444bee47f fixes from review; use mempool.TxsAvailable() directly 2017-07-28 23:42:43 -04:00
Ethan Buchman
cf3abe5096 consensus: remove rs from handleMsg 2017-07-28 23:42:19 -04:00
Ethan Buchman
ecdda69fab commit empty blocks when needed to prove app hash 2017-07-28 22:12:11 -04:00
Ethan Buchman
fc3fe9292f fixes from review 2017-07-28 22:12:11 -04:00
Ethan Buchman
d396053872 changelog 2017-07-28 22:11:45 -04:00
Ethan Buchman
e9a2389300 cmd: --consensus.no_empty_blocks 2017-07-28 22:11:45 -04:00
Ethan Buchman
678a9a2e42 TxsAvailable tests 2017-07-28 22:11:45 -04:00
Ethan Buchman
124032e3e9 NoEmptyBlocks config option 2017-07-28 22:11:45 -04:00
Ethan Buchman
4beac54bd9 no empty blocks 2017-07-28 22:11:45 -04:00
Ethan Buchman
39493bcd9a consensus: isProposer func 2017-07-28 22:11:10 -04:00
Zach Ramsay
f96771e753 cleanup CONTRIBUTING.md, part of https://github.com/zramsay/tendermint/issues/7 2017-07-28 16:45:03 -04:00
Zach Ramsay
62f94a7948 deduplicate install, closes https://github.com/zramsay/tendermint/issues/2 2017-07-28 16:34:38 -04:00
Ethan Buchman
e9b7221292 consensus: more comments 2017-07-20 00:59:28 -04:00
Anton Kaliaev
5fea1d2675 [.editorconfig] add rule for .proto files [ci skip] 2017-07-19 12:29:10 +03:00
Anton Kaliaev
7a492e3612 Merge pull request #549 from tendermint/rpc-server-proposal
RPC server ADR
2017-07-19 10:44:12 +03:00
Anton Kaliaev
b893df3348 add rpc server proposal [ci skip] 2017-07-19 10:43:30 +03:00
Anton Kaliaev
742b5b705f update link to contributing guidelines in README.md [ci skip] 2017-07-19 10:35:54 +03:00
Peng Zhong
0153d21f3b Update website links on README.md (#589) [ci skip]
* Update website links on README.md

Changed the blog links to Medium.
Updated Tendermint website links.

* correct link for Tendermint blog [ci skip]
2017-07-19 10:31:49 +03:00
Ethan Buchman
695ad5fe2d Merge pull request #588 from tendermint/comments_cleanup
Comments and cleanup
2017-07-17 13:32:23 -04:00
Ethan Buchman
d8ca0580a8 rpc: move grpc_test from test/ to grpc/ 2017-07-17 12:58:15 -04:00
Ethan Buchman
525fc0ae5b types: block comments 2017-07-17 12:58:15 -04:00
Ethan Buchman
311f18bebf mempool: comments 2017-07-17 12:58:10 -04:00
Ethan Buchman
d49a5166ac scripts/txs: add 0x and randomness 2017-07-17 12:57:30 -04:00
Ethan Buchman
e560dd839f Merge pull request #587 from ramilexe/feature/sync_status
fast sync status
2017-07-17 11:51:44 -04:00
ramil
6f8d385dfa fast sync status 2017-07-17 09:44:23 +03:00
caojingqi
086544e367 p2p: sw.peers.List() is empty in sw.OnStart 2017-07-10 20:43:38 -04:00
Ethan Buchman
eed0297ed5 Merge pull request #538 from zramsay/docs-rework
rework docs
2017-07-10 20:32:03 -04:00
Ethan Buchman
b467515719 Merge pull request #576 from tendermint/release-v0.10.2
Release v0.10.2
2017-07-10 16:22:09 -04:00
Ethan Buchman
75df0d91ba comments from review 2017-07-10 13:39:23 -04:00
Adrian Brink
05c0dfac12 First crack it providing fast-sync endpoint 2017-07-10 19:30:54 +02:00
Ethan Buchman
bcde80fc4f changelog 2017-07-09 18:49:22 -04:00
Ethan Buchman
5f6b996d22 breakup some long lines; add more comments to consensus reactor 2017-07-09 18:38:59 -04:00
Ethan Buchman
74a3a2b56a fix comments 2017-07-09 18:01:25 -04:00
Adrian Brink
b07d01f102 Add more comments on public functions and extra logging during 'enterPrevote'
Signed-off-by: Adrian Brink <adrian@brink-holdings.com>
2017-07-09 20:35:48 +02:00
Ethan Buchman
eed3959749 version bump 2017-07-09 13:17:01 -04:00
Zach Ramsay
278b2344fe move docs/architechture to docs/hidden 2017-07-08 14:25:15 -04:00
Zach Ramsay
05095954c9 use official golang site for installing 2017-07-08 14:09:56 -04:00
Zach Ramsay
6fef4b080e docs: add docs from website 2017-07-08 14:05:24 -04:00
Zach Ramsay
259111d0e8 address adrian's comment 2017-07-08 14:05:24 -04:00
Zach Ramsay
a707748893 add roadmap.md from site 2017-07-08 14:05:24 -04:00
Zach Ramsay
130ae133c1 fix link in changelog 2017-07-08 14:05:24 -04:00
Zach Ramsay
1d387d7452 contributing: use full version from site 2017-07-08 14:05:24 -04:00
Ethan Buchman
612726d9f6 consensus: better logging 2017-07-07 16:58:16 -04:00
Ethan Buchman
5888ddaab1 consensus: improve logging for conflicting votes 2017-07-07 13:41:50 -04:00
Ethan Buchman
e6cecb9595 p2p: fix test 2017-07-07 13:33:15 -04:00
Ethan Buchman
e36e507463 changelog 2017-07-07 13:30:30 -04:00
Ethan Buchman
3c10f7a122 add p2p flush throttle to config 2017-07-07 13:08:52 -04:00
Ethan Buchman
ca8c34f966 add consensus reactor sleep durations to the config 2017-07-07 12:39:40 -04:00
Ethan Buchman
8355fee16a Merge pull request #563 from tendermint/bugfix/ignore-tmhome-in-tests
Do some cleanup in the test, so it doesn't fail in my shell
2017-07-06 21:48:40 -04:00
Ethan Buchman
7f3c697b6e Merge pull request #559 from tendermint/release-v0.10.1
Release v0.10.1
2017-06-28 11:45:41 -04:00
Ethan Buchman
0d1fa8e884 fixes from review 2017-06-28 11:12:45 -04:00
Ethan Frey
2e0a4aafa7 Update glide files to proper versions 2017-06-28 13:05:51 +02:00
Ethan Buchman
bfecb5a135 some fixes from review 2017-06-27 16:05:21 -04:00
Ethan Frey
850da13622 Do some cleanup in the test, so it doesn't fail in my shell 2017-06-27 13:31:32 +02:00
Ethan Buchman
b284d39e05 update changelog 2017-06-26 17:29:39 -04:00
Ethan Buchman
23d8de8962 Merge branch 'develop' into release-v0.10.1 2017-06-26 17:22:57 -04:00
Ethan Buchman
3065059da7 update changelog 2017-06-26 17:20:27 -04:00
Ethan Buchman
e2ed15fa02 rpc: SetWriteDeadline for ws ping. fixes #553 2017-06-26 17:13:39 -04:00
Ethan Buchman
b92814c933 Merge pull request #560 from tendermint/feature/docker-build-automation
Docker build automation
2017-06-26 16:20:05 -04:00
Ethan Buchman
12c084c8c0 ParseGenesisFile -> types.GenesisDocFromFile 2017-06-26 16:16:54 -04:00
Ethan Frey
e4caf96bcb Calculate validator hash from genesis doc 2017-06-26 17:06:49 +02:00
Ethan Frey
10982f4d8f Add argument to ParseGenesis to use in light-client 2017-06-26 16:56:51 +02:00
Ethan Frey
6b38abd57b Cleanup for loop in genesis file load 2017-06-26 13:58:34 +02:00
Ethan Frey
58105dbd4e Refactored some commands to be more reusable 2017-06-26 13:58:34 +02:00
Anton Kaliaev
d0b1bf4e26 [docker] bump golang version to 1.8.3 2017-06-26 12:08:38 +04:00
Anton Kaliaev
aa7b3a73f5 [docker] remove dummy app as default [ci skip] 2017-06-26 12:05:50 +04:00
Anton Kaliaev
852375be00 [docker] update link to mintnet-k8s [ci skip] 2017-06-26 12:05:50 +04:00
Anton Kaliaev
9a80730f38 [docker] fix link to 0.10.0 Dockerfile [ci skip] 2017-06-26 12:05:40 +04:00
Anton Kaliaev
24a1b130f9 [docker] build and push scripts [ci skip] 2017-06-26 12:05:34 +04:00
Anton Kaliaev
e2fe4a6e9b [docker] bump alpine version [ci skip] 2017-06-26 12:05:23 +04:00
Ethan Buchman
4f5b6528a1 version bump 2017-06-24 22:56:31 -04:00
Ethan Buchman
10828ad063 changelog 2017-06-24 22:55:48 -04:00
Ethan Buchman
b6031d5f4b rpc/lib: set logger on ws conn 2017-06-24 21:55:31 -04:00
Ethan Buchman
5c29d7aba9 rpc/lib: test tcp and unix 2017-06-24 21:27:19 -04:00
Ethan Buchman
bdbefe334d Merge pull request #539 from tendermint/feature/readme_update
Add Code of Conduct
2017-06-24 00:14:08 -04:00
Ethan Buchman
77a3d03385 blockchain: explain isCaughtUp logic 2017-06-23 22:34:38 -04:00
Ethan Buchman
468982ffe4 fixes 2017-06-23 22:12:45 -04:00
Ethan Buchman
2750343de5 Merge branch 'apply-megacheck' into unstable 2017-06-23 21:38:22 -04:00
Ethan Buchman
3c0128a680 undo some megacheck suggestions 2017-06-23 21:36:47 -04:00
Ethan Buchman
4522456d55 Merge remote-tracking branch 'origin/feature/496-deduplicate-tests-in-rpc-tests-and-rpc-client' into unstable 2017-06-23 21:24:09 -04:00
Ethan Buchman
dcc538ff32 Merge remote-tracking branch 'origin/feature/506-tracing-logger' into unstable 2017-06-23 21:16:02 -04:00
Ethan Buchman
a22f7358cd Merge pull request #552 from tendermint/feature/551-default-to-tcp
httpDialer accepts no prefix or http:// as tcp://
2017-06-22 23:49:50 -04:00
Ethan Frey
aac85a14f0 httpDialer accepts no prefix or http:// as tcp:// 2017-06-22 20:56:57 +02:00
Adrian Brink
df934c1707 Add Code of Conduct 2017-06-19 15:46:15 +02:00
Anton Kaliaev
eaec0c8ea5 deduplicate tests in rpc/test and rpc/client (Refs #496) 2017-06-16 17:14:27 +04:00
Anton Kaliaev
4f0f50c62d Merge pull request #535 from tendermint/bugfix/fix-invalid-logger-keys
Fix invalid logger keys
2017-06-14 13:20:10 +04:00
Anton Kaliaev
b4ece65726 standardize key for errors (should be "err") 2017-06-14 12:50:49 +04:00
Anton Kaliaev
562dd67e16 fix invalid keys (space is prohibited by logfmt encoder) 2017-06-14 12:50:01 +04:00
Anton Kaliaev
da646a8986 Merge pull request #534 from hxzqlh/master
logger key doesn't support blank space
2017-06-14 12:38:13 +04:00
hxzqlh
b9b2782c3c logger key doesn't support space 2017-06-14 14:41:36 +08:00
Anton Kaliaev
f1c087116f Merge pull request #518 from tendermint/feature/504-move-log-level-parsing-to-cli-package
move log level parsing to tmlibs/cli package (Refs #504)
2017-06-12 19:47:22 +04:00
Anton Kaliaev
9dcf130d67 update tmlibs (Refs #504) 2017-06-12 19:25:34 +04:00
Anton Kaliaev
ed7183936d move log level parsing to tmlibs/cli package (Refs #504) 2017-06-12 19:08:39 +04:00
Ethan Buchman
e5342f4054 docker: update for 0.10.0 [ci skip]" 2017-06-03 21:37:59 -04:00
Ethan Buchman
2e173d4abf Merge branch 'master' into develop 2017-06-03 00:13:14 -04:00
Ethan Buchman
ca632c9e90 Merge pull request #527 from tendermint/release-v0.10.0
Release v0.10.0
2017-06-02 23:59:46 -04:00
Ethan Buchman
c94a92b30d update changelog and readme 2017-06-02 23:33:03 -04:00
Ethan Buchman
84fea82043 dist: dont mkdir in container 2017-06-02 23:20:02 -04:00
Ethan Buchman
d608e2b7ad bump version to 0.10.0 2017-06-02 23:19:30 -04:00
Ethan Buchman
d19d8e7914 update readme and changelog 2017-06-02 23:18:10 -04:00
zramsay
9c3eee0b00 Makefile: add megacheck & some additional fixes 2017-05-30 13:35:34 -04:00
zramsay
bf5181d9ca address PR comments 2017-05-30 13:27:08 -04:00
Anton Kaliaev
989cb6dd2e update Dockerfile [ci skip] 2017-05-30 16:33:48 +03:00
zramsay
cf31f8d06f core: apply megacheck vet tool (unused, gosimple, staticcheck) 2017-05-29 23:11:40 -04:00
Ethan Buchman
c7cd62b449 Merge branch 'master' into develop 2017-05-29 10:53:33 -04:00
Ethan Buchman
2b5b017253 Merge pull request #516 from tendermint/release-v0.10.0-rc2
fixes to changelog, config, default logging
2017-05-29 08:58:20 -04:00
Ethan Buchman
630c6ef7b5 bump version 2017-05-29 02:47:09 -04:00
Ethan Buchman
ee88272216 enable unsafe rpc routes in tests via flag 2017-05-26 14:20:23 -04:00
Ethan Buchman
bd7ec18c19 fix tests 2017-05-26 12:17:32 -04:00
Ethan Buchman
42626d9e16 [types] overwrite pubkey/addr in LoadPrivValidator. closes #500 2017-05-25 13:40:13 -04:00
Ethan Buchman
fc6611b2d9 [config] RPCConfig 2017-05-24 13:56:12 -04:00
Ethan Buchman
4f27752468 [rpc] dont enable unsafe by default; limit /blockchain_info to 20 blocks 2017-05-24 11:31:31 -04:00
Anton Kaliaev
9bc1790320 tracing logger (Refs #506)
DEPENDS ON https://github.com/tendermint/tmlibs/pull/21
2017-05-24 14:28:25 +02:00
Ethan Buchman
3fbe286e5a small fixes to changelog, config, default logging 2017-05-22 08:16:25 -04:00
Anton Kaliaev
aa22bd8464 Merge pull request #511 from spring1843/fix-typos
Fix commonly misspelled words
2017-05-21 13:24:35 +02:00
spring1843
cf686d4f83 Fix commonly misspelled words 2017-05-20 21:43:00 -07:00
Ethan Buchman
267f134d44 Merge pull request #508 from tendermint/release-v0.10.0-rc1
Release v0.10.0 rc1
2017-05-18 13:45:17 +02:00
Ethan Buchman
d4fa98de68 update version for rc1 2017-05-18 07:20:37 -04:00
Ethan Buchman
790e04ed3e add link to list of breaking funcs/methods 2017-05-18 12:32:34 +02:00
Ethan Buchman
772306cac8 update changelog and glide 2017-05-18 06:16:08 -04:00
Ethan Buchman
30a19fc899 [consensus] Info->Debug for is a validator log msg 2017-05-18 11:26:15 +02:00
Ethan Buchman
6a30a902c9 [types] more []byte->data.Bytes and some %X->%v 2017-05-17 01:08:41 +02:00
Ethan Buchman
883b71ca70 update CHANGELOG 2017-05-17 00:27:03 +02:00
Ethan Buchman
11b5d11e9e Merge pull request #505 from tendermint/dont-hash-accum
[types] dont hash validator.Accum
2017-05-17 00:19:28 +02:00
Ethan Buchman
6d83c60c40 [types] dont hash validator.Accum 2017-05-17 00:16:38 +02:00
Ethan Buchman
cc2b430e68 update glide and changelog 2017-05-17 00:12:56 +02:00
Ethan Buchman
9d7d8075d1 Merge remote-tracking branch 'origin/update-for-new-abci-api' into develop 2017-05-16 23:58:48 +02:00
Ethan Buchman
fe87623674 Merge pull request #501 from tendermint/feature/493-per-module-log-levels
Feature/493 per module log levels
2017-05-16 23:33:21 +02:00
Anton Kaliaev
91dc87e7c4 update for a new ABCI API 2017-05-16 19:06:35 +02:00
Anton Kaliaev
fb0df75de0 changes per Frey comments (Refs #493) 2017-05-16 15:16:50 +02:00
Ethan Buchman
e1792c1ea5 fix tx string format take 2 2017-05-16 14:12:48 +02:00
Ethan Buchman
d5113377e2 fix tx string format 2017-05-16 14:01:52 +02:00
Adrian Brink
eb9ca23250 log whether node is a validator in each round 2017-05-16 14:01:52 +02:00
Anton Kaliaev
05a8204508 per module log levels (Refs #493) 2017-05-16 13:57:28 +02:00
Adrian Brink
e041b2eee6 Merge pull request #502 from tendermint/adrianbrink-patch-1
Update README.md
2017-05-16 10:13:31 +02:00
Adrian Brink
0a4ab7e38f Update README.md 2017-05-16 09:50:52 +02:00
Ethan Buchman
c4ad0f76e6 Merge pull request #494 from tendermint/json-naming
Clean up json output
2017-05-15 19:03:25 +02:00
Ethan Buchman
0f1edcd57d Merge pull request #497 from tendermint/feature/color-code-different-consensus-instances
Color code different consensus instances in consensus tests
2017-05-15 19:02:02 +02:00
Adrian Brink
118d565534 Merge pull request #472 from tendermint/string_reprs
Add Tx String representation. Got the ok from Anton.
2017-05-15 09:49:40 +02:00
Anton Kaliaev
1dfb95f719 [consensus] color code different consensus instances in consensus tests
(Refs #492)
2017-05-15 09:35:29 +02:00
Ethan Buchman
33f807d9a1 Merge pull request #489 from tendermint/feature/adrian-#469
Use ld flags to set git hash instead of "revision_file"
2017-05-14 21:52:29 +02:00
Ethan Frey
926fb83e33 Re-added comment 2017-05-14 19:10:58 +02:00
Ethan Frey
2b324b7eb9 RPC returns pretty formated json 2017-05-14 19:06:34 +02:00
Ethan Frey
157ec8af2d Add json tags to validator set 2017-05-14 19:06:33 +02:00
Ethan Buchman
f14f167297 Merge pull request #491 from tendermint/feature/new-logging
New logging
2017-05-14 00:45:21 +02:00
Anton Kaliaev
4fe67652ff move SetLogger down 2017-05-14 00:24:58 +02:00
Anton Kaliaev
c5bccc5474 set missing logger on switch
```
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x882cec]

goroutine 328 [running]:
github.com/tendermint/tendermint/p2p.(*Switch).DialPeerWithAddress(0xc42000a500, 0xc4202088d0, 0xc420403500, 0x0, 0x0, 0x0)
        /home/vagrant/go/src/github.com/tendermint/tendermint/p2p/switch.go:324 +0x2fc
github.com/tendermint/tendermint/p2p.(*PEXReactor).ensurePeers.func1(0xc4201663f0, 0xc4202088d0)
        /home/vagrant/go/src/github.com/tendermint/tendermint/p2p/pex_reactor.go:280 +0x3e
created by github.com/tendermint/tendermint/p2p.(*PEXReactor).ensurePeers
        /home/vagrant/go/src/github.com/tendermint/tendermint/p2p/pex_reactor.go:284 +0x5d4
```
2017-05-13 17:05:44 +02:00
Anton Kaliaev
f8fdbe3dbc changes as per Bucky's review 2017-05-13 16:22:51 +02:00
Anton Kaliaev
c9cd8de9c6 set logger 2017-05-13 10:25:00 +02:00
Anton Kaliaev
3e1343dc6b has as a base16 string 2017-05-13 10:24:59 +02:00
Anton Kaliaev
bc4e6566e7 [p2p] refactor upnp to use new logger 2017-05-13 10:24:59 +02:00
Ethan Buchman
16509ac3db p2p: fix race by peer.Start() before peers.Add() 2017-05-13 10:24:59 +02:00
Anton Kaliaev
f803544195 new logging 2017-05-13 10:24:58 +02:00
Anton Kaliaev
8eb07800b3 Merge pull request #473 from tendermint/logging-arch-proposal
Logging architecture proposal
2017-05-12 20:40:28 +02:00
Anton Kaliaev
538b325150 update arch proposal [ci skip] [circleci skip] 2017-05-12 20:32:57 +02:00
Adrian Brink
cd3c3c3bad Modify makefile 2017-05-09 11:39:46 +02:00
Adrian Brink
8c91014cd8 Add git commit hash to version. 2017-05-09 11:37:59 +02:00
Adrian Brink
6312eb91be Change "make build" to set GitCommit variable
As described above.
2017-05-06 12:37:48 +02:00
Ethan Frey
d2ae7e164a Make testify a testImport for consistency 2017-05-05 19:46:32 +02:00
Ethan Buchman
627a686f90 Merge pull request #487 from tendermint/fixp2ptests
One silly tests passes on osx, fails on linux...
2017-05-05 12:53:39 -04:00
Ethan Frey
57527f9f67 One silly tests passes on osx, fails on linux... comment out so i can develop 2017-05-05 18:48:39 +02:00
Ethan Buchman
14d0d395f2 Merge pull request #484 from tendermint/config
Config
2017-05-05 12:46:39 -04:00
Ethan Buchman
edd7263f06 fixes from review 2017-05-05 12:25:53 -04:00
Ethan Frey
2a9e89b34f Add basic tests on config, mainly to raise test coverage 2017-05-05 15:50:23 +02:00
Ethan Frey
dd1f5a2268 Test config parsing in root command 2017-05-05 15:30:39 +02:00
Ethan Buchman
8a0466d81d config: pex_reactor -> pex 2017-05-05 02:04:24 -04:00
Ethan Buchman
fb9d3842e4 test: p2p.seeds and p2p.pex 2017-05-05 01:28:43 -04:00
Ethan Buchman
8c3823545d update glide 2017-05-05 00:51:03 -04:00
Ethan Buchman
75989342b0 fixes from rebase 2017-05-04 23:03:42 -04:00
Ethan Buchman
46151720f8 fix tests 2017-05-04 22:46:41 -04:00
Ethan Buchman
9109b20852 SetRoot 2017-05-04 22:46:41 -04:00
Ethan Frey
6b059e0063 Accept relative paths in all configs, TODO: must SetRoot 2017-05-04 22:46:40 -04:00
Ethan Frey
92dee7ea3c Commands compile (mostly) with new config reading 2017-05-04 22:46:40 -04:00
Ethan Frey
604bf03f3a Pulled out all config structs (except p2p.PeerConfig) into config package 2017-05-04 22:46:40 -04:00
Ethan Buchman
f217f2b2c5 cleanup run_node flags 2017-05-04 22:46:13 -04:00
Ethan Buchman
92bafa7ecd consensus: fix tests 2017-05-04 22:46:13 -04:00
Ethan Buchman
6afee8f117 rpc: fix tests 2017-05-04 22:45:13 -04:00
Ethan Buchman
1ef7c1d25b cmd: fixes for new config 2017-05-04 22:43:55 -04:00
Ethan Buchman
7db7bbe464 node: ConfigFromViper 2017-05-04 22:43:55 -04:00
Ethan Buchman
57151d6043 p2p: use cmn instead of . 2017-05-04 22:43:55 -04:00
Ethan Buchman
5d660e073a remove viper from p2p 2017-05-04 22:43:55 -04:00
Ethan Buchman
4982cb4d1f fix tests for state and mempool 2017-05-04 22:43:55 -04:00
Ethan Buchman
24ce90fc72 fix up config defaults 2017-05-04 22:43:55 -04:00
Ethan Buchman
75b6c5215f fewer structs. remove viper from consensus 2017-05-04 22:43:55 -04:00
Ethan Buchman
d8fb226ec4 new config 2017-05-04 22:43:55 -04:00
Ethan Buchman
95c74b2ccd remove some more viper 2017-05-04 22:43:55 -04:00
Ethan Buchman
f0e7f0acf8 remove viper from rpc except test 2017-05-04 22:43:55 -04:00
Ethan Buchman
1fcc9dc654 remove viper from proxy 2017-05-04 22:39:22 -04:00
Ethan Buchman
7c0f51e24b remove viper from mempool 2017-05-04 22:39:22 -04:00
Ethan Buchman
29c0e6e4f4 remove viper from blockchain and state 2017-05-04 22:39:21 -04:00
Ethan Buchman
56a1a2d917 remove logrotate.config 2017-05-04 22:39:03 -04:00
Anton Kaliaev
4871e64b03 update docker README [ci skip] [circleci skip] 2017-05-04 13:36:27 +04:00
Anton Kaliaev
809e0e8c59 update Dockerfile [ci skip] [circleci skip] 2017-05-04 13:32:46 +04:00
Ethan Buchman
4305d054d6 Merge pull request #465 from tendermint/bytes
RPC Serialization Overhaul
2017-05-03 14:37:21 -04:00
Ethan Buchman
9860c8fee1 rpc: cleanup some comments [ci skip] 2017-05-03 14:33:07 -04:00
Ethan Frey
7ebf011fcd Fixed rpctypes.Request creation to new format 2017-05-03 16:58:21 +02:00
Ethan Frey
4a1b714ca4 All tests pass without go-wire json ptr madness 2017-05-03 16:45:00 +02:00
Ethan Frey
4c1d41c12e Test json rpc parsing 2017-05-03 16:26:18 +02:00
Ethan Frey
6ba799132c json.RawMessage in RPCRequest to defer parsing 2017-05-03 16:13:58 +02:00
Adrian Brink
e5d8fcd198 Merge pull request #474 from faddat/patch-4
Fix Mintnet Kubernetes link
2017-05-03 13:56:45 +02:00
Adrian Brink
f31d6ffb8c Fix Mintnet kubernetes url 2017-05-03 13:55:42 +02:00
Ethan Buchman
2608b95b4b Merge pull request #463 from tendermint/feature/adrian-initchain
InitChain message gets send at genesis.
2017-05-01 12:00:29 -04:00
Adrian Brink
2bf8c40cff Add extra memory to virtual machine and add coverage report to gitignore. 2017-05-01 17:29:44 +02:00
Jacob Gadikian
b5ab74fd38 Update README.md
updated mintnet to mintnet-kubernetes
2017-05-01 15:50:11 +07:00
Anton Kaliaev
556fbf0c4c logging arch proposal [ci skip] [circleci skip] 2017-05-01 11:58:43 +04:00
Jae Kwon
4c7a2be06a Add Tx String representation 2017-04-30 16:06:49 -07:00
Ethan Buchman
6dbcfb32d2 comment on copied wire file 2017-04-28 23:22:54 -04:00
Ethan Buchman
efeadcc0f4 some cleanup from review 2017-04-28 23:18:38 -04:00
Ethan Buchman
297772e009 Merge pull request #467 from tendermint/nowire
Nowire
2017-04-28 22:58:39 -04:00
Ethan Buchman
aa9e673ed7 test: jq .result[1] -> jq .result 2017-04-28 22:31:30 -04:00
Ethan Buchman
4e781961e9 remove TMResult. ::drinks champagne:: 2017-04-28 22:26:23 -04:00
Ethan Buchman
884060eb9b rpc/lib: no Result wrapper 2017-04-28 22:04:14 -04:00
Ethan Buchman
07e59e63f9 TMEventDataInner 2017-04-28 17:57:06 -04:00
Ethan Buchman
ac28b12fa8 add readReflectJSON from wire 2017-04-28 17:56:44 -04:00
Ethan Frey
257f45b768 ebuchman: added some demos on how to parse unknown types 2017-04-28 22:01:46 +02:00
Ethan Buchman
acfbea6d49 rpc: decode args without wire 2017-04-28 14:36:38 -04:00
Ethan Frey
6c60c07f16 BROKEN: attempt to replace go-wire.JSON with json.Unmarshall in rpc 2017-04-28 16:24:06 +02:00
Ethan Frey
bff8402fe8 Fix json for TMResult to not include "TMResultInner" 2017-04-28 15:26:06 +02:00
Ethan Frey
f6f1f1992c Prepare rpc responses for go-data compatibility, still use go-wire 2017-04-28 14:46:04 +02:00
Ethan Frey
194f345470 Use non-standard port so tests don't die when I am running basecoin 2017-04-28 14:45:34 +02:00
Ethan Buchman
2bf7e9c968 update glide 2017-04-27 19:58:12 -04:00
Ethan Buchman
c930f43cbe rpc: fix tests 2017-04-27 19:56:14 -04:00
Ethan Buchman
a518d08839 rpc: response types use Result instead of pb Response 2017-04-27 19:34:25 -04:00
Ethan Buchman
cdf650fba9 rpc: repsonse types use data.Bytes 2017-04-27 19:06:07 -04:00
Ethan Buchman
bdb34f9f4e types: []byte -> data.Bytes 2017-04-27 19:01:18 -04:00
Ethan Buchman
0be3480729 consensus: comment about test_data [ci skip] 2017-04-27 18:34:57 -04:00
Ethan Buchman
495283e2d4 fix replay tests and update test wals for InitChain 2017-04-27 18:30:43 -04:00
Adrian Brink
842609ddcb Send InitChain message from ABCI to Core on Genesis
InitChain is send from the ABCI to the Core node when the ABCI
app has no blocks stored.
2017-04-27 20:22:11 +02:00
Ethan Buchman
1310c72647 version bump and changelog 2017-04-26 20:06:45 -04:00
Ethan Buchman
cc6dde96c1 rpc -> rpc/lib and rpc/tendermint -> rpc 2017-04-26 19:57:33 -04:00
Ethan Buchman
cc7b2d26e5 Merge branch 'master' into develop 2017-04-26 19:21:06 -04:00
Ethan Buchman
1781a52147 Merge pull request #459 from tendermint/http_codes
rpc: use HTTP error codes
2017-04-26 19:05:43 -04:00
Ethan Buchman
0ba449c8ba Merge pull request #455 from tendermint/unstable
Unstable
2017-04-26 19:04:52 -04:00
Ethan Buchman
f6e28c4975 Merge pull request #461 from tendermint/release-v0.9.2
Release v0.9.2
2017-04-26 11:07:00 -04:00
Ethan Buchman
6bcd4242f1 CHANGELOG and version bump 2017-04-26 00:17:53 -04:00
Ethan Buchman
9851265d4f rpc: use HTTP error codes 2017-04-25 23:09:47 -04:00
Ethan Buchman
098646c5ff test: test_libs all use Makefile 2017-04-25 18:35:22 -04:00
Ethan Buchman
0e5cd6dc2f Merge pull request #442 from tendermint/viper
go-config -> viper, commands: Run -> RunE
2017-04-25 17:59:17 -04:00
Ethan Buchman
2fcb2b9232 remove unsafe_set_config 2017-04-25 17:58:26 -04:00
Ethan Buchman
fcf78a5da7 cleanup go-config/viper and some unnamed imports 2017-04-25 14:54:56 -04:00
rigel rozanski
72c4be35e8 tiny fix 2017-04-25 13:44:13 -04:00
Rigel Rozanski
5d0c2a1414 commands: Run -> RunE 2017-04-25 13:44:13 -04:00
Rigel Rozanski
7bb638e3b8 fix test_integrations error 2017-04-25 13:44:13 -04:00
Rigel Rozanski
7448753257 fixing tests 2017-04-25 13:43:57 -04:00
Rigel Rozanski
6e662337ff dont export resetPrivValidator 2017-04-25 13:43:57 -04:00
Rigel Rozanski
270b68a893 glide lock updates 2017-04-25 13:43:22 -04:00
Rigel Rozanski
cefb2bede0 adding viper
int

int
2017-04-25 13:42:22 -04:00
rigelrozanski
47852122d0 changed reset commands 2017-04-25 13:34:46 -04:00
Ethan Buchman
00055fa2e8 Merge pull request #456 from tendermint/repo-merge
Repo merge
2017-04-25 13:13:32 -04:00
Ethan Frey
bd93f76950 Improve rpc to properly format any alias for []byte in URIClient 2017-04-25 17:17:51 +02:00
Ethan Frey
803b1f2115 Improve client test cases 2017-04-25 16:49:01 +02:00
Ethan Buchman
3cdd2daf08 fix tests 2017-04-21 18:44:37 -04:00
Ethan Buchman
4e0afc55e6 glide update 2017-04-21 18:35:48 -04:00
Ethan Buchman
e160318eef glide update 2017-04-21 18:21:59 -04:00
Ethan Buchman
56c60fba23 go-p2p -> tendermint/p2p 2017-04-21 18:19:41 -04:00
Ethan Buchman
9e82d132ce go-rpc -> tendermint/rpc 2017-04-21 18:19:29 -04:00
Ethan Buchman
a70c95b79e tmlibs/common/test -> tmlibs/test 2017-04-21 18:18:22 -04:00
Ethan Buchman
d5b524e309 go-merkle -> merkleeyes/iavl and tmlibs/merkle 2017-04-21 18:16:05 -04:00
Ethan Buchman
e6fe6b5b76 go-data -> go-wire/data 2017-04-21 18:13:25 -04:00
Ethan Buchman
d1926bcad1 use tmlibs 2017-04-21 18:12:54 -04:00
Ethan Buchman
fa451fc55c tendermint/rpc -> tendermint/rpc/tendermint 2017-04-21 18:10:41 -04:00
Ethan Buchman
5da9b3a803 postmerge 2017-04-21 18:09:47 -04:00
Ethan Buchman
93c58d0b24 remove glide and license from rpc and p2p 2017-04-21 18:08:25 -04:00
Ethan Buchman
23a6a6f8fc move into p2p package 2017-04-21 18:07:52 -04:00
Ethan Buchman
63f546497b Merge remote-tracking branch 'p2p/develop' into repo-merge 2017-04-21 18:06:57 -04:00
Ethan Buchman
c55d83281a move into rpc package 2017-04-21 18:05:39 -04:00
Ethan Buchman
35f1db09a9 Merge remote-tracking branch 'rpc/develop' into repo-merge 2017-04-21 18:04:42 -04:00
Ethan Buchman
34965f610d crypto Wrap/Unwrap 2017-04-21 18:02:25 -04:00
Ethan Buchman
eaeb547938 use tmlibs 2017-04-21 17:53:22 -04:00
Ethan Buchman
15d5b2ac49 use tmlibs 2017-04-21 17:51:11 -04:00
Ethan Buchman
992b11c450 premerge2: rpc -> rpc/tendermint 2017-04-21 17:39:56 -04:00
Ethan Buchman
0017fb7ffe premerge 2017-04-21 17:38:40 -04:00
Ethan Buchman
3240ce21b8 update glide 2017-04-21 17:28:13 -04:00
Ethan Buchman
207798b09a Merge pull request #454 from tendermint/reset_fix
reset fix
2017-04-21 17:27:29 -04:00
Ethan Frey
543eea4f4e update deps to unstable 2017-04-21 16:56:10 -04:00
Ethan Frey
6d223d5526 Update to latest go-crypto 2017-04-21 16:55:58 -04:00
Ethan Frey
3d9ca32e95 Update all config for p2p integration tests 2017-04-21 16:55:38 -04:00
Ethan Frey
90abc61c56 Improve go-data json support in rpc 2017-04-21 16:55:37 -04:00
Ethan Frey
6a0217688f Ensure private validator addresses are hex 2017-04-21 16:51:17 -04:00
Ethan Frey
b798169c6e Update go-crypto to read/write properly with go-wire in wal files 2017-04-21 16:51:17 -04:00
Anton Kaliaev
7e56aad51a [consensus/test_data/build.sh] install tendermint if absent 2017-04-21 16:51:17 -04:00
Ethan Frey
e325ffc681 Lots of updates to use new go-crypto / json style 2017-04-21 16:51:17 -04:00
Ethan Frey
516e78ea54 Fix types to use updated go-crypto 2017-04-21 16:50:27 -04:00
rigel rozanski
c21cec002c reset fix 2017-04-21 16:39:50 -04:00
Ethan Buchman
f89279cf11 fix CHANGELOG 2017-04-21 13:39:02 -04:00
Ethan Buchman
44f25864d9 Merge pull request #451 from tendermint/release-v0.9.1
Release v0.9.1
2017-04-21 13:35:23 -04:00
Ethan Buchman
7f20eca892 update glide 2017-04-21 13:09:55 -04:00
Ethan Buchman
2c8df0ee6b Merge pull request #17 from tendermint/develop
v0.7.0
2017-04-21 13:08:14 -04:00
Ethan Buchman
e8f33a4784 Merge pull request #25 from tendermint/develop
v0.5.0
2017-04-21 13:06:44 -04:00
Ethan Buchman
58ccefa407 update changelog 2017-04-21 13:06:26 -04:00
Ethan Buchman
e8cad948e3 cli: ResetAll doesnt depend on cobra 2017-04-21 12:54:53 -04:00
Ethan Buchman
53e2b9693a export ResetAll cmd 2017-04-21 12:46:12 -04:00
Ethan Buchman
559613689d Merge pull request #18 from tendermint/bugfix/fix-backward-compatibility-for-ws
fix backward compatibility for WS
2017-04-21 12:20:34 -04:00
Ethan Buchman
a01cff9ce6 jsonParamsToArgsRPC func 2017-04-21 12:18:21 -04:00
Ethan Buchman
62860e4919 Merge pull request #453 from tendermint/feature/rename-txid-to-hash
Rename txid to hash
2017-04-21 12:16:08 -04:00
Anton Kaliaev
5e5fb37774 rename TxID to Hash 2017-04-21 18:39:02 +03:00
Anton Kaliaev
d6fd0c4ca0 fix backward compatibility for WS 2017-04-21 18:30:22 +03:00
Ethan Buchman
7cf773e2d3 cli: testnet cmd inits files for testnet 2017-04-20 18:22:42 -04:00
Ethan Buchman
f5b77d50b5 fix setting log level 2017-04-20 17:56:49 -04:00
Ethan Buchman
757a548edf update glide 2017-04-20 17:56:35 -04:00
Ethan Buchman
17124989a9 Merge pull request #10 from tendermint/pex-reactor-fixes-#9
Pex reactor fixes #9
2017-04-20 17:32:38 -04:00
Ethan Buchman
75bad132fc msgCountByPeer is a CMap 2017-04-20 17:29:43 -04:00
Ethan Buchman
391c738959 update comment about outbound peers and addrbook 2017-04-20 12:21:45 -04:00
Anton Kaliaev
8655e2456e it is non-deterministic (could fail sometimes) 2017-04-20 13:37:06 +04:00
Anton Kaliaev
17ec70fc09 revert 2710873 2017-04-20 13:36:40 +04:00
Anton Kaliaev
9ce71013df revert e448199 2017-04-20 13:36:40 +04:00
Anton Kaliaev
5ab8ca0868 fix race 2017-04-20 13:36:40 +04:00
Anton Kaliaev
4c0d1d3ad2 return wg to addrbook 2017-04-20 13:36:39 +04:00
Anton Kaliaev
0277e52bd5 fix merge 2017-04-20 13:36:39 +04:00
Anton Kaliaev
cf18bf2966 add public RemoveAddress API
after discussion with @ebuchman (https://github.com/tendermint/go-p2p/pull/10#discussion_r96471729)
2017-04-20 13:36:39 +04:00
Anton Kaliaev
324293f4cb note on preventing abuse [ci skip] 2017-04-20 13:36:39 +04:00
Anton Kaliaev
52d9cf080e make GoLint happy 2017-04-20 13:36:39 +04:00
Anton Kaliaev
590efc1040 call saveToFile OnStop
This is better than waiting because while we wait, anything could happen
(crash, timeout of the code who's using addrbook, ...). If we save
immediately, we have much greater chances of success.
2017-04-20 13:36:38 +04:00
Anton Kaliaev
5eeaffd38e do not create file, just temp dir 2017-04-20 13:36:38 +04:00
Anton Kalyaev
07e7b98c70 improve ensurePeers routine
optimizations:

- if we move peer to the old bucket as soon as connected and pick only
  from new group, we can skip alreadyConnected check
2017-04-20 13:36:38 +04:00
Anton Kalyaev
873d34157d prevent abuse from peers 2017-04-20 13:36:38 +04:00
Anton Kalyaev
47df1fb7d4 test PEXReactor#Receive 2017-04-20 13:36:38 +04:00
Anton Kalyaev
1a59b6a3b4 replace repeate timer with simple ticker
no need for repeate timer here (no need for goroutine safety)
2017-04-20 13:36:38 +04:00
Anton Kalyaev
0109f1e524 test ensurePeers goroutine 2017-04-20 13:36:37 +04:00
Anton Kalyaev
37d5a2cf3e implement RemovePeer for PEXReactor 2017-04-20 13:36:37 +04:00
Anton Kalyaev
3af7c67757 add Dockerfile 2017-04-20 13:36:37 +04:00
Anton Kalyaev
26f661a5dd prefer short names 2017-04-20 13:36:37 +04:00
Anton Kalyaev
057cfb30f1 remove unused error 2017-04-20 13:36:37 +04:00
Ethan Buchman
1a42f946dc version bump 2017-04-19 00:05:18 -04:00
Ethan Buchman
e05052b079 update glide 2017-04-19 00:01:55 -04:00
Ethan Buchman
7d5b62b61f CHANGELOG and version bump 2017-04-18 23:58:24 -04:00
Ethan Buchman
a75353da6d version bump 2017-04-18 23:21:57 -04:00
Ethan Buchman
083fe959e2 Update README [ci skip] 2017-04-18 23:14:10 -04:00
Ethan Buchman
480b24d9f0 CHANGELOG [ci skip] 2017-04-18 23:05:48 -04:00
Ethan Buchman
aecf860b26 update glide 2017-04-18 22:27:24 -04:00
Ethan Buchman
bd369cc451 Merge pull request #450 from tendermint/fix-fastsync
blockpool: fix removePeer bug
2017-04-18 22:21:10 -04:00
Ethan Buchman
07143a4353 Merge pull request #412 from tendermint/feature/237-tx-indexing
tx indexing (Refs #237)
2017-04-18 22:20:48 -04:00
Ethan Buchman
bf7521a6ab Merge branch 'develop' into feature/237-tx-indexing 2017-04-18 22:20:13 -04:00
Ethan Buchman
fdeceb9a74 Merge pull request #449 from tendermint/fix-replay
Fix replay
2017-04-18 22:18:34 -04:00
Ethan Buchman
52d03d0071 post rebase fixes 2017-04-18 21:35:00 -04:00
Ethan Buchman
29a893b193 update comment 2017-04-18 21:28:11 -04:00
Jae Kwon
cf4074cc80 defer gr.Close() fixes 2017-04-18 21:28:10 -04:00
Ethan Buchman
cb2ed5bb7c fixes from review 2017-04-18 21:28:10 -04:00
Ethan Buchman
2ba3656ffd wal: gr.Close() 2017-04-18 21:28:10 -04:00
Jae Kwon
cd9e9e9f45 s/ExecBlock/ValExecBlock/g; s/sm.ApplyBlock/sm.ExecCommitBlock/g 2017-04-18 21:28:10 -04:00
Ethan Buchman
f9d0096744 support #HEIGHT based WAL 2017-04-18 21:28:10 -04:00
Ethan Buchman
935f70a346 comments and cleanup 2017-04-18 21:28:10 -04:00
Ethan Buchman
5109746b1c Handshake uses ApplyBlock, no ConsensuState 2017-04-18 21:28:10 -04:00
Ethan Buchman
1684ec163f ABCIResponses not needed as field in state 2017-04-18 21:27:50 -04:00
Ethan Buchman
3a973b80ac update glide 2017-04-18 21:27:31 -04:00
Ethan Buchman
ed03cb5c17 consensus/replay: remove timeout 2017-04-18 21:27:31 -04:00
Ethan Buchman
54b26869d5 consensus/wal: #HEIGHT -> #ENDHEIGHT 2017-04-18 21:27:31 -04:00
Ethan Buchman
cb279bf662 state: ABCIResponses, s.Save() in ApplyBlock 2017-04-18 21:27:31 -04:00
Ethan Buchman
9d2de2b756 tx_indexer -> tx_index 2017-04-18 20:55:40 -04:00
Ethan Buchman
e6b7e66bbe Merge pull request #23 from tendermint/more-tests
More tests
2017-04-18 20:26:54 -04:00
Ethan Buchman
45876d0828 NewBatch takes size, batch.Add doesn't use append 2017-04-18 20:23:58 -04:00
Ethan Buchman
b6a04a3456 more fixes from review 2017-04-18 20:11:53 -04:00
Ethan Buchman
f4d0076344 TxResult includes Tx. /tx only works if indexer active 2017-04-18 19:56:41 -04:00
Ethan Buchman
d572bb0c5d state/txindex and pkg per indexer impl 2017-04-18 19:29:02 -04:00
Anton Kaliaev
2ac69176e1 add a comment for MConnection#CanSend
also add a note to TestMConnectionSend
2017-04-18 12:11:48 +04:00
Ethan Buchman
00847cdc6b blockpool: fix removePeer bug 2017-04-15 02:22:03 -04:00
Anton Kaliaev
fbedb426ce tests for NetAddress 2017-04-14 16:37:07 +04:00
Anton Kaliaev
6dc113aa80 [netaddress] panic only when normal run 2017-04-14 14:56:02 +04:00
Anton Kaliaev
ebe23f1379 refactor MConnection#sendBytes 2017-04-14 14:21:58 +04:00
Anton Kaliaev
06d219db8e test peer with no auth enc 2017-04-14 12:43:28 +04:00
Ethan Buchman
16bffdf7ab rpc/test: restore txindexer after setting null 2017-04-13 16:38:44 -04:00
Ethan Frey
c648bd5b31 Test /tx with indexer disabled 2017-04-13 22:26:07 +02:00
Ethan Buchman
6e065affe5 rpc: /tx allows height+hash 2017-04-13 16:04:36 -04:00
Ethan Frey
4ee9acb8a7 Improve tx tests for both prove true/false 2017-04-13 16:04:35 -04:00
Ethan Frey
20458564b2 Expose Tx method in the clients 2017-04-13 21:49:21 +02:00
Ethan Buchman
c848056438 rpc: better arg validation for /tx 2017-04-13 15:18:58 -04:00
Ethan Frey
b33a7c46ce Break new Tx rpc endpoint 2017-04-13 20:49:06 +02:00
Ethan Frey
a4ee7d25d1 Add TxIndexEnabled method to ResultStatus 2017-04-13 20:21:40 +02:00
Ethan Buchman
58c860ba11 rpc/test: /tx 2017-04-13 14:18:35 -04:00
Ethan Buchman
90d1ed87fd add tx_indexer to NodeInfo 2017-04-13 14:18:20 -04:00
Ethan Buchman
df35989742 /tx can take height+index or hash 2017-04-13 13:47:48 -04:00
Ethan Buchman
23dbd0c8c3 enable tx_indexer by default 2017-04-13 13:36:12 -04:00
Ethan Buchman
257d81ddd1 rpc/core/types: uintX -> int 2017-04-13 13:35:16 -04:00
Anton Kaliaev
1d01f6af98 2 kinds of peers: outbound and inbound 2017-04-13 12:36:16 +04:00
Anton Kaliaev
715b8c629f use the peer struct to simulate remote peer 2017-04-13 12:09:43 +04:00
Anton Kaliaev
a63e1bb2dc fix possible panic 2017-04-13 12:08:57 +04:00
Anton Kaliaev
5965578c56 [fuzz] only one way to set config variables 2017-04-13 11:55:14 +04:00
Ethan Buchman
4671c44b2d Merge pull request #13 from tendermint/allow-for-multiple-restarts
[WSClient] allow for multiple restarts
2017-04-12 19:32:24 -04:00
Ethan Buchman
052c2c1575 Merge pull request #11 from tendermint/feature/refactor-tests
WSClient failing to echo bytes
2017-04-12 19:32:14 -04:00
Ethan Buchman
4b30cb3083 test: check err on cmd.Wait 2017-04-12 19:30:05 -04:00
Ethan Buchman
585ce45a5e rpc: dial_seeds msg. addresses #403 2017-04-12 19:12:22 -04:00
Ethan Buchman
6899c91ebe add optional 'prove' flag to /tx 2017-04-12 18:55:00 -04:00
Ethan Buchman
7fb0e8b30b Merge branch 'feature/tx-proof' into feature/237-tx-indexing
Conflicts:
	types/tx.go
2017-04-12 18:48:39 -04:00
Ethan Buchman
a1387c7c17 remove expected panic in test 2017-04-12 18:45:37 -04:00
Ethan Buchman
ffe6d58a58 add Height to ResultBroadcastTxCommit and EventDataTx 2017-04-12 18:33:48 -04:00
Ethan Buchman
2a59cda77e /tx returns tx bytes 2017-04-12 18:18:17 -04:00
Ethan Buchman
8c38543357 fix error msg 2017-04-12 18:15:51 -04:00
Ethan Buchman
2742831fb9 Merge pull request #443 from tendermint/draw_deps
Draw deps
2017-04-12 17:50:38 -04:00
Adrian Brink
6bcbd14d5a Add line counting badge 2017-04-12 17:39:47 -04:00
Adrian Brink
cf875a51fd Fix draw_deps in Makefile and add resulting dependency graph 2017-04-12 17:39:44 -04:00
Ethan Buchman
c34e136be9 update go-rpc to support map and array params 2017-04-12 13:44:10 -04:00
Ethan Buchman
c3295f4878 RPCRequest.Params can be map[string]interface{} or []interface{} 2017-04-12 13:42:19 -04:00
Ethan Frey
705e7bd577 Implemented and tested Txs.Index, hopefully better coverage 2017-04-12 15:18:09 +02:00
Ethan Frey
285a2a7061 More thorough testing of mutated bytes, use fixed go-wire 2017-04-12 15:18:09 +02:00
Ethan Frey
fd68bc7cfd Test Tx proofs secure 2017-04-12 15:16:46 +02:00
Ethan Frey
28307fd4c9 Add proof generation for one tx 2017-04-12 15:16:46 +02:00
Anton Kaliaev
7dcc3dbcd1 test peer 2017-04-12 16:55:17 +04:00
Ethan Buchman
72b2be51ec Merge pull request #446 from tendermint/fix-develop
Fix develop
2017-04-11 16:16:10 -04:00
Ethan Buchman
9775ecde99 update glide 2017-04-11 15:45:21 -04:00
Ethan Frey
c0f026a9b3 update go-rpc to fix race condition 2017-04-11 15:45:16 -04:00
Ethan Buchman
ac86e664c7 Revert "Undo last two commits"
This reverts commit d1fc37ff9e.
2017-04-11 15:44:36 -04:00
Ethan Buchman
c39e001a95 Merge pull request #22 from tendermint/persistent
fix closing conn
2017-04-11 13:40:06 -04:00
Ethan Buchman
8067cdb5f2 fix closing conn 2017-04-11 12:42:11 -04:00
Ethan Frey
9d18cbe74e Remove race condition between read go-routine and stop 2017-04-11 13:38:15 +02:00
Ethan Frey
d1fc37ff9e Undo last two commits 2017-04-11 12:57:06 +02:00
Ethan Buchman
7fbe8e47d4 fix tests 2017-04-10 17:32:48 -04:00
Ethan Buchman
d7c5690f17 index by bytes. add TxID to broadcast_tx responses 2017-04-10 17:21:37 -04:00
Ethan Frey
d3069b0f5b Update abci develop 2017-04-10 22:46:03 +02:00
Ethan Buchman
ebd3929c0d Merge pull request #18 from tendermint/13-reconnect-to-seeds
persistent peers (Refs 13)
2017-04-10 16:21:02 -04:00
Ethan Buchman
9a1a6c56b4 dont expose makePersistent 2017-04-10 16:05:00 -04:00
Ethan Buchman
b6f744c732 fix AddPeerWithConnection 2017-04-10 16:03:14 -04:00
Ethan Buchman
a9bb6734e7 SetDeadline for authEnc. Stop peer if Add fails 2017-04-10 16:02:01 -04:00
Ethan Frey
e4e17a2c95 Play well with go-{rpc,crypto,data}:develop 2017-04-10 21:16:41 +02:00
Anton Kaliaev
8bb3a2e1d7 persistent peers (Refs #13) 2017-04-10 22:47:05 +04:00
Anton Kaliaev
f88d56b2f8 add glide 2017-04-10 22:47:04 +04:00
Anton Kaliaev
5b0489cdb4 use plain struct instead of go-config 2017-04-10 22:46:49 +04:00
Anton Kaliaev
b8a939a894 test non persistent mconnection 2017-04-10 22:46:48 +04:00
Anton Kaliaev
2b02843453 remove unused const 2017-04-10 22:46:48 +04:00
Anton Kaliaev
5be72672fe use golang time datatype instead of time units in name 2017-04-10 22:46:48 +04:00
Anton Kaliaev
549d3bd09a tests for MConnection 2017-04-10 22:46:48 +04:00
Anton Kaliaev
868017cf1a import go-common as cmn 2017-04-10 22:46:48 +04:00
Anton Kaliaev
3478de50a1 no need for map - tx responses should arrive in order (Refs #237)
```
me: so we are executing them in order and receiving them in order and there is no way we could receive them out of order (due to network or something else), correct?
ebuchman: if we receive them out of order, ABCI is broken
ebuchman: so it is possible, if the ABCI server we're talking to is not implementing the spec
ebuchman: but that shouldn't justify us building a map
```
2017-04-10 22:44:08 +04:00
Anton Kaliaev
63704454a3 expose /tx?hash="XXXXXXXXXXXX" RPC call 2017-04-10 22:44:07 +04:00
Anton Kaliaev
b08f29cb71 add config option for tx indexing and disable it by default 2017-04-10 22:44:07 +04:00
Anton Kaliaev
d62e85757f execution test 2017-04-10 22:44:07 +04:00
Anton Kaliaev
c3f1b08b6a tx indexing (Refs #237)
save transactions to blockstore

move to a separate module

benchmark KVIndexer

batch write transactions

Benchmarks:

```
BenchmarkKVIndexerIndex-2         100000            516300 ns/op
PASS
ok      github.com/tendermint/tendermint/blockchain/tx  56.506s

5,16 s for 10000 transactions
1 s for 2000 transactions
```

```
BenchmarkKVIndexerIndex-2       h 3000000             8622 ns/op
PASS
ok      github.com/tendermint/tendermint/blockchain/tx  34.210s

86 ms for 10000 transactions
16 ms for 2000 transactions
```

```
BenchmarkKVIndexerIndex1-2               5000000              7160 ns/op
BenchmarkKVIndexerIndex500-2               20000           1750411 ns/op
BenchmarkKVIndexerIndex1000-2              10000           3573973 ns/op
BenchmarkKVIndexerIndex2000-2               5000           7836851 ns/op
BenchmarkKVIndexerIndex10000-2              1000          33438980 ns/op
PASS
ok      github.com/tendermint/tendermint/blockchain/tx  209.482s

7,8 ms for 2000 transactions
```

[state] write test for ApplyBlock

review comments

- move txindexer to state
- fix type

save Tx Index as well

do not store tx itself in the result
2017-04-10 22:44:07 +04:00
Ethan Buchman
fc95c9872f Merge pull request #438 from tendermint/replay-fix
consensus: timeout on replayLastBlock
2017-04-10 10:33:48 -04:00
Ethan Buchman
09f7dabd5e update comment 2017-03-28 14:06:03 -04:00
Ethan Buchman
4fd1471f11 remove BaseService.OnStart 2017-03-28 12:09:11 -04:00
Ethan Buchman
85e83934a1 fixes from review 2017-03-28 12:07:32 -04:00
Anton Kaliaev
ba5382b70e open result&error channels on start 2017-03-28 14:17:40 +04:00
Anton Kaliaev
b0d2032488 use BaseService.OnReset method to recreate channels 2017-03-28 14:01:22 +04:00
Ethan Buchman
162fbdd17b Merge pull request #419 from tendermint/cli_cobra
added use of Cobra CLI
2017-03-27 17:16:43 -04:00
Ethan Buchman
0413a87eb4 fix typo 2017-03-27 16:59:54 -04:00
Ethan Buchman
c1dc1a1a45 Merge pull request #437 from tendermint/feature/431-data-flag
rename TMROOT to TMHOME
2017-03-27 15:47:02 -04:00
Ethan Buchman
077cf13a1f consensus: timeout on replayLastBlock 2017-03-27 15:41:45 -04:00
Anton Kaliaev
a416c37ebd Merge pull request #12 from tendermint/close-ws-connection
close ws connection on Stop
2017-03-27 20:50:18 +04:00
Anton Kaliaev
12ead6cc7e make changes backwards compatible (Refs #431) 2017-03-27 20:41:00 +04:00
Anton Kaliaev
5f6de800a0 rename TMROOT to TMHOME (Refs #431) 2017-03-27 15:17:10 +04:00
Anton Kaliaev
d6587be7bc [WSClient] allow for multiple restarts
needed for 3044f66ba9
See https://github.com/tendermint/tools/issues/6
2017-03-21 22:08:08 +04:00
Anton Kaliaev
afc39febed close ws connection on Stop 2017-03-21 22:02:25 +04:00
Anton Kaliaev
b54b9b4ecc update url to network monitor [ci skip] [circleci skip] 2017-03-13 14:25:57 +04:00
Anton Kaliaev
5d19a008ce add Call method to WSClient, which does proper encoding of params 2017-03-10 15:33:45 +04:00
Anton Kaliaev
3233c9c003 WSClient failed to "echo_bytes"
Error:
```
Expected nil, but got: encoding/hex: invalid byte: U+0078 'x'
```
2017-03-10 14:56:04 +04:00
Anton Kaliaev
c88257b038 rename rpc function status to echo
echo means we're returning the input, which is exactly what this
function does.
2017-03-10 12:57:14 +04:00
Anton Kaliaev
0874c72819 refactor tests 2017-03-10 12:52:40 +04:00
Anton Kaliaev
d66ebbd904 use testify package 2017-03-10 12:03:16 +04:00
Anton Kaliaev
1a3573bf17 Merge pull request #10 from tendermint/feature/4-rename-http-clients
rename ClientURI -> URIClient, ClientJSONRPC -> JSONRPCClient
2017-03-10 10:42:22 +04:00
Anton Kaliaev
e6c083f589 rename ClientURI -> URIClient, ClientJSONRPC -> JSONRPCClient (Refs #4) 2017-03-10 10:41:10 +04:00
Anton Kaliaev
759060f47e Merge pull request #9 from tendermint/feature/8-http-interface-and-1-key-value-params-json-rpc
support key-value params in JSONRPC
2017-03-10 10:29:00 +04:00
Ethan Frey
715f78e26a Properly encode json.RawMessage 2017-03-10 10:20:38 +04:00
Anton Kaliaev
db69845ded introduce errors pkg 2017-03-09 19:01:37 +04:00
Anton Kaliaev
ff90224ba8 fix "Expected map but got type string" error
Error from tendermint:

```
panic: Expected map but got type string [recovered]
        panic: Expected map but got type string

goroutine 82 [running]:
testing.tRunner.func1(0xc420464000)
        /usr/local/go/src/testing/testing.go:622 +0x29d
panic(0xa1fda0, 0xc4201eecd0)
        /usr/local/go/src/runtime/panic.go:489 +0x2cf
github.com/tendermint/tendermint/rpc/test.waitForEvent(0xc420464000, 0xc420064000, 0xae6fae, 0x8, 0xae6f01, 0xc2e998, 0xc2e9a0)
        /home/vagrant/go/src/github.com/tendermint/tendermint/rpc/test/helpers.go:179 +0x53a
github.com/tendermint/tendermint/rpc/test.TestWSNewBlock(0xc420464000)
        /home/vagrant/go/src/github.com/tendermint/tendermint/rpc/test/client_test.go:190 +0x12e
testing.tRunner(0xc420464000, 0xc2e9a8)
        /usr/local/go/src/testing/testing.go:657 +0x96
created by testing.(*T).Run
        /usr/local/go/src/testing/testing.go:697 +0x2ca
```
2017-03-09 19:01:32 +04:00
Anton Kaliaev
720b74d89e read from ErrorsCh also 2017-03-09 17:44:00 +04:00
Anton Kaliaev
05e1a22d5b encode params before sending in JSONRPC 2017-03-09 13:46:48 +04:00
Anton Kaliaev
cf11e6ba65 add CHANGELOG 2017-03-09 12:43:24 +04:00
Anton Kaliaev
1ddb60b6e7 refactor jsonParamsToArgs
Suggested in https://github.com/tendermint/go-rpc/pull/9#discussion_r105098390
2017-03-09 12:23:21 +04:00
Ethan Buchman
5420254b36 changelog: add prehistory 2017-03-09 01:34:11 -05:00
Ethan Buchman
55b3c22d99 publish.sh to push build to s3 2017-03-09 01:34:11 -05:00
Anton Kaliaev
fed84f875c fix jsonParamsToArgsWS index error
Error from tendermint:
```
panic: runtime error: index out of range

goroutine 82 [running]:
github.com/tendermint/tendermint/vendor/github.com/tendermint/go-rpc/server.jsonParamsToArgsWS(0xc4200960e0, 0xc42024d4a0, 0xc420215380, 0x3, 0x0, 0x0, 0xc420215383, 0x9, 0xc42024d4a0, 0xf1ecc0, ...)
        /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-rpc/server/handlers.go:184 +0x654
github.com/tendermint/tendermint/vendor/github.com/tendermint/go-rpc/server.(*wsConnection).readRoutine(0xc4201fd0e0)
        /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-rpc/server/handlers.go:496 +0x3a9
created by github.com/tendermint/tendermint/vendor/github.com/tendermint/go-rpc/server.(*wsConnection).OnStart
        /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-rpc/server/handlers.go:377 +0x45
```
2017-03-08 17:55:08 +04:00
Anton Kaliaev
1842e03315 revert using local import
this breaks the client's code (e.g. tendermint)
2017-03-08 17:33:46 +04:00
Anton Kaliaev
d033cd54b8 add editorconfig 2017-03-08 17:17:42 +04:00
Anton Kaliaev
2dc6ab3896 use golang default if an arg is missing (Refs #7) 2017-03-08 17:16:01 +04:00
Anton Kaliaev
6d66cc68ed make sure we are using correct server
also remove it afterwards
2017-03-08 16:55:15 +04:00
Anton Kaliaev
51d760f29f use local import for testing 2017-03-08 16:23:38 +04:00
Anton Kaliaev
a4154e76c5 update docker readme [ci skip] [circleci skip] 2017-03-08 14:09:54 +04:00
Anton Kaliaev
524f3b2d57 Merge pull request #429 from tendermint/update-docker-image-for-0.9.0
update Dockerfile for 0.9.0 release
2017-03-08 14:06:57 +04:00
Anton Kaliaev
d474baeeea update Dockerfile for 0.9.0 release 2017-03-08 14:05:32 +04:00
Anton Kaliaev
22ba8bdef8 fix Call method signature in HTTPClient interface 2017-03-08 10:26:13 +04:00
Anton Kaliaev
d43e3db978 fix circleci 2017-03-07 19:28:00 +04:00
Anton Kaliaev
26ccb4c94a remove private call methods
Q: what was the reason to create them?
2017-03-07 19:27:52 +04:00
Anton Kaliaev
c128957723 "must remove file for test to run again" - no way I am doing this by hands, too lazy :) 2017-03-07 19:27:38 +04:00
Anton Kaliaev
66867bf949 remove "rpc" prefix from package imports 2017-03-07 19:27:32 +04:00
Anton Kaliaev
e1d5873bdf support key-value params in JSONRPC (Refs #1)
More changes:

- remove Client interface (reason: empty)
- introduce HTTPClient interface, which can be used for both ClientURI
  and ClientJSONRPC clients (so our users don't have to create their own) (Refs #8)
- rename integration tests script to `integration_test.sh`
- do not update deps on `get_deps`
2017-03-07 19:27:27 +04:00
Anton Kaliaev
b03facd828 add Dockerfile 2017-03-07 18:34:13 +04:00
rigelrozanski
4e743649be glide update 2017-03-06 17:36:53 -05:00
rigelrozanski
fab518fc98 flag fix, glide update
squash
2017-03-06 17:32:42 -05:00
rigelrozanski
fa609366d4 melekes change request 2017-03-06 17:30:06 -05:00
rigelrozanski
569fd474c2 added use of Cobra CLI
squash
2017-03-06 17:30:06 -05:00
Ethan Buchman
d4f6254551 Merge pull request #426 from tendermint/release-0.9.0
Release 0.9.0
2017-03-06 05:34:45 -05:00
Ethan Buchman
e31ed6dc2f Merge branch 'master' into release-0.9.0 (woops) 2017-03-06 04:27:38 -05:00
Ethan Buchman
8449e9794a CHANGELOG 2017-03-06 04:25:55 -05:00
Ethan Buchman
07a9242dba update glide 2017-03-06 04:02:09 -05:00
Ethan Buchman
097da55a2c test/p2p: shellcheck 2017-03-06 03:49:48 -05:00
Ethan Buchman
97a5ed2d1a Merge pull request #16 from tendermint/develop
v0.4.0
2017-03-06 03:11:04 -05:00
Ethan Buchman
caaafa192b test/persist: use unix socket for rpc 2017-03-06 02:42:00 -05:00
Ethan Buchman
53d777a2d5 CHANGELOG.md 2017-03-06 01:30:41 -05:00
Ethan Buchman
fc6d22db32 test: better client naming 2017-03-06 01:11:00 -05:00
Ethan Buchman
d58a666445 test/persist: bump sleep to 5 for bound ports release 2017-03-06 00:32:24 -05:00
Ethan Buchman
22d95c7b51 test: docker exec doesnt work on circle 2017-03-05 23:38:11 -05:00
Ethan Buchman
b1cd677711 types: valSet LastProposer->Proposer and Proposer()->GetProposer() 2017-03-05 23:28:42 -05:00
Ethan Buchman
1208296dc0 DialSeeds takes an AddrBook 2017-03-05 23:14:15 -05:00
Ethan Buchman
cfc7fed31c test/pex: dial_seeds 2017-03-05 23:14:11 -05:00
Ethan Buchman
beb3eda438 fix addrbook start/stop 2017-03-05 22:59:18 -05:00
Ethan Buchman
c94bc2bc2b DialSeeds takes an addrbook 2017-03-05 21:57:07 -05:00
Ethan Buchman
e4e70ece3f test: fix docker and apps 2017-03-05 20:39:52 -05:00
Ethan Buchman
0fa34f7f67 fix ProposerSelection by persisting proposer 2017-03-05 19:45:24 -05:00
Ethan Buchman
55602b9be6 failing ProposerSelection test 2017-03-05 15:05:36 -05:00
Ethan Buchman
749df0536f test/docker: install abci apps first 2017-03-05 14:59:02 -05:00
Ethan Buchman
2037d2631a fix race 2017-03-05 03:40:36 -05:00
Ethan Buchman
de0153a1c4 consensus: some more informative logging 2017-03-05 02:15:46 -05:00
Ethan Buchman
d93d754972 test/p2p/fast_sync: use --pex on restart 2017-03-05 02:03:49 -05:00
Ethan Buchman
a5ce4f6c36 update glide 2017-03-05 00:19:53 -05:00
Ethan Buchman
05d8cd50b5 update glide and node.go for update to p2p.AddrBook 2017-03-04 23:45:54 -05:00
Anton Kaliaev
af5cd5cc75 [p2p tests] test other peers connect to us
if we have neither seeds nor the addrbook
2017-03-04 23:23:02 -05:00
Anton Kaliaev
4090a31d19 save seeds to addrBook (Refs #335) 2017-03-04 23:23:02 -05:00
Anton Kalyaev
163fe1731b test p2p pex reactor (Refs #335) 2017-03-04 23:22:48 -05:00
Anton Kalyaev
eef9124d1b fix typo 2017-03-04 23:21:57 -05:00
Ethan Buchman
56eebb95ee Merge pull request #12 from tendermint/bugfix/pex-issues-335
PEX issues #335
2017-03-04 23:19:51 -05:00
Ethan Buchman
88b5c724f2 remove public addr book funcs from pex 2017-03-04 22:55:42 -05:00
Anton Kaliaev
65b1756978 expose 2 API functions for tendermint#node/node.go 2017-03-04 22:44:25 -05:00
Anton Kaliaev
108beae7a8 more tests for AddrBook 2017-03-04 22:44:25 -05:00
Anton Kaliaev
2773410de4 prevent nil addr
Error:

```
Error: runtime error: invalid memoryaddress or nil pointer dereference\nStack:
goroutine 549 [running]:\nruntime/debug.Stack(0x0, 0x0,
0x0)\n\t/usr/local/go/src/runtime/debug/stack.go:24
+0x80\ngithub.com/tendermint/tendermint/vendor/github.com/tendermint/go-p2p.(*MConnection)._recover(0xc821723b00)\n\t/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-p2p/connection.go:173
+0x53\npanic(0xbe1500, 0xc820012080)\n\t/usr/local/go/src/runtime/panic.go:443
+0x4e9\ngithub.com/tendermint/tendermint/vendor/github.com/tendermint/go-p2p.(*NetAddress).Valid(0x0,
0x0)\n\t/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-p2p/netaddress.go:125
+0x1c\ngithub.com/tendermint/tendermint/vendor/github.com/tendermint/go-p2p.(*NetAddress).Routable(0x0,
0xc8217bb740)\n\t/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-p2p/netaddress.go:117
+0x25\ngithub.com/tendermint/tendermint/vendor/github.com/tendermint/go-p2p.(*AddrBook).addAddress(0xc820108380,
0x0,
0xc821739590)\n\t/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-p2p/addrbook.go:524
+0x45\ngithub.com/tendermint/tendermint/vendor/github.com/tendermint/go-p2p.(*AddrBook).AddAddress(0xc820108380,
0x0,
0xc821739590)\n\t/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-p2p/addrbook.go:160
+0x286\ngithub.com/tendermint/tendermint/vendor/github.com/tendermint/go-p2p.(*PEXReactor).Receive(0xc82000be60,
0xc820149f00, 0xc8218163f0, 0xc82184e000, 0x5b,
0x1000)\n\t/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-p2p/pex_reactor.go:109
+0x457\ngithub.com/tendermint/tendermint/vendor/github.com/tendermint/go-p2p.newPeer.func1(0xc82011d500,
0xc82184e000, 0x5b,
0x1000)\n\t/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-p2p/peer.go:58
+0x202\ngithub.com/tendermint/tendermint/vendor/github.com/tendermint/go-p2p.(*MConnection).recvRoutine(0xc821723b00)\n\t/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-p2p/connection.go:439
+0x1177\ncreated by
github.com/tendermint/tendermint/vendor/github.com/tendermint/go-p2p.(*MConnection).OnStart\n\t/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/go-p2p/connection.go:138
+0x1a1\n
```
2017-03-04 22:40:48 -05:00
Anton Kalyaev
e7656873c1 public save API 2017-03-04 22:40:48 -05:00
Anton Kalyaev
332f7056f7 start/stop the book with reactor
Refs https://github.com/tendermint/tendermint/issues/335
2017-03-04 22:40:48 -05:00
Ethan Buchman
027cb8dc6b glide: use versions where applicable 2017-03-04 22:11:44 -05:00
Ethan Buchman
c7386b139b glide update 2017-03-04 22:05:14 -05:00
rigelrozanski
f5c4fdc82a seeds fix 2017-03-04 21:43:03 -05:00
Ethan Buchman
8352ec4e5d circle sigh 2017-03-03 19:58:17 -05:00
Ethan Buchman
8192bb0aaf stop rpc listeners in node.OnStop() 2017-03-03 19:47:39 -05:00
Ethan Buchman
b13924701e test/persist: wait for ports to be freed 2017-03-03 18:38:40 -05:00
Ethan Buchman
17e6ae813f Merge pull request #15 from tendermint/seedsfix
Seedsfix
2017-03-03 17:33:23 -05:00
Ethan Buchman
dab31d0166 version bump to 0.4.0 2017-03-03 17:30:38 -05:00
Ethan Buchman
0e7baf027b some dial seeds fixes 2017-03-03 16:42:10 -05:00
rigelrozanski
26275ba66c dial seeds error handling 2017-03-03 16:42:10 -05:00
Ethan Buchman
7098e5c7eb glide update 2017-03-03 01:33:13 -05:00
Ethan Buchman
4747f14a2c version bump to 0.9.0 2017-03-02 23:58:55 -05:00
Ethan Buchman
692691938c remove comment 2017-03-02 23:57:28 -05:00
Ethan Buchman
a200505ae5 Merge pull request #422 from tendermint/fix-race
Fix race
2017-03-02 23:56:18 -05:00
Ethan Buchman
8ba79252c8 types: use mtx on PartSet.String() 2017-03-02 23:50:59 -05:00
Ethan Buchman
6456654307 test: add extra kill after fail index triggered 2017-03-02 20:46:30 -05:00
Ethan Frey
21501815dd Fix EventSwitch usage in WaitForOneEvent 2017-02-27 14:55:29 +01:00
Ethan Frey
d56cb2ab4b fix typo 2017-02-24 23:40:05 +01:00
Ethan Frey
4fead237f0 Client embeds EventSwitch, client.HTTP properly un/subscribes events over websocket 2017-02-24 21:26:17 +01:00
Ethan Frey
6282fad518 Clean up event switch add helper function 2017-02-24 19:59:40 +01:00
Ethan Frey
175bb329e4 Expose EventSwitch on top of websocket client 2017-02-24 19:15:22 +01:00
Ethan Frey
9be3064904 Expose and test EventSwitch in client.Local 2017-02-24 17:51:19 +01:00
Ethan Frey
98450ee2db Client: DumpConsensusState, not DialSeeds. Cleanup 2017-02-24 17:05:15 +01:00
Ethan Frey
931af6a072 Combine local and http into client package, unify tests with table-driven tests 2017-02-24 17:05:15 +01:00
Ethan Frey
202146e4ce Fix up checktx/delivertx in broadcastTx(A)sync 2017-02-24 17:05:15 +01:00
Ethan Frey
9693795c4c Move common code to merkleeyes/testutil 2017-02-24 17:05:15 +01:00
Ethan Frey
cd9ee9d84b cleanup 2017-02-24 17:05:15 +01:00
Ethan Frey
42a9b847ec Make all client tests safe to run in parallel 2017-02-24 17:05:15 +01:00
Ethan Frey
d92a5b1074 Reworked WaitForHeight to avoid race conditions 2017-02-24 17:05:14 +01:00
Ethan Frey
f7f7cf576a Fix race condition in websocket event listening 2017-02-24 17:05:14 +01:00
Ethan Frey
26f4b5c98e Clean up package names 2017-02-24 17:05:14 +01:00
Ethan Frey
70f19e809b Add MockStatus, WaitForHeight helper 2017-02-24 17:05:14 +01:00
Ethan Frey
0905332f1d MockClient for real abci app 2017-02-24 17:05:14 +01:00
Ethan Frey
df172fa840 Provide mock interfaces for calling abci app over tendermint rpc 2017-02-24 17:05:14 +01:00
Ethan Frey
ce044dbb76 Extracted Clients into a consistent interface, fixed type issue in http.Client 2017-02-24 17:05:14 +01:00
Ethan Frey
7c26be3242 Begin implementation of local client 2017-02-24 17:05:13 +01:00
Ethan Frey
bf1ee89b27 Moved httpclient into subpackage 2017-02-24 17:05:13 +01:00
Ethan Frey
a0bdae4f9c Added missing pkg/errors dependency 2017-02-24 17:05:13 +01:00
Ethan Frey
64feaa05f4 Full test coverage for rpc client 2017-02-24 17:05:13 +01:00
Ethan Frey
5ea3f24304 test info 2017-02-24 17:05:13 +01:00
Ethan Frey
c9d36cd713 Add dependencies, pull out HTTPClient test code 2017-02-24 17:05:13 +01:00
Ethan Frey
2c75c9daf9 Clean up tests, remove panics 2017-02-24 17:05:12 +01:00
Ethan Frey
d971416d12 collision merge of light-client code 2017-02-24 17:05:12 +01:00
Anton Kaliaev
1f1dcead3d [Vagrantfile] go 1.8 and permissions fix 2017-02-24 13:31:27 +04:00
Anton Kaliaev
d38a6e329f verbose output when get_deps 2017-02-24 13:30:46 +04:00
Anton Kaliaev
f73f53c486 [Dockerfile.develop] fix error "/bin/sh: #: not found" when building 2017-02-23 12:17:20 +04:00
Ethan Buchman
b8279d424c Merge pull request #414 from tendermint/ci
Ci
2017-02-21 15:17:16 -05:00
Ethan Buchman
98a509ed97 Merge pull request #373 from melekes/bugfix/174-peer-send-failures-are-not-checked
Check `peer.Send` failures
2017-02-21 15:00:50 -05:00
Ethan Buchman
7c1e79cbc5 test/persists: wait for tendermint proc 2017-02-21 14:14:08 -05:00
Ethan Buchman
7fab31fbe3 test: more logging 2017-02-21 11:18:40 -05:00
Anton Kaliaev
2c724d5eee remove warning messages in favor of "Send failed" 2017-02-21 13:25:16 +04:00
Anton Kaliaev
6dbe9febce log warning if peer send failed (Refs #174)
make lint happy

remove dead code

remove not needed go-common dependency

check peer.Send failures (Refs #174)
2017-02-21 11:57:33 +04:00
Ethan Buchman
d754d210cd test: only use syslog on circle 2017-02-21 01:28:58 -05:00
Ethan Buchman
62adbe69ff Merge pull request #413 from tendermint/ci
test: forward CIRCLECI var through docker
2017-02-20 23:11:22 -05:00
Ethan Buchman
f3da6d23cb test: forward CIRCLECI var through docker 2017-02-20 22:53:14 -05:00
Ethan Buchman
a4d5ec491e Merge pull request #411 from tendermint/bugfix/init-should-not-overwrite-existing-files
do not overwrite existing files when doing `tendermint init`
2017-02-20 22:03:22 -05:00
Ethan Buchman
bed86da8ae Merge pull request #408 from tendermint/mock_app
handshake replay through consensus using mockApp
2017-02-20 22:01:26 -05:00
Ethan Buchman
0c4b6cd071 consensus: more handshake replay tests 2017-02-20 21:45:53 -05:00
Ethan Buchman
f9df4294f3 move some interfaces to types/services.go 2017-02-20 20:09:15 -05:00
Ethan Buchman
0765613778 move handshake to consensus package 2017-02-20 19:52:36 -05:00
Ethan Buchman
1fa6e7f3b1 test: shellcheck 2017-02-20 18:51:00 -05:00
Ethan Buchman
2b1b8da58d test/persist: dont use log files on circle 2017-02-20 18:41:29 -05:00
Ethan Buchman
756213c5f5 check appHash 2017-02-20 17:08:38 -05:00
Ethan Buchman
bc67859672 make ReplayBlocks logic exhaustive 2017-02-20 16:32:48 -05:00
Anton Kaliaev
a99885cd43 do not overwrite existing files when doing tendermint init (Fixes #410) 2017-02-20 11:14:27 +04:00
Ethan Buchman
44d472ddd3 comments from review 2017-02-18 22:15:59 -05:00
Ethan Buchman
7228b11e3f state: remove StateIntermediate 2017-02-17 19:13:35 -05:00
Ethan Buchman
0bec99fbd4 consensus: handshake replay test using wal 2017-02-17 19:12:05 -05:00
Ethan Buchman
3c5adebcd3 applyBlock to simplify replay of many blocks. still wip 2017-02-17 11:32:56 -05:00
Ethan Buchman
edc5e272db consensus: nice error msg if ApplyBlock fails 2017-02-17 10:57:09 -05:00
Ethan Buchman
6403b2f468 fixes for handshake replay through consensus 2017-02-17 10:51:05 -05:00
Ethan Buchman
cbe6dbe7a1 handshake replay through consensus using mockApp 2017-02-16 17:56:45 -05:00
Ethan Buchman
3ebd69db1b Merge pull request #405 from tendermint/rpc-commit
Rpc commit
2017-02-16 16:11:39 -05:00
Ethan Buchman
a3898fae0f rpc: fix SeenCommit condition 2017-02-16 15:35:34 -05:00
Ethan Buchman
75c856ed3f Merge pull request #404 from tendermint/feature/develop-docker-container
dockerfile for develop branch
2017-02-16 13:21:54 -05:00
Ethan Buchman
d081485e6a Merge pull request #402 from tendermint/feature/collect-logs-from-docker-containers
collect and add docker logs to CircleCI artifacts (Refs #387)
2017-02-16 13:13:13 -05:00
Ethan Buchman
bb5688b1be make: dont use -v on go test 2017-02-14 17:09:47 -05:00
Ethan Buchman
6f83f0bd4e glide update 2017-02-14 17:09:12 -05:00
Ethan Buchman
99b068b313 BlockMeta uses BlockID 2017-02-14 17:06:58 -05:00
Ethan Buchman
e229c8c3d7 rpc: /commit 2017-02-14 17:06:47 -05:00
Anton Kaliaev
6b499e2a8b add Makefile for docker files [ci skip] [circleci skip] 2017-02-15 01:10:29 +04:00
Anton Kaliaev
a756a9feba add readme mirroring https://hub.docker.com/r/tendermint/tendermint/ 2017-02-15 01:10:29 +04:00
Anton Kaliaev
d0ca0cb308 otherwise we end up with unmount errors due to busy disk
```
local_testnet_1
Failed to remove container (local_testnet_1): Error response from daemon: Unable to remove filesystem for 74e1104c705d4f21137bbe9e6710f33511948fc04d26475b482d1314fe70d537: remove /var/lib/docker/containers/74e1104c705d4f21137bbe9e6710f33511948fc04d26475b482d1314fe70d537/shm: device or resource busy
local_testnet_2
Failed to remove container (local_testnet_2): Error response from daemon: Unable to remove filesystem for 6f2ec44e01afd85875c1d9958afa957f1623ed9464df2b4fcac06feb8254145e: remove /var/lib/docker/containers/6f2ec44e01afd85875c1d9958afa957f1623ed9464df2b4fcac06feb8254145e/shm: device or resource busy
local_testnet_3
Failed to remove container (local_testnet_3): Error response from daemon: Unable to remove filesystem for 6160c73d45376920d90473a487a07f0e283e383011a0d23050b22cd82ab5d255: remove /var/lib/docker/containers/6160c73d45376920d90473a487a07f0e283e383011a0d23050b22cd82ab5d255/shm: device or resource busy
local_testnet_4
Failed to remove container (local_testnet_4): Error response from daemon: Driver btrfs failed to remove root filesystem c93a30d66ff3710617915a8f6075755ebc45aebf73569420225105cc81003e2f: Failed to destroy btrfs snapshot /var/lib/docker/btrfs/subvolumes for cac56069a3682b6d133e239f479fb82a67c4ccd09b877794d9f440a1125d59d6: operation not permitted

* [15:37:40] stopping rsyslog container
Failed to remove container (rsyslog): Error response from daemon: Driver btrfs failed to remove root filesystem 477287a170a48156e663c55092257064e1f14770d0b6c634a9972893636d9451: Failed to destroy btrfs snapshot /var/lib/docker/btrfs/subvolumes for 76b66d582a478743c099f479bf88eb334ad303ef607f91021dfa58d28c9161eb: operation not permitted
make: *** [test_integrations] Error 1
```
2017-02-14 20:40:23 +04:00
Anton Kaliaev
86f85525dd dockerfile for develop branch 2017-02-14 20:30:15 +04:00
Anton Kaliaev
ad354c4c48 stop containers to avoid futher errors
Error:

```
Failed to remove container (rsyslog): Error response from daemon: Unable to remove filesystem
```
2017-02-14 19:23:28 +04:00
Anton Kaliaev
b5bb1657d2 increase memory quota for Vagrant users
`make test_integrations` requires > 2Gi of memory
2017-02-14 19:09:03 +04:00
Anton Kaliaev
1275458c3f collect and add docker logs to CircleCI artifacts (Refs #387)
How: 1) we start syslog docker container 2) all other containers use
syslog logging driver to ship their logs to that container
2017-02-14 19:02:11 +04:00
Ethan Buchman
ac971c1a19 some docs fixes 2017-02-13 21:40:26 -05:00
Ethan Buchman
7d91d4300b update glide 2017-02-13 21:07:26 -05:00
Ethan Buchman
e972b942e6 Merge pull request #399 from tendermint/feature/cli-cleanup-gen-validator-output
[cli] cleanup gen_validator output
2017-02-09 13:32:30 -05:00
Anton Kaliaev
4896364952 [cli] cleanup gen_validator output (Fixes #396)
so we can pipe result to a file:

```
tendermint gen_validator > example.json
```

before this we had to cut first 3 lines:

```
tendermint gen_validator | sed 1,3d > example.json
```
2017-02-08 23:24:19 +04:00
Anton Kaliaev
ca21b6be93 update Vagrantfile
* glide now gets installed as a part of make get_vendor_deps
* rm go .tar.gz file
* autoremove garbage
* set LC_ALL to en_US.UTF-8
2017-02-08 22:56:17 +04:00
Ethan Frey
d4b3dde853 make install now uses vendored dependencies 2017-02-08 19:28:25 +01:00
Ethan Buchman
1aceeb7116 Merge pull request #392 from tendermint/feature/issue-template
ISSUE_TEMPLATE for Github
2017-02-08 02:17:11 -05:00
Ethan Buchman
930b3bcf5c Merge pull request #393 from tendermint/feature/CONTRIBUTING.md
contributing.md
2017-02-08 02:16:22 -05:00
Ethan Buchman
937c06a0c9 Merge pull request #395 from tendermint/feature/alpine-docker-base-image
[docker] move to alpine base image
2017-02-08 02:11:57 -05:00
Ethan Buchman
77cc15a77a Merge pull request #394 from tendermint/feature/improve-circleci-setup
Improve CircleCI setup (WIP)
2017-02-07 09:25:07 -05:00
Anton Kaliaev
d5d7286cb6 improve circleci config
- update docker machine version to 0.9.0
- save coverage.txt as artifact
- save docker logs as artifact
- test if coverage.txt exists
- 2>&1 redirect stderr to stdout in tests
2017-02-07 13:16:47 +04:00
Anton Kaliaev
bf64dd21fd [docker] move to alpine base image
813Mb => 29.5Mb
2017-02-06 00:11:03 +04:00
Ethan Frey
36b5d86eda [ci skip] [circle skip] Document research on backing dbs 2017-02-05 16:29:37 +01:00
Ethan Frey
1bf5ae9c8d Finish main argument on merkle data store architecture 2017-02-03 19:44:08 +01:00
Ethan Frey
5c2ead741c Lots more text on transactions and interface 2017-02-03 18:54:19 +01:00
Ethan Frey
b31e5d7764 Starting some thoughts on merkle storage 2017-02-03 15:39:35 +01:00
Anton Kaliaev
e5ef3e3019 add ISSUE_TEMPLATE for github [ci skip] [circle skip] 2017-02-03 13:07:59 +04:00
Anton Kaliaev
d696550494 add CONTRIBUTING.md [ci skip] [circle skip] 2017-02-03 13:07:11 +04:00
Ethan Frey
5fe0440916 Strawman docs/architecture dir 2017-02-02 20:10:56 +01:00
Ethan Buchman
d545ac4c51 Merge pull request #383 from tendermint/feature/rename-dockerimage-to-tendermint
Rename docker image to tendermint
2017-01-31 11:18:18 -08:00
Ethan Buchman
23eb8691a8 Merge pull request #386 from tendermint/bugfix/385-dist-build-error
permissions error during make dist
2017-01-31 11:07:28 -08:00
Anton Kaliaev
f849e2c414 copy instead of move (Fixes #385)
```
mv: cannot move ‘./build/dist/windows_386.zip’ to ‘./build/dist/tendermint_0.8.0_windows_386.zip’: Permission denied
```
2017-01-31 11:30:54 +04:00
Anton Kaliaev
1f527c5013 fix go version in Vagrantfile 2017-01-30 12:03:09 +04:00
Anton Kaliaev
65dfacac4b do we really have to build in order to test? 2017-01-30 12:02:24 +04:00
Anton Kaliaev
c0e7d05b5c rename tmbase image to tendermint
The main reason is people usually expect docker image to have the same
name as the repo. Plus, tendermint is cleaner than tmbase.

tmbase would make sense if we had multiple docker images within
tendermint, but we don't.
2017-01-30 11:59:35 +04:00
Jae Kwon
67ab574e98 Cleanup, add stub for VerifyCommitAny 2017-01-29 13:50:53 -08:00
Ethan Buchman
846e6d3bda fix readme links [ci skip] 2017-01-29 13:38:54 -08:00
Ethan Buchman
9707cb4471 README for local testnets [ci skip] 2017-01-28 19:25:28 -05:00
Jae Kwon
7c15b54ccc Fix test/app/dummy_test.sh 2017-01-28 09:11:31 -08:00
Jae Kwon
1af930441c Support new Query message for proofs 2017-01-28 08:27:13 -08:00
Ethan Buchman
9257d648bf test: update docker to 1.7.4 2017-01-28 00:12:39 -05:00
Ethan Buchman
f890ae696a Merge remote-tracking branch 'anton/feature/binaries' into unstable 2017-01-27 23:22:47 -05:00
Ethan Buchman
82828524fe Merge remote-tracking branch 'anton/improvement/dockerfile' into unstable 2017-01-27 22:59:01 -05:00
Ethan Buchman
d120c328c9 Merge remote-tracking branch 'anton/feature/output-all-tendermint-commands' into unstable 2017-01-27 22:54:14 -05:00
rigelrozanski
449a29b817 fixed README link for contributing guidelines 2017-01-27 22:44:01 -05:00
Anton Kaliaev
ce18332b52 update Dockerfile
- update golang to 1.7.4
- version as env variable
- change DATA_ROOT from /tendermint/data to /tendermint (it's not just
  data that gets stored in DATA_ROOT; we create data folder on start; as
  a result we get /tendermint/data/data, which is confusing)
- remove noninteractive env vars (do we really need these?)
- remove nodejs dep (some apps may require nodejs, but core is not one
  of them; it was convenient before, but now I believe we ought to
  remove it because other people who are using java do not want a
  bloated container with nodejs)
- build tendermint inside a container (once again, it was convenient
  before, but now I am testing kubernetes and I don't want to wait every
  time TM compiles)
2017-01-27 21:10:13 +04:00
Anton Kaliaev
3b7a1d7149 check that we have enough arguments
Otherwise:

```
panic: runtime error: index out of range

goroutine 1 [running]:
panic(0xbb8de0, 0xc82000e080)
        /usr/local/go/src/runtime/panic.go:464 +0x3e6
main.main()
        /go/src/github.com/tendermint/tendermint/cmd/tendermint/main.go:48 +0x811
```
2017-01-24 21:20:29 +04:00
Anton Kaliaev
17e822757b output all commands 2017-01-24 21:19:45 +04:00
Anton Kaliaev
a388ff198d try to get version from version/version.go 2017-01-23 15:23:39 +04:00
Anton Kaliaev
cc2457f7d5 dist command to make binaries and package them for distribution 2017-01-23 14:45:13 +04:00
Anton Kaliaev
c6375e414d refactor Makefile
- mute most of the commands
- replace github.com/tendermint/tendermint with just "."
- introduce PACKAGES variable
- delete unused NEWLINE
2017-01-22 18:04:58 +04:00
Anton Kaliaev
e825d77736 add .editorconfig to maintain consistent coding style 2017-01-22 18:00:08 +04:00
Ethan Buchman
ce026f4099 Merge pull request #369 from tendermint/node_refactor
Refactor Node; Node is a simple BaseService
2017-01-20 14:19:37 -05:00
Jae Kwon
9a2dd8bc92 Refactor Node; Node is a simple BaseService 2017-01-15 16:59:10 -08:00
Jae Kwon
a073b1db9c Refactor replay console -> replay_file in consensus/replay_file.go 2017-01-15 16:19:02 -08:00
Jae Kwon
cf0cb9558a Clean glide files 2017-01-14 21:03:12 -08:00
Jae Kwon
0aecfe2dae Merge pull request #368 from tendermint/release-0.8.0
Merge release-0.8.0 to develop
2017-01-13 16:03:27 -08:00
Jae Kwon
764091dfbb Merge pull request #366 from tendermint/release-0.8.0
Release 0.8.0
2017-01-13 15:59:34 -08:00
Ethan Buchman
ab1fa4db8c test: split up test/net/test.sh 2017-01-13 17:20:03 -05:00
Ethan Buchman
9123e63a33 more glide updates 2017-01-13 00:54:06 -05:00
Ethan Buchman
ba7ca4f372 fix tests 2017-01-13 00:54:01 -05:00
Ethan Buchman
1e25e9f58f glide update for go-db test fix 2017-01-12 22:49:52 -05:00
Ethan Buchman
9b4660b458 version bump to 0.8.0 2017-01-12 22:07:55 -05:00
Ethan Buchman
16a5c6b2c8 update all glide deps to master 2017-01-12 22:05:03 -05:00
Ethan Buchman
fcea0cda21 Merge pull request #6 from tendermint/develop
v0.6.0
2017-01-12 22:02:40 -05:00
Ethan Buchman
6177eb8398 love you circley 2017-01-12 22:01:20 -05:00
Ethan Buchman
ac443fa61f run tests from bash script 2017-01-12 21:59:02 -05:00
Ethan Buchman
08f2b5bc84 get deps for testing 2017-01-12 21:51:34 -05:00
Ethan Buchman
de56442660 Merge branch 'master' into develop 2017-01-12 21:40:43 -05:00
Ethan Buchman
3d98f675f3 Merge pull request #11 from tendermint/develop
Develop
2017-01-12 21:39:57 -05:00
Ethan Buchman
67c9086b74 optional panic on AddPeer err 2017-01-12 21:09:24 -05:00
Ethan Buchman
e47722ecb2 Connect2Switches: panic on err 2017-01-12 20:49:48 -05:00
Jae Kwon
2b750ea49f Make Connect2Switches blocking 2017-01-12 20:49:48 -05:00
Ethan Buchman
58e42397f8 close conns on filter; fix order in MakeConnectedSwitch 2017-01-12 20:49:48 -05:00
Jae Kwon
bd353e004a QuitService->BaseService 2017-01-12 20:49:48 -05:00
Ethan Buchman
f347143b3d Merge branch 'master' into develop
Conflicts:
	cmd/tendermint/flags.go
	glide.lock
	glide.yaml
	node/node.go
	rpc/core/routes.go
	version/version.go
2017-01-12 20:48:10 -05:00
Ethan Buchman
c1952523cd update glide 2017-01-12 20:22:23 -05:00
Ethan Buchman
1afe0cb45f test: always rebuild grpc_client 2017-01-12 18:45:41 -05:00
Ethan Buchman
7075a8ed81 Merge pull request #365 from tendermint/rename
Rename
2017-01-12 16:41:52 -05:00
Ethan Buchman
d83ca54b36 file name fixes 2017-01-12 16:17:43 -05:00
Ethan Buchman
e0aead0be2 update glide 2017-01-12 16:12:41 -05:00
Ethan Buchman
94b6dd65ee AppendTx -> DeliverTx 2017-01-12 15:55:03 -05:00
Ethan Buchman
c147b41013 TMSP -> ABCI 2017-01-12 15:53:32 -05:00
Ethan Buchman
3a55339114 Merge pull request #364 from tendermint/collapse_info
tmsp: ResponseInfo and ResponseEndBlock
2017-01-12 15:43:59 -05:00
Ethan Buchman
2dd7030579 tmsp: ResponseInfo and ResponseEndBlock 2017-01-12 15:21:20 -05:00
Ethan Buchman
e7a12f8e38 cs.Wait() 2017-01-12 14:44:42 -05:00
Ethan Buchman
0525e8ed5c rearrange common_test.go; EnsureDir for privVal 2017-01-12 12:38:09 -05:00
Ethan Buchman
1d091bafe9 update glide 2017-01-12 11:00:43 -05:00
Ethan Buchman
814ef37f75 fix tests 2017-01-12 10:58:44 -05:00
Ethan Buchman
94fed25975 fix test 2017-01-12 10:22:23 -05:00
Ethan Buchman
56341de5eb Merge remote-tracking branch 'mappum/rpc-tests' into unstable 2017-01-12 02:46:10 -05:00
Ethan Buchman
3c589dac19 startConsensusNet and stopConsensusNet 2017-01-12 02:29:53 -05:00
Ethan Buchman
0eb278ad3b version bump 0.6.0 2017-01-12 00:13:20 -05:00
Ethan Buchman
b494cc5219 Merge pull request #5 from mappum/develop
Fix hex string handling
2017-01-12 00:12:11 -05:00
Ethan Buchman
618fce8d32 update glide 2017-01-11 23:47:28 -05:00
Ethan Buchman
ce0c638005 little fix 2017-01-11 18:37:36 -05:00
Ethan Buchman
15d3d12098 Merge pull request #342 from akalyaev/feature/update-vagrant-setup
Feature: Update Vagrant setup
2017-01-11 18:03:20 -05:00
Ethan Buchman
76b4a45f98 Merge pull request #360 from tendermint/optional-skip-timeout-commit
Optional skip timeout commit
2017-01-11 18:02:46 -05:00
Anton Kalyaev
535fc6cd63 test we can make blocks with skip_timeout_commit=false 2017-01-11 18:00:27 -05:00
Anton Kalyaev
3308ac7d83 set skip_timeout_commit to true for tests
For the tests its better to not use the timeout_commit, and to wait for all the
votes, because otherwise we can end up with timing dependencies in the testing
code which can lead to nondeterministic failures. That was part of the reason
for this change originally.
2017-01-11 18:00:26 -05:00
Anton Kalyaev
a1fd312bb1 make progress asap on full precommit votes optional (Refs #348) 2017-01-11 18:00:26 -05:00
Anton Kalyaev
b096651e10 fix glide error: unable to export dependencies to vendor directory
```
[ERROR] Unable to export dependencies to vendor directory: remove /home/vagrant/go/src/github.com/tendermint/tendermint/vendor/golang.org/x/sys/unix: directory not empty
```
2017-01-11 17:59:05 -05:00
Ethan Buchman
44f939c841 Merge pull request #359 from tendermint/val-set-changes
Val set changes
2017-01-11 17:56:52 -05:00
Ethan Buchman
d68cdce2d5 consensus: check HasAll when TwoThirdsMajority 2017-01-11 17:53:46 -05:00
Jae Kwon
43fdc4a1ce Fix #341 2017-01-11 08:57:10 -08:00
Anton Kalyaev
cb2f2b94ee log stages to stdout 2017-01-11 10:35:04 -05:00
Anton Kalyaev
4722410e5e test validator set changes more extensively 2017-01-11 10:35:04 -05:00
Ethan Buchman
55b4bfa1fe consensus: let time.Timer handle non-positive durations 2017-01-11 10:24:40 -05:00
Matt Bell
4d7aa62a10 Added test for unexpected hex string type HTTP args 2017-01-07 20:40:45 -08:00
Matt Bell
86506cd4f8 Handle quoted and hex string type HTTP args for both 'string' and '[]byte' type function args 2017-01-07 20:40:29 -08:00
Matt Bell
1532879c64 Fixed RPC client tests 2017-01-07 14:35:54 -08:00
Matt Bell
af1212897c Exit early in bash tests 2017-01-07 14:00:27 -08:00
Matt Bell
34a806578a Handle hex strings and quoted strings in HTTP params
Use 0x-prefixed hex strings in client

server: Decode hex string args

Encode all string args as 0x<hex> without trying to encode as JSON

Added tests for special string arguments

Fix server handling quoted string args

Added string arg handling test cases to bash test script
2017-01-07 13:59:33 -08:00
Ethan Buchman
12d92fd5db Merge pull request #343 from tendermint/restart_test
Crash/Restart tests
2017-01-06 11:55:05 -08:00
Ethan Buchman
bae0bc02a6 consensus: be more explicit when we need to write height after handshake 2017-01-05 20:16:42 -08:00
Anton Kalyaev
99974ddc8b update vagrant setup
I am in favor of docker, but people say that running docker containers
inside another container is bad.

Things included in provision:

- docker (latest)
- jq
- curl
- shellcheck
- golang 1.7
- glide
2016-12-26 21:21:06 +04:00
Ethan Buchman
f30a9752e2 more fixes from review 2016-12-23 11:11:22 -05:00
Ethan Buchman
c90985d309 test: set log_level=info 2016-12-22 22:25:47 -05:00
Ethan Buchman
bd222d6e3c test: more unique container names 2016-12-22 22:10:40 -05:00
Ethan Buchman
0c01b0ded9 state.State and wal.writeHeight after handshake 2016-12-22 22:10:36 -05:00
Ethan Buchman
e5fb681615 consensus: remove crankTimeoutPropose from tests 2016-12-22 22:03:42 -05:00
Ethan Buchman
c9698e4848 fixes from review 2016-12-22 22:03:42 -05:00
Ethan Buchman
0e7694ca94 state: AppHashIsStale -> IntermediateState 2016-12-22 15:01:22 -05:00
Ethan Buchman
f4e6cf4439 consensus: sync wal.writeHeight 2016-12-22 15:01:02 -05:00
Ethan Buchman
e4921733df test/persist: use fail-test failure indices 2016-12-22 02:49:54 -05:00
Anton Kalyaev
69a449a073 test/p2p: use PROXY_APP=persistent_dummy 2016-12-22 01:43:23 -05:00
Anton Kalyaev
30328548f7 test/p2p: kill and restart all nodes 2016-12-22 01:42:51 -05:00
Anton Kalyaev
57f3592411 fix 2 errors when running p2p tests more than once
Error #1:

```
Error response from daemon: network with name local_testnet already exists
```

Fixed by stopping and removing local_testnet containers and removing
the network

Error #2:

```
docker: Error response from daemon: Conflict. The name "/test_container_basic" is already in use by container a7cd15d479a964675e7f259de4ed852e7dfef85b447514728f437cd0b980a709. You have to remove (or rename) that container to beable to reuse that name..
```

Fixed by adding `--rm` flag.
2016-12-21 19:18:18 -05:00
Anton Kalyaev
1c24031dd2 rename COUNT to ID 2016-12-21 19:18:18 -05:00
Anton Kalyaev
3d47ef9d74 fix typo 2016-12-21 19:18:18 -05:00
Anton Kalyaev
6488894210 add .vagrant to .gitignore 2016-12-21 19:18:18 -05:00
Ethan Buchman
2f0d31b4b6 test: remove codecov patch threshold 2016-12-20 01:47:59 -05:00
Ethan Buchman
b2376058a1 blockchain: thread safe store.Height() 2016-12-20 00:45:45 -05:00
Ethan Buchman
e981ff4e7d tests: shorten timeouts 2016-12-20 00:17:41 -05:00
Ethan Buchman
b126ca0606 consensus: no internal vars in reactor.String() 2016-12-20 00:17:37 -05:00
Ethan Buchman
40b08f2494 consensus: mv timeoutRoutine into TimeoutTicker 2016-12-19 22:29:32 -05:00
Ethan Buchman
8211fa6ce4 enterNewRound on HasAll 2016-12-19 20:12:37 -05:00
Ethan Buchman
706dd1d6c5 test: dont start cs until all peers connected 2016-12-19 19:50:40 -05:00
Ethan Buchman
6bc3b8dc6d test: circle artifacts 2016-12-19 15:42:39 -05:00
Ethan Buchman
faf23aa0d4 consensus: TimeoutTicker, skip TimeoutCommit on HasAll 2016-12-19 15:42:36 -05:00
Ethan Buchman
18b3821e06 glide: update go-wire 2016-12-18 00:18:59 -05:00
Ethan Buchman
1bd700ee52 test: automate building consensus/test_data 2016-12-18 00:15:10 -05:00
Ethan Buchman
38783e7fa1 types: SignatureEd25519 -> Signature 2016-12-18 00:14:07 -05:00
Ethan Buchman
dcbb35089f consensus: wal.Flush() and cleanup replay tests 2016-12-17 23:43:17 -05:00
Ethan Buchman
f33cc3fb3b Merge pull request #325 from tendermint/sign_bytes
Sign bytes
2016-12-17 21:48:19 -05:00
Ethan Buchman
1b3766d802 types: canonical_json.go 2016-12-17 21:21:27 -05:00
zachary balder
da8b043612 sign bytes w struct literals 2016-12-17 21:21:27 -05:00
Ethan Buchman
ed42f70248 types: benchmark WriteSignBytes 2016-12-17 21:21:27 -05:00
Ethan Buchman
0204d3c6a6 Merge pull request #330 from tendermint/fix_tests
Fix tests
2016-12-17 21:18:14 -05:00
Ethan Buchman
81f91aebc2 test: crank circle timeouts 2016-12-17 15:16:58 -05:00
Ethan Buchman
de6bba4609 test: randConsensusNet takes more args 2016-12-17 14:45:20 -05:00
Ethan Buchman
c3a3cc76d8 Merge pull request #334 from tendermint/release-0.7.4
shame: version bump 0.7.4
2016-12-14 10:08:12 -05:00
Ethan Buchman
74ff489e63 shame: version bump 0.7.4 2016-12-14 10:05:25 -05:00
Ethan Buchman
55b47bcb8e Merge pull request #323 from tendermint/release-0.7.4
Release 0.7.4: feature flag: pex reactor
2016-12-14 09:44:34 -05:00
Ethan Buchman
bcd8712ec3 test: more cleanup on p2p 2016-12-12 16:00:21 -05:00
Ethan Buchman
b212aa0db3 fixes from review 2016-12-12 14:12:37 -05:00
Ethan Buchman
8390e88e27 fix flowrate dep and test 2016-12-09 01:28:08 -05:00
Ethan Buchman
bc95700dcf update README 2016-12-09 00:31:56 -05:00
Ethan Buchman
12c6594c9b rpc: remove restriction on DialSeeds 2016-12-09 00:31:53 -05:00
Ethan Buchman
d800a51da4 test: crank it to eleventy 2016-12-07 20:13:52 -05:00
Ethan Buchman
73502fab0d shame: forgot a file 2016-12-06 23:17:53 -05:00
Ethan Buchman
2425711734 blockchain: use ApplyBlock 2016-12-06 23:01:55 -05:00
Ethan Buchman
6be5bda8c9 types: copy commit bit array 2016-12-06 23:01:25 -05:00
Ethan Buchman
2abcde03ad tests: cleanup and fix scripts 2016-12-06 23:00:39 -05:00
Ethan Buchman
69ef1da58c types: copy vote set bit array 2016-12-06 20:53:02 -05:00
Ethan Buchman
8df32cd540 test: increase proposal timeout 2016-12-06 19:54:10 -05:00
Jae Kwon
3aa06d0851 Try CircleCI timeout extension again 2016-12-06 04:28:05 -08:00
Jae Kwon
e611e97339 Try to extend CircleCI timeout 2016-12-06 04:25:31 -08:00
Jae Kwon
3000c8b349 Update glide w/ TMSP updates 2016-12-06 04:04:08 -08:00
Jae Kwon
6f88d04ac4 call db.SetSync when necessary 2016-12-06 02:52:07 -08:00
Jae Kwon
1c16ce8cf0 Update go-db and tmsp 2016-12-06 02:18:13 -08:00
Jae Kwon
070728429b Upgrade autofile and merkle 2016-12-06 01:59:21 -08:00
Jae Kwon
918f76f96a Remove flowcontrol 2016-12-06 01:26:05 -08:00
Jae Kwon
4202c4bf20 Fix Merge pull request #319 2016-12-06 01:16:13 -08:00
Jae Kwon
eab2baa363 use go-flowrate instead of flowcontrol 2016-12-04 18:20:37 -08:00
Ethan Buchman
dc436e72f9 Merge pull request #255 from tendermint/broadcast_tx_grpc
broadcast_tx via grpc
2016-12-02 00:31:40 -05:00
Ethan Buchman
b74a97a4f6 update grpc broadcast tx 2016-12-02 00:29:25 -05:00
Ethan Buchman
2ef695da97 include check/append responses in broadcast_tx_commit 2016-12-02 00:29:25 -05:00
Ethan Buchman
db437e7a45 broadcast_tx via grpc 2016-12-02 00:29:25 -05:00
Ethan Buchman
538a50c9e8 Merge pull request #319 from tendermint/valset
Validator Set Changes
2016-12-02 00:28:07 -05:00
Ethan Buchman
0fe53dc5cf remove privValIndex; Stale->AppHashIsStale 2016-12-02 00:27:36 -05:00
Ethan Buchman
8b80f8ee05 feature flag: pex reactor 2016-11-30 23:07:45 -05:00
Ethan Buchman
2cee364692 addrbook: toggle strict routability 2016-11-30 22:57:21 -05:00
Ethan Buchman
3e7c7f51fa update glide 2016-11-23 19:44:25 -05:00
Ethan Buchman
2f9063c1d6 consensus: test validator set change 2016-11-23 18:20:46 -05:00
Ethan Buchman
65496ace20 test: tmsp query result is json 2016-11-22 21:50:54 -05:00
Ethan Buchman
64c7a0ad0d update glide 2016-11-22 21:28:57 -05:00
Ethan Buchman
5046d5b181 more handshake replay cleanup 2016-11-22 20:38:14 -05:00
Ethan Buchman
6f8c91b651 use NewValidator; fix setPrivValidatorIndex 2016-11-22 20:38:14 -05:00
Ethan Buchman
e0db20c0cf update privValidatorIndex on valset change 2016-11-22 20:38:14 -05:00
Ethan Buchman
e1e2c1c740 cleanup ReplayBlocks 2016-11-22 20:38:14 -05:00
Ethan Buchman
655b6300f5 val set changes 2016-11-22 20:38:14 -05:00
Ethan Buchman
d7f6c0775a remove LastCommitHeight 2016-11-22 20:38:14 -05:00
Ethan Buchman
a3d863f83b consensus: track index of privVal 2016-11-22 20:38:14 -05:00
Jae Kwon
deb4c428fd Update go-autofile 2016-11-21 20:29:30 -08:00
Jae Kwon
b8e94b1d30 Update autofile 2016-11-21 20:19:49 -08:00
Jae Kwon
a151216e5e Update go-autofile dependency; fixed checkTotalSizeLimit 2016-11-21 19:58:40 -08:00
Jae Kwon
e09950d3fb Use new Group semantics 2016-11-21 19:16:19 -08:00
Ethan Buchman
3206e101f5 Merge pull request #265 from tendermint/handshake
Handshake
2016-11-16 17:00:01 -05:00
Ethan Buchman
c6a648fad7 consensus: lock before loading commit 2016-11-16 16:47:31 -05:00
Ethan Buchman
904eeddf36 update glide 2016-11-16 16:25:52 -05:00
Ethan Buchman
07597dfd45 post rebase fixes for BlockID, partSize 2016-11-16 16:13:17 -05:00
Ethan Buchman
4360c360a4 move handshake to state, use Handshaker, more tests 2016-11-16 13:29:22 -05:00
Ethan Buchman
befd8b0cb2 post rebase fixes 2016-11-16 13:27:06 -05:00
Ethan Buchman
3f90fcae48 fail tests and fix 2016-11-16 13:26:39 -05:00
Ethan Buchman
8ec1839f5d save block b4 apply; track stale apphash 2016-11-16 13:26:39 -05:00
Ethan Buchman
fb9735ef46 rebase fixes and BeginBlock(hash,header) 2016-11-16 13:25:13 -05:00
Ethan Buchman
138de19e1e test: app persistence 2016-11-16 13:25:13 -05:00
Ethan Buchman
d3ae920bd0 state: ApplyBlock 2016-11-16 13:25:13 -05:00
Ethan Buchman
f37f56d4f1 fixes 2016-11-16 13:25:13 -05:00
Ethan Buchman
a0e4253edc handshake 2016-11-16 13:25:13 -05:00
Ethan Buchman
c3d5634efa begin block 2016-11-16 13:23:57 -05:00
Ethan Buchman
c05b2c5c59 Merge pull request #314 from tendermint/bft_fix_rebase
Bft fix rebase
2016-11-16 01:52:54 -05:00
Ethan Buchman
f763a9ef56 scripts/txs/random.sh 2016-11-16 01:32:13 -05:00
Ethan Buchman
81e6df0d57 cswal: write #HEIGHT:1 for empty wal 2016-11-16 01:23:19 -05:00
Jae Kwon
d83fc02597 MakePartSet takes partSize from config. fix replay test 2016-11-16 01:23:16 -05:00
Jae Kwon
95c8bb4252 Fixing issues from review in #229 2016-11-15 18:48:35 -05:00
Jae Kwon
c1729addce Fix BFT issue where VoteSetMaj23Message wasn't being sent where prs.Round == blockStore.Round() 2016-11-15 18:48:35 -05:00
Jae Kwon
3e3b034252 Make ConsensusReactor use ConsensusState's blockstore; debug functions 2016-11-15 18:48:34 -05:00
Ethan Buchman
9d0c7f6ec7 fix bft test. still halts 2016-11-15 18:47:19 -05:00
Ethan Buchman
c5a803a146 DataChannel -> StateChannel for Maj23Msg 2016-11-15 18:45:36 -05:00
Ethan Buchman
5f55ed2a40 consensus: ensure dir for cswal on reactor tests 2016-11-15 18:45:36 -05:00
Ethan Buchman
7afcf92539 consensus: fix panic on POLRound=-1 2016-11-15 18:45:36 -05:00
Ethan Buchman
57da2e4af5 make byzantine logic testable 2016-11-15 18:45:36 -05:00
Ethan Buchman
f837252ff1 consensus: test reactor 2016-11-15 18:37:33 -05:00
Jae Kwon
fd128c7180 Fix comments from review 2016-11-15 18:37:00 -05:00
Jae Kwon
ea4b60a602 Fix compile bug 2016-11-15 18:37:00 -05:00
Jae Kwon
b73a6905a1 Initial pass at bft_fix_2 completion 2016-11-15 18:37:00 -05:00
Jae Kwon
655d829314 Fix proposal sign bytes. Start tracking blockID in POL 2016-11-15 18:35:17 -05:00
Jae Kwon
1173a85c85 Use BlockID everywhere 2016-11-15 18:34:58 -05:00
Jae Kwon
40791d886d Add test for new VoteSet 2016-11-15 18:33:16 -05:00
Jae Kwon
7221887330 VoteSet can handle conflicting votes. TODO: add more tests 2016-11-15 18:33:16 -05:00
Jae Kwon
3c5a2f55c2 Add validator index and address to Vote. 2016-11-15 18:33:16 -05:00
Ethan Buchman
0b098a2eee use glide in install_tmsp_apps.sh 2016-11-15 18:05:38 -05:00
Ethan Buchman
1765d3c866 update glide 2016-11-15 15:57:03 -05:00
Ethan Buchman
1fedf5b332 Merge branch 'consensus_wal_autofile' into develop_old 2016-11-15 15:43:22 -05:00
Ethan Buchman
f94836783c Merge branch 'master' into develop_old 2016-11-15 15:43:10 -05:00
Jae Kwon
2aecb2a4a3 Ensure *_wal_dir exists 2016-11-05 09:15:34 -07:00
Jae Kwon
bf1bceec87 Use go-flowcontrol 2016-11-04 06:46:34 -07:00
Ethan Buchman
01a3ac50af update glide 2016-11-03 20:16:44 -04:00
Ethan Buchman
3ff9355e7b change some logs to debug 2016-11-03 20:13:39 -04:00
Ethan Buchman
94ac890859 send BeginBlock 2016-11-03 19:51:22 -04:00
Jae Kwon
3d3d8b5b7b cswal -> cs_wal_dir 2016-10-30 03:55:27 -07:00
Jae Kwon
1788a68b1c Consensus WAL uses AutoFile/Group 2016-10-28 15:01:14 -07:00
Jae Kwon
480f44f16c QuitService->BaseService 2016-10-28 12:14:24 -07:00
Jae Kwon
161e36fd56 QuitService->BaseService 2016-10-28 12:04:58 -07:00
Jae Kwon
9a089482dc Unnest 2016-10-28 11:58:09 -07:00
Jae Kwon
fc31b463b1 Don't use io.Seek*, not supported in older versions 2016-10-26 22:19:44 -07:00
Jae Kwon
539f8f91bd Update glide file w/ go-common develop branch & go-autofile 2016-10-26 21:59:56 -07:00
Jae Kwon
830e84adc4 Fix minor bug in Consensus WAL; Fix AutoFile dependency 2016-10-26 21:51:03 -07:00
Ethan Buchman
40f2a128b8 Merge pull request #307 from tendermint/release-0.7.3
Release 0.7.3
2016-10-20 21:36:40 -04:00
Ethan Buchman
e6e3853dc7 Merge pull request #3 from tendermint/develop
use EventSwitch interface; less logging
2016-10-20 21:31:26 -04:00
Ethan Buchman
1786890e32 Merge branch 'master' into release-0.7.3 2016-10-20 21:11:17 -04:00
Ethan Buchman
435d1e3da7 test: install glide for network test 2016-10-20 12:30:22 -04:00
Jae Kwon
642a24dc9c Mempool WAL 2016-10-17 16:54:51 -07:00
Jae Kwon
eab4e1cfa1 Remove redundant cs.WAL from NewNode() 2016-10-17 16:37:32 -07:00
Jae Kwon
a9d8039082 Fix peer memleak; stop goroutine when peer is offline 2016-10-17 11:29:43 -07:00
Ethan Buchman
2113b6f4bb rpc: use interfaces for pipe 2016-10-14 21:38:49 -04:00
Ethan Buchman
7d493774c7 log: move some Info to Debug 2016-10-14 20:27:50 -04:00
Ethan Buchman
de0fc87c1c test: use glide with mintnet/netmon 2016-10-12 12:28:34 -04:00
Ethan Buchman
f0871e4f5e update some scripts 2016-10-12 12:27:37 -04:00
Ethan Buchman
1b37c8affd version: bump 0.7.3 2016-10-12 00:24:24 -04:00
Ethan Buchman
3e29f2bdc2 Merge pull request #297 from tendermint/docs
remove INSTALL dir, add INSTALL.md, update DOCKER
2016-10-11 23:30:38 -04:00
Ethan Buchman
3f4af438c5 remove INSTALL dir, add INSTALL.md, update DOCKER 2016-10-11 23:27:59 -04:00
Ethan Buchman
2cb13bf852 Merge pull request #298 from tendermint/get_round_state
consensus: hvs.StringIndented() was missing a lock. addresses #284
2016-10-11 22:25:37 -04:00
Ethan Buchman
0098387fbf consensus: hvs.StringIndented needed a lock. addresses #284 2016-10-11 19:12:39 -04:00
Ethan Buchman
7e07919d9d Merge pull request #296 from tendermint/replay_fixes
Replay fixes
2016-10-11 16:36:21 -04:00
Ethan Buchman
71baad59df replay: ensure cs.height and wal.height match 2016-10-11 16:06:46 -04:00
Ethan Buchman
9365d33243 replay: more tests 2016-10-11 12:55:04 -04:00
Ethan Buchman
3c18d841fa replay: larger read buffer 2016-10-11 12:51:48 -04:00
Ethan Buchman
7dcb567e53 replay test data 2016-10-11 11:44:07 -04:00
Ethan Buchman
7a424e6b12 Merge pull request #291 from tendermint/type-safe-fire-event
Type safe fire event
2016-10-10 17:36:52 -04:00
Ethan Buchman
302bbc5dbd Merge pull request #295 from tendermint/codecov
add codecov.yml to lower threshold
2016-10-10 17:32:00 -04:00
Ethan Buchman
97a51c130f Merge pull request #290 from tendermint/mempool_txcache
Fix race condition; Refactor out txCache
2016-10-10 17:20:42 -04:00
Ethan Buchman
00d53714f9 add codecov.yml to lower threshold 2016-10-10 17:18:34 -04:00
Ethan Buchman
a07063f119 add test for mempool deadlock 2016-10-10 17:05:50 -04:00
Ethan Buchman
9393be7a49 update glide 2016-10-10 14:55:54 -04:00
Ethan Buchman
1eb390680d Merge pull request #8 from tendermint/develop
update MakeConnectedSwitches
2016-10-10 14:48:58 -04:00
Ethan Buchman
9d331715c0 Merge pull request #293 from ethanfrey/type-safe-fire-event
Add test for proper tx event
2016-10-10 14:36:01 -04:00
Ethan Frey
0665bc6e4b Add test for proper tx event 2016-10-10 09:55:47 +02:00
Ethan Buchman
855255d73e use EventSwitch interface; less logging 2016-10-10 03:22:34 -04:00
Ethan Buchman
35d4cca8bb type safe events 2016-10-10 03:10:29 -04:00
Jae Kwon
b38748ad1a Fix race condition; Refactor out txCache 2016-10-07 15:30:17 -07:00
Ethan Frey
22979d9365 Fire proper EventData object on append transaction 2016-10-01 22:12:48 +02:00
Ethan Buchman
be3592adf6 Merge pull request #280 from tendermint/develop
Develop
2016-09-26 00:44:53 +09:00
Ethan Buchman
a5f6069967 Merge pull request #279 from tendermint/codecov
Codecov and badges
2016-09-26 00:29:23 +09:00
Ethan Buchman
c481d2bcbb README shields 2016-09-26 00:17:58 +09:00
Ethan Buchman
a1649f774e test: codecov 2016-09-25 22:08:48 +09:00
Jae Kwon
22155df759 Merge pull request #276 from silasdavis/mempool-reap
Fix mistake in Mempool Reap documentation
2016-09-16 08:31:11 -07:00
Silas Davis
7f31ec3398 Fix doc comment on mempool reap 2016-09-16 17:09:18 +02:00
Ethan Buchman
153ac88672 update MakeConnectedSwitches 2016-09-14 00:57:53 -04:00
Ethan Buchman
121714c040 Merge branch 'master' into develop 2016-09-12 12:01:42 -04:00
Ethan Buchman
f508f3f20b Merge pull request #6 from tendermint/develop
filter conn by addr/pubkey. closes #3
2016-09-09 19:45:12 -04:00
Ethan Buchman
642901d5aa filter conn by addr/pubkey. closes #3 2016-08-25 13:46:43 -04:00
Ethan Buchman
479510be0e support full urls (with eg tcp:// prefix) 2016-08-10 01:13:13 -04:00
Ethan Buchman
dea910cd3e Makefile: go test --race 2016-07-22 01:15:52 -04:00
Ethan Buchman
39ee59c26e server: return result with error 2016-07-22 01:13:16 -04:00
Ethan Buchman
929cf433b9 fix chDesc race 2016-06-26 00:34:34 -04:00
Ethan Buchman
711d2541f5 MakeConnectedSwitches function 2016-06-26 00:34:23 -04:00
Ethan Buchman
a8ac819139 link issue from readme 2016-06-23 20:40:16 -04:00
Ethan Buchman
a44e0e0f4b add test example 2016-06-23 20:37:35 -04:00
Ethan Buchman
0c70a4636a add better docs to readme 2016-06-23 20:33:04 -04:00
Ethan Buchman
7ea86f6506 fix test race and update readme 2016-06-21 15:19:56 -04:00
Ethan Buchman
7376a72dd7 circle badge 2016-06-21 14:40:24 -04:00
Ethan Buchman
981c6868ad fix race conditions in tests 2016-06-21 14:35:29 -04:00
Ethan Buchman
5bd7692323 version bump 2016-05-12 00:10:46 -04:00
Ethan Buchman
6684a18730 fix test 2016-05-12 00:08:41 -04:00
Ethan Buchman
0dc6ebc325 configurable fuzz conn 2016-05-12 00:00:52 -04:00
Ethan Buchman
ffbd6d8782 drop the p2p_ 2016-05-11 23:47:51 -04:00
Jae Kwon
7d997ca8e6 No global config 2016-05-08 14:59:27 -07:00
Jae Kwon
1d9e89812a Remove go-alert dependency 2016-05-08 14:58:28 -07:00
Ethan Buchman
e8538d606a add blank client interface 2016-05-04 10:39:43 -04:00
Ethan Buchman
78c9d526c3 change some log.Info to log.Debug 2016-03-29 11:37:51 -07:00
Jae Kwon
4baf007fb8 Merge pull request #2 from tendermint/develop
make some params configurable
2016-03-27 20:44:10 -07:00
Ethan Buchman
69c7ae5e3f version bump 2016-03-17 05:05:02 -04:00
Ethan Buchman
114d90bec8 don't reallocate on recvMsgPacket 2016-03-17 05:05:02 -04:00
Jae Kwon
10619248c6 Use go-crypto CRand* 2016-03-13 09:42:36 -07:00
Ethan Buchman
389e4b8b69 config: toggle authenticated encryption 2016-03-10 19:07:01 -05:00
Jae Kwon
1bc871162d Conform to new go-config behavior; ApplyConfig not needed 2016-03-06 12:33:07 -08:00
Ethan Buchman
f28f791fff make some params configurable 2016-03-06 12:24:01 -08:00
Jae Kwon
7f6aad20fb Add note on nondeterminism of Broadcast 2016-03-04 22:04:05 -08:00
Ethan Buchman
1410693eae support unix domain websockets 2016-02-19 02:05:24 +00:00
Ethan Buchman
74130008f7 deduplicate dialFunc 2016-02-19 00:20:20 +00:00
Ethan Buchman
6607232a5d add support for unix sockets 2016-02-18 22:45:55 +00:00
Jae Kwon
1370f89864 Fix bug in receiveEventsRoutine error handling 2016-02-08 02:20:34 -08:00
Jae Kwon
8b7969d6ea Add comments about goroutine-safety 2016-02-08 00:58:12 -08:00
Ethan Buchman
45f57198cc client: wsc.String() 2016-02-03 02:01:28 -05:00
Ethan Buchman
fbc5ac8052 print method in client log 2016-01-21 23:03:39 -05:00
Ethan Buchman
b9eec7e438 version bump 2016-01-20 13:07:57 -05:00
Ethan Buchman
9bc75eaf24 move DialSeeds in from tendermint/tendermint/node 2016-01-20 11:32:23 -05:00
Jae Kwon
0380e404bd Conform to go-wire version 0.6.0 2016-01-17 21:30:06 -08:00
Ethan Buchman
14735d5eb5 RawMessage fix 2016-01-13 22:16:56 -05:00
Ethan Buchman
91c734d02e client: ResultsCh chan json.RawMessage, ErrorsCh 2016-01-13 21:21:16 -05:00
Ethan Buchman
aff561d8c3 RPCResponse.Result is json.RawMessage 2016-01-13 18:37:35 -05:00
Ethan Buchman
0bcae125c2 use comma separated string for arg names 2016-01-12 18:29:31 -05:00
Ethan Buchman
3d59e13dd8 move from tendermint/tendermint 2016-01-12 16:50:06 -05:00
Ethan Buchman
c52524a215 Initial commit 2016-01-12 15:26:00 -05:00
Jae Kwon
1f2c1d0760 Fix prioritization logic; Add Status() 2016-01-03 06:20:18 -08:00
Jae Kwon
3abc18d7ba Add MConnection.Status() 2016-01-02 20:53:10 -08:00
Jae Kwon
0c9b9fe8bb Change license to Apache2.0 2015-12-23 14:08:39 -08:00
Jae Kwon
44d8e62689 Confirm to go-wire new TypeByte behavior 2015-12-21 12:55:52 -08:00
Jae Kwon
8b308c1c08 Tweak send/recv rates for performance test 2015-12-09 13:53:50 -08:00
Jae Kwon
4347b91b89 Remove wrong comment 2015-12-05 09:48:08 -08:00
Jae Kwon
432a37857d Add RemoteAddr and ListenAddr to NodeInfo; Refactor IPRange logic 2015-12-05 09:44:03 -08:00
Jae Kwon
c37e25e76b Conform to go-common WriteFile*(path, mode) 2015-12-04 00:02:44 -08:00
Jae Kwon
1c628a97ad Conform to go-wire 1.0 2015-11-10 12:29:43 -08:00
Jae Kwon
abc3a2cc3c initial commit 2015-10-25 18:21:51 -07:00
303 changed files with 24876 additions and 5264 deletions

21
.codecov.yml Normal file
View File

@@ -0,0 +1,21 @@
#
# 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

19
.editorconfig Normal file
View File

@@ -0,0 +1,19 @@
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[Makefile]
indent_style = tab
[*.sh]
indent_style = tab
[*.proto]
indent_style = space
indent_size = 2

41
.github/ISSUE_TEMPLATE vendored Normal file
View File

@@ -0,0 +1,41 @@
<!-- Thanks for filing an issue! Before hitting the button, please answer these questions.-->
**Is this a BUG REPORT or FEATURE REQUEST?** (choose one):
<!--
If this is a BUG REPORT, please:
- Fill in as much of the template below as you can.
If this is a FEATURE REQUEST, please:
- Describe *in detail* the feature/behavior/change you'd like to see.
In both cases, be ready for followup questions, and please respond in a timely
manner. We might ask you to provide additional logs and data (tendermint & app)
in a case of bug.
-->
**Tendermint version** (use `tendermint version` or `git rev-parse --verify HEAD` if installed from source):
**ABCI app** (name for built-in, URL for self-written if it's publicly available):
**Merkleeyes version** (use `git rev-parse --verify HEAD`, skip if you don't use it):
**Environment**:
- **OS** (e.g. from /etc/os-release):
- **Install tools**:
- **Others**:
**What happened**:
**What you expected to happen**:
**How to reproduce it** (as minimally and precisely as possible):
**Anything else do we need to know**:

5
.gitignore vendored
View File

@@ -10,3 +10,8 @@ rpc/test/.tendermint
remote_dump
.revision
vendor
.vagrant
test/p2p/data/
test/logs
.glide
coverage.txt

388
CHANGELOG.md Normal file
View File

@@ -0,0 +1,388 @@
# Changelog
## 0.10.3 (August 10, 2017)
FEATURES:
- control over empty block production:
- new flag, `--consensus.create_empty_blocks`; when set to false, blocks are only created when there are txs or when the AppHash changes.
- new config option, `consensus.create_empty_blocks_interval`; an empty block is created after this many seconds.
- in normal operation, `create_empty_blocks = true` and `create_empty_blocks_interval = 0`, so blocks are being created all the time (as in all previous versions of tendermint). The number of empty blocks can be reduced by increasing `create_empty_blocks_interval` or by setting `create_empty_blocks = false`.
- new `TxsAvailable()` method added to Mempool that returns a channel which fires when txs are available.
- new heartbeat message added to consensus reactor to notify peers that a node is waiting for txs before entering propose step.
- rpc: Add `syncing` field to response returned by `/status`. Is `true` while in fast-sync mode.
IMPROVEMENTS:
- various improvements to documentation and code comments
BUG FIXES:
- mempool: pass height into constructor so it doesn't always start at 0
## 0.10.2 (July 10, 2017)
FEATURES:
- Enable lower latency block commits by adding consensus reactor sleep durations and p2p flush throttle timeout to the config
IMPROVEMENTS:
- More detailed logging in the consensus reactor and state machine
- More in-code documentation for many exposed functions, especially in consensus/reactor.go and p2p/switch.go
- Improved readability for some function definitions and code blocks with long lines
## 0.10.1 (June 28, 2017)
FEATURES:
- Use `--trace` to get stack traces for logged errors
- types: GenesisDoc.ValidatorHash returns the hash of the genesis validator set
- types: GenesisDocFromFile parses a GenesiDoc from a JSON file
IMPROVEMENTS:
- Add a Code of Conduct
- Variety of improvements as suggested by `megacheck` tool
- rpc: deduplicate tests between rpc/client and rpc/tests
- rpc: addresses without a protocol prefix default to `tcp://`. `http://` is also accepted as an alias for `tcp://`
- cmd: commands are more easily reuseable from other tools
- DOCKER: automate build/push
BUG FIXES:
- Fix log statements using keys with spaces (logger does not currently support spaces)
- rpc: set logger on websocket connection
- rpc: fix ws connection stability by setting write deadline on pings
## 0.10.0 (June 2, 2017)
Includes major updates to configuration, logging, and json serialization.
Also includes the Grand Repo-Merge of 2017.
BREAKING CHANGES:
- Config and Flags:
- The `config` map is replaced with a [`Config` struct](https://github.com/tendermint/tendermint/blob/master/config/config.go#L11),
containing substructs: `BaseConfig`, `P2PConfig`, `MempoolConfig`, `ConsensusConfig`, `RPCConfig`
- This affects the following flags:
- `--seeds` is now `--p2p.seeds`
- `--node_laddr` is now `--p2p.laddr`
- `--pex` is now `--p2p.pex`
- `--skip_upnp` is now `--p2p.skip_upnp`
- `--rpc_laddr` is now `--rpc.laddr`
- `--grpc_laddr` is now `--rpc.grpc_laddr`
- Any configuration option now within a substract must come under that heading in the `config.toml`, for instance:
```
[p2p]
laddr="tcp://1.2.3.4:46656"
[consensus]
timeout_propose=1000
```
- Use viper and `DefaultConfig() / TestConfig()` functions to handle defaults, and remove `config/tendermint` and `config/tendermint_test`
- Change some function and method signatures to
- Change some [function and method signatures](https://gist.github.com/ebuchman/640d5fc6c2605f73497992fe107ebe0b) accomodate new config
- Logger
- Replace static `log15` logger with a simple interface, and provide a new implementation using `go-kit`.
See our new [logging library](https://github.com/tendermint/tmlibs/log) and [blog post](https://tendermint.com/blog/abstracting-the-logger-interface-in-go) for more details
- Levels `warn` and `notice` are removed (you may need to change them in your `config.toml`!)
- Change some [function and method signatures](https://gist.github.com/ebuchman/640d5fc6c2605f73497992fe107ebe0b) to accept a logger
- JSON serialization:
- Replace `[TypeByte, Xxx]` with `{"type": "some-type", "data": Xxx}` in RPC and all `.json` files by using `go-wire/data`. For instance, a public key is now:
```
"pub_key": {
"type": "ed25519",
"data": "83DDF8775937A4A12A2704269E2729FCFCD491B933C4B0A7FFE37FE41D7760D0"
}
```
- Remove type information about RPC responses, so `[TypeByte, {"jsonrpc": "2.0", ... }]` is now just `{"jsonrpc": "2.0", ... }`
- Change `[]byte` to `data.Bytes` in all serialized types (for hex encoding)
- Lowercase the JSON tags in `ValidatorSet` fields
- Introduce `EventDataInner` for serializing events
- Other:
- Send InitChain message in handshake if `appBlockHeight == 0`
- Do not include the `Accum` field when computing the validator hash. This makes the ValidatorSetHash unique for a given validator set, rather than changing with every block (as the Accum changes)
- Unsafe RPC calls are not enabled by default. This includes `/dial_seeds`, and all calls prefixed with `unsafe`. Use the `--rpc.unsafe` flag to enable.
FEATURES:
- Per-module log levels. For instance, the new default is `state:info,*:error`, which means the `state` package logs at `info` level, and everything else logs at `error` level
- Log if a node is validator or not in every consensus round
- Use ldflags to set git hash as part of the version
- Ignore `address` and `pub_key` fields in `priv_validator.json` and overwrite them with the values derrived from the `priv_key`
IMPROVEMENTS:
- Merge `tendermint/go-p2p -> tendermint/tendermint/p2p` and `tendermint/go-rpc -> tendermint/tendermint/rpc/lib`
- Update paths for grand repo merge:
- `go-common -> tmlibs/common`
- `go-data -> go-wire/data`
- All other `go-` libs, except `go-crypto` and `go-wire`, are merged under `tmlibs`
- No global loggers (loggers are passed into constructors, or preferably set with a SetLogger method)
- Return HTTP status codes with errors for RPC responses
- Limit `/blockchain_info` call to return a maximum of 20 blocks
- Use `.Wrap()` and `.Unwrap()` instead of eg. `PubKeyS` for `go-crypto` types
- RPC JSON responses use pretty printing (via `json.MarshalIndent`)
- Color code different instances of the consensus for tests
- Isolate viper to `cmd/tendermint/commands` and do not read config from file for tests
## 0.9.2 (April 26, 2017)
BUG FIXES:
- Fix bug in `ResetPrivValidator` where we were using the global config and log (causing external consumers, eg. basecoin, to fail).
## 0.9.1 (April 21, 2017)
FEATURES:
- Transaction indexing - txs are indexed by their hash using a simple key-value store; easily extended to more advanced indexers
- New `/tx?hash=X` endpoint to query for transactions and their DeliverTx result by hash. Optionally returns a proof of the tx's inclusion in the block
- `tendermint testnet` command initializes files for a testnet
IMPROVEMENTS:
- CLI now uses Cobra framework
- TMROOT is now TMHOME (TMROOT will stop working in 0.10.0)
- `/broadcast_tx_XXX` also returns the Hash (can be used to query for the tx)
- `/broadcast_tx_commit` also returns the height the block was committed in
- ABCIResponses struct persisted to disk before calling Commit; makes handshake replay much cleaner
- WAL uses #ENDHEIGHT instead of #HEIGHT (#HEIGHT will stop working in 0.10.0)
- Peers included via `--seeds`, under `seeds` in the config, or in `/dial_seeds` are now persistent, and will be reconnected to if the connection breaks
BUG FIXES:
- Fix bug in fast-sync where we stop syncing after a peer is removed, even if they're re-added later
- Fix handshake replay to handle validator set changes and results of DeliverTx when we crash after app.Commit but before state.Save()
## 0.9.0 (March 6, 2017)
BREAKING CHANGES:
- Update ABCI to v0.4.0, where Query is now `Query(RequestQuery) ResponseQuery`, enabling precise proofs at particular heights:
```
message RequestQuery{
bytes data = 1;
string path = 2;
uint64 height = 3;
bool prove = 4;
}
message ResponseQuery{
CodeType code = 1;
int64 index = 2;
bytes key = 3;
bytes value = 4;
bytes proof = 5;
uint64 height = 6;
string log = 7;
}
```
- `BlockMeta` data type unifies its Hash and PartSetHash under a `BlockID`:
```
type BlockMeta struct {
BlockID BlockID `json:"block_id"` // the block hash and partsethash
Header *Header `json:"header"` // The block's Header
}
```
- `ValidatorSet.Proposer` is exposed as a field and persisted with the `State`. Use `GetProposer()` to initialize or update after validator-set changes.
- `tendermint gen_validator` command output is now pure JSON
FEATURES:
- New RPC endpoint `/commit?height=X` returns header and commit for block at height `X`
- Client API for each endpoint, including mocks for testing
IMPROVEMENTS:
- `Node` is now a `BaseService`
- Simplified starting Tendermint in-process from another application
- Better organized Makefile
- Scripts for auto-building binaries across platforms
- Docker image improved, slimmed down (using Alpine), and changed from tendermint/tmbase to tendermint/tendermint
- New repo files: `CONTRIBUTING.md`, Github `ISSUE_TEMPLATE`, `CHANGELOG.md`
- Improvements on CircleCI for managing build/test artifacts
- Handshake replay is doen through the consensus package, possibly using a mockApp
- Graceful shutdown of RPC listeners
- Tests for the PEX reactor and DialSeeds
BUG FIXES:
- Check peer.Send for failure before updating PeerState in consensus
- Fix panic in `/dial_seeds` with invalid addresses
- Fix proposer selection logic in ValidatorSet by taking the address into account in the `accumComparable`
- Fix inconcistencies with `ValidatorSet.Proposer` across restarts by persisting it in the `State`
## 0.8.0 (January 13, 2017)
BREAKING CHANGES:
- New data type `BlockID` to represent blocks:
```
type BlockID struct {
Hash []byte `json:"hash"`
PartsHeader PartSetHeader `json:"parts"`
}
```
- `Vote` data type now includes validator address and index:
```
type Vote struct {
ValidatorAddress []byte `json:"validator_address"`
ValidatorIndex int `json:"validator_index"`
Height int `json:"height"`
Round int `json:"round"`
Type byte `json:"type"`
BlockID BlockID `json:"block_id"` // zero if vote is nil.
Signature crypto.Signature `json:"signature"`
}
```
- Update TMSP to v0.3.0, where it is now called ABCI and AppendTx is DeliverTx
- Hex strings in the RPC are now "0x" prefixed
FEATURES:
- New message type on the ConsensusReactor, `Maj23Msg`, for peers to alert others they've seen a Maj23,
in order to track and handle conflicting votes intelligently to prevent Byzantine faults from causing halts:
```
type VoteSetMaj23Message struct {
Height int
Round int
Type byte
BlockID types.BlockID
}
```
- Configurable block part set size
- Validator set changes
- Optionally skip TimeoutCommit if we have all the votes
- Handshake between Tendermint and App on startup to sync latest state and ensure consistent recovery from crashes
- GRPC server for BroadcastTx endpoint
IMPROVEMENTS:
- Less verbose logging
- Better test coverage (37% -> 49%)
- Canonical SignBytes for signable types
- Write-Ahead Log for Mempool and Consensus via tmlibs/autofile
- Better in-process testing for the consensus reactor and byzantine faults
- Better crash/restart testing for individual nodes at preset failure points, and of networks at arbitrary points
- Better abstraction over timeout mechanics
BUG FIXES:
- Fix memory leak in mempool peer
- Fix panic on POLRound=-1
- Actually set the CommitTime
- Actually send BeginBlock message
- Fix a liveness issues caused by Byzantine proposals/votes. Uses the new `Maj23Msg`.
## 0.7.4 (December 14, 2016)
FEATURES:
- Enable the Peer Exchange reactor with the `--pex` flag for more resilient gossip network (feature still in development, beware dragons)
IMPROVEMENTS:
- Remove restrictions on RPC endpoint `/dial_seeds` to enable manual network configuration
## 0.7.3 (October 20, 2016)
IMPROVEMENTS:
- Type safe FireEvent
- More WAL/replay tests
- Cleanup some docs
BUG FIXES:
- Fix deadlock in mempool for synchronous apps
- Replay handles non-empty blocks
- Fix race condition in HeightVoteSet
## 0.7.2 (September 11, 2016)
BUG FIXES:
- Set mustConnect=false so tendermint will retry connecting to the app
## 0.7.1 (September 10, 2016)
FEATURES:
- New TMSP connection for Query/Info
- New RPC endpoints:
- `tmsp_query`
- `tmsp_info`
- Allow application to filter peers through Query (off by default)
IMPROVEMENTS:
- TMSP connection type enforced at compile time
- All listen/client urls use a "tcp://" or "unix://" prefix
BUG FIXES:
- Save LastSignature/LastSignBytes to `priv_validator.json` for recovery
- Fix event unsubscribe
- Fix fastsync/blockchain reactor
## 0.7.0 (August 7, 2016)
BREAKING CHANGES:
- Strict SemVer starting now!
- Update to ABCI v0.2.0
- Validation types now called Commit
- NewBlock event only returns the block header
FEATURES:
- TMSP and RPC support TCP and UNIX sockets
- Addition config options including block size and consensus parameters
- New WAL mode `cswal_light`; logs only the validator's own votes
- New RPC endpoints:
- for starting/stopping profilers, and for updating config
- `/broadcast_tx_commit`, returns when tx is included in a block, else an error
- `/unsafe_flush_mempool`, empties the mempool
IMPROVEMENTS:
- Various optimizations
- Remove bad or invalidated transactions from the mempool cache (allows later duplicates)
- More elaborate testing using CircleCI including benchmarking throughput on 4 digitalocean droplets
BUG FIXES:
- Various fixes to WAL and replay logic
- Various race conditions
## PreHistory
Strict versioning only began with the release of v0.7.0, in late summer 2016.
The project itself began in early summer 2014 and was workable decentralized cryptocurrency software by the end of that year.
Through the course of 2015, in collaboration with Eris Industries (now Monax Indsutries),
many additional features were integrated, including an implementation from scratch of the Ethereum Virtual Machine.
That implementation now forms the heart of [Burrow](https://github.com/hyperledger/burrow).
In the later half of 2015, the consensus algorithm was upgraded with a more asynchronous design and a more deterministic and robust implementation.
By late 2015, frustration with the difficulty of forking a large monolithic stack to create alternative cryptocurrency designs led to the
invention of the Application Blockchain Interface (ABCI), then called the Tendermint Socket Protocol (TMSP).
The Ethereum Virtual Machine and various other transaction features were removed, and Tendermint was whittled down to a core consensus engine
driving an application running in another process.
The ABCI interface and implementation were iterated on and improved over the course of 2016,
until versioned history kicked in with v0.7.0.

56
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,56 @@
# The Tendermint Code of Conduct
This code of conduct applies to all projects run by the Tendermint/COSMOS team and hence to tendermint.
----
# Conduct
## Contact: adrian@tendermint.com
* We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic.
* On Slack, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and welcoming environment for all.
* Please be kind and courteous. Theres no need to be mean or rude.
* Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer.
* Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works.
* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behaviour. We interpret the term “harassment” as including the definition in the [Citizen Code of Conduct](http://citizencodeofconduct.org/); if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we dont tolerate behavior that excludes people in socially marginalized groups.
* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the channel admins or the person mentioned above immediately. Whether youre a regular contributor or a newcomer, we care about making this community a safe place for you and weve got your back.
* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behaviour is not welcome.
----
# Moderation
These are the policies for upholding our communitys standards of conduct. If you feel that a thread needs moderation, please contact the above mentioned person.
1. Remarks that violate the Tendermint/COSMOS standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.)
2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed.
3. Moderators will first respond to such remarks with a warning.
4. If the warning is unheeded, the user will be “kicked,” i.e., kicked out of the communication channel to cool off.
5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded.
6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended party a genuine apology.
7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a different moderator, in private. Complaints about bans in-channel are not allowed.
8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate situation, they should expect less leeway than others.
In the Tendermint/COSMOS community we strive to go the extra step to look out for each other. Dont just aim to be technically unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly if theyre off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can drive people away from the community entirely.
And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good there was something you couldve communicated better — remember that its your responsibility to make your fellow Cosmonauts comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their trust.
The enforcement policies listed above apply to all official Tendermint/COSMOS venues.For other projects adopting the Tendermint/COSMOS Code of Conduct, please contact the maintainers of those projects for enforcement. If you wish to use this code of conduct for your own project, consider explicitly mentioning your moderation policy or making a copy with your own moderation policy so as to avoid confusion.
*Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling), the [Contributor Covenant v1.3.0](http://contributor-covenant.org/version/1/3/0/) and the [Rust Code of Conduct](https://www.rust-lang.org/en-US/conduct.html).

77
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,77 @@
# Contributing
Thank you for considering making contributions to Tendermint and related repositories (Basecoin, Merkleeyes, etc.)!
Please follow standard github best practices: fork the repo, branch from the tip of develop, make some commits, and submit a pull request to develop. See the [open issues](https://github.com/tendermint/tendermint/issues) for things we need help with!
Please make sure to use `gofmt` before every commit - the easiest way to do this is have your editor run it for you upon saving a file.
## Forking
Please note that Go requires code to live under absolute paths, which complicates forking.
While my fork lives at `https://github.com/ebuchman/tendermint`,
the code should never exist at `$GOPATH/src/github.com/ebuchman/tendermint`.
Instead, we use `git remote` to add the fork as a new remote for the original repo,
`$GOPATH/src/github.com/tendermint/tendermint `, and do all the work there.
For instance, to create a fork and work on a branch of it, I would:
* Create the fork on github, using the fork button.
* Go to the original repo checked out locally (ie. `$GOPATH/src/github.com/tendermint/tendermint`)
* `git remote rename origin upstream`
* `git remote add origin git@github.com:ebuchman/basecoin.git`
Now `origin` refers to my fork and `upstream` refers to the tendermint version.
So I can `git push -u origin master` to update my fork, and make pull requests to tendermint from there.
Of course, replace `ebuchman` with your git handle.
To pull in updates from the origin repo, run
* `git fetch upstream`
* `git rebase upstream/master` (or whatever branch you want)
Please don't make Pull Requests to `master`.
## Dependencies
We use [glide](https://github.com/masterminds/glide) 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.
## Testing
All repos should be hooked up to circle.
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
User-facing repos should adhere to the branching model: http://nvie.com/posts/a-successful-git-branching-model/.
That is, these repos should be well versioned, and any merge to master requires a version bump and tagged release.
Libraries need not follow the model strictly, but would be wise to,
especially `go-p2p` and `go-rpc`, as their versions are referenced in tendermint core.
### Development Procedure:
- the latest state of development is on `develop`
- `develop` must never fail `make test`
- no --force onto `develop` (except when reverting a broken commit, which should seldom happen)
- create a development branch either on github.com/tendermint/tendermint, or your fork (using `git add origin`)
- before submitting a pull request, begin `git rebase` on top of `develop`
### Pull Merge Procedure:
- ensure pull branch is rebased on develop
- run `make test` to ensure that all tests pass
- merge pull request
- the `unstable` branch may be used to aggregate pull merges before testing once
- push master may request that pull requests be rebased on top of `unstable`
### Release Procedure:
- start on `develop`
- run integration tests (see `test_integrations` in Makefile)
- prepare changelog/release issue
- bump versions
- push to release-vX.X.X to run the extended integration tests on the CI
- merge to master
- merge master back to develop

View File

@@ -1,54 +1,45 @@
# Pull base image.
FROM golang:1.6
FROM alpine:3.6
ENV USER tmuser
ENV DATA_ROOT /data/tendermint
# This is the release of tendermint to pull in.
ENV TM_VERSION 0.10.0
ENV TM_SHA256SUM a29852b8d51c00db93c87c3d148fa419a047abd38f32b2507a905805131acc19
# 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
# validator file into /tendermint.
#
# The /tendermint/data dir is used by tendermint to store state.
ENV DATA_ROOT /tendermint
ENV TMHOME $DATA_ROOT
# Set user right away for determinism
RUN groupadd -r $USER \
&& useradd -r -s /bin/false -g $USER $USER
# Create home directory for USER
# Needed for nodejs/nom
RUN mkdir -p /home/$USER \
&& chown -R $USER:$USER /home/$USER
RUN addgroup tmuser && \
adduser -S -G tmuser tmuser
# Create directory for persistence and give our user ownership
RUN mkdir -p $DATA_ROOT \
&& chown -R $USER:$USER $DATA_ROOT
RUN mkdir -p $DATA_ROOT && \
chown -R tmuser:tmuser $DATA_ROOT
# Set the env variables to non-interactive
ENV DEBIAN_FRONTEND noninteractive
ENV DEBIAN_PRIORITY critical
ENV DEBCONF_NOWARNINGS yes
ENV TERM linux
RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
# jq and curl used for extracting `pub_key` from private validator while
# deploying tendermint with Kubernetes. It is nice to have bash so the users
# could execute bash commands.
RUN apk add --no-cache bash curl jq
# Grab deps (git)
RUN apt-get update && \
apt-get install -y --no-install-recommends \
git && \
rm -rf /var/lib/apt/lists/*
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 && \
echo "${TM_SHA256SUM} tendermint_${TM_VERSION}_linux_amd64.zip" | sha256sum -c && \
unzip -d /bin tendermint_${TM_VERSION}_linux_amd64.zip && \
apk del openssl && \
rm -f tendermint_${TM_VERSION}_linux_amd64.zip
# Grab deps (node)
RUN curl -sL https://deb.nodesource.com/setup_5.x | bash -
RUN apt-get update && \
apt-get install -y --no-install-recommends \
nodejs && \
rm -rf /var/lib/apt/lists/*
# Copy run.sh
COPY ./run.sh $DATA_ROOT/run.sh
RUN chmod +x $DATA_ROOT/run.sh
# Persist data, set user
WORKDIR $DATA_ROOT
# Expose the data directory as a volume since there's mutable state in there
VOLUME $DATA_ROOT
USER $USER
ENV TMROOT $DATA_ROOT
# p2p port
EXPOSE 46656
# rpc port
EXPOSE 46657
# Run tendermint
CMD ["./run.sh"]
ENTRYPOINT ["tendermint"]
CMD ["node", "--moniker=`hostname`"]

35
DOCKER/Dockerfile.develop Normal file
View File

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

13
DOCKER/Makefile Normal file
View File

@@ -0,0 +1,13 @@
build:
@sh -c "'$(CURDIR)/build.sh'"
push:
@sh -c "'$(CURDIR)/push.sh'"
build_develop:
docker build -t "tendermint/tendermint:develop" -f Dockerfile.develop .
push_develop:
docker push "tendermint/tendermint:develop"
.PHONY: build build_develop push push_develop

View File

@@ -1,13 +1,58 @@
# Build and run a docker image and container
# Supported tags and respective `Dockerfile` links
- `0.10.0`, `latest` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/e5342f4054ab784b2cd6150e14f01053d7c8deb2/DOCKER/Dockerfile)
- `0.9.1`, `0.9`, [(Dockerfile)](https://github.com/tendermint/tendermint/blob/809e0e8c5933604ba8b2d096803ada7c5ec4dfd3/DOCKER/Dockerfile)
- `0.9.0` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/d474baeeea6c22b289e7402449572f7c89ee21da/DOCKER/Dockerfile)
- `0.8.0`, `0.8` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/bf64dd21fdb193e54d8addaaaa2ecf7ac371de8c/DOCKER/Dockerfile)
- `develop` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/master/DOCKER/Dockerfile.develop)
`develop` tag points to the [develop](https://github.com/tendermint/tendermint/tree/develop) branch.
# Tendermint
Tendermint Core is Byzantine Fault Tolerant (BFT) middleware that takes a state transition machine, written in any programming language, and securely replicates it on many machines.
For more background, see the [introduction](https://tendermint.com/intro).
To get started developing applications, see the [application developers guide](https://tendermint.com/docs/guides/app-development).
# How to use this image
## Start one instance of the Tendermint core with the `dummy` app
A very simple example of a built-in app and Tendermint core in one container.
```
# Build base Docker image
# Make sure ./run.sh exists.
docker build -t tendermint/tmbase -f Dockerfile .
# Log into dockerhub
docker login
# Push latest build to dockerhub
docker push tendermint/tmbase
docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint init
docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint node --proxy_app=dummy
```
## mintnet-kubernetes
If you want to see many containers talking to each other, consider using [mintnet-kubernetes](https://github.com/tendermint/tools/tree/master/mintnet-kubernetes), which is a tool for running Tendermint-based applications on a Kubernetes cluster.
# Supported Docker versions
This image is officially supported on Docker version 1.13.1.
Support for older versions (down to 1.6) is provided on a best-effort basis.
Please see [the Docker installation documentation](https://docs.docker.com/installation/) for details on how to upgrade your Docker daemon.
# License
View [license information](https://raw.githubusercontent.com/tendermint/tendermint/master/LICENSE) for the software contained in this image.
# User Feedback
## Issues
If you have any problems with or questions about this image, please contact us through a [GitHub](https://github.com/tendermint/tendermint/issues) issue. If the issue is related to a CVE, please check for [a `cve-tracker` issue on the `official-images` repository](https://github.com/docker-library/official-images/issues?q=label%3Acve-tracker) first.
You can also reach the image maintainers via [Slack](http://forum.tendermint.com:3000/).
## Contributing
You are invited to contribute new features, fixes, or updates, large or small; we are always thrilled to receive pull requests, and do our best to process them as fast as we can.
Before you start to code, we recommend discussing your plans through a [GitHub](https://github.com/tendermint/tendermint/issues) issue, especially for more ambitious contributions. This gives other contributors a chance to point you in the right direction, give you feedback on your design, and help you find out if someone else is working on the same thing.

20
DOCKER/build.sh Executable file
View File

@@ -0,0 +1,20 @@
#!/usr/bin/env bash
set -e
# Get the tag from the version, or try to figure it out.
if [ -z "$TAG" ]; then
TAG=$(awk -F\" '/Version =/ { print $2; exit }' < ../version/version.go)
fi
if [ -z "$TAG" ]; then
echo "Please specify a tag."
exit 1
fi
TAG_NO_PATCH=${TAG%.*}
read -p "==> Build 3 docker images with the following tags (latest, $TAG, $TAG_NO_PATCH)? y/n" -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]
then
docker build -t "tendermint/tendermint" -t "tendermint/tendermint:$TAG" -t "tendermint/tendermint:$TAG_NO_PATCH" .
fi

22
DOCKER/push.sh Executable file
View File

@@ -0,0 +1,22 @@
#!/usr/bin/env bash
set -e
# Get the tag from the version, or try to figure it out.
if [ -z "$TAG" ]; then
TAG=$(awk -F\" '/Version =/ { print $2; exit }' < ../version/version.go)
fi
if [ -z "$TAG" ]; then
echo "Please specify a tag."
exit 1
fi
TAG_NO_PATCH=${TAG%.*}
read -p "==> Push 3 docker images with the following tags (latest, $TAG, $TAG_NO_PATCH)? y/n" -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]
then
docker push "tendermint/tendermint:latest"
docker push "tendermint/tendermint:$TAG"
docker push "tendermint/tendermint:$TAG_NO_PATCH"
fi

View File

@@ -1,10 +0,0 @@
#! /bin/bash
mkdir -p $GOPATH/src/$TMREPO
cd $GOPATH/src/$TMREPO
git clone https://$TMREPO.git .
git fetch
git reset --hard $TMHEAD
go get -d $TMREPO/cmd/tendermint
make
tendermint node --seeds="$TMSEEDS" --moniker="$TMNAME"

1
INSTALL.md Normal file
View File

@@ -0,0 +1 @@
The installation guide has moved to the [docs directory](docs/guides/install-from-source.md) in order to easily be rendered by the website. Please update your links accordingly.

View File

@@ -1,21 +0,0 @@
1. Fork github.com/tendermint/tendermint.
2. Run "make", it should install the daemon, which we named "tendermint".
3. Run "tendermint gen_account". Save the address, pub_key bytes, and priv_key bytes.
This is your developer key for controlling the cloud nodes.
4. Also run "tendermint gen_validator" 5 times, once for each cloud node. Save the output.
5. Create a directory ~/.debora/ and copy cmd/debora/default.cfg into ~/.debora/default.cfg
Copy the priv_key bytes from step 4 into ~/.debora/default.cfg where it says so.
Change the list of hosts in ~/.debora/default.cfg with your own set of 5 cloud nodes.
6. Replace cmd/barak/seed's pubkey with the pub_key bytes from step 3.
7. Update config/tendermint/config.go's genesis with validator pubkeys from step 4.
Give each of your nodes the same amount of voting power.
Set up the accounts however you want.
8. On each cloud node, follow the instructions here: https://github.com/tendermint/tendermint/tree/master/INSTALL
Create tmuser, install go, and also install 'barak'.
Then, run `barak -config="cmd/barak/seed"`.
You don't need to start the node at this time.
9. Now you can run "debora list" on your development machine and post commands to each cloud node.
10. Run scripts/unsafe_upgrade_barak.sh to test that barak is running.
The old barak you started on step 8 should now have quit.
A new instance of barak should be running. Check with `ps -ef | grep "barak"`
11. Run scripts/unsafe_restart_net.sh start your new testnet.

View File

@@ -1,30 +0,0 @@
NOTE: Only Ubuntu 14.04 64bit is supported at this time.
### Server setup / create `tmuser`
Secure the server, install dependencies, and create a new user `tmuser`
curl -L https://raw.githubusercontent.com/tendermint/tendermint/master/INSTALL/install_env.sh > install_env.sh
source install_env.sh
cd /home/tmuser
### Install Go as `tmuser`
Don't use `apt-get install golang`, it's still on an old version.
curl -L https://raw.githubusercontent.com/tendermint/tendermint/master/INSTALL/install_golang.sh > install_golang.sh
source install_golang.sh
### Run Barak
WARNING: THIS STEP WILL GIVE CONTROL OF THE CURRENT USER TO THE DEV TEAM.
go get -u github.com/tendermint/tendermint/cmd/barak
nohup barak -config="$GOPATH/src/github.com/tendermint/tendermint/cmd/barak/seed" &
### Install/Update MintDB
go get -u github.com/tendermint/tendermint/cmd/tendermint
mkdir -p ~/.tendermint
cp $GOPATH/src/github.com/tendermint/tendermint/config/tendermint/genesis.json ~/.tendermint/
tendermint node --seeds="goldenalchemist.chaintest.net:46656"

View File

@@ -1,63 +0,0 @@
#!/bin/bash
# Run this as root user
# This part is for hardening the server and setting up a user account
if [ `whoami` != "root" ];
then
echo "You must run this script as root"
exit 1
fi
USER="tmuser"
OPEN_PORTS=(46656 46657 46658 46659 46660 46661 46662 46663 46664 46665 46666 46667 46668 46669 46670 46671)
SSH_PORT=22
WHITELIST=()
# update and upgrade
apt-get update -y
apt-get upgrade -y
# fail2ban for monitoring logins
apt-get install -y fail2ban
# set up the network time daemon
apt-get install -y ntp
# install dependencies
apt-get install -y make screen gcc git mercurial libc6-dev pkg-config libgmp-dev
# set up firewall
echo "ENABLE FIREWALL ..."
set -x
# white list ssh access
for ip in "${WHITELIST[@]}"; do
ufw allow from $ip to any port $SSH_PORT
done
if [ ${#WHITELIST[@]} -eq 0 ]; then
ufw allow $SSH_PORT
fi
# open ports
for port in "${OPEN_PORTS[@]}"; do
ufw allow $port
done
# apply
ufw --force enable
set +x
# set up firewall END
# watch the logs and have them emailed to me
# apt-get install -y logwatch
# echo "/usr/sbin/logwatch --output mail --mailto $ADMIN_EMAIL --detail high" >> /etc/cron.daily/00logwatch
# set up user account
echo "CREATE USER $USER ..."
useradd $USER -d /home/$USER
# This user should not have root access.
# usermod -aG sudo $USER
mkdir /home/$USER
cp /etc/skel/.bashrc .
cp /etc/skel/.profile .
chown -R $USER:$USER /home/$USER
echo "Done setting env. Switching to $USER..."
su $USER

View File

@@ -1,29 +0,0 @@
#!/bin/bash
# Run this as tmuser user
# This part is for installing go
if [ `whoami` == "root" ];
then
echo "You should not run this script as root"
exit 1
fi
USER=`whoami`
PWD=`pwd`
# get dependencies
# sudo apt-get install -y make screen gcc git mercurial libc6-dev pkg-config libgmp-dev
# install golang
cd /home/$USER
mkdir gocode
wget https://storage.googleapis.com/golang/go1.4.2.src.tar.gz
tar -xzvf go*.tar.gz
cd go/src
./make.bash
mkdir -p /home/$USER/go/src
echo 'export GOROOT=/home/$USER/go' >> /home/$USER/.bashrc
echo 'export GOPATH=/home/$USER/gocode' >> /home/$USER/.bashrc
echo 'export PATH=$PATH:$GOROOT/bin:$GOPATH/bin' >> /home/$USER/.bashrc
source /home/$USER/.bashrc
cd $PWD

View File

@@ -1,58 +1,83 @@
.PHONY: get_deps build all list_deps install
GOTOOLS = \
github.com/mitchellh/gox \
github.com/Masterminds/glide \
honnef.co/go/tools/cmd/megacheck
all: get_deps install test
PACKAGES=$(shell go list ./... | grep -v '/vendor/')
BUILD_TAGS?=tendermint
TMHOME = $${TMHOME:-$$HOME/.tendermint}
TMROOT = $${TMROOT:-$$HOME/.tendermint}
define NEWLINE
all: install test
endef
NOVENDOR = go list github.com/tendermint/tendermint/... | grep -v /vendor/
install: get_deps
go install github.com/tendermint/tendermint/cmd/tendermint
install: get_vendor_deps
@go install --ldflags '-extldflags "-static"' \
--ldflags "-X github.com/tendermint/tendermint/version.GitCommit=`git rev-parse HEAD`" ./cmd/tendermint
build:
go build -o build/tendermint github.com/tendermint/tendermint/cmd/tendermint
go build \
--ldflags "-X github.com/tendermint/tendermint/version.GitCommit=`git rev-parse HEAD`" -o build/tendermint ./cmd/tendermint/
build_race:
go build -race -o build/tendermint github.com/tendermint/tendermint/cmd/tendermint
go build -race -o build/tendermint ./cmd/tendermint
test: build
go test `${NOVENDOR}`
# dist builds binaries for all platforms and packages them for distribution
dist:
@BUILD_TAGS='$(BUILD_TAGS)' sh -c "'$(CURDIR)/scripts/dist.sh'"
test_race: build
go test -race `${NOVENDOR}`
test:
@echo "--> Running go test"
@go test $(PACKAGES)
test_race:
@echo "--> Running go test --race"
@go test -v -race $(PACKAGES)
test_integrations:
bash ./test/test.sh
@bash ./test/test.sh
test100: build
for i in {1..100}; do make test; done
test100:
@for i in {1..100}; do make test; done
draw_deps:
# requires brew install graphviz
go get github.com/hirokidaichi/goviz
goviz -i github.com/tendermint/tendermint/cmd/tendermint | dot -Tpng -o huge.png
# requires brew install graphviz or apt-get install graphviz
go get github.com/RobotsAndPencils/goviz
@goviz -i github.com/tendermint/tendermint/cmd/tendermint -d 3 | dot -Tpng -o dependency-graph.png
list_deps:
go list -f '{{join .Deps "\n"}}' github.com/tendermint/tendermint/... | \
@go list -f '{{join .Deps "\n"}}' ./... | \
grep -v /vendor/ | sort | uniq | \
xargs go list -f '{{if not .Standard}}{{.ImportPath}}{{end}}'
get_deps:
go get -d `${NOVENDOR}`
go list -f '{{join .TestImports "\n"}}' github.com/tendermint/tendermint/... | \
@echo "--> Running go get"
@go get -v -d $(PACKAGES)
@go list -f '{{join .TestImports "\n"}}' ./... | \
grep -v /vendor/ | sort | uniq | \
xargs go get
xargs go get -v -d
get_vendor_deps:
go get github.com/Masterminds/glide
glide install
get_vendor_deps: ensure_tools
@rm -rf vendor/
@echo "--> Running glide install"
@glide install
update_deps:
go get -d -u github.com/tendermint/tendermint/...
update_deps: tools
@echo "--> Updating dependencies"
@go get -d -u ./...
revision:
-echo `git rev-parse --verify HEAD` > $(TMROOT)/revision
-echo `git rev-parse --verify HEAD` >> $(TMROOT)/revision_history
-echo `git rev-parse --verify HEAD` > $(TMHOME)/revision
-echo `git rev-parse --verify HEAD` >> $(TMHOME)/revision_history
tools:
go get -u -v $(GOTOOLS)
ensure_tools:
go get $(GOTOOLS)
### Formatting, linting, and vetting
megacheck:
@for pkg in ${PACKAGES}; do megacheck "$$pkg"; done
.PHONY: install build build_race dist test test_race test_integrations test100 draw_deps list_deps get_deps get_vendor_deps update_deps revision tools

View File

@@ -1,43 +1,76 @@
# Tendermint
Simple, Secure, Scalable Blockchain Platform
[![CircleCI](https://circleci.com/gh/tendermint/tendermint.svg?style=svg)](https://circleci.com/gh/tendermint/tendermint)
[![codecov](https://codecov.io/gh/tendermint/tendermint/branch/develop/graph/badge.svg)](https://codecov.io/gh/tendermint/tendermint)
[Byzantine-Fault Tolerant](https://en.wikipedia.org/wiki/Byzantine_fault_tolerance)
[State Machine Replication](https://en.wikipedia.org/wiki/State_machine_replication).
Or [Blockchain](https://en.wikipedia.org/wiki/Blockchain_(database)) for short.
_NOTE: This is yet pre-alpha non-production-quality software._
[![version](https://img.shields.io/github/tag/tendermint/tendermint.svg)](https://github.com/tendermint/tendermint/releases/latest)
[![API Reference](
https://camo.githubusercontent.com/915b7be44ada53c290eb157634330494ebe3e30a/68747470733a2f2f676f646f632e6f72672f6769746875622e636f6d2f676f6c616e672f6764646f3f7374617475732e737667
)](https://godoc.org/github.com/tendermint/tendermint)
[![chat](https://img.shields.io/badge/slack-join%20chat-pink.svg)](http://forum.tendermint.com:3000/)
[![license](https://img.shields.io/github/license/tendermint/tendermint.svg)](https://github.com/tendermint/tendermint/blob/master/LICENSE)
[![](https://tokei.rs/b1/github/tendermint/tendermint?category=lines)](https://github.com/tendermint/tendermint)
Branch | Tests | Coverage | Report Card
----------|-------|----------|-------------
develop | [![CircleCI](https://circleci.com/gh/tendermint/tendermint/tree/develop.svg?style=shield)](https://circleci.com/gh/tendermint/tendermint/tree/develop) | [![codecov](https://codecov.io/gh/tendermint/tendermint/branch/develop/graph/badge.svg)](https://codecov.io/gh/tendermint/tendermint) | [![Go Report Card](https://goreportcard.com/badge/github.com/tendermint/tendermint/tree/develop)](https://goreportcard.com/report/github.com/tendermint/tendermint/tree/develop)
master | [![CircleCI](https://circleci.com/gh/tendermint/tendermint/tree/master.svg?style=shield)](https://circleci.com/gh/tendermint/tendermint/tree/master) | [![codecov](https://codecov.io/gh/tendermint/tendermint/branch/master/graph/badge.svg)](https://codecov.io/gh/tendermint/tendermint) | [![Go Report Card](https://goreportcard.com/badge/github.com/tendermint/tendermint/tree/master)](https://goreportcard.com/report/github.com/tendermint/tendermint/tree/master)
_NOTE: This is alpha software. Please contact us if you intend to run it in production._
Tendermint Core is Byzantine Fault Tolerant (BFT) middleware that takes a state transition machine, written in any programming language,
and replicates it on many machines.
See the [application developers guide](https://github.com/tendermint/tendermint/wiki/Application-Developers) to get started.
and securely replicates it on many machines.
For more background, see the [introduction](https://tendermint.com/intro).
To get started developing applications, see the [application developers guide](https://tendermint.com/docs/guides/app-development).
### Code of Conduct
Please read, understand and adhere to our [code of conduct](CODE_OF_CONDUCT.md).
## Install
To download pre-built binaries, see our [downloads page](https://tendermint.com/downloads).
To install from source, you should be able to:
`go get -u github.com/tendermint/tendermint/cmd/tendermint`
For more details (or if it fails), see the [install guide](https://tendermint.com/docs/guides/install-from-source).
## Contributing
Yay open source! Please see our [contributing guidelines](https://github.com/tendermint/tendermint/wiki/Contributing).
Yay open source! Please see our [contributing guidelines](CONTRIBUTING.md).
## Resources
### Tendermint Core
- [Introduction](https://github.com/tendermint/tendermint/wiki/Introduction)
- [Validators](https://github.com/tendermint/tendermint/wiki/Validators)
- [Byzantine Consensus Algorithm](https://github.com/tendermint/tendermint/wiki/Byzantine-Consensus-Algorithm)
- [Block Structure](https://github.com/tendermint/tendermint/wiki/Block-Structure)
- [RPC](https://github.com/tendermint/tendermint/wiki/RPC)
- [Genesis](https://github.com/tendermint/tendermint/wiki/Genesis)
- [Configuration](https://github.com/tendermint/tendermint/wiki/Configuration)
- [Light Client Protocol](https://github.com/tendermint/tendermint/wiki/Light-Client-Protocol)
- [Roadmap for V2](https://github.com/tendermint/tendermint/wiki/Roadmap-for-V2)
- [Introduction](https://tendermint.com/intro)
- [Docs](https://tendermint.com/docs)
- [Software using Tendermint](https://tendermint.com/ecosystem)
### Sub-projects
* [TMSP](http://github.com/tendermint/tmsp)
* [Mintnet](http://github.com/tendermint/mintnet)
* [Go-Wire](http://github.com/tendermint/go-wire)
* [Go-P2P](http://github.com/tendermint/go-p2p)
* [Go-Merkle](http://github.com/tendermint/go-merkle)
* [ABCI](http://github.com/tendermint/abci), the Application Blockchain Interface
* [Go-Wire](http://github.com/tendermint/go-wire), a deterministic serialization library
* [Go-Crypto](http://github.com/tendermint/go-crypto), an elliptic curve cryptography library
* [TmLibs](http://github.com/tendermint/tmlibs), an assortment of Go libraries
* [Merkleeyes](http://github.com/tendermint/merkleeyes), a balanced, binary Merkle tree for ABCI apps
## Install
### Tools
* [Deployment, Benchmarking, and Monitoring](https://github.com/tendermint/tools)
`go get -u github.com/tendermint/tendermint/cmd/tendermint`
### Applications
For more details, see the [install guide](https://github.com/tendermint/tendermint/wiki/Installation).
* [Ethermint](http://github.com/tendermint/ethermint): Ethereum on Tendermint
* [Basecoin](http://github.com/tendermint/basecoin), a cryptocurrency application framework
### More
* [Tendermint Blog](https://blog.cosmos.network/tendermint/home)
* [Cosmos Blog](https://blog.cosmos.network)
* [Original Whitepaper (out-of-date)](http://www.the-blockchain.com/docs/Tendermint%20Consensus%20without%20Mining.pdf)
* [Master's Thesis on Tendermint](https://atrium.lib.uoguelph.ca/xmlui/handle/10214/9769)

49
Vagrantfile vendored
View File

@@ -1,25 +1,38 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
VAGRANTFILE_API_VERSION = "2"
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/trusty64"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.box = "phusion-open-ubuntu-14.04-amd64"
config.vm.box_url = "https://oss-binaries.phusionpassenger.com/vagrant/boxes/latest/ubuntu-14.04-amd64-vbox.box"
# Or, for Ubuntu 12.04:
config.vm.provider :vmware_fusion do |f, override|
override.vm.box_url = "https://oss-binaries.phusionpassenger.com/vagrant/boxes/latest/ubuntu-14.04-amd64-vmwarefusion.box"
config.vm.provider "virtualbox" do |v|
v.memory = 4096
v.cpus = 2
end
if Dir.glob("#{File.dirname(__FILE__)}/.vagrant/machines/default/*/id").empty?
# Install Docker
pkg_cmd = "wget -q -O - https://get.docker.io/gpg | apt-key add -;" \
"echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list;" \
"apt-get update -qq; apt-get install -q -y --force-yes lxc-docker; "
# Add vagrant user to the docker group
pkg_cmd << "usermod -a -G docker vagrant; "
config.vm.provision :shell, :inline => pkg_cmd
end
config.vm.provision "shell", inline: <<-SHELL
apt-get update
apt-get install -y --no-install-recommends wget curl jq shellcheck bsdmainutils psmisc
wget -qO- https://get.docker.com/ | sh
usermod -a -G docker vagrant
apt-get autoremove -y
curl -O https://storage.googleapis.com/golang/go1.8.linux-amd64.tar.gz
tar -xvf go1.8.linux-amd64.tar.gz
rm -rf /usr/local/go
mv go /usr/local
rm -f go1.8.linux-amd64.tar.gz
mkdir -p /home/vagrant/go/bin
echo 'export PATH=$PATH:/usr/local/go/bin:/home/vagrant/go/bin' >> /home/vagrant/.bash_profile
echo 'export GOPATH=/home/vagrant/go' >> /home/vagrant/.bash_profile
echo 'export LC_ALL=en_US.UTF-8' >> /home/vagrant/.bash_profile
mkdir -p /home/vagrant/go/src/github.com/tendermint
ln -s /vagrant /home/vagrant/go/src/github.com/tendermint/tendermint
chown -R vagrant:vagrant /home/vagrant/go
su - vagrant -c 'cd /home/vagrant/go/src/github.com/tendermint/tendermint && make get_vendor_deps'
SHELL
end

View File

@@ -4,7 +4,7 @@ import (
"testing"
"github.com/tendermint/go-crypto"
"github.com/tendermint/go-p2p"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/go-wire"
proto "github.com/tendermint/tendermint/benchmarks/proto"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
@@ -12,10 +12,10 @@ import (
func BenchmarkEncodeStatusWire(b *testing.B) {
b.StopTimer()
pubKey := crypto.GenPrivKeyEd25519().PubKey().(crypto.PubKeyEd25519)
pubKey := crypto.GenPrivKeyEd25519().PubKey()
status := &ctypes.ResultStatus{
NodeInfo: &p2p.NodeInfo{
PubKey: pubKey,
PubKey: pubKey.Unwrap().(crypto.PubKeyEd25519),
Moniker: "SOMENAME",
Network: "SOMENAME",
RemoteAddr: "SOMEADDR",
@@ -40,7 +40,7 @@ func BenchmarkEncodeStatusWire(b *testing.B) {
func BenchmarkEncodeNodeInfoWire(b *testing.B) {
b.StopTimer()
pubKey := crypto.GenPrivKeyEd25519().PubKey().(crypto.PubKeyEd25519)
pubKey := crypto.GenPrivKeyEd25519().PubKey().Unwrap().(crypto.PubKeyEd25519)
nodeInfo := &p2p.NodeInfo{
PubKey: pubKey,
Moniker: "SOMENAME",
@@ -61,7 +61,7 @@ func BenchmarkEncodeNodeInfoWire(b *testing.B) {
func BenchmarkEncodeNodeInfoBinary(b *testing.B) {
b.StopTimer()
pubKey := crypto.GenPrivKeyEd25519().PubKey().(crypto.PubKeyEd25519)
pubKey := crypto.GenPrivKeyEd25519().PubKey().Unwrap().(crypto.PubKeyEd25519)
nodeInfo := &p2p.NodeInfo{
PubKey: pubKey,
Moniker: "SOMENAME",
@@ -83,7 +83,7 @@ func BenchmarkEncodeNodeInfoBinary(b *testing.B) {
func BenchmarkEncodeNodeInfoProto(b *testing.B) {
b.StopTimer()
pubKey := crypto.GenPrivKeyEd25519().PubKey().(crypto.PubKeyEd25519)
pubKey := crypto.GenPrivKeyEd25519().PubKey().Unwrap().(crypto.PubKeyEd25519)
pubKey2 := &proto.PubKey{Ed25519: &proto.PubKeyEd25519{Bytes: pubKey[:]}}
nodeInfo := &proto.NodeInfo{
PubKey: pubKey2,

View File

@@ -1,7 +1,7 @@
package benchmarks
import (
. "github.com/tendermint/go-common"
. "github.com/tendermint/tmlibs/common"
"testing"
)

View File

@@ -4,7 +4,7 @@ import (
"os"
"testing"
. "github.com/tendermint/go-common"
. "github.com/tendermint/tmlibs/common"
)
func BenchmarkFileWrite(b *testing.B) {

View File

@@ -7,11 +7,11 @@ import (
"fmt"
"github.com/gorilla/websocket"
. "github.com/tendermint/go-common"
"github.com/tendermint/go-rpc/client"
"github.com/tendermint/go-rpc/types"
"github.com/tendermint/go-wire"
_ "github.com/tendermint/tendermint/rpc/core/types" // Register RPCResponse > Result types
"github.com/tendermint/tendermint/rpc/lib/client"
"github.com/tendermint/tendermint/rpc/lib/types"
. "github.com/tendermint/tmlibs/common"
)
func main() {
@@ -37,11 +37,16 @@ func main() {
for i := 0; ; i++ {
binary.BigEndian.PutUint64(buf, uint64(i))
//txBytes := hex.EncodeToString(buf[:n])
request := rpctypes.NewRPCRequest("fakeid", "broadcast_tx", Arr(buf[:8]))
request, err := rpctypes.MapToRequest("fakeid",
"broadcast_tx",
map[string]interface{}{"tx": buf[:8]})
if err != nil {
Exit(err.Error())
}
reqBytes := wire.JSONBytes(request)
//fmt.Println("!!", string(reqBytes))
fmt.Print(".")
err := ws.WriteMessage(websocket.TextMessage, reqBytes)
err = ws.WriteMessage(websocket.TextMessage, reqBytes)
if err != nil {
Exit(err.Error())
}

View File

@@ -1,7 +0,0 @@
package blockchain
import (
"github.com/tendermint/go-logger"
)
var log = logger.New("module", "blockchain")

View File

@@ -5,9 +5,10 @@ import (
"sync"
"time"
flow "github.com/tendermint/flowcontrol"
. "github.com/tendermint/go-common"
"github.com/tendermint/tendermint/types"
. "github.com/tendermint/tmlibs/common"
flow "github.com/tendermint/tmlibs/flowrate"
"github.com/tendermint/tmlibs/log"
)
const (
@@ -26,13 +27,13 @@ var peerTimeoutSeconds = time.Duration(15) // not const so we can override with
in sequence from peers that reported higher heights than ours.
Every so often we ask peers what height they're on so we can keep going.
Requests are continuously made for blocks of heigher heights until
the limits. If most of the requests have no available peers, and we
Requests are continuously made for blocks of higher heights until
the limit is reached. If most of the requests have no available peers, and we
are not at peer limits, we can probably switch to consensus reactor
*/
type BlockPool struct {
QuitService
BaseService
startTime time.Time
mtx sync.Mutex
@@ -58,19 +59,18 @@ func NewBlockPool(start int, requestsCh chan<- BlockRequest, timeoutsCh chan<- s
requestsCh: requestsCh,
timeoutsCh: timeoutsCh,
}
bp.QuitService = *NewQuitService(log, "BlockPool", bp)
bp.BaseService = *NewBaseService(nil, "BlockPool", bp)
return bp
}
func (pool *BlockPool) OnStart() error {
pool.QuitService.OnStart()
go pool.makeRequestersRoutine()
pool.startTime = time.Now()
return nil
}
func (pool *BlockPool) OnStop() {
pool.QuitService.OnStop()
pool.BaseService.OnStop()
}
// Run spawns requesters as needed.
@@ -107,7 +107,7 @@ func (pool *BlockPool) removeTimedoutPeers() {
// XXX remove curRate != 0
if curRate != 0 && curRate < minRecvRate {
pool.sendTimeout(peer.id)
log.Warn("SendTimeout", "peer", peer.id, "reason", "curRate too low")
pool.Logger.Error("SendTimeout", "peer", peer.id, "reason", "curRate too low")
peer.didTimeout = true
}
}
@@ -129,11 +129,9 @@ func (pool *BlockPool) IsCaughtUp() bool {
pool.mtx.Lock()
defer pool.mtx.Unlock()
height := pool.height
// Need at least 1 peer to be considered caught up.
if len(pool.peers) == 0 {
log.Debug("Blockpool has no peers")
pool.Logger.Debug("Blockpool has no peers")
return false
}
@@ -142,8 +140,11 @@ func (pool *BlockPool) IsCaughtUp() bool {
maxPeerHeight = MaxInt(maxPeerHeight, peer.height)
}
isCaughtUp := (height > 0 || time.Now().Sub(pool.startTime) > 5*time.Second) && (maxPeerHeight == 0 || height >= maxPeerHeight)
log.Notice(Fmt("IsCaughtUp: %v", isCaughtUp), "height", height, "maxPeerHeight", maxPeerHeight)
// some conditions to determine if we're caught up
receivedBlockOrTimedOut := (pool.height > 0 || time.Since(pool.startTime) > 5*time.Second)
ourChainIsLongestAmongPeers := maxPeerHeight == 0 || pool.height >= maxPeerHeight
isCaughtUp := receivedBlockOrTimedOut && ourChainIsLongestAmongPeers
pool.Logger.Info(Fmt("IsCaughtUp: %v", isCaughtUp), "height", pool.height, "maxPeerHeight", maxPeerHeight)
return isCaughtUp
}
@@ -227,6 +228,7 @@ func (pool *BlockPool) SetPeerHeight(peerID string, height int) {
peer.height = height
} else {
peer = newBPPeer(pool, peerID, height)
peer.setLogger(pool.Logger.With("peer", peerID))
pool.peers[peerID] = peer
}
}
@@ -241,7 +243,9 @@ func (pool *BlockPool) RemovePeer(peerID string) {
func (pool *BlockPool) removePeer(peerID string) {
for _, requester := range pool.requesters {
if requester.getPeerID() == peerID {
if requester.getBlock() != nil {
pool.numPending++
}
go requester.redo() // pick another peer and ...
}
}
@@ -258,7 +262,6 @@ func (pool *BlockPool) pickIncrAvailablePeer(minHeight int) *bpPeer {
if peer.didTimeout {
pool.removePeer(peer.id)
continue
} else {
}
if peer.numPending >= maxPendingRequestsPerPeer {
continue
@@ -278,6 +281,7 @@ func (pool *BlockPool) makeNextRequester() {
nextHeight := pool.height + len(pool.requesters)
request := newBPRequester(pool, nextHeight)
request.SetLogger(pool.Logger.With("height", nextHeight))
pool.requesters[nextHeight] = request
pool.numPending++
@@ -299,6 +303,7 @@ func (pool *BlockPool) sendTimeout(peerID string) {
pool.timeoutsCh <- peerID
}
// unused by tendermint; left for debugging purposes
func (pool *BlockPool) debug() string {
pool.mtx.Lock() // Lock
defer pool.mtx.Unlock()
@@ -322,11 +327,12 @@ type bpPeer struct {
id string
recvMonitor *flow.Monitor
mtx sync.Mutex
height int
numPending int32
timeout *time.Timer
didTimeout bool
logger log.Logger
}
func newBPPeer(pool *BlockPool, peerID string, height int) *bpPeer {
@@ -335,10 +341,15 @@ func newBPPeer(pool *BlockPool, peerID string, height int) *bpPeer {
id: peerID,
height: height,
numPending: 0,
logger: log.NewNopLogger(),
}
return peer
}
func (peer *bpPeer) setLogger(l log.Logger) {
peer.logger = l
}
func (peer *bpPeer) resetMonitor() {
peer.recvMonitor = flow.New(time.Second, time.Second*40)
var initialValue = float64(minRecvRate) * math.E
@@ -376,14 +387,14 @@ func (peer *bpPeer) onTimeout() {
defer peer.pool.mtx.Unlock()
peer.pool.sendTimeout(peer.id)
log.Warn("SendTimeout", "peer", peer.id, "reason", "onTimeout")
peer.logger.Error("SendTimeout", "reason", "onTimeout")
peer.didTimeout = true
}
//-------------------------------------
type bpRequester struct {
QuitService
BaseService
pool *BlockPool
height int
gotBlockCh chan struct{}
@@ -404,12 +415,11 @@ func newBPRequester(pool *BlockPool, height int) *bpRequester {
peerID: "",
block: nil,
}
bpr.QuitService = *NewQuitService(nil, "bpRequester", bpr)
bpr.BaseService = *NewBaseService(nil, "bpRequester", bpr)
return bpr
}
func (bpr *bpRequester) OnStart() error {
bpr.QuitService.OnStart()
go bpr.requestRoutine()
return nil
}

View File

@@ -5,8 +5,9 @@ import (
"testing"
"time"
. "github.com/tendermint/go-common"
"github.com/tendermint/tendermint/types"
. "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/log"
)
func init() {
@@ -34,7 +35,9 @@ func TestBasic(t *testing.T) {
timeoutsCh := make(chan string, 100)
requestsCh := make(chan BlockRequest, 100)
pool := NewBlockPool(start, requestsCh, timeoutsCh)
pool.SetLogger(log.TestingLogger())
pool.Start()
defer pool.Stop()
// Introduce each peer.
go func() {
@@ -64,7 +67,7 @@ func TestBasic(t *testing.T) {
case peerID := <-timeoutsCh:
t.Errorf("timeout: %v", peerID)
case request := <-requestsCh:
log.Info("TEST: Pulled new BlockRequest", "request", request)
t.Logf("Pulled new BlockRequest %v", request)
if request.Height == 300 {
return // Done!
}
@@ -72,12 +75,10 @@ func TestBasic(t *testing.T) {
go func() {
block := &types.Block{Header: &types.Header{Height: request.Height}}
pool.AddBlock(request.PeerID, block, 123)
log.Info("TEST: Added block", "block", request.Height, "peer", request.PeerID)
t.Logf("Added block from peer %v (height: %v)", request.PeerID, request.Height)
}()
}
}
pool.Stop()
}
func TestTimeout(t *testing.T) {
@@ -86,10 +87,12 @@ func TestTimeout(t *testing.T) {
timeoutsCh := make(chan string, 100)
requestsCh := make(chan BlockRequest, 100)
pool := NewBlockPool(start, requestsCh, timeoutsCh)
pool.SetLogger(log.TestingLogger())
pool.Start()
defer pool.Stop()
for _, peer := range peers {
log.Info("Peer", "id", peer.id)
t.Logf("Peer %v", peer.id)
}
// Introduce each peer.
@@ -120,7 +123,7 @@ func TestTimeout(t *testing.T) {
for {
select {
case peerID := <-timeoutsCh:
log.Info("Timeout", "peerID", peerID)
t.Logf("Peer %v timeouted", peerID)
if _, ok := timedOut[peerID]; !ok {
counter++
if counter == len(peers) {
@@ -128,9 +131,7 @@ func TestTimeout(t *testing.T) {
}
}
case request := <-requestsCh:
log.Info("TEST: Pulled new BlockRequest", "request", request)
t.Logf("Pulled new BlockRequest %+v", request)
}
}
pool.Stop()
}

View File

@@ -3,23 +3,22 @@ package blockchain
import (
"bytes"
"errors"
"fmt"
"reflect"
"time"
. "github.com/tendermint/go-common"
"github.com/tendermint/go-events"
"github.com/tendermint/go-p2p"
"github.com/tendermint/go-wire"
wire "github.com/tendermint/go-wire"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/proxy"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common"
)
const (
// BlockchainChannel is a channel for blocks and status updates (`BlockStore` height)
BlockchainChannel = byte(0x40)
defaultChannelCapacity = 100
defaultSleepIntervalMS = 500
trySyncIntervalMS = 100
// stop syncing when last block's time is
// within this much of the system time.
@@ -42,7 +41,6 @@ type consensusReactor interface {
type BlockchainReactor struct {
p2p.BaseReactor
sw *p2p.Switch
state *sm.State
proxyAppConn proxy.AppConnConsensus // same as consensus.proxyAppConn
store *BlockStore
@@ -50,17 +48,17 @@ type BlockchainReactor struct {
fastSync bool
requestsCh chan BlockRequest
timeoutsCh chan string
lastBlock *types.Block
evsw *events.EventSwitch
evsw types.EventSwitch
}
// NewBlockchainReactor returns new reactor instance.
func NewBlockchainReactor(state *sm.State, proxyAppConn proxy.AppConnConsensus, store *BlockStore, fastSync bool) *BlockchainReactor {
if state.LastBlockHeight == store.Height()-1 {
store.height -= 1 // XXX HACK, make this better
store.height-- // XXX HACK, make this better
}
if state.LastBlockHeight != store.Height() {
PanicSanity(Fmt("state (%v) and store (%v) height mismatch", state.LastBlockHeight, store.Height()))
cmn.PanicSanity(cmn.Fmt("state (%v) and store (%v) height mismatch", state.LastBlockHeight, store.Height()))
}
requestsCh := make(chan BlockRequest, defaultChannelCapacity)
timeoutsCh := make(chan string, defaultChannelCapacity)
@@ -78,10 +76,11 @@ func NewBlockchainReactor(state *sm.State, proxyAppConn proxy.AppConnConsensus,
requestsCh: requestsCh,
timeoutsCh: timeoutsCh,
}
bcR.BaseReactor = *p2p.NewBaseReactor(log, "BlockchainReactor", bcR)
bcR.BaseReactor = *p2p.NewBaseReactor("BlockchainReactor", bcR)
return bcR
}
// OnStart implements BaseService
func (bcR *BlockchainReactor) OnStart() error {
bcR.BaseReactor.OnStart()
if bcR.fastSync {
@@ -94,12 +93,13 @@ func (bcR *BlockchainReactor) OnStart() error {
return nil
}
// OnStop implements BaseService
func (bcR *BlockchainReactor) OnStop() {
bcR.BaseReactor.OnStop()
bcR.pool.Stop()
}
// Implements Reactor
// GetChannels implements Reactor
func (bcR *BlockchainReactor) GetChannels() []*p2p.ChannelDescriptor {
return []*p2p.ChannelDescriptor{
&p2p.ChannelDescriptor{
@@ -110,28 +110,29 @@ func (bcR *BlockchainReactor) GetChannels() []*p2p.ChannelDescriptor {
}
}
// Implements Reactor
// AddPeer implements Reactor by sending our state to peer.
func (bcR *BlockchainReactor) AddPeer(peer *p2p.Peer) {
// Send peer our state.
peer.Send(BlockchainChannel, struct{ BlockchainMessage }{&bcStatusResponseMessage{bcR.store.Height()}})
if !peer.Send(BlockchainChannel, struct{ BlockchainMessage }{&bcStatusResponseMessage{bcR.store.Height()}}) {
// doing nothing, will try later in `poolRoutine`
}
}
// Implements Reactor
// RemovePeer implements Reactor by removing peer from the pool.
func (bcR *BlockchainReactor) RemovePeer(peer *p2p.Peer, reason interface{}) {
// Remove peer from the pool.
bcR.pool.RemovePeer(peer.Key)
}
// Implements Reactor
// Receive implements Reactor by handling 4 types of messages (look below).
func (bcR *BlockchainReactor) Receive(chID byte, src *p2p.Peer, msgBytes []byte) {
_, msg, err := DecodeMessage(msgBytes)
if err != nil {
log.Warn("Error decoding message", "error", err)
bcR.Logger.Error("Error decoding message", "err", err)
return
}
log.Notice("Receive", "src", src, "chID", chID, "msg", msg)
bcR.Logger.Debug("Receive", "src", src, "chID", chID, "msg", msg)
// TODO: improve logic to satisfy megacheck
switch msg := msg.(type) {
case *bcBlockRequestMessage:
// Got a request for a block. Respond with block if we have it.
@@ -158,7 +159,7 @@ func (bcR *BlockchainReactor) Receive(chID byte, src *p2p.Peer, msgBytes []byte)
// Got a peer status. Unverified.
bcR.pool.SetPeerHeight(src.Key, msg.Height)
default:
log.Warn(Fmt("Unknown message type %v", reflect.TypeOf(msg)))
bcR.Logger.Error(cmn.Fmt("Unknown message type %v", reflect.TypeOf(msg)))
}
}
@@ -192,16 +193,16 @@ FOR_LOOP:
if peer != nil {
bcR.Switch.StopPeerForError(peer, errors.New("BlockchainReactor Timeout"))
}
case _ = <-statusUpdateTicker.C:
case <-statusUpdateTicker.C:
// ask for status updates
go bcR.BroadcastStatusRequest()
case _ = <-switchToConsensusTicker.C:
case <-switchToConsensusTicker.C:
height, numPending, _ := bcR.pool.GetStatus()
outbound, inbound, _ := bcR.Switch.NumPeers()
log.Info("Consensus ticker", "numPending", numPending, "total", len(bcR.pool.requesters),
bcR.Logger.Info("Consensus ticker", "numPending", numPending, "total", len(bcR.pool.requesters),
"outbound", outbound, "inbound", inbound)
if bcR.pool.IsCaughtUp() {
log.Notice("Time to switch to consensus reactor!", "height", height)
bcR.Logger.Info("Time to switch to consensus reactor!", "height", height)
bcR.pool.Stop()
conR := bcR.Switch.Reactor("CONSENSUS").(consensusReactor)
@@ -209,45 +210,43 @@ FOR_LOOP:
break FOR_LOOP
}
case _ = <-trySyncTicker.C: // chan time
case <-trySyncTicker.C: // chan time
// This loop can be slow as long as it's doing syncing work.
SYNC_LOOP:
for i := 0; i < 10; i++ {
// See if there are any blocks to sync.
first, second := bcR.pool.PeekTwoBlocks()
//log.Info("TrySync peeked", "first", first, "second", second)
//bcR.Logger.Info("TrySync peeked", "first", first, "second", second)
if first == nil || second == nil {
// We need both to sync the first block.
break SYNC_LOOP
}
firstParts := first.MakePartSet()
firstParts := first.MakePartSet(types.DefaultBlockPartSize)
firstPartsHeader := firstParts.Header()
// Finally, verify the first block using the second's commit
// NOTE: we can probably make this more efficient, but note that calling
// first.Hash() doesn't verify the tx contents, so MakePartSet() is
// currently necessary.
err := bcR.state.Validators.VerifyCommit(
bcR.state.ChainID, first.Hash(), firstPartsHeader, first.Height, second.LastCommit)
bcR.state.ChainID, types.BlockID{first.Hash(), firstPartsHeader}, first.Height, second.LastCommit)
if err != nil {
log.Info("error in validation", "error", err)
bcR.Logger.Info("error in validation", "err", err)
bcR.pool.RedoRequest(first.Height)
break SYNC_LOOP
} else {
bcR.pool.PopRequest()
// TODO: use ApplyBlock instead of Exec/Commit/SetAppHash/Save
err := bcR.state.ExecBlock(bcR.evsw, bcR.proxyAppConn, first, firstPartsHeader)
if err != nil {
// TODO This is bad, are we zombie?
PanicQ(Fmt("Failed to process committed block (%d:%X): %v", first.Height, first.Hash(), err))
}
bcR.store.SaveBlock(first, firstParts, second.LastCommit)
// TODO: should we be firing events? need to fire NewBlock events manually ...
// NOTE: we could improve performance if we
// didn't make the app commit to disk every block
// ... but we would need a way to get the hash without it persisting
res := bcR.proxyAppConn.CommitSync()
if res.IsErr() {
// TODO Handle gracefully.
PanicQ(Fmt("Failed to commit block at application: %v", res))
err := bcR.state.ApplyBlock(bcR.evsw, bcR.proxyAppConn, first, firstPartsHeader, types.MockMempool{})
if err != nil {
// TODO This is bad, are we zombie?
cmn.PanicQ(cmn.Fmt("Failed to process committed block (%d:%X): %v", first.Height, first.Hash(), err))
}
bcR.store.SaveBlock(first, firstParts, second.LastCommit)
bcR.state.AppHash = res.Data
bcR.state.Save()
}
}
continue FOR_LOOP
@@ -257,18 +256,14 @@ FOR_LOOP:
}
}
func (bcR *BlockchainReactor) BroadcastStatusResponse() error {
bcR.Switch.Broadcast(BlockchainChannel, struct{ BlockchainMessage }{&bcStatusResponseMessage{bcR.store.Height()}})
return nil
}
// BroadcastStatusRequest broadcasts `BlockStore` height.
func (bcR *BlockchainReactor) BroadcastStatusRequest() error {
bcR.Switch.Broadcast(BlockchainChannel, struct{ BlockchainMessage }{&bcStatusRequestMessage{bcR.store.Height()}})
return nil
}
// implements events.Eventable
func (bcR *BlockchainReactor) SetEventSwitch(evsw *events.EventSwitch) {
// SetEventSwitch implements events.Eventable
func (bcR *BlockchainReactor) SetEventSwitch(evsw types.EventSwitch) {
bcR.evsw = evsw
}
@@ -282,6 +277,7 @@ const (
msgTypeStatusRequest = byte(0x21)
)
// BlockchainMessage is a generic message for this reactor.
type BlockchainMessage interface{}
var _ = wire.RegisterInterface(
@@ -292,6 +288,7 @@ var _ = wire.RegisterInterface(
wire.ConcreteType{&bcStatusRequestMessage{}, msgTypeStatusRequest},
)
// DecodeMessage decodes BlockchainMessage.
// TODO: ensure that bz is completely read.
func DecodeMessage(bz []byte) (msgType byte, msg BlockchainMessage, err error) {
msgType = bz[0]
@@ -299,7 +296,7 @@ func DecodeMessage(bz []byte) (msgType byte, msg BlockchainMessage, err error) {
r := bytes.NewReader(bz)
msg = wire.ReadBinary(struct{ BlockchainMessage }{}, r, maxBlockchainResponseSize, &n, &err).(struct{ BlockchainMessage }).BlockchainMessage
if err != nil && n != len(bz) {
err = errors.New("DecodeMessage() had bytes left over.")
err = errors.New("DecodeMessage() had bytes left over")
}
return
}
@@ -311,7 +308,7 @@ type bcBlockRequestMessage struct {
}
func (m *bcBlockRequestMessage) String() string {
return fmt.Sprintf("[bcBlockRequestMessage %v]", m.Height)
return cmn.Fmt("[bcBlockRequestMessage %v]", m.Height)
}
//-------------------------------------
@@ -322,7 +319,7 @@ type bcBlockResponseMessage struct {
}
func (m *bcBlockResponseMessage) String() string {
return fmt.Sprintf("[bcBlockResponseMessage %v]", m.Block.Height)
return cmn.Fmt("[bcBlockResponseMessage %v]", m.Block.Height)
}
//-------------------------------------
@@ -332,7 +329,7 @@ type bcStatusRequestMessage struct {
}
func (m *bcStatusRequestMessage) String() string {
return fmt.Sprintf("[bcStatusRequestMessage %v]", m.Height)
return cmn.Fmt("[bcStatusRequestMessage %v]", m.Height)
}
//-------------------------------------
@@ -342,5 +339,5 @@ type bcStatusResponseMessage struct {
}
func (m *bcStatusResponseMessage) String() string {
return fmt.Sprintf("[bcStatusResponseMessage %v]", m.Height)
return cmn.Fmt("[bcStatusResponseMessage %v]", m.Height)
}

View File

@@ -5,9 +5,10 @@ import (
"encoding/json"
"fmt"
"io"
"sync"
. "github.com/tendermint/go-common"
dbm "github.com/tendermint/go-db"
. "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/go-wire"
"github.com/tendermint/tendermint/types"
)
@@ -27,8 +28,10 @@ the Commit data outside the Block.
Panics indicate probable corruption in the data
*/
type BlockStore struct {
height int
db dbm.DB
mtx sync.RWMutex
height int
}
func NewBlockStore(db dbm.DB) *BlockStore {
@@ -41,6 +44,8 @@ func NewBlockStore(db dbm.DB) *BlockStore {
// Height() returns the last known contiguous block height.
func (bs *BlockStore) Height() int {
bs.mtx.RLock()
defer bs.mtx.RUnlock()
return bs.height
}
@@ -59,12 +64,12 @@ func (bs *BlockStore) LoadBlock(height int) *types.Block {
if r == nil {
return nil
}
meta := wire.ReadBinary(&types.BlockMeta{}, r, 0, &n, &err).(*types.BlockMeta)
blockMeta := wire.ReadBinary(&types.BlockMeta{}, r, 0, &n, &err).(*types.BlockMeta)
if err != nil {
PanicCrisis(Fmt("Error reading block meta: %v", err))
}
bytez := []byte{}
for i := 0; i < meta.PartsHeader.Total; i++ {
for i := 0; i < blockMeta.BlockID.PartsHeader.Total; i++ {
part := bs.LoadBlockPart(height, i)
bytez = append(bytez, part.Bytes...)
}
@@ -96,11 +101,11 @@ func (bs *BlockStore) LoadBlockMeta(height int) *types.BlockMeta {
if r == nil {
return nil
}
meta := wire.ReadBinary(&types.BlockMeta{}, r, 0, &n, &err).(*types.BlockMeta)
blockMeta := wire.ReadBinary(&types.BlockMeta{}, r, 0, &n, &err).(*types.BlockMeta)
if err != nil {
PanicCrisis(Fmt("Error reading block meta: %v", err))
}
return meta
return blockMeta
}
// The +2/3 and other Precommit-votes for block at `height`.
@@ -141,16 +146,16 @@ func (bs *BlockStore) LoadSeenCommit(height int) *types.Commit {
// most recent height. Otherwise they'd stall at H-1.
func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) {
height := block.Height
if height != bs.height+1 {
PanicSanity(Fmt("BlockStore can only save contiguous blocks. Wanted %v, got %v", bs.height+1, height))
if height != bs.Height()+1 {
PanicSanity(Fmt("BlockStore can only save contiguous blocks. Wanted %v, got %v", bs.Height()+1, height))
}
if !blockParts.IsComplete() {
PanicSanity(Fmt("BlockStore can only save complete block part sets"))
}
// Save block meta
meta := types.NewBlockMeta(block, blockParts)
metaBytes := wire.BinaryBytes(meta)
blockMeta := types.NewBlockMeta(block, blockParts)
metaBytes := wire.BinaryBytes(blockMeta)
bs.db.Set(calcBlockMetaKey(height), metaBytes)
// Save block parts
@@ -163,6 +168,7 @@ func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, s
bs.db.Set(calcBlockCommitKey(height-1), blockCommitBytes)
// Save seen commit (seen +2/3 precommits for block)
// NOTE: we can delete this at a later height
seenCommitBytes := wire.BinaryBytes(seenCommit)
bs.db.Set(calcSeenCommitKey(height), seenCommitBytes)
@@ -170,12 +176,17 @@ func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, s
BlockStoreStateJSON{Height: height}.Save(bs.db)
// Done!
bs.mtx.Lock()
bs.height = height
bs.mtx.Unlock()
// Flush
bs.db.SetSync(nil, nil)
}
func (bs *BlockStore) saveBlockPart(height int, index int, part *types.Part) {
if height != bs.height+1 {
PanicSanity(Fmt("BlockStore can only save contiguous blocks. Wanted %v, got %v", bs.height+1, height))
if height != bs.Height()+1 {
PanicSanity(Fmt("BlockStore can only save contiguous blocks. Wanted %v, got %v", bs.Height()+1, height))
}
partBytes := wire.BinaryBytes(part)
bs.db.Set(calcBlockPartKey(height, index), partBytes)
@@ -212,7 +223,7 @@ func (bsj BlockStoreStateJSON) Save(db dbm.DB) {
if err != nil {
PanicSanity(Fmt("Could not marshal state bytes: %v", err))
}
db.Set(blockStoreKey, bytes)
db.SetSync(blockStoreKey, bytes)
}
func LoadBlockStoreStateJSON(db dbm.DB) BlockStoreStateJSON {

View File

@@ -1,34 +1,33 @@
---
machine:
environment:
MACH_PREFIX: tendermint-test-mach
GOPATH: /home/ubuntu/.go_workspace
REPO: $GOPATH/src/github.com/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME
DOCKER_VERSION: 1.10.0
DOCKER_MACHINE_VERSION: 0.6.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"
hosts:
circlehost: 127.0.0.1
localhost: 127.0.0.1
checkout:
post:
- rm -rf $REPO
- mkdir -p $HOME/.go_workspace/src/github.com/$CIRCLE_PROJECT_USERNAME
- mv $HOME/$CIRCLE_PROJECT_REPONAME $REPO
# - git submodule sync
# - git submodule update --init # use submodules
dependencies:
override:
- echo $MACH_PREFIX $GOPATH $REPO $DOCKER_VERSION $DOCKER_MACHINE_VERSION
- curl -sSL https://s3.amazonaws.com/circle-downloads/install-circleci-docker.sh | sudo bash -s -- $DOCKER_VERSION
- sudo curl -sSL -o /usr/bin/docker-machine https://github.com/docker/machine/releases/download/v$DOCKER_MACHINE_VERSION/docker-machine-linux-x86_64; sudo chmod 0755 /usr/bin/docker-machine
- 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 $REPO && make test_integrations"
- cd "$PROJECT_PATH" && set -o pipefail && make test_integrations 2>&1 | tee test_integrations.log:
timeout: 1800
post:
- bash <(curl -s https://codecov.io/bash)
- 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_logs.txt"

View File

@@ -0,0 +1,27 @@
package commands
import (
"encoding/json"
"fmt"
"github.com/spf13/cobra"
"github.com/tendermint/tendermint/types"
)
var genValidatorCmd = &cobra.Command{
Use: "gen_validator",
Short: "Generate new validator keypair",
Run: genValidator,
}
func init() {
RootCmd.AddCommand(genValidatorCmd)
}
func genValidator(cmd *cobra.Command, args []string) {
privValidator := types.GenPrivValidator()
privValidatorJSONBytes, _ := json.MarshalIndent(privValidator, "", "\t")
fmt.Printf(`%v
`, string(privValidatorJSONBytes))
}

View File

@@ -0,0 +1,47 @@
package commands
import (
"os"
"github.com/spf13/cobra"
"github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common"
)
var initFilesCmd = &cobra.Command{
Use: "init",
Short: "Initialize Tendermint",
Run: initFiles,
}
func init() {
RootCmd.AddCommand(initFilesCmd)
}
func initFiles(cmd *cobra.Command, args []string) {
privValFile := config.PrivValidatorFile()
if _, err := os.Stat(privValFile); os.IsNotExist(err) {
privValidator := types.GenPrivValidator()
privValidator.SetFile(privValFile)
privValidator.Save()
genFile := config.GenesisFile()
if _, err := os.Stat(genFile); os.IsNotExist(err) {
genDoc := types.GenesisDoc{
ChainID: cmn.Fmt("test-chain-%v", cmn.RandStr(6)),
}
genDoc.Validators = []types.GenesisValidator{types.GenesisValidator{
PubKey: privValidator.PubKey,
Amount: 10,
}}
genDoc.SaveAs(genFile)
}
logger.Info("Initialized tendermint", "genesis", config.GenesisFile(), "priv_validator", config.PrivValidatorFile())
} else {
logger.Info("Already initialized", "priv_validator", config.PrivValidatorFile())
}
}

View File

@@ -0,0 +1,35 @@
package commands
import (
"encoding/json"
"fmt"
"github.com/spf13/cobra"
"github.com/tendermint/tendermint/p2p/upnp"
)
var probeUpnpCmd = &cobra.Command{
Use: "probe_upnp",
Short: "Test UPnP functionality",
RunE: probeUpnp,
}
func init() {
RootCmd.AddCommand(probeUpnpCmd)
}
func probeUpnp(cmd *cobra.Command, args []string) error {
capabilities, err := upnp.Probe(logger)
if err != nil {
fmt.Println("Probe failed: %v", err)
} else {
fmt.Println("Probe success!")
jsonBytes, err := json.Marshal(capabilities)
if err != nil {
return err
}
fmt.Println(string(jsonBytes))
}
return nil
}

View File

@@ -0,0 +1,28 @@
package commands
import (
"github.com/spf13/cobra"
"github.com/tendermint/tendermint/consensus"
)
var replayCmd = &cobra.Command{
Use: "replay",
Short: "Replay messages from WAL",
Run: func(cmd *cobra.Command, args []string) {
consensus.RunReplayFile(config.BaseConfig, config.Consensus, false)
},
}
var replayConsoleCmd = &cobra.Command{
Use: "replay_console",
Short: "Replay messages from WAL in a console",
Run: func(cmd *cobra.Command, args []string) {
consensus.RunReplayFile(config.BaseConfig, config.Consensus, true)
},
}
func init() {
RootCmd.AddCommand(replayCmd)
RootCmd.AddCommand(replayConsoleCmd)
}

View File

@@ -0,0 +1,61 @@
package commands
import (
"os"
"github.com/spf13/cobra"
"github.com/tendermint/tendermint/types"
"github.com/tendermint/tmlibs/log"
)
var resetAllCmd = &cobra.Command{
Use: "unsafe_reset_all",
Short: "(unsafe) Remove all the data and WAL, reset this node's validator",
Run: resetAll,
}
var resetPrivValidatorCmd = &cobra.Command{
Use: "unsafe_reset_priv_validator",
Short: "(unsafe) Reset this node's validator",
Run: resetPrivValidator,
}
func init() {
RootCmd.AddCommand(resetAllCmd)
RootCmd.AddCommand(resetPrivValidatorCmd)
}
// XXX: this is totally unsafe.
// it's only suitable for testnets.
func resetAll(cmd *cobra.Command, args []string) {
ResetAll(config.DBDir(), config.PrivValidatorFile(), logger)
}
// XXX: this is totally unsafe.
// it's only suitable for testnets.
func resetPrivValidator(cmd *cobra.Command, args []string) {
resetPrivValidatorLocal(config.PrivValidatorFile(), logger)
}
// Exported so other CLI tools can use it
func ResetAll(dbDir, privValFile string, logger log.Logger) {
resetPrivValidatorLocal(privValFile, logger)
os.RemoveAll(dbDir)
logger.Info("Removed all data", "dir", dbDir)
}
func resetPrivValidatorLocal(privValFile string, logger log.Logger) {
// Get PrivValidator
var privValidator *types.PrivValidator
if _, err := os.Stat(privValFile); err == nil {
privValidator = types.LoadPrivValidator(privValFile)
privValidator.Reset()
logger.Info("Reset PrivValidator", "file", privValFile)
} else {
privValidator = types.GenPrivValidator()
privValidator.SetFile(privValFile)
privValidator.Save()
logger.Info("Generated PrivValidator", "file", privValFile)
}
}

View File

@@ -0,0 +1,53 @@
package commands
import (
"os"
"github.com/spf13/cobra"
"github.com/spf13/viper"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tmlibs/cli"
tmflags "github.com/tendermint/tmlibs/cli/flags"
"github.com/tendermint/tmlibs/log"
)
var (
config = cfg.DefaultConfig()
logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "main")
)
func init() {
RootCmd.PersistentFlags().String("log_level", config.LogLevel, "Log level")
}
// ParseConfig will setup the tendermint configuration properly
func ParseConfig() (*cfg.Config, error) {
conf := cfg.DefaultConfig()
err := viper.Unmarshal(conf)
if err != nil {
return nil, err
}
conf.SetRoot(conf.RootDir)
cfg.EnsureRoot(conf.RootDir)
return conf, err
}
var RootCmd = &cobra.Command{
Use: "tendermint",
Short: "Tendermint Core (BFT Consensus) in Go",
PersistentPreRunE: func(cmd *cobra.Command, args []string) (err error) {
config, err = ParseConfig()
if err != nil {
return err
}
logger, err = tmflags.ParseLogLevel(config.LogLevel, logger, cfg.DefaultLogLevel())
if err != nil {
return err
}
if viper.GetBool(cli.TraceFlag) {
logger = log.NewTracingLogger(logger)
}
return nil
},
}

View File

@@ -0,0 +1,105 @@
package commands
import (
"os"
"strconv"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tmlibs/cli"
"testing"
)
var (
defaultRoot = os.ExpandEnv("$HOME/.some/test/dir")
)
const (
rootName = "root"
)
// isolate provides a clean setup and returns a copy of RootCmd you can
// modify in the test cases.
// NOTE: it unsets all TM* env variables.
func isolate(cmds ...*cobra.Command) cli.Executable {
os.Unsetenv("TMHOME")
os.Unsetenv("TM_HOME")
os.Unsetenv("TMROOT")
os.Unsetenv("TM_ROOT")
viper.Reset()
config = cfg.DefaultConfig()
r := &cobra.Command{
Use: rootName,
PersistentPreRunE: RootCmd.PersistentPreRunE,
}
r.AddCommand(cmds...)
wr := cli.PrepareBaseCmd(r, "TM", defaultRoot)
return wr
}
func TestRootConfig(t *testing.T) {
assert, require := assert.New(t), require.New(t)
// we pre-create a config file we can refer to in the rest of
// the test cases.
cvals := map[string]string{
"moniker": "monkey",
"fast_sync": "false",
}
// proper types of the above settings
cfast := false
conf, err := cli.WriteDemoConfig(cvals)
require.Nil(err)
defaults := cfg.DefaultConfig()
dmax := defaults.P2P.MaxNumPeers
cases := []struct {
args []string
env map[string]string
root string
moniker string
fastSync bool
maxPeer int
}{
{nil, nil, defaultRoot, defaults.Moniker, defaults.FastSync, dmax},
// try multiple ways of setting root (two flags, cli vs. env)
{[]string{"--home", conf}, nil, conf, cvals["moniker"], cfast, dmax},
{nil, map[string]string{"TMROOT": conf}, conf, cvals["moniker"], cfast, dmax},
// check setting p2p subflags two different ways
{[]string{"--p2p.max_num_peers", "420"}, nil, defaultRoot, defaults.Moniker, defaults.FastSync, 420},
{nil, map[string]string{"TM_P2P_MAX_NUM_PEERS": "17"}, defaultRoot, defaults.Moniker, defaults.FastSync, 17},
// try to set env that have no flags attached...
{[]string{"--home", conf}, map[string]string{"TM_MONIKER": "funny"}, conf, "funny", cfast, dmax},
}
for idx, tc := range cases {
i := strconv.Itoa(idx)
// test command that does nothing, except trigger unmarshalling in root
noop := &cobra.Command{
Use: "noop",
RunE: func(cmd *cobra.Command, args []string) error {
return nil
},
}
noop.Flags().Int("p2p.max_num_peers", defaults.P2P.MaxNumPeers, "")
cmd := isolate(noop)
args := append([]string{rootName, noop.Use}, tc.args...)
err := cli.RunWithArgs(cmd, args, tc.env)
require.Nil(err, i)
assert.Equal(tc.root, config.RootDir, i)
assert.Equal(tc.root, config.P2P.RootDir, i)
assert.Equal(tc.root, config.Consensus.RootDir, i)
assert.Equal(tc.root, config.Mempool.RootDir, i)
assert.Equal(tc.moniker, config.Moniker, i)
assert.Equal(tc.fastSync, config.FastSync, i)
assert.Equal(tc.maxPeer, config.P2P.MaxNumPeers, i)
}
}

View File

@@ -0,0 +1,89 @@
package commands
import (
"fmt"
"time"
"github.com/spf13/cobra"
"github.com/tendermint/tendermint/node"
"github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common"
)
var runNodeCmd = &cobra.Command{
Use: "node",
Short: "Run the tendermint node",
RunE: runNode,
}
func init() {
AddNodeFlags(runNodeCmd)
RootCmd.AddCommand(runNodeCmd)
}
// AddNodeFlags exposes some common configuration options on the command-line
// These are exposed for convenience of commands embedding a tendermint node
func AddNodeFlags(cmd *cobra.Command) {
// bind flags
cmd.Flags().String("moniker", config.Moniker, "Node Name")
// node flags
cmd.Flags().Bool("fast_sync", config.FastSync, "Fast blockchain syncing")
// abci flags
cmd.Flags().String("proxy_app", config.ProxyApp, "Proxy app address, or 'nilapp' or 'dummy' for local testing.")
cmd.Flags().String("abci", config.ABCI, "Specify abci transport (socket | grpc)")
// rpc flags
cmd.Flags().String("rpc.laddr", config.RPC.ListenAddress, "RPC listen address. Port required")
cmd.Flags().String("rpc.grpc_laddr", config.RPC.GRPCListenAddress, "GRPC listen address (BroadcastTx only). Port required")
cmd.Flags().Bool("rpc.unsafe", config.RPC.Unsafe, "Enabled unsafe rpc methods")
// 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.seeds", config.P2P.Seeds, "Comma delimited host:port seed nodes")
cmd.Flags().Bool("p2p.skip_upnp", config.P2P.SkipUPNP, "Skip UPNP configuration")
cmd.Flags().Bool("p2p.pex", config.P2P.PexReactor, "Enable Peer-Exchange (dev feature)")
// 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")
}
// Users wishing to:
// * Use an external signer for their validators
// * Supply an in-proc abci app
// should import github.com/tendermint/tendermint/node and implement
// their own run_node to call node.NewNode (instead of node.NewNodeDefault)
// with their custom priv validator and/or custom proxy.ClientCreator
func runNode(cmd *cobra.Command, args []string) error {
// Wait until the genesis doc becomes available
// This is for Mintnet compatibility.
// TODO: If Mintnet gets deprecated or genesis_file is
// always available, remove.
genDocFile := config.GenesisFile()
for !cmn.FileExists(genDocFile) {
logger.Info(cmn.Fmt("Waiting for genesis file %v...", genDocFile))
time.Sleep(time.Second)
}
genDoc, err := types.GenesisDocFromFile(genDocFile)
if err != nil {
return err
}
config.ChainID = genDoc.ChainID
// Create & start node
n := node.NewNodeDefault(config, logger.With("module", "node"))
if _, err := n.Start(); err != nil {
return fmt.Errorf("Failed to start node: %v", err)
} else {
logger.Info("Started node", "nodeInfo", n.Switch().NodeInfo())
}
// Trap signal, run forever.
n.RunForever()
return nil
}

View File

@@ -0,0 +1,26 @@
package commands
import (
"fmt"
"github.com/spf13/cobra"
"github.com/tendermint/go-wire/data"
"github.com/tendermint/tendermint/types"
)
var showValidatorCmd = &cobra.Command{
Use: "show_validator",
Short: "Show this node's validator info",
Run: showValidator,
}
func init() {
RootCmd.AddCommand(showValidatorCmd)
}
func showValidator(cmd *cobra.Command, args []string) {
privValidator := types.LoadOrGenPrivValidator(config.PrivValidatorFile(), logger)
pubKeyJSONBytes, _ := data.ToJSON(privValidator.PubKey)
fmt.Println(string(pubKeyJSONBytes))
}

View File

@@ -0,0 +1,93 @@
package commands
import (
"fmt"
"path"
"time"
"github.com/spf13/cobra"
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tendermint/types"
)
var testnetFilesCmd = &cobra.Command{
Use: "testnet",
Short: "Initialize files for a Tendermint testnet",
Run: testnetFiles,
}
//flags
var (
nValidators int
dataDir string
)
func init() {
testnetFilesCmd.Flags().IntVar(&nValidators, "n", 4,
"Number of validators to initialize the testnet with")
testnetFilesCmd.Flags().StringVar(&dataDir, "dir", "mytestnet",
"Directory to store initialization data for the testnet")
RootCmd.AddCommand(testnetFilesCmd)
}
func testnetFiles(cmd *cobra.Command, args []string) {
genVals := make([]types.GenesisValidator, nValidators)
// Initialize core dir and priv_validator.json's
for i := 0; i < nValidators; i++ {
mach := cmn.Fmt("mach%d", i)
err := initMachCoreDirectory(dataDir, mach)
if err != nil {
cmn.Exit(err.Error())
}
// Read priv_validator.json to populate vals
privValFile := path.Join(dataDir, mach, "priv_validator.json")
privVal := types.LoadPrivValidator(privValFile)
genVals[i] = types.GenesisValidator{
PubKey: privVal.PubKey,
Amount: 1,
Name: mach,
}
}
// Generate genesis doc from generated validators
genDoc := &types.GenesisDoc{
GenesisTime: time.Now(),
ChainID: "chain-" + cmn.RandStr(6),
Validators: genVals,
}
// Write genesis file.
for i := 0; i < nValidators; i++ {
mach := cmn.Fmt("mach%d", i)
genDoc.SaveAs(path.Join(dataDir, mach, "genesis.json"))
}
fmt.Println(cmn.Fmt("Successfully initialized %v node directories", nValidators))
}
// Initialize per-machine core directory
func initMachCoreDirectory(base, mach string) error {
dir := path.Join(base, mach)
err := cmn.EnsureDir(dir, 0777)
if err != nil {
return err
}
// Create priv_validator.json file if not present
ensurePrivValidator(path.Join(dir, "priv_validator.json"))
return nil
}
func ensurePrivValidator(file string) {
if cmn.FileExists(file) {
return
}
privValidator := types.GenPrivValidator()
privValidator.SetFile(file)
privValidator.Save()
}

View File

@@ -0,0 +1,21 @@
package commands
import (
"fmt"
"github.com/spf13/cobra"
"github.com/tendermint/tendermint/version"
)
var versionCmd = &cobra.Command{
Use: "version",
Short: "Show version info",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(version.Version)
},
}
func init() {
RootCmd.AddCommand(versionCmd)
}

View File

@@ -1,53 +0,0 @@
package main
import (
flag "github.com/spf13/pflag"
"os"
cfg "github.com/tendermint/go-config"
)
func parseFlags(config cfg.Config, args []string) {
var (
printHelp bool
moniker string
nodeLaddr string
seeds string
fastSync bool
skipUPNP bool
rpcLaddr string
logLevel string
proxyApp string
tmspTransport string
)
// Declare flags
var flags = flag.NewFlagSet("main", flag.ExitOnError)
flags.BoolVar(&printHelp, "help", false, "Print this help message.")
flags.StringVar(&moniker, "moniker", config.GetString("moniker"), "Node Name")
flags.StringVar(&nodeLaddr, "node_laddr", config.GetString("node_laddr"), "Node listen address. (0.0.0.0:0 means any interface, any port)")
flags.StringVar(&seeds, "seeds", config.GetString("seeds"), "Comma delimited host:port seed nodes")
flags.BoolVar(&fastSync, "fast_sync", config.GetBool("fast_sync"), "Fast blockchain syncing")
flags.BoolVar(&skipUPNP, "skip_upnp", config.GetBool("skip_upnp"), "Skip UPNP configuration")
flags.StringVar(&rpcLaddr, "rpc_laddr", config.GetString("rpc_laddr"), "RPC listen address. Port required")
flags.StringVar(&logLevel, "log_level", config.GetString("log_level"), "Log level")
flags.StringVar(&proxyApp, "proxy_app", config.GetString("proxy_app"),
"Proxy app address, or 'nilapp' or 'dummy' for local testing.")
flags.StringVar(&tmspTransport, "tmsp", config.GetString("tmsp"), "Specify tmsp transport (socket | grpc)")
flags.Parse(args)
if printHelp {
flags.PrintDefaults()
os.Exit(0)
}
// Merge parsed flag values onto app.
config.Set("moniker", moniker)
config.Set("node_laddr", nodeLaddr)
config.Set("seeds", seeds)
config.Set("fast_sync", fastSync)
config.Set("skip_upnp", skipUPNP)
config.Set("rpc_laddr", rpcLaddr)
config.Set("log_level", logLevel)
config.Set("proxy_app", proxyApp)
config.Set("tmsp", tmspTransport)
}

View File

@@ -1,23 +0,0 @@
package main
import (
"fmt"
"github.com/tendermint/go-wire"
"github.com/tendermint/tendermint/types"
)
func gen_validator() {
privValidator := types.GenPrivValidator()
privValidatorJSONBytes := wire.JSONBytesPretty(privValidator)
fmt.Printf(`Generated a new validator!
Paste the following JSON into your %v file
%v
`,
config.GetString("priv_validator_file"),
string(privValidatorJSONBytes),
)
}

View File

@@ -1,23 +0,0 @@
package main
import (
. "github.com/tendermint/go-common"
"github.com/tendermint/tendermint/types"
)
func init_files() {
privValidator := types.GenPrivValidator()
privValidator.SetFile(config.GetString("priv_validator_file"))
privValidator.Save()
genDoc := types.GenesisDoc{
ChainID: Fmt("test-chain-%v", RandStr(6)),
}
genDoc.Validators = []types.GenesisValidator{types.GenesisValidator{
PubKey: privValidator.PubKey,
Amount: 10,
}}
genDoc.SaveAs(config.GetString("genesis_file"))
}

View File

@@ -1,7 +0,0 @@
package main
import (
"github.com/tendermint/go-logger"
)
var log = logger.New("module", "main")

View File

@@ -1,66 +1,13 @@
package main
import (
"fmt"
"os"
. "github.com/tendermint/go-common"
cfg "github.com/tendermint/go-config"
"github.com/tendermint/go-logger"
tmcfg "github.com/tendermint/tendermint/config/tendermint"
"github.com/tendermint/tendermint/node"
"github.com/tendermint/tendermint/version"
"github.com/tendermint/tendermint/cmd/tendermint/commands"
"github.com/tendermint/tmlibs/cli"
)
var config cfg.Config
func main() {
args := os.Args[1:]
if len(args) == 0 {
fmt.Println(`Tendermint
Commands:
node Run the tendermint node
show_validator Show this node's validator info
gen_validator Generate new validator keypair
probe_upnp Test UPnP functionality
version Show version info
`)
return
}
// Get configuration
config = tmcfg.GetConfig("")
parseFlags(config, args[1:]) // Command line overrides
// set the log level
logger.SetLogLevel(config.GetString("log_level"))
switch args[0] {
case "node":
node.RunNode(config)
case "replay":
if len(args) > 1 && args[1] == "console" {
node.RunReplayConsole(config)
} else {
node.RunReplay(config)
}
case "init":
init_files()
case "show_validator":
show_validator()
case "gen_validator":
gen_validator()
case "probe_upnp":
probe_upnp()
case "unsafe_reset_all":
reset_all()
case "unsafe_reset_priv_validator":
reset_priv_validator()
case "version":
fmt.Println(version.Version)
default:
Exit(Fmt("Unknown command %v\n", args[0]))
}
cmd := cli.PrepareBaseCmd(commands.RootCmd, "TM", os.ExpandEnv("$HOME/.tendermint"))
cmd.Execute()
}

View File

@@ -1,24 +0,0 @@
package main
import (
"encoding/json"
"fmt"
"github.com/tendermint/go-p2p/upnp"
)
func probe_upnp() {
capabilities, err := upnp.Probe()
if err != nil {
fmt.Println("Probe failed: %v", err)
} else {
fmt.Println("Probe success!")
jsonBytes, err := json.Marshal(capabilities)
if err != nil {
panic(err)
}
fmt.Println(string(jsonBytes))
}
}

View File

@@ -1,33 +0,0 @@
package main
import (
"os"
"github.com/tendermint/tendermint/types"
)
// NOTE: this is totally unsafe.
// it's only suitable for testnets.
func reset_all() {
reset_priv_validator()
os.RemoveAll(config.GetString("db_dir"))
os.Remove(config.GetString("cswal"))
}
// NOTE: this is totally unsafe.
// it's only suitable for testnets.
func reset_priv_validator() {
// Get PrivValidator
var privValidator *types.PrivValidator
privValidatorFile := config.GetString("priv_validator_file")
if _, err := os.Stat(privValidatorFile); err == nil {
privValidator = types.LoadPrivValidator(privValidatorFile)
privValidator.Reset()
log.Notice("Reset PrivValidator", "file", privValidatorFile)
} else {
privValidator = types.GenPrivValidator()
privValidator.SetFile(privValidatorFile)
privValidator.Save()
log.Notice("Generated PrivValidator", "file", privValidatorFile)
}
}

View File

@@ -1,14 +0,0 @@
package main
import (
"fmt"
"github.com/tendermint/go-wire"
"github.com/tendermint/tendermint/types"
)
func show_validator() {
privValidatorFile := config.GetString("priv_validator_file")
privValidator := types.LoadOrGenPrivValidator(privValidatorFile)
fmt.Println(string(wire.JSONBytesPretty(privValidator.PubKey)))
}

419
config/config.go Normal file
View File

@@ -0,0 +1,419 @@
package config
import (
"fmt"
"path/filepath"
"time"
"github.com/tendermint/tendermint/types" // TODO: remove
)
// Config defines the top level configuration for a Tendermint node
type Config struct {
// Top level options use an anonymous struct
BaseConfig `mapstructure:",squash"`
// Options for services
RPC *RPCConfig `mapstructure:"rpc"`
P2P *P2PConfig `mapstructure:"p2p"`
Mempool *MempoolConfig `mapstructure:"mempool"`
Consensus *ConsensusConfig `mapstructure:"consensus"`
}
// DefaultConfig returns a default configuration for a Tendermint node
func DefaultConfig() *Config {
return &Config{
BaseConfig: DefaultBaseConfig(),
RPC: DefaultRPCConfig(),
P2P: DefaultP2PConfig(),
Mempool: DefaultMempoolConfig(),
Consensus: DefaultConsensusConfig(),
}
}
// TestConfig returns a configuration that can be used for testing
func TestConfig() *Config {
return &Config{
BaseConfig: TestBaseConfig(),
RPC: TestRPCConfig(),
P2P: TestP2PConfig(),
Mempool: DefaultMempoolConfig(),
Consensus: TestConsensusConfig(),
}
}
// SetRoot sets the RootDir for all Config structs
func (cfg *Config) SetRoot(root string) *Config {
cfg.BaseConfig.RootDir = root
cfg.RPC.RootDir = root
cfg.P2P.RootDir = root
cfg.Mempool.RootDir = root
cfg.Consensus.RootDir = root
return cfg
}
//-----------------------------------------------------------------------------
// BaseConfig
// BaseConfig defines the base configuration for a Tendermint node
type BaseConfig struct {
// The root directory for all data.
// This should be set in viper so it can unmarshal into this struct
RootDir string `mapstructure:"home"`
// The ID of the chain to join (should be signed with every transaction and vote)
ChainID string `mapstructure:"chain_id"`
// A JSON file containing the initial validator set and other meta data
Genesis string `mapstructure:"genesis_file"`
// A JSON file containing the private key to use as a validator in the consensus protocol
PrivValidator string `mapstructure:"priv_validator_file"`
// A custom human readable name for this node
Moniker string `mapstructure:"moniker"`
// TCP or UNIX socket address of the ABCI application,
// or the name of an ABCI application compiled in with the Tendermint binary
ProxyApp string `mapstructure:"proxy_app"`
// Mechanism to connect to the ABCI application: socket | grpc
ABCI string `mapstructure:"abci"`
// Output level for logging
LogLevel string `mapstructure:"log_level"`
// TCP or UNIX socket address for the profiling server to listen on
ProfListenAddress string `mapstructure:"prof_laddr"`
// If this node is many blocks behind the tip of the chain, FastSync
// allows them to catchup quickly by downloading blocks in parallel
// and verifying their commits
FastSync bool `mapstructure:"fast_sync"`
// If true, query the ABCI app on connecting to a new peer
// so the app can decide if we should keep the connection or not
FilterPeers bool `mapstructure:"filter_peers"` // false
// What indexer to use for transactions
TxIndex string `mapstructure:"tx_index"`
// Database backend: leveldb | memdb
DBBackend string `mapstructure:"db_backend"`
// Database directory
DBPath string `mapstructure:"db_dir"`
}
// DefaultBaseConfig returns a default base configuration for a Tendermint node
func DefaultBaseConfig() BaseConfig {
return BaseConfig{
Genesis: "genesis.json",
PrivValidator: "priv_validator.json",
Moniker: "anonymous",
ProxyApp: "tcp://127.0.0.1:46658",
ABCI: "socket",
LogLevel: DefaultPackageLogLevels(),
ProfListenAddress: "",
FastSync: true,
FilterPeers: false,
TxIndex: "kv",
DBBackend: "leveldb",
DBPath: "data",
}
}
// TestBaseConfig returns a base configuration for testing a Tendermint node
func TestBaseConfig() BaseConfig {
conf := DefaultBaseConfig()
conf.ChainID = "tendermint_test"
conf.ProxyApp = "dummy"
conf.FastSync = false
conf.DBBackend = "memdb"
return conf
}
// GenesisFile returns the full path to the genesis.json file
func (b BaseConfig) GenesisFile() string {
return rootify(b.Genesis, b.RootDir)
}
// PrivValidatorFile returns the full path to the priv_validator.json file
func (b BaseConfig) PrivValidatorFile() string {
return rootify(b.PrivValidator, b.RootDir)
}
// DBDir returns the full path to the database directory
func (b BaseConfig) DBDir() string {
return rootify(b.DBPath, b.RootDir)
}
// DefaultLogLevel returns a default log level of "error"
func DefaultLogLevel() string {
return "error"
}
// DefaultPackageLogLevels returns a default log level setting so all packages log at "error", while the `state` package logs at "info"
func DefaultPackageLogLevels() string {
return fmt.Sprintf("state:info,*:%s", DefaultLogLevel())
}
//-----------------------------------------------------------------------------
// RPCConfig
// RPCConfig defines the configuration options for the Tendermint RPC server
type RPCConfig struct {
RootDir string `mapstructure:"home"`
// TCP or UNIX socket address for the RPC server to listen on
ListenAddress string `mapstructure:"laddr"`
// TCP or UNIX socket address for the gRPC server to listen on
// NOTE: This server only supports /broadcast_tx_commit
GRPCListenAddress string `mapstructure:"grpc_laddr"`
// Activate unsafe RPC commands like /dial_seeds and /unsafe_flush_mempool
Unsafe bool `mapstructure:"unsafe"`
}
// DefaultRPCConfig returns a default configuration for the RPC server
func DefaultRPCConfig() *RPCConfig {
return &RPCConfig{
ListenAddress: "tcp://0.0.0.0:46657",
GRPCListenAddress: "",
Unsafe: false,
}
}
// TestRPCConfig returns a configuration for testing the RPC server
func TestRPCConfig() *RPCConfig {
conf := DefaultRPCConfig()
conf.ListenAddress = "tcp://0.0.0.0:36657"
conf.GRPCListenAddress = "tcp://0.0.0.0:36658"
conf.Unsafe = true
return conf
}
//-----------------------------------------------------------------------------
// P2PConfig
// P2PConfig defines the configuration options for the Tendermint peer-to-peer networking layer
type P2PConfig struct {
RootDir string `mapstructure:"home"`
// Address to listen for incoming connections
ListenAddress string `mapstructure:"laddr"`
// Comma separated list of seed nodes to connect to
Seeds string `mapstructure:"seeds"`
// Skip UPNP port forwarding
SkipUPNP bool `mapstructure:"skip_upnp"`
// Path to address book
AddrBook string `mapstructure:"addr_book_file"`
// Set true for strict address routability rules
AddrBookStrict bool `mapstructure:"addr_book_strict"`
// Set true to enable the peer-exchange reactor
PexReactor bool `mapstructure:"pex"`
// Maximum number of peers to connect to
MaxNumPeers int `mapstructure:"max_num_peers"`
// Time to wait before flushing messages out on the connection. In ms
FlushThrottleTimeout int `mapstructure:"flush_throttle_timeout"`
}
// DefaultP2PConfig returns a default configuration for the peer-to-peer layer
func DefaultP2PConfig() *P2PConfig {
return &P2PConfig{
ListenAddress: "tcp://0.0.0.0:46656",
AddrBook: "addrbook.json",
AddrBookStrict: true,
MaxNumPeers: 50,
FlushThrottleTimeout: 100,
}
}
// TestP2PConfig returns a configuration for testing the peer-to-peer layer
func TestP2PConfig() *P2PConfig {
conf := DefaultP2PConfig()
conf.ListenAddress = "tcp://0.0.0.0:36656"
conf.SkipUPNP = true
return conf
}
// AddrBookFile returns the full path to the address bool
func (p *P2PConfig) AddrBookFile() string {
return rootify(p.AddrBook, p.RootDir)
}
//-----------------------------------------------------------------------------
// MempoolConfig
// MempoolConfig defines the configuration options for the Tendermint mempool
type MempoolConfig struct {
RootDir string `mapstructure:"home"`
Recheck bool `mapstructure:"recheck"`
RecheckEmpty bool `mapstructure:"recheck_empty"`
Broadcast bool `mapstructure:"broadcast"`
WalPath string `mapstructure:"wal_dir"`
}
// DefaultMempoolConfig returns a default configuration for the Tendermint mempool
func DefaultMempoolConfig() *MempoolConfig {
return &MempoolConfig{
Recheck: true,
RecheckEmpty: true,
Broadcast: true,
WalPath: "data/mempool.wal",
}
}
// WalDir returns the full path to the mempool's write-ahead log
func (m *MempoolConfig) WalDir() string {
return rootify(m.WalPath, m.RootDir)
}
//-----------------------------------------------------------------------------
// ConsensusConfig
// ConsensusConfig defines the confuguration for the Tendermint consensus service,
// including timeouts and details about the WAL and the block structure.
type ConsensusConfig struct {
RootDir string `mapstructure:"home"`
WalPath string `mapstructure:"wal_file"`
WalLight bool `mapstructure:"wal_light"`
walFile string // overrides WalPath if set
// All timeouts are in ms
TimeoutPropose int `mapstructure:"timeout_propose"`
TimeoutProposeDelta int `mapstructure:"timeout_propose_delta"`
TimeoutPrevote int `mapstructure:"timeout_prevote"`
TimeoutPrevoteDelta int `mapstructure:"timeout_prevote_delta"`
TimeoutPrecommit int `mapstructure:"timeout_precommit"`
TimeoutPrecommitDelta int `mapstructure:"timeout_precommit_delta"`
TimeoutCommit int `mapstructure:"timeout_commit"`
// Make progress as soon as we have all the precommits (as if TimeoutCommit = 0)
SkipTimeoutCommit bool `mapstructure:"skip_timeout_commit"`
// BlockSize
MaxBlockSizeTxs int `mapstructure:"max_block_size_txs"`
MaxBlockSizeBytes int `mapstructure:"max_block_size_bytes"`
// EmptyBlocks mode and possible interval between empty blocks in seconds
CreateEmptyBlocks bool `mapstructure:"create_empty_blocks"`
CreateEmptyBlocksInterval int `mapstructure:"create_empty_blocks_interval"`
// TODO: This probably shouldn't be exposed but it makes it
// easy to write tests for the wal/replay
BlockPartSize int `mapstructure:"block_part_size"`
// Reactor sleep duration parameters are in ms
PeerGossipSleepDuration int `mapstructure:"peer_gossip_sleep_duration"`
PeerQueryMaj23SleepDuration int `mapstructure:"peer_query_maj23_sleep_duration"`
}
// WaitForTxs returns true if the consensus should wait for transactions before entering the propose step
func (cfg *ConsensusConfig) WaitForTxs() bool {
return !cfg.CreateEmptyBlocks || cfg.CreateEmptyBlocksInterval > 0
}
// EmptyBlocks returns the amount of time to wait before proposing an empty block or starting the propose timer if there are no txs available
func (cfg *ConsensusConfig) EmptyBlocksInterval() time.Duration {
return time.Duration(cfg.CreateEmptyBlocksInterval) * time.Second
}
// Propose returns the amount of time to wait for a proposal
func (cfg *ConsensusConfig) Propose(round int) time.Duration {
return time.Duration(cfg.TimeoutPropose+cfg.TimeoutProposeDelta*round) * time.Millisecond
}
// Prevote returns the amount of time to wait for straggler votes after receiving any +2/3 prevotes
func (cfg *ConsensusConfig) Prevote(round int) time.Duration {
return time.Duration(cfg.TimeoutPrevote+cfg.TimeoutPrevoteDelta*round) * time.Millisecond
}
// Precommit returns the amount of time to wait for straggler votes after receiving any +2/3 precommits
func (cfg *ConsensusConfig) Precommit(round int) time.Duration {
return time.Duration(cfg.TimeoutPrecommit+cfg.TimeoutPrecommitDelta*round) * time.Millisecond
}
// Commit returns the amount of time to wait for straggler votes after receiving +2/3 precommits for a single block (ie. a commit).
func (cfg *ConsensusConfig) Commit(t time.Time) time.Time {
return t.Add(time.Duration(cfg.TimeoutCommit) * time.Millisecond)
}
// PeerGossipSleep returns the amount of time to sleep if there is nothing to send from the ConsensusReactor
func (cfg *ConsensusConfig) PeerGossipSleep() time.Duration {
return time.Duration(cfg.PeerGossipSleepDuration) * time.Millisecond
}
// PeerQueryMaj23Sleep returns the amount of time to sleep after each VoteSetMaj23Message is sent in the ConsensusReactor
func (cfg *ConsensusConfig) PeerQueryMaj23Sleep() time.Duration {
return time.Duration(cfg.PeerQueryMaj23SleepDuration) * time.Millisecond
}
// DefaultConsensusConfig returns a default configuration for the consensus service
func DefaultConsensusConfig() *ConsensusConfig {
return &ConsensusConfig{
WalPath: "data/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,
BlockPartSize: types.DefaultBlockPartSize, // TODO: we shouldnt be importing types
PeerGossipSleepDuration: 100,
PeerQueryMaj23SleepDuration: 2000,
}
}
// TestConsensusConfig returns a configuration for testing the consensus service
func TestConsensusConfig() *ConsensusConfig {
config := DefaultConsensusConfig()
config.TimeoutPropose = 2000
config.TimeoutProposeDelta = 1
config.TimeoutPrevote = 10
config.TimeoutPrevoteDelta = 1
config.TimeoutPrecommit = 10
config.TimeoutPrecommitDelta = 1
config.TimeoutCommit = 10
config.SkipTimeoutCommit = true
return config
}
// WalFile returns the full path to the write-ahead log file
func (c *ConsensusConfig) WalFile() string {
if c.walFile != "" {
return c.walFile
}
return rootify(c.WalPath, c.RootDir)
}
// SetWalFile sets the path to the write-ahead log file
func (c *ConsensusConfig) SetWalFile(walFile string) {
c.walFile = walFile
}
//-----------------------------------------------------------------------------
// Utils
// helper function to make config creation independent of root dir
func rootify(path, root string) string {
if filepath.IsAbs(path) {
return path
}
return filepath.Join(root, path)
}

28
config/config_test.go Normal file
View File

@@ -0,0 +1,28 @@
package config
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestDefaultConfig(t *testing.T) {
assert := assert.New(t)
// set up some defaults
cfg := DefaultConfig()
assert.NotNil(cfg.P2P)
assert.NotNil(cfg.Mempool)
assert.NotNil(cfg.Consensus)
// check the root dir stuff...
cfg.SetRoot("/foo")
cfg.Genesis = "bar"
cfg.DBPath = "/opt/data"
cfg.Mempool.WalPath = "wal/mem/"
assert.Equal("/foo/bar", cfg.GenesisFile())
assert.Equal("/opt/data", cfg.DBDir())
assert.Equal("/foo/wal/mem", cfg.Mempool.WalDir())
}

View File

@@ -1,107 +0,0 @@
package tendermint
import (
"os"
"path"
"strings"
. "github.com/tendermint/go-common"
cfg "github.com/tendermint/go-config"
)
func getTMRoot(rootDir string) string {
if rootDir == "" {
rootDir = os.Getenv("TMROOT")
}
if rootDir == "" {
rootDir = os.Getenv("HOME") + "/.tendermint"
}
return rootDir
}
func initTMRoot(rootDir string) {
rootDir = getTMRoot(rootDir)
EnsureDir(rootDir, 0700)
configFilePath := path.Join(rootDir, "config.toml")
// Write default config file if missing.
if !FileExists(configFilePath) {
// Ask user for moniker
// moniker := cfg.Prompt("Type hostname: ", "anonymous")
MustWriteFile(configFilePath, []byte(defaultConfig("anonymous")), 0644)
}
}
func GetConfig(rootDir string) cfg.Config {
rootDir = getTMRoot(rootDir)
initTMRoot(rootDir)
configFilePath := path.Join(rootDir, "config.toml")
mapConfig, err := cfg.ReadMapConfigFromFile(configFilePath)
if err != nil {
Exit(Fmt("Could not read config: %v", err))
}
// Set defaults or panic
if mapConfig.IsSet("chain_id") {
Exit("Cannot set 'chain_id' via config.toml")
}
if mapConfig.IsSet("revision_file") {
Exit("Cannot set 'revision_file' via config.toml. It must match what's in the Makefile")
}
mapConfig.SetRequired("chain_id") // blows up if you try to use it before setting.
mapConfig.SetDefault("genesis_file", rootDir+"/genesis.json")
mapConfig.SetDefault("proxy_app", "tcp://127.0.0.1:46658")
mapConfig.SetDefault("tmsp", "socket")
mapConfig.SetDefault("moniker", "anonymous")
mapConfig.SetDefault("node_laddr", "tcp://0.0.0.0:46656")
mapConfig.SetDefault("seeds", "")
// mapConfig.SetDefault("seeds", "goldenalchemist.chaintest.net:46656")
mapConfig.SetDefault("fast_sync", true)
mapConfig.SetDefault("skip_upnp", false)
mapConfig.SetDefault("addrbook_file", rootDir+"/addrbook.json")
mapConfig.SetDefault("priv_validator_file", rootDir+"/priv_validator.json")
mapConfig.SetDefault("db_backend", "leveldb")
mapConfig.SetDefault("db_dir", rootDir+"/data")
mapConfig.SetDefault("log_level", "info")
mapConfig.SetDefault("rpc_laddr", "tcp://0.0.0.0:46657")
mapConfig.SetDefault("prof_laddr", "")
mapConfig.SetDefault("revision_file", rootDir+"/revision")
mapConfig.SetDefault("cswal", rootDir+"/data/cswal")
mapConfig.SetDefault("cswal_light", false)
mapConfig.SetDefault("filter_peers", false)
mapConfig.SetDefault("block_size", 10000)
mapConfig.SetDefault("disable_data_hash", false)
mapConfig.SetDefault("timeout_propose", 3000)
mapConfig.SetDefault("timeout_propose_delta", 500)
mapConfig.SetDefault("timeout_prevote", 1000)
mapConfig.SetDefault("timeout_prevote_delta", 500)
mapConfig.SetDefault("timeout_precommit", 1000)
mapConfig.SetDefault("timeout_precommit_delta", 500)
mapConfig.SetDefault("timeout_commit", 1000)
mapConfig.SetDefault("mempool_recheck", true)
mapConfig.SetDefault("mempool_recheck_empty", true)
mapConfig.SetDefault("mempool_broadcast", true)
return mapConfig
}
var defaultConfigTmpl = `# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml
proxy_app = "tcp://127.0.0.1:46658"
moniker = "__MONIKER__"
node_laddr = "tcp://0.0.0.0:46656"
seeds = ""
fast_sync = true
db_backend = "leveldb"
log_level = "notice"
rpc_laddr = "tcp://0.0.0.0:46657"
`
func defaultConfig(moniker string) (defaultConfig string) {
defaultConfig = strings.Replace(defaultConfigTmpl, "__MONIKER__", moniker, -1)
return
}

View File

@@ -1,22 +0,0 @@
// If you wanted to use logrotate, I suppose this might be the config you want.
// Instead, I'll just write our own, that way we don't need sudo to install.
$HOME/.tendermint/logs/tendermint.log {
missingok
notifempty
rotate 12
daily
size 10M
compress
delaycompress
}
$HOME/.barak/logs/barak.log {
missingok
notifempty
rotate 12
weekly
size 10M
compress
delaycompress
}

View File

@@ -1,151 +0,0 @@
// Import this in all *_test.go files to initialize ~/.tendermint_test.
package tendermint_test
import (
"os"
"path"
"strings"
. "github.com/tendermint/go-common"
cfg "github.com/tendermint/go-config"
)
func init() {
// Creates ~/.tendermint_test
EnsureDir(os.Getenv("HOME")+"/.tendermint_test", 0700)
}
func initTMRoot(rootDir string) {
// Remove ~/.tendermint_test_bak
if FileExists(rootDir + "_bak") {
err := os.RemoveAll(rootDir + "_bak")
if err != nil {
PanicSanity(err.Error())
}
}
// Move ~/.tendermint_test to ~/.tendermint_test_bak
if FileExists(rootDir) {
err := os.Rename(rootDir, rootDir+"_bak")
if err != nil {
PanicSanity(err.Error())
}
}
// Create new dir
EnsureDir(rootDir, 0700)
configFilePath := path.Join(rootDir, "config.toml")
genesisFilePath := path.Join(rootDir, "genesis.json")
privFilePath := path.Join(rootDir, "priv_validator.json")
// Write default config file if missing.
if !FileExists(configFilePath) {
// Ask user for moniker
// moniker := cfg.Prompt("Type hostname: ", "anonymous")
MustWriteFile(configFilePath, []byte(defaultConfig("anonymous")), 0644)
}
if !FileExists(genesisFilePath) {
MustWriteFile(genesisFilePath, []byte(defaultGenesis), 0644)
}
// we always overwrite the priv val
MustWriteFile(privFilePath, []byte(defaultPrivValidator), 0644)
}
func ResetConfig(localPath string) cfg.Config {
rootDir := os.Getenv("HOME") + "/.tendermint_test/" + localPath
initTMRoot(rootDir)
configFilePath := path.Join(rootDir, "config.toml")
mapConfig, err := cfg.ReadMapConfigFromFile(configFilePath)
if err != nil {
Exit(Fmt("Could not read config: %v", err))
}
// Set defaults or panic
if mapConfig.IsSet("chain_id") {
Exit("Cannot set 'chain_id' via config.toml")
}
mapConfig.SetDefault("chain_id", "tendermint_test")
mapConfig.SetDefault("genesis_file", rootDir+"/genesis.json")
mapConfig.SetDefault("proxy_app", "dummy")
mapConfig.SetDefault("tmsp", "socket")
mapConfig.SetDefault("moniker", "anonymous")
mapConfig.SetDefault("node_laddr", "tcp://0.0.0.0:36656")
mapConfig.SetDefault("fast_sync", false)
mapConfig.SetDefault("skip_upnp", true)
mapConfig.SetDefault("addrbook_file", rootDir+"/addrbook.json")
mapConfig.SetDefault("priv_validator_file", rootDir+"/priv_validator.json")
mapConfig.SetDefault("db_backend", "memdb")
mapConfig.SetDefault("db_dir", rootDir+"/data")
mapConfig.SetDefault("log_level", "debug")
mapConfig.SetDefault("rpc_laddr", "tcp://0.0.0.0:36657")
mapConfig.SetDefault("prof_laddr", "")
mapConfig.SetDefault("revision_file", rootDir+"/revision")
mapConfig.SetDefault("cswal", rootDir+"/data/cswal")
mapConfig.SetDefault("cswal_light", false)
mapConfig.SetDefault("filter_peers", false)
mapConfig.SetDefault("block_size", 10000)
mapConfig.SetDefault("disable_data_hash", false)
mapConfig.SetDefault("timeout_propose", 2000)
mapConfig.SetDefault("timeout_propose_delta", 500)
mapConfig.SetDefault("timeout_prevote", 1000)
mapConfig.SetDefault("timeout_prevote_delta", 500)
mapConfig.SetDefault("timeout_precommit", 1000)
mapConfig.SetDefault("timeout_precommit_delta", 500)
mapConfig.SetDefault("timeout_commit", 100)
mapConfig.SetDefault("mempool_recheck", true)
mapConfig.SetDefault("mempool_recheck_empty", true)
mapConfig.SetDefault("mempool_broadcast", true)
return mapConfig
}
var defaultConfigTmpl = `# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml
proxy_app = "dummy"
moniker = "__MONIKER__"
node_laddr = "tcp://0.0.0.0:36656"
seeds = ""
fast_sync = false
db_backend = "memdb"
log_level = "debug"
rpc_laddr = "tcp://0.0.0.0:36657"
`
func defaultConfig(moniker string) (defaultConfig string) {
defaultConfig = strings.Replace(defaultConfigTmpl, "__MONIKER__", moniker, -1)
return
}
var defaultGenesis = `{
"genesis_time": "0001-01-01T00:00:00.000Z",
"chain_id": "tendermint_test",
"validators": [
{
"pub_key": [
1,
"3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8"
],
"amount": 10,
"name": ""
}
],
"app_hash": ""
}`
var defaultPrivValidator = `{
"address": "D028C9981F7A87F3093672BF0D5B0E2A1B3ED456",
"pub_key": [
1,
"3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8"
],
"priv_key": [
1,
"27F82582AEFAE7AB151CFB01C48BB6C1A0DA78F9BDDA979A9F70A84D074EB07D3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8"
],
"last_height": 0,
"last_round": 0,
"last_step": 0
}`

142
config/toml.go Normal file
View File

@@ -0,0 +1,142 @@
package config
import (
"os"
"path"
"path/filepath"
"strings"
cmn "github.com/tendermint/tmlibs/common"
)
/****** these are for production settings ***********/
func EnsureRoot(rootDir string) {
cmn.EnsureDir(rootDir, 0700)
cmn.EnsureDir(rootDir+"/data", 0700)
configFilePath := path.Join(rootDir, "config.toml")
// Write default config file if missing.
if !cmn.FileExists(configFilePath) {
// Ask user for moniker
// moniker := cfg.Prompt("Type hostname: ", "anonymous")
cmn.MustWriteFile(configFilePath, []byte(defaultConfig("anonymous")), 0644)
}
}
var defaultConfigTmpl = `# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml
proxy_app = "tcp://127.0.0.1:46658"
moniker = "__MONIKER__"
fast_sync = true
db_backend = "leveldb"
log_level = "state:info,*:error"
[rpc]
laddr = "tcp://0.0.0.0:46657"
[p2p]
laddr = "tcp://0.0.0.0:46656"
seeds = ""
`
func defaultConfig(moniker string) string {
return strings.Replace(defaultConfigTmpl, "__MONIKER__", moniker, -1)
}
/****** these are for test settings ***********/
func ResetTestRoot(testName string) *Config {
rootDir := os.ExpandEnv("$HOME/.tendermint_test")
rootDir = filepath.Join(rootDir, testName)
// Remove ~/.tendermint_test_bak
if cmn.FileExists(rootDir + "_bak") {
err := os.RemoveAll(rootDir + "_bak")
if err != nil {
cmn.PanicSanity(err.Error())
}
}
// Move ~/.tendermint_test to ~/.tendermint_test_bak
if cmn.FileExists(rootDir) {
err := os.Rename(rootDir, rootDir+"_bak")
if err != nil {
cmn.PanicSanity(err.Error())
}
}
// Create new dir
cmn.EnsureDir(rootDir, 0700)
cmn.EnsureDir(rootDir+"/data", 0700)
configFilePath := path.Join(rootDir, "config.toml")
genesisFilePath := path.Join(rootDir, "genesis.json")
privFilePath := path.Join(rootDir, "priv_validator.json")
// Write default config file if missing.
if !cmn.FileExists(configFilePath) {
// Ask user for moniker
cmn.MustWriteFile(configFilePath, []byte(testConfig("anonymous")), 0644)
}
if !cmn.FileExists(genesisFilePath) {
cmn.MustWriteFile(genesisFilePath, []byte(testGenesis), 0644)
}
// we always overwrite the priv val
cmn.MustWriteFile(privFilePath, []byte(testPrivValidator), 0644)
config := TestConfig().SetRoot(rootDir)
return config
}
var testConfigTmpl = `# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml
proxy_app = "dummy"
moniker = "__MONIKER__"
fast_sync = false
db_backend = "memdb"
log_level = "info"
[rpc]
laddr = "tcp://0.0.0.0:36657"
[p2p]
laddr = "tcp://0.0.0.0:36656"
seeds = ""
`
func testConfig(moniker string) (testConfig string) {
testConfig = strings.Replace(testConfigTmpl, "__MONIKER__", moniker, -1)
return
}
var testGenesis = `{
"genesis_time": "0001-01-01T00:00:00.000Z",
"chain_id": "tendermint_test",
"validators": [
{
"pub_key": {
"type": "ed25519",
"data":"3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8"
},
"amount": 10,
"name": ""
}
],
"app_hash": ""
}`
var testPrivValidator = `{
"address": "D028C9981F7A87F3093672BF0D5B0E2A1B3ED456",
"pub_key": {
"type": "ed25519",
"data": "3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8"
},
"priv_key": {
"type": "ed25519",
"data": "27F82582AEFAE7AB151CFB01C48BB6C1A0DA78F9BDDA979A9F70A84D074EB07D3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8"
},
"last_height": 0,
"last_round": 0,
"last_step": 0
}`

57
config/toml_test.go Normal file
View File

@@ -0,0 +1,57 @@
package config
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func ensureFiles(t *testing.T, rootDir string, files ...string) {
for _, f := range files {
p := rootify(rootDir, f)
_, err := os.Stat(p)
assert.Nil(t, err, p)
}
}
func TestEnsureRoot(t *testing.T) {
assert, require := assert.New(t), require.New(t)
// setup temp dir for test
tmpDir, err := ioutil.TempDir("", "config-test")
require.Nil(err)
defer os.RemoveAll(tmpDir)
// create root dir
EnsureRoot(tmpDir)
// make sure config is set properly
data, err := ioutil.ReadFile(filepath.Join(tmpDir, "config.toml"))
require.Nil(err)
assert.Equal([]byte(defaultConfig("anonymous")), data)
ensureFiles(t, tmpDir, "data")
}
func TestEnsureTestRoot(t *testing.T) {
assert, require := assert.New(t), require.New(t)
testName := "ensureTestRoot"
// create root dir
cfg := ResetTestRoot(testName)
rootDir := cfg.RootDir
// make sure config is set properly
data, err := ioutil.ReadFile(filepath.Join(rootDir, "config.toml"))
require.Nil(err)
assert.Equal([]byte(testConfig("anonymous")), data)
// TODO: make sure the cfg returned and testconfig are the same!
ensureFiles(t, rootDir, "data", "genesis.json", "priv_validator.json")
}

307
consensus/byzantine_test.go Normal file
View File

@@ -0,0 +1,307 @@
package consensus
import (
"sync"
"testing"
"time"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/types"
. "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/events"
)
func init() {
config = ResetConfig("consensus_byzantine_test")
}
//----------------------------------------------
// byzantine failures
// 4 validators. 1 is byzantine. The other three are partitioned into A (1 val) and B (2 vals).
// byzantine validator sends conflicting proposals into A and B,
// and prevotes/precommits on both of them.
// B sees a commit, A doesn't.
// Byzantine validator refuses to prevote.
// Heal partition and ensure A sees the commit
func TestByzantine(t *testing.T) {
N := 4
logger := consensusLogger()
css := randConsensusNet(N, "consensus_byzantine_test", newMockTickerFunc(false), newCounter)
// give the byzantine validator a normal ticker
css[0].SetTimeoutTicker(NewTimeoutTicker())
switches := make([]*p2p.Switch, N)
p2pLogger := logger.With("module", "p2p")
for i := 0; i < N; i++ {
switches[i] = p2p.NewSwitch(config.P2P)
switches[i].SetLogger(p2pLogger.With("validator", i))
}
reactors := make([]p2p.Reactor, N)
defer func() {
for _, r := range reactors {
if rr, ok := r.(*ByzantineReactor); ok {
rr.reactor.Switch.Stop()
} else {
r.(*ConsensusReactor).Switch.Stop()
}
}
}()
eventChans := make([]chan interface{}, N)
eventLogger := logger.With("module", "events")
for i := 0; i < N; i++ {
if i == 0 {
css[i].privValidator = NewByzantinePrivValidator(css[i].privValidator.(*types.PrivValidator))
// make byzantine
css[i].decideProposal = func(j int) func(int, int) {
return func(height, round int) {
byzantineDecideProposalFunc(t, height, round, css[j], switches[j])
}
}(i)
css[i].doPrevote = func(height, round int) {}
}
eventSwitch := events.NewEventSwitch()
eventSwitch.SetLogger(eventLogger.With("validator", i))
_, err := eventSwitch.Start()
if err != nil {
t.Fatalf("Failed to start switch: %v", err)
}
eventChans[i] = subscribeToEvent(eventSwitch, "tester", types.EventStringNewBlock(), 1)
conR := NewConsensusReactor(css[i], true) // so we dont start the consensus states
conR.SetLogger(logger.With("validator", i))
conR.SetEventSwitch(eventSwitch)
var conRI p2p.Reactor
conRI = conR
if i == 0 {
conRI = NewByzantineReactor(conR)
}
reactors[i] = conRI
}
p2p.MakeConnectedSwitches(config.P2P, N, func(i int, s *p2p.Switch) *p2p.Switch {
// ignore new switch s, we already made ours
switches[i].AddReactor("CONSENSUS", reactors[i])
return switches[i]
}, func(sws []*p2p.Switch, i, j int) {
// the network starts partitioned with globally active adversary
if i != 0 {
return
}
p2p.Connect2Switches(sws, i, j)
})
// start the state machines
byzR := reactors[0].(*ByzantineReactor)
s := byzR.reactor.conS.GetState()
byzR.reactor.SwitchToConsensus(s)
for i := 1; i < N; i++ {
cr := reactors[i].(*ConsensusReactor)
cr.SwitchToConsensus(cr.conS.GetState())
}
// byz proposer sends one block to peers[0]
// and the other block to peers[1] and peers[2].
// note peers and switches order don't match.
peers := switches[0].Peers().List()
ind0 := getSwitchIndex(switches, peers[0])
ind1 := getSwitchIndex(switches, peers[1])
ind2 := getSwitchIndex(switches, peers[2])
// connect the 2 peers in the larger partition
p2p.Connect2Switches(switches, ind1, ind2)
// wait for someone in the big partition to make a block
<-eventChans[ind2]
t.Log("A block has been committed. Healing partition")
// connect the partitions
p2p.Connect2Switches(switches, ind0, ind1)
p2p.Connect2Switches(switches, ind0, ind2)
// wait till everyone makes the first new block
// (one of them already has)
wg := new(sync.WaitGroup)
wg.Add(2)
for i := 1; i < N-1; i++ {
go func(j int) {
<-eventChans[j]
wg.Done()
}(i)
}
done := make(chan struct{})
go func() {
wg.Wait()
close(done)
}()
tick := time.NewTicker(time.Second * 10)
select {
case <-done:
case <-tick.C:
for i, reactor := range reactors {
t.Log(Fmt("Consensus Reactor %v", i))
t.Log(Fmt("%v", reactor))
}
t.Fatalf("Timed out waiting for all validators to commit first block")
}
}
//-------------------------------
// byzantine consensus functions
func byzantineDecideProposalFunc(t *testing.T, height, round int, cs *ConsensusState, sw *p2p.Switch) {
// byzantine user should create two proposals and try to split the vote.
// Avoid sending on internalMsgQueue and running consensus state.
// Create a new proposal block from state/txs from the mempool.
block1, blockParts1 := cs.createProposalBlock()
polRound, polBlockID := cs.Votes.POLInfo()
proposal1 := types.NewProposal(height, round, blockParts1.Header(), polRound, polBlockID)
cs.privValidator.SignProposal(cs.state.ChainID, proposal1) // byzantine doesnt err
// Create a new proposal block from state/txs from the mempool.
block2, blockParts2 := cs.createProposalBlock()
polRound, polBlockID = cs.Votes.POLInfo()
proposal2 := types.NewProposal(height, round, blockParts2.Header(), polRound, polBlockID)
cs.privValidator.SignProposal(cs.state.ChainID, proposal2) // byzantine doesnt err
block1Hash := block1.Hash()
block2Hash := block2.Hash()
// broadcast conflicting proposals/block parts to peers
peers := sw.Peers().List()
t.Logf("Byzantine: broadcasting conflicting proposals to %d peers", len(peers))
for i, peer := range peers {
if i < len(peers)/2 {
go sendProposalAndParts(height, round, cs, peer, proposal1, block1Hash, blockParts1)
} else {
go sendProposalAndParts(height, round, cs, peer, proposal2, block2Hash, blockParts2)
}
}
}
func sendProposalAndParts(height, round int, cs *ConsensusState, peer *p2p.Peer, proposal *types.Proposal, blockHash []byte, parts *types.PartSet) {
// proposal
msg := &ProposalMessage{Proposal: proposal}
peer.Send(DataChannel, struct{ ConsensusMessage }{msg})
// parts
for i := 0; i < parts.Total(); i++ {
part := parts.GetPart(i)
msg := &BlockPartMessage{
Height: height, // This tells peer that this part applies to us.
Round: round, // This tells peer that this part applies to us.
Part: part,
}
peer.Send(DataChannel, struct{ ConsensusMessage }{msg})
}
// votes
cs.mtx.Lock()
prevote, _ := cs.signVote(types.VoteTypePrevote, blockHash, parts.Header())
precommit, _ := cs.signVote(types.VoteTypePrecommit, blockHash, parts.Header())
cs.mtx.Unlock()
peer.Send(VoteChannel, struct{ ConsensusMessage }{&VoteMessage{prevote}})
peer.Send(VoteChannel, struct{ ConsensusMessage }{&VoteMessage{precommit}})
}
//----------------------------------------
// byzantine consensus reactor
type ByzantineReactor struct {
Service
reactor *ConsensusReactor
}
func NewByzantineReactor(conR *ConsensusReactor) *ByzantineReactor {
return &ByzantineReactor{
Service: conR,
reactor: conR,
}
}
func (br *ByzantineReactor) SetSwitch(s *p2p.Switch) { br.reactor.SetSwitch(s) }
func (br *ByzantineReactor) GetChannels() []*p2p.ChannelDescriptor { return br.reactor.GetChannels() }
func (br *ByzantineReactor) AddPeer(peer *p2p.Peer) {
if !br.reactor.IsRunning() {
return
}
// Create peerState for peer
peerState := NewPeerState(peer)
peer.Data.Set(types.PeerStateKey, peerState)
// Send our state to peer.
// If we're fast_syncing, broadcast a RoundStepMessage later upon SwitchToConsensus().
if !br.reactor.fastSync {
br.reactor.sendNewRoundStepMessages(peer)
}
}
func (br *ByzantineReactor) RemovePeer(peer *p2p.Peer, reason interface{}) {
br.reactor.RemovePeer(peer, reason)
}
func (br *ByzantineReactor) Receive(chID byte, peer *p2p.Peer, msgBytes []byte) {
br.reactor.Receive(chID, peer, msgBytes)
}
//----------------------------------------
// byzantine privValidator
type ByzantinePrivValidator struct {
Address []byte `json:"address"`
types.Signer `json:"-"`
mtx sync.Mutex
}
// Return a priv validator that will sign anything
func NewByzantinePrivValidator(pv *types.PrivValidator) *ByzantinePrivValidator {
return &ByzantinePrivValidator{
Address: pv.Address,
Signer: pv.Signer,
}
}
func (privVal *ByzantinePrivValidator) GetAddress() []byte {
return privVal.Address
}
func (privVal *ByzantinePrivValidator) SignVote(chainID string, vote *types.Vote) error {
privVal.mtx.Lock()
defer privVal.mtx.Unlock()
// Sign
vote.Signature = privVal.Sign(types.SignBytes(chainID, vote))
return nil
}
func (privVal *ByzantinePrivValidator) SignProposal(chainID string, proposal *types.Proposal) error {
privVal.mtx.Lock()
defer privVal.mtx.Unlock()
// Sign
proposal.Signature = privVal.Sign(types.SignBytes(chainID, proposal))
return nil
}
func (privVal *ByzantinePrivValidator) SignHeartbeat(chainID string, heartbeat *types.Heartbeat) error {
privVal.mtx.Lock()
defer privVal.mtx.Unlock()
// Sign
heartbeat.Signature = privVal.Sign(types.SignBytes(chainID, heartbeat))
return nil
}
func (privVal *ByzantinePrivValidator) String() string {
return Fmt("PrivValidator{%X}", privVal.Address)
}

View File

@@ -1,15 +1,35 @@
package consensus
import (
"github.com/tendermint/go-events"
"github.com/tendermint/tendermint/types"
)
// NOTE: this is blocking
func subscribeToEvent(evsw *events.EventSwitch, receiver, eventID string, chanCap int) chan interface{} {
// listen for new round
// XXX: WARNING: these functions can halt the consensus as firing events is synchronous.
// Make sure to read off the channels, and in the case of subscribeToEventRespond, to write back on it
// NOTE: if chanCap=0, this blocks on the event being consumed
func subscribeToEvent(evsw types.EventSwitch, receiver, eventID string, chanCap int) chan interface{} {
// listen for event
ch := make(chan interface{}, chanCap)
evsw.AddListenerForEvent(receiver, eventID, func(data events.EventData) {
types.AddListenerForEvent(evsw, receiver, eventID, func(data types.TMEventData) {
ch <- data
})
return ch
}
// NOTE: this blocks on receiving a response after the event is consumed
func subscribeToEventRespond(evsw types.EventSwitch, receiver, eventID string) chan interface{} {
// listen for event
ch := make(chan interface{})
types.AddListenerForEvent(evsw, receiver, eventID, func(data types.TMEventData) {
ch <- data
<-ch
})
return ch
}
func discardFromChan(ch chan interface{}, n int) {
for i := 0; i < n; i++ {
<-ch
}
}

View File

@@ -3,52 +3,79 @@ package consensus
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"path"
"sort"
"sync"
"testing"
"time"
cfg "github.com/tendermint/go-config"
dbm "github.com/tendermint/go-db"
"github.com/tendermint/go-events"
abcicli "github.com/tendermint/abci/client"
abci "github.com/tendermint/abci/types"
bc "github.com/tendermint/tendermint/blockchain"
cfg "github.com/tendermint/tendermint/config"
mempl "github.com/tendermint/tendermint/mempool"
"github.com/tendermint/tendermint/p2p"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
tmspcli "github.com/tendermint/tmsp/client"
tmsp "github.com/tendermint/tmsp/types"
. "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
"github.com/tendermint/tmsp/example/counter"
"github.com/tendermint/abci/example/counter"
"github.com/tendermint/abci/example/dummy"
"github.com/go-kit/kit/log/term"
)
var config cfg.Config // NOTE: must be reset for each _test.go file
var ensureTimeout = time.Duration(2)
// genesis, chain_id, priv_val
var config *cfg.Config // NOTE: must be reset for each _test.go file
var ensureTimeout = time.Second * 2
func ensureDir(dir string, mode os.FileMode) {
if err := EnsureDir(dir, mode); err != nil {
panic(err)
}
}
func ResetConfig(name string) *cfg.Config {
return cfg.ResetTestRoot(name)
}
//-------------------------------------------------------------------------------
// validator stub (a dummy consensus peer we control)
type validatorStub struct {
Index int // Validator index. NOTE: we don't assume validator set changes.
Height int
Round int
*types.PrivValidator
}
func NewValidatorStub(privValidator *types.PrivValidator) *validatorStub {
var testMinPower = 10
func NewValidatorStub(privValidator *types.PrivValidator, valIndex int) *validatorStub {
return &validatorStub{
Index: valIndex,
PrivValidator: privValidator,
}
}
func (vs *validatorStub) signVote(voteType byte, hash []byte, header types.PartSetHeader) (*types.Vote, error) {
vote := &types.Vote{
ValidatorIndex: vs.Index,
ValidatorAddress: vs.PrivValidator.Address,
Height: vs.Height,
Round: vs.Round,
Type: voteType,
BlockHash: hash,
BlockPartsHeader: header,
BlockID: types.BlockID{hash, header},
}
err := vs.PrivValidator.SignVote(config.GetString("chain_id"), vote)
err := vs.PrivValidator.SignVote(config.ChainID, vote)
return vote, err
}
// convenienve function for testing
// Sign vote for type/hash/header
func signVote(vs *validatorStub, voteType byte, hash []byte, header types.PartSetHeader) *types.Vote {
v, err := vs.signVote(voteType, hash, header)
if err != nil {
@@ -57,102 +84,7 @@ func signVote(vs *validatorStub, voteType byte, hash []byte, header types.PartSe
return v
}
// create proposal block from cs1 but sign it with vs
func decideProposal(cs1 *ConsensusState, cs2 *validatorStub, height, round int) (proposal *types.Proposal, block *types.Block) {
block, blockParts := cs1.createProposalBlock()
if block == nil { // on error
panic("error creating proposal block")
}
// Make proposal
proposal = types.NewProposal(height, round, blockParts.Header(), cs1.Votes.POLRound())
if err := cs2.SignProposal(config.GetString("chain_id"), proposal); err != nil {
panic(err)
}
return
}
//-------------------------------------------------------------------------------
// utils
/*
func nilRound(t *testing.T, cs1 *ConsensusState, vss ...*validatorStub) {
cs1.mtx.Lock()
height, round := cs1.Height, cs1.Round
cs1.mtx.Unlock()
waitFor(t, cs1, height, round, RoundStepPrevote)
signAddVoteToFromMany(types.VoteTypePrevote, cs1, nil, cs1.ProposalBlockParts.Header(), vss...)
waitFor(t, cs1, height, round, RoundStepPrecommit)
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, cs1.ProposalBlockParts.Header(), vss...)
waitFor(t, cs1, height, round+1, RoundStepNewRound)
}
*/
// NOTE: this switches the propser as far as `perspectiveOf` is concerned,
// but for simplicity we return a block it generated.
func changeProposer(t *testing.T, perspectiveOf *ConsensusState, newProposer *validatorStub) *types.Block {
_, v1 := perspectiveOf.Validators.GetByAddress(perspectiveOf.privValidator.Address)
v1.Accum, v1.VotingPower = 0, 0
if updated := perspectiveOf.Validators.Update(v1); !updated {
panic("failed to update validator")
}
_, v2 := perspectiveOf.Validators.GetByAddress(newProposer.Address)
v2.Accum, v2.VotingPower = 100, 100
if updated := perspectiveOf.Validators.Update(v2); !updated {
panic("failed to update validator")
}
// make the proposal
propBlock, _ := perspectiveOf.createProposalBlock()
if propBlock == nil {
panic("Failed to create proposal block with cs2")
}
return propBlock
}
func fixVotingPower(t *testing.T, cs1 *ConsensusState, addr2 []byte) {
_, v1 := cs1.Validators.GetByAddress(cs1.privValidator.Address)
_, v2 := cs1.Validators.GetByAddress(addr2)
v1.Accum, v1.VotingPower = v2.Accum, v2.VotingPower
if updated := cs1.Validators.Update(v1); !updated {
panic("failed to update validator")
}
}
func addVoteToFromMany(to *ConsensusState, votes []*types.Vote, froms ...*validatorStub) {
if len(votes) != len(froms) {
panic("len(votes) and len(froms) must match")
}
for i, from := range froms {
addVoteToFrom(to, from, votes[i])
}
}
func addVoteToFrom(to *ConsensusState, from *validatorStub, vote *types.Vote) {
to.mtx.Lock() // NOTE: wont need this when the vote comes with the index!
valIndex, _ := to.Validators.GetByAddress(from.PrivValidator.Address)
to.mtx.Unlock()
to.peerMsgQueue <- msgInfo{Msg: &VoteMessage{valIndex, vote}}
// added, err := to.TryAddVote(valIndex, vote, "")
/*
if _, ok := err.(*types.ErrVoteConflictingSignature); ok {
// let it fly
} else if !added {
fmt.Println("to, from, vote:", to.Height, from.Height, vote.Height)
panic(fmt.Sprintln("Failed to add vote. Err:", err))
} else if err != nil {
panic(fmt.Sprintln("Failed to add vote:", err))
}*/
}
func signVoteMany(voteType byte, hash []byte, header types.PartSetHeader, vss ...*validatorStub) []*types.Vote {
func signVotes(voteType byte, hash []byte, header types.PartSetHeader, vss ...*validatorStub) []*types.Vote {
votes := make([]*types.Vote, len(vss))
for i, vs := range vss {
votes[i] = signVote(vs, voteType, hash, header)
@@ -160,79 +92,6 @@ func signVoteMany(voteType byte, hash []byte, header types.PartSetHeader, vss ..
return votes
}
// add vote to one cs from another
// if voteCh is not nil, read all votes
func signAddVoteToFromMany(voteType byte, to *ConsensusState, hash []byte, header types.PartSetHeader, voteCh chan interface{}, froms ...*validatorStub) {
var wg chan struct{} // when done reading all votes
if voteCh != nil {
wg = readVotes(voteCh, len(froms))
}
for _, from := range froms {
vote := signVote(from, voteType, hash, header)
addVoteToFrom(to, from, vote)
}
if voteCh != nil {
<-wg
}
}
func signAddVoteToFrom(voteType byte, to *ConsensusState, from *validatorStub, hash []byte, header types.PartSetHeader, voteCh chan interface{}) *types.Vote {
var wg chan struct{} // when done reading all votes
if voteCh != nil {
wg = readVotes(voteCh, 1)
}
vote := signVote(from, voteType, hash, header)
addVoteToFrom(to, from, vote)
if voteCh != nil {
<-wg
}
return vote
}
func ensureNoNewStep(stepCh chan interface{}) {
timeout := time.NewTicker(ensureTimeout * time.Second)
select {
case <-timeout.C:
break
case <-stepCh:
panic("We should be stuck waiting for more votes, not moving to the next step")
}
}
/*
func ensureNoNewStep(t *testing.T, cs *ConsensusState) {
timeout := time.NewTicker(ensureTimeout * time.Second)
select {
case <-timeout.C:
break
case <-cs.NewStepCh():
panic("We should be stuck waiting for more votes, not moving to the next step")
}
}
func ensureNewStep(t *testing.T, cs *ConsensusState) *RoundState {
timeout := time.NewTicker(ensureTimeout * time.Second)
select {
case <-timeout.C:
panic("We should have gone to the next step, not be stuck waiting")
case rs := <-cs.NewStepCh():
return rs
}
}
func waitFor(t *testing.T, cs *ConsensusState, height int, round int, step RoundStepType) {
for {
rs := ensureNewStep(t, cs)
if CompareHRS(rs.Height, rs.Round, rs.Step, height, round, step) < 0 {
continue
} else {
break
}
}
}
*/
func incrementHeight(vss ...*validatorStub) {
for _, vs := range vss {
vs.Height += 1
@@ -245,6 +104,41 @@ func incrementRound(vss ...*validatorStub) {
}
}
//-------------------------------------------------------------------------------
// Functions for transitioning the consensus state
func startTestRound(cs *ConsensusState, height, round int) {
cs.enterNewRound(height, round)
cs.startRoutines(0)
}
// Create proposal block from cs1 but sign it with vs
func decideProposal(cs1 *ConsensusState, vs *validatorStub, height, round int) (proposal *types.Proposal, block *types.Block) {
block, blockParts := cs1.createProposalBlock()
if block == nil { // on error
panic("error creating proposal block")
}
// Make proposal
polRound, polBlockID := cs1.Votes.POLInfo()
proposal = types.NewProposal(height, round, blockParts.Header(), polRound, polBlockID)
if err := vs.SignProposal(config.ChainID, proposal); err != nil {
panic(err)
}
return
}
func addVotes(to *ConsensusState, votes ...*types.Vote) {
for _, vote := range votes {
to.peerMsgQueue <- msgInfo{Msg: &VoteMessage{vote}}
}
}
func signAddVotes(to *ConsensusState, voteType byte, hash []byte, header types.PartSetHeader, vss ...*validatorStub) {
votes := signVotes(voteType, hash, header, vss...)
addVotes(to, votes...)
}
func validatePrevote(t *testing.T, cs *ConsensusState, round int, privVal *validatorStub, blockHash []byte) {
prevotes := cs.Votes.Prevotes(round)
var vote *types.Vote
@@ -252,12 +146,12 @@ func validatePrevote(t *testing.T, cs *ConsensusState, round int, privVal *valid
panic("Failed to find prevote from validator")
}
if blockHash == nil {
if vote.BlockHash != nil {
panic(fmt.Sprintf("Expected prevote to be for nil, got %X", vote.BlockHash))
if vote.BlockID.Hash != nil {
panic(fmt.Sprintf("Expected prevote to be for nil, got %X", vote.BlockID.Hash))
}
} else {
if !bytes.Equal(vote.BlockHash, blockHash) {
panic(fmt.Sprintf("Expected prevote to be for %X, got %X", blockHash, vote.BlockHash))
if !bytes.Equal(vote.BlockID.Hash, blockHash) {
panic(fmt.Sprintf("Expected prevote to be for %X, got %X", blockHash, vote.BlockID.Hash))
}
}
}
@@ -268,8 +162,8 @@ func validateLastPrecommit(t *testing.T, cs *ConsensusState, privVal *validatorS
if vote = votes.GetByAddress(privVal.Address); vote == nil {
panic("Failed to find precommit from validator")
}
if !bytes.Equal(vote.BlockHash, blockHash) {
panic(fmt.Sprintf("Expected precommit to be for %X, got %X", blockHash, vote.BlockHash))
if !bytes.Equal(vote.BlockID.Hash, blockHash) {
panic(fmt.Sprintf("Expected precommit to be for %X, got %X", blockHash, vote.BlockID.Hash))
}
}
@@ -281,11 +175,11 @@ func validatePrecommit(t *testing.T, cs *ConsensusState, thisRound, lockRound in
}
if votedBlockHash == nil {
if vote.BlockHash != nil {
if vote.BlockID.Hash != nil {
panic("Expected precommit to be for nil")
}
} else {
if !bytes.Equal(vote.BlockHash, votedBlockHash) {
if !bytes.Equal(vote.BlockID.Hash, votedBlockHash) {
panic("Expected precommit to be for proposal block")
}
}
@@ -311,39 +205,77 @@ func validatePrevoteAndPrecommit(t *testing.T, cs *ConsensusState, thisRound, lo
cs.mtx.Unlock()
}
func fixedConsensusState() *ConsensusState {
stateDB := dbm.NewMemDB()
state := sm.MakeGenesisStateFromFile(stateDB, config.GetString("genesis_file"))
privValidatorFile := config.GetString("priv_validator_file")
privValidator := types.LoadOrGenPrivValidator(privValidatorFile)
privValidator.Reset()
cs := newConsensusState(state, privValidator, counter.NewCounterApplication(true))
return cs
// genesis
func subscribeToVoter(cs *ConsensusState, addr []byte) chan interface{} {
voteCh0 := subscribeToEvent(cs.evsw, "tester", types.EventStringVote(), 1)
voteCh := make(chan interface{})
go func() {
for {
v := <-voteCh0
vote := v.(types.TMEventData).Unwrap().(types.EventDataVote)
// we only fire for our own votes
if bytes.Equal(addr, vote.Vote.ValidatorAddress) {
voteCh <- v
}
}
}()
return voteCh
}
func newConsensusState(state *sm.State, pv *types.PrivValidator, app tmsp.Application) *ConsensusState {
//-------------------------------------------------------------------------------
// consensus states
func newConsensusState(state *sm.State, pv *types.PrivValidator, app abci.Application) *ConsensusState {
return newConsensusStateWithConfig(config, state, pv, app)
}
func newConsensusStateWithConfig(thisConfig *cfg.Config, state *sm.State, pv *types.PrivValidator, app abci.Application) *ConsensusState {
// Get BlockStore
blockDB := dbm.NewMemDB()
blockStore := bc.NewBlockStore(blockDB)
// one for mempool, one for consensus
mtx := new(sync.Mutex)
proxyAppConnMem := tmspcli.NewLocalClient(mtx, app)
proxyAppConnCon := tmspcli.NewLocalClient(mtx, app)
proxyAppConnMem := abcicli.NewLocalClient(mtx, app)
proxyAppConnCon := abcicli.NewLocalClient(mtx, app)
// Make Mempool
mempool := mempl.NewMempool(config, proxyAppConnMem)
mempool := mempl.NewMempool(thisConfig.Mempool, proxyAppConnMem, 0)
mempool.SetLogger(log.TestingLogger().With("module", "mempool"))
if thisConfig.Consensus.WaitForTxs() {
mempool.EnableTxsAvailable()
}
// Make ConsensusReactor
cs := NewConsensusState(config, state, proxyAppConnCon, blockStore, mempool)
cs := NewConsensusState(thisConfig.Consensus, state, proxyAppConnCon, blockStore, mempool)
cs.SetLogger(log.TestingLogger())
cs.SetPrivValidator(pv)
evsw := events.NewEventSwitch()
evsw := types.NewEventSwitch()
evsw.SetLogger(log.TestingLogger().With("module", "events"))
cs.SetEventSwitch(evsw)
evsw.Start()
return cs
}
func loadPrivValidator(config *cfg.Config) *types.PrivValidator {
privValidatorFile := config.PrivValidatorFile()
ensureDir(path.Dir(privValidatorFile), 0700)
privValidator := types.LoadOrGenPrivValidator(privValidatorFile, log.TestingLogger())
privValidator.Reset()
return privValidator
}
func fixedConsensusStateDummy() *ConsensusState {
stateDB := dbm.NewMemDB()
state := sm.MakeGenesisStateFromFile(stateDB, config.GenesisFile())
state.SetLogger(log.TestingLogger().With("module", "state"))
privValidator := loadPrivValidator(config)
cs := newConsensusState(state, privValidator, dummy.NewDummyApplication())
cs.SetLogger(log.TestingLogger())
return cs
}
func randConsensusState(nValidators int) (*ConsensusState, []*validatorStub) {
// Get State
state, privVals := randGenesisState(nValidators, false, 10)
@@ -351,9 +283,10 @@ func randConsensusState(nValidators int) (*ConsensusState, []*validatorStub) {
vss := make([]*validatorStub, nValidators)
cs := newConsensusState(state, privVals[0], counter.NewCounterApplication(true))
cs.SetLogger(log.TestingLogger())
for i := 0; i < nValidators; i++ {
vss[i] = NewValidatorStub(privVals[i])
vss[i] = NewValidatorStub(privVals[i], i)
}
// since cs1 starts at 1
incrementHeight(vss[1:]...)
@@ -361,41 +294,105 @@ func randConsensusState(nValidators int) (*ConsensusState, []*validatorStub) {
return cs, vss
}
func subscribeToVoter(cs *ConsensusState, addr []byte) chan interface{} {
voteCh0 := subscribeToEvent(cs.evsw, "tester", types.EventStringVote(), 1)
voteCh := make(chan interface{})
go func() {
for {
v := <-voteCh0
vote := v.(types.EventDataVote)
// we only fire for our own votes
if bytes.Equal(addr, vote.Address) {
voteCh <- v
//-------------------------------------------------------------------------------
func ensureNoNewStep(stepCh chan interface{}) {
timer := time.NewTimer(ensureTimeout)
select {
case <-timer.C:
break
case <-stepCh:
panic("We should be stuck waiting, not moving to the next step")
}
}
}()
return voteCh
}
func readVotes(ch chan interface{}, reads int) chan struct{} {
wg := make(chan struct{})
go func() {
for i := 0; i < reads; i++ {
<-ch // read the precommit event
func ensureNewStep(stepCh chan interface{}) {
timer := time.NewTimer(ensureTimeout)
select {
case <-timer.C:
panic("We shouldnt be stuck waiting")
case <-stepCh:
break
}
close(wg)
}()
return wg
}
func randGenesisState(numValidators int, randPower bool, minPower int64) (*sm.State, []*types.PrivValidator) {
db := dbm.NewMemDB()
genDoc, privValidators := randGenesisDoc(numValidators, randPower, minPower)
s0 := sm.MakeGenesisState(db, genDoc)
s0.Save()
return s0, privValidators
//-------------------------------------------------------------------------------
// consensus nets
// consensusLogger is a TestingLogger which uses a different
// color for each validator ("validator" key must exist).
func consensusLogger() log.Logger {
return log.TestingLoggerWithColorFn(func(keyvals ...interface{}) term.FgBgColor {
for i := 0; i < len(keyvals)-1; i += 2 {
if keyvals[i] == "validator" {
return term.FgBgColor{Fg: term.Color(uint8(keyvals[i+1].(int) + 1))}
}
}
return term.FgBgColor{}
})
}
func randConsensusNet(nValidators int, testName string, tickerFunc func() TimeoutTicker, appFunc func() abci.Application, configOpts ...func(*cfg.Config)) []*ConsensusState {
genDoc, privVals := randGenesisDoc(nValidators, false, 10)
css := make([]*ConsensusState, nValidators)
logger := consensusLogger()
for i := 0; i < nValidators; i++ {
db := dbm.NewMemDB() // each state needs its own db
state := sm.MakeGenesisState(db, genDoc)
state.SetLogger(logger.With("module", "state", "validator", i))
state.Save()
thisConfig := ResetConfig(Fmt("%s_%d", testName, i))
for _, opt := range configOpts {
opt(thisConfig)
}
ensureDir(path.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal
css[i] = newConsensusStateWithConfig(thisConfig, state, privVals[i], appFunc())
css[i].SetLogger(logger.With("validator", i))
css[i].SetTimeoutTicker(tickerFunc())
}
return css
}
// nPeers = nValidators + nNotValidator
func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerFunc func() TimeoutTicker, appFunc func() abci.Application) []*ConsensusState {
genDoc, privVals := randGenesisDoc(nValidators, false, int64(testMinPower))
css := make([]*ConsensusState, nPeers)
for i := 0; i < nPeers; i++ {
db := dbm.NewMemDB() // each state needs its own db
state := sm.MakeGenesisState(db, genDoc)
state.SetLogger(log.TestingLogger().With("module", "state"))
state.Save()
thisConfig := ResetConfig(Fmt("%s_%d", testName, i))
ensureDir(path.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal
var privVal *types.PrivValidator
if i < nValidators {
privVal = privVals[i]
} else {
privVal = types.GenPrivValidator()
_, tempFilePath := Tempfile("priv_validator_")
privVal.SetFile(tempFilePath)
}
css[i] = newConsensusStateWithConfig(thisConfig, state, privVal, appFunc())
css[i].SetLogger(log.TestingLogger())
css[i].SetTimeoutTicker(tickerFunc())
}
return css
}
func getSwitchIndex(switches []*p2p.Switch, peer *p2p.Peer) int {
for i, s := range switches {
if bytes.Equal(peer.NodeInfo.PubKey.Address(), s.NodeInfo().PubKey.Address()) {
return i
}
}
panic("didnt find peer in switches")
return -1
}
//-------------------------------------------------------------------------------
// genesis
func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.GenesisDoc, []*types.PrivValidator) {
validators := make([]types.GenesisValidator, numValidators)
privValidators := make([]*types.PrivValidator, numValidators)
@@ -410,13 +407,76 @@ func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.G
sort.Sort(types.PrivValidatorsByAddress(privValidators))
return &types.GenesisDoc{
GenesisTime: time.Now(),
ChainID: config.GetString("chain_id"),
ChainID: config.ChainID,
Validators: validators,
}, privValidators
}
func startTestRound(cs *ConsensusState, height, round int) {
cs.enterNewRound(height, round)
cs.startRoutines(0)
func randGenesisState(numValidators int, randPower bool, minPower int64) (*sm.State, []*types.PrivValidator) {
genDoc, privValidators := randGenesisDoc(numValidators, randPower, minPower)
db := dbm.NewMemDB()
s0 := sm.MakeGenesisState(db, genDoc)
s0.SetLogger(log.TestingLogger().With("module", "state"))
s0.Save()
return s0, privValidators
}
//------------------------------------
// mock ticker
func newMockTickerFunc(onlyOnce bool) func() TimeoutTicker {
return func() TimeoutTicker {
return &mockTicker{
c: make(chan timeoutInfo, 10),
onlyOnce: onlyOnce,
}
}
}
// mock ticker only fires on RoundStepNewHeight
// and only once if onlyOnce=true
type mockTicker struct {
c chan timeoutInfo
mtx sync.Mutex
onlyOnce bool
fired bool
}
func (m *mockTicker) Start() (bool, error) {
return true, nil
}
func (m *mockTicker) Stop() bool {
return true
}
func (m *mockTicker) ScheduleTimeout(ti timeoutInfo) {
m.mtx.Lock()
defer m.mtx.Unlock()
if m.onlyOnce && m.fired {
return
}
if ti.Step == RoundStepNewHeight {
m.c <- ti
m.fired = true
}
}
func (m *mockTicker) Chan() <-chan timeoutInfo {
return m.c
}
func (mockTicker) SetLogger(log.Logger) {
}
//------------------------------------
func newCounter() abci.Application {
return counter.NewCounterApplication(true)
}
func newPersistentDummy() abci.Application {
dir, _ := ioutil.TempDir("/tmp", "persistent-dummy")
return dummy.NewPersistentDummyApplication(dir)
}

View File

@@ -4,8 +4,8 @@ import (
"strings"
"sync"
. "github.com/tendermint/go-common"
"github.com/tendermint/tendermint/types"
. "github.com/tendermint/tmlibs/common"
)
type RoundVoteSet struct {
@@ -24,6 +24,8 @@ but which round is not known in advance, so when a peer
provides a precommit for a round greater than mtx.round,
we create a new entry in roundVoteSets but also remember the
peer to prevent abuse.
We let each peer provide us with up to 2 unexpected "catchup" rounds.
One for their LastCommit round, and another for the official commit round.
*/
type HeightVoteSet struct {
chainID string
@@ -33,7 +35,7 @@ type HeightVoteSet struct {
mtx sync.Mutex
round int // max tracked round
roundVoteSets map[int]RoundVoteSet // keys: [0...round]
peerCatchupRounds map[string]int // keys: peer.Key; values: round
peerCatchupRounds map[string][]int // keys: peer.Key; values: at most 2 rounds
}
func NewHeightVoteSet(chainID string, height int, valSet *types.ValidatorSet) *HeightVoteSet {
@@ -51,7 +53,7 @@ func (hvs *HeightVoteSet) Reset(height int, valSet *types.ValidatorSet) {
hvs.height = height
hvs.valSet = valSet
hvs.roundVoteSets = make(map[int]RoundVoteSet)
hvs.peerCatchupRounds = make(map[string]int)
hvs.peerCatchupRounds = make(map[string][]int)
hvs.addRound(0)
hvs.round = 0
@@ -89,7 +91,7 @@ func (hvs *HeightVoteSet) addRound(round int) {
if _, ok := hvs.roundVoteSets[round]; ok {
PanicSanity("addRound() for an existing round")
}
log.Debug("addRound(round)", "round", round)
// log.Debug("addRound(round)", "round", round)
prevotes := types.NewVoteSet(hvs.chainID, hvs.height, round, types.VoteTypePrevote, hvs.valSet)
precommits := types.NewVoteSet(hvs.chainID, hvs.height, round, types.VoteTypePrecommit, hvs.valSet)
hvs.roundVoteSets[round] = RoundVoteSet{
@@ -100,24 +102,27 @@ func (hvs *HeightVoteSet) addRound(round int) {
// Duplicate votes return added=false, err=nil.
// By convention, peerKey is "" if origin is self.
func (hvs *HeightVoteSet) AddByIndex(valIndex int, vote *types.Vote, peerKey string) (added bool, address []byte, err error) {
func (hvs *HeightVoteSet) AddVote(vote *types.Vote, peerKey string) (added bool, err error) {
hvs.mtx.Lock()
defer hvs.mtx.Unlock()
if !types.IsVoteTypeValid(vote.Type) {
return
}
voteSet := hvs.getVoteSet(vote.Round, vote.Type)
if voteSet == nil {
if _, ok := hvs.peerCatchupRounds[peerKey]; !ok {
if rndz := hvs.peerCatchupRounds[peerKey]; len(rndz) < 2 {
hvs.addRound(vote.Round)
voteSet = hvs.getVoteSet(vote.Round, vote.Type)
hvs.peerCatchupRounds[peerKey] = vote.Round
hvs.peerCatchupRounds[peerKey] = append(rndz, vote.Round)
} else {
// Peer has sent a vote that does not match our round,
// for more than one round. Bad peer!
// TODO punish peer.
log.Warn("Deal with peer giving votes from unwanted rounds")
// log.Warn("Deal with peer giving votes from unwanted rounds")
return
}
}
added, address, err = voteSet.AddByIndex(valIndex, vote)
added, err = voteSet.AddVote(vote)
return
}
@@ -133,17 +138,19 @@ func (hvs *HeightVoteSet) Precommits(round int) *types.VoteSet {
return hvs.getVoteSet(round, types.VoteTypePrecommit)
}
// Last round that has +2/3 prevotes for a particular block or nil.
// Last round and blockID that has +2/3 prevotes for a particular block or nil.
// Returns -1 if no such round exists.
func (hvs *HeightVoteSet) POLRound() int {
func (hvs *HeightVoteSet) POLInfo() (polRound int, polBlockID types.BlockID) {
hvs.mtx.Lock()
defer hvs.mtx.Unlock()
for r := hvs.round; r >= 0; r-- {
if hvs.getVoteSet(r, types.VoteTypePrevote).HasTwoThirdsMajority() {
return r
rvs := hvs.getVoteSet(r, types.VoteTypePrevote)
polBlockID, ok := rvs.TwoThirdsMajority()
if ok {
return r, polBlockID
}
}
return -1
return -1, types.BlockID{}
}
func (hvs *HeightVoteSet) getVoteSet(round int, type_ byte) *types.VoteSet {
@@ -167,6 +174,8 @@ func (hvs *HeightVoteSet) String() string {
}
func (hvs *HeightVoteSet) StringIndented(indent string) string {
hvs.mtx.Lock()
defer hvs.mtx.Unlock()
vsStrings := make([]string, 0, (len(hvs.roundVoteSets)+1)*2)
// rounds 0 ~ hvs.round inclusive
for round := 0; round <= hvs.round; round++ {
@@ -192,3 +201,20 @@ func (hvs *HeightVoteSet) StringIndented(indent string) string {
indent, strings.Join(vsStrings, "\n"+indent+" "),
indent)
}
// If a peer claims that it has 2/3 majority for given blockKey, call this.
// NOTE: if there are too many peers, or too much peer churn,
// this can cause memory issues.
// TODO: implement ability to remove peers too
func (hvs *HeightVoteSet) SetPeerMaj23(round int, type_ byte, peerID string, blockID types.BlockID) {
hvs.mtx.Lock()
defer hvs.mtx.Unlock()
if !types.IsVoteTypeValid(type_) {
return
}
voteSet := hvs.getVoteSet(round, type_)
if voteSet == nil {
return
}
voteSet.SetPeerMaj23(peerID, blockID)
}

View File

@@ -3,47 +3,58 @@ package consensus
import (
"testing"
. "github.com/tendermint/go-common"
"github.com/tendermint/tendermint/config/tendermint_test"
"github.com/tendermint/tendermint/types"
. "github.com/tendermint/tmlibs/common"
)
func init() {
config = tendermint_test.ResetConfig("consensus_height_vote_set_test")
config = ResetConfig("consensus_height_vote_set_test")
}
func TestPeerCatchupRounds(t *testing.T) {
valSet, privVals := types.RandValidatorSet(10, 1)
hvs := NewHeightVoteSet(config.GetString("chain_id"), 1, valSet)
hvs := NewHeightVoteSet(config.ChainID, 1, valSet)
vote999_0 := makeVoteHR(t, 1, 999, privVals[0])
added, _, err := hvs.AddByIndex(0, vote999_0, "peer1")
vote999_0 := makeVoteHR(t, 1, 999, privVals, 0)
added, err := hvs.AddVote(vote999_0, "peer1")
if !added || err != nil {
t.Error("Expected to successfully add vote from peer", added, err)
}
vote1000_0 := makeVoteHR(t, 1, 1000, privVals[0])
added, _, err = hvs.AddByIndex(0, vote1000_0, "peer1")
vote1000_0 := makeVoteHR(t, 1, 1000, privVals, 0)
added, err = hvs.AddVote(vote1000_0, "peer1")
if !added || err != nil {
t.Error("Expected to successfully add vote from peer", added, err)
}
vote1001_0 := makeVoteHR(t, 1, 1001, privVals, 0)
added, err = hvs.AddVote(vote1001_0, "peer1")
if err != nil {
t.Error("AddVote error", err)
}
if added {
t.Error("Expected to *not* add vote from peer, too many catchup rounds.")
}
added, _, err = hvs.AddByIndex(0, vote1000_0, "peer2")
added, err = hvs.AddVote(vote1001_0, "peer2")
if !added || err != nil {
t.Error("Expected to successfully add vote from another peer")
}
}
func makeVoteHR(t *testing.T, height, round int, privVal *types.PrivValidator) *types.Vote {
func makeVoteHR(t *testing.T, height, round int, privVals []*types.PrivValidator, valIndex int) *types.Vote {
privVal := privVals[valIndex]
vote := &types.Vote{
ValidatorAddress: privVal.Address,
ValidatorIndex: valIndex,
Height: height,
Round: round,
Type: types.VoteTypePrecommit,
BlockHash: []byte("fakehash"),
BlockID: types.BlockID{[]byte("fakehash"), types.PartSetHeader{}},
}
chainID := config.GetString("chain_id")
chainID := config.ChainID
err := privVal.SignVote(chainID, vote)
if err != nil {
panic(Fmt("Error signing vote: %v", err))

View File

@@ -1,18 +0,0 @@
package consensus
import (
"github.com/tendermint/go-logger"
)
var log = logger.New("module", "consensus")
/*
func init() {
log.SetHandler(
logger.LvlFilterHandler(
logger.LvlDebug,
logger.BypassHandler(),
),
)
}
*/

View File

@@ -2,19 +2,93 @@ package consensus
import (
"encoding/binary"
// "math/rand"
"testing"
"time"
"github.com/tendermint/tendermint/config/tendermint_test"
abci "github.com/tendermint/abci/types"
"github.com/tendermint/tendermint/types"
tmsp "github.com/tendermint/tmsp/types"
. "github.com/tendermint/go-common"
. "github.com/tendermint/tmlibs/common"
)
func init() {
config = tendermint_test.ResetConfig("consensus_mempool_test")
config = ResetConfig("consensus_mempool_test")
}
func TestNoProgressUntilTxsAvailable(t *testing.T) {
config := ResetConfig("consensus_mempool_txs_available_test")
config.Consensus.CreateEmptyBlocks = false
state, privVals := randGenesisState(1, false, 10)
cs := newConsensusStateWithConfig(config, state, privVals[0], NewCounterApplication())
cs.mempool.EnableTxsAvailable()
height, round := cs.Height, cs.Round
newBlockCh := subscribeToEvent(cs.evsw, "tester", types.EventStringNewBlock(), 1)
startTestRound(cs, height, round)
ensureNewStep(newBlockCh) // first block gets committed
ensureNoNewStep(newBlockCh)
deliverTxsRange(cs, 0, 2)
ensureNewStep(newBlockCh) // commit txs
ensureNewStep(newBlockCh) // commit updated app hash
ensureNoNewStep(newBlockCh)
}
func TestProgressAfterCreateEmptyBlocksInterval(t *testing.T) {
config := ResetConfig("consensus_mempool_txs_available_test")
config.Consensus.CreateEmptyBlocksInterval = int(ensureTimeout.Seconds())
state, privVals := randGenesisState(1, false, 10)
cs := newConsensusStateWithConfig(config, state, privVals[0], NewCounterApplication())
cs.mempool.EnableTxsAvailable()
height, round := cs.Height, cs.Round
newBlockCh := subscribeToEvent(cs.evsw, "tester", types.EventStringNewBlock(), 1)
startTestRound(cs, height, round)
ensureNewStep(newBlockCh) // first block gets committed
ensureNoNewStep(newBlockCh) // then we dont make a block ...
ensureNewStep(newBlockCh) // until the CreateEmptyBlocksInterval has passed
}
func TestProgressInHigherRound(t *testing.T) {
config := ResetConfig("consensus_mempool_txs_available_test")
config.Consensus.CreateEmptyBlocks = false
state, privVals := randGenesisState(1, false, 10)
cs := newConsensusStateWithConfig(config, state, privVals[0], NewCounterApplication())
cs.mempool.EnableTxsAvailable()
height, round := cs.Height, cs.Round
newBlockCh := subscribeToEvent(cs.evsw, "tester", types.EventStringNewBlock(), 1)
newRoundCh := subscribeToEvent(cs.evsw, "tester", types.EventStringNewRound(), 1)
timeoutCh := subscribeToEvent(cs.evsw, "tester", types.EventStringTimeoutPropose(), 1)
cs.setProposal = func(proposal *types.Proposal) error {
if cs.Height == 2 && cs.Round == 0 {
// dont set the proposal in round 0 so we timeout and
// go to next round
cs.Logger.Info("Ignoring set proposal at height 2, round 0")
return nil
}
return cs.defaultSetProposal(proposal)
}
startTestRound(cs, height, round)
ensureNewStep(newRoundCh) // first round at first height
ensureNewStep(newBlockCh) // first block gets committed
ensureNewStep(newRoundCh) // first round at next height
deliverTxsRange(cs, 0, 2) // we deliver txs, but dont set a proposal so we get the next round
<-timeoutCh
ensureNewStep(newRoundCh) // wait for the next round
ensureNewStep(newBlockCh) // now we can commit the block
}
func deliverTxsRange(cs *ConsensusState, start, end int) {
// Deliver some txs.
for i := start; i < end; i++ {
txBytes := make([]byte, 8)
binary.BigEndian.PutUint64(txBytes, uint64(i))
err := cs.mempool.CheckTx(txBytes, nil)
if err != nil {
panic(Fmt("Error after CheckTx: %v", err))
}
}
}
func TestTxConcurrentWithCommit(t *testing.T) {
@@ -24,36 +98,83 @@ func TestTxConcurrentWithCommit(t *testing.T) {
height, round := cs.Height, cs.Round
newBlockCh := subscribeToEvent(cs.evsw, "tester", types.EventStringNewBlock(), 1)
appendTxsRange := func(start, end int) {
// Append some txs.
for i := start; i < end; i++ {
txBytes := make([]byte, 8)
binary.BigEndian.PutUint64(txBytes, uint64(i))
err := cs.mempool.CheckTx(txBytes, nil)
if err != nil {
panic(Fmt("Error after CheckTx: %v", err))
}
// time.Sleep(time.Microsecond * time.Duration(rand.Int63n(3000)))
}
}
NTxs := 10000
go appendTxsRange(0, NTxs)
go deliverTxsRange(cs, 0, NTxs)
startTestRound(cs, height, round)
ticker := time.NewTicker(time.Second * 20)
for nTxs := 0; nTxs < NTxs; {
select {
case b := <-newBlockCh:
nTxs += b.(types.EventDataNewBlock).Block.Header.NumTxs
nTxs += b.(types.TMEventData).Unwrap().(types.EventDataNewBlock).Block.Header.NumTxs
case <-ticker.C:
panic("Timed out waiting to commit blocks with transactions")
}
}
}
func TestRmBadTx(t *testing.T) {
state, privVals := randGenesisState(1, false, 10)
app := NewCounterApplication()
cs := newConsensusState(state, privVals[0], app)
// increment the counter by 1
txBytes := make([]byte, 8)
binary.BigEndian.PutUint64(txBytes, uint64(0))
app.DeliverTx(txBytes)
app.Commit()
ch := make(chan struct{})
cbCh := make(chan struct{})
go func() {
// Try to send the tx through the mempool.
// CheckTx should not err, but the app should return a bad abci code
// and the tx should get removed from the pool
err := cs.mempool.CheckTx(txBytes, func(r *abci.Response) {
if r.GetCheckTx().Code != abci.CodeType_BadNonce {
t.Fatalf("expected checktx to return bad nonce, got %v", r)
}
cbCh <- struct{}{}
})
if err != nil {
t.Fatal("Error after CheckTx: %v", err)
}
// check for the tx
for {
time.Sleep(time.Second)
txs := cs.mempool.Reap(1)
if len(txs) == 0 {
ch <- struct{}{}
return
}
}
}()
// Wait until the tx returns
ticker := time.After(time.Second * 5)
select {
case <-cbCh:
// success
case <-ticker:
t.Fatalf("Timed out waiting for tx to return")
}
// Wait until the tx is removed
ticker = time.After(time.Second * 5)
select {
case <-ch:
// success
case <-ticker:
t.Fatalf("Timed out waiting for tx to be removed")
}
}
// CounterApplication that maintains a mempool state and resets it upon commit
type CounterApplication struct {
abci.BaseApplication
txCount int
mempoolTxCount int
}
@@ -62,49 +183,37 @@ func NewCounterApplication() *CounterApplication {
return &CounterApplication{}
}
func (app *CounterApplication) Info() string {
return Fmt("txs:%v", app.txCount)
func (app *CounterApplication) Info() abci.ResponseInfo {
return abci.ResponseInfo{Data: Fmt("txs:%v", app.txCount)}
}
func (app *CounterApplication) SetOption(key string, value string) (log string) {
return ""
}
func (app *CounterApplication) AppendTx(tx []byte) tmsp.Result {
func (app *CounterApplication) DeliverTx(tx []byte) abci.Result {
return runTx(tx, &app.txCount)
}
func (app *CounterApplication) CheckTx(tx []byte) tmsp.Result {
func (app *CounterApplication) CheckTx(tx []byte) abci.Result {
return runTx(tx, &app.mempoolTxCount)
}
func runTx(tx []byte, countPtr *int) tmsp.Result {
func runTx(tx []byte, countPtr *int) abci.Result {
count := *countPtr
tx8 := make([]byte, 8)
copy(tx8[len(tx8)-len(tx):], tx)
txValue := binary.BigEndian.Uint64(tx8)
if txValue != uint64(count) {
return tmsp.Result{
Code: tmsp.CodeType_BadNonce,
Data: nil,
Log: Fmt("Invalid nonce. Expected %v, got %v", count, txValue),
}
return abci.ErrBadNonce.AppendLog(Fmt("Invalid nonce. Expected %v, got %v", count, txValue))
}
*countPtr += 1
return tmsp.OK
return abci.OK
}
func (app *CounterApplication) Commit() tmsp.Result {
func (app *CounterApplication) Commit() abci.Result {
app.mempoolTxCount = app.txCount
if app.txCount == 0 {
return tmsp.OK
return abci.OK
} else {
hash := make([]byte, 8)
binary.BigEndian.PutUint64(hash, uint64(app.txCount))
return tmsp.NewResultOK(hash, "")
return abci.NewResultOK(hash, "")
}
}
func (app *CounterApplication) Query(query []byte) tmsp.Result {
return tmsp.NewResultOK(nil, Fmt("Query is not supported"))
}

File diff suppressed because it is too large Load Diff

340
consensus/reactor_test.go Normal file
View File

@@ -0,0 +1,340 @@
package consensus
import (
"fmt"
"sync"
"testing"
"time"
"github.com/tendermint/abci/example/dummy"
"github.com/tendermint/tmlibs/events"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/types"
)
func init() {
config = ResetConfig("consensus_reactor_test")
}
//----------------------------------------------
// in-process testnets
func startConsensusNet(t *testing.T, css []*ConsensusState, N int, subscribeEventRespond bool) ([]*ConsensusReactor, []chan interface{}) {
reactors := make([]*ConsensusReactor, N)
eventChans := make([]chan interface{}, N)
logger := consensusLogger()
for i := 0; i < N; i++ {
reactors[i] = NewConsensusReactor(css[i], true) // so we dont start the consensus states
reactors[i].SetLogger(logger.With("validator", i))
eventSwitch := events.NewEventSwitch()
eventSwitch.SetLogger(logger.With("module", "events", "validator", i))
_, err := eventSwitch.Start()
if err != nil {
t.Fatalf("Failed to start switch: %v", err)
}
reactors[i].SetEventSwitch(eventSwitch)
if subscribeEventRespond {
eventChans[i] = subscribeToEventRespond(eventSwitch, "tester", types.EventStringNewBlock())
} else {
eventChans[i] = subscribeToEvent(eventSwitch, "tester", types.EventStringNewBlock(), 1)
}
}
// make connected switches and start all reactors
p2p.MakeConnectedSwitches(config.P2P, N, func(i int, s *p2p.Switch) *p2p.Switch {
s.AddReactor("CONSENSUS", reactors[i])
return s
}, p2p.Connect2Switches)
// now that everyone is connected, start the state machines
// If we started the state machines before everyone was connected,
// we'd block when the cs fires NewBlockEvent and the peers are trying to start their reactors
for i := 0; i < N; i++ {
s := reactors[i].conS.GetState()
reactors[i].SwitchToConsensus(s)
}
return reactors, eventChans
}
func stopConsensusNet(reactors []*ConsensusReactor) {
for _, r := range reactors {
r.Switch.Stop()
}
}
// Ensure a testnet makes blocks
func TestReactor(t *testing.T) {
N := 4
css := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter)
reactors, eventChans := startConsensusNet(t, css, N, false)
defer stopConsensusNet(reactors)
// wait till everyone makes the first new block
timeoutWaitGroup(t, N, func(wg *sync.WaitGroup, j int) {
<-eventChans[j]
wg.Done()
}, css)
}
// Ensure a testnet sends proposal heartbeats and makes blocks when there are txs
func TestReactorProposalHeartbeats(t *testing.T) {
N := 4
css := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter,
func(c *cfg.Config) {
c.Consensus.CreateEmptyBlocks = false
})
reactors, eventChans := startConsensusNet(t, css, N, false)
defer stopConsensusNet(reactors)
heartbeatChans := make([]chan interface{}, N)
for i := 0; i < N; i++ {
heartbeatChans[i] = subscribeToEvent(css[i].evsw, "tester", types.EventStringProposalHeartbeat(), 1)
}
// wait till everyone sends a proposal heartbeat
timeoutWaitGroup(t, N, func(wg *sync.WaitGroup, j int) {
<-heartbeatChans[j]
wg.Done()
}, css)
// send a tx
css[3].mempool.CheckTx([]byte{1, 2, 3}, nil)
// wait till everyone makes the first new block
timeoutWaitGroup(t, N, func(wg *sync.WaitGroup, j int) {
<-eventChans[j]
wg.Done()
}, css)
}
//-------------------------------------------------------------
// ensure we can make blocks despite cycling a validator set
func TestVotingPowerChange(t *testing.T) {
nVals := 4
css := randConsensusNet(nVals, "consensus_voting_power_changes_test", newMockTickerFunc(true), newPersistentDummy)
reactors, eventChans := startConsensusNet(t, css, nVals, true)
defer stopConsensusNet(reactors)
// map of active validators
activeVals := make(map[string]struct{})
for i := 0; i < nVals; i++ {
activeVals[string(css[i].privValidator.GetAddress())] = struct{}{}
}
// wait till everyone makes block 1
timeoutWaitGroup(t, nVals, func(wg *sync.WaitGroup, j int) {
<-eventChans[j]
eventChans[j] <- struct{}{}
wg.Done()
}, css)
//---------------------------------------------------------------------------
t.Log("---------------------------- Testing changing the voting power of one validator a few times")
val1PubKey := css[0].privValidator.(*types.PrivValidator).PubKey
updateValidatorTx := dummy.MakeValSetChangeTx(val1PubKey.Bytes(), 25)
previousTotalVotingPower := css[0].GetRoundState().LastValidators.TotalVotingPower()
waitForAndValidateBlock(t, nVals, activeVals, eventChans, css, updateValidatorTx)
waitForAndValidateBlock(t, nVals, activeVals, eventChans, css)
waitForAndValidateBlock(t, nVals, activeVals, eventChans, css)
waitForAndValidateBlock(t, nVals, activeVals, eventChans, css)
if css[0].GetRoundState().LastValidators.TotalVotingPower() == previousTotalVotingPower {
t.Fatalf("expected voting power to change (before: %d, after: %d)", previousTotalVotingPower, css[0].GetRoundState().LastValidators.TotalVotingPower())
}
updateValidatorTx = dummy.MakeValSetChangeTx(val1PubKey.Bytes(), 2)
previousTotalVotingPower = css[0].GetRoundState().LastValidators.TotalVotingPower()
waitForAndValidateBlock(t, nVals, activeVals, eventChans, css, updateValidatorTx)
waitForAndValidateBlock(t, nVals, activeVals, eventChans, css)
waitForAndValidateBlock(t, nVals, activeVals, eventChans, css)
waitForAndValidateBlock(t, nVals, activeVals, eventChans, css)
if css[0].GetRoundState().LastValidators.TotalVotingPower() == previousTotalVotingPower {
t.Fatalf("expected voting power to change (before: %d, after: %d)", previousTotalVotingPower, css[0].GetRoundState().LastValidators.TotalVotingPower())
}
updateValidatorTx = dummy.MakeValSetChangeTx(val1PubKey.Bytes(), 100)
previousTotalVotingPower = css[0].GetRoundState().LastValidators.TotalVotingPower()
waitForAndValidateBlock(t, nVals, activeVals, eventChans, css, updateValidatorTx)
waitForAndValidateBlock(t, nVals, activeVals, eventChans, css)
waitForAndValidateBlock(t, nVals, activeVals, eventChans, css)
waitForAndValidateBlock(t, nVals, activeVals, eventChans, css)
if css[0].GetRoundState().LastValidators.TotalVotingPower() == previousTotalVotingPower {
t.Fatalf("expected voting power to change (before: %d, after: %d)", previousTotalVotingPower, css[0].GetRoundState().LastValidators.TotalVotingPower())
}
}
func TestValidatorSetChanges(t *testing.T) {
nPeers := 7
nVals := 4
css := randConsensusNetWithPeers(nVals, nPeers, "consensus_val_set_changes_test", newMockTickerFunc(true), newPersistentDummy)
reactors, eventChans := startConsensusNet(t, css, nPeers, true)
defer stopConsensusNet(reactors)
// map of active validators
activeVals := make(map[string]struct{})
for i := 0; i < nVals; i++ {
activeVals[string(css[i].privValidator.GetAddress())] = struct{}{}
}
// wait till everyone makes block 1
timeoutWaitGroup(t, nPeers, func(wg *sync.WaitGroup, j int) {
<-eventChans[j]
eventChans[j] <- struct{}{}
wg.Done()
}, css)
//---------------------------------------------------------------------------
t.Log("---------------------------- Testing adding one validator")
newValidatorPubKey1 := css[nVals].privValidator.(*types.PrivValidator).PubKey
newValidatorTx1 := dummy.MakeValSetChangeTx(newValidatorPubKey1.Bytes(), uint64(testMinPower))
// wait till everyone makes block 2
// ensure the commit includes all validators
// send newValTx to change vals in block 3
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css, newValidatorTx1)
// wait till everyone makes block 3.
// it includes the commit for block 2, which is by the original validator set
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css)
// wait till everyone makes block 4.
// it includes the commit for block 3, which is by the original validator set
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css)
// the commits for block 4 should be with the updated validator set
activeVals[string(newValidatorPubKey1.Address())] = struct{}{}
// wait till everyone makes block 5
// it includes the commit for block 4, which should have the updated validator set
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css)
//---------------------------------------------------------------------------
t.Log("---------------------------- Testing changing the voting power of one validator")
updateValidatorPubKey1 := css[nVals].privValidator.(*types.PrivValidator).PubKey
updateValidatorTx1 := dummy.MakeValSetChangeTx(updateValidatorPubKey1.Bytes(), 25)
previousTotalVotingPower := css[nVals].GetRoundState().LastValidators.TotalVotingPower()
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css, updateValidatorTx1)
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css)
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css)
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css)
if css[nVals].GetRoundState().LastValidators.TotalVotingPower() == previousTotalVotingPower {
t.Errorf("expected voting power to change (before: %d, after: %d)", previousTotalVotingPower, css[nVals].GetRoundState().LastValidators.TotalVotingPower())
}
//---------------------------------------------------------------------------
t.Log("---------------------------- Testing adding two validators at once")
newValidatorPubKey2 := css[nVals+1].privValidator.(*types.PrivValidator).PubKey
newValidatorTx2 := dummy.MakeValSetChangeTx(newValidatorPubKey2.Bytes(), uint64(testMinPower))
newValidatorPubKey3 := css[nVals+2].privValidator.(*types.PrivValidator).PubKey
newValidatorTx3 := dummy.MakeValSetChangeTx(newValidatorPubKey3.Bytes(), uint64(testMinPower))
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css, newValidatorTx2, newValidatorTx3)
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css)
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css)
activeVals[string(newValidatorPubKey2.Address())] = struct{}{}
activeVals[string(newValidatorPubKey3.Address())] = struct{}{}
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css)
//---------------------------------------------------------------------------
t.Log("---------------------------- Testing removing two validators at once")
removeValidatorTx2 := dummy.MakeValSetChangeTx(newValidatorPubKey2.Bytes(), 0)
removeValidatorTx3 := dummy.MakeValSetChangeTx(newValidatorPubKey3.Bytes(), 0)
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css, removeValidatorTx2, removeValidatorTx3)
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css)
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css)
delete(activeVals, string(newValidatorPubKey2.Address()))
delete(activeVals, string(newValidatorPubKey3.Address()))
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css)
}
// Check we can make blocks with skip_timeout_commit=false
func TestReactorWithTimeoutCommit(t *testing.T) {
N := 4
css := randConsensusNet(N, "consensus_reactor_with_timeout_commit_test", newMockTickerFunc(false), newCounter)
// override default SkipTimeoutCommit == true for tests
for i := 0; i < N; i++ {
css[i].config.SkipTimeoutCommit = false
}
reactors, eventChans := startConsensusNet(t, css, N-1, false)
defer stopConsensusNet(reactors)
// wait till everyone makes the first new block
timeoutWaitGroup(t, N-1, func(wg *sync.WaitGroup, j int) {
<-eventChans[j]
wg.Done()
}, css)
}
func waitForAndValidateBlock(t *testing.T, n int, activeVals map[string]struct{}, eventChans []chan interface{}, css []*ConsensusState, txs ...[]byte) {
timeoutWaitGroup(t, n, func(wg *sync.WaitGroup, j int) {
newBlockI := <-eventChans[j]
newBlock := newBlockI.(types.TMEventData).Unwrap().(types.EventDataNewBlock).Block
t.Logf("[WARN] Got block height=%v validator=%v", newBlock.Height, j)
err := validateBlock(newBlock, activeVals)
if err != nil {
t.Fatal(err)
}
for _, tx := range txs {
css[j].mempool.CheckTx(tx, nil)
}
eventChans[j] <- struct{}{}
wg.Done()
}, css)
}
// expects high synchrony!
func validateBlock(block *types.Block, activeVals map[string]struct{}) error {
if block.LastCommit.Size() != len(activeVals) {
return fmt.Errorf("Commit size doesn't match number of active validators. Got %d, expected %d", block.LastCommit.Size(), len(activeVals))
}
for _, vote := range block.LastCommit.Precommits {
if _, ok := activeVals[string(vote.ValidatorAddress)]; !ok {
return fmt.Errorf("Found vote for unactive validator %X", vote.ValidatorAddress)
}
}
return nil
}
func timeoutWaitGroup(t *testing.T, n int, f func(*sync.WaitGroup, int), css []*ConsensusState) {
wg := new(sync.WaitGroup)
wg.Add(n)
for i := 0; i < n; i++ {
go f(wg, i)
}
done := make(chan struct{})
go func() {
wg.Wait()
close(done)
}()
select {
case <-done:
case <-time.After(time.Second * 10):
for i, cs := range css {
fmt.Println("#################")
fmt.Println("Validator", i)
fmt.Println(cs.GetRoundState())
fmt.Println("")
}
panic("Timed out waiting for all validators to commit a block")
}
}

View File

@@ -1,29 +1,46 @@
package consensus
import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
"os"
"reflect"
"strconv"
"strings"
"time"
. "github.com/tendermint/go-common"
"github.com/tendermint/go-wire"
abci "github.com/tendermint/abci/types"
wire "github.com/tendermint/go-wire"
auto "github.com/tendermint/tmlibs/autofile"
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/log"
"github.com/tendermint/tendermint/proxy"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
)
// unmarshal and apply a single message to the consensus state
// Functionality to replay blocks and messages on recovery from a crash.
// There are two general failure scenarios: failure during consensus, and failure while applying the block.
// The former is handled by the WAL, the latter by the proxyApp Handshake on restart,
// which ultimately hands off the work to the WAL.
//-----------------------------------------
// recover from failure during consensus
// by replaying messages from the WAL
// Unmarshal and apply a single message to the consensus state
// as if it were received in receiveRoutine
// Lines that start with "#" are ignored.
// NOTE: receiveRoutine should not be running
func (cs *ConsensusState) readReplayMessage(msgBytes []byte, newStepCh chan interface{}) error {
// Skip over empty and meta lines
if len(msgBytes) == 0 || msgBytes[0] == '#' {
return nil
}
var err error
var msg ConsensusLogMessage
var msg TimedWALMessage
wire.ReadJSON(&msg, msgBytes, &err)
if err != nil {
fmt.Println("MsgBytes:", msgBytes, string(msgBytes))
@@ -33,7 +50,7 @@ func (cs *ConsensusState) readReplayMessage(msgBytes []byte, newStepCh chan inte
// for logging
switch m := msg.Msg.(type) {
case types.EventDataRoundState:
log.Notice("Replay: New Step", "height", m.Height, "round", m.Round, "step", m.Step)
cs.Logger.Info("Replay: New Step", "height", m.Height, "round", m.Round, "step", m.Step)
// these are playback checks
ticker := time.After(time.Second * 2)
if newStepCh != nil {
@@ -55,306 +72,349 @@ func (cs *ConsensusState) readReplayMessage(msgBytes []byte, newStepCh chan inte
switch msg := m.Msg.(type) {
case *ProposalMessage:
p := msg.Proposal
log.Notice("Replay: Proposal", "height", p.Height, "round", p.Round, "header",
cs.Logger.Info("Replay: Proposal", "height", p.Height, "round", p.Round, "header",
p.BlockPartsHeader, "pol", p.POLRound, "peer", peerKey)
case *BlockPartMessage:
log.Notice("Replay: BlockPart", "height", msg.Height, "round", msg.Round, "peer", peerKey)
cs.Logger.Info("Replay: BlockPart", "height", msg.Height, "round", msg.Round, "peer", peerKey)
case *VoteMessage:
v := msg.Vote
log.Notice("Replay: Vote", "height", v.Height, "round", v.Round, "type", v.Type,
"hash", v.BlockHash, "header", v.BlockPartsHeader, "peer", peerKey)
cs.Logger.Info("Replay: Vote", "height", v.Height, "round", v.Round, "type", v.Type,
"blockID", v.BlockID, "peer", peerKey)
}
cs.handleMsg(m, cs.RoundState)
cs.handleMsg(m)
case timeoutInfo:
log.Notice("Replay: Timeout", "height", m.Height, "round", m.Round, "step", m.Step, "dur", m.Duration)
cs.Logger.Info("Replay: Timeout", "height", m.Height, "round", m.Round, "step", m.Step, "dur", m.Duration)
cs.handleTimeout(m, cs.RoundState)
default:
return fmt.Errorf("Replay: Unknown ConsensusLogMessage type: %v", reflect.TypeOf(msg.Msg))
return fmt.Errorf("Replay: Unknown TimedWALMessage type: %v", reflect.TypeOf(msg.Msg))
}
return nil
}
// replay only those messages since the last block.
// timeoutRoutine should run concurrently to read off tickChan
func (cs *ConsensusState) catchupReplay(height int) error {
if !cs.wal.Exists() {
return nil
}
func (cs *ConsensusState) catchupReplay(csHeight int) error {
// set replayMode
cs.replayMode = true
defer func() { cs.replayMode = false }()
// starting from end of file,
// read messages until a new height is found
nLines, err := cs.wal.SeekFromEnd(func(lineBytes []byte) bool {
var err error
var msg ConsensusLogMessage
wire.ReadJSON(&msg, lineBytes, &err)
if err != nil {
panic(Fmt("Failed to read cs_msg_log json: %v", err))
// Ensure that ENDHEIGHT for this height doesn't exist
// NOTE: This is just a sanity check. As far as we know things work fine without it,
// and Handshake could reuse ConsensusState if it weren't for this check (since we can crash after writing ENDHEIGHT).
gr, found, err := cs.wal.group.Search("#ENDHEIGHT: ", makeHeightSearchFunc(csHeight))
if gr != nil {
gr.Close()
}
m, ok := msg.Msg.(types.EventDataRoundState)
if ok && m.Step == RoundStepNewHeight.String() {
// TODO: ensure the height matches
return true
}
return false
})
if err != nil {
return err
if found {
return errors.New(cmn.Fmt("WAL should not contain #ENDHEIGHT %d.", csHeight))
}
var beginning bool // if we had to go back to the beginning
if c, _ := cs.wal.fp.Seek(0, 1); c == 0 {
beginning = true
}
log.Notice("Catchup by replaying consensus messages", "n", nLines)
// now we can replay the latest nLines on consensus state
// note we can't use scan because we've already been reading from the file
reader := bufio.NewReader(cs.wal.fp)
for i := 0; i < nLines; i++ {
msgBytes, err := reader.ReadBytes('\n')
// Search for last height marker
gr, found, err = cs.wal.group.Search("#ENDHEIGHT: ", makeHeightSearchFunc(csHeight-1))
if err == io.EOF {
break
cs.Logger.Error("Replay: wal.group.Search returned EOF", "#ENDHEIGHT", csHeight-1)
// if we upgraded from 0.9 to 0.9.1, we may have #HEIGHT instead
// TODO (0.10.0): remove this
gr, found, err = cs.wal.group.Search("#HEIGHT: ", makeHeightSearchFunc(csHeight))
if err == io.EOF {
cs.Logger.Error("Replay: wal.group.Search returned EOF", "#HEIGHT", csHeight)
return nil
} else if err != nil {
return err
} else if len(msgBytes) == 0 {
continue
} else if len(msgBytes) == 1 && msgBytes[0] == '\n' {
continue
}
// the first msg is the NewHeight event (if we're not at the beginning), so we can ignore it
if !beginning && i == 1 {
continue
} else if err != nil {
return err
} else {
defer gr.Close()
}
if !found {
// if we upgraded from 0.9 to 0.9.1, we may have #HEIGHT instead
// TODO (0.10.0): remove this
gr, _, err = cs.wal.group.Search("#HEIGHT: ", makeHeightSearchFunc(csHeight))
if err == io.EOF {
cs.Logger.Error("Replay: wal.group.Search returned EOF", "#HEIGHT", csHeight)
return nil
} else if err != nil {
return err
} else {
defer gr.Close()
}
// TODO (0.10.0): uncomment
// return errors.New(cmn.Fmt("Cannot replay height %d. WAL does not contain #ENDHEIGHT for %d.", csHeight, csHeight-1))
}
cs.Logger.Info("Catchup by replaying consensus messages", "height", csHeight)
for {
line, err := gr.ReadLine()
if err != nil {
if err == io.EOF {
break
} else {
return err
}
}
// NOTE: since the priv key is set when the msgs are received
// it will attempt to eg double sign but we can just ignore it
// since the votes will be replayed and we'll get to the next step
if err := cs.readReplayMessage(msgBytes, nil); err != nil {
if err := cs.readReplayMessage([]byte(line), nil); err != nil {
return err
}
}
log.Notice("Done catchup replay")
cs.Logger.Info("Replay: Done")
return nil
}
//--------------------------------------------------------
// replay messages interactively or all at once
//--------------------------------------------------------------------------------
// Interactive playback
func (cs ConsensusState) ReplayConsole(file string) error {
return cs.replay(file, true)
}
// Full playback, with tests
func (cs ConsensusState) ReplayMessages(file string) error {
return cs.replay(file, false)
}
// replay all msgs or start the console
func (cs *ConsensusState) replay(file string, console bool) error {
if cs.IsRunning() {
return errors.New("cs is already running, cannot replay")
// Parses marker lines of the form:
// #ENDHEIGHT: 12345
func makeHeightSearchFunc(height int) auto.SearchFunc {
return func(line string) (int, error) {
line = strings.TrimRight(line, "\n")
parts := strings.Split(line, " ")
if len(parts) != 2 {
return -1, errors.New("Line did not have 2 parts")
}
if cs.wal != nil {
return errors.New("cs wal is open, cannot replay")
}
cs.startForReplay()
// ensure all new step events are regenerated as expected
newStepCh := subscribeToEvent(cs.evsw, "replay-test", types.EventStringNewRoundStep(), 1)
// just open the file for reading, no need to use wal
fp, err := os.OpenFile(file, os.O_RDONLY, 0666)
i, err := strconv.Atoi(parts[1])
if err != nil {
return err
return -1, errors.New("Failed to parse INFO: " + err.Error())
}
if height < i {
return 1, nil
} else if height == i {
return 0, nil
} else {
return -1, nil
}
}
}
//----------------------------------------------
// Recover from failure during block processing
// by handshaking with the app to figure out where
// we were last and using the WAL to recover there
type Handshaker struct {
state *sm.State
store types.BlockStore
logger log.Logger
nBlocks int // number of blocks applied to the state
}
func NewHandshaker(state *sm.State, store types.BlockStore) *Handshaker {
return &Handshaker{state, store, log.NewNopLogger(), 0}
}
func (h *Handshaker) SetLogger(l log.Logger) {
h.logger = l
}
func (h *Handshaker) NBlocks() int {
return h.nBlocks
}
// TODO: retry the handshake/replay if it fails ?
func (h *Handshaker) Handshake(proxyApp proxy.AppConns) error {
// handshake is done via info request on the query conn
res, err := proxyApp.Query().InfoSync()
if err != nil {
return errors.New(cmn.Fmt("Error calling Info: %v", err))
}
pb := newPlayback(file, fp, cs, cs.state.Copy())
defer pb.fp.Close()
blockHeight := int(res.LastBlockHeight) // XXX: beware overflow
appHash := res.LastBlockAppHash
var nextN int // apply N msgs in a row
for pb.scanner.Scan() {
if nextN == 0 && console {
nextN = pb.replayConsoleLoop()
h.logger.Info("ABCI Handshake", "appHeight", blockHeight, "appHash", fmt.Sprintf("%X", appHash))
// TODO: check version
// replay blocks up to the latest in the blockstore
_, err = h.ReplayBlocks(appHash, blockHeight, proxyApp)
if err != nil {
return errors.New(cmn.Fmt("Error on replay: %v", err))
}
if err := pb.cs.readReplayMessage(pb.scanner.Bytes(), newStepCh); err != nil {
return err
h.logger.Info("Completed ABCI Handshake - Tendermint and App are synced", "appHeight", blockHeight, "appHash", fmt.Sprintf("%X", appHash))
// TODO: (on restart) replay mempool
return nil
}
// Replay all blocks since appBlockHeight and ensure the result matches the current state.
// Returns the final AppHash or an error
func (h *Handshaker) ReplayBlocks(appHash []byte, appBlockHeight int, proxyApp proxy.AppConns) ([]byte, error) {
storeBlockHeight := h.store.Height()
stateBlockHeight := h.state.LastBlockHeight
h.logger.Info("ABCI Replay Blocks", "appHeight", appBlockHeight, "storeHeight", storeBlockHeight, "stateHeight", stateBlockHeight)
// If appBlockHeight == 0 it means that we are at genesis and hence should send InitChain
if appBlockHeight == 0 {
validators := types.TM2PB.Validators(h.state.Validators)
proxyApp.Consensus().InitChainSync(validators)
}
if nextN > 0 {
nextN -= 1
// First handle edge cases and constraints on the storeBlockHeight
if storeBlockHeight == 0 {
return appHash, h.checkAppHash(appHash)
} else if storeBlockHeight < appBlockHeight {
// the app should never be ahead of the store (but this is under app's control)
return appHash, sm.ErrAppBlockHeightTooHigh{storeBlockHeight, appBlockHeight}
} else if storeBlockHeight < stateBlockHeight {
// the state should never be ahead of the store (this is under tendermint's control)
cmn.PanicSanity(cmn.Fmt("StateBlockHeight (%d) > StoreBlockHeight (%d)", stateBlockHeight, storeBlockHeight))
} else if storeBlockHeight > stateBlockHeight+1 {
// store should be at most one ahead of the state (this is under tendermint's control)
cmn.PanicSanity(cmn.Fmt("StoreBlockHeight (%d) > StateBlockHeight + 1 (%d)", storeBlockHeight, stateBlockHeight+1))
}
pb.count += 1
// Now either store is equal to state, or one ahead.
// For each, consider all cases of where the app could be, given app <= store
if storeBlockHeight == stateBlockHeight {
// Tendermint ran Commit and saved the state.
// Either the app is asking for replay, or we're all synced up.
if appBlockHeight < storeBlockHeight {
// the app is behind, so replay blocks, but no need to go through WAL (state is already synced to store)
return h.replayBlocks(proxyApp, appBlockHeight, storeBlockHeight, false)
} else if appBlockHeight == storeBlockHeight {
// We're good!
return appHash, h.checkAppHash(appHash)
}
} else if storeBlockHeight == stateBlockHeight+1 {
// We saved the block in the store but haven't updated the state,
// so we'll need to replay a block using the WAL.
if appBlockHeight < stateBlockHeight {
// the app is further behind than it should be, so replay blocks
// but leave the last block to go through the WAL
return h.replayBlocks(proxyApp, appBlockHeight, storeBlockHeight, true)
} else if appBlockHeight == stateBlockHeight {
// We haven't run Commit (both the state and app are one block behind),
// so replayBlock with the real app.
// NOTE: We could instead use the cs.WAL on cs.Start,
// but we'd have to allow the WAL to replay a block that wrote it's ENDHEIGHT
h.logger.Info("Replay last block using real app")
return h.replayBlock(storeBlockHeight, proxyApp.Consensus())
} else if appBlockHeight == storeBlockHeight {
// We ran Commit, but didn't save the state, so replayBlock with mock app
abciResponses := h.state.LoadABCIResponses()
mockApp := newMockProxyApp(appHash, abciResponses)
h.logger.Info("Replay last block using mock app")
return h.replayBlock(storeBlockHeight, mockApp)
}
}
cmn.PanicSanity("Should never happen")
return nil, nil
}
func (h *Handshaker) replayBlocks(proxyApp proxy.AppConns, appBlockHeight, storeBlockHeight int, mutateState bool) ([]byte, error) {
// App is further behind than it should be, so we need to replay blocks.
// We replay all blocks from appBlockHeight+1.
// Note that we don't have an old version of the state,
// so we by-pass state validation/mutation using sm.ExecCommitBlock.
// If mutateState == true, the final block is replayed with h.replayBlock()
var appHash []byte
var err error
finalBlock := storeBlockHeight
if mutateState {
finalBlock -= 1
}
for i := appBlockHeight + 1; i <= finalBlock; i++ {
h.logger.Info("Applying block", "height", i)
block := h.store.LoadBlock(i)
appHash, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, h.logger)
if err != nil {
return nil, err
}
h.nBlocks += 1
}
if mutateState {
// sync the final block
return h.replayBlock(storeBlockHeight, proxyApp.Consensus())
}
return appHash, h.checkAppHash(appHash)
}
// ApplyBlock on the proxyApp with the last block.
func (h *Handshaker) replayBlock(height int, proxyApp proxy.AppConnConsensus) ([]byte, error) {
mempool := types.MockMempool{}
var eventCache types.Fireable // nil
block := h.store.LoadBlock(height)
meta := h.store.LoadBlockMeta(height)
if err := h.state.ApplyBlock(eventCache, proxyApp, block, meta.BlockID.PartsHeader, mempool); err != nil {
return nil, err
}
h.nBlocks += 1
return h.state.AppHash, nil
}
func (h *Handshaker) checkAppHash(appHash []byte) error {
if !bytes.Equal(h.state.AppHash, appHash) {
panic(errors.New(cmn.Fmt("Tendermint state.AppHash does not match AppHash after replay. Got %X, expected %X", appHash, h.state.AppHash)).Error())
return nil
}
return nil
}
//------------------------------------------------
// playback manager
//--------------------------------------------------------------------------------
// mockProxyApp uses ABCIResponses to give the right results
// Useful because we don't want to call Commit() twice for the same block on the real app.
type playback struct {
cs *ConsensusState
fp *os.File
scanner *bufio.Scanner
count int // how many lines/msgs into the file are we
// replays can be reset to beginning
fileName string // so we can close/reopen the file
genesisState *sm.State // so the replay session knows where to restart from
func newMockProxyApp(appHash []byte, abciResponses *sm.ABCIResponses) proxy.AppConnConsensus {
clientCreator := proxy.NewLocalClientCreator(&mockProxyApp{
appHash: appHash,
abciResponses: abciResponses,
})
cli, _ := clientCreator.NewABCIClient()
cli.Start()
return proxy.NewAppConnConsensus(cli)
}
func newPlayback(fileName string, fp *os.File, cs *ConsensusState, genState *sm.State) *playback {
return &playback{
cs: cs,
fp: fp,
fileName: fileName,
genesisState: genState,
scanner: bufio.NewScanner(fp),
type mockProxyApp struct {
abci.BaseApplication
appHash []byte
txCount int
abciResponses *sm.ABCIResponses
}
func (mock *mockProxyApp) DeliverTx(tx []byte) abci.Result {
r := mock.abciResponses.DeliverTx[mock.txCount]
mock.txCount += 1
return abci.Result{
r.Code,
r.Data,
r.Log,
}
}
// go back count steps by resetting the state and running (pb.count - count) steps
func (pb *playback) replayReset(count int, newStepCh chan interface{}) error {
pb.cs.Stop()
newCS := NewConsensusState(pb.cs.config, pb.genesisState.Copy(), pb.cs.proxyAppConn, pb.cs.blockStore, pb.cs.mempool)
newCS.SetEventSwitch(pb.cs.evsw)
newCS.startForReplay()
pb.fp.Close()
fp, err := os.OpenFile(pb.fileName, os.O_RDONLY, 0666)
if err != nil {
return err
}
pb.fp = fp
pb.scanner = bufio.NewScanner(fp)
count = pb.count - count
log.Notice(Fmt("Reseting from %d to %d", pb.count, count))
pb.count = 0
pb.cs = newCS
for i := 0; pb.scanner.Scan() && i < count; i++ {
if err := pb.cs.readReplayMessage(pb.scanner.Bytes(), newStepCh); err != nil {
return err
}
pb.count += 1
}
return nil
func (mock *mockProxyApp) EndBlock(height uint64) abci.ResponseEndBlock {
mock.txCount = 0
return mock.abciResponses.EndBlock
}
func (cs *ConsensusState) startForReplay() {
// don't want to start full cs
cs.BaseService.OnStart()
// since we replay tocks we just ignore ticks
go func() {
for {
select {
case <-cs.tickChan:
case <-cs.Quit:
return
}
}
}()
}
// console function for parsing input and running commands
func (pb *playback) replayConsoleLoop() int {
for {
fmt.Printf("> ")
bufReader := bufio.NewReader(os.Stdin)
line, more, err := bufReader.ReadLine()
if more {
Exit("input is too long")
} else if err != nil {
Exit(err.Error())
}
tokens := strings.Split(string(line), " ")
if len(tokens) == 0 {
continue
}
switch tokens[0] {
case "next":
// "next" -> replay next message
// "next N" -> replay next N messages
if len(tokens) == 1 {
return 0
} else {
i, err := strconv.Atoi(tokens[1])
if err != nil {
fmt.Println("next takes an integer argument")
} else {
return i
}
}
case "back":
// "back" -> go back one message
// "back N" -> go back N messages
// NOTE: "back" is not supported in the state machine design,
// so we restart and replay up to
// ensure all new step events are regenerated as expected
newStepCh := subscribeToEvent(pb.cs.evsw, "replay-test", types.EventStringNewRoundStep(), 1)
if len(tokens) == 1 {
pb.replayReset(1, newStepCh)
} else {
i, err := strconv.Atoi(tokens[1])
if err != nil {
fmt.Println("back takes an integer argument")
} else if i > pb.count {
fmt.Printf("argument to back must not be larger than the current count (%d)\n", pb.count)
} else {
pb.replayReset(i, newStepCh)
}
}
case "rs":
// "rs" -> print entire round state
// "rs short" -> print height/round/step
// "rs <field>" -> print another field of the round state
rs := pb.cs.RoundState
if len(tokens) == 1 {
fmt.Println(rs)
} else {
switch tokens[1] {
case "short":
fmt.Printf("%v/%v/%v\n", rs.Height, rs.Round, rs.Step)
case "validators":
fmt.Println(rs.Validators)
case "proposal":
fmt.Println(rs.Proposal)
case "proposal_block":
fmt.Printf("%v %v\n", rs.ProposalBlockParts.StringShort(), rs.ProposalBlock.StringShort())
case "locked_round":
fmt.Println(rs.LockedRound)
case "locked_block":
fmt.Printf("%v %v\n", rs.LockedBlockParts.StringShort(), rs.LockedBlock.StringShort())
case "votes":
fmt.Println(rs.Votes.StringIndented(" "))
default:
fmt.Println("Unknown option", tokens[1])
}
}
case "n":
fmt.Println(pb.count)
}
}
return 0
func (mock *mockProxyApp) Commit() abci.Result {
return abci.NewResultOK(mock.appHash, "")
}

264
consensus/replay_file.go Normal file
View File

@@ -0,0 +1,264 @@
package consensus
import (
"bufio"
"errors"
"fmt"
"os"
"strconv"
"strings"
bc "github.com/tendermint/tendermint/blockchain"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/proxy"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
)
//--------------------------------------------------------
// replay messages interactively or all at once
func RunReplayFile(config cfg.BaseConfig, csConfig *cfg.ConsensusConfig, console bool) {
consensusState := newConsensusStateForReplay(config, csConfig)
if err := consensusState.ReplayFile(csConfig.WalFile(), console); err != nil {
cmn.Exit(cmn.Fmt("Error during consensus replay: %v", err))
}
}
// Replay msgs in file or start the console
func (cs *ConsensusState) ReplayFile(file string, console bool) error {
if cs.IsRunning() {
return errors.New("cs is already running, cannot replay")
}
if cs.wal != nil {
return errors.New("cs wal is open, cannot replay")
}
cs.startForReplay()
// ensure all new step events are regenerated as expected
newStepCh := subscribeToEvent(cs.evsw, "replay-test", types.EventStringNewRoundStep(), 1)
// just open the file for reading, no need to use wal
fp, err := os.OpenFile(file, os.O_RDONLY, 0666)
if err != nil {
return err
}
pb := newPlayback(file, fp, cs, cs.state.Copy())
defer pb.fp.Close()
var nextN int // apply N msgs in a row
for pb.scanner.Scan() {
if nextN == 0 && console {
nextN = pb.replayConsoleLoop()
}
if err := pb.cs.readReplayMessage(pb.scanner.Bytes(), newStepCh); err != nil {
return err
}
if nextN > 0 {
nextN -= 1
}
pb.count += 1
}
return nil
}
//------------------------------------------------
// playback manager
type playback struct {
cs *ConsensusState
fp *os.File
scanner *bufio.Scanner
count int // how many lines/msgs into the file are we
// replays can be reset to beginning
fileName string // so we can close/reopen the file
genesisState *sm.State // so the replay session knows where to restart from
}
func newPlayback(fileName string, fp *os.File, cs *ConsensusState, genState *sm.State) *playback {
return &playback{
cs: cs,
fp: fp,
fileName: fileName,
genesisState: genState,
scanner: bufio.NewScanner(fp),
}
}
// go back count steps by resetting the state and running (pb.count - count) steps
func (pb *playback) replayReset(count int, newStepCh chan interface{}) error {
pb.cs.Stop()
pb.cs.Wait()
newCS := NewConsensusState(pb.cs.config, pb.genesisState.Copy(), pb.cs.proxyAppConn, pb.cs.blockStore, pb.cs.mempool)
newCS.SetEventSwitch(pb.cs.evsw)
newCS.startForReplay()
pb.fp.Close()
fp, err := os.OpenFile(pb.fileName, os.O_RDONLY, 0666)
if err != nil {
return err
}
pb.fp = fp
pb.scanner = bufio.NewScanner(fp)
count = pb.count - count
fmt.Printf("Reseting from %d to %d\n", pb.count, count)
pb.count = 0
pb.cs = newCS
for i := 0; pb.scanner.Scan() && i < count; i++ {
if err := pb.cs.readReplayMessage(pb.scanner.Bytes(), newStepCh); err != nil {
return err
}
pb.count += 1
}
return nil
}
func (cs *ConsensusState) startForReplay() {
cs.Logger.Error("Replay commands are disabled until someone updates them and writes tests")
/* TODO:!
// since we replay tocks we just ignore ticks
go func() {
for {
select {
case <-cs.tickChan:
case <-cs.Quit:
return
}
}
}()*/
}
// console function for parsing input and running commands
func (pb *playback) replayConsoleLoop() int {
for {
fmt.Printf("> ")
bufReader := bufio.NewReader(os.Stdin)
line, more, err := bufReader.ReadLine()
if more {
cmn.Exit("input is too long")
} else if err != nil {
cmn.Exit(err.Error())
}
tokens := strings.Split(string(line), " ")
if len(tokens) == 0 {
continue
}
switch tokens[0] {
case "next":
// "next" -> replay next message
// "next N" -> replay next N messages
if len(tokens) == 1 {
return 0
} else {
i, err := strconv.Atoi(tokens[1])
if err != nil {
fmt.Println("next takes an integer argument")
} else {
return i
}
}
case "back":
// "back" -> go back one message
// "back N" -> go back N messages
// NOTE: "back" is not supported in the state machine design,
// so we restart and replay up to
// ensure all new step events are regenerated as expected
newStepCh := subscribeToEvent(pb.cs.evsw, "replay-test", types.EventStringNewRoundStep(), 1)
if len(tokens) == 1 {
pb.replayReset(1, newStepCh)
} else {
i, err := strconv.Atoi(tokens[1])
if err != nil {
fmt.Println("back takes an integer argument")
} else if i > pb.count {
fmt.Printf("argument to back must not be larger than the current count (%d)\n", pb.count)
} else {
pb.replayReset(i, newStepCh)
}
}
case "rs":
// "rs" -> print entire round state
// "rs short" -> print height/round/step
// "rs <field>" -> print another field of the round state
rs := pb.cs.RoundState
if len(tokens) == 1 {
fmt.Println(rs)
} else {
switch tokens[1] {
case "short":
fmt.Printf("%v/%v/%v\n", rs.Height, rs.Round, rs.Step)
case "validators":
fmt.Println(rs.Validators)
case "proposal":
fmt.Println(rs.Proposal)
case "proposal_block":
fmt.Printf("%v %v\n", rs.ProposalBlockParts.StringShort(), rs.ProposalBlock.StringShort())
case "locked_round":
fmt.Println(rs.LockedRound)
case "locked_block":
fmt.Printf("%v %v\n", rs.LockedBlockParts.StringShort(), rs.LockedBlock.StringShort())
case "votes":
fmt.Println(rs.Votes.StringIndented(" "))
default:
fmt.Println("Unknown option", tokens[1])
}
}
case "n":
fmt.Println(pb.count)
}
}
return 0
}
//--------------------------------------------------------------------------------
// convenience for replay mode
func newConsensusStateForReplay(config cfg.BaseConfig, csConfig *cfg.ConsensusConfig) *ConsensusState {
// Get BlockStore
blockStoreDB := dbm.NewDB("blockstore", config.DBBackend, config.DBDir())
blockStore := bc.NewBlockStore(blockStoreDB)
// Get State
stateDB := dbm.NewDB("state", config.DBBackend, config.DBDir())
state := sm.MakeGenesisStateFromFile(stateDB, config.GenesisFile())
// Create proxyAppConn connection (consensus, mempool, query)
clientCreator := proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir())
proxyApp := proxy.NewAppConns(clientCreator, NewHandshaker(state, blockStore))
_, err := proxyApp.Start()
if err != nil {
cmn.Exit(cmn.Fmt("Error starting proxy app conns: %v", err))
}
// Make event switch
eventSwitch := types.NewEventSwitch()
if _, err := eventSwitch.Start(); err != nil {
cmn.Exit(cmn.Fmt("Failed to start event switch: %v", err))
}
consensusState := NewConsensusState(csConfig, state.Copy(), proxyApp.Consensus(), blockStore, types.MockMempool{})
consensusState.SetEventSwitch(eventSwitch)
return consensusState
}

View File

@@ -1,151 +1,220 @@
package consensus
import (
"bytes"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path"
"strings"
"testing"
"time"
. "github.com/tendermint/go-common"
"github.com/tendermint/go-events"
"github.com/tendermint/go-wire"
"github.com/tendermint/abci/example/dummy"
crypto "github.com/tendermint/go-crypto"
wire "github.com/tendermint/go-wire"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/proxy"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
"github.com/tendermint/tmlibs/log"
)
/*
The easiest way to generate this data is to copy ~/.tendermint_test/somedir/* to ~/.tendermint
and to run a local node.
Be sure to set the db to "leveldb" to create a cswal file in ~/.tendermint/data/cswal.
If you need to change the signatures, you can use a script as follows:
The privBytes comes from config/tendermint_test/...
```
package main
import (
"encoding/hex"
"fmt"
"github.com/tendermint/go-crypto"
)
func main() {
signBytes, err := hex.DecodeString("7B22636861696E5F6964223A2274656E6465726D696E745F74657374222C22766F7465223A7B22626C6F636B5F68617368223A2242453544373939433846353044354645383533364334333932464443384537423342313830373638222C22626C6F636B5F70617274735F686561646572223A506172745365747B543A31204236323237323535464632307D2C22686569676874223A312C22726F756E64223A302C2274797065223A327D7D")
if err != nil {
panic(err)
}
privBytes, err := hex.DecodeString("27F82582AEFAE7AB151CFB01C48BB6C1A0DA78F9BDDA979A9F70A84D074EB07D3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8")
if err != nil {
panic(err)
}
privKey := crypto.PrivKeyEd25519{}
copy(privKey[:], privBytes)
signature := privKey.Sign(signBytes)
signatureEd25519 := signature.(crypto.SignatureEd25519)
fmt.Printf("Signature Bytes: %X\n", signatureEd25519[:])
}
```
*/
var testLog = `{"time":"2016-04-03T11:23:54.387Z","msg":[3,{"duration":972835254,"height":1,"round":0,"step":1}]}
{"time":"2016-04-03T11:23:54.388Z","msg":[1,{"height":1,"round":0,"step":"RoundStepPropose"}]}
{"time":"2016-04-03T11:23:54.388Z","msg":[2,{"msg":[17,{"Proposal":{"height":1,"round":0,"block_parts_header":{"total":1,"hash":"3BA1E90CB868DA6B4FD7F3589826EC461E9EB4EF"},"pol_round":-1,"signature":"3A2ECD5023B21EC144EC16CFF1B992A4321317B83EEDD8969FDFEA6EB7BF4389F38DDA3E7BB109D63A07491C16277A197B241CF1F05F5E485C59882ECACD9E07"}}],"peer_key":""}]}
{"time":"2016-04-03T11:23:54.389Z","msg":[2,{"msg":[19,{"Height":1,"Round":0,"Part":{"index":0,"bytes":"0101010F74656E6465726D696E745F7465737401011441D59F4B718AC00000000000000114C4B01D3810579550997AC5641E759E20D99B51C10001000100","proof":{"aunts":[]}}}],"peer_key":""}]}
{"time":"2016-04-03T11:23:54.390Z","msg":[1,{"height":1,"round":0,"step":"RoundStepPrevote"}]}
{"time":"2016-04-03T11:23:54.390Z","msg":[2,{"msg":[20,{"ValidatorIndex":0,"Vote":{"height":1,"round":0,"type":1,"block_hash":"4291966B8A9DFBA00AEC7C700F2718E61DF4331D","block_parts_header":{"total":1,"hash":"3BA1E90CB868DA6B4FD7F3589826EC461E9EB4EF"},"signature":"47D2A75A4E2F15DB1F0D1B656AC0637AF9AADDFEB6A156874F6553C73895E5D5DC948DBAEF15E61276C5342D0E638DFCB77C971CD282096EA8735A564A90F008"}}],"peer_key":""}]}
{"time":"2016-04-03T11:23:54.392Z","msg":[1,{"height":1,"round":0,"step":"RoundStepPrecommit"}]}
{"time":"2016-04-03T11:23:54.392Z","msg":[2,{"msg":[20,{"ValidatorIndex":0,"Vote":{"height":1,"round":0,"type":2,"block_hash":"4291966B8A9DFBA00AEC7C700F2718E61DF4331D","block_parts_header":{"total":1,"hash":"3BA1E90CB868DA6B4FD7F3589826EC461E9EB4EF"},"signature":"39147DA595F08B73CF8C899967C8403B5872FD9042FFA4E239159E0B6C5D9665C9CA81D766EACA2AE658872F94C2FCD1E34BF51859CD5B274DA8512BACE4B50D"}}],"peer_key":""}]}
`
// map lines in the above wal to privVal step
var mapPrivValStep = map[int]int8{
0: 0,
1: 0,
2: 1,
3: 1,
4: 1,
5: 2,
6: 2,
7: 3,
func init() {
config = ResetConfig("consensus_replay_test")
}
func writeWAL(log string) string {
fmt.Println("writing", log)
// write the needed wal to file
f, err := ioutil.TempFile(os.TempDir(), "replay_test_")
if err != nil {
panic(err)
}
// These tests ensure we can always recover from failure at any part of the consensus process.
// There are two general failure scenarios: failure during consensus, and failure while applying the block.
// Only the latter interacts with the app and store,
// but the former has to deal with restrictions on re-use of priv_validator keys.
// The `WAL Tests` are for failures during the consensus;
// the `Handshake Tests` are for failures in applying the block.
// With the help of the WAL, we can recover from it all!
_, err = f.WriteString(log)
if err != nil {
panic(err)
}
name := f.Name()
f.Close()
return name
// NOTE: Files in this dir are generated by running the `build.sh` therein.
// It's a simple way to generate wals for a single block, or multiple blocks, with random transactions,
// and different part sizes. The output is not deterministic, and the stepChanges may need to be adjusted
// after running it (eg. sometimes small_block2 will have 5 block parts, sometimes 6).
// It should only have to be re-run if there is some breaking change to the consensus data structures (eg. blocks, votes)
// or to the behaviour of the app (eg. computes app hash differently)
var data_dir = path.Join(cmn.GoPath, "src/github.com/tendermint/tendermint/consensus", "test_data")
//------------------------------------------------------------------------------------------
// WAL Tests
// TODO: It would be better to verify explicitly which states we can recover from without the wal
// and which ones we need the wal for - then we'd also be able to only flush the
// wal writer when we need to, instead of with every message.
// the priv validator changes step at these lines for a block with 1 val and 1 part
var baseStepChanges = []int{3, 6, 8}
// test recovery from each line in each testCase
var testCases = []*testCase{
newTestCase("empty_block", baseStepChanges), // empty block (has 1 block part)
newTestCase("small_block1", baseStepChanges), // small block with txs in 1 block part
newTestCase("small_block2", []int{3, 11, 13}), // small block with txs across 6 smaller block parts
}
func waitForBlock(newBlockCh chan interface{}) {
type testCase struct {
name string
log string //full cs wal
stepMap map[int]int8 // map lines of log to privval step
proposeLine int
prevoteLine int
precommitLine int
}
func newTestCase(name string, stepChanges []int) *testCase {
if len(stepChanges) != 3 {
panic(cmn.Fmt("a full wal has 3 step changes! Got array %v", stepChanges))
}
return &testCase{
name: name,
log: readWAL(path.Join(data_dir, name+".cswal")),
stepMap: newMapFromChanges(stepChanges),
proposeLine: stepChanges[0],
prevoteLine: stepChanges[1],
precommitLine: stepChanges[2],
}
}
func newMapFromChanges(changes []int) map[int]int8 {
changes = append(changes, changes[2]+1) // so we add the last step change to the map
m := make(map[int]int8)
var count int
for changeNum, nextChange := range changes {
for ; count < nextChange; count++ {
m[count] = int8(changeNum)
}
}
return m
}
func readWAL(p string) string {
b, err := ioutil.ReadFile(p)
if err != nil {
panic(err)
}
return string(b)
}
func writeWAL(walMsgs string) string {
tempDir := os.TempDir()
walDir := path.Join(tempDir, "/wal"+cmn.RandStr(12))
walFile := path.Join(walDir, "wal")
// Create WAL directory
err := cmn.EnsureDir(walDir, 0700)
if err != nil {
panic(err)
}
// Write the needed WAL to file
err = cmn.WriteFile(walFile, []byte(walMsgs), 0600)
if err != nil {
panic(err)
}
return walFile
}
func waitForBlock(newBlockCh chan interface{}, thisCase *testCase, i int) {
after := time.After(time.Second * 10)
select {
case <-newBlockCh:
case <-after:
panic("Timed out waiting for new block")
panic(cmn.Fmt("Timed out waiting for new block for case '%s' line %d", thisCase.name, i))
}
}
func runReplayTest(t *testing.T, cs *ConsensusState, fileName string, newBlockCh chan interface{}) {
cs.config.Set("cswal", fileName)
cs.Start()
func runReplayTest(t *testing.T, cs *ConsensusState, walFile string, newBlockCh chan interface{},
thisCase *testCase, i int) {
cs.config.SetWalFile(walFile)
started, err := cs.Start()
if err != nil {
t.Fatalf("Cannot start consensus: %v", err)
}
if !started {
t.Error("Consensus did not start")
}
// Wait to make a new block.
// This is just a signal that we haven't halted; its not something contained in the WAL itself.
// Assuming the consensus state is running, replay of any WAL, including the empty one,
// should eventually be followed by a new block, or else something is wrong
waitForBlock(newBlockCh)
waitForBlock(newBlockCh, thisCase, i)
cs.evsw.Stop()
cs.Stop()
LOOP:
for {
select {
case <-newBlockCh:
default:
break LOOP
}
}
cs.Wait()
}
func setupReplayTest(nLines int, crashAfter bool) (*ConsensusState, chan interface{}, string, string) {
fmt.Println("-------------------------------------")
log.Notice(Fmt("Starting replay test of %d lines of WAL (crash before write)", nLines))
func toPV(pv PrivValidator) *types.PrivValidator {
return pv.(*types.PrivValidator)
}
func setupReplayTest(t *testing.T, thisCase *testCase, nLines int, crashAfter bool) (*ConsensusState, chan interface{}, string, string) {
t.Log("-------------------------------------")
t.Logf("Starting replay test %v (of %d lines of WAL). Crash after = %v", thisCase.name, nLines, crashAfter)
lineStep := nLines
if crashAfter {
lineStep -= 1
}
split := strings.Split(testLog, "\n")
split := strings.Split(thisCase.log, "\n")
lastMsg := split[nLines]
// we write those lines up to (not including) one with the signature
fileName := writeWAL(strings.Join(split[:nLines], "\n") + "\n")
walFile := writeWAL(strings.Join(split[:nLines], "\n") + "\n")
cs := fixedConsensusState()
cs := fixedConsensusStateDummy()
// set the last step according to when we crashed vs the wal
cs.privValidator.LastHeight = 1 // first block
cs.privValidator.LastStep = mapPrivValStep[lineStep]
toPV(cs.privValidator).LastHeight = 1 // first block
toPV(cs.privValidator).LastStep = thisCase.stepMap[lineStep]
fmt.Println("LAST STEP", cs.privValidator.LastStep)
t.Logf("[WARN] setupReplayTest LastStep=%v", toPV(cs.privValidator).LastStep)
newBlockCh := subscribeToEvent(cs.evsw, "tester", types.EventStringNewBlock(), 1)
return cs, newBlockCh, lastMsg, fileName
return cs, newBlockCh, lastMsg, walFile
}
func readTimedWALMessage(t *testing.T, walMsg string) TimedWALMessage {
var err error
var msg TimedWALMessage
wire.ReadJSON(&msg, []byte(walMsg), &err)
if err != nil {
t.Fatalf("Error reading json data: %v", err)
}
return msg
}
//-----------------------------------------------
// Test the log at every iteration, and set the privVal last step
// as if the log was written after signing, before the crash
func TestReplayCrashAfterWrite(t *testing.T) {
split := strings.Split(testLog, "\n")
func TestWALCrashAfterWrite(t *testing.T) {
for _, thisCase := range testCases {
split := strings.Split(thisCase.log, "\n")
for i := 0; i < len(split)-1; i++ {
cs, newBlockCh, _, f := setupReplayTest(i+1, true)
runReplayTest(t, cs, f, newBlockCh)
cs, newBlockCh, _, walFile := setupReplayTest(t, thisCase, i+1, true)
runReplayTest(t, cs, walFile, newBlockCh, thisCase, i+1)
}
}
}
@@ -153,51 +222,380 @@ func TestReplayCrashAfterWrite(t *testing.T) {
// Test the log as if we crashed after signing but before writing.
// This relies on privValidator.LastSignature being set
func TestReplayCrashBeforeWritePropose(t *testing.T) {
cs, newBlockCh, proposalMsg, f := setupReplayTest(2, false) // propose
// Set LastSig
var err error
var msg ConsensusLogMessage
wire.ReadJSON(&msg, []byte(proposalMsg), &err)
func TestWALCrashBeforeWritePropose(t *testing.T) {
for _, thisCase := range testCases {
lineNum := thisCase.proposeLine
// setup replay test where last message is a proposal
cs, newBlockCh, proposalMsg, walFile := setupReplayTest(t, thisCase, lineNum, false)
msg := readTimedWALMessage(t, proposalMsg)
proposal := msg.Msg.(msgInfo).Msg.(*ProposalMessage)
if err != nil {
t.Fatalf("Error reading json data: %v", err)
// Set LastSig
toPV(cs.privValidator).LastSignBytes = types.SignBytes(cs.state.ChainID, proposal.Proposal)
toPV(cs.privValidator).LastSignature = proposal.Proposal.Signature
runReplayTest(t, cs, walFile, newBlockCh, thisCase, lineNum)
}
cs.privValidator.LastSignBytes = types.SignBytes(cs.state.ChainID, proposal.Proposal)
cs.privValidator.LastSignature = proposal.Proposal.Signature
runReplayTest(t, cs, f, newBlockCh)
}
func TestReplayCrashBeforeWritePrevote(t *testing.T) {
cs, newBlockCh, voteMsg, f := setupReplayTest(5, false) // prevote
cs.evsw.AddListenerForEvent("tester", types.EventStringCompleteProposal(), func(data events.EventData) {
// Set LastSig
var err error
var msg ConsensusLogMessage
wire.ReadJSON(&msg, []byte(voteMsg), &err)
vote := msg.Msg.(msgInfo).Msg.(*VoteMessage)
if err != nil {
t.Fatalf("Error reading json data: %v", err)
func TestWALCrashBeforeWritePrevote(t *testing.T) {
for _, thisCase := range testCases {
testReplayCrashBeforeWriteVote(t, thisCase, thisCase.prevoteLine, types.EventStringCompleteProposal())
}
cs.privValidator.LastSignBytes = types.SignBytes(cs.state.ChainID, vote.Vote)
cs.privValidator.LastSignature = vote.Vote.Signature
})
runReplayTest(t, cs, f, newBlockCh)
}
func TestReplayCrashBeforeWritePrecommit(t *testing.T) {
cs, newBlockCh, voteMsg, f := setupReplayTest(7, false) // precommit
cs.evsw.AddListenerForEvent("tester", types.EventStringPolka(), func(data events.EventData) {
// Set LastSig
var err error
var msg ConsensusLogMessage
wire.ReadJSON(&msg, []byte(voteMsg), &err)
vote := msg.Msg.(msgInfo).Msg.(*VoteMessage)
if err != nil {
t.Fatalf("Error reading json data: %v", err)
func TestWALCrashBeforeWritePrecommit(t *testing.T) {
for _, thisCase := range testCases {
testReplayCrashBeforeWriteVote(t, thisCase, thisCase.precommitLine, types.EventStringPolka())
}
cs.privValidator.LastSignBytes = types.SignBytes(cs.state.ChainID, vote.Vote)
cs.privValidator.LastSignature = vote.Vote.Signature
})
runReplayTest(t, cs, f, newBlockCh)
}
func testReplayCrashBeforeWriteVote(t *testing.T, thisCase *testCase, lineNum int, eventString string) {
// setup replay test where last message is a vote
cs, newBlockCh, voteMsg, walFile := setupReplayTest(t, thisCase, lineNum, false)
types.AddListenerForEvent(cs.evsw, "tester", eventString, func(data types.TMEventData) {
msg := readTimedWALMessage(t, voteMsg)
vote := msg.Msg.(msgInfo).Msg.(*VoteMessage)
// Set LastSig
toPV(cs.privValidator).LastSignBytes = types.SignBytes(cs.state.ChainID, vote.Vote)
toPV(cs.privValidator).LastSignature = vote.Vote.Signature
})
runReplayTest(t, cs, walFile, newBlockCh, thisCase, lineNum)
}
//------------------------------------------------------------------------------------------
// Handshake Tests
var (
NUM_BLOCKS = 6 // number of blocks in the test_data/many_blocks.cswal
mempool = types.MockMempool{}
testPartSize int
)
//---------------------------------------
// Test handshake/replay
// 0 - all synced up
// 1 - saved block but app and state are behind
// 2 - save block and committed but state is behind
var modes = []uint{0, 1, 2}
// Sync from scratch
func TestHandshakeReplayAll(t *testing.T) {
for _, m := range modes {
testHandshakeReplay(t, 0, m)
}
}
// Sync many, not from scratch
func TestHandshakeReplaySome(t *testing.T) {
for _, m := range modes {
testHandshakeReplay(t, 1, m)
}
}
// Sync from lagging by one
func TestHandshakeReplayOne(t *testing.T) {
for _, m := range modes {
testHandshakeReplay(t, NUM_BLOCKS-1, m)
}
}
// Sync from caught up
func TestHandshakeReplayNone(t *testing.T) {
for _, m := range modes {
testHandshakeReplay(t, NUM_BLOCKS, m)
}
}
// Make some blocks. Start a fresh app and apply nBlocks blocks. Then restart the app and sync it up with the remaining blocks
func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) {
config := ResetConfig("proxy_test_")
// copy the many_blocks file
walBody, err := cmn.ReadFile(path.Join(data_dir, "many_blocks.cswal"))
if err != nil {
t.Fatal(err)
}
walFile := writeWAL(string(walBody))
config.Consensus.SetWalFile(walFile)
privVal := types.LoadPrivValidator(config.PrivValidatorFile())
testPartSize = config.Consensus.BlockPartSize
wal, err := NewWAL(walFile, false)
if err != nil {
t.Fatal(err)
}
wal.SetLogger(log.TestingLogger())
if _, err := wal.Start(); err != nil {
t.Fatal(err)
}
chain, commits, err := makeBlockchainFromWAL(wal)
if err != nil {
t.Fatalf(err.Error())
}
state, store := stateAndStore(config, privVal.PubKey)
store.chain = chain
store.commits = commits
// run the chain through state.ApplyBlock to build up the tendermint state
latestAppHash := buildTMStateFromChain(config, state, chain, mode)
// make a new client creator
dummyApp := dummy.NewPersistentDummyApplication(path.Join(config.DBDir(), "2"))
clientCreator2 := proxy.NewLocalClientCreator(dummyApp)
if nBlocks > 0 {
// run nBlocks against a new client to build up the app state.
// use a throwaway tendermint state
proxyApp := proxy.NewAppConns(clientCreator2, nil)
state, _ := stateAndStore(config, privVal.PubKey)
buildAppStateFromChain(proxyApp, state, chain, nBlocks, mode)
}
// now start the app using the handshake - it should sync
handshaker := NewHandshaker(state, store)
proxyApp := proxy.NewAppConns(clientCreator2, handshaker)
if _, err := proxyApp.Start(); err != nil {
t.Fatalf("Error starting proxy app connections: %v", err)
}
// get the latest app hash from the app
res, err := proxyApp.Query().InfoSync()
if err != nil {
t.Fatal(err)
}
// the app hash should be synced up
if !bytes.Equal(latestAppHash, res.LastBlockAppHash) {
t.Fatalf("Expected app hashes to match after handshake/replay. got %X, expected %X", res.LastBlockAppHash, latestAppHash)
}
expectedBlocksToSync := NUM_BLOCKS - nBlocks
if nBlocks == NUM_BLOCKS && mode > 0 {
expectedBlocksToSync += 1
} else if nBlocks > 0 && mode == 1 {
expectedBlocksToSync += 1
}
if handshaker.NBlocks() != expectedBlocksToSync {
t.Fatalf("Expected handshake to sync %d blocks, got %d", expectedBlocksToSync, handshaker.NBlocks())
}
}
func applyBlock(st *sm.State, blk *types.Block, proxyApp proxy.AppConns) {
err := st.ApplyBlock(nil, proxyApp.Consensus(), blk, blk.MakePartSet(testPartSize).Header(), mempool)
if err != nil {
panic(err)
}
}
func buildAppStateFromChain(proxyApp proxy.AppConns,
state *sm.State, chain []*types.Block, nBlocks int, mode uint) {
// start a new app without handshake, play nBlocks blocks
if _, err := proxyApp.Start(); err != nil {
panic(err)
}
validators := types.TM2PB.Validators(state.Validators)
proxyApp.Consensus().InitChainSync(validators)
defer proxyApp.Stop()
switch mode {
case 0:
for i := 0; i < nBlocks; i++ {
block := chain[i]
applyBlock(state, block, proxyApp)
}
case 1, 2:
for i := 0; i < nBlocks-1; i++ {
block := chain[i]
applyBlock(state, block, proxyApp)
}
if mode == 2 {
// update the dummy height and apphash
// as if we ran commit but not
applyBlock(state, chain[nBlocks-1], proxyApp)
}
}
}
func buildTMStateFromChain(config *cfg.Config, state *sm.State, chain []*types.Block, mode uint) []byte {
// run the whole chain against this client to build up the tendermint state
clientCreator := proxy.NewLocalClientCreator(dummy.NewPersistentDummyApplication(path.Join(config.DBDir(), "1")))
proxyApp := proxy.NewAppConns(clientCreator, nil) // sm.NewHandshaker(config, state, store, ReplayLastBlock))
if _, err := proxyApp.Start(); err != nil {
panic(err)
}
defer proxyApp.Stop()
validators := types.TM2PB.Validators(state.Validators)
proxyApp.Consensus().InitChainSync(validators)
var latestAppHash []byte
switch mode {
case 0:
// sync right up
for _, block := range chain {
applyBlock(state, block, proxyApp)
}
latestAppHash = state.AppHash
case 1, 2:
// sync up to the penultimate as if we stored the block.
// whether we commit or not depends on the appHash
for _, block := range chain[:len(chain)-1] {
applyBlock(state, block, proxyApp)
}
// apply the final block to a state copy so we can
// get the right next appHash but keep the state back
stateCopy := state.Copy()
applyBlock(stateCopy, chain[len(chain)-1], proxyApp)
latestAppHash = stateCopy.AppHash
}
return latestAppHash
}
//--------------------------
// utils for making blocks
func makeBlockchainFromWAL(wal *WAL) ([]*types.Block, []*types.Commit, error) {
// Search for height marker
gr, found, err := wal.group.Search("#ENDHEIGHT: ", makeHeightSearchFunc(0))
if err != nil {
return nil, nil, err
}
if !found {
return nil, nil, errors.New(cmn.Fmt("WAL does not contain height %d.", 1))
}
defer gr.Close()
// log.Notice("Build a blockchain by reading from the WAL")
var blockParts *types.PartSet
var blocks []*types.Block
var commits []*types.Commit
for {
line, err := gr.ReadLine()
if err != nil {
if err == io.EOF {
break
} else {
return nil, nil, err
}
}
piece, err := readPieceFromWAL([]byte(line))
if err != nil {
return nil, nil, err
}
if piece == nil {
continue
}
switch p := piece.(type) {
case *types.PartSetHeader:
// if its not the first one, we have a full block
if blockParts != nil {
var n int
block := wire.ReadBinary(&types.Block{}, blockParts.GetReader(), types.MaxBlockSize, &n, &err).(*types.Block)
blocks = append(blocks, block)
}
blockParts = types.NewPartSetFromHeader(*p)
case *types.Part:
_, err := blockParts.AddPart(p, false)
if err != nil {
return nil, nil, err
}
case *types.Vote:
if p.Type == types.VoteTypePrecommit {
commit := &types.Commit{
BlockID: p.BlockID,
Precommits: []*types.Vote{p},
}
commits = append(commits, commit)
}
}
}
// grab the last block too
var n int
block := wire.ReadBinary(&types.Block{}, blockParts.GetReader(), types.MaxBlockSize, &n, &err).(*types.Block)
blocks = append(blocks, block)
return blocks, commits, nil
}
func readPieceFromWAL(msgBytes []byte) (interface{}, error) {
// Skip over empty and meta lines
if len(msgBytes) == 0 || msgBytes[0] == '#' {
return nil, nil
}
var err error
var msg TimedWALMessage
wire.ReadJSON(&msg, msgBytes, &err)
if err != nil {
fmt.Println("MsgBytes:", msgBytes, string(msgBytes))
return nil, fmt.Errorf("Error reading json data: %v", err)
}
// for logging
switch m := msg.Msg.(type) {
case msgInfo:
switch msg := m.Msg.(type) {
case *ProposalMessage:
return &msg.Proposal.BlockPartsHeader, nil
case *BlockPartMessage:
return msg.Part, nil
case *VoteMessage:
return msg.Vote, nil
}
}
return nil, nil
}
// fresh state and mock store
func stateAndStore(config *cfg.Config, pubKey crypto.PubKey) (*sm.State, *mockBlockStore) {
stateDB := dbm.NewMemDB()
state := sm.MakeGenesisStateFromFile(stateDB, config.GenesisFile())
state.SetLogger(log.TestingLogger().With("module", "state"))
store := NewMockBlockStore(config)
return state, store
}
//----------------------------------
// mock block store
type mockBlockStore struct {
config *cfg.Config
chain []*types.Block
commits []*types.Commit
}
// TODO: NewBlockStore(db.NewMemDB) ...
func NewMockBlockStore(config *cfg.Config) *mockBlockStore {
return &mockBlockStore{config, nil, nil}
}
func (bs *mockBlockStore) Height() int { return len(bs.chain) }
func (bs *mockBlockStore) LoadBlock(height int) *types.Block { return bs.chain[height-1] }
func (bs *mockBlockStore) LoadBlockMeta(height int) *types.BlockMeta {
block := bs.chain[height-1]
return &types.BlockMeta{
BlockID: types.BlockID{block.Hash(), block.MakePartSet(bs.config.Consensus.BlockPartSize).Header()},
Header: block.Header,
}
}
func (bs *mockBlockStore) LoadBlockPart(height int, index int) *types.Part { return nil }
func (bs *mockBlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) {
}
func (bs *mockBlockStore) LoadBlockCommit(height int) *types.Commit {
return bs.commits[height-1]
}
func (bs *mockBlockStore) LoadSeenCommit(height int) *types.Commit {
return bs.commits[height-1]
}

File diff suppressed because it is too large Load Diff

View File

@@ -6,18 +6,16 @@ import (
"testing"
"time"
"github.com/tendermint/tendermint/config/tendermint_test"
//"github.com/tendermint/go-events"
. "github.com/tendermint/go-common"
"github.com/tendermint/tendermint/types"
. "github.com/tendermint/tmlibs/common"
)
func init() {
config = tendermint_test.ResetConfig("consensus_state_test")
config = ResetConfig("consensus_state_test")
}
func (tp *TimeoutParams) ensureProposeTimeout() time.Duration {
return time.Duration(tp.Propose0*2) * time.Millisecond
func ensureProposeTimeout(timeoutPropose int) time.Duration {
return time.Duration(timeoutPropose*2) * time.Millisecond
}
/*
@@ -31,7 +29,7 @@ x * TestBadProposal - 2 vals, bad proposal (bad block state hash), should prevot
FullRoundSuite
x * TestFullRound1 - 1 val, full successful round
x * TestFullRoundNil - 1 val, full round of nil
x * TestFullRound2 - 2 vals, both required for fuill round
x * TestFullRound2 - 2 vals, both required for full round
LockSuite
x * TestLockNoPOL - 2 vals, 4 rounds. one val locked, precommits nil every round except first.
x * TestLockPOLRelock - 4 vals, one precommits, other 3 polka at next round, so we unlock and precomit the polka
@@ -66,21 +64,21 @@ func TestProposerSelection0(t *testing.T) {
<-newRoundCh
// lets commit a block and ensure proposer for the next height is correct
prop := cs1.GetRoundState().Validators.Proposer()
if !bytes.Equal(prop.Address, cs1.privValidator.Address) {
panic(Fmt("expected proposer to be validator %d. Got %X", 0, prop.Address))
prop := cs1.GetRoundState().Validators.GetProposer()
if !bytes.Equal(prop.Address, cs1.privValidator.GetAddress()) {
t.Fatalf("expected proposer to be validator %d. Got %X", 0, prop.Address)
}
// wait for complete proposal
<-proposalCh
rs := cs1.GetRoundState()
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), nil, vss[1:]...)
signAddVotes(cs1, types.VoteTypePrecommit, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vss[1:]...)
// wait for new round so next validator is set
<-newRoundCh
prop = cs1.GetRoundState().Validators.Proposer()
prop = cs1.GetRoundState().Validators.GetProposer()
if !bytes.Equal(prop.Address, vss[1].Address) {
panic(Fmt("expected proposer to be validator %d. Got %X", 1, prop.Address))
}
@@ -101,13 +99,13 @@ func TestProposerSelection2(t *testing.T) {
// everyone just votes nil. we get a new proposer each round
for i := 0; i < len(vss); i++ {
prop := cs1.GetRoundState().Validators.Proposer()
prop := cs1.GetRoundState().Validators.GetProposer()
if !bytes.Equal(prop.Address, vss[(i+2)%len(vss)].Address) {
panic(Fmt("expected proposer to be validator %d. Got %X", (i+2)%len(vss), prop.Address))
}
rs := cs1.GetRoundState()
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, rs.ProposalBlockParts.Header(), nil, vss[1:]...)
signAddVotes(cs1, types.VoteTypePrecommit, nil, rs.ProposalBlockParts.Header(), vss[1:]...)
<-newRoundCh // wait for the new round event each round
incrementRound(vss[1:]...)
@@ -127,7 +125,7 @@ func TestEnterProposeNoPrivValidator(t *testing.T) {
startTestRound(cs, height, round)
// if we're not a validator, EnterPropose should timeout
ticker := time.NewTicker(cs.timeoutParams.ensureProposeTimeout())
ticker := time.NewTicker(ensureProposeTimeout(cs.config.TimeoutPropose))
select {
case <-timeoutCh:
case <-ticker.C:
@@ -168,7 +166,7 @@ func TestEnterProposeYesPrivValidator(t *testing.T) {
}
// if we're a validator, enterPropose should not timeout
ticker := time.NewTicker(cs.timeoutParams.ensureProposeTimeout())
ticker := time.NewTicker(ensureProposeTimeout(cs.config.TimeoutPropose))
select {
case <-timeoutCh:
panic("Expected EnterPropose not to timeout")
@@ -180,12 +178,14 @@ func TestEnterProposeYesPrivValidator(t *testing.T) {
func TestBadProposal(t *testing.T) {
cs1, vss := randConsensusState(2)
height, round := cs1.Height, cs1.Round
cs2 := vss[1]
vs2 := vss[1]
partSize := config.Consensus.BlockPartSize
proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1)
voteCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringVote(), 1)
propBlock, _ := cs1.createProposalBlock() //changeProposer(t, cs1, cs2)
propBlock, _ := cs1.createProposalBlock() //changeProposer(t, cs1, vs2)
// make the second validator the proposer by incrementing round
round = round + 1
@@ -198,10 +198,10 @@ func TestBadProposal(t *testing.T) {
}
stateHash[0] = byte((stateHash[0] + 1) % 255)
propBlock.AppHash = stateHash
propBlockParts := propBlock.MakePartSet()
proposal := types.NewProposal(cs2.Height, round, propBlockParts.Header(), -1)
if err := cs2.SignProposal(config.GetString("chain_id"), proposal); err != nil {
panic("failed to sign bad proposal: " + err.Error())
propBlockParts := propBlock.MakePartSet(partSize)
proposal := types.NewProposal(vs2.Height, round, propBlockParts.Header(), -1, types.BlockID{})
if err := vs2.SignProposal(config.ChainID, proposal); err != nil {
t.Fatal("failed to sign bad proposal", err)
}
// set the proposal block
@@ -218,14 +218,15 @@ func TestBadProposal(t *testing.T) {
validatePrevote(t, cs1, round, vss[0], nil)
// add bad prevote from cs2 and wait for it
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, propBlock.Hash(), propBlock.MakePartSet().Header(), voteCh)
// add bad prevote from vs2 and wait for it
signAddVotes(cs1, types.VoteTypePrevote, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2)
<-voteCh
// wait for precommit
<-voteCh
validatePrecommit(t, cs1, round, 0, vss[0], nil, nil)
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, propBlock.Hash(), propBlock.MakePartSet().Header(), voteCh)
signAddVotes(cs1, types.VoteTypePrecommit, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2)
}
//----------------------------------------------------------------------------------------------------
@@ -246,7 +247,7 @@ func TestFullRound1(t *testing.T) {
// grab proposal
re := <-propCh
propBlockHash := re.(types.EventDataRoundState).RoundState.(*RoundState).ProposalBlock.Hash()
propBlockHash := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState).ProposalBlock.Hash()
<-voteCh // wait for prevote
// NOTE: voteChan cap of 0 ensures we can complete this
@@ -282,7 +283,7 @@ func TestFullRoundNil(t *testing.T) {
// where the first validator has to wait for votes from the second
func TestFullRound2(t *testing.T) {
cs1, vss := randConsensusState(2)
cs2 := vss[1]
vs2 := vss[1]
height, round := cs1.Height, cs1.Round
voteCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringVote(), 1)
@@ -297,8 +298,9 @@ func TestFullRound2(t *testing.T) {
rs := cs1.GetRoundState()
propBlockHash, propPartsHeader := rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header()
// prevote arrives from cs2:
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, propBlockHash, propPartsHeader, voteCh)
// prevote arrives from vs2:
signAddVotes(cs1, types.VoteTypePrevote, propBlockHash, propPartsHeader, vs2)
<-voteCh
<-voteCh //precommit
@@ -307,8 +309,9 @@ func TestFullRound2(t *testing.T) {
// we should be stuck in limbo waiting for more precommits
// precommit arrives from cs2:
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, propBlockHash, propPartsHeader, voteCh)
// precommit arrives from vs2:
signAddVotes(cs1, types.VoteTypePrecommit, propBlockHash, propPartsHeader, vs2)
<-voteCh
// wait to finish commit, propose in next height
<-newBlockCh
@@ -321,9 +324,11 @@ func TestFullRound2(t *testing.T) {
// two vals take turns proposing. val1 locks on first one, precommits nil on everything else
func TestLockNoPOL(t *testing.T) {
cs1, vss := randConsensusState(2)
cs2 := vss[1]
vs2 := vss[1]
height := cs1.Height
partSize := config.Consensus.BlockPartSize
timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1)
timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1)
voteCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringVote(), 1)
@@ -339,14 +344,15 @@ func TestLockNoPOL(t *testing.T) {
cs1.startRoutines(0)
re := <-proposalCh
rs := re.(types.EventDataRoundState).RoundState.(*RoundState)
rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState)
theBlockHash := rs.ProposalBlock.Hash()
<-voteCh // prevote
// we should now be stuck in limbo forever, waiting for more prevotes
// prevote arrives from cs2:
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), voteCh)
// prevote arrives from vs2:
signAddVotes(cs1, types.VoteTypePrevote, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header(), vs2)
<-voteCh // prevote
<-voteCh // precommit
@@ -359,7 +365,8 @@ func TestLockNoPOL(t *testing.T) {
hash := make([]byte, len(theBlockHash))
copy(hash, theBlockHash)
hash[0] = byte((hash[0] + 1) % 255)
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, hash, rs.ProposalBlock.MakePartSet().Header(), voteCh)
signAddVotes(cs1, types.VoteTypePrecommit, hash, rs.ProposalBlock.MakePartSet(partSize).Header(), vs2)
<-voteCh // precommit
// (note we're entering precommit for a second time this round)
// but with invalid args. then we enterPrecommitWait, and the timeout to new round
@@ -368,16 +375,16 @@ func TestLockNoPOL(t *testing.T) {
///
<-newRoundCh
log.Notice("#### ONTO ROUND 1")
t.Log("#### ONTO ROUND 1")
/*
Round2 (cs1, B) // B B2
*/
incrementRound(cs2)
incrementRound(vs2)
// now we're on a new round and not the proposer, so wait for timeout
re = <-timeoutProposeCh
rs = re.(types.EventDataRoundState).RoundState.(*RoundState)
rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState)
if rs.ProposalBlock != nil {
panic("Expected proposal block to be nil")
@@ -390,7 +397,8 @@ func TestLockNoPOL(t *testing.T) {
validatePrevote(t, cs1, 1, vss[0], rs.LockedBlock.Hash())
// add a conflicting prevote from the other validator
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, hash, rs.ProposalBlock.MakePartSet().Header(), voteCh)
signAddVotes(cs1, types.VoteTypePrevote, hash, rs.ProposalBlock.MakePartSet(partSize).Header(), vs2)
<-voteCh
// now we're going to enter prevote again, but with invalid args
// and then prevote wait, which should timeout. then wait for precommit
@@ -402,24 +410,25 @@ func TestLockNoPOL(t *testing.T) {
// we should precommit nil and be locked on the proposal
validatePrecommit(t, cs1, 1, 0, vss[0], nil, theBlockHash)
// add conflicting precommit from cs2
// 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
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, hash, rs.ProposalBlock.MakePartSet().Header(), voteCh)
signAddVotes(cs1, types.VoteTypePrecommit, hash, rs.ProposalBlock.MakePartSet(partSize).Header(), vs2)
<-voteCh
// (note we're entering precommit for a second time this round, but with invalid args
// then we enterPrecommitWait and timeout into NewRound
<-timeoutWaitCh
<-newRoundCh
log.Notice("#### ONTO ROUND 2")
t.Log("#### ONTO ROUND 2")
/*
Round3 (cs2, _) // B, B2
Round3 (vs2, _) // B, B2
*/
incrementRound(cs2)
incrementRound(vs2)
re = <-proposalCh
rs = re.(types.EventDataRoundState).RoundState.(*RoundState)
rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState)
// now we're on a new round and are the proposer
if !bytes.Equal(rs.ProposalBlock.Hash(), rs.LockedBlock.Hash()) {
@@ -430,33 +439,36 @@ func TestLockNoPOL(t *testing.T) {
validatePrevote(t, cs1, 2, vss[0], rs.LockedBlock.Hash())
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, hash, rs.ProposalBlock.MakePartSet().Header(), voteCh)
signAddVotes(cs1, types.VoteTypePrevote, hash, rs.ProposalBlock.MakePartSet(partSize).Header(), vs2)
<-voteCh
<-timeoutWaitCh // prevote wait
<-voteCh // precommit
validatePrecommit(t, cs1, 2, 0, vss[0], nil, theBlockHash) // precommit nil but be locked on proposal
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, hash, rs.ProposalBlock.MakePartSet().Header(), voteCh) // NOTE: conflicting precommits at same height
signAddVotes(cs1, types.VoteTypePrecommit, hash, rs.ProposalBlock.MakePartSet(partSize).Header(), vs2) // NOTE: conflicting precommits at same height
<-voteCh
<-timeoutWaitCh
// before we time out into new round, set next proposal block
prop, propBlock := decideProposal(cs1, cs2, cs2.Height, cs2.Round+1)
prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
if prop == nil || propBlock == nil {
panic("Failed to create proposal block with cs2")
t.Fatal("Failed to create proposal block with vs2")
}
incrementRound(cs2)
incrementRound(vs2)
<-newRoundCh
log.Notice("#### ONTO ROUND 3")
t.Log("#### ONTO ROUND 3")
/*
Round4 (cs2, C) // B C // B C
Round4 (vs2, C) // B C // B C
*/
// now we're on a new round and not the proposer
// so set the proposal block
cs1.SetProposalAndBlock(prop, propBlock, propBlock.MakePartSet(), "")
cs1.SetProposalAndBlock(prop, propBlock, propBlock.MakePartSet(partSize), "")
<-proposalCh
<-voteCh // prevote
@@ -464,19 +476,24 @@ func TestLockNoPOL(t *testing.T) {
// prevote for locked block (not proposal)
validatePrevote(t, cs1, 0, vss[0], cs1.LockedBlock.Hash())
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, propBlock.Hash(), propBlock.MakePartSet().Header(), voteCh)
signAddVotes(cs1, types.VoteTypePrevote, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2)
<-voteCh
<-timeoutWaitCh
<-voteCh
validatePrecommit(t, cs1, 2, 0, vss[0], nil, theBlockHash) // precommit nil but locked on proposal
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, propBlock.Hash(), propBlock.MakePartSet().Header(), voteCh) // NOTE: conflicting precommits at same height
signAddVotes(cs1, types.VoteTypePrecommit, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2) // NOTE: conflicting precommits at same height
<-voteCh
}
// 4 vals, one precommits, other 3 polka at next round, so we unlock and precomit the polka
func TestLockPOLRelock(t *testing.T) {
cs1, vss := randConsensusState(4)
cs2, cs3, cs4 := vss[1], vss[2], vss[3]
vs2, vs3, vs4 := vss[1], vss[2], vss[3]
partSize := config.Consensus.BlockPartSize
timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1)
timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1)
@@ -485,14 +502,14 @@ func TestLockPOLRelock(t *testing.T) {
newRoundCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRound(), 1)
newBlockCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewBlockHeader(), 1)
log.Debug("cs2 last round", "lr", cs2.PrivValidator.LastRound)
t.Logf("vs2 last round %v", vs2.PrivValidator.LastRound)
// everything done from perspective of cs1
/*
Round1 (cs1, B) // B B B B// B nil B nil
eg. cs2 and cs4 didn't see the 2/3 prevotes
eg. vs2 and vs4 didn't see the 2/3 prevotes
*/
// start round and wait for propose and prevote
@@ -500,40 +517,43 @@ func TestLockPOLRelock(t *testing.T) {
<-newRoundCh
re := <-proposalCh
rs := re.(types.EventDataRoundState).RoundState.(*RoundState)
rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState)
theBlockHash := rs.ProposalBlock.Hash()
theBlockPartsHeader := rs.ProposalBlockParts.Header()
<-voteCh // prevote
signAddVoteToFromMany(types.VoteTypePrevote, cs1, theBlockHash, theBlockPartsHeader, voteCh, cs2, cs3, cs4)
signAddVotes(cs1, types.VoteTypePrevote, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header(), vs2, vs3, vs4)
// prevotes
discardFromChan(voteCh, 3)
<-voteCh // our precommit
// the proposed block should now be locked and our precommit added
validatePrecommit(t, cs1, 0, 0, vss[0], theBlockHash, theBlockHash)
// add precommits from the rest
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, types.PartSetHeader{}, voteCh, cs2, cs4)
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs3, theBlockHash, theBlockPartsHeader, voteCh)
signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2, vs4)
signAddVotes(cs1, types.VoteTypePrecommit, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header(), vs3)
// precommites
discardFromChan(voteCh, 3)
// before we timeout to the new round set the new proposal
prop, propBlock := decideProposal(cs1, cs2, cs2.Height, cs2.Round+1)
propBlockParts := propBlock.MakePartSet()
prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
propBlockParts := propBlock.MakePartSet(partSize)
propBlockHash := propBlock.Hash()
incrementRound(cs2, cs3, cs4)
incrementRound(vs2, vs3, vs4)
// timeout to new round
<-timeoutWaitCh
//XXX: this isnt gauranteed to get there before the timeoutPropose ...
//XXX: this isnt guaranteed to get there before the timeoutPropose ...
cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer")
<-newRoundCh
log.Notice("### ONTO ROUND 1")
t.Log("### ONTO ROUND 1")
/*
Round2 (cs2, C) // B C C C // C C C _)
Round2 (vs2, C) // B C C C // C C C _)
cs1 changes lock!
*/
@@ -551,7 +571,9 @@ func TestLockPOLRelock(t *testing.T) {
validatePrevote(t, cs1, 0, vss[0], theBlockHash)
// now lets add prevotes from everyone else for the new block
signAddVoteToFromMany(types.VoteTypePrevote, cs1, propBlockHash, propBlockParts.Header(), voteCh, cs2, cs3, cs4)
signAddVotes(cs1, types.VoteTypePrevote, propBlockHash, propBlockParts.Header(), vs2, vs3, vs4)
// prevotes
discardFromChan(voteCh, 3)
// now either we go to PrevoteWait or Precommit
select {
@@ -565,12 +587,13 @@ func TestLockPOLRelock(t *testing.T) {
// we should have unlocked and locked on the new block
validatePrecommit(t, cs1, 1, 1, vss[0], propBlockHash, propBlockHash)
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, propBlockHash, propBlockParts.Header(), voteCh, cs2, cs3)
signAddVotes(cs1, types.VoteTypePrecommit, propBlockHash, propBlockParts.Header(), vs2, vs3)
discardFromChan(voteCh, 2)
be := <-newBlockCh
b := be.(types.EventDataNewBlockHeader)
b := be.(types.TMEventData).Unwrap().(types.EventDataNewBlockHeader)
re = <-newRoundCh
rs = re.(types.EventDataRoundState).RoundState.(*RoundState)
rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState)
if rs.Height != 2 {
panic("Expected height to increment")
}
@@ -583,14 +606,16 @@ func TestLockPOLRelock(t *testing.T) {
// 4 vals, one precommits, other 3 polka at next round, so we unlock and precomit the polka
func TestLockPOLUnlock(t *testing.T) {
cs1, vss := randConsensusState(4)
cs2, cs3, cs4 := vss[1], vss[2], vss[3]
vs2, vs3, vs4 := vss[1], vss[2], vss[3]
partSize := config.Consensus.BlockPartSize
proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1)
timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1)
timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1)
newRoundCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRound(), 1)
unlockCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringUnlock(), 1)
voteCh := subscribeToVoter(cs1, cs1.privValidator.Address)
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
// everything done from perspective of cs1
@@ -604,12 +629,12 @@ func TestLockPOLUnlock(t *testing.T) {
startTestRound(cs1, cs1.Height, 0)
<-newRoundCh
re := <-proposalCh
rs := re.(types.EventDataRoundState).RoundState.(*RoundState)
rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState)
theBlockHash := rs.ProposalBlock.Hash()
<-voteCh // prevote
signAddVoteToFromMany(types.VoteTypePrevote, cs1, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), nil, cs2, cs3, cs4)
signAddVotes(cs1, types.VoteTypePrevote, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header(), vs2, vs3, vs4)
<-voteCh //precommit
@@ -619,27 +644,27 @@ func TestLockPOLUnlock(t *testing.T) {
rs = cs1.GetRoundState()
// add precommits from the rest
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, types.PartSetHeader{}, nil, cs2, cs4)
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs3, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), nil)
signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2, vs4)
signAddVotes(cs1, types.VoteTypePrecommit, cs1.ProposalBlock.Hash(), cs1.ProposalBlockParts.Header(), vs3)
// before we time out into new round, set next proposal block
prop, propBlock := decideProposal(cs1, cs2, cs2.Height, cs2.Round+1)
propBlockParts := propBlock.MakePartSet()
prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
propBlockParts := propBlock.MakePartSet(partSize)
incrementRound(cs2, cs3, cs4)
incrementRound(vs2, vs3, vs4)
// timeout to new round
re = <-timeoutWaitCh
rs = re.(types.EventDataRoundState).RoundState.(*RoundState)
rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState)
lockedBlockHash := rs.LockedBlock.Hash()
//XXX: this isnt gauranteed to get there before the timeoutPropose ...
//XXX: this isnt guaranteed to get there before the timeoutPropose ...
cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer")
<-newRoundCh
log.Notice("#### ONTO ROUND 1")
t.Log("#### ONTO ROUND 1")
/*
Round2 (cs2, C) // B nil nil nil // nil nil nil _
Round2 (vs2, C) // B nil nil nil // nil nil nil _
cs1 unlocks!
*/
@@ -656,7 +681,7 @@ func TestLockPOLUnlock(t *testing.T) {
<-voteCh
validatePrevote(t, cs1, 0, vss[0], lockedBlockHash)
// now lets add prevotes from everyone else for nil (a polka!)
signAddVoteToFromMany(types.VoteTypePrevote, cs1, nil, types.PartSetHeader{}, nil, cs2, cs3, cs4)
signAddVotes(cs1, types.VoteTypePrevote, nil, types.PartSetHeader{}, vs2, vs3, vs4)
// the polka makes us unlock and precommit nil
<-unlockCh
@@ -666,7 +691,7 @@ func TestLockPOLUnlock(t *testing.T) {
// NOTE: since we don't relock on nil, the lock round is 0
validatePrecommit(t, cs1, 1, 0, vss[0], nil, nil)
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, types.PartSetHeader{}, nil, cs2, cs3)
signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2, vs3)
<-newRoundCh
}
@@ -676,19 +701,21 @@ func TestLockPOLUnlock(t *testing.T) {
// then we see the polka from round 1 but shouldn't unlock
func TestLockPOLSafety1(t *testing.T) {
cs1, vss := randConsensusState(4)
cs2, cs3, cs4 := vss[1], vss[2], vss[3]
vs2, vs3, vs4 := vss[1], vss[2], vss[3]
partSize := config.Consensus.BlockPartSize
proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1)
timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1)
timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1)
newRoundCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRound(), 1)
voteCh := subscribeToVoter(cs1, cs1.privValidator.Address)
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
// start round and wait for propose and prevote
startTestRound(cs1, cs1.Height, 0)
<-newRoundCh
re := <-proposalCh
rs := re.(types.EventDataRoundState).RoundState.(*RoundState)
rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState)
propBlock := rs.ProposalBlock
<-voteCh // prevote
@@ -696,7 +723,7 @@ func TestLockPOLSafety1(t *testing.T) {
validatePrevote(t, cs1, 0, vss[0], propBlock.Hash())
// the others sign a polka but we don't see it
prevotes := signVoteMany(types.VoteTypePrevote, propBlock.Hash(), propBlock.MakePartSet().Header(), cs2, cs3, cs4)
prevotes := signVotes(types.VoteTypePrevote, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2, vs3, vs4)
// before we time out into new round, set next proposer
// and next proposal block
@@ -707,22 +734,22 @@ func TestLockPOLSafety1(t *testing.T) {
panic("failed to update validator")
}*/
log.Warn("old prop", "hash", fmt.Sprintf("%X", propBlock.Hash()))
t.Logf("old prop hash %v", fmt.Sprintf("%X", propBlock.Hash()))
// we do see them precommit nil
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, types.PartSetHeader{}, nil, cs2, cs3, cs4)
signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2, vs3, vs4)
prop, propBlock := decideProposal(cs1, cs2, cs2.Height, cs2.Round+1)
prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
propBlockHash := propBlock.Hash()
propBlockParts := propBlock.MakePartSet()
propBlockParts := propBlock.MakePartSet(partSize)
incrementRound(cs2, cs3, cs4)
incrementRound(vs2, vs3, vs4)
//XXX: this isnt gauranteed to get there before the timeoutPropose ...
//XXX: this isnt guaranteed to get there before the timeoutPropose ...
cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer")
<-newRoundCh
log.Notice("### ONTO ROUND 1")
t.Log("### ONTO ROUND 1")
/*Round2
// we timeout and prevote our lock
// a polka happened but we didn't see it!
@@ -736,33 +763,33 @@ func TestLockPOLSafety1(t *testing.T) {
re = <-proposalCh
}
rs = re.(types.EventDataRoundState).RoundState.(*RoundState)
rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState)
if rs.LockedBlock != nil {
panic("we should not be locked!")
}
log.Warn("new prop", "hash", fmt.Sprintf("%X", propBlockHash))
t.Logf("new prop hash %v", fmt.Sprintf("%X", propBlockHash))
// go to prevote, prevote for proposal block
<-voteCh
validatePrevote(t, cs1, 1, vss[0], propBlockHash)
// now we see the others prevote for it, so we should lock on it
signAddVoteToFromMany(types.VoteTypePrevote, cs1, propBlockHash, propBlockParts.Header(), nil, cs2, cs3, cs4)
signAddVotes(cs1, types.VoteTypePrevote, propBlockHash, propBlockParts.Header(), vs2, vs3, vs4)
<-voteCh // precommit
// we should have precommitted
validatePrecommit(t, cs1, 1, 1, vss[0], propBlockHash, propBlockHash)
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, types.PartSetHeader{}, nil, cs2, cs3)
signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2, vs3)
<-timeoutWaitCh
incrementRound(cs2, cs3, cs4)
incrementRound(vs2, vs3, vs4)
<-newRoundCh
log.Notice("### ONTO ROUND 2")
t.Log("### ONTO ROUND 2")
/*Round3
we see the polka from round 1 but we shouldn't unlock!
*/
@@ -779,9 +806,9 @@ func TestLockPOLSafety1(t *testing.T) {
newStepCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRoundStep(), 1)
// add prevotes from the earlier round
addVoteToFromMany(cs1, prevotes, cs2, cs3, cs4)
addVotes(cs1, prevotes...)
log.Warn("Done adding prevotes!")
t.Log("Done adding prevotes!")
ensureNoNewStep(newStepCh)
}
@@ -795,34 +822,37 @@ func TestLockPOLSafety1(t *testing.T) {
// dont see P0, lock on P1 at R1, dont unlock using P0 at R2
func TestLockPOLSafety2(t *testing.T) {
cs1, vss := randConsensusState(4)
cs2, cs3, cs4 := vss[1], vss[2], vss[3]
vs2, vs3, vs4 := vss[1], vss[2], vss[3]
partSize := config.Consensus.BlockPartSize
proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1)
timeoutProposeCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutPropose(), 1)
timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1)
newRoundCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRound(), 1)
unlockCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringUnlock(), 1)
voteCh := subscribeToVoter(cs1, cs1.privValidator.Address)
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
// the block for R0: gets polkad but we miss it
// (even though we signed it, shhh)
_, propBlock0 := decideProposal(cs1, vss[0], cs1.Height, cs1.Round)
propBlockHash0 := propBlock0.Hash()
propBlockParts0 := propBlock0.MakePartSet()
propBlockParts0 := propBlock0.MakePartSet(partSize)
// the others sign a polka but we don't see it
prevotes := signVoteMany(types.VoteTypePrevote, propBlockHash0, propBlockParts0.Header(), cs2, cs3, cs4)
prevotes := signVotes(types.VoteTypePrevote, propBlockHash0, propBlockParts0.Header(), vs2, vs3, vs4)
// the block for round 1
prop1, propBlock1 := decideProposal(cs1, cs2, cs2.Height, cs2.Round+1)
prop1, propBlock1 := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
propBlockHash1 := propBlock1.Hash()
propBlockParts1 := propBlock1.MakePartSet()
propBlockParts1 := propBlock1.MakePartSet(partSize)
propBlockID1 := types.BlockID{propBlockHash1, propBlockParts1.Header()}
incrementRound(cs2, cs3, cs4)
incrementRound(vs2, vs3, vs4)
cs1.updateRoundStep(0, RoundStepPrecommitWait)
log.Notice("### ONTO Round 1")
t.Log("### ONTO Round 1")
// jump in at round 1
height := cs1.Height
startTestRound(cs1, height, 1)
@@ -833,31 +863,33 @@ func TestLockPOLSafety2(t *testing.T) {
<-voteCh // prevote
signAddVoteToFromMany(types.VoteTypePrevote, cs1, propBlockHash1, propBlockParts1.Header(), nil, cs2, cs3, cs4)
signAddVotes(cs1, types.VoteTypePrevote, propBlockHash1, propBlockParts1.Header(), vs2, vs3, vs4)
<-voteCh // precommit
// the proposed block should now be locked and our precommit added
validatePrecommit(t, cs1, 1, 1, vss[0], propBlockHash1, propBlockHash1)
// add precommits from the rest
signAddVoteToFromMany(types.VoteTypePrecommit, cs1, nil, types.PartSetHeader{}, nil, cs2, cs4)
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs3, propBlockHash1, propBlockParts1.Header(), nil)
signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2, vs4)
signAddVotes(cs1, types.VoteTypePrecommit, propBlockHash1, propBlockParts1.Header(), vs3)
incrementRound(cs2, cs3, cs4)
incrementRound(vs2, vs3, vs4)
// timeout of precommit wait to new round
<-timeoutWaitCh
// in round 2 we see the polkad block from round 0
newProp := types.NewProposal(height, 2, propBlockParts0.Header(), 0)
if err := cs3.SignProposal(config.GetString("chain_id"), newProp); err != nil {
panic(err)
newProp := types.NewProposal(height, 2, propBlockParts0.Header(), 0, propBlockID1)
if err := vs3.SignProposal(config.ChainID, newProp); err != nil {
t.Fatal(err)
}
cs1.SetProposalAndBlock(newProp, propBlock0, propBlockParts0, "some peer")
addVoteToFromMany(cs1, prevotes, cs2, cs3, cs4) // add the pol votes
// Add the pol votes
addVotes(cs1, prevotes...)
<-newRoundCh
log.Notice("### ONTO Round 2")
t.Log("### ONTO Round 2")
/*Round2
// now we see the polka from round 1, but we shouldnt unlock
*/
@@ -885,13 +917,13 @@ func TestLockPOLSafety2(t *testing.T) {
/*
func TestSlashingPrevotes(t *testing.T) {
cs1, vss := randConsensusState(2)
cs2 := vss[1]
vs2 := vss[1]
proposalCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringCompleteProposal() , 1)
timeoutWaitCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringTimeoutWait() , 1)
newRoundCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringNewRound() , 1)
voteCh := subscribeToVoter(cs1, cs1.privValidator.Address)
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
// start round and wait for propose and prevote
startTestRound(cs1, cs1.Height, 0)
@@ -905,7 +937,7 @@ func TestSlashingPrevotes(t *testing.T) {
// add one for a different block should cause us to go into prevote wait
hash := rs.ProposalBlock.Hash()
hash[0] = byte(hash[0]+1) % 255
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, hash, rs.ProposalBlockParts.Header(), nil)
signAddVotes(cs1, types.VoteTypePrevote, hash, rs.ProposalBlockParts.Header(), vs2)
<-timeoutWaitCh
@@ -913,20 +945,20 @@ func TestSlashingPrevotes(t *testing.T) {
// away and ignore more prevotes (and thus fail to slash!)
// add the conflicting vote
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(),nil)
signAddVotes(cs1, types.VoteTypePrevote, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vs2)
// XXX: Check for existence of Dupeout info
}
func TestSlashingPrecommits(t *testing.T) {
cs1, vss := randConsensusState(2)
cs2 := vss[1]
vs2 := vss[1]
proposalCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringCompleteProposal() , 1)
timeoutWaitCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringTimeoutWait() , 1)
newRoundCh := subscribeToEvent(cs1.evsw,"tester",types.EventStringNewRound() , 1)
voteCh := subscribeToVoter(cs1, cs1.privValidator.Address)
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
// start round and wait for propose and prevote
startTestRound(cs1, cs1.Height, 0)
@@ -934,8 +966,8 @@ func TestSlashingPrecommits(t *testing.T) {
re := <-proposalCh
<-voteCh // prevote
// add prevote from cs2
signAddVoteToFrom(types.VoteTypePrevote, cs1, cs2, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), nil)
// add prevote from vs2
signAddVotes(cs1, types.VoteTypePrevote, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vs2)
<-voteCh // precommit
@@ -943,13 +975,13 @@ func TestSlashingPrecommits(t *testing.T) {
// add one for a different block should cause us to go into prevote wait
hash := rs.ProposalBlock.Hash()
hash[0] = byte(hash[0]+1) % 255
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, hash, rs.ProposalBlockParts.Header(),nil)
signAddVotes(cs1, types.VoteTypePrecommit, hash, rs.ProposalBlockParts.Header(), vs2)
// NOTE: we have to send the vote for different block first so we don't just go into precommit round right
// away and ignore more prevotes (and thus fail to slash!)
// add precommit from cs2
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(),nil)
// add precommit from vs2
signAddVotes(cs1, types.VoteTypePrecommit, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vs2)
// XXX: Check for existence of Dupeout info
}
@@ -965,44 +997,46 @@ func TestSlashingPrecommits(t *testing.T) {
// we receive a final precommit after going into next round, but others might have gone to commit already!
func TestHalt1(t *testing.T) {
cs1, vss := randConsensusState(4)
cs2, cs3, cs4 := vss[1], vss[2], vss[3]
vs2, vs3, vs4 := vss[1], vss[2], vss[3]
partSize := config.Consensus.BlockPartSize
proposalCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringCompleteProposal(), 1)
timeoutWaitCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringTimeoutWait(), 1)
newRoundCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewRound(), 1)
newBlockCh := subscribeToEvent(cs1.evsw, "tester", types.EventStringNewBlock(), 1)
voteCh := subscribeToVoter(cs1, cs1.privValidator.Address)
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
// start round and wait for propose and prevote
startTestRound(cs1, cs1.Height, 0)
<-newRoundCh
re := <-proposalCh
rs := re.(types.EventDataRoundState).RoundState.(*RoundState)
rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState)
propBlock := rs.ProposalBlock
propBlockParts := propBlock.MakePartSet()
propBlockParts := propBlock.MakePartSet(partSize)
<-voteCh // prevote
signAddVoteToFromMany(types.VoteTypePrevote, cs1, propBlock.Hash(), propBlockParts.Header(), nil, cs3, cs4)
signAddVotes(cs1, types.VoteTypePrevote, propBlock.Hash(), propBlockParts.Header(), vs3, vs4)
<-voteCh // precommit
// the proposed block should now be locked and our precommit added
validatePrecommit(t, cs1, 0, 0, vss[0], propBlock.Hash(), propBlock.Hash())
// add precommits from the rest
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs2, nil, types.PartSetHeader{}, nil) // didnt receive proposal
signAddVoteToFrom(types.VoteTypePrecommit, cs1, cs3, propBlock.Hash(), propBlockParts.Header(), nil)
// we receive this later, but cs3 might receive it earlier and with ours will go to commit!
precommit4 := signVote(cs4, types.VoteTypePrecommit, propBlock.Hash(), propBlockParts.Header())
signAddVotes(cs1, types.VoteTypePrecommit, nil, types.PartSetHeader{}, vs2) // didnt receive proposal
signAddVotes(cs1, types.VoteTypePrecommit, propBlock.Hash(), propBlockParts.Header(), vs3)
// we receive this later, but vs3 might receive it earlier and with ours will go to commit!
precommit4 := signVote(vs4, types.VoteTypePrecommit, propBlock.Hash(), propBlockParts.Header())
incrementRound(cs2, cs3, cs4)
incrementRound(vs2, vs3, vs4)
// timeout to new round
<-timeoutWaitCh
re = <-newRoundCh
rs = re.(types.EventDataRoundState).RoundState.(*RoundState)
rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState)
log.Notice("### ONTO ROUND 1")
t.Log("### ONTO ROUND 1")
/*Round2
// we timeout and prevote our lock
// a polka happened but we didn't see it!
@@ -1013,12 +1047,12 @@ func TestHalt1(t *testing.T) {
validatePrevote(t, cs1, 0, vss[0], rs.LockedBlock.Hash())
// now we receive the precommit from the previous round
addVoteToFrom(cs1, cs4, precommit4)
addVotes(cs1, precommit4)
// receiving that precommit should take us straight to commit
<-newBlockCh
re = <-newRoundCh
rs = re.(types.EventDataRoundState).RoundState.(*RoundState)
rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState)
if rs.Height != 2 {
panic("expected height to increment")

View File

@@ -0,0 +1,36 @@
# Generating test data
To generate the data, run `build.sh`. See that script for more details.
Make sure to adjust the stepChanges in the testCases if the number of messages changes.
This sometimes happens for the `small_block2.cswal`, where the number of block parts changes between 4 and 5.
If you need to change the signatures, you can use a script as follows:
The privBytes comes from `config/tendermint_test/...`:
```
package main
import (
"encoding/hex"
"fmt"
"github.com/tendermint/go-crypto"
)
func main() {
signBytes, err := hex.DecodeString("7B22636861696E5F6964223A2274656E6465726D696E745F74657374222C22766F7465223A7B22626C6F636B5F68617368223A2242453544373939433846353044354645383533364334333932464443384537423342313830373638222C22626C6F636B5F70617274735F686561646572223A506172745365747B543A31204236323237323535464632307D2C22686569676874223A312C22726F756E64223A302C2274797065223A327D7D")
if err != nil {
panic(err)
}
privBytes, err := hex.DecodeString("27F82582AEFAE7AB151CFB01C48BB6C1A0DA78F9BDDA979A9F70A84D074EB07D3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8")
if err != nil {
panic(err)
}
privKey := crypto.PrivKeyEd25519{}
copy(privKey[:], privBytes)
signature := privKey.Sign(signBytes)
fmt.Printf("Signature Bytes: %X\n", signature.Bytes())
}
```

110
consensus/test_data/build.sh Executable file
View File

@@ -0,0 +1,110 @@
#!/usr/bin/env bash
# XXX: removes tendermint dir
cd "$GOPATH/src/github.com/tendermint/tendermint" || exit 1
# Make sure we have a tendermint command.
if ! hash tendermint 2>/dev/null; then
make install
fi
# specify a dir to copy
# TODO: eventually we should replace with `tendermint init --test`
DIR_TO_COPY=$HOME/.tendermint_test/consensus_state_test
TMHOME="$HOME/.tendermint"
rm -rf "$TMHOME"
cp -r "$DIR_TO_COPY" "$TMHOME"
cp $TMHOME/config.toml $TMHOME/config.toml.bak
function reset(){
tendermint unsafe_reset_all
cp $TMHOME/config.toml.bak $TMHOME/config.toml
}
reset
# empty block
function empty_block(){
tendermint node --proxy_app=persistent_dummy &> /dev/null &
sleep 5
killall tendermint
# /q would print up to and including the match, then quit.
# /Q doesn't include the match.
# http://unix.stackexchange.com/questions/11305/grep-show-all-the-file-up-to-the-match
sed '/ENDHEIGHT: 1/Q' ~/.tendermint/data/cs.wal/wal > consensus/test_data/empty_block.cswal
reset
}
# many blocks
function many_blocks(){
bash scripts/txs/random.sh 1000 36657 &> /dev/null &
PID=$!
tendermint node --proxy_app=persistent_dummy &> /dev/null &
sleep 7
killall tendermint
kill -9 $PID
sed '/ENDHEIGHT: 6/Q' ~/.tendermint/data/cs.wal/wal > consensus/test_data/many_blocks.cswal
reset
}
# small block 1
function small_block1(){
bash scripts/txs/random.sh 1000 36657 &> /dev/null &
PID=$!
tendermint node --proxy_app=persistent_dummy &> /dev/null &
sleep 10
killall tendermint
kill -9 $PID
sed '/ENDHEIGHT: 1/Q' ~/.tendermint/data/cs.wal/wal > consensus/test_data/small_block1.cswal
reset
}
# small block 2 (part size = 512)
function small_block2(){
echo "" >> ~/.tendermint/config.toml
echo "block_part_size = 512" >> ~/.tendermint/config.toml
bash scripts/txs/random.sh 1000 36657 &> /dev/null &
PID=$!
tendermint node --proxy_app=persistent_dummy &> /dev/null &
sleep 5
killall tendermint
kill -9 $PID
sed '/ENDHEIGHT: 1/Q' ~/.tendermint/data/cs.wal/wal > consensus/test_data/small_block2.cswal
reset
}
case "$1" in
"small_block1")
small_block1
;;
"small_block2")
small_block2
;;
"empty_block")
empty_block
;;
"many_blocks")
many_blocks
;;
*)
small_block1
small_block2
empty_block
many_blocks
esac

View File

@@ -0,0 +1,10 @@
#ENDHEIGHT: 0
{"time":"2017-04-27T22:24:01.346Z","msg":[3,{"duration":972946821,"height":1,"round":0,"step":1}]}
{"time":"2017-04-27T22:24:01.349Z","msg":[1,{"height":1,"round":0,"step":"RoundStepPropose"}]}
{"time":"2017-04-27T22:24:01.349Z","msg":[2,{"msg":[17,{"Proposal":{"height":1,"round":0,"block_parts_header":{"total":1,"hash":"ACED4A95DDEBD24E66A681F7EAB4CA22C4B8546D"},"pol_round":-1,"pol_block_id":{"hash":"","parts":{"total":0,"hash":""}},"signature":[1,"E785764AED6D92D7CC65C0A3A4ED9C8465198A05142C3E6C7F3EF601FDCD3A604900B77B7B87C046221EF99FD038A960398385BD5BBAA50EE4F86DE757B8F704"]}}],"peer_key":""}]}
{"time":"2017-04-27T22:24:01.350Z","msg":[2,{"msg":[19,{"Height":1,"Round":0,"Part":{"index":0,"bytes":"0101010F74656E6465726D696E745F74657374010114B96165CF4496C00000000000000114354594CBFC1A7BCA1AD0050ED6AA010023EADA390001000100000000","proof":{"aunts":[]}}}],"peer_key":""}]}
{"time":"2017-04-27T22:24:01.351Z","msg":[1,{"height":1,"round":0,"step":"RoundStepPrevote"}]}
{"time":"2017-04-27T22:24:01.351Z","msg":[2,{"msg":[20,{"Vote":{"validator_address":"D028C9981F7A87F3093672BF0D5B0E2A1B3ED456","validator_index":0,"height":1,"round":0,"type":1,"block_id":{"hash":"F3BBFBE7E4A5D619E2C498C3D1B912883786DD71","parts":{"total":1,"hash":"ACED4A95DDEBD24E66A681F7EAB4CA22C4B8546D"}},"signature":[1,"35C937C78D061ECDC3770982A1330C9AA7F6FEF00835C43DEB50B8FCF69A3EEF221E675EE5E469114F64E4FBBABA414EB9170E1025FC47D3F0EADE46767D2E00"]}}],"peer_key":""}]}
{"time":"2017-04-27T22:24:01.352Z","msg":[1,{"height":1,"round":0,"step":"RoundStepPrecommit"}]}
{"time":"2017-04-27T22:24:01.352Z","msg":[2,{"msg":[20,{"Vote":{"validator_address":"D028C9981F7A87F3093672BF0D5B0E2A1B3ED456","validator_index":0,"height":1,"round":0,"type":2,"block_id":{"hash":"F3BBFBE7E4A5D619E2C498C3D1B912883786DD71","parts":{"total":1,"hash":"ACED4A95DDEBD24E66A681F7EAB4CA22C4B8546D"}},"signature":[1,"D1A7D27FCD5D352F3A3EDA8DE368520BC5B796662E32BCD8D91CDB8209A88DAF37CB7C4C93143D3C12B37C1435229268098CFFD0AD1400D88DA7606454692301"]}}],"peer_key":""}]}
{"time":"2017-04-27T22:24:01.352Z","msg":[1,{"height":1,"round":0,"step":"RoundStepCommit"}]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,15 @@
#ENDHEIGHT: 0
{"time":"2017-04-27T22:23:56.310Z","msg":[3,{"duration":969732098,"height":1,"round":0,"step":1}]}
{"time":"2017-04-27T22:23:56.312Z","msg":[1,{"height":1,"round":0,"step":"RoundStepPropose"}]}
{"time":"2017-04-27T22:23:56.312Z","msg":[2,{"msg":[17,{"Proposal":{"height":1,"round":0,"block_parts_header":{"total":6,"hash":"A3C176F13F5CBC7C48EE27A472800410C9D487DC"},"pol_round":-1,"pol_block_id":{"hash":"","parts":{"total":0,"hash":""}},"signature":[1,"7624F6E943B7A207E16D1FA87EA099BD924E930F98E7DECBC01DB37735C619409588A67C2EABA9845FD6B80FDB65ECFCDA5F0DEFCEF74B8C34DB8E0540480203"]}}],"peer_key":""}]}
{"time":"2017-04-27T22:23:56.312Z","msg":[2,{"msg":[19,{"Height":1,"Round":0,"Part":{"index":0,"bytes":"0101010F74656E6465726D696E745F74657374010114B96164A30A118001620000000001141F6753D22BACA2180B1EADD722434EB28444D91D0114354594CBFC1A7BCA1AD0050ED6AA010023EADA3900010162011A3631363236333634333133303344363436333632363133313330011A3631363236333634333133313344363436333632363133313331011A3631363236333634333133323344363436333632363133313332011A3631363236333634333133333344363436333632363133313333011A3631363236333634333133343344363436333632363133313334011A3631363236333634333133353344363436333632363133313335011A3631363236333634333133363344363436333632363133313336011A3631363236333634333133373344363436333632363133313337011A3631363236333634333133383344363436333632363133313338011A3631363236333634333133393344363436333632363133313339011A3631363236333634333233303344363436333632363133323330011A3631363236333634333233313344363436333632363133323331011A3631363236333634333233323344363436333632363133323332011A3631363236333634333233333344363436333632363133323333011A3631363236333634333233343344363436333632363133323334011A36313632363336","proof":{"aunts":["49F4B71E3D7C457415069E2EA916DB12F67AA8D0","D35A72BEDAAAAC17045D7BFAAFA94C2EC0B0A4C2","705BC647374F3495EE73C3F44C21E9BDB4731738"]}}}],"peer_key":""}]}
{"time":"2017-04-27T22:23:56.312Z","msg":[2,{"msg":[19,{"Height":1,"Round":0,"Part":{"index":1,"bytes":"34333233353344363436333632363133323335011A3631363236333634333233363344363436333632363133323336011A3631363236333634333233373344363436333632363133323337011A3631363236333634333233383344363436333632363133323338011A3631363236333634333233393344363436333632363133323339011A3631363236333634333333303344363436333632363133333330011A3631363236333634333333313344363436333632363133333331011A3631363236333634333333323344363436333632363133333332011A3631363236333634333333333344363436333632363133333333011A3631363236333634333333343344363436333632363133333334011A3631363236333634333333353344363436333632363133333335011A3631363236333634333333363344363436333632363133333336011A3631363236333634333333373344363436333632363133333337011A3631363236333634333333383344363436333632363133333338011A3631363236333634333333393344363436333632363133333339011A3631363236333634333433303344363436333632363133343330011A3631363236333634333433313344363436333632363133343331011A3631363236333634333433323344363436333632363133343332011A363136323633363433343333334436","proof":{"aunts":["5AD2A9A1A49A1FD6EF83F05FA4588F800B29DEF1","D35A72BEDAAAAC17045D7BFAAFA94C2EC0B0A4C2","705BC647374F3495EE73C3F44C21E9BDB4731738"]}}}],"peer_key":""}]}
{"time":"2017-04-27T22:23:56.312Z","msg":[2,{"msg":[19,{"Height":1,"Round":0,"Part":{"index":2,"bytes":"3436333632363133343333011A3631363236333634333433343344363436333632363133343334011A3631363236333634333433353344363436333632363133343335011A3631363236333634333433363344363436333632363133343336011A3631363236333634333433373344363436333632363133343337011A3631363236333634333433383344363436333632363133343338011A3631363236333634333433393344363436333632363133343339011A3631363236333634333533303344363436333632363133353330011A3631363236333634333533313344363436333632363133353331011A3631363236333634333533323344363436333632363133353332011A3631363236333634333533333344363436333632363133353333011A3631363236333634333533343344363436333632363133353334011A3631363236333634333533353344363436333632363133353335011A3631363236333634333533363344363436333632363133353336011A3631363236333634333533373344363436333632363133353337011A3631363236333634333533383344363436333632363133353338011A3631363236333634333533393344363436333632363133353339011A3631363236333634333633303344363436333632363133363330011A3631363236333634333633313344363436333632363133","proof":{"aunts":["8B5786C3D871EE37B0F4B2DECAC39E157340DFBE","705BC647374F3495EE73C3F44C21E9BDB4731738"]}}}],"peer_key":""}]}
{"time":"2017-04-27T22:23:56.312Z","msg":[2,{"msg":[19,{"Height":1,"Round":0,"Part":{"index":3,"bytes":"363331011A3631363236333634333633323344363436333632363133363332011A3631363236333634333633333344363436333632363133363333011A3631363236333634333633343344363436333632363133363334011A3631363236333634333633353344363436333632363133363335011A3631363236333634333633363344363436333632363133363336011A3631363236333634333633373344363436333632363133363337011A3631363236333634333633383344363436333632363133363338011A3631363236333634333633393344363436333632363133363339011A3631363236333634333733303344363436333632363133373330011A3631363236333634333733313344363436333632363133373331011A3631363236333634333733323344363436333632363133373332011A3631363236333634333733333344363436333632363133373333011A3631363236333634333733343344363436333632363133373334011A3631363236333634333733353344363436333632363133373335011A3631363236333634333733363344363436333632363133373336011A3631363236333634333733373344363436333632363133373337011A3631363236333634333733383344363436333632363133373338011A3631363236333634333733393344363436333632363133373339011A363136","proof":{"aunts":["56097661A1B2707588100586B3B1C2C8A51057D1","6DE889147DF528EEB5F7422E95DC45900CAFB619","247C721D5CEB90BB1FE389BA74C43DF0955E1647"]}}}],"peer_key":""}]}
{"time":"2017-04-27T22:23:56.312Z","msg":[2,{"msg":[19,{"Height":1,"Round":0,"Part":{"index":4,"bytes":"3236333634333833303344363436333632363133383330011A3631363236333634333833313344363436333632363133383331011A3631363236333634333833323344363436333632363133383332011A3631363236333634333833333344363436333632363133383333011A3631363236333634333833343344363436333632363133383334011A3631363236333634333833353344363436333632363133383335011A3631363236333634333833363344363436333632363133383336011A3631363236333634333833373344363436333632363133383337011A3631363236333634333833383344363436333632363133383338011A3631363236333634333833393344363436333632363133383339011A3631363236333634333933303344363436333632363133393330011A3631363236333634333933313344363436333632363133393331011A3631363236333634333933323344363436333632363133393332011A3631363236333634333933333344363436333632363133393333011A3631363236333634333933343344363436333632363133393334011A3631363236333634333933353344363436333632363133393335011A3631363236333634333933363344363436333632363133393336011A3631363236333634333933373344363436333632363133393337011A3631363236333634333933","proof":{"aunts":["081D3DC5F11850851D5F0D760B98EE87BFA6B8B0","6DE889147DF528EEB5F7422E95DC45900CAFB619","247C721D5CEB90BB1FE389BA74C43DF0955E1647"]}}}],"peer_key":""}]}
{"time":"2017-04-27T22:23:56.313Z","msg":[2,{"msg":[19,{"Height":1,"Round":0,"Part":{"index":5,"bytes":"383344363436333632363133393338011A3631363236333634333933393344363436333632363133393339011E363136323633363433313330333033443634363336323631333133303330011E363136323633363433313330333133443634363336323631333133303331011E363136323633363433313330333233443634363336323631333133303332011E363136323633363433313330333333443634363336323631333133303333011E363136323633363433313330333433443634363336323631333133303334011E363136323633363433313330333533443634363336323631333133303335011E363136323633363433313330333633443634363336323631333133303336011E3631363236333634333133303337334436343633363236313331333033370100000000","proof":{"aunts":["6AA912328C2B52EFA0ECE71F523E137E400EC484","247C721D5CEB90BB1FE389BA74C43DF0955E1647"]}}}],"peer_key":""}]}
{"time":"2017-04-27T22:23:56.314Z","msg":[1,{"height":1,"round":0,"step":"RoundStepPrevote"}]}
{"time":"2017-04-27T22:23:56.314Z","msg":[2,{"msg":[20,{"Vote":{"validator_address":"D028C9981F7A87F3093672BF0D5B0E2A1B3ED456","validator_index":0,"height":1,"round":0,"type":1,"block_id":{"hash":"62371CF72F8662378691706DB256C833CF1AF81B","parts":{"total":6,"hash":"A3C176F13F5CBC7C48EE27A472800410C9D487DC"}},"signature":[1,"255906FAAA50C84E85DABF7DE73468E4F95DB4E46F598848145926E2FAD77CA682BF07E09E2F3EC81FFBD9A036B67914A3C02F819B69248D777AEBA792725907"]}}],"peer_key":""}]}
{"time":"2017-04-27T22:23:56.315Z","msg":[1,{"height":1,"round":0,"step":"RoundStepPrecommit"}]}
{"time":"2017-04-27T22:23:56.315Z","msg":[2,{"msg":[20,{"Vote":{"validator_address":"D028C9981F7A87F3093672BF0D5B0E2A1B3ED456","validator_index":0,"height":1,"round":0,"type":2,"block_id":{"hash":"62371CF72F8662378691706DB256C833CF1AF81B","parts":{"total":6,"hash":"A3C176F13F5CBC7C48EE27A472800410C9D487DC"}},"signature":[1,"056CC15C748434D0A59B64B45CB56EDC1A437A426E68FA63DC7D61A7C17B0F768F207D81340D129A57C5A64195F8AFDD03B6BF28D7B2286290D61BCE88FCA304"]}}],"peer_key":""}]}
{"time":"2017-04-27T22:23:56.316Z","msg":[1,{"height":1,"round":0,"step":"RoundStepCommit"}]}

134
consensus/ticker.go Normal file
View File

@@ -0,0 +1,134 @@
package consensus
import (
"time"
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/log"
)
var (
tickTockBufferSize = 10
)
// TimeoutTicker is a timer that schedules timeouts
// conditional on the height/round/step in the timeoutInfo.
// The timeoutInfo.Duration may be non-positive.
type TimeoutTicker interface {
Start() (bool, error)
Stop() bool
Chan() <-chan timeoutInfo // on which to receive a timeout
ScheduleTimeout(ti timeoutInfo) // reset the timer
SetLogger(log.Logger)
}
// timeoutTicker wraps time.Timer,
// scheduling timeouts only for greater height/round/step
// than what it's already seen.
// Timeouts are scheduled along the tickChan,
// and fired on the tockChan.
type timeoutTicker struct {
cmn.BaseService
timer *time.Timer
tickChan chan timeoutInfo // for scheduling timeouts
tockChan chan timeoutInfo // for notifying about them
}
// NewTimeoutTicker returns a new TimeoutTicker.
func NewTimeoutTicker() TimeoutTicker {
tt := &timeoutTicker{
timer: time.NewTimer(0),
tickChan: make(chan timeoutInfo, tickTockBufferSize),
tockChan: make(chan timeoutInfo, tickTockBufferSize),
}
tt.BaseService = *cmn.NewBaseService(nil, "TimeoutTicker", tt)
tt.stopTimer() // don't want to fire until the first scheduled timeout
return tt
}
// OnStart implements cmn.Service. It starts the timeout routine.
func (t *timeoutTicker) OnStart() error {
go t.timeoutRoutine()
return nil
}
// OnStop implements cmn.Service. It stops the timeout routine.
func (t *timeoutTicker) OnStop() {
t.BaseService.OnStop()
t.stopTimer()
}
// Chan returns a channel on which timeouts are sent.
func (t *timeoutTicker) Chan() <-chan timeoutInfo {
return t.tockChan
}
// ScheduleTimeout schedules a new timeout by sending on the internal tickChan.
// The timeoutRoutine is alwaya available to read from tickChan, so this won't block.
// The scheduling may fail if the timeoutRoutine has already scheduled a timeout for a later height/round/step.
func (t *timeoutTicker) ScheduleTimeout(ti timeoutInfo) {
t.tickChan <- ti
}
//-------------------------------------------------------------
// stop the timer and drain if necessary
func (t *timeoutTicker) stopTimer() {
// Stop() returns false if it was already fired or was stopped
if !t.timer.Stop() {
select {
case <-t.timer.C:
default:
t.Logger.Debug("Timer already stopped")
}
}
}
// send on tickChan to start a new timer.
// timers are interupted and replaced by new ticks from later steps
// timeouts of 0 on the tickChan will be immediately relayed to the tockChan
func (t *timeoutTicker) timeoutRoutine() {
t.Logger.Debug("Starting timeout routine")
var ti timeoutInfo
for {
select {
case newti := <-t.tickChan:
t.Logger.Debug("Received tick", "old_ti", ti, "new_ti", newti)
// ignore tickers for old height/round/step
if newti.Height < ti.Height {
continue
} else if newti.Height == ti.Height {
if newti.Round < ti.Round {
continue
} else if newti.Round == ti.Round {
if ti.Step > 0 && newti.Step <= ti.Step {
continue
}
}
}
// stop the last timer
t.stopTimer()
// update timeoutInfo and reset timer
// NOTE time.Timer allows duration to be non-positive
ti = newti
t.timer.Reset(ti.Duration)
t.Logger.Debug("Scheduled timeout", "dur", ti.Duration, "height", ti.Height, "round", ti.Round, "step", ti.Step)
case <-t.timer.C:
t.Logger.Info("Timed out", "dur", ti.Duration, "height", ti.Height, "round", ti.Round, "step", ti.Step)
// go routine here guarantees timeoutRoutine doesn't block.
// Determinism comes from playback in the receiveRoutine.
// We can eliminate it by merging the timeoutRoutine into receiveRoutine
// and managing the timeouts ourselves with a millisecond ticker
go func(toi timeoutInfo) { t.tockChan <- toi }(ti)
case <-t.Quit:
return
}
}
}

View File

@@ -1,7 +1,7 @@
package consensus
import (
. "github.com/tendermint/go-common"
. "github.com/tendermint/tmlibs/common"
)
// kind of arbitrary

View File

@@ -1,27 +1,26 @@
package consensus
import (
"bufio"
"os"
"time"
. "github.com/tendermint/go-common"
"github.com/tendermint/go-wire"
wire "github.com/tendermint/go-wire"
"github.com/tendermint/tendermint/types"
auto "github.com/tendermint/tmlibs/autofile"
. "github.com/tendermint/tmlibs/common"
)
//--------------------------------------------------------
// types and functions for savings consensus messages
type ConsensusLogMessage struct {
type TimedWALMessage struct {
Time time.Time `json:"time"`
Msg ConsensusLogMessageInterface `json:"msg"`
Msg WALMessage `json:"msg"`
}
type ConsensusLogMessageInterface interface{}
type WALMessage interface{}
var _ = wire.RegisterInterface(
struct{ ConsensusLogMessageInterface }{},
struct{ WALMessage }{},
wire.ConcreteType{types.EventDataRoundState{}, 0x01},
wire.ConcreteType{msgInfo{}, 0x02},
wire.ConcreteType{timeoutInfo{}, 0x03},
@@ -35,111 +34,71 @@ var _ = wire.RegisterInterface(
// TODO: currently the wal is overwritten during replay catchup
// give it a mode so it's either reading or appending - must read to end to start appending again
type WAL struct {
fp *os.File
exists bool // if the file already existed (restarted process)
done chan struct{}
BaseService
group *auto.Group
light bool // ignore block parts
}
func NewWAL(file string, light bool) (*WAL, error) {
var walExists bool
if _, err := os.Stat(file); err == nil {
walExists = true
}
fp, err := os.OpenFile(file, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600)
func NewWAL(walFile string, light bool) (*WAL, error) {
group, err := auto.OpenGroup(walFile)
if err != nil {
return nil, err
}
return &WAL{
fp: fp,
exists: walExists,
done: make(chan struct{}),
wal := &WAL{
group: group,
light: light,
}, nil
}
wal.BaseService = *NewBaseService(nil, "WAL", wal)
return wal, nil
}
func (wal *WAL) Exists() bool {
if wal == nil {
log.Warn("consensus msg log is nil")
return false
func (wal *WAL) OnStart() error {
size, err := wal.group.Head.Size()
if err != nil {
return err
} else if size == 0 {
wal.writeEndHeight(0)
}
return wal.exists
_, err = wal.group.Start()
return err
}
func (wal *WAL) OnStop() {
wal.BaseService.OnStop()
wal.group.Stop()
}
// called in newStep and for each pass in receiveRoutine
func (wal *WAL) Save(clm ConsensusLogMessageInterface) {
if wal != nil {
func (wal *WAL) Save(wmsg WALMessage) {
if wal == nil {
return
}
if wal.light {
// in light mode we only write new steps, timeouts, and our own votes (no proposals, block parts)
if mi, ok := clm.(msgInfo); ok {
_ = mi
if mi, ok := wmsg.(msgInfo); ok {
if mi.PeerKey != "" {
return
}
}
}
var n int
var err error
wire.WriteJSON(ConsensusLogMessage{time.Now(), clm}, wal.fp, &n, &err)
wire.WriteTo([]byte("\n"), wal.fp, &n, &err) // one message per line
// Write the wal message
var wmsgBytes = wire.JSONBytes(TimedWALMessage{time.Now(), wmsg})
err := wal.group.WriteLine(string(wmsgBytes))
if err != nil {
PanicQ(Fmt("Error writing msg to consensus wal. Error: %v \n\nMessage: %v", err, clm))
PanicQ(Fmt("Error writing msg to consensus wal. Error: %v \n\nMessage: %v", err, wmsg))
}
// TODO: only flush when necessary
if err := wal.group.Flush(); err != nil {
PanicQ(Fmt("Error flushing consensus wal buf to file. Error: %v \n", err))
}
}
// Must not be called concurrently with a write.
func (wal *WAL) Close() {
if wal != nil {
wal.fp.Close()
}
wal.done <- struct{}{}
}
func (wal *WAL) writeEndHeight(height int) {
wal.group.WriteLine(Fmt("#ENDHEIGHT: %v", height))
func (wal *WAL) Wait() {
<-wal.done
}
func (wal *WAL) SeekFromEnd(found func([]byte) bool) (nLines int, err error) {
var current int64
// start at the end
current, err = wal.fp.Seek(0, 2)
if err != nil {
return
}
// backup until we find the the right line
// current is how far we are from the beginning
for {
current -= 1
if current < 0 {
wal.fp.Seek(0, 0) // back to beginning
return
}
// backup one and read a new byte
if _, err = wal.fp.Seek(current, 0); err != nil {
return
}
b := make([]byte, 1)
if _, err = wal.fp.Read(b); err != nil {
return
}
if b[0] == '\n' || len(b) == 0 {
nLines += 1
// read a full line
reader := bufio.NewReader(wal.fp)
lineBytes, _ := reader.ReadBytes('\n')
if len(lineBytes) == 0 {
continue
}
if found(lineBytes) {
wal.fp.Seek(0, 1) // (?)
wal.fp.Seek(current, 0)
return
}
}
// TODO: only flush when necessary
if err := wal.group.Flush(); err != nil {
PanicQ(Fmt("Error flushing consensus wal buf to file. Error: %v \n", err))
}
}

View File

@@ -1,78 +0,0 @@
package consensus
import (
"io/ioutil"
"os"
"path"
"strings"
"testing"
. "github.com/tendermint/go-common"
)
var testTxt = `{"time":"2016-01-16T04:42:00.390Z","msg":[1,{"height":28219,"round":0,"step":"RoundStepPrevote"}]}
{"time":"2016-01-16T04:42:00.390Z","msg":[2,{"msg":[20,{"ValidatorIndex":0,"Vote":{"height":28219,"round":0,"type":1,"block_hash":"67F9689F15BEC30BF311FB4C0C80C5E661AA44E0","block_parts_header":{"total":1,"hash":"DFFD4409A1E273ED61AC27CAF975F446020D5676"},"signature":"4CC6845A128E723A299B470CCBB2A158612AA51321447F6492F3DA57D135C27FCF4124B3B19446A248252BDA45B152819C76AAA5FD35E1C07091885CE6955E05"}}],"peer_key":""}]}
{"time":"2016-01-16T04:42:00.392Z","msg":[1,{"height":28219,"round":0,"step":"RoundStepPrecommit"}]}
{"time":"2016-01-16T04:42:00.392Z","msg":[2,{"msg":[20,{"ValidatorIndex":0,"Vote":{"height":28219,"round":0,"type":2,"block_hash":"67F9689F15BEC30BF311FB4C0C80C5E661AA44E0","block_parts_header":{"total":1,"hash":"DFFD4409A1E273ED61AC27CAF975F446020D5676"},"signature":"1B9924E010F47E0817695DFE462C531196E5A12632434DE12180BBA3EFDAD6B3960FDB9357AFF085EB61729A7D4A6AD8408555D7569C87D9028F280192FD4E05"}}],"peer_key":""}]}
{"time":"2016-01-16T04:42:00.393Z","msg":[1,{"height":28219,"round":0,"step":"RoundStepCommit"}]}
{"time":"2016-01-16T04:42:00.395Z","msg":[1,{"height":28220,"round":0,"step":"RoundStepNewHeight"}]}`
func TestSeek(t *testing.T) {
f, err := ioutil.TempFile(os.TempDir(), "seek_test_")
if err != nil {
panic(err)
}
stat, _ := f.Stat()
name := stat.Name()
_, err = f.WriteString(testTxt)
if err != nil {
panic(err)
}
f.Close()
wal, err := NewWAL(path.Join(os.TempDir(), name), config.GetBool("cswal_light"))
if err != nil {
panic(err)
}
keyWord := "Precommit"
n, err := wal.SeekFromEnd(func(b []byte) bool {
if strings.Contains(string(b), keyWord) {
return true
}
return false
})
if err != nil {
panic(err)
}
// confirm n
spl := strings.Split(testTxt, "\n")
var i int
var s string
for i, s = range spl {
if strings.Contains(s, keyWord) {
break
}
}
// n is lines from the end.
spl = spl[i:]
if n != len(spl) {
panic(Fmt("Wrong nLines. Got %d, expected %d", n, len(spl)))
}
b, err := ioutil.ReadAll(wal.fp)
if err != nil {
panic(err)
}
// first char is a \n
spl2 := strings.Split(strings.Trim(string(b), "\n"), "\n")
for i, s := range spl {
if s != spl2[i] {
panic(Fmt("Mismatch. Got %s, expected %s", spl2[i], s))
}
}
}

View File

@@ -0,0 +1,246 @@
# First Tendermint App
As a general purpose blockchain engine, Tendermint is agnostic to the application you want to run.
So, to run a complete blockchain that does something useful, you must start two programs:
one is Tendermint Core, the other is your application, which can be written in any programming language.
Recall from [the intro to ABCI](/intro/abci-overview) that Tendermint Core handles all the p2p and consensus stuff,
and just forwards transactions to the application when they need to be validated, or when they're ready to be committed to a block.
In this guide, we show you some examples of how to run an application using Tendermint.
**Note:** It is highly recommended to read the [Using Tendermint Guide](/docs/guides/using-tendermint) prior to working through this tutorial.
## Install
First, make sure you have [installed Tendermint](/download).
The first apps we will work with are written in Go.
To install them, you need to [install Go](https://golang.org/doc/install) and
[put `$GOPATH/bin` in your `$PATH`](https://github.com/tendermint/tendermint/wiki/Setting-GOPATH).
Then run
```
go get -u github.com/tendermint/abci/cmd/...
```
If there is an error, install and run the `glide` tool to pin the dependencies:
```
go get github.com/Masterminds/glide
cd $GOPATH/src/github.com/tendermint/abci
glide install
go install ./cmd/...
```
Now you should have the `abci-cli` plus two apps installed:
```
dummy --help
counter --help
```
These binaries are installed on `$GOPATH/bin` and all come from within the `./cmd/...` directory of the abci repository.
Both of these example applications are in Go. See below for an application written in Javascript.
Now, let's run some apps!
## A First Example - Dummy
The dummy app is a [Merkle tree](https://en.wikipedia.org/wiki/Merkle_tree) that just stores all transactions.
If the transaction contains an `=`, eg. `key=value`,
then the `value` is stored under the `key` in the Merkle tree.
Otherwise, the full transaction bytes are stored as the key and the value.
Let's start a dummy application.
```
dummy
```
In another terminal, we can start Tendermint.
If you have never run Tendermint before, use:
```
tendermint init
tendermint node
```
If you have used Tendermint, you may want to reset the data for a new blockchain by running `tendermint unsafe_reset_all`.
Then you can run `tendermint node` to start Tendermint, and connect to the app.
For more details, see [the guide on using Tendermint](/docs/guides/using-tendermint).
You should see Tendermint making blocks!
We can get the status of our Tendermint node as follows:
```
curl -s localhost:46657/status
```
The `-s` just silences `curl`. For nicer output, pipe the result into a tool like [jq](https://stedolan.github.io/jq/)
or [jsonpp](https://github.com/jmhodges/jsonpp).
Now let's send some transactions to the dummy.
```
curl -s 'localhost:46657/broadcast_tx_commit?tx="abcd"'
```
Note the single quote (`'`) around the url, which ensures that the double quotes (`"`) are not escaped by bash.
This command sent a transaction with bytes `abcd`, so `abcd` will be stored as both the key and the value in the Merkle tree.
The response should look something like:
```
{"jsonrpc":"2.0","id":"","result":[98,{"check_tx":{},"deliver_tx":{}}],"error":""}
```
The `98` is a type-byte, and can be ignored (it's useful for serializing and deserializing arbitrary json).
Otherwise, this result is empty - there's nothing to report on and everything is OK.
We can confirm that our transaction worked and the value got stored by querying the app:
```
curl -s 'localhost:46657/abci_query?data="abcd"&path=""&prove=false'
```
The `path` and `prove` arguments can be ignored for now, and in a future release can be left out.
The result should look like:
```
{"jsonrpc":"2.0","id":"","result":[112,{"response":{"value":"61626364","log":"exists"}}],"error":""}
```
Again, the `112` is the type-byte. Note the `value` in the result (`61626364`); this is the hex-encoding of the ASCII of `abcd`.
You can verify this in a python shell by running `"61626364".decode('hex')`.
Stay tuned for a future release that makes this output more human-readable ;).
Now let's try setting a different key and value:
```
curl -s 'localhost:46657/broadcast_tx_commit?tx="name=satoshi"'
```
Now if we query for `name`, we should get `satoshi`, or `7361746F736869` in hex:
```
curl -s 'localhost:46657/abci_query?data="name"&path=""&prove=false'
```
Try some other transactions and queries to make sure everything is working!
## Another Example - Counter
Now that we've got the hang of it, let's try another application, the "counter" app.
The counter app doesn't use a Merkle tree, it just counts how many times we've sent a transaction,
or committed the state.
This application has two modes: `serial=off` and `serial=on`.
When `serial=on`, transactions must be a big-endian encoded incrementing integer, starting at 0.
If `serial=off`, there are no restrictions on transactions.
In a live blockchain, transactions collect in memory before they are committed into blocks.
To avoid wasting resources on invalid transactions,
ABCI provides the `CheckTx` message,
which application developers can use to accept or reject transactions,
before they are stored in memory or gossipped to other peers.
In this instance of the counter app, with `serial=on`, `CheckTx` only allows transactions whose integer is greater than the last committed one.
Let's kill the previous instance of `tendermint` and the `dummy` application, and start the counter app.
We can enable `serial=on` with a flag:
```
counter --serial
```
In another window, reset then start Tendermint:
```
tendermint unsafe_reset_all
tendermint node
```
Once again, you can see the blocks streaming by. Let's send some transactions.
Since we have set `serial=on`, the first transaction must be the number `0`:
```
curl localhost:46657/broadcast_tx_commit?tx=0x00
```
Note the empty (hence successful) response.
The next transaction must be the number `1`. If instead, we try to send a `5`, we get an error:
```
> curl localhost:46657/broadcast_tx_commit?tx=0x05
{"jsonrpc":"2.0","id":"","result":[98,{"check_tx":{},"deliver_tx":{"code":3,"log":"Invalid nonce. Expected 1, got 5"}}],"error":""}
```
But if we send a `1`, it works again:
```
> curl localhost:46657/broadcast_tx_commit?tx=0x01
{"jsonrpc":"2.0","id":"","result":[98,{"check_tx":{},"deliver_tx":{}}],"error":""}
```
For more details on the `broadcast_tx` API,
see [the guide on using Tendermint](/docs/guides/using-tendermint).
## Example in Another Language - CounterJS
We also want to run applications in another language - in this case, we'll run a Javascript version of the `counter`.
To run it, you'll need to [install node](https://nodejs.org/en/download/).
You'll also need to fetch the relevant repository, from https://github.com/tendermint/js-abci then install it.
As go devs, we keep all our code under the `$GOPATH`, so run:
```
go get github.com/tendermint/js-abci &> /dev/null
cd $GOPATH/src/github.com/tendermint/js-abci/example
npm install
```
Kill the previous `counter` and `tendermint` processes. Now run the app:
```
node example/app.js
```
In another window, reset and start `tendermint`:
```
tendermint unsafe_reset_all
tendermint node
```
Once again, you should see blocks streaming by - but now, our application is written in javascript!
Try sending some transactions, and like before - the results should be the same:
```
curl localhost:46657/broadcast_tx_commit?tx=0x00 # ok
curl localhost:46657/broadcast_tx_commit?tx=0x05 # invalid nonce
curl localhost:46657/broadcast_tx_commit?tx=0x01 # ok
```
Neat, eh?
## A More Interesting Example - Basecoin
Before concluding, we'd like to introduce you to our star application, [Basecoin](https://github.com/tendermint/basecoin).
Unlike the `dummy` and `counter`, which are strictly for example purposes,
`basecoin` is designed to be actually useful - it's a general purpose framework for building cryptocurrencies.
The default `basecoin` application is a multi-asset cryptocurrency that supports inter-blockchain communication.
For more details on how basecoin works and how to use it, see our [basecoin guide](https://github.com/tendermint/basecoin/blob/develop/docs/guide/basecoin-basics.md)
## Next Step
In this tutorial you learned how to run applications using Tendermint on a single node.
You saw how applications could be written in different languages,
and how to send transactions and query for the latest state.
But the true power of Tendermint comes from its ability to securely and efficiently run an application
across a distributed network of nodes, while keeping them all in sync using its state-of-the-art consensus protocol.
This is the subject of the next tutorial, where we show you [how to deploy Tendermint networks](/docs/getting-started/deploy-testnet).

View File

@@ -0,0 +1,39 @@
# Deploy a Testnet
Now that we've seen how ABCI works, and even played with a few applications on a single validator node,
it's time to deploy a test network to four validator nodes.
For this deployment, we'll use the `basecoin` application.
## Manual Deployments
It's relatively easy to setup a Tendermint cluster manually.
The only requirements for a particular Tendermint node are a private key for the validator,
stored as `priv_validator.json`, and a list of the public keys of all validators, stored as `genesis.json`.
These files should be stored in `~/.tendermint`, or wherever the `$TMROOT` variable might be set to.
Here are the steps to setting up a testnet manually:
1) Provision nodes on your cloud provider of choice
2) Install Tendermint and the application of interest on all nodes
3) Generate a private key for each validator using `tendermint gen_validator`
4) Compile a list of public keys for each validator into a `genesis.json` file.
5) Run `tendermint node --p2p.seeds=< seed addresses >` on each node, where `< seed addresses >` is a
comma separated list of the IP:PORT combination for each node. The default port for Tendermint is `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 would look like: `tendermint node --p2p.seeds=192.168.0.1:46656,192.168.0.2:46656,192.168.0.3:46656,192.168.0.4:46656`.
After a few seconds, all the nodes should connect to eachother and start making blocks!
For more information, see the Tendermint Networks section of [the guide to using Tendermint](/docs/guides/using-tendermint).
## Automated Deployments
While the manual deployment is easy enough, an automated deployment is always better.
For this, we have the [mintnet-kubernetes tool](https://github.com/tendermint/tools/tree/master/mintnet-kubernetes),
which allows us to automate the deployment of a Tendermint network on an already provisioned kubernetes cluster.
For more details, see the [mintnet-kubernetes directory](https://github.com/tendermint/tools/tree/master/mintnet-kubernetes),
and check out [Google Cloud Platform](https://cloud.google.com/) for simple provisioning of kubernetes clusters.
## Next Steps
Done trying out the testnet? Continue [onwards](/docs/getting-started/next-steps).

View File

@@ -0,0 +1,21 @@
# Next Steps
By now you've seen how to run a simple example ABCI application on a local Tendermint node
and on a remote Tendermint cluster.
To learn more about building ABCI applications and integrating with Tendermint, see the [Developer Guides](/docs/guides/app-development).
To learn more about running the Tendermint software, see the [Using Tendermint Guide](/docs/guides/using-tendermint).
To learn more about Tendermint's various pieces, check out the [Documentation](/docs).
For a deeper dive, see [this thesis](https://atrium.lib.uoguelph.ca/xmlui/handle/10214/9769).
There is also the [original whitepaper](/static/docs/tendermint.pdf), though it is now quite outdated.
The Tendermint [Software Ecosystem](/ecosystem) contains many example applications and related software built by the Tendermint team and others. Check it out for some inspiration!
For details on how the software has changed, and what changes are in store, see the [Changelog](/docs/changelog) and the [Roadmap](/docs/roadmap).
See our [Community](/community) page for more ways to collaborate.
You can also [get in touch with the team](/contact).
Most importantly, enjoy!

219
docs/guides/abci-cli.md Normal file
View File

@@ -0,0 +1,219 @@
# Using the abci-cli
To facilitate testing and debugging of ABCI servers and simple apps,
we built a CLI, the `abci-cli`, for sending ABCI messages from the command line.
## Install
Make sure you [have Go installed](https://golang.org/doc/install) and [put `$GOPATH/bin` in your `$PATH`](https://github.com/tendermint/tendermint/wiki/Setting-GOPATH).
Next, install the `abci-cli` tool and example applications:
```
go get -u github.com/tendermint/abci/cmd/...
```
If this fails, you may need to use `glide` to get vendored dependencies:
```
go get github.com/Masterminds/glide
cd $GOPATH/src/github.com/tendermint/abci
glide install
go install ./cmd/...
```
Now run `abci-cli --help` to see the list of commands:
```
COMMANDS:
batch Run a batch of ABCI commands against an application
console Start an interactive console for multiple commands
echo Have the application echo a message
info Get some info about the application
set_option Set an option on the application
deliver_tx Append a new tx to application
check_tx Validate a tx
commit Get application Merkle root hash
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--address "tcp://127.0.0.1:46658" address of application socket
--help, -h show help
--version, -v print the version
```
## First Example - Dummy
The `abci-cli` tool lets us send ABCI messages to our application, to help build and debug them.
The most important messages are `deliver_tx`, `check_tx`, and `commit`,
but there are others for convenience, configuration, and information purposes.
Let's start a dummy application, which was installed at the same time as `abci-cli` above. The dummy just stores transactions in a merkle tree:
```
dummy
```
In another terminal, run
```
abci-cli echo hello
abci-cli info
```
The application should echo `hello` and give you some information about itself.
An ABCI application must provide two things:
- a socket server
- a handler for ABCI messages
When we run the `abci-cli` tool we open a new connection to the application's socket server,
send the given ABCI message, and wait for a response.
The server may be generic for a particular language, and we provide a [reference implementation
in Golang](https://github.com/tendermint/abci/tree/master/server).
See the [list of other ABCI implementations](https://tendermint.com/ecosystem)
for servers in other languages.
The handler is specific to the application, and may be arbitrary,
so long as it is deterministic and conforms to the ABCI interface specification.
So when we run `abci-cli info`, we open a new connection to the ABCI server, which calls the `Info()` method on the application, which tells us the number of transactions in our Merkle tree.
Now, since every command opens a new connection, we provide the `abci-cli console` and `abci-cli batch` commands,
to allow multiple ABCI messages to be sent over a single connection.
Running `abci-cli console` should drop you in an interactive console for speaking ABCI messages to your application.
Try running these commands:
```
> echo hello
-> data: hello
> info
-> data: {"size":0}
> commit
-> data: 0x
> deliver_tx "abc"
-> code: OK
> info
-> data: {"size":1}
> commit
-> data: 0x750502FC7E84BBD788ED589624F06CFA871845D1
> query "abc"
-> code: OK
-> data: {"index":0,"value":"abc","exists":true}
> deliver_tx "def=xyz"
-> code: OK
> commit
-> data: 0x76393B8A182E450286B0694C629ECB51B286EFD5
> query "def"
-> code: OK
-> data: {"index":1,"value":"xyz","exists":true}
```
Note that if we do `deliver_tx "abc"` it will store `(abc, abc)`,
but if we do `deliver_tx "abc=efg"` it will store `(abc, efg)`.
Similarly, you could put the commands in a file and run `abci-cli --verbose batch < myfile`.
## Another Example - Counter
Now that we've got the hang of it, let's try another application, the "counter" app.
The counter app doesn't use a Merkle tree, it just counts how many times we've sent a transaction,
asked for a hash, or committed the state. The result of `commit` is just the number of transactions sent.
This application has two modes: `serial=off` and `serial=on`.
When `serial=on`, transactions must be a big-endian encoded incrementing integer, starting at 0.
If `serial=off`, there are no restrictions on transactions.
We can toggle the value of `serial` using the `set_option` ABCI message.
When `serial=on`, some transactions are invalid.
In a live blockchain, transactions collect in memory before they are committed into blocks.
To avoid wasting resources on invalid transactions,
ABCI provides the `check_tx` message,
which application developers can use to accept or reject transactions,
before they are stored in memory or gossipped to other peers.
In this instance of the counter app, `check_tx` only allows transactions whose integer is greater than the last committed one.
Let's kill the console and the dummy application, and start the counter app:
```
counter
```
In another window, start the `abci-cli console`:
```
> set_option serial on
-> data: serial=on
> check_tx 0x00
-> code: OK
> check_tx 0xff
-> code: OK
> deliver_tx 0x00
-> code: OK
> check_tx 0x00
-> code: BadNonce
-> log: Invalid nonce. Expected >= 1, got 0
> deliver_tx 0x01
-> code: OK
> deliver_tx 0x04
-> code: BadNonce
-> log: Invalid nonce. Expected 2, got 4
> info
-> data: {"hashes":0,"txs":2}
```
This is a very simple application, but between `counter` and `dummy`, its easy to see how you can build out arbitrary application states on top of the ABCI.
[Hyperledger's Burrow](https://github.com/hyperledger/burrow) also runs atop ABCI, bringing with it Ethereum-like accounts, the Ethereum virtual-machine, Monax's permissioning scheme, and native contracts extensions.
But the ultimate flexibility comes from being able to write the application easily in any language.
We have implemented the counter in a number of languages (see the example directory).
To run the Node JS version, `cd` to `example/js` and run
```
node app.js
```
(you'll have to kill the other counter application process).
In another window, run the console and those previous ABCI commands.
You should get the same results as for the Go version.
Want to write the counter app in your favorite language?! We'd be happy to add you to our [ecosystem](https://tendermint.com/ecosystem)! We're also offering [bounties](https://tendermint.com/bounties) for implementations in new languages!
## Notes
The `abci-cli` is designed strictly for testing and debugging.
In a real deployment, the role of sending messages is taken by Tendermint,
which connects to the app using three separate connections,
each with its own pattern of messages.
For more information, see the [application developers guide](/docs/guides/app-development).
For examples of running an ABCI app with Tendermint, see the [introductory guide](/docs/getting-started/first-abci-app).

View File

@@ -0,0 +1,64 @@
# Application Architecture Guide
## Overview
A blockchain application is more than the consensus engine and the transaction logic (eg. smart contracts, business logic) as implemented in the ABCI app. There are also (mobile, web, desktop) clients that will need to connect and make use of the app. We will assume for now that you have a well designed transactions and database model, but maybe this will be the topic of another article. This article is more interested in various ways of setting up the "plumbing" and connecting these pieces, and demonstrating some evolving best practices.
## Security
A very important aspect when constructing a blockchain is security. The consensus model can be DoSed (no consensus possible) by corrupting 1/3 of the validators and exploited (writing arbitrary blocks) by corrupting 2/3 of the validators. So, while the security is not that of the "weakest link", you should take care that the "average link" is sufficiently hardened.
One big attack surface on the validators is the communication between the ABCI app and the tendermint core. This should be highly protected. Ideally, the app and the core are running on the same machine, so no external agent can target the communication channel. You can use unix sockets (with permissions preventing access from other users), or even compile the two apps into one binary if the ABCI app is also writen in go. If you are unable to do that due to language support, then the ABCI app should bind a TCP connection to localhost (127.0.0.1), which is less efficient and secure, but still not reachable from outside. If you must run the ABCI app and tendermint core on separate machines, make sure you have a secure communication channel (ssh tunnel?)
Now assuming, you have linked together your app and the core securely, you must also make sure no one can get on the machine it is hosted on. At this point it is basic network security. Run on a secure operating system (SELinux?). Limit who has access to the machine (user accounts, but also where the physical machine is hosted). Turn off all services except for ssh, which should only be accessible by some well-guarded public/private key pairs (no password). And maybe even firewall off access to the ports used by the validators, so only known validators can connect.
There was also a suggestion on slack from @jhon about compiling everything together with a unikernel for more security, such as [Mirage](https://mirage.io) or [UNIK](https://github.com/emc-advanced-dev/unik).
## Connecting your client to the blockchain
### Tendermint Core RPC
The concept is that the ABCI app is completely hidden from the outside world and only communicated through a tested and secured [interface exposed by the tendermint core](/docs/specs/rpc). This interface exposes a lot of data on the block header and consensus process, which is quite useful for externally verifying the system. It also includes 3(!) methods to broadcast a transaction (propose it for the blockchain, and possibly await a response). And one method to query app-specific data from the ABCI application.
Pros:
* Server code already written
* Access to block headers to validate merkle proofs (nice for light clients)
* Basic read/write functionality is supported
Cons:
* Limited interface to app. All queries must be serialized into []byte (less expressive than JSON over HTTP) and there is no way to push data from ABCI app to the client (eg. notify me if account X receives a transaction)
### Custom ABCI server
This was proposed by @wolfposd on slack and demonstrated by [TMChat](https://github.com/wolfposd/TMChat), a sample app. The concept is to write a custom server for your app (with typical REST API/websockets/etc for easy use by a mobile app). This custom server is in the same binary as the ABCI app and data store, so can easily react to complex events there that involve understanding the data format (send a message if my balance drops below 500). All "writes" sent to this server are proxied via websocket/JSON-RPC to tendermint core. When they come back as deliver_tx over ABCI, they will be written to the data store. For "reads", we can do any queries we wish that are supported by our architecture, using any web technology that is useful. The general architecture is shown in the following diagram:
<img alt="Application Architecture" src="../assets/images/tm-app-example.png">
Pros:
* Separates application logic from blockchain logic
* Allows much richer, more flexible client-facing API
* Allows pub-sub, watching certain fields, etc.
Cons:
* Access to ABCI app can be dangerous (be VERY careful not to write unless it comes from the validator node)
* No direct access to the blockchain headers to verify tx
* You must write your own API (but maybe that's a pro...)
### Hybrid solutions
Likely the least secure but most versatile. The client can access both the tendermint node for all blockchain info, as well as a custom app server, for complex queries and pub-sub on the abci app.
Pros:
* All from both above solutions
Cons:
* Even more complexity
* Even more attack vectors (less security)
## Scalability
Read replica using non-validating nodes? They could forward transactions to the validators (fewer connections, more security), and locally allow all queries in any of the above configurations. Thus, while transaction-processing speed is limited by the speed of the abci app and the number of validators, one should be able to scale our read performance to quite an extent (until the replication process drains too many resources from the validator nodes).
## Example Code
* [TMChat](https://github.com/wolfposd/TMChat)

View File

@@ -0,0 +1,155 @@
# Application Development Guide
## ABCI Design
The purpose of ABCI is to provide a clean interface between state transition machines on one computer and the mechanics of their replication across multiple computers. The former we call 'application logic' and the latter the 'consensus engine'. Application logic validates transactions and optionally executes transactions against some persistent state. A consensus engine ensures all transactions are replicated in the same order on every machine. We call each machine in a consensus engine a 'validator', and each validator runs the same transactions through the same application logic. In particular, we are interested in blockchain-style consensus engines, where transactions are committed in hash-linked blocks.
The ABCI design has a few distinct components:
- message protocol
- pairs of request and response messages
- consensus makes requests, application responds
- defined using protobuf
- server/client
- consensus engine runs the client
- application runs the server
- two implementations:
- async raw bytes
- grpc
- blockchain protocol
- abci is connection oriented
- Tendermint Core maintains three connections:
- [mempool connection](#mempool-connection): for checking if transactions should be relayed before they are committed; only uses `CheckTx`
- [consensus connection](#consensus-connection): for executing transactions that have been committed. Message sequence is - for every block - `BeginBlock, [DeliverTx, ...], EndBlock, Commit`
- [query connection](#query-connection): for querying the application state; only uses Query and Info
<img src="../assets/images/abci.png">
The mempool and consensus logic act as clients, and each maintains an open ABCI connection with the application, which hosts an ABCI server. Shown are the request and response types sent on each connection.
## Message Protocol
The message protocol consists of pairs of requests and responses. Some messages have no fields, while others may include byte-arrays, strings, or integers. See the `message Request` and `message Response` definitions in [the protobuf definition file](https://github.com/tendermint/abci/blob/master/types/types.proto), and the [protobuf documentation](https://developers.google.com/protocol-buffers/docs/overview) for more details.
For each request, a server should respond with the corresponding response, where order of requests is preserved in the order of responses.
## Server
To use ABCI in your programming language of choice, there must be a ABCI server in that language.
Tendermint supports two kinds of implementation of the server:
- Asynchronous, raw socket server (Tendermint Socket Protocol, also known as TSP or Teaspoon)
- GRPC
Both can be tested using the `abci-cli` by setting the `--abci` flag appropriately (ie. to `socket` or `grpc`).
See examples, in various stages of maintenance, in [Go](https://github.com/tendermint/abci/tree/master/server), [JavaScript](https://github.com/tendermint/js-abci), [Python](https://github.com/tendermint/abci/tree/master/example/python3/abci), [C++](https://github.com/mdyring/cpp-tmsp), and [Java](https://github.com/jTendermint/jabci).
### GRPC
If GRPC is available in your language, this is the easiest approach,
though it will have significant performance overhead.
To get started with GRPC, copy in the [protobuf file](https://github.com/tendermint/abci/blob/master/types/types.proto) and compile it using the GRPC plugin for your language.
For instance, for golang, the command is `protoc --go_out=plugins=grpc:. types.proto`. See the [grpc documentation for more details](http://www.grpc.io/docs/). `protoc` will autogenerate all the necessary code for ABCI client and server in your language, including whatever interface your application must satisfy to be used by the ABCI server for handling requests.
### TSP
If GRPC is not available in your language, or you require higher performance, or otherwise enjoy programming, you may implement your own ABCI server
using the Tendermint Socket Protocol, known affectionaltely as Teaspoon.
The first step is still to auto-generate the relevant data types and codec in your language using `protoc`.
Messages coming over the socket are Protobuf3 encoded, but additionally length-prefixed to facilitate use as a streaming protocol. Protobuf3 doesn't have an official length-prefix standard, so we use our own. The first byte in the prefix represents the length of the Big Endian encoded length. The remaining bytes in the prefix are the Big Endian encoded length.
For example, if the Protobuf3 encoded ABCI message is 0xDEADBEEF (4 bytes), the length-prefixed message is 0x0104DEADBEEF. If the Protobuf3 encoded ABCI message is 65535 bytes long, the length-prefixed message would be like 0x02FFFF....
Note this prefixing does not apply for grpc.
An ABCI server must also be able to support multiple connections, as Tendermint uses three connections.
## Client
There are currently two use-cases for an ABCI client.
One is a testing tool, as in the `abci-cli`, which allows ABCI requests to be sent via command line.
The other is a consensus engine, such as Tendermint Core, which makes requests to the application every time a new transaction is received or a block is committed.
It is unlikely that you will need to implement a client. For details of our client, see [here](https://github.com/tendermint/abci/tree/master/client).
## Blockchain Protocol
In ABCI, a transaction is simply an arbitrary length byte-array.
It is the application's responsibility to define the transaction codec as they please,
and to use it for both CheckTx and DeliverTx.
Note that there are two distinct means for running transactions, corresponding to stages of 'awareness'
of the transaction in the network. The first stage is when a transaction is received by a validator from a client into the so-called mempool or transaction pool - this is where we use CheckTx. The second is when the transaction is successfully committed on more than 2/3 of validators - where we use DeliverTx. In the former case, it may not be necessary to run all the state transitions associated with the transaction, as the transaction may not ultimately be committed until some much later time, when the result of its execution will be different.
For instance, an Ethereum ABCI app would check signatures and amounts in CheckTx, but would not actually execute any contract code until the DeliverTx, so as to avoid executing state transitions that have not been finalized.
To formalize the distinction further, two explicit ABCI connections are made between Tendermint Core and the application: the mempool connection and the consensus connection. We also make a third connection, the query connection, to query the local state of the app.
### Mempool Connection
The mempool connection is used *only* for CheckTx requests.
Transactions are run using CheckTx in the same order they were received by the validator.
If the CheckTx returns `OK`, the transaction is kept in memory and relayed to other peers in the same order it was received. Otherwise, it is discarded.
CheckTx requests run concurrently with block processing;
so they should run against a copy of the main application state which is reset after every block.
This copy is necessary to track transitions made by a sequence of CheckTx requests before they are included in a block. When a block is committed, the application must ensure to reset the mempool state to the latest committed state. Tendermint Core will then filter through all transactions in the mempool, removing any that were included in the block, and re-run the rest using CheckTx against the post-Commit mempool state.
### Consensus Connection
The consensus connection is used only when a new block is committed, and communicates all information from the block in a series of requests: `BeginBlock, [DeliverTx, ...], EndBlock, Commit`.
That is, when a block is committed in the consensus, we send a list of DeliverTx requests (one for each transaction) sandwiched by BeginBlock and EndBlock requests, and followed by a Commit.
#### DeliverTx
DeliverTx is the workhorse of the blockchain. Tendermint sends the DeliverTx requests asynchronously but in order,
and relies on the underlying socket protocol (ie. TCP) to ensure they are received by the app in order. They have already been ordered in the global consensus by the Tendermint protocol.
DeliverTx returns a abci.Result, which includes a Code, Data, and Log. The code may be non-zero (non-OK), meaning the corresponding transaction should have been rejected by the mempool,
but may have been included in a block by a Byzantine proposer.
The block header will be updated (TODO) to include some commitment to the results of DeliverTx, be it a bitarray of non-OK transactions, or a merkle root of the data returned by the DeliverTx requests, or both.
#### Commit
Once all processing of the block is complete, Tendermint sends the Commit request and blocks waiting
for a response. While the mempool may run concurrently with block processing (the BeginBlock, DeliverTxs, and EndBlock), it is locked for the Commit request so that its state can be safely reset during Commit. This means the app *MUST NOT* do any blocking communication with the mempool (ie. broadcast_tx) during Commit, or there will be deadlock. Note also that all remaining transactions in the mempool are replayed on the mempool connection (CheckTx) following a commit.
The Commit response includes a byte array, which is the deterministic state root of the application. It is included in the header of the next block. It can be used to provide easily verified Merkle-proofs of the state of the application.
It is expected that the app will persist state to disk on Commit. The option to have all transactions replayed from some previous block is the job of the [Handshake](#handshake).
#### BeginBlock
The BeginBlock request can be used to run some code at the beginning of every block. It also allows Tendermint to send the current block hash and header to the application, before it sends any of the transactions.
The app should remember the latest height and header (ie. from which it has run a successful Commit) so that it can tell Tendermint where to pick up from when it restarts. See information on the Handshake, below.
#### EndBlock
The EndBlock request can be used to run some code at the end of every block. Additionally, the response may contain a list of validators, which can be used to update the validator set. To add a new validator or update an existing one, simply include them in the list returned in the EndBlock response. To remove one, include it in the list with a `power` equal to `0`. Tendermint core will take care of updating the validator set. Note validator set changes are only available in v0.8.0 and up.
### Query Connection
This connection is used to query the application without engaging consensus. It's exposed over the tendermint core rpc, so clients can query the app without exposing a server on the app itself, but they must serialize each query as a single byte array. Additionally, certain "standardized" queries may be used to inform local decisions, for instance about which peers to connect to.
Tendermint Core currently uses the Query connection to filter peers upon connecting, according to IP address or public key. For instance, returning non-OK ABCI response to either of the following queries will cause Tendermint to not connect to the corresponding peer:
- `p2p/filter/addr/<addr>`, where `<addr>` is an IP address.
- `p2p/filter/pubkey/<pubkey>`, where `<pubkey>` is the hex-encoded ED25519 key of the node (not it's validator key)
Note: these query formats are subject to change!
### Handshake
When the app or tendermint restarts, they need to sync to a common height.
When an ABCI connection is first established, Tendermint will call `Info` on the Query connection.
The response should contain the LastBlockHeight and LastBlockAppHash
- the former is the last block for the which the app ran Commit successfully,
the latter is the response from that Commit.
Using this information, Tendermint will determine what needs to be replayed, if anything, against the app,
to ensure both Tendermint and the app are synced to the latest block height.
If the app returns a LastBlockHeight of 0, Tendermint will just replay all blocks.

View File

@@ -0,0 +1,100 @@
# Install from Source
This page provides instructions on installing Tendermint from source.
To download pre-built binaries, see the [Download page](/download).
## Install Go
Make sure you have [installed Go](https://golang.org/doc/install) and set the `GOPATH`.
## Install Tendermint
You should be able to install the latest with a simple
```
go get github.com/tendermint/tendermint/cmd/tendermint
```
Run `tendermint --help` for more.
If the installation failed, a dependency may been updated and become incompatible with the latest Tendermint master branch.
We solve this using the `glide` tool for dependency management.
Fist, install `glide`:
```
go get github.com/Masterminds/glide
```
Now we can fetch the correct versions of each dependency by running:
```
cd $GOPATH/src/github.com/tendermint/tendermint
glide install
go install ./cmd/tendermint
```
Note that even though `go get` originally failed,
the repository was still cloned to the correct location in the `$GOPATH`.
The latest Tendermint Core version is now installed.
### Reinstall
If you already have Tendermint installed, and you make updates,
simply
```
cd $GOPATH/src/github.com/tendermint/tendermint
go install ./cmd/tendermint
```
To upgrade, there are a few options:
- set a new `$GOPATH` and run `go get github.com/tendermint/tendermint/cmd/tendermint`. This makes a fresh copy of everything for the new version.
- run `go get -u github.com/tendermint/tendermint/cmd/tendermint`, where the `-u` fetches the latest updates for the repository and its dependencies
- fetch and checkout the latest master branch in `$GOPATH/src/github.com/tendermint/tendermint`, and then run `glide install && go install ./cmd/tendermint` as above.
Note the first two options should usually work, but may fail.
If they do, use `glide`, as above:
```
cd $GOPATH/src/github.com/tendermint/tendermint
glide install
go install ./cmd/tendermint
```
Since the third option just uses `glide` right away, it should always work.
### Troubleshooting
If `go get` failing bothers you, fetch the code using `git`:
```
mkdir -p $GOPATH/src/github.com/tendermint
git clone https://github.com/tendermint/tendermint $GOPATH/src/github.com/tendermint/tendermint
cd $GOPATH/src/github.com/tendermint/tendermint
glide install
go install ./cmd/tendermint
```
### Run
To start a one-node blockchain with a simple in-process application:
```
tendermint init
tendermint node --proxy_app=dummy
```
See the
[App Development](/docs/guides/app-development)
guide for more details on building applications,
and the
[Using Tendermint](/docs/guides/using-tendermint)
guide for more details about using the `tendermint` program.
## Next Step
Learn how to [create your first ABCI app](/docs/getting-started/first-abci-app).

View File

@@ -0,0 +1,306 @@
# Using Tendermint
This is a guide to using the `tendermint` program from the command line.
It assumes only that you [have tendermint installed](/download) and have some rudimentary idea
of what Tendermint and ABCI are.
You can see the help menu with `tendermint --help`, and the version number with `tendermint version`.
## Directory Root
The default directory for blockchain data is `~/.tendermint`. Override this by setting the `TMROOT` environment variable.
## Initialize
Initialize the root directory by running:
```
tendermint init
```
This will create a new private key (`priv_validator.json`), and a genesis file (`genesis.json`) containing the associated public key.
This is all that's necessary to run a local testnet with one validator.
For more elaborate initialization, see our [testnet deployment tool](https://github.com/tendermint/tools/tree/master/mintnet-kubernetes).
## Run
To run a tendermint node, use
```
tendermint node
```
By default, Tendermint will try to connect to a abci appliction on [127.0.0.1:46658](127.0.0.1:46658).
If you have the `dummy` ABCI app installed, run it in another window.
If you don't, kill tendermint and run an in-process version with
```
tendermint node --proxy_app=dummy
```
After a few seconds you should see blocks start streaming in.
Note that blocks are produced regularly, even if there are no transactions.
This changes [with this pull request](https://github.com/tendermint/tendermint/pull/584).
Tendermint supports in-process versions of the dummy, counter, and nil apps that ship as examples in the [ABCI 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 app is not written in Go, simply run it in another process,
and use the `--proxy_app` flag to specify the address of the socket it is listening on, for instance
```
tendermint node --proxy_app=/var/run/abci.sock
```
## Transactions
To send a transaction, use `curl` to make requests to the Tendermint RPC server:
```
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:
```
curl http://localhost:46657/status | jsonpp
```
and the `latest_app_hash` in particular:
```
curl http://localhost:46657/status | jsonpp | grep app_hash
```
Visit [http://localhost:46657](http://localhost:46657) in your browser to see the list of other endpoints.
Some take no arguments (like `/status`), while others specify the argument name and use `_` as a placeholder.
## Reset
**WARNING: UNSAFE** Only do this in development and only if you can afford to lose all blockchain data!
To reset a blockchain, stop the node, remove the `~/.tendermint/data` directory and run
```
tendermint unsafe_reset_priv_validator
```
This final step is necessary to reset the `priv_validator.json`,
which otherwise prevents you from making conflicting votes in the consensus
(something that could get you in trouble if you do it on a real blockchain).
If you don't reset the `priv_validator.json`, your fresh new blockchain will not make any blocks.
## Configuration
Tendermint uses a `config.toml` for configutation. For details, see [the documentation](/docs/specs/configuration).
Notable options include the socket address of the application (`proxy_app`),
the listenting address of the tendermint peer (`p2p.laddr`),
and the listening address of the rpc server (`rpc.laddr`).
Some fields from the config file can be overwritten with flags.
## Broadcast API
Earlier, we used the `broadcast_tx_commit` endpoint to send a transaction.
When a transaction is sent to a tendermint node,
it will run via `CheckTx` against the application.
If it passes `CheckTx`, it will be included in the mempool,
broadcast to other peers, and eventually included in a block.
Since there are multiple phases to processing a transaction, we offer multiple endpoints to broadcast a transaction:
```
/broadcast_tx_async
/broadcast_tx_sync
/broadcast_tx_commit
```
These correspond to no-processing, processing through the mempool, and processing through a block, respectively.
That is, `broadcast_tx_async`, will return right away without waiting to hear if the transaction is even valid,
while `broadcast_tx_sync` will return with the result of running the transaction through `CheckTx`.
Using `broadcast_tx_commit` will wait until the transaction is committed in a block or until some timeout is reached,
but will return right away if the transaction does not pass `CheckTx`.
The return value for `broadcast_tx_commit` includes two fields, `check_tx` and `deliver_tx`, pertaining to the result of running
the transaction through those ABCI messages.
The benefit of using `broadcast_tx_commit` is that the request returns after the transaction is committed (ie. included in a block), but that can take on the order of a second. For a quick result, use `broadcast_tx_sync`,
but the transaction will not be committed until later, and by that point its effect on the state may change.
## Tendermint Networks
When `tendermint init` is run, both a `genesis.json` and `priv_validator.json` are created in `~/.tendermint`.
The `genesis.json` might look like:
```
{
"app_hash": "",
"chain_id": "test-chain-HZw6TB",
"genesis_time": "0001-01-01T00:00:00.000Z",
"validators": [
{
"amount": 10,
"name": "",
"pub_key": [
1,
"5770B4DD55B3E08B7F5711C48B516347D8C33F47C30C226315D21AA64E0DFF2E"
]
}
]
}
```
And the `priv_validator.json`:
```
{
"address": "4F4D895F882A18E1D1FC608D102601DA8D3570E5",
"last_height": 0,
"last_round": 0,
"last_signature": null,
"last_signbytes": "",
"last_step": 0,
"priv_key": [
1,
"F9FA3CD435BDAE54D0BCA8F1BC289D718C23D855C6DB21E8543F5E4F457E62805770B4DD55B3E08B7F5711C48B516347D8C33F47C30C226315D21AA64E0DFF2E"
],
"pub_key": [
1,
"5770B4DD55B3E08B7F5711C48B516347D8C33F47C30C226315D21AA64E0DFF2E"
]
}
```
The `priv_validator.json` actually contains a private key, and should thus be kept absolutely secret;
for now we work with the plain text.
Note the `last_` fields, which are used to prevent us from signing conflicting messages.
Note also that the `pub_key` (the public key) in the `priv_validator.json` is also present in the `genesis.json`.
The genesis file contains the list of public keys which may participate in the consensus,
and their corresponding voting power.
Greater than 2/3 of the voting power must be active (ie. the corresponding private keys must be producing signatures)
for the consensus to make progress.
In our case, the genesis file contains the public key of our `priv_validator.json`,
so a tendermint node started with the default root directory will be able to make new blocks,
as we've already seen.
If we want to add more nodes to the network, we have two choices:
we can add a new validator node, who will also participate in the consensus
by proposing blocks and voting on them,
or we can add a new non-validator node, who will not participate directly,
but will verify and keep up with the consensus protocol.
### Peers
To connect to peers on start-up, specify them in the `config.toml` or on the command line.
For instance,
```
tendermint node --p2p.seeds "1.2.3.4:46656,5.6.7.8:46656"
```
Alternatively, you can use the `/dial_seeds` endpoint of the RPC to specify peers for a running node to connect to:
```
curl --data-urlencode "seeds=[\"1.2.3.4:46656\",\"5.6.7.8:46656\"]" localhost:46657/dial_seeds
```
Additionally, the peer-exchange protocol can be enabled using the `--pex` flag,
though this feature is [still under development](https://github.com/tendermint/tendermint/issues/598)
If `--pex` is enabled, peers will gossip about known peers and form a more resilient network.
### Adding a Non-Validator
Adding a non-validator is simple. Just copy the original `genesis.json` to `~/.tendermint` on the new machine
and start the node, specifying seeds as necessary.
If no seeds are specified, the node won't make any blocks, because it's not a validator,
and it won't hear about any blocks, because it's not connected to the other peer.
### Adding a Validator
The easiest way to add new validators is to do it in the `genesis.json`, before starting the network.
For instance, we could make a new `priv_validator.json`, and copy it's `pub_key` into the above genesis.
We can generate a new `priv_validator.json` with the command:
```
tendermint gen_validator
```
Now we can update our genesis file. For instance, if the new `priv_validator.json` looks like:
```
{
"address": "AC379688105901436A34A65F185C115B8BB277A1",
"last_height": 0,
"last_round": 0,
"last_signature": null,
"last_signbytes": "",
"last_step": 0,
"priv_key": [
1,
"0D2ED337D748ADF79BE28559B9E59EBE1ABBA0BAFE6D65FCB9797985329B950C8F2B5AACAACC9FCE41881349743B0CFDE190DF0177744568D4E82A18F0B7DF94"
],
"pub_key": [
1,
"8F2B5AACAACC9FCE41881349743B0CFDE190DF0177744568D4E82A18F0B7DF94"
]
}
```
then the new `genesis.json` will be:
```
{
"app_hash": "",
"chain_id": "test-chain-HZw6TB",
"genesis_time": "0001-01-01T00:00:00.000Z",
"validators": [
{
"amount": 10,
"name": "",
"pub_key": [
1,
"5770B4DD55B3E08B7F5711C48B516347D8C33F47C30C226315D21AA64E0DFF2E"
]
},
{
"amount": 10,
"name": "",
"pub_key": [
1,
"8F2B5AACAACC9FCE41881349743B0CFDE190DF0177744568D4E82A18F0B7DF94"
]
}
]
}
```
Update the `genesis.json` in `~/.tendermint`. Copy the genesis file and the new `priv_validator.json`
to the `~/.tendermint` on a new machine.
Now run `tendermint node` on both machines, and use either `--p2p.seeds` or the `/dial_seeds` to get them to peer up.
They should start making blocks, and will only continue to do so as long as both of them are online.
To make a Tendermint network that can tolerate one of the validators failing, you need at least four validator nodes (> 2/3).
Updating validators in a live network is supported but must be explicitly programmed by the application developer.
See the [application developers guide](/docs/guides/app-development#Handshake) for more details.
### Local Network
To run a network locally, say on a single machine, you must change the `_laddr` fields in the `config.toml` (or using the flags)
so that the listening addresses of the various sockets don't conflict.
Additionally, you must set `addrbook_strict=false` in the `config.toml`,
otherwise Tendermint's p2p library will deny making connections to peers with the same IP address.
## More
Got a couple nodes talking to each other using the dummy app?
Try a more sophisticated app like [Ethermint](https://github.com/tendermint/ethermint),
or learn more about building your own in the [Application Developer's Guide](/docs/guides/app-development).

16
docs/hidden/ABCI.md Normal file
View File

@@ -0,0 +1,16 @@
# ABCI
ABCI is an interface between the consensus/blockchain engine known as tendermint, and the application-specific business logic, known as an ABCi app.
The tendermint core should run unchanged for all apps. Each app can customize it, the supported transactions, queries, even the validator sets and how to handle staking / slashing stake. This customization is achieved by implementing the ABCi app to send the proper information to the tendermint engine to perform as directed.
To understand this decision better, think of the design of the tendermint engine.
* A blockchain is simply consensus on a unique global ordering of events.
* This consensus can efficiently be implemented using BFT and PoS
* This code can be generalized to easily support a large number of blockchains
* The block-chain specific code, the interpretation of the individual events, can be implemented by a 3rd party app without touching the consensus engine core
* Use an efficient, language-agnostic layer to implement this (ABCi)
Bucky, please make this doc real.

16
docs/hidden/README.md Normal file
View File

@@ -0,0 +1,16 @@
# Architecture Decision Records
This is a location to record all high-level architecture decisions in the tendermint project. Not the implementation details, but the reasoning that happened. This should be refered to for guidance of the "right way" to extend the application. And if we notice that the original decisions were lacking, we should have another open discussion, record the new decisions here, and then modify the code to match.
This is like our guide and mentor when Jae and Bucky are offline.... The concept comes from a [blog post](https://product.reverb.com/documenting-architecture-decisions-the-reverb-way-a3563bb24bd0#.78xhdix6t) that resonated among the team when Anton shared it.
Each section of the code can have it's own markdown file in this directory, and please add a link to the readme.
## Sections
* [ABCI](./ABCI.md)
* [go-merkle / merkleeyes](./merkle.md)
* [Frey's thoughts on the data store](./merkle-frey.md)
* basecoin
* tendermint core (multiple sections)
* ???

216
docs/hidden/adr-001.md Normal file
View File

@@ -0,0 +1,216 @@
# ADR 1: Logging
## Context
Current logging system in Tendermint is very static and not flexible enough.
Issues: [358](https://github.com/tendermint/tendermint/issues/358), [375](https://github.com/tendermint/tendermint/issues/375).
What we want from the new system:
- per package dynamic log levels
- dynamic logger setting (logger tied to the processing struct)
- conventions
- be more visually appealing
"dynamic" here means the ability to set smth in runtime.
## Decision
### 1) An interface
First, we will need an interface for all of our libraries (`tmlibs`, Tendermint, etc.). My personal preference is go-kit `Logger` interface (see Appendix A.), but that is too much a bigger change. Plus we will still need levels.
```go
# log.go
type Logger interface {
Debug(msg string, keyvals ...interface{}) error
Info(msg string, keyvals ...interface{}) error
Error(msg string, keyvals ...interface{}) error
With(keyvals ...interface{}) Logger
}
```
On a side note: difference between `Info` and `Notice` is subtle. We probably
could do without `Notice`. Don't think we need `Panic` or `Fatal` as a part of
the interface. These funcs could be implemented as helpers. In fact, we already
have some in `tmlibs/common`.
- `Debug` - extended output for devs
- `Info` - all that is useful for a user
- `Error` - errors
`Notice` should become `Info`, `Warn` either `Error` or `Debug` depending on the message, `Crit` -> `Error`.
This interface should go into `tmlibs/log`. All libraries which are part of the core (tendermint/tendermint) should obey it.
### 2) Logger with our current formatting
On top of this interface, we will need to implement a stdout logger, which will be used when Tendermint is configured to output logs to STDOUT.
Many people say that they like the current output, so let's stick with it.
```
NOTE[04-25|14:45:08] ABCI Replay Blocks module=consensus appHeight=0 storeHeight=0 stateHeight=0
```
Couple of minor changes:
```
I[04-25|14:45:08.322] ABCI Replay Blocks module=consensus appHeight=0 storeHeight=0 stateHeight=0
```
Notice the level is encoded using only one char plus milliseconds.
Note: there are many other formats out there like [logfmt](https://brandur.org/logfmt).
This logger could be implemented using any logger - [logrus](https://github.com/sirupsen/logrus), [go-kit/log](https://github.com/go-kit/kit/tree/master/log), [zap](https://github.com/uber-go/zap), log15 so far as it
a) supports coloring output<br>
b) is moderately fast (buffering) <br>
c) conforms to the new interface or adapter could be written for it <br>
d) is somewhat configurable<br>
go-kit is my favorite so far. Check out how easy it is to color errors in red https://github.com/go-kit/kit/blob/master/log/term/example_test.go#L12. Although, coloring could only be applied to the whole string :(
```
go-kit +: flexible, modular
go-kit “-”: logfmt format https://brandur.org/logfmt
logrus +: popular, feature rich (hooks), API and output is more like what we want
logrus -: not so flexible
```
```go
# tm_logger.go
// NewTmLogger returns a logger that encodes keyvals to the Writer in
// tm format.
func NewTmLogger(w io.Writer) Logger {
return &tmLogger{kitlog.NewLogfmtLogger(w)}
}
func (l tmLogger) SetLevel(level string() {
switch (level) {
case "debug":
l.sourceLogger = level.NewFilter(l.sourceLogger, level.AllowDebug())
}
}
func (l tmLogger) Info(msg string, keyvals ...interface{}) error {
l.sourceLogger.Log("msg", msg, keyvals...)
}
# log.go
func With(logger Logger, keyvals ...interface{}) Logger {
kitlog.With(logger.sourceLogger, keyvals...)
}
```
Usage:
```go
logger := log.NewTmLogger(os.Stdout)
logger.SetLevel(config.GetString("log_level"))
node.SetLogger(log.With(logger, "node", Name))
```
**Other log formatters**
In the future, we may want other formatters like JSONFormatter.
```
{ "level": "notice", "time": "2017-04-25 14:45:08.562471297 -0400 EDT", "module": "consensus", "msg": "ABCI Replay Blocks", "appHeight": 0, "storeHeight": 0, "stateHeight": 0 }
```
### 3) Dynamic logger setting
https://dave.cheney.net/2017/01/23/the-package-level-logger-anti-pattern
This is the hardest part and where the most work will be done. logger should be tied to the processing struct, or the context if it adds some fields to the logger.
```go
type BaseService struct {
log log15.Logger
name string
started uint32 // atomic
stopped uint32 // atomic
...
}
```
BaseService already contains `log` field, so most of the structs embedding it should be fine. We should rename it to `logger`.
The only thing missing is the ability to set logger:
```
func (bs *BaseService) SetLogger(l log.Logger) {
bs.logger = l
}
```
### 4) Conventions
Important keyvals should go first. Example:
```
correct
I[04-25|14:45:08.322] ABCI Replay Blocks module=consensus instance=1 appHeight=0 storeHeight=0 stateHeight=0
```
not
```
wrong
I[04-25|14:45:08.322] ABCI Replay Blocks module=consensus appHeight=0 storeHeight=0 stateHeight=0 instance=1
```
for that in most cases you'll need to add `instance` field to a logger upon creating, not when u log a particular message:
```go
colorFn := func(keyvals ...interface{}) term.FgBgColor {
for i := 1; i < len(keyvals); i += 2 {
if keyvals[i] == "instance" && keyvals[i+1] == "1" {
return term.FgBgColor{Fg: term.Blue}
} else if keyvals[i] == "instance" && keyvals[i+1] == "1" {
return term.FgBgColor{Fg: term.Red}
}
}
return term.FgBgColor{}
}
logger := term.NewLogger(os.Stdout, log.NewTmLogger, colorFn)
c1 := NewConsensusReactor(...)
c1.SetLogger(log.With(logger, "instance", 1))
c2 := NewConsensusReactor(...)
c2.SetLogger(log.With(logger, "instance", 2))
```
## Status
proposed
## Consequences
### Positive
Dynamic logger, which could be turned off for some modules at runtime. Public interface for other projects using Tendermint libraries.
### Negative
We may loose the ability to color keys in keyvalue pairs. go-kit allow you to easily change foreground / background colors of the whole string, but not its parts.
### Neutral
## Appendix A.
I really like a minimalistic approach go-kit took with his logger https://github.com/go-kit/kit/tree/master/log:
```
type Logger interface {
Log(keyvals ...interface{}) error
}
```
See [The Hunt for a Logger Interface](https://go-talks.appspot.com/github.com/ChrisHines/talks/structured-logging/structured-logging.slide). The advantage is greater composability (check out how go-kit defines colored logging or log-leveled logging on top of this interface https://github.com/go-kit/kit/tree/master/log).

34
docs/hidden/adr-003.md Normal file
View File

@@ -0,0 +1,34 @@
# ADR 1: Must an ABCI-app have an RPC server?
## Context
ABCI-server could expose its own RPC-server and act as a proxy to Tendermint.
The idea was for the Tendermint RPC to just be a transparent proxy to the app.
Clients need to talk to Tendermint for proofs, unless we burden all app devs
with exposing Tendermint proof stuff. Also seems less complex to lock down one
server than two, but granted it makes querying a bit more kludgy since it needs
to be passed as a `Query`. Also, **having a very standard rpc interface means
the light-client can work with all apps and handle proofs**. The only
app-specific logic is decoding the binary data to a more readable form (eg.
json). This is a huge advantage for code-reuse and standardization.
## Decision
We dont expose an RPC server on any of our ABCI-apps.
## Status
accepted
## Consequences
### Positive
- Unified interface for all apps
### Negative
- `Query` interface
### Neutral

240
docs/hidden/merkle-frey.md Normal file
View File

@@ -0,0 +1,240 @@
# Merkle data stores - Frey's proposal
## TL;DR
To allow the efficient creation of an ABCi app, tendermint wishes to provide a reference implementation of a key-value store that provides merkle proofs of the data. These proofs then quickly allow the ABCi app to provide an app hash to the consensus engine, as well as a full proof to any client.
This is equivalent to building a database, and I would propose designing it from the API first, then looking how to implement this (or make an adapter from the API to existing implementations). Once we agree on the functionality and the interface, we can implement the API bindings, and then work on building adapters to existence merkle-ized data stores, or modifying the stores to support this interface.
We need to consider the API (both in-process and over the network), language bindings, maintaining handles to old state (and garbage collecting), persistence, security, providing merkle proofs, and general key-value store operations. To stay consistent with the blockchains "single global order of operations", this data store should only allow one connection at a time to have write access.
## Overview
* **State**
* There are two concepts of state, "committed state" and "working state"
* The working state is only accessible from the ABCi app, allows writing, but does not need to support proofs.
* When we commit the "working state", it becomes a new "committed state" and has an immutable root hash, provides proofs, and can be exposed to external clients.
* **Transactions**
* The database always allows creating a read-only transaction at the last "committed state", this transaction can serve read queries and proofs.
* The database maintains all data to serve these read transactions until they are closed by the client (or time out). This allows the client(s) to determine how much old info is needed
* The database can only support *maximal* one writable transaction at a time. This makes it easy to enforce serializability, and attempting to start a second writable transaction may trigger a panic.
* **Functionality**
* It must support efficient key-value operations (get/set/delete)
* It must support returning merkle proofs for any "committed state"
* It should support range queries on subsets of the key space if possible (ie. if the db doesn't hash keys)
* It should also support listening to changes to a desired key via pub-sub or similar method, so I can quickly notify you on a change to your balance without constant polling.
* It may support other db-specific query types as an extension to this interface, as long as all specified actions maintain their meaning.
* **Interface**
* This interface should be domain-specific - ie. designed just for this use case
* It should present a simple go interface for embedding the data store in-process
* It should create a gRPC/protobuf API for calling from any client
* It should provide and maintain client adapters from our in-process interface to gRPC client calls for at least golang and Java (maybe more languages?)
* It should provide and maintain server adapters from our gRPC calls to the in-process interface for golang at least (unless there is another server we wish to support)
* **Persistence**
* It must support atomic persistence upon committing a new block. That is, upon crash recovery, the state is guaranteed to represent the state at the end of a complete block (along with a note of which height it was).
* It must delay deletion of old data as long as there are open read-only transactions referring to it, thus we must maintain some sort of WAL to keep track of pending cleanup.
* When a transaction is closed, or when we recover from a crash, it should clean up all no longer needed data to avoid memory/storage leaks.
* **Security and Auth**
* If we allow connections over gRPC, we must consider this issues and allow both encryption (SSL), and some basic auth rules to prevent undesired access to the DB
* This is client-specific and does not need to be supported in the in-process, embedded version.
## Details
Here we go more in-depth in each of the sections, explaining the reasoning and more details on the desired behavior. This document is only the high-level architecture and should support multiple implementations. When building out a specific implementation, a similar document should be provided for that repo, showing how it implements these concepts, and details about memory usage, storage, efficiency, etc.
### State
The current ABCi interface avoids this question a bit and that has brought confusion. If I use `merkleeyes` to store data, which state is returned from `Query`? The current "working" state, which I would like to refer to in my ABCi application? Or the last committed state, which I would like to return to a client's query? Or an old state, which I may select based on height?
Right now, `merkleeyes` implements `Query` like a normal ABCi app and only returns committed state, which has lead to problems and confusion. Thus, we need to be explicit about which state we want to view. Each viewer can then specify which state it wants to view. This allows the app to query the working state in DeliverTx, but the committed state in Query.
We can easily provide two global references for "last committed" and "current working" states. However, if we want to also allow querying of older commits... then we need some way to keep track of which ones are still in use, so we can garbage collect the unneeded ones. There is a non-trivial overhead in holding references to all past states, but also a hard-coded solution (hold onto the last 5 commits) may not support all clients. We should let the client define this somehow.
### Transactions
Transactions (in the typical database sense) are a clean and established solution to this issue. We can look at the [isolations levels](https://en.wikipedia.org/wiki/Isolation_(database_systems)#Serializable) which attempt to provide us things like "repeatable reads". That means if we open a transaction, and query some data 100 times while other processes are writing to the db, we get the same result each time. This transaction has a reference to its own local state from the time the transaction started. (We are referring to the highest isolation levels here, which correlate well this the blockchain use case).
If we implement a read-only transaction as a reference to state at the time of creation of that transaction, we can then hold these references to various snapshots, one per block that we are interested, and allow the client to multiplex queries and proofs from these various blocks.
If we continue using these concepts (which have informed 30+ years of server side design), we can add a few nice features to our write transactions. The first of which is `Rollback` and `Commit`. That means all the changes we make in this transaction have no effect on the database until they are committed. And until they are committed, we can always abort if we detect an anomaly, returning to the last committed state with a rollback.
There is also a nice extension to this available on some database servers, basically, "nested" transactions or "savepoints". This means that within one transaction, you can open a subtransaction/savepoint and continue work. Later you have the option to commit or rollback all work since the savepoint/subtransaction. And then continue with the main transaction.
If you don't understand why this is useful, look at how basecoin needs to [hold cached state for AppTx](https://github.com/tendermint/basecoin/blob/master/state/execution.go#L126-L149), meaning that it rolls back all modifications if the AppTx returns an error. This was implemented as a wrapper in basecoin, but it is a reasonable thing to support in the DB interface itself (especially since the implementation becomes quite non-trivial as soon as you support range queries).
To give a bit more reference to this concept in practice, read about [Savepoints in Postgresql](https://www.postgresql.org/docs/current/static/tutorial-transactions.html) ([reference](https://www.postgresql.org/docs/current/static/sql-savepoint.html)) or [Nesting transactions in SQL Server](http://dba-presents.com/index.php/databases/sql-server/43-nesting-transactions-and-save-transaction-command) (TL;DR: scroll to the bottom, section "Real nesting transactions with SAVE TRANSACTION")
### Functionality
Merkle trees work with key-value pairs, so we should most importantly focus on the basic Key-Value operations. That is `Get`, `Set`, and `Remove`. We also need to return a merkle proof for any key, along with a root hash of the tree for committing state to the blockchain. This is just the basic merkle-tree stuff.
If it is possible with the implementation, it is nice to provide access to Range Queries. That is, return all values where the key is between X and Y. If you construct your keys wisely, it is possible to store lists (1:N) relations this way. Eg, storing blog posts and the key is blog:`poster_id`:`sequence`, then I could search for all blog posts by a given `poster_id`, or even return just posts 10-19 from the given poster.
The construction of a tree that supports range queries was one of the [design decisions of go-merkle](https://github.com/tendermint/go-merkle/blob/master/README.md). It is also kind of possible with [ethereum's patricia trie](https://github.com/ethereum/wiki/wiki/Patricia-Tree) as long as the key is less than 32 bytes.
In addition to range queries, there is one more nice feature that we could add to our data store - listening to events. Depending on your context, this is "reactive programming", "event emitters", "notifications", etc... But the basic concept is that a client can listen for all changes to a given key (or set of keys), and receive a notification when this happens. This is very important to avoid [repeated polling and wasted queries](http://resthooks.org/) when a client simply wants to [detect changes](https://www.rethinkdb.com/blog/realtime-web/).
If the database provides access to some "listener" functionality, the app can choose to expose this to the external client via websockets, web hooks, http2 push events, android push notifications, etc, etc etc.... But if we want to support modern client functionality, let's add support for this reactive paradigm in our DB interface.
**TODO** support for more advanced backends, eg. Bolt....
### Go Interface
I will start with a simple go interface to illustrate the in-process interface. Once there is agreement on how this looks, we can work out the gRPC bindings to support calling out of process. These interfaces are not finalized code, but I think the demonstrate the concepts better than text and provide a strawman to get feedback.
```
// DB represents the committed state of a merkle-ized key-value store
type DB interface {
// Snapshot returns a reference to last committed state to use for
// providing proofs, you must close it at the end to garbage collect
// the historical state we hold on to to make these proofs
Snapshot() Prover
// Start a transaction - only way to change state
// This will return an error if there is an open Transaction
Begin() (Transaction, error)
// These callbacks are triggered when the Transaction is Committed
// to the DB. They can be used to eg. notify clients via websockets when
// their account balance changes.
AddListener(key []byte, listener Listener)
RemoveListener(listener Listener)
}
// DBReader represents a read-only connection to a snapshot of the db
type DBReader interface {
// Queries on my local view
Has(key []byte) (bool, error)
Get(key []byte) (Model, error)
GetRange(start, end []byte, ascending bool, limit int) ([]Model, error)
Closer
}
// Prover is an interface that lets one query for Proofs, holding the
// data at a specific location in memory
type Prover interface {
DBReader
// Hash is the AppHash (RootHash) for this block
Hash() (hash []byte)
// Prove returns the data along with a merkle Proof
// Model and Proof are nil if not found
Prove(key []byte) (Model, Proof, error)
}
// Transaction is a set of state changes to the DB to be applied atomically.
// There can only be one open transaction at a time, which may only have
// maximum one subtransaction at a time.
// In short, at any time, there is exactly one object that can write to the
// DB, and we can use Subtransactions to group operations and roll them back
// together (kind of like `types.KVCache` from basecoin)
type Transaction interface {
DBReader
// Change the state - will raise error immediately if this Transaction
// is not holding the exclusive write lock
Set(model Model) (err error)
Remove(key []byte) (removed bool, err error)
// Subtransaction starts a new subtransaction, rollback will not affect the
// parent. Only on Commit are the changes applied to this transaction.
// While the subtransaction exists, no write allowed on the parent.
// (You must Commit or Rollback the child to continue)
Subtransaction() Transaction
// Commit this transaction (or subtransaction), the parent reference is
// now updated.
// This only updates persistant store if the top level transaction commits
// (You may have any number of nested sub transactions)
Commit() error
// Rollback ends the transaction and throw away all transaction-local state,
// allowing the tree to prune those elements.
// The parent transaction now recovers the write lock.
Rollback()
}
// Listener registers callbacks on changes to the data store
type Listener interface {
OnSet(key, value, oldValue []byte)
OnRemove(key, oldValue []byte)
}
// Proof represents a merkle proof for a key
type Proof interface {
RootHash() []byte
Verify(key, value, root []byte) bool
}
type Model interface {
Key() []byte
Value() []byte
}
// Closer releases the reference to this state, allowing us to garbage collect
// Make sure to call it before discarding.
type Closer interface {
Close()
}
```
### Remote Interface
The use-case of allowing out-of-process calls is very powerful. Not just to provide a powerful merkle-ready data store to non-go applications.
It we allow the ABCi app to maintain the only writable connections, we can guarantee that all transactions are only processed through the tendermint consensus engine. We could then allow multiple "web server" machines "read-only" access and scale out the database reads, assuming the consensus engine, ABCi logic, and public key cryptography is more the bottleneck than the database. We could even place the consensus engine, ABCi app, and data store on one machine, connected with unix sockets for security, and expose a tcp/ssl interface for reading the data, to scale out query processing over multiple machines.
But returning our focus directly to the ABCi app (which is the most important use case). An app may well want to maintain 100 or 1000 snapshots of different heights to allow people to easily query many proofs at a given height without race conditions (very important for IBC, ask Jae). Thus, we should not require a separate TCP connection for each height, as this gets quite awkward with so many connections. Also, if we want to use gRPC, we should consider the connections potentially transient (although they are more efficient with keep-alive).
Thus, the wire encoding of a transaction or a snapshot should simply return a unique id. All methods on a `Prover` or `Transaction` over the wire can send this id along with the arguments for the method call. And we just need a hash map on the server to map this id to a state.
The only negative of not requiring a persistent tcp connection for each snapshot is there is no auto-detection if the client crashes without explicitly closing the connections. Thus, I would suggest adding a `Ping` thread in the gRPC interface which keeps the Snapshot alive. If no ping is received within a server-defined time, it may automatically close those transactions. And if we consider a client with 500 snapshots that needs to ping each every 10 seconds, that is a lot of overhead, so we should design the ping to accept a list of IDs for the client and update them all. Or associate all snapshots with a clientID and then just send the clientID in the ping. (Please add other ideas on how to detect client crashes without persistent connections).
To encourage adoption, we should provide a nice client that uses this gRPC interface (like we do with ABCi). For go, the client may have the exact same interface as the in-process version, just that the error call may return network errors, not just illegal operations. We should also add a client with a clean API for Java, since that seems to be popular among app developers in the current tendermint community. Other bindings as we see the need in the server space.
### Persistence
Any data store worth it's name should not lose all data on a crash. Even [redis provides some persistence](https://redis.io/topics/persistence) these days. Ideally, if the system crashes and restarts, it should have the data at the last block N that was committed. If the system crash during the commit of block N+1, then the recovered state should either be block N or completely committed block N+1, but no partial state between the two. Basically, the commit must be an atomic operation (even if updating 100's of records).
To avoid a lot of headaches ourselves, we can use an existing data store, such as leveldb, which provides `WriteBatch` to group all operations.
The other issue is cleaning up old state. We cannot delete any information from our persistent store, as long as any snapshot holds a reference to it (or else we get some panics when the data we query is not there). So, we need to store the outstanding deletions that we can perform when the snapshot is `Close`d. In addition, we must consider the case that the data store crashes with open snapshots. Thus, the info on outstanding deletions must also be persisted somewhere. Something like a "delete-behind log" (the opposite of a "write ahead log").
This is not a concern of the generic interface, but each implementation should take care to handle this well to avoid accumulation of unused references in the data store and eventual data bloat.
#### Backing stores
It is way outside the scope of this project to build our own database that is capable of efficiently storing the data, provide multiple read-only snapshots at once, and save it atomically. The best approach seems to select an existing database (best a simple one) that provides this functionality and build upon it, much like the current `go-merkle` implementation builds upon `leveldb`. After some research here are winners and losers:
**Winners**
* Leveldb - [provides consistent snapshots](https://ayende.com/blog/161705/reviewing-leveldb-part-xiii-smile-and-here-is-your-snapshot), and [provides tooling for building ACID compliance](http://codeofrob.com/entries/writing-a-transaction-manager-on-top-of-leveldb.html)
* Note there are at least two solid implementations available in go - [goleveldb](https://github.com/syndtr/goleveldb) - a pure go implementation, and [levigo](https://github.com/jmhodges/levigo) - a go wrapper around leveldb.
* Goleveldb is much easier to compile and cross-compile (not requiring cgo), while levigo (or cleveldb) seems to provide a significant performance boosts (but I had trouble even running benchmarks)
* PostgreSQL - fully supports these ACID semantics if you call `SET TRANSACTION ISOLATION LEVEL SERIALIZABLE` at the beginning of a transaction (tested)
* This may be total overkill unless we also want to make use of other features, like storing data in multiple columns with secondary indexes.
* Trillian can show an example of [how to store a merkle tree in sql](https://github.com/google/trillian/blob/master/storage/mysql/tree_storage.go)
**Losers**
* Bolt - open [read-only snapshots can block writing](https://github.com/boltdb/bolt/issues/378)
* Mongo - [barely even supports atomic operations](https://docs.mongodb.com/manual/core/write-operations-atomicity/), much less multiple snapshots
**To investigate**
* [Trillian](https://github.com/google/trillian) - has a [persistent merkle tree interface](https://github.com/google/trillian/blob/master/storage/tree_storage.go) along with [backend storage with mysql](https://github.com/google/trillian/blob/master/storage/mysql/tree_storage.go), good inspiration for our design if not directly using it
* [Moss](https://github.com/couchbase/moss) - another key-value store in go, seems similar to leveldb, maybe compare with performance tests?
### Security
When allowing access out-of-process, we should provide different mechanisms to secure it. The first is the choice of binding to a local unix socket or a tcp port. The second is the optional use of ssl to encrypt the connection (very important over tcp). The third is authentication to control access to the database.
We may also want to consider the case of two server connections with different permissions, eg. a local unix socket that allows write access with no more credentials, and a public TCP connection with ssl and authentication that only provides read-only access.
The use of ssl is quite easy in go, we just need to generate and sign a certificate, so it is nice to be able to disable it for dev machines, but it is very important for production.
For authentication, let me sketch out a minimal solution. The server could just have a simple config file with key/bcrypt(password) pairs along with read/write permission level, and read that upon startup. The client must provide a username and password in the HTTP headers when making the original HTTPS gRPC connection.
This is super minimal to provide some protection. Things like LDAP, OAuth and single-sign on seem overkill and even potential security holes. Maybe there is another solution somewhere in the middle.

17
docs/hidden/merkle.md Normal file
View File

@@ -0,0 +1,17 @@
# Merkle data stores
To allow the efficient creation of an ABCi app, tendermint wishes to provide a reference implemention of a key-value store that provides merkle proofs of the data. These proofs then quickly allow the ABCi app to provide an apphash to the consensus engine, as well as a full proof to any client.
This engine is currently implemented in `go-merkle` with `merkleeyes` providing a language-agnostic binding via ABCi. It uses `tmlibs/db` bindings internally to persist data to leveldb.
What are some of the requirements of this store:
* It must support efficient key-value operations (get/set/delete)
* It must support persistance.
* We must only persist complete blocks, so when we come up after a crash we are at the state of block N or N+1, but not in-between these two states.
* It must allow us to read/write from one uncommited state (working state), while serving other queries from the last commited state. And a way to determine which one to serve for each client.
* It must allow us to hold references to old state, to allow providing proofs from 20 blocks ago. We can define some limits as to the maximum time to hold this data.
* We provide in process binding in Go
* We provide language-agnostic bindings when running the data store as it's own process.

28
docs/index.md Normal file
View File

@@ -0,0 +1,28 @@
# Documentation
If you're new here, start with the [Tendermint Intro](/intro).
To start building an ABCI application and integrating with Tendermint,
see the [App Development](/docs/guides/app-development)
and [App Architecture](/docs/guides/app-architecture) guides.
To learn more about running the Tendermint software, see the [Using Tendermint Guide](/docs/guides/using-tendermint).
To learn more about Tendermint's various pieces, check out the [Documentation](/docs).
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.
You 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 details on how the software has changed, and what changes are in store, see the [Changelog](https://github.com/tendermint/tendermint/blob/master/CHANGELOG.md) and the [Roadmap](https://github.com/tendermint/tendermint/blob/master/roadmap.md).
If you're interested in contributing, see our [Contributor Guidelines](https://github.com/tendermint/tendermint/blob/master/CONTRIBUTING.md)
The Tendermint [Software Ecosystem](/ecosystem) contains many example applications and related software built by the Tendermint team and others.
Check it out for some motivation and inspiration!
See our [Community](/community) page for more ways to collaborate.
You can also [get in touch with the team](/contact).
Most importantly, enjoy!

View File

@@ -0,0 +1,168 @@
# Block Structure
The tendermint consensus engine records all agreements by a supermajority of
nodes into a blockchain, which is replicated among all nodes. This blockchain
is accessible via various rpc endpoints, mainly `/block?height=` to get the full
block, as well as `/blockchain?minHeight=_&maxHeight=_` to get a list of headers.
But what exactly is stored in these blocks?
### Block
A [Block](https://godoc.org/github.com/tendermint/tendermint/types#Block) contains:
* a [Header](#header) contains merkle hashes for various chain states
* the [Data](https://godoc.org/github.com/tendermint/tendermint/types#Data) is all transactions which are to be processed
* the [LastCommit](#commit) > 2/3 signatures for the last block
The signatures returned along with block `H` are those validating block `H-1`.
This can be a little confusing, but we must also consider that the
`Header` also contains the `LastCommitHash`.
It would be impossible for a Header to include the commits that sign it, as it
would cause an infinite loop here. But when we get block `H`, we find
`Header.LastCommitHash`, which must match the hash of `LastCommit`.
### Header
The [Header](https://godoc.org/github.com/tendermint/tendermint/types#Header) contains lots of information (follow
link for up-to-date info). Notably, it maintains the `Height`, the `LastBlockID`
(to make it a chain), and hashes of the data, the app state, and the validator set.
This is important as the only item that is signed by the validators is the `Header`,
and all other data must be validated against one of the merkle hashes in the `Header`.
The `DataHash` can provide a nice check on the [Data](https://godoc.org/github.com/tendermint/tendermint/types#Data)
returned in this same block. If you are subscribed to new blocks, via tendermint RPC, in order to display or process the new transactions
you should at least validate that the `DataHash` is valid.
If it is important to verify autheniticity, you must wait for the `LastCommit` from the next block to make sure the block header (including `DataHash`) was properly signed.
The `ValidatorHash` contains a hash of the current
[Validators](https://godoc.org/github.com/tendermint/tendermint/types#Validator). Tracking all changes in the
validator set is complex, but a client can quickly compare this hash
with the [hash of the currently known validators](https://godoc.org/github.com/tendermint/tendermint/types#ValidatorSet.Hash)
to see if there have been changes.
The `AppHash` serves as the basis for validating any merkle proofs that come
from the [ABCI application](https://github.com/tendermint/abci). It represents
the state of the actual application, rather that the state of the blockchain
itself. This means it's necessary in order to perform any business logic,
such as verifying and account balance.
**Note** After the transactions are committed to a block, they still need to
be processed in a separate step, which happens between the blocks. If you
find a given transaction in the block at height `H`, the effects of running
that transaction will be first visible in the `AppHash` from the block
header at height `H+1`.
Like the `LastCommit` issue, this is a requirement of the
immutability of the block chain, as the application only applies transactions
*after* they are commited to the chain.
### Commit
The [Commit](https://godoc.org/github.com/tendermint/tendermint/types#Commit) contains a set of
[Votes](https://godoc.org/github.com/tendermint/tendermint/types#Vote) that were made by the validator set to
reach consensus on this block. This is the key to the security in any PoS
system, and actually no data that cannot be traced back to a block header
with a valid set of Votes can be trusted. Thus, getting the Commit data
and verifying the votes is extremely important.
As mentioned above, in order to find the `precommit votes` for block header `H`,
we need to query block `H+1`. Then we need to check the votes, make sure they
really are for that block, and properly formatted. Much of this code is implemented
in Go in the [light-client](https://github.com/tendermint/light-client) package.
If you look at the code, you will notice that we need to provide the `chainID`
of the blockchain in order to properly calculate the votes. This is to protect
anyone from swapping votes between chains to fake (or frame) a validator.
Also note that this `chainID` is in the `genesis.json` from _Tendermint_,
not the `genesis.json` from the basecoin app ([that is a different chainID...](https://github.com/tendermint/basecoin/issues/32)).
Once we have those votes,
and we calculated the proper [sign bytes](https://godoc.org/github.com/tendermint/tendermint/types#Vote.WriteSignBytes)
using the chainID and a [nice helper function](https://godoc.org/github.com/tendermint/tendermint/types#SignBytes),
we can verify them. The light client is responsible for maintaining a set of
validators that we trust. Each vote only stores the validators `Address`, as well
as the `Signature`. Assuming we have a local copy of the trusted validator set,
we can look up the `Public Key` of the validator given its `Address`, then
verify that the `Signature` matches the `SignBytes` and `Public Key`.
Then we sum up the total voting power of all validators, whose votes fulfilled
all these stringent requirements. If the total number of voting power for a single block is greater
than 2/3 of all voting power, then we can finally trust the
block header, the AppHash, and the proof we got from the ABCI application.
#### Vote Sign Bytes
The `sign-bytes` of a vote is produced by taking a [`stable-json`](https://github.com/substack/json-stable-stringify)-like deterministic JSON [`wire`](/docs/specs/wire-protocol) encoding of the vote (excluding the `Signature` field), and wrapping it with `{"chain_id":"my_chain","vote":...}`.
For example, a precommit vote might have the following `sign-bytes`:
```json
{"chain_id":"my_chain","vote":{"block_hash":"611801F57B4CE378DF1A3FFF1216656E89209A99","block_parts_header":{"hash":"B46697379DBE0774CC2C3B656083F07CA7E0F9CE","total":123},"height":1234,"round":1,"type":2}}
```
### Block Hash
The [block hash](https://godoc.org/github.com/tendermint/tendermint/types#Block.Hash) is the [Simple Tree hash](Merkle-Trees#simple-tree-with-dictionaries) of the fields of the block `Header` encoded as a list of `KVPair`s.
### Transaction
A transaction is any sequence of bytes. It is up to your [ABCI](https://github.com/tendermint/abci) application to accept or reject transactions.
### BlockID
Many of these data structures refer to the [BlockID](https://godoc.org/github.com/tendermint/tendermint/types#BlockID),
which is the `BlockHash` (hash of the block header, also referred to by the next block)
along with the `PartSetHeader`. The `PartSetHeader` is explained below and is used internally
to orchestrate the p2p propogation. For clients, it is basically opaque bytes,
but they must match for all votes.
### PartSetHeader
The [PartSetHeader](https://godoc.org/github.com/tendermint/tendermint/types#PartSetHeader) contains the total number of pieces in a [PartSet](https://godoc.org/github.com/tendermint/tendermint/types#PartSet), and the Merkle root hash of those pieces.
### PartSet
PartSet is used to split a byteslice of data into parts (pieces) for transmission.
By splitting data into smaller parts and computing a Merkle root hash on the list,
you can verify that a part is legitimately part of the complete data, and the
part can be forwarded to other peers before all the parts are known. In short,
it's a fast way to securely propagate a large chunk of data (like a block) over a gossip network.
PartSet was inspired by the LibSwift project.
Usage:
```go
data := RandBytes(2 << 20) // Something large
partSet := NewPartSetFromData(data)
partSet.Total() // Total number of 4KB parts
partSet.Count() // Equal to the Total, since we already have all the parts
partSet.Hash() // The Merkle root hash
partSet.BitArray() // A BitArray of partSet.Total() 1's
header := partSet.Header() // Send this to the peer
header.Total // Total number of parts
header.Hash // The merkle root hash
// Now we'll reconstruct the data from the parts
partSet2 := NewPartSetFromHeader(header)
partSet2.Total() // Same total as partSet.Total()
partSet2.Count() // Zero, since this PartSet doesn't have any parts yet.
partSet2.Hash() // Same hash as in partSet.Hash()
partSet2.BitArray() // A BitArray of partSet.Total() 0's
// In a gossip network the parts would arrive in arbitrary order, perhaps
// in response to explicit requests for parts, or optimistically in response
// to the receiving peer's partSet.BitArray().
for !partSet2.IsComplete() {
part := receivePartFromGossipNetwork()
added, err := partSet2.AddPart(part)
if err != nil {
// A wrong part,
// the merkle trail does not hash to partSet2.Hash()
} else if !added {
// A duplicate part already received
}
}
data2, _ := ioutil.ReadAll(partSet2.GetReader())
bytes.Equal(data, data2) // true
```

View File

@@ -0,0 +1,191 @@
# Byzantine Consensus Algorithm
_The draft 0.6 whitepaper is outdated. The new algorithm is detailed below. See [revisions](#revisions)_
## Terms
- The network is composed of optionally connected _nodes_. Nodes directly connected to a particular node are called _peers_.
- The consensus process in deciding the next block (at some _height_ `H`) is composed of one or many _rounds_.
- `NewHeight`, `Propose`, `Prevote`, `Precommit`, and `Commit` represent state machine states of a round. (aka `RoundStep` or just "step").
- A node is said to be _at_ a given height, round, and step, or at `(H,R,S)`, or at `(H,R)` in short to omit the step.
- To _prevote_ or _precommit_ something means to broadcast a [prevote vote](https://godoc.org/github.com/tendermint/tendermint/types#Vote) or [first precommit vote](https://godoc.org/github.com/tendermint/tendermint/types#FirstPrecommit) for something.
- A vote _at_ `(H,R)` is a vote signed with the bytes for `H` and `R` included in its [`sign-bytes`](/docs/specs/block-structure#vote-sign-bytes).
- _+2/3_ is short for "more than 2/3"
- _1/3+_ is short for "1/3 or more"
- A set of +2/3 of prevotes for a particular block or `<nil>` at `(H,R)` is called a _proof-of-lock-change_ or _PoLC_ for short.
## State Machine Overview
At each height of the blockchain a round-based protocol is run to determine
the next block. Each round is composed of three _steps_ (`Propose`, `Prevote`, and
`Precommit`), along with two special steps `Commit` and `NewHeight`.
In the optimal scenario, the order of steps is:
```
NewHeight -> (Propose -> Prevote -> Precommit)+ -> Commit -> NewHeight ->...
```
The sequence `(Propose -> Prevote -> Precommit)` is called a _round_. There may be more than one round required to commit a block at a given height. Examples for why more rounds may be required include:
- The designated proposer was not online.
- The block proposed by the designated proposer was not valid.
- The block proposed by the designated proposer did not propagate in time.
- The block proposed was valid, but +2/3 of prevotes for the proposed block were not received in time for enough validator nodes by the time they reached the `Precommit` step. Even though +2/3 of prevotes are necessary to progress to the next step, at least one validator may have voted `<nil>` or maliciously voted for something else.
- The block proposed was valid, and +2/3 of prevotes were received for enough nodes, but +2/3 of precommits for the proposed block were not received for enough validator nodes.
Some of these problems are resolved by moving onto the next round & proposer. Others are resolved by increasing certain round timeout parameters over each successive round.
## State Machine Diagram
```
+-------------------------------------+
v |(Wait til `CommmitTime+timeoutCommit`)
+-----------+ +-----+-----+
+----------> | Propose +--------------+ | NewHeight |
| +-----------+ | +-----------+
| | ^
|(Else, after timeoutPrecommit) v |
+-----+-----+ +-----------+ |
| Precommit | <------------------------+ Prevote | |
+-----+-----+ +-----------+ |
|(When +2/3 Precommits for block found) |
v |
+--------------------------------------------------------------------+
| Commit |
| |
| * Set CommitTime = now; |
| * Wait for block, then stage/save/commit block; |
+--------------------------------------------------------------------+
```
## Background Gossip
A node may not have a corresponding validator private key, but it nevertheless plays an active role in the consensus process by relaying relevant meta-data, proposals, blocks, and votes to its peers. A node that has the private keys of an active validator and is engaged in signing votes is called a _validator-node_. All nodes (not just validator-nodes) have an associated state (the current height, round, and step) and work to make progress.
Between two nodes there exists a `Connection`, and multiplexed on top of this connection are fairly throttled `Channel`s of information. An epidemic gossip protocol is implemented among some of these channels to bring peers up to speed on the most recent state of consensus. For example,
- Nodes gossip `PartSet` parts of the current round's proposer's proposed block. A LibSwift inspired algorithm is used to quickly broadcast blocks across the gossip network.
- Nodes gossip prevote/precommit votes. A node NODE_A that is ahead of NODE_B can send NODE_B prevotes or precommits for NODE_B's current (or future) round to enable it to progress forward.
- Nodes gossip prevotes for the proposed PoLC (proof-of-lock-change) round if one is proposed.
- Nodes gossip to nodes lagging in blockchain height with block [commits](https://godoc.org/github.com/tendermint/tendermint/types#Commit) for older blocks.
- Nodes opportunistically gossip `HasVote` messages to hint peers what votes it already has.
- Nodes broadcast their current state to all neighboring peers. (but is not gossiped further)
There's more, but let's not get ahead of ourselves here.
## Proposals
A proposal is signed and published by the designated proposer at each round. The proposer is chosen by a deterministic and non-choking round robin selection algorithm that selects proposers in proportion to their voting power. (see [implementation](https://github.com/tendermint/tendermint/blob/develop/types/validator_set.go))
A proposal at `(H,R)` is composed of a block and an optional latest `PoLC-Round < R` which is included iff the proposer knows of one. This hints the network to allow nodes to unlock (when safe) to ensure the liveness property.
## State Machine Spec
### Propose Step (height:H,round:R)
Upon entering `Propose`:
- The designated proposer proposes a block at `(H,R)`.
The `Propose` step ends:
- After `timeoutProposeR` after entering `Propose`. --> goto `Prevote(H,R)`
- After receiving proposal block and all prevotes at `PoLC-Round`. --> goto `Prevote(H,R)`
- After [common exit conditions](#common-exit-conditions)
### Prevote Step (height:H,round:R)
Upon entering `Prevote`, each validator broadcasts its prevote vote.
- First, if the validator is locked on a block since `LastLockRound` but now has a PoLC for something else at round `PoLC-Round` where `LastLockRound < PoLC-Round < R`, then it unlocks.
- If the validator is still locked on a block, it prevotes that.
- Else, if the proposed block from `Propose(H,R)` is good, it prevotes that.
- Else, if the proposal is invalid or wasn't received on time, it prevotes `<nil>`.
The `Prevote` step ends:
- After +2/3 prevotes for a particular block or `<nil>`. --> goto `Precommit(H,R)`
- After `timeoutPrevote` after receiving any +2/3 prevotes. --> goto `Precommit(H,R)`
- After [common exit conditions](#common-exit-conditions)
### Precommit Step (height:H,round:R)
Upon entering `Precommit`, each validator broadcasts its precommit vote.
- If the validator has a PoLC at `(H,R)` for a particular block `B`, it (re)locks (or changes lock to) and precommits `B` and sets `LastLockRound = R`.
- Else, if the validator has a PoLC at `(H,R)` for `<nil>`, it unlocks and precommits `<nil>`.
- Else, it keeps the lock unchanged and precommits `<nil>`.
A precommit for `<nil>` means "I didnt see a PoLC for this round, but I did get +2/3 prevotes and waited a bit".
The Precommit step ends:
- After +2/3 precommits for `<nil>`. --> goto `Propose(H,R+1)`
- After `timeoutPrecommit` after receiving any +2/3 precommits. --> goto `Propose(H,R+1)`
- After [common exit conditions](#common-exit-conditions)
#### common exit conditions
- After +2/3 precommits for a particular block. --> goto `Commit(H)`
- After any +2/3 prevotes received at `(H,R+x)`. --> goto `Prevote(H,R+x)`
- After any +2/3 precommits received at `(H,R+x)`. --> goto `Precommit(H,R+x)`
### Commit Step (height:H)
- Set `CommitTime = now()`
- Wait until block is received. --> goto `NewHeight(H+1)`
### NewHeight Step (height:H)
- Move `Precommits` to `LastCommit` and increment height.
- Set `StartTime = CommitTime+timeoutCommit`
- Wait until `StartTime` to receive straggler commits. --> goto `Propose(H,0)`
## Proofs
### Proof of Safety
Assume that at most -1/3 of the voting power of validators is byzantine. If a validator commits block `B` at round `R`, it's because it saw +2/3 of precommits at round `R`. This implies that 1/3+ of honest nodes are still locked at round `R' > R`. These locked validators will remain locked until they see a PoLC at `R' > R`, but this won't happen because 1/3+ are locked and honest, so at most -2/3 are available to vote for anything other than `B`.
### Proof of Liveness
If 1/3+ honest validators are locked on two different blocks from different rounds, a proposers' `PoLC-Round` will eventually cause nodes locked from the earlier round to unlock. Eventually, the designated proposer will be one that is aware of a PoLC at the later round. Also, `timeoutProposalR` increments with round `R`, while the size of a proposal are capped, so eventually the network is able to "fully gossip" the whole proposal (e.g. the block & PoLC).
### Proof of Fork Accountability
Define the JSet (justification-vote-set) at height `H` of a validator `V1` to be all the votes signed by the validator at `H` along with justification PoLC prevotes for each lock change. For example, if `V1` signed the following precommits: `Precommit(B1 @ round 0)`, `Precommit(<nil> @ round 1)`, `Precommit(B2 @ round 4)` (note that no precommits were signed for rounds 2 and 3, and that's ok), `Precommit(B1 @ round 0)` must be justified by a PoLC at round 0, and `Precommit(B2 @ round 4)` must be justified by a PoLC at round 4; but the precommit for `<nil>` at round 1 is not a lock-change by definition so the JSet for `V1` need not include any prevotes at round 1, 2, or 3 (unless `V1` happened to have prevoted for those rounds).
Further, define the JSet at height `H` of a set of validators `VSet` to be the union of the JSets for each validator in `VSet`. For a given commit by honest validators at round `R` for block `B` we can construct a JSet to justify the commit for `B` at `R`.
We say that a JSet _justifies_ a commit at `(H,R)` if all the committers (validators in the commit-set) are each justified in the JSet with no duplicitous vote signatures (by the committers).
- **Lemma**: When a fork is detected by the existence of two conflicting [commits](/docs/specs/validators#commiting-a-block), the union of the JSets for both commits (if they can be compiled) must include double-signing by at least 1/3+ of the validator set. **Proof**: The commit cannot be at the same round, because that would immediately imply double-signing by 1/3+. Take the union of the JSets of both commits. If there is no double-signing by at least 1/3+ of the validator set in the union, then no honest validator could have precommitted any different block after the first commit. Yet, +2/3 did. Reductio ad absurdum.
As a corollary, when there is a fork, an external process can determine the blame by requiring each validator to justify all of its round votes. Either we will find 1/3+ who cannot justify at least one of their votes, and/or, we will find 1/3+ who had double-signed.
### Alternative algorithm
Alternatively, we can take the JSet of a commit to be the "full commit". That is, if light clients and validators do not consider a block to be committed unless the JSet of the commit is also known, then we get the desirable property that if there ever is a fork (e.g. there are two conflicting "full commits"), then 1/3+ of the validators are immediately punishable for double-signing.
There are many ways to ensure that the gossip network efficiently share the JSet of a commit. One solution is to add a new message type that tells peers that this node has (or does not have) a +2/3 majority for B (or <nil>) at (H,R), and a bitarray of which votes contributed towards that majority. Peers can react by responding with appropriate votes.
We will implement such an algorithm for the next iteration of the Tendermint consensus protocol.
Other potential improvements include adding more data in votes such as the last known PoLC round that caused a lock change, and the last voted round/step (or, we may require that validators not skip any votes). This may make JSet verification/gossip logic easier to implement.
### Censorship Attacks
Due to the definition of a block [commit](/docs/specs/validators#commiting-a-block), any 1/3+ coalition of validators can halt the blockchain by not broadcasting their votes. Such a coalition can also censor particular transactions by rejecting blocks that include these transactions, though this would result in a significant proportion of block proposals to be rejected, which would slow down the rate of block commits of the blockchain, reducing its utility and value. The malicious coalition might also broadcast votes in a trickle so as to grind blockchain block commits to a near halt, or engage in any combination of these attacks.
If a global active adversary were also involved, it can partition the network in such a way that it may appear that the wrong subset of validators were responsible for the slowdown. This is not just a limitation of Tendermint, but rather a limitation of all consensus protocols whose network is potentially controlled by an active adversary.
### Overcoming Forks and Censorship Attacks
For these types of attacks, a subset of the validators through external means
should coordinate to sign a reorg-proposal that chooses a fork (and any evidence
thereof) and the initial subset of validators with their signatures. Validators
who sign such a reorg-proposal forego its collateral on all other forks.
Clients should verify the signatures on the reorg-proposal, verify any evidence,
and make a judgement or prompt the end-user for a decision. For example, a
phone wallet app may prompt the user with a security warning, while a
refrigerator may accept any reorg-proposal signed by +½ of the original
validators.
No non-synchronous Byzantine fault-tolerant algorithm can come to consensus when
⅓+ of validators are dishonest, yet a fork assumes that ⅓+ of validators have
already been dishonest by double-signing or lock-changing without justification.
So, signing the reorg-proposal is a coordination problem that cannot be solved
by any non-synchronous protocol (i.e. automatically, and without making
assumptions about the reliability of the underlying network). It must be
provided by means external to the weakly-synchronous Tendermint consensus
algorithm. For now, we leave the problem of reorg-proposal coordination to
human coordination via internet media. Validators must take care to ensure that
there are no significant network partitions, to avoid situations where two
conflicting reorg-proposals are signed.
Assuming that the external coordination medium and protocol is robust, it follows that forks are less of a concern than [censorship attacks](#censorship-attacks).

View File

@@ -0,0 +1,36 @@
# Configuration
TendermintCore can be configured via a TOML file in `$TMHOME/config.toml`.
Some of these parameters can be overridden by command-line flags.
### Config parameters
The main config parameters are defined [here](https://github.com/tendermint/tendermint/blob/master/config/config.go).
* `abci`: ABCI transport (socket | grpc). _Default_: `socket`
* `db_backend`: Database backend for the blockchain and TendermintCore state. `leveldb` or `memdb`. _Default_: `"leveldb"`
* `db_dir`: Database dir. _Default_: `"$TMHOME/data"`
* `fast_sync`: Whether to sync faster from the block pool. _Default_: `true`
* `genesis_file`: The location of the genesis file. _Default_: `"$TMHOME/genesis.json"`
* `log_level`: _Default_: `"state:info,*:error"`
* `moniker`: Name of this node. _Default_: `"anonymous"`
* `priv_validator_file`: Validator private key file. _Default_: `"$TMHOME/priv_validator.json"`
* `prof_laddr`: Profile listen address. _Default_: `""`
* `proxy_app`: The ABCI app endpoint. _Default_: `"tcp://127.0.0.1:46658"`
* `consensus.max_block_size_txs`: Maximum number of block txs. _Default_: `10000`
* `consensus.timeout_*`: Various consensus timeout parameters **TODO**
* `consensus.wal_file`: Consensus state WAL. _Default_: `"$TMHOME/data/cswal"`
* `consensus.wal_light`: Whether to use light-mode for Consensus state WAL. _Default_: `false`
* `mempool.*`: Various mempool parameters **TODO**
* `p2p.addr_book_file`: Peer address book. _Default_: `"$TMHOME/addrbook.json"`. **NOT USED**
* `p2p.laddr`: Node listen address. (0.0.0.0:0 means any interface, any port). _Default_: `"0.0.0.0:46656"`
* `p2p.pex`: Enable Peer-Exchange (dev feature). _Default_: `false`
* `p2p.seeds`: Comma delimited host:port seed nodes. _Default_: `""`
* `p2p.skip_upnp`: Skip UPNP detection. _Default_: `false`
* `rpc.grpc_laddr`: GRPC listen address (BroadcastTx only). Port required. _Default_: `""`
* `rpc.laddr`: RPC listen address. Port required. _Default_: `"0.0.0.0:46657"`
* `rpc.unsafe`: Enabled unsafe rpc methods. _Default_: `true`

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