Compare commits

...

308 Commits

Author SHA1 Message Date
Ethan Buchman
a017f2fdd4 Merge pull request #1654 from tendermint/release/v0.19.7
Release/v0.19.7
2018-05-31 09:15:13 -04:00
Ethan Buchman
aaaa5f23e2 changelog and version 2018-05-30 23:36:36 -04:00
Ethan Buchman
178e357d7f Merge pull request #1618 from tendermint/1494-production-notes
[docs] notes about running a production system
2018-05-30 23:23:51 -04:00
Ethan Buchman
683b527534 Merge pull request #1636 from tendermint/bucky/docs
gut docs/app-arch
2018-05-30 23:17:26 -04:00
Ethan Buchman
d584e03427 fix link and typo 2018-05-30 23:23:39 -04:00
Ethan Buchman
d454b1b25f SkipDuplicate -> AllowDuplicate; fix p2p test on mac 2018-05-30 21:44:39 -04:00
Ethan Buchman
432f21452d Merge branch 'master' into develop 2018-05-30 21:43:31 -04:00
Anton Kaliaev
f0ce8b3883 note about state syncing 2018-05-30 16:55:39 +04:00
Anton Kaliaev
3da5198631 fixes after @zramsay's review 2018-05-30 16:51:20 +04:00
Anton Kaliaev
252a0a392b move reactor descriptions to relevant specs 2018-05-30 16:51:19 +04:00
Anton Kaliaev
f7106bfb39 more config variables
Refs #1494
2018-05-30 16:51:19 +04:00
Anton Kaliaev
2a517ac98c hardware specs and configuration params
Refs #1494
2018-05-30 16:51:19 +04:00
Anton Kaliaev
b542dce2e1 [docs] signal handling
Refs #1494
2018-05-30 16:51:19 +04:00
Anton Kaliaev
82ded582f2 [docs] debugging/monitoring sections, restart handling
Refs #1494
2018-05-30 16:51:19 +04:00
Anton Kaliaev
e0d4fe2dba document DOS exposure and mitigation
Refs #1494
2018-05-30 16:51:19 +04:00
Anton Kaliaev
83c6f2864d document the consensus WAL
Refs #1494
2018-05-30 16:51:19 +04:00
Anton Kaliaev
33ec8cb609 document logging
Refs #1494
2018-05-30 16:51:19 +04:00
Ethan Buchman
43a652bcbf Merge pull request #1647 from tendermint/1643-events-query-pubsub
[docs] indexing transactions and subscribing to events
2018-05-30 08:16:08 -04:00
Ethan Buchman
094a40084d Merge pull request #1646 from tendermint/xla/duplicate-ip-check-config
Introduce option to skip duplicate ip check
2018-05-30 08:12:46 -04:00
Anton Kaliaev
eec9f142b5 [docs] indexing transactions and subscribing to events
Refs #1643
2018-05-30 13:10:04 +04:00
Alexander Simmerl
5796e879b9 Introduce option to skip duplicate ip check
In some scenarios like tests we want to disable the guard which prevents
peers connecting from the same ip.

Fixes #1632
Closes #1634
2018-05-30 10:40:22 +02:00
Ethan Buchman
846ca5ce56 Merge pull request #1641 from tendermint/release/v0.19.6
Release/v0.19.6
2018-05-29 13:14:44 -04:00
Jae Kwon
fd6021876b Potential fix for blockchain pool halting issue 2018-05-29 12:32:22 -04:00
Ethan Buchman
2763c8539c changelog and version 2018-05-29 12:31:01 -04:00
Ethan Buchman
1c0bfe1158 Merge pull request #1633 from tendermint/jae/blockchain_pool
Potential fix for blockchain pool halting issue
2018-05-29 10:27:53 -04:00
Ethan Buchman
1e87ef7f75 circle: add GOCACHE=off and -v to tests 2018-05-29 09:28:29 -04:00
Ethan Buchman
c8be091d4a gut docs/app-arch 2018-05-28 16:58:38 -04:00
Zach
ec34c8f9d2 docs: update ABCI output (#1635) 2018-05-28 22:06:02 +04:00
Anton Kaliaev
6004587347 expect all tags to be strings (#1498)
* expect all tags to be strings

Refs #1369

* port changes from https://github.com/tendermint/tmlibs/pull/204

Refs #1369
2018-05-28 14:37:11 +04:00
Jae Kwon
f55725ebfa Potential fix for blockchain pool halting issue 2018-05-26 22:08:42 -07:00
Zach
7f20eb5f8e generate RPC docs using Slate (#1612)
* generate RPC docs using Slate (#691)

* update changelog

* skip if branch not develop

* slate: only build if rpc/core has changes

* fetch develop to compare against

* slate: build on master only

* [rpc/core] use original repo, not fork in README
2018-05-25 15:59:24 +04:00
Anton Kaliaev
eeabb4c06b Merge pull request #1607 from tendermint/1600-wal-bug
[wal] small fixes in SearchEndHeight & replay logic
2018-05-25 15:41:57 +04:00
Anton Kaliaev
4da81aa0b7 commented out TestPEXReactorRunning 2018-05-25 15:11:32 +04:00
Anton Kaliaev
67068a34f2 log requesting addresses 2018-05-25 15:11:32 +04:00
Anton Kaliaev
2a0e9f93ce provide arg to error
BEFORE:

```
E[05-24|11:55:37.229] Dialing failed                               pex=0 addr=022ec801d79025caab3afbbf816d92ff8450d040@127.0.0.2:6593 err="Connect to self: <nil>" attempts=0
```

AFTER:

```
E[05-24|11:55:37.229] Dialing failed                               pex=0 addr=022ec801d79025caab3afbbf816d92ff8450d040@127.0.0.2:6593 err="Connect to self: 022ec801d79025caab3afbbf816d92ff8450d040@127.0.0.2:6593" attempts=0
```
2018-05-25 15:11:32 +04:00
Anton Kaliaev
708f35e5c1 do not look for height in older files if we've seen height - 1
Refs #1600
2018-05-25 15:11:15 +04:00
Anton Kaliaev
f3f5c7f472 we must only return io.EOF to progress to the next file in auto.Group
since we never write msg partially, if we've encountered io.EOF in the
middle of the msg, we must abort
2018-05-25 15:10:51 +04:00
Anton Kaliaev
68f6226bea data is corrupted, but this requires manual intervention
i.e., can't be skipped

and we should only return DataCorruptionError if we can skip a msg safely
2018-05-25 15:10:51 +04:00
Anton Kaliaev
118b86b1ef fix nil panic error
msg is nil and if we continue executing, we'll get nil exception at
`msg.Msg.(....)`
2018-05-25 15:10:51 +04:00
Anton Kaliaev
b9afcbe3a2 fix typo 2018-05-25 15:10:51 +04:00
Anton Kaliaev
a885af0826 Merge pull request #1574 from tendermint/847-separate-internal-pubsub
[pubsub] Prioritise internal subscribers (e.g. reactor) over external (e.g. RPC)
2018-05-24 21:09:56 +04:00
Ethan Buchman
3a947b0117 Merge pull request #1619 from tendermint/zach/cleaner-repo
clean up links & spec docs
2018-05-23 21:09:15 -04:00
Ethan Buchman
caf5afc084 Merge pull request #1520 from tendermint/bucky/p2p-same-ip
p2p: prevent connections from same ip
2018-05-23 20:57:17 -04:00
Zach Ramsay
2aa5285c66 fix from self-review 2018-05-23 10:08:57 -04:00
Zach Ramsay
b166831fb5 link to both consensus specs 2018-05-23 10:05:03 -04:00
Zach Ramsay
423fef1416 docs: use absolute links (#1617) 2018-05-23 10:01:32 -04:00
Zach Ramsay
b4d10b5b91 consensus: link to spec from readme (#1609) 2018-05-23 09:41:54 -04:00
Ethan Buchman
6f1bfb6280 Merge pull request #1616 from tendermint/zach/no-debora
remove debora scripts
2018-05-23 08:43:22 -04:00
Ethan Buchman
5e7177053c Merge pull request #1611 from tendermint/zach/dead-links
fix dead links & other doc updates
2018-05-23 08:42:25 -04:00
Zach Ramsay
a0201e7862 docker readme: update 2018-05-23 08:28:55 -04:00
Zach Ramsay
126ddca1a6 remove debora scripts (#1610) 2018-05-23 08:01:07 -04:00
Alexander Simmerl
186d38dd8a Use different loopback addresses for test switch 2018-05-23 02:36:48 +02:00
Alexander Simmerl
01fd102dba Incoporate review feedback 2018-05-23 01:56:03 +02:00
Alexander Simmerl
e11f3167ff Fix pex reactor test 2018-05-23 01:35:03 +02:00
Alexander Simmerl
7d98cfd3d6 Test duplicate IP guard in peer set 2018-05-23 01:24:27 +02:00
Alexander Simmerl
4848e88737 Fix persistent peer switch test 2018-05-23 00:24:40 +02:00
Zach Ramsay
60d7486de2 docs: fix dead links, closes #1608 2018-05-22 14:46:56 -04:00
Ethan Buchman
229c18f1bd Merge branch 'master' into develop 2018-05-21 16:13:11 -04:00
Alexander Simmerl
91b6d3f18c Do not set address for self error 2018-05-21 18:47:14 +02:00
Alexander Simmerl
20e9dd0737 Return fake IP even when there is no conn 2018-05-21 17:55:40 +02:00
Alexander Simmerl
7b02b5b66b Add RemoteIP to test implementation 2018-05-21 17:41:34 +02:00
Alexander Simmerl
0cd92a4948 Fix race in test suffix 2018-05-21 17:35:49 +02:00
Ethan Buchman
747f28f85f Merge pull request #1602 from tendermint/bucky/readme
update readme
2018-05-21 09:27:15 -04:00
Anton Kaliaev
a9d0adbdef update changelog 2018-05-21 10:56:33 +04:00
Anton Kaliaev
3485edf4f5 update test docker image Go version to 1.10 2018-05-21 10:51:47 +04:00
Anton Kaliaev
c6f612bfc3 subscribe before state emits NewRoundStep
I had to alter events package for that. Hope that's fine.
Refs #847
2018-05-21 10:51:47 +04:00
Anton Kaliaev
bb9aa85d22 copy events and pubsub packages from tmlibs
Refs #847
2018-05-21 10:51:47 +04:00
Anton Kaliaev
c4fef499b6 switch to events package 2018-05-21 10:50:55 +04:00
Anton Kaliaev
b77d5344fc rename methods for clarity 2018-05-21 10:50:55 +04:00
Anton Kaliaev
21f5f3faa7 use channels to send votes, ... from consensus state to reactor
Refs #847
2018-05-21 10:50:55 +04:00
Ethan Buchman
bf6527fc59 Merge pull request #1382 from EugeneChung/develop
remove Heap.Update() call when setting Proposer field
2018-05-20 19:32:13 -04:00
Ethan Buchman
ed8d9951c0 point to terraform-ansible docs 2018-05-20 17:05:57 -04:00
Ethan Buchman
97b39f340e update readme 2018-05-20 17:01:15 -04:00
Ethan Buchman
383c255f35 dev version bump 2018-05-20 16:54:21 -04:00
Ethan Buchman
931fb385d7 Merge pull request #1599 from tendermint/release/v0.19.5
Release/v0.19.5
2018-05-20 16:45:00 -04:00
Ethan Buchman
018e096748 Merge pull request #1601 from tendermint/bucky/wal-sync
consensus: only fsync wal after internal msgs
2018-05-20 16:36:33 -04:00
Ethan Buchman
ee4eb59355 update comments 2018-05-20 16:44:08 -04:00
Ethan Buchman
082a02e6d1 consensus: only fsync wal after internal msgs 2018-05-20 14:40:47 -04:00
Ethan Buchman
2c40966e46 fix changelog 2018-05-20 14:08:35 -04:00
Ethan Buchman
0a9dc9f875 changelog 2018-05-20 10:25:21 -04:00
Ethan Buchman
87cefb724d version 2018-05-20 10:22:42 -04:00
Ethan Buchman
6701dba876 changelog 2018-05-20 10:22:22 -04:00
Ethan Buchman
442bbe592f Merge pull request #1598 from tendermint/bucky/docs-clean
Bucky/docs clean
2018-05-20 09:46:09 -04:00
Ethan Buchman
301aa92f9c phony 2018-05-20 09:53:38 -04:00
Ethan Buchman
52f27686ef Merge pull request #1567 from tendermint/909-limit-tx-search-output
limit /tx_search output
2018-05-20 09:44:41 -04:00
Ethan Buchman
6f9867cba6 fix validate pagination params 2018-05-20 01:47:15 -04:00
Ethan Buchman
02615c8695 update readme 2018-05-20 00:41:54 -04:00
Ethan Buchman
2df137193c security.md 2018-05-20 00:33:37 -04:00
Ethan Buchman
1ef415728d docs/spec: blockchain and consensus dirs 2018-05-20 00:28:47 -04:00
Ethan Buchman
773e3917ec networks: update readmes 2018-05-19 23:35:45 -04:00
Ethan Buchman
26fdfe10fd update readme 2018-05-19 23:28:27 -04:00
Ethan Buchman
d76e2dc3ff readme.rst -> readme.md 2018-05-19 23:26:02 -04:00
Ethan Buchman
420f925a4d link bug bounty in readme 2018-05-19 23:21:42 -04:00
Ethan Buchman
d7d12c8030 update networks/local readme 2018-05-19 23:21:35 -04:00
Ethan Buchman
6c4a26f248 update readme 2018-05-19 22:44:29 -04:00
Ethan Buchman
2a26c47da5 Merge pull request #1590 from tendermint/1227-msg-bytes
nice output for msgBytes
2018-05-18 19:17:22 -04:00
Ethan Buchman
aabe96f1af Merge pull request #1594 from tendermint/zach/tests-less-bash
test cleanup
2018-05-18 19:00:44 -04:00
Zach Ramsay
0e1f730fbb rm unused file, fix docker-compose.yml 2018-05-18 14:12:01 -04:00
Zach Ramsay
0b68ec4b8e move a file to remove a directory 2018-05-18 13:37:58 -04:00
Zach Ramsay
ca120798e4 s/dummy/kvstore/g for compatibility (#1532) 2018-05-18 13:26:26 -04:00
Zach Ramsay
595fc24c56 remove very old bash file 2018-05-18 13:22:04 -04:00
Anton Kaliaev
4611cf44f0 Merge pull request #1591 from tendermint/zach/dawks
docs improvements
2018-05-18 18:32:43 +04:00
Alexander Simmerl
d596ed1bc2 Let peerConn handle IPs in for tests 2018-05-18 16:27:57 +02:00
Zach Ramsay
0fb33ca91d docs: update install instructions, closes #1580 2018-05-18 10:12:52 -04:00
Zach Ramsay
35428ceb53 docs: lil fixes 2018-05-18 10:01:56 -04:00
Ethan Buchman
de8d4325de Merge pull request #1585 from tendermint/345-mempool-cache
Mempool cache
2018-05-18 08:54:47 -04:00
Anton Kaliaev
5a041baa36 nice output for msgBytes
Closes #1227
2018-05-18 12:45:09 +04:00
Anton Kaliaev
202a43a5af remove TODO
no longer relevant, I guess. since ABCI only defines 0 (success) code.
2018-05-18 10:48:13 +04:00
Anton Kaliaev
2987158a65 [docs] add a note about replay protection
Refs #345, https://github.com/tendermint/tendermint/issues/458#issuecomment-297077148
2018-05-18 10:45:51 +04:00
Anton Kaliaev
c9001d5a11 bound the mempool
Refs #345
2018-05-18 10:45:51 +04:00
Anton Kaliaev
90446261f3 [docs] document transactional semantics
Refs #1494, #345
2018-05-18 10:45:51 +04:00
Anton Kaliaev
ae572b9038 remove extra call to Exists
Refs #345

https://github.com/tendermint/tendermint/issues/1539#issuecomment-387240606
2018-05-18 10:17:09 +04:00
Zach
0908e668bd document testnet command, closes #1535 (#1588) 2018-05-18 09:52:31 +04:00
Ethan Buchman
e0dbc3673c version bump 2018-05-17 22:28:53 -04:00
Ethan Buchman
545990f845 Merge pull request #1587 from tendermint/release/v0.19.4
Release/v0.19.4
2018-05-17 22:20:17 -04:00
Ethan Buchman
19ccd1842f version and changelog 2018-05-17 15:47:07 -04:00
Ethan Buchman
b4d6bf7697 add back new-spec/README to tell people it moved 2018-05-17 15:39:49 -04:00
Ethan Buchman
1854ce41fc Merge pull request #1586 from tendermint/bucky/add-part-fix
Bucky/add part fix
2018-05-17 15:21:29 -04:00
Ethan Buchman
547e8223b9 fix 2018-05-17 15:24:37 -04:00
Ethan Buchman
8e46df14e7 improve consensus logger 2018-05-17 13:59:41 -04:00
Anton Kaliaev
8d60a5a7bd update changelog 2018-05-17 21:25:45 +04:00
Anton Kaliaev
5115618550 add limit param to /unconfirmed_txs 2018-05-17 21:25:04 +04:00
Anton Kaliaev
a6b74b82d1 limit /tx_search output
Refs #909
2018-05-17 21:25:03 +04:00
Ethan Buchman
e5220360c5 AddPart always verifies 2018-05-17 13:17:50 -04:00
Zach
b5c4098c53 update docs/examples & terraform/ansible (#1534)
* update docs/examples

* ansible: add node IDs from docs/examples

* better monikers

* ansible: clearer paths

* upgrade version

* examples: updates

* docs: consolidate terraform & ansible

* remove deprecated info, small reorgs

* docs build fix

* docs: t&a critical commit

* s/dummy/kvstore/g

* terraform/DO region unavailable, persistent error can't be bothered to debug rn

* terraform: need vars

* networks: t&a standalone integration script for DO

* t&a more updates

* examples: add script that shows what the testnet command does

* use AMS3, since AMS2 is not available
2018-05-17 18:05:59 +04:00
Anton Kaliaev
bc8768cfea Merge pull request #1550 from tendermint/perf-improvements
Performance improvements
2018-05-17 10:50:28 +04:00
Anton Kaliaev
d832bde280 update Vagrantfile 2018-05-17 10:43:38 +04:00
Anton Kaliaev
5e3a23df6d simplify indexer service main loop 2018-05-17 10:00:59 +04:00
Anton Kaliaev
6f7333fd5f fix tests 2018-05-17 10:00:59 +04:00
Anton Kaliaev
58e3246ffc batch index txs 2018-05-17 10:00:59 +04:00
Anton Kaliaev
bbe1355957 log only hash, not tx itself 2018-05-17 10:00:59 +04:00
Anton Kaliaev
7c14fa820d do not log txs at info level
BEFORE:

```
./tm-bench -c 5 -r 1000 127.0.0.1:46657
Stats          Avg       StdDev     Max
Txs/sec        1826      843        2744
Blocks/sec     1.100     0.300      2
```

AFTER:

```
./tm-bench -T 30 -c 5 -r 1000 127.0.0.1:46657
Stats          Avg       StdDev     Max
Txs/sec        6120      1970       9776
Blocks/sec     1.000     0.000      1
```
2018-05-17 10:00:59 +04:00
Anton Kaliaev
0d93424c6a disable indexer by default 2018-05-17 10:00:59 +04:00
Anton Kaliaev
efc01cf582 stop localnet before starting
in order to avoid having to stop it manually
2018-05-17 10:00:58 +04:00
Zach
754be1887c spec: move to final location (#1576) 2018-05-17 09:58:15 +04:00
Zach
775b015173 docs: add diagram, closes #1565 (#1577) 2018-05-17 09:57:28 +04:00
Alexander Simmerl
b698a9febc Remove double locking in HasIP 2018-05-16 19:21:12 +02:00
Alexander Simmerl
c5f45275ec Use remotePeer for test switch 2018-05-16 19:21:12 +02:00
Alexander Simmerl
77f09f5b5e Move to ne.IP 2018-05-16 19:21:12 +02:00
Ethan Buchman
1fe41be929 p2p: prevent connections from same ip 2018-05-16 19:21:12 +02:00
Ethan Buchman
68a0b3f95b version bump. add roadmap back. minor fixes 2018-05-15 22:42:29 -04:00
Ethan Buchman
b1f3c11948 Merge branch 'master' into develop 2018-05-15 22:41:03 -04:00
Jae Kwon
e1a3f16fa4 Comment tweaks 2018-05-15 08:48:59 -07:00
Ethan Buchman
aab98828fe Merge pull request #1570 from tendermint/release/v0.19.3
Release/v0.19.3
2018-05-15 08:44:16 -04:00
Ethan Buchman
03f6a29a64 changelog and version 2018-05-14 17:00:57 -04:00
Ethan Buchman
4851653d8e Merge pull request #1569 from tendermint/bucky/update-valid-block-rule
Fix validBlock rule
2018-05-14 16:48:20 -04:00
Ethan Buchman
162811476a update some comments 2018-05-14 16:32:19 -04:00
Zarko Milosevic
b5ac9ede8a Add rules in gossipVotesForHeight to clarify priorities on messages to send 2018-05-14 16:18:50 -04:00
Jae Kwon
ff5dfc0c15 Add more comments for Valid* 2018-05-14 16:18:50 -04:00
Jae Kwon
d3a98675aa Refactor addVote() to be clearer 2018-05-14 16:17:21 -04:00
Jae Kwon
e3c4625e63 Suggested changes to consensus/reactor.go 2018-05-14 16:17:21 -04:00
Zarko Milosevic
01ac378c96 Update condition based on Jae input 2018-05-14 16:17:21 -04:00
Zarko Milosevic
83ca46396d Update gossipVotes routine to align with validBlock mechanism 2018-05-14 16:17:21 -04:00
Zarko Milosevic
2c125b6c78 Fix validValue rule 2018-05-14 16:17:21 -04:00
Ethan Buchman
3dde0584ed Merge pull request #1529 from tendermint/greg/persistent-script-fix
Persistence test bash fix for relative folder path
2018-05-14 15:44:27 -04:00
Greg Szabo
4bca7c1009 Added tendermint/testing docker image description 2018-05-14 12:24:54 -04:00
Greg Szabo
4be3ffbe9b Persistence test bash fix for relative folder path 2018-05-14 12:24:54 -04:00
Ethan Buchman
5b9a1423ae Merge pull request #1564 from tendermint/bucky/dump-consensus
Improve consensus state RPC
2018-05-14 12:21:54 -04:00
Ethan Buchman
16932f889f changelog 2018-05-14 10:45:18 -04:00
Ethan Buchman
e9804d76cf fixes from review 2018-05-14 10:33:31 -04:00
Ethan Buchman
a41f0d3891 rpc: /consensus_state for simplified output 2018-05-13 19:53:54 -04:00
Ethan Buchman
658060150c rpc: add voting power totals to vote bitarrays 2018-05-13 19:22:23 -04:00
Ethan Buchman
d0229e8b1e Merge pull request #1554 from tendermint/jae/expose_peer_stats
Expose peer stats for dump_consensus_state
2018-05-13 16:16:35 -04:00
Jae Kwon
56c9e0da7e Add back sample output in rpc/core/consensus.go; Tweak DumpConsensusState output for peers 2018-05-12 15:39:30 -07:00
Ethan Buchman
fbe253767e Merge pull request #1540 from tendermint/set-genesis-time-on-init
set GenesisTime to now during `tendermint init`
2018-05-12 10:10:06 -04:00
Jae Kwon
edbec10f9e Expose peer stats for dump_consensus_state 2018-05-10 22:43:21 -07:00
Allen
c0a1a8d3c0 add link to github (#1542) 2018-05-09 12:13:07 +04:00
Anton Kaliaev
ac2d3a917e set GenesisTime to now during tendermint init 2018-05-08 12:04:20 +04:00
Ethan Buchman
64408a4041 Merge pull request #1528 from tendermint/release/v0.19.2
Release/v0.19.2
2018-04-30 18:28:38 -04:00
Ethan Buchman
cae31157b1 fix lint 2018-04-30 18:23:02 -04:00
Ethan Buchman
66c2b60324 version and changelog 2018-04-30 16:06:45 -04:00
Ethan Buchman
e2e2127365 Merge pull request #1519 from tendermint/bucky/p2p-cleanup
Cleanup P2P
2018-04-30 15:54:04 -04:00
Ethan Buchman
f395b82f73 node: remove commented out trustMetric 2018-04-30 15:59:05 -04:00
Ethan Buchman
47557f868a create addrbook even if pex=false. fixes #1525 2018-04-30 08:31:03 -04:00
Ethan Buchman
b6c062c451 fixes from review 2018-04-30 08:19:19 -04:00
Max Levy
12fc396101 Split CMD's param on space (#1523)
This is to avoid "ERROR: unknown flag: --proxy_app dummy" message when running "docker-compose up"
2018-04-29 17:35:10 +03:00
Ethan Buchman
c195772de1 p2p: small lint 2018-04-28 21:17:28 -04:00
Ethan Buchman
d3c4f746a7 spec: abci notes. closes #1257 2018-04-28 21:12:25 -04:00
Ethan Buchman
fae94a44a2 p2p/pex: some addrbook fixes
* fix before/after in isBad()
* allow multiple IPs per ID even if ID isOld
2018-04-28 20:09:02 -04:00
Ethan Buchman
3a30ee75b9 minor fixes 2018-04-28 17:27:51 -04:00
Ethan Buchman
3498b676a6 update spec and addrbook.go 2018-04-28 17:14:58 -04:00
Ethan Buchman
6157c700dd forgot errors file 2018-04-28 16:15:30 -04:00
Ethan Buchman
c90bf77566 rpc: add n_peers to /net_info 2018-04-28 16:09:18 -04:00
Ethan Buchman
6805ddf1b8 p2p: change some logs from Error to Debug. #1476 2018-04-28 16:00:45 -04:00
Ethan Buchman
2761861b6b p2p: MinNumOutboundPeers. Closes #1501 2018-04-28 15:52:05 -04:00
Ethan Buchman
64569b15e5 fix build and test 2018-04-28 15:39:09 -04:00
Ethan Buchman
0450e35d67 some comments 2018-04-28 15:19:33 -04:00
Ethan Buchman
268055e549 node: remove dup code from rebase 2018-04-28 15:04:37 -04:00
Ethan Buchman
aaa81092e7 p2p: some comments and a log line 2018-04-28 15:01:33 -04:00
Ethan Buchman
3ee1d7909e p2p: explicit netaddress errors 2018-04-28 14:48:51 -04:00
Ethan Buchman
32268a8135 limit maxPexMessageSize based on maxAddressSize 2018-04-28 14:41:36 -04:00
Ethan Buchman
f645187122 spec: pex update 2018-04-28 13:11:32 -04:00
Ethan Buchman
40c79235c0 p2p: dont require minor versions to match in handshake 2018-04-28 13:09:17 -04:00
Ethan Buchman
c23909eecf p2p/pex: minor cleanup and comments 2018-04-28 13:08:44 -04:00
Ethan Buchman
936d1a0e68 some notes about the p2p layer 2018-04-28 11:35:09 -04:00
Ethan Buchman
0cbbb61962 minor cleanup 2018-04-28 01:02:39 -04:00
Ethan Buchman
fa66694f2e Merge branch 'develop' into fix-persistent-first 2018-04-28 00:34:03 -04:00
Ethan Buchman
d92def4b60 version bump 2018-04-28 00:06:05 -04:00
Ethan Buchman
26f633ed48 Merge pull request #1514 from tendermint/release/v0.19.1
Release/v0.19.1
2018-04-27 23:36:15 -04:00
Ethan Buchman
79bfbebfff rpc: docs/comments 2018-04-27 23:19:40 -04:00
Ethan Buchman
f33da8817a rpc: lower_case peer_round_states, use a list, add the node_address 2018-04-27 23:00:09 -04:00
Ethan Buchman
ffe81a0206 changelog and version 2018-04-27 12:35:21 -04:00
Ethan Buchman
0e00154fcc Merge branch 'master' into develop 2018-04-27 12:23:12 -04:00
Ethan Buchman
1ab89e6cbf update changelog 2018-04-27 12:23:00 -04:00
Ethan Buchman
14cff484f1 Merge pull request #1440 from tendermint/1428-remove-wal-light
[config] remove wal_light setting
2018-04-27 12:06:51 -04:00
Ethan Buchman
25cee8827a Merge branch 'develop' into 1428-remove-wal-light 2018-04-27 12:06:08 -04:00
Jae Kwon
65ebbccb74 Add GoAmino DeepCopy() benchmark for RoundState 2018-04-27 11:55:45 -04:00
Ethan Buchman
1188dfe7ee Merge pull request #1511 from tendermint/bucky/rpc-fixes
Various RPC fixes
2018-04-27 11:45:46 -04:00
Ethan Buchman
c45ba2967a fixes from review 2018-04-27 10:29:05 -04:00
Ethan Buchman
f67c5a9e7b forgot wire.go file 2018-04-27 10:00:34 -04:00
Ethan Buchman
593a785ae2 set NodeInfo on switch before starting RPC server
should fix #1199
2018-04-26 23:49:48 -04:00
Ethan Buchman
94e823cc91 p2p: NodeInfo.Channels is HexBytes 2018-04-26 23:43:51 -04:00
Ethan Buchman
47e4d64973 add validator address to /status 2018-04-26 23:43:51 -04:00
Ethan Buchman
a2d77cbe4e add MarshalJSON methods to fix dump_consensus_state 2018-04-26 23:43:48 -04:00
Ethan Buchman
390b592dec Merge pull request #1510 from tendermint/bucky/docs-and-fixes
JSON Indent and some docs
2018-04-26 23:03:47 -04:00
Ethan Buchman
9ab1fafdf1 some amino json for #1504 2018-04-26 22:06:15 -04:00
Ethan Buchman
94c016a04e use MarshalJSONIndent for init files. closes #1506 2018-04-26 21:32:18 -04:00
Ethan Buchman
97f3ada9c2 update godep 2018-04-26 21:31:48 -04:00
Ethan Buchman
d7d4471072 Merge pull request #1507 from tendermint/bucky/docs-pre-amino
document pre-amino pubkeys/addresses
2018-04-26 20:40:53 -04:00
Ethan Buchman
d48a6f930d document pre-amino pubkeys/addresses 2018-04-26 20:46:08 -04:00
Ethan Buchman
389a6ffa16 Merge pull request #1500 from tendermint/bucky/spec-updates
Bucky/spec updates
2018-04-26 11:44:27 -04:00
Ethan Buchman
f1ead2df70 typo 2018-04-26 11:50:45 -04:00
Ethan Buchman
e5951acfb4 SHA256 -> RIPEMD160 2018-04-26 11:47:13 -04:00
Ethan Buchman
0e1414ef9d spec: add Address spec. notes about Query 2018-04-26 11:46:20 -04:00
Ethan Buchman
97be1eef87 add abci notes 2018-04-26 11:08:34 -04:00
Anton Kaliaev
0d9004a854 [cmd] Turn off strict routability when using testnet cmd (#1493)
* [cmd] Turn off strict routability when using testnet cmd

Refs https://github.com/tendermint/tendermint/pull/1454

* use "testnet" instead of "it"
2018-04-26 16:52:11 +02:00
Ethan Buchman
91c81ef9a1 spec: note on byte arrays, clean up bitarrays and more, add merkle proof, add crypto.go script 2018-04-26 10:42:58 -04:00
Ethan Buchman
e2f0778c14 spec: update encoding.md 2018-04-26 09:36:41 -04:00
Anton Kaliaev
63f8c58009 Merge pull request #1471 from tendermint/greg/devopstools
Greg/devopstools
2018-04-25 10:37:28 +02:00
Greg Szabo
9ba208c1f5 wrapper.sh logfile check fix 2018-04-24 16:40:51 -04:00
Greg Szabo
6ce6b20993 Renamed remotenet to sentrynet to match the purpose better. 2018-04-24 14:22:19 -04:00
Anton Kaliaev
5361073439 Merge pull request #1483 from tendermint/jae/lower_case_round_state
Jae/lower case round state
2018-04-24 09:57:42 +02:00
suyuhuang
6c04465d3d Fix Prove int abci.ABCIQuery (#1485)
* fix Prove in abci query

* fix Prove in abci query

* fix doc

* fix doc
2018-04-24 09:56:25 +02:00
Greg Szabo
089ce6744c Added ansible playbook to remote networks to ship logs to logz.io 2018-04-23 22:38:49 -04:00
Max Levy
17a5c6fa1a typo fix 2018-04-23 21:03:34 +02:00
Rigel
18c3f8f3f1 writeDefaultCondigFile -> writeDefaultConfigFile (#1490) 2018-04-23 21:01:40 +02:00
Jae Kwon
b42d5a2211 blockID -> block_id for JSON 2018-04-21 20:24:50 -07:00
Jae Kwon
b20e777f53 lower_case json field names 2018-04-20 23:20:44 -07:00
Greg Szabo
659762736c Makefile targets for remotenet fixed 2018-04-18 05:56:13 -04:00
Anton Kaliaev
ece3f678da [docs/spec] update msg type and Tendermint behavior (#1468)
Refs #1422
2018-04-17 19:38:10 +02:00
Greg Szabo
5b5acbb343 Ansible README update and small fixes 2018-04-17 12:28:05 -04:00
Ethan Buchman
a2930cd723 Merge pull request #1474 from tendermint/release/v0.19.0
Release/v0.19.0
2018-04-17 10:59:36 +02:00
Ethan Buchman
45a05b4726 Merge pull request #1461 from tendermint/update-changelog
update changelog
2018-04-17 09:56:53 +02:00
Greg Szabo
8bdfe15de9 Dockerfile, localnode, sentry node scripts changes
- Updated Dockerfile and created build-docker target
- Changed localnode docker image to set permissions to more permissive (docker has different user than host system)
- Added sentry node terraform and ansible script
2018-04-16 11:34:01 -04:00
Greg Szabo
b3904b8da8 ShellChecked wrapper.sh 2018-04-13 21:34:05 -04:00
Greg Szabo
f2dae2a2d8 Moved to networks folder, introduced cloud server scripts using terraform and ansible (sentry nodes) 2018-04-13 21:03:25 -04:00
Anton Kaliaev
1706ce6f7f update changelog for 0.19.0 release 2018-04-13 10:50:34 +02:00
Jae Kwon
d0beaba7e8 Bump version to 0.19.0 2018-04-13 01:32:47 -07:00
Anton Kaliaev
c28784de5e Merge pull request #1453 from tendermint/fix-localnet
Fix permissions and folder structure for localnet
2018-04-12 17:50:08 +02:00
Anton Kaliaev
d06390638d [localnet] use routable IPs 2018-04-12 16:02:31 +02:00
Anton Kaliaev
3a0edc561d log error from AddrBook#AddAddress in DialPeersAsync
Refs #1434
2018-04-12 15:51:17 +02:00
Anton Kaliaev
f8ed578325 [localnet] execute cmd from root
not secure, but we don't care because it's local tooling
2018-04-12 15:51:32 +02:00
Anton Kaliaev
5babaf9a88 [localnet] fix folder permissions errors 2018-04-12 15:51:17 +02:00
Greg Szabo
c0610b2c32 Greg/localnet (#1450)
* Added new Makefile targets for local testnet running using docker

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

* Fixes for the new tendermint testnet command

* More fixes on tendermint testnet and docker-compose

* Changed logging

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

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

* relax permissions

* cleanup output dir every time

* do not remove dir

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

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

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

* fix misuse of ResultStatus.

* updated changelog

* Fixed tests

* fixed rpc helper tests

* fixed rpc_tests

* fixed mock/status_test

* fixed typo

* fixed ommitempty on validatorstatus and the changelog

* fixed extra line in changelog

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

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

* Reformated the ResultStatus

* fix misuse of ResultStatus.

* updated changelog

* Fixed tests

* fixed rpc helper tests

* fixed rpc_tests

* fixed mock/status_test

* fixed typo

* fixed ommitempty on validatorstatus and the changelog

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

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

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

* Fixed tests and replaced NodeID with ID

* Removed unnecessary method ID()

* Fixed codec_test.go

* Fixed codec_test.go

* Removed unnecessary bracket

* Fixed all tests

* Fixed peer_set_test.go

* Fixed peer_test.go

* Fixed common_test.go

* Fixed common_test.go

* Renamed node_id to id

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

* Replaced NodeInfo pubKey to ID

* Fixed codec_test.go

* Fixed peer_set_test.go

* Fix pex_reactor_test.go

* Refactored code for privateKey initiali

* Fixed peer_set_test.go

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

* Fixed pointer to a string

* generate node_key when running tendermint init

* [docs] prefix IPs with node IDs

Refs #1429

* gen_node_key cmd

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

* fix data race

Closes #1442

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

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

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

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

* removed excessive comment

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

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

* use the tag interface for pubsub.

* update tmlibs.

* Fix unresolved conflict.

* improve `show_node_id` (#1433)

* fix show_node_id

* make LoadNodeKey public

* make LoadNodeKey public

* remove if

* remove if
2018-04-11 10:11:11 +02:00
Anton Kaliaev
e88f74bb9b remove wal_light setting
Closes #1428
2018-04-11 10:08:03 +02:00
suyuhuang
384b3ea065 improve show_node_id (#1433)
* fix show_node_id

* make LoadNodeKey public

* make LoadNodeKey public

* remove if

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

* update tmlibs.

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

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

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

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

Goroutine 47 (running) created at:
  net/http.(*Server).Serve()
      /usr/lib/go-1.9/src/net/http/server.go:2720 +0x37c
  net/http.Serve()
      /usr/lib/go-1.9/src/net/http/server.go:2323 +0xe2
  github.com/tendermint/tendermint/rpc/lib/server.StartHTTPServer.func1()
      /home/vagrant/go/src/github.com/tendermint/tendermint/rpc/lib/server/http_server.go:35 +0xb3
```
2018-04-10 11:15:16 +02:00
Anton Kaliaev
609452958c [docs/specification/secure-p2p] add a note about config 2018-04-09 17:02:48 +02:00
Anton Kaliaev
c954fca376 gen_node_key cmd 2018-04-09 17:02:47 +02:00
Anton Kaliaev
9be16d56ba [docs] prefix IPs with node IDs
Refs #1429
2018-04-09 17:02:47 +02:00
Anton Kaliaev
2b732bc11a generate node_key when running tendermint init 2018-04-09 17:02:47 +02:00
Ethan Buchman
dcd00b0e68 update deps and changelog 2018-04-09 16:36:42 +03:00
Ethan Buchman
ff3f35c5f4 Merge pull request #1347 from tendermint/jae/aminoify
Convert Tendermint to use GoAmino
2018-04-09 16:24:38 +03:00
Ethan Buchman
93c4312cdd Merge pull request #1432 from tendermint/bucky/aminoify
Bucky/aminoify
2018-04-09 15:20:36 +03:00
Ethan Buchman
1a1e4e767b check max msg size in DecodeMessage 2018-04-09 15:18:47 +03:00
Ethan Buchman
bb1b249e8a types: lock block on MakePartSet 2018-04-09 15:04:59 +03:00
Ethan Buchman
c778d7f5d1 fix addresses 2018-04-07 23:13:41 +03:00
Ethan Buchman
bb9b12d67a add scripts/wire2amino.go 2018-04-07 22:04:28 +03:00
Ethan Buchman
767521ac52 update test/p2p/data for amino 2018-04-07 22:03:48 +03:00
Ethan Buchman
df9bf60b05 forgot Gopkg.lock 2018-04-07 20:59:13 +03:00
Ethan Buchman
466c3ab1c7 forgot node/wire.go 2018-04-07 19:53:29 +03:00
Ethan Buchman
c68d406195 fix tests 2018-04-07 19:47:19 +03:00
Ethan Buchman
02c0835e9b fixes post merge 2018-04-07 16:25:10 +03:00
Ethan Buchman
c170800fbd Merge branch 'develop' into jae/aminoify 2018-04-07 16:16:53 +03:00
Jae Kwon
7afe74a963 Update go-crypto to 0.6.1 and change config/toml.go privval address 2018-04-07 02:01:45 -07:00
Javed Khan
5d8767e656 p2p: don't use dial funcn in peerconfig 2018-04-07 12:51:51 +05:30
Javed Khan
54adb790f2 p2p: switch - reconnect only if persistent 2018-04-07 11:46:48 +05:30
Jae Kwon
02531ca5a3 Fix race testing (cont;) Bump version to 0.19.0 2018-04-06 17:06:46 -07:00
Jae Kwon
d24e4cb821 Fix race testing 2018-04-06 17:02:29 -07:00
Jae Kwon
fb64314d1c Review from Anton 2018-04-06 13:46:40 -07:00
Jae Kwon
32e1d195a0 Fix cmd and lite 2018-04-05 22:05:30 -07:00
Jae Kwon
3ca5292dc9 Fix rpc tests 2018-04-05 21:19:14 -07:00
Jae Kwon
c541d58d2f WIP: fix rpc/core 2018-04-05 16:07:29 -07:00
Jae Kwon
3037b5b7ca Fix rpc/lib/... 2018-04-05 15:45:11 -07:00
Jae Kwon
e4492afbad Merge 2018-04-05 08:17:10 -07:00
Ethan Buchman
799beebd36 fix consensus tests 2018-04-05 17:54:26 +03:00
Jae Kwon
45ec5fd170 WIP consensus 2018-04-05 07:05:45 -07:00
Jae Kwon
5d1c758730 Fix evidence 2018-04-05 05:43:23 -07:00
Jae Kwon
1b9323f105 Fix blockchain tests 2018-04-05 05:17:43 -07:00
Jae Kwon
196f8410ba WIP commit; Fix types/results_test 2018-04-03 07:03:08 -07:00
Jae Kwon
89cdde7f1e Fix state tests 2018-04-03 06:50:53 -07:00
Javed Khan
5ef639fcbe p2p: persistent - redial if first dial fails
Fixes #1401
2018-04-03 09:27:06 +05:30
Jae Kwon
35a1d747b0 Fix mempool 2018-03-31 11:51:32 +02:00
Jae Kwon
34974e3932 Make types use Amino; Refactor PrivValidator* to FilePV/SocketPV 2018-03-31 00:18:43 +02:00
Eugene Chung
34f5d439ee remove Heap.Update() call when setting Proposer field
In for loop of IncrementAccum(), Heap.Update() call is unnecessary when i == times - 1.
2018-03-28 12:58:53 +09:00
Jae Kwon
901b456151 P2P now works with Amino 2018-03-26 06:40:02 +02:00
Jae Kwon
ced74251e9 maxPacketMsg -> packetMsgMax... 2018-03-21 02:47:38 +01:00
Jae Kwon
6c345f9fa2 First stab: p2p/conn 2018-03-21 02:27:10 +01:00
372 changed files with 14459 additions and 6328 deletions

View File

@@ -77,6 +77,22 @@ jobs:
paths:
- "bin/abci*"
build_slate:
<<: *defaults
steps:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v1-pkg-cache
- restore_cache:
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
- run:
name: slate docs
command: |
set -ex
export PATH="$GOBIN:$PATH"
make build-slate
lint:
<<: *defaults
steps:
@@ -123,7 +139,7 @@ jobs:
for pkg in $(go list github.com/tendermint/tendermint/... | grep -v /vendor/ | circleci tests split --split-by=timings); do
id=$(basename "$pkg")
go test -timeout 5m -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg"
GOCACHE=off go test -v -timeout 5m -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg"
done
- persist_to_workspace:
root: /tmp/workspace
@@ -153,7 +169,7 @@ jobs:
- checkout
- run: mkdir -p $GOPATH/src/github.com/tendermint
- run: ln -sf /home/circleci/project $GOPATH/src/github.com/tendermint/tendermint
- run: bash test/circleci/p2p.sh
- run: bash test/p2p/circleci.sh
upload_coverage:
<<: *defaults
@@ -180,6 +196,9 @@ workflows:
test-suite:
jobs:
- setup_dependencies
- build_slate:
requires:
- setup_dependencies
- setup_abci:
requires:
- setup_dependencies

View File

@@ -19,10 +19,6 @@ in a case of bug.
**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**:

4
.gitignore vendored
View File

@@ -5,7 +5,6 @@
.DS_Store
build/*
rpc/test/.tendermint
.debora
.tendermint
remote_dump
.revision
@@ -13,7 +12,6 @@ vendor
.vagrant
test/p2p/data/
test/logs
.glide
coverage.txt
docs/_build
docs/tools
@@ -25,3 +23,5 @@ scripts/cutWALUntil/cutWALUntil
.idea/
*.iml
libs/pubsub/query/fuzz_test/output

View File

@@ -1,28 +1,158 @@
# Changelog
## Roadmap
## 0.19.7
BREAKING CHANGES:
- Better support for injecting randomness
- Upgrade consensus for more real-time use of evidence
*May 31st, 2018*
FEATURES:
- Use the chain as its own CA for nodes and validators
- Tooling to run multiple blockchains/apps, possibly in a single process
- State syncing (without transaction replay)
- Add authentication and rate-limitting to the RPC
BREAKING:
- [libs/pubsub] TagMap#Get returns a string value
- [libs/pubsub] NewTagMap accepts a map of strings
FEATURES
- [rpc] the RPC documentation is now published to https://tendermint.github.io/slate
- [p2p] AllowDuplicateIP config option to refuse connections from same IP.
- true by default for now, false by default in next breaking release
- [docs] Add docs for query, tx indexing, events, pubsub
- [docs] Add some notes about running Tendermint in production
IMPROVEMENTS:
- Improve subtleties around mempool caching and logic
- Consensus optimizations:
- cache block parts for faster agreement after round changes
- propagate block parts rarest first
- Better testing of the consensus state machine (ie. use a DSL)
- Auto compiled serialization/deserialization code instead of go-wire reflection
- [consensus] consensus reactor now receives events from a separate event bus,
which is not dependant on external RPC load
- [consensus/wal] do not look for height in older files if we've seen height - 1
- [docs] Various cleanup and link fixes
## 0.19.6
*May 29th, 2018*
BUG FIXES
- [blockchain] Fix fast-sync deadlock during high peer turnover
## 0.19.5
*May 20th, 2018*
BREAKING CHANGES
- [rpc/client] TxSearch and UnconfirmedTxs have new arguments (see below)
- [rpc/client] TxSearch returns ResultTxSearch
- [version] Breaking changes to Go APIs will not be reflected in breaking
version change, but will be included in changelog.
FEATURES
- [rpc] `/tx_search` takes `page` (starts at 1) and `per_page` (max 100, default 30) args to paginate results
- [rpc] `/unconfirmed_txs` takes `limit` (max 100, default 30) arg to limit the output
- [config] `mempool.size` and `mempool.cache_size` options
IMPROVEMENTS
- [docs] Lots of updates
- [consensus] Only Fsync() the WAL before executing msgs from ourselves
BUG FIXES
- [mempool] Enforce upper bound on number of transactions
## 0.19.4 (May 17th, 2018)
IMPROVEMENTS
- [state] Improve tx indexing by using batches
- [consensus, state] Improve logging (more consensus logs, fewer tx logs)
- [spec] Moved to `docs/spec` (TODO cleanup the rest of the docs ...)
BUG FIXES
- [consensus] Fix issue #1575 where a late proposer can get stuck
## 0.19.3 (May 14th, 2018)
FEATURES
- [rpc] New `/consensus_state` returns just the votes seen at the current height
IMPROVEMENTS
- [rpc] Add stringified votes and fraction of power voted to `/dump_consensus_state`
- [rpc] Add PeerStateStats to `/dump_consensus_state`
BUG FIXES
- [cmd] Set GenesisTime during `tendermint init`
- [consensus] fix ValidBlock rules
## 0.19.2 (April 30th, 2018)
FEATURES:
- [p2p] Allow peers with different Minor versions to connect
- [rpc] `/net_info` includes `n_peers`
IMPROVEMENTS:
- [p2p] Various code comments, cleanup, error types
- [p2p] Change some Error logs to Debug
BUG FIXES:
- Graceful handling/recovery for apps that have non-determinism or fail to halt
- Graceful handling/recovery for violations of safety, or liveness
- [p2p] Fix reconnect to persistent peer when first dial fails
- [p2p] Validate NodeInfo.ListenAddr
- [p2p] Only allow (MaxNumPeers - MaxNumOutboundPeers) inbound peers
- [p2p/pex] Limit max msg size to 64kB
- [p2p] Fix panic when pex=false
- [p2p] Allow multiple IPs per ID in AddrBook
- [p2p] Fix before/after bugs in addrbook isBad()
## 0.19.1 (April 27th, 2018)
Note this release includes some small breaking changes in the RPC and one in the
config that are really bug fixes. v0.19.1 will work with existing chains, and make Tendermint
easier to use and debug. With <3
BREAKING (MINOR)
- [config] Removed `wal_light` setting. If you really needed this, let us know
FEATURES:
- [networks] moved in tooling from devops repo: terraform and ansible scripts for deploying testnets !
- [cmd] Added `gen_node_key` command
BUG FIXES
Some of these are breaking in the RPC response, but they're really bugs!
- [spec] Document address format and pubkey encoding pre and post Amino
- [rpc] Lower case JSON field names
- [rpc] Fix missing entries, improve, and lower case the fields in `/dump_consensus_state`
- [rpc] Fix NodeInfo.Channels format to hex
- [rpc] Add Validator address to `/status`
- [rpc] Fix `prove` in ABCIQuery
- [cmd] MarshalJSONIndent on init
## 0.19.0 (April 13th, 2018)
BREAKING:
- [cmd] improved `testnet` command; now it can fill in `persistent_peers` for you in the config file and much more (see `tendermint testnet --help` for details)
- [cmd] `show_node_id` now returns an error if there is no node key
- [rpc]: changed the output format for the `/status` endpoint (see https://godoc.org/github.com/tendermint/tendermint/rpc/core#Status)
Upgrade from go-wire to go-amino. This is a sweeping change that breaks everything that is
serialized to disk or over the network.
See github.com/tendermint/go-amino for details on the new format.
See `scripts/wire2amino.go` for a tool to upgrade
genesis/priv_validator/node_key JSON files.
FEATURES
- [test] docker-compose for local testnet setup (thanks Greg!)
## 0.18.0 (April 6th, 2018)

1
DOCKER/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
tendermint

View File

@@ -1,45 +1,39 @@
FROM alpine:3.7
MAINTAINER Greg Szabo <greg@tendermint.com>
# This is the release of tendermint to pull in.
ENV TM_VERSION 0.17.1
ENV TM_SHA256SUM d57008c63d2d9176861137e38ed203da486febf20ae7d388fb810a75afff8f24
# 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.
# Tendermint will be looking for the genesis file in /tendermint/config/genesis.json
# (unless you change `genesis_file` in config.toml). You can put your config.toml and
# private validator file into /tendermint/config.
#
# The /tendermint/data dir is used by tendermint to store state.
ENV DATA_ROOT /tendermint
ENV TMHOME $DATA_ROOT
# Set user right away for determinism
RUN addgroup tmuser && \
adduser -S -G tmuser tmuser
# Create directory for persistence and give our user ownership
RUN mkdir -p $DATA_ROOT && \
chown -R tmuser:tmuser $DATA_ROOT
ENV TMHOME /tendermint
# OS environment setup
# Set user right away for determinism, create directory for persistence and give our user ownership
# jq and curl used for extracting `pub_key` from private validator while
# deploying tendermint with Kubernetes. It is nice to have bash so the users
# could execute bash commands.
RUN apk add --no-cache bash curl jq
RUN apk update && \
apk upgrade && \
apk --no-cache add curl jq bash && \
addgroup tmuser && \
adduser -S -G tmuser tmuser -h "$TMHOME"
RUN apk add --no-cache openssl && \
wget https://github.com/tendermint/tendermint/releases/download/v${TM_VERSION}/tendermint_${TM_VERSION}_linux_amd64.zip && \
echo "${TM_SHA256SUM} tendermint_${TM_VERSION}_linux_amd64.zip" | sha256sum -c && \
unzip -d /bin tendermint_${TM_VERSION}_linux_amd64.zip && \
apk del openssl && \
rm -f tendermint_${TM_VERSION}_linux_amd64.zip
# Run the container with tmuser by default. (UID=100, GID=1000)
USER tmuser
# Expose the data directory as a volume since there's mutable state in there
VOLUME $DATA_ROOT
VOLUME [ $TMHOME ]
# p2p port
EXPOSE 46656
# rpc port
EXPOSE 46657
WORKDIR $TMHOME
ENTRYPOINT ["tendermint"]
# p2p and rpc port
EXPOSE 46656 46657
ENTRYPOINT ["/usr/bin/tendermint"]
CMD ["node", "--moniker=`hostname`"]
STOPSIGNAL SIGTERM
ARG BINARY=tendermint
COPY $BINARY /usr/bin/tendermint

18
DOCKER/Dockerfile.testing Normal file
View File

@@ -0,0 +1,18 @@
FROM golang:1.10.1
# Grab deps (jq, hexdump, xxd, killall)
RUN apt-get update && \
apt-get install -y --no-install-recommends \
jq bsdmainutils vim-common psmisc netcat
# Add testing deps for curl
RUN echo 'deb http://httpredir.debian.org/debian testing main non-free contrib' >> /etc/apt/sources.list && \
apt-get update && \
apt-get install -y --no-install-recommends curl
VOLUME /go
EXPOSE 46656
EXPOSE 46657

View File

@@ -7,6 +7,9 @@ push:
build_develop:
docker build -t "tendermint/tendermint:develop" -f Dockerfile.develop .
build_testing:
docker build --tag tendermint/testing -f ./Dockerfile.testing .
push_develop:
docker push "tendermint/tendermint:develop"

View File

@@ -17,7 +17,7 @@
# Quick reference
* **Where to get help:**
https://tendermint.com/community
https://cosmos.network/community
* **Where to file issues:**
https://github.com/tendermint/tendermint/issues
@@ -37,25 +37,29 @@ To get started developing applications, see the [application developers guide](h
## Start one instance of the Tendermint core with the `kvstore` app
A very simple example of a built-in app and Tendermint core in one container.
A quick example of a built-in app and Tendermint core in one container.
```
docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint init
docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint node --proxy_app=kvstore
```
## mintnet-kubernetes
# Local cluster
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.
To run a 4-node network, see the `Makefile` in the root of [the repo](https://github.com/tendermint/tendermint/master/Makefile) and run:
```
make build-linux
make build-docker-localnode
make localnet-start
```
Note that this will build and use a different image than the ones provided here.
# License
View [license information](https://raw.githubusercontent.com/tendermint/tendermint/master/LICENSE) for the software contained in this image.
- Tendermint's license is [Apache 2.0](https://github.com/tendermint/tendermint/master/LICENSE).
# User Feedback
# Contributing
## 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.
Contributions are most welcome! See the [contributing file](https://github.com/tendermint/tendermint/blob/master/CONTRIBUTING.md) for more information.

45
Gopkg.lock generated
View File

@@ -5,7 +5,7 @@
branch = "master"
name = "github.com/btcsuite/btcd"
packages = ["btcec"]
revision = "2be2f12b358dc57d70b8f501b00be450192efbc3"
revision = "675abc5df3c5531bc741b56a765e35623459da6d"
[[projects]]
name = "github.com/davecgh/go-spew"
@@ -159,7 +159,7 @@
branch = "master"
name = "github.com/rcrowley/go-metrics"
packages = ["."]
revision = "8732c616f52954686704c8645fe1a9d59e9df7c1"
revision = "d932a24a8ccb8fcadc993e5c6c58f93dac168294"
[[projects]]
name = "github.com/spf13/afero"
@@ -191,8 +191,8 @@
[[projects]]
name = "github.com/spf13/pflag"
packages = ["."]
revision = "e57e3eeb33f795204c1ca35f56c44f83227c6e66"
version = "v1.0.0"
revision = "583c0c0531f06d5278b7d917446061adc344b5cd"
version = "v1.0.1"
[[projects]]
name = "github.com/spf13/viper"
@@ -238,8 +238,8 @@
"server",
"types"
]
revision = "46686763ba8ea595ede16530ed4a40fb38f49f94"
version = "v0.10.2"
revision = "78a8905690ef54f9d57e3b2b0ee7ad3a04ef3f1f"
version = "v0.10.3"
[[projects]]
branch = "master"
@@ -251,20 +251,22 @@
]
revision = "d8387025d2b9d158cf4efb07e7ebf814bcce2057"
[[projects]]
name = "github.com/tendermint/go-amino"
packages = ["."]
revision = "ed62928576cfcaf887209dc96142cd79cdfff389"
version = "0.9.9"
[[projects]]
name = "github.com/tendermint/go-crypto"
packages = ["."]
revision = "c3e19f3ea26f5c3357e0bcbb799b0761ef923755"
version = "v0.5.0"
revision = "915416979bf70efa4bcbf1c6cd5d64c5fff9fc19"
version = "v0.6.2"
[[projects]]
name = "github.com/tendermint/go-wire"
packages = [
".",
"data"
]
packages = ["."]
revision = "fa721242b042ecd4c6ed1a934ee740db4f74e45c"
source = "github.com/tendermint/go-amino"
version = "v0.7.3"
[[projects]]
@@ -279,12 +281,10 @@
"flowrate",
"log",
"merkle",
"pubsub",
"pubsub/query",
"test"
]
revision = "2e24b64fc121dcdf1cabceab8dc2f7257675483c"
version = "0.8.1"
revision = "cc5f287c4798ffe88c04d02df219ecb6932080fd"
version = "v0.8.3-rc0"
[[projects]]
branch = "master"
@@ -299,13 +299,14 @@
"ripemd160",
"salsa20/salsa"
]
revision = "b2aa35443fbc700ab74c586ae79b81c171851023"
revision = "b0697eccbea9adec5b7ba8008f4c33d98d733388"
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = [
"context",
"http/httpguts",
"http2",
"http2/hpack",
"idna",
@@ -313,13 +314,13 @@
"lex/httplex",
"trace"
]
revision = "b3c676e531a6dc479fa1b35ac961c13f5e2b4d2e"
revision = "5f9ae10d9af5b1c89ae6904293b14b064d4ada23"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["unix"]
revision = "1d206c9fa8975fb4cf00df1dc8bf3283dc24ba0e"
revision = "bb9c189858d91f42db229b04d45a4c3d23a7662a"
[[projects]]
name = "golang.org/x/text"
@@ -346,7 +347,7 @@
branch = "master"
name = "google.golang.org/genproto"
packages = ["googleapis/rpc/status"]
revision = "35de2414665fc36f56b72d982c5af480d86de5ab"
revision = "7fd901a49ba6a7f87732eb344f6e3c5b19d1b200"
[[projects]]
name = "google.golang.org/grpc"
@@ -381,6 +382,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "b7c02a311569ec5fe2197614444fb231ea60f3e65a11a20e318421f1752054d7"
inputs-digest = "d85c98dcac32cc1fe05d006aa75e8985f6447a150a041b972a673a65e7681da9"
solver-name = "gps-cdcl"
solver-version = 1

View File

@@ -26,12 +26,12 @@
[[constraint]]
branch = "master"
name = "github.com/ebuchman/fail-test"
branch = "master"
[[constraint]]
branch = "master"
name = "github.com/fortytw2/leaktest"
branch = "master"
[[constraint]]
name = "github.com/go-kit/kit"
@@ -54,8 +54,8 @@
version = "~0.8.0"
[[constraint]]
branch = "master"
name = "github.com/rcrowley/go-metrics"
branch = "master"
[[constraint]]
name = "github.com/spf13/cobra"
@@ -71,22 +71,19 @@
[[constraint]]
name = "github.com/tendermint/abci"
version = "~0.10.2"
version = "~0.10.3"
[[constraint]]
name = "github.com/tendermint/go-crypto"
version = "~0.5.0"
version = "~0.6.2"
[[constraint]]
name = "github.com/tendermint/go-wire"
source = "github.com/tendermint/go-amino"
version = "~0.7.3"
name = "github.com/tendermint/go-amino"
version = "0.9.9"
[[override]]
# [[constraint]]
name = "github.com/tendermint/tmlibs"
version = "~0.8.1"
# branch = "develop"
version = "~0.8.3-rc0"
[[constraint]]
name = "google.golang.org/grpc"

54
Makefile Normal file → Executable file
View File

@@ -178,7 +178,59 @@ metalinter_all:
@echo "--> Running linter (all)"
gometalinter.v2 --vendor --deadline=600s --enable-all --disable=lll ./...
###########################################################
### Docker image
build-docker:
cp build/tendermint DOCKER/tendermint
docker build --label=tendermint --tag="tendermint/tendermint" DOCKER
rm -rf DOCKER/tendermint
###########################################################
### Local testnet using docker
# Build linux binary on other platforms
build-linux:
GOOS=linux GOARCH=amd64 $(MAKE) build
build-docker-localnode:
cd networks/local
make
# Run a 4-node testnet locally
localnet-start: localnet-stop
@if ! [ -f build/node0/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/tendermint:Z tendermint/localnode testnet --v 4 --o . --populate-persistent-peers --starting-ip-address 192.167.10.2 ; fi
docker-compose up
# Stop testnet
localnet-stop:
docker-compose down
###########################################################
### Remote full-nodes (sentry) using terraform and ansible
# Server management
sentry-start:
@if [ -z "$(DO_API_TOKEN)" ]; then echo "DO_API_TOKEN environment variable not set." ; false ; fi
@if ! [ -f $(HOME)/.ssh/id_rsa.pub ]; then ssh-keygen ; fi
cd networks/remote/terraform && terraform init && terraform apply -var DO_API_TOKEN="$(DO_API_TOKEN)" -var SSH_KEY_FILE="$(HOME)/.ssh/id_rsa.pub"
@if ! [ -f $(CURDIR)/build/node0/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/tendermint:Z tendermint/localnode testnet --v 0 --n 4 --o . ; fi
cd networks/remote/ansible && ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook -i inventory/digital_ocean.py -l sentrynet install.yml
@echo "Next step: Add your validator setup in the genesis.json and config.tml files and run \"make sentry-config\". (Public key of validator, chain ID, peer IP and node ID.)"
# Configuration management
sentry-config:
cd networks/remote/ansible && ansible-playbook -i inventory/digital_ocean.py -l sentrynet config.yml -e BINARY=$(CURDIR)/build/tendermint -e CONFIGDIR=$(CURDIR)/build
sentry-stop:
@if [ -z "$(DO_API_TOKEN)" ]; then echo "DO_API_TOKEN environment variable not set." ; false ; fi
cd networks/remote/terraform && terraform destroy -var DO_API_TOKEN="$(DO_API_TOKEN)" -var SSH_KEY_FILE="$(HOME)/.ssh/id_rsa.pub"
# meant for the CI, inspect script & adapt accordingly
build-slate:
bash scripts/slate.sh
# To avoid unintended conflicts with file names, always add to .PHONY
# unless there is a reason not to.
# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
.PHONY: check build build_race dist install check_tools get_tools update_tools get_vendor_deps draw_deps test_cover test_apps test_persistence test_p2p test test_race test_integrations test_release test100 vagrant_test fmt
.PHONY: check build build_race dist install check_tools get_tools update_tools get_vendor_deps draw_deps test_cover test_apps test_persistence test_p2p test test_race test_integrations test_release test100 vagrant_test fmt build-linux localnet-start localnet-stop build-docker build-docker-localnode sentry-start sentry-config sentry-stop build-slate

View File

@@ -24,7 +24,14 @@ _NOTE: This is alpha software. Please contact us if you intend to run it in prod
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 information, from introduction to install to application development, [Read The Docs](https://tendermint.readthedocs.io/en/master/).
For protocol details, see [the specification](/docs/spec).
## Security
To report a security vulnerability, see our [bug bounty
program](https://tendermint.com/security).
For examples of the kinds of bugs we're looking for, see [SECURITY.md](SECURITY.md)
## Minimum requirements
@@ -34,19 +41,21 @@ Go version | Go1.9 or higher
## Install
To download pre-built binaries, see our [downloads page](https://tendermint.com/downloads).
See the [install instructions](/docs/install.rst)
To install from source, you should be able to:
## Quick Start
`go get -u github.com/tendermint/tendermint/cmd/tendermint`
For more details (or if it fails), [read the docs](https://tendermint.readthedocs.io/en/master/install.html).
- [Single node](/docs/using-tendermint.rst)
- [Local cluster using docker-compose](/networks/local)
- [Remote cluster using terraform and ansible](/docs/terraform-and-ansible.rst)
- [Join the public testnet](https://cosmos.network/testnet)
## Resources
### Tendermint Core
All resources involving the use of, building application on, or developing for, tendermint, can be found at [Read The Docs](https://tendermint.readthedocs.io/en/master/). Additional information about some - and eventually all - of the sub-projects below, can be found at Read The Docs.
For more, [Read The Docs](https://tendermint.readthedocs.io/en/master/).
Additional information about some - and eventually all - of the sub-projects below, can be found at Read The Docs.
### Sub-projects
@@ -61,8 +70,8 @@ All resources involving the use of, building application on, or developing for,
### Applications
* [Ethermint](http://github.com/tendermint/ethermint); Ethereum on Tendermint
* [Cosmos SDK](http://github.com/cosmos/cosmos-sdk); a cryptocurrency application framework
* [Ethermint](http://github.com/tendermint/ethermint); Ethereum on Tendermint
* [Many more](https://tendermint.readthedocs.io/en/master/ecosystem.html#abci-applications)
### More
@@ -85,7 +94,11 @@ According to SemVer, anything in the public API can change at any time before ve
To provide some stability to Tendermint users in these 0.X.X days, the MINOR version is used
to signal breaking changes across a subset of the total public API. This subset includes all
interfaces exposed to other processes (cli, rpc, p2p, etc.), as well as parts of the following packages:
interfaces exposed to other processes (cli, rpc, p2p, etc.), but does not
include the in-process Go APIs.
That said, breaking changes in the following packages will be documented in the
CHANGELOG even if they don't lead to MINOR version bumps:
- types
- rpc/client

23
ROADMAP.md Normal file
View File

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

71
SECURITY.md Normal file
View File

@@ -0,0 +1,71 @@
# Security
As part of our [Coordinated Vulnerability Disclosure
Policy](https://tendermint.com/security), we operate a bug bounty.
See the policy for more details on submissions and rewards.
Here is a list of examples of the kinds of bugs we're most interested in:
## Specification
- Conceptual flaws
- Ambiguities, inconsistencies, or incorrect statements
- Mis-match between specification and implementation of any component
## Consensus
Assuming less than 1/3 of the voting power is Byzantine (malicious):
- Validation of blockchain data structures, including blocks, block parts,
votes, and so on
- Execution of blocks
- Validator set changes
- Proposer round robin
- Two nodes committing conflicting blocks for the same height (safety failure)
- A correct node signing conflicting votes
- A node halting (liveness failure)
- Syncing new and old nodes
## Networking
- Authenticated encryption (MITM, information leakage)
- Eclipse attacks
- Sybil attacks
- Long-range attacks
- Denial-of-Service
## RPC
- Write-access to anything besides sending transactions
- Denial-of-Service
- Leakage of secrets
## Denial-of-Service
Attacks may come through the P2P network or the RPC:
- Amplification attacks
- Resource abuse
- Deadlocks and race conditions
- Panics and unhandled errors
## Libraries
- Serialization (Amino)
- Reading/Writing files and databases
- Logging and monitoring
## Cryptography
- Elliptic curves for validator signatures
- Hash algorithms and Merkle trees for block validation
- Authenticated encryption for P2P connections
## Light Client
- Validation of blockchain data structures
- Correctly validating an incorrect proof
- Incorrectly validating a correct proof
- Syncing validator set changes

34
Vagrantfile vendored
View File

@@ -10,31 +10,37 @@ Vagrant.configure("2") do |config|
end
config.vm.provision "shell", inline: <<-SHELL
# add docker repo
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu xenial stable"
# and golang 1.9 support
# official repo doesn't have race detection runtime...
# add-apt-repository ppa:gophers/archive
add-apt-repository ppa:longsleep/golang-backports
apt-get update
# install base requirements
apt-get update
apt-get install -y --no-install-recommends wget curl jq zip \
make shellcheck bsdmainutils psmisc
apt-get install -y docker-ce golang-1.9-go
apt-get install -y language-pack-en
# install docker
apt-get install -y --no-install-recommends apt-transport-https \
ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
apt-get install -y docker-ce
usermod -a -G docker vagrant
# install go
wget -q https://dl.google.com/go/go1.10.1.linux-amd64.tar.gz
tar -xvf go1.10.1.linux-amd64.tar.gz
mv go /usr/local
rm -f go1.10.1.linux-amd64.tar.gz
# cleanup
apt-get autoremove -y
# needed for docker
usermod -a -G docker vagrant
# set env variables
echo 'export PATH=$PATH:/usr/lib/go-1.9/bin:/home/vagrant/go/bin' >> /home/vagrant/.bash_profile
echo 'export GOROOT=/usr/local/go' >> /home/vagrant/.bash_profile
echo 'export GOPATH=/home/vagrant/go' >> /home/vagrant/.bash_profile
echo 'export PATH=$PATH:$GOROOT/bin:$GOPATH/bin' >> /home/vagrant/.bash_profile
echo 'export LC_ALL=en_US.UTF-8' >> /home/vagrant/.bash_profile
echo 'cd go/src/github.com/tendermint/tendermint' >> /home/vagrant/.bash_profile

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"math"
"sync"
"sync/atomic"
"time"
cmn "github.com/tendermint/tmlibs/common"
@@ -66,11 +67,13 @@ type BlockPool struct {
// block requests
requesters map[int64]*bpRequester
height int64 // the lowest key in requesters.
numPending int32 // number of requests pending assignment or block response
// peers
peers map[p2p.ID]*bpPeer
maxPeerHeight int64
// atomic
numPending int32 // number of requests pending assignment or block response
requestsCh chan<- BlockRequest
errorsCh chan<- peerError
}
@@ -151,7 +154,7 @@ func (pool *BlockPool) GetStatus() (height int64, numPending int32, lenRequester
pool.mtx.Lock()
defer pool.mtx.Unlock()
return pool.height, pool.numPending, len(pool.requesters)
return pool.height, atomic.LoadInt32(&pool.numPending), len(pool.requesters)
}
// TODO: relax conditions, prevent abuse.
@@ -245,7 +248,7 @@ func (pool *BlockPool) AddBlock(peerID p2p.ID, block *types.Block, blockSize int
}
if requester.setBlock(block, peerID) {
pool.numPending--
atomic.AddInt32(&pool.numPending, -1)
peer := pool.peers[peerID]
if peer != nil {
peer.decrPending(blockSize)
@@ -291,10 +294,7 @@ func (pool *BlockPool) RemovePeer(peerID p2p.ID) {
func (pool *BlockPool) removePeer(peerID p2p.ID) {
for _, requester := range pool.requesters {
if requester.getPeerID() == peerID {
if requester.getBlock() != nil {
pool.numPending++
}
go requester.redo() // pick another peer and ...
requester.redo()
}
}
delete(pool.peers, peerID)
@@ -332,7 +332,7 @@ func (pool *BlockPool) makeNextRequester() {
// request.SetLogger(pool.Logger.With("height", nextHeight))
pool.requesters[nextHeight] = request
pool.numPending++
atomic.AddInt32(&pool.numPending, 1)
err := request.Start()
if err != nil {
@@ -360,7 +360,7 @@ func (pool *BlockPool) sendError(err error, peerID p2p.ID) {
// unused by tendermint; left for debugging purposes
func (pool *BlockPool) debug() string {
pool.mtx.Lock() // Lock
pool.mtx.Lock()
defer pool.mtx.Unlock()
str := ""
@@ -466,8 +466,8 @@ func newBPRequester(pool *BlockPool, height int64) *bpRequester {
bpr := &bpRequester{
pool: pool,
height: height,
gotBlockCh: make(chan struct{}),
redoCh: make(chan struct{}),
gotBlockCh: make(chan struct{}, 1),
redoCh: make(chan struct{}, 1),
peerID: "",
block: nil,
@@ -481,7 +481,7 @@ func (bpr *bpRequester) OnStart() error {
return nil
}
// Returns true if the peer matches
// Returns true if the peer matches and block doesn't already exist.
func (bpr *bpRequester) setBlock(block *types.Block, peerID p2p.ID) bool {
bpr.mtx.Lock()
if bpr.block != nil || bpr.peerID != peerID {
@@ -491,7 +491,10 @@ func (bpr *bpRequester) setBlock(block *types.Block, peerID p2p.ID) bool {
bpr.block = block
bpr.mtx.Unlock()
bpr.gotBlockCh <- struct{}{}
select {
case bpr.gotBlockCh <- struct{}{}:
default:
}
return true
}
@@ -507,17 +510,27 @@ func (bpr *bpRequester) getPeerID() p2p.ID {
return bpr.peerID
}
// This is called from the requestRoutine, upon redo().
func (bpr *bpRequester) reset() {
bpr.mtx.Lock()
defer bpr.mtx.Unlock()
if bpr.block != nil {
atomic.AddInt32(&bpr.pool.numPending, 1)
}
bpr.peerID = ""
bpr.block = nil
bpr.mtx.Unlock()
}
// Tells bpRequester to pick another peer and try again.
// NOTE: blocking
// NOTE: Nonblocking, and does nothing if another redo
// was already requested.
func (bpr *bpRequester) redo() {
bpr.redoCh <- struct{}{}
select {
case bpr.redoCh <- struct{}{}:
default:
}
}
// Responsible for making more requests as necessary
@@ -546,17 +559,8 @@ OUTER_LOOP:
// Send request and wait.
bpr.pool.sendRequest(bpr.height, peer.id)
select {
case <-bpr.pool.Quit():
bpr.Stop()
return
case <-bpr.Quit():
return
case <-bpr.redoCh:
bpr.reset()
continue OUTER_LOOP // When peer is removed
case <-bpr.gotBlockCh:
// We got the block, now see if it's good.
WAIT_LOOP:
for {
select {
case <-bpr.pool.Quit():
bpr.Stop()
@@ -566,6 +570,10 @@ OUTER_LOOP:
case <-bpr.redoCh:
bpr.reset()
continue OUTER_LOOP
case <-bpr.gotBlockCh:
// We got a block!
// Continue the for-loop and wait til Quit.
continue WAIT_LOOP
}
}
}

View File

@@ -1,21 +1,16 @@
package blockchain
import (
"bytes"
"errors"
"fmt"
"reflect"
"sync"
"time"
wire "github.com/tendermint/go-wire"
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/log"
"github.com/tendermint/go-amino"
"github.com/tendermint/tendermint/p2p"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/log"
)
const (
@@ -31,6 +26,13 @@ const (
statusUpdateIntervalSeconds = 10
// check if we should switch to consensus reactor
switchToConsensusIntervalSeconds = 1
// NOTE: keep up to date with bcBlockResponseMessage
bcBlockResponseMessagePrefixSize = 4
bcBlockResponseMessageFieldKeySize = 1
maxMsgSize = types.MaxBlockSizeBytes +
bcBlockResponseMessagePrefixSize +
bcBlockResponseMessageFieldKeySize
)
type consensusReactor interface {
@@ -52,9 +54,6 @@ func (e peerError) Error() string {
type BlockchainReactor struct {
p2p.BaseReactor
mtx sync.Mutex
params types.ConsensusParams
// immutable
initialState sm.State
@@ -87,7 +86,6 @@ func NewBlockchainReactor(state sm.State, blockExec *sm.BlockExecutor, store *Bl
)
bcR := &BlockchainReactor{
params: state.ConsensusParams,
initialState: state,
blockExec: blockExec,
store: store,
@@ -131,17 +129,19 @@ func (bcR *BlockchainReactor) OnStop() {
func (bcR *BlockchainReactor) GetChannels() []*p2p.ChannelDescriptor {
return []*p2p.ChannelDescriptor{
{
ID: BlockchainChannel,
Priority: 10,
SendQueueCapacity: 1000,
ID: BlockchainChannel,
Priority: 10,
SendQueueCapacity: 1000,
RecvBufferCapacity: 50 * 4096,
RecvMessageCapacity: maxMsgSize,
},
}
}
// AddPeer implements Reactor by sending our state to peer.
func (bcR *BlockchainReactor) AddPeer(peer p2p.Peer) {
if !peer.Send(BlockchainChannel,
struct{ BlockchainMessage }{&bcStatusResponseMessage{bcR.store.Height()}}) {
msgBytes := cdc.MustMarshalBinaryBare(&bcStatusResponseMessage{bcR.store.Height()})
if !peer.Send(BlockchainChannel, msgBytes) {
// doing nothing, will try later in `poolRoutine`
}
// peer is added to the pool once we receive the first
@@ -162,20 +162,19 @@ func (bcR *BlockchainReactor) respondToPeer(msg *bcBlockRequestMessage,
block := bcR.store.LoadBlock(msg.Height)
if block != nil {
msg := &bcBlockResponseMessage{Block: block}
return src.TrySend(BlockchainChannel, struct{ BlockchainMessage }{msg})
msgBytes := cdc.MustMarshalBinaryBare(&bcBlockResponseMessage{Block: block})
return src.TrySend(BlockchainChannel, msgBytes)
}
bcR.Logger.Info("Peer asking for a block we don't have", "src", src, "height", msg.Height)
return src.TrySend(BlockchainChannel, struct{ BlockchainMessage }{
&bcNoBlockResponseMessage{Height: msg.Height},
})
msgBytes := cdc.MustMarshalBinaryBare(&bcNoBlockResponseMessage{Height: msg.Height})
return src.TrySend(BlockchainChannel, msgBytes)
}
// 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, bcR.maxMsgSize())
msg, err := DecodeMessage(msgBytes)
if err != nil {
bcR.Logger.Error("Error decoding message", "src", src, "chId", chID, "msg", msg, "err", err, "bytes", msgBytes)
bcR.Switch.StopPeerForError(src, err)
@@ -194,8 +193,8 @@ func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
bcR.pool.AddBlock(src.ID(), msg.Block, len(msgBytes))
case *bcStatusRequestMessage:
// Send peer our state.
queued := src.TrySend(BlockchainChannel,
struct{ BlockchainMessage }{&bcStatusResponseMessage{bcR.store.Height()}})
msgBytes := cdc.MustMarshalBinaryBare(&bcStatusResponseMessage{bcR.store.Height()})
queued := src.TrySend(BlockchainChannel, msgBytes)
if !queued {
// sorry
}
@@ -207,21 +206,6 @@ func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
}
}
// maxMsgSize returns the maximum allowable size of a
// message on the blockchain reactor.
func (bcR *BlockchainReactor) maxMsgSize() int {
bcR.mtx.Lock()
defer bcR.mtx.Unlock()
return bcR.params.BlockSize.MaxBytes + 2
}
// updateConsensusParams updates the internal consensus params
func (bcR *BlockchainReactor) updateConsensusParams(params types.ConsensusParams) {
bcR.mtx.Lock()
defer bcR.mtx.Unlock()
bcR.params = params
}
// Handle messages from the poolReactor telling the reactor what to do.
// NOTE: Don't sleep in the FOR_LOOP or otherwise slow it down!
// (Except for the SYNC_LOOP, which is the primary purpose and must be synchronous.)
@@ -247,8 +231,8 @@ FOR_LOOP:
if peer == nil {
continue FOR_LOOP // Peer has since been disconnected.
}
msg := &bcBlockRequestMessage{request.Height}
queued := peer.TrySend(BlockchainChannel, struct{ BlockchainMessage }{msg})
msgBytes := cdc.MustMarshalBinaryBare(&bcBlockRequestMessage{request.Height})
queued := peer.TrySend(BlockchainChannel, msgBytes)
if !queued {
// We couldn't make the request, send-queue full.
// The pool handles timeouts, just let it go.
@@ -321,9 +305,6 @@ FOR_LOOP:
}
blocksSynced++
// update the consensus params
bcR.updateConsensusParams(state.ConsensusParams)
if blocksSynced%100 == 0 {
lastRate = 0.9*lastRate + 0.1*(100/time.Since(lastHundred).Seconds())
bcR.Logger.Info("Fast Sync Rate", "height", bcR.pool.height,
@@ -341,43 +322,36 @@ FOR_LOOP:
// BroadcastStatusRequest broadcasts `BlockStore` height.
func (bcR *BlockchainReactor) BroadcastStatusRequest() error {
bcR.Switch.Broadcast(BlockchainChannel,
struct{ BlockchainMessage }{&bcStatusRequestMessage{bcR.store.Height()}})
msgBytes := cdc.MustMarshalBinaryBare(&bcStatusRequestMessage{bcR.store.Height()})
bcR.Switch.Broadcast(BlockchainChannel, msgBytes)
return nil
}
//-----------------------------------------------------------------------------
// Messages
const (
msgTypeBlockRequest = byte(0x10)
msgTypeBlockResponse = byte(0x11)
msgTypeNoBlockResponse = byte(0x12)
msgTypeStatusResponse = byte(0x20)
msgTypeStatusRequest = byte(0x21)
)
// BlockchainMessage is a generic message for this reactor.
type BlockchainMessage interface{}
var _ = wire.RegisterInterface(
struct{ BlockchainMessage }{},
wire.ConcreteType{&bcBlockRequestMessage{}, msgTypeBlockRequest},
wire.ConcreteType{&bcBlockResponseMessage{}, msgTypeBlockResponse},
wire.ConcreteType{&bcNoBlockResponseMessage{}, msgTypeNoBlockResponse},
wire.ConcreteType{&bcStatusResponseMessage{}, msgTypeStatusResponse},
wire.ConcreteType{&bcStatusRequestMessage{}, msgTypeStatusRequest},
)
func RegisterBlockchainMessages(cdc *amino.Codec) {
cdc.RegisterInterface((*BlockchainMessage)(nil), nil)
cdc.RegisterConcrete(&bcBlockRequestMessage{}, "tendermint/mempool/BlockRequest", nil)
cdc.RegisterConcrete(&bcBlockResponseMessage{}, "tendermint/mempool/BlockResponse", nil)
cdc.RegisterConcrete(&bcNoBlockResponseMessage{}, "tendermint/mempool/NoBlockResponse", nil)
cdc.RegisterConcrete(&bcStatusResponseMessage{}, "tendermint/mempool/StatusResponse", nil)
cdc.RegisterConcrete(&bcStatusRequestMessage{}, "tendermint/mempool/StatusRequest", nil)
}
// DecodeMessage decodes BlockchainMessage.
// TODO: ensure that bz is completely read.
func DecodeMessage(bz []byte, maxSize int) (msgType byte, msg BlockchainMessage, err error) {
msgType = bz[0]
n := int(0)
r := bytes.NewReader(bz)
msg = wire.ReadBinary(struct{ BlockchainMessage }{}, r, maxSize, &n, &err).(struct{ BlockchainMessage }).BlockchainMessage
if err != nil && n != len(bz) {
err = errors.New("DecodeMessage() had bytes left over")
func DecodeMessage(bz []byte) (msg BlockchainMessage, err error) {
if len(bz) > maxMsgSize {
return msg, fmt.Errorf("Msg exceeds max size (%d > %d)",
len(bz), maxMsgSize)
}
err = cdc.UnmarshalBinaryBare(bz, &msg)
if err != nil {
err = cmn.ErrorWrap(err, "DecodeMessage() had bytes left over")
}
return
}
@@ -402,7 +376,6 @@ func (brm *bcNoBlockResponseMessage) String() string {
//-------------------------------------
// NOTE: keep up-to-date with maxBlockchainResponseSize
type bcBlockResponseMessage struct {
Block *types.Block
}

View File

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

View File

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

View File

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

13
blockchain/wire.go Normal file
View File

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

View File

@@ -30,7 +30,7 @@ func main() {
"privPath", *privValPath,
)
privVal := priv_val.LoadPrivValidatorJSON(*privValPath)
privVal := priv_val.LoadFilePV(*privValPath)
rs := priv_val.NewRemoteSigner(
logger,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -16,10 +16,12 @@ var ShowNodeIDCmd = &cobra.Command{
}
func showNodeID(cmd *cobra.Command, args []string) error {
nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
nodeKey, err := p2p.LoadNodeKey(config.NodeKeyFile())
if err != nil {
return err
}
fmt.Println(nodeKey.ID())
return nil
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -270,7 +270,7 @@ type P2PConfig struct {
FlushThrottleTimeout int `mapstructure:"flush_throttle_timeout"`
// Maximum size of a message packet payload, in bytes
MaxMsgPacketPayloadSize int `mapstructure:"max_msg_packet_payload_size"`
MaxPacketMsgPayloadSize int `mapstructure:"max_packet_msg_payload_size"`
// Rate at which packets can be sent, in bytes/second
SendRate int64 `mapstructure:"send_rate"`
@@ -292,6 +292,9 @@ type P2PConfig struct {
// Comma separated list of peer IDs to keep private (will not be gossiped to other peers)
PrivatePeerIDs string `mapstructure:"private_peer_ids"`
// Toggle to disable guard against peers connecting from the same ip.
AllowDuplicateIP bool `mapstructure:"allow_duplicate_ip"`
}
// DefaultP2PConfig returns a default configuration for the peer-to-peer layer
@@ -302,12 +305,13 @@ func DefaultP2PConfig() *P2PConfig {
AddrBookStrict: true,
MaxNumPeers: 50,
FlushThrottleTimeout: 100,
MaxMsgPacketPayloadSize: 1024, // 1 kB
MaxPacketMsgPayloadSize: 1024, // 1 kB
SendRate: 512000, // 500 kB/s
RecvRate: 512000, // 500 kB/s
PexReactor: true,
SeedMode: false,
AuthEnc: true,
AllowDuplicateIP: true, // so non-breaking yet
}
}
@@ -317,6 +321,7 @@ func TestP2PConfig() *P2PConfig {
cfg.ListenAddress = "tcp://0.0.0.0:36656"
cfg.SkipUPNP = true
cfg.FlushThrottleTimeout = 10
cfg.AllowDuplicateIP = true
return cfg
}
@@ -335,6 +340,7 @@ type MempoolConfig struct {
RecheckEmpty bool `mapstructure:"recheck_empty"`
Broadcast bool `mapstructure:"broadcast"`
WalPath string `mapstructure:"wal_dir"`
Size int `mapstructure:"size"`
CacheSize int `mapstructure:"cache_size"`
}
@@ -345,6 +351,7 @@ func DefaultMempoolConfig() *MempoolConfig {
RecheckEmpty: true,
Broadcast: true,
WalPath: filepath.Join(defaultDataDir, "mempool.wal"),
Size: 100000,
CacheSize: 100000,
}
}
@@ -367,10 +374,9 @@ func (cfg *MempoolConfig) WalDir() string {
// 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
RootDir string `mapstructure:"home"`
WalPath string `mapstructure:"wal_file"`
walFile string // overrides WalPath if set
// All timeouts are in milliseconds
TimeoutPropose int `mapstructure:"timeout_propose"`
@@ -401,7 +407,6 @@ type ConsensusConfig struct {
func DefaultConsensusConfig() *ConsensusConfig {
return &ConsensusConfig{
WalPath: filepath.Join(defaultDataDir, "cs.wal", "wal"),
WalLight: false,
TimeoutPropose: 3000,
TimeoutProposeDelta: 500,
TimeoutPrevote: 1000,

View File

@@ -37,16 +37,21 @@ func EnsureRoot(rootDir string) {
// Write default config file if missing.
if !cmn.FileExists(configFilePath) {
writeConfigFile(configFilePath)
writeDefaultConfigFile(configFilePath)
}
}
// XXX: this func should probably be called by cmd/tendermint/commands/init.go
// alongside the writing of the genesis.json and priv_validator.json
func writeConfigFile(configFilePath string) {
func writeDefaultConfigFile(configFilePath string) {
WriteConfigFile(configFilePath, DefaultConfig())
}
// WriteConfigFile renders config using the template and writes it to configFilePath.
func WriteConfigFile(configFilePath string, config *Config) {
var buffer bytes.Buffer
if err := configTemplate.Execute(&buffer, DefaultConfig()); err != nil {
if err := configTemplate.Execute(&buffer, config); err != nil {
panic(err)
}
@@ -124,11 +129,11 @@ unsafe = {{ .RPC.Unsafe }}
laddr = "{{ .P2P.ListenAddress }}"
# Comma separated list of seed nodes to connect to
seeds = ""
seeds = "{{ .P2P.Seeds }}"
# Comma separated list of nodes to keep persistent connections to
# Do not add private peers to this list if you don't want them advertised
persistent_peers = ""
persistent_peers = "{{ .P2P.PersistentPeers }}"
# Path to address book
addr_book_file = "{{ .P2P.AddrBook }}"
@@ -143,7 +148,7 @@ flush_throttle_timeout = {{ .P2P.FlushThrottleTimeout }}
max_num_peers = {{ .P2P.MaxNumPeers }}
# Maximum size of a message packet payload, in bytes
max_msg_packet_payload_size = {{ .P2P.MaxMsgPacketPayloadSize }}
max_packet_msg_payload_size = {{ .P2P.MaxPacketMsgPayloadSize }}
# Rate at which packets can be sent, in bytes/second
send_rate = {{ .P2P.SendRate }}
@@ -174,11 +179,16 @@ recheck_empty = {{ .Mempool.RecheckEmpty }}
broadcast = {{ .Mempool.Broadcast }}
wal_dir = "{{ .Mempool.WalPath }}"
# size of the mempool
size = {{ .Mempool.Size }}
# size of the cache (used to filter transactions we saw earlier)
cache_size = {{ .Mempool.CacheSize }}
##### consensus configuration options #####
[consensus]
wal_file = "{{ .Consensus.WalPath }}"
wal_light = {{ .Consensus.WalLight }}
# All timeouts are in milliseconds
timeout_propose = {{ .Consensus.TimeoutPropose }}
@@ -262,7 +272,7 @@ func ResetTestRoot(testName string) *Config {
// Write default config file if missing.
if !cmn.FileExists(configFilePath) {
writeConfigFile(configFilePath)
writeDefaultConfigFile(configFilePath)
}
if !cmn.FileExists(genesisFilePath) {
cmn.MustWriteFile(genesisFilePath, []byte(testGenesis), 0644)
@@ -280,8 +290,8 @@ var testGenesis = `{
"validators": [
{
"pub_key": {
"type": "ed25519",
"data":"3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8"
"type": "AC26791624DE60",
"value":"AT/+aaL1eB0477Mud9JMm8Sh8BIvOYlPGC9KkIUmFaE="
},
"power": 10,
"name": ""
@@ -291,14 +301,14 @@ var testGenesis = `{
}`
var testPrivValidator = `{
"address": "D028C9981F7A87F3093672BF0D5B0E2A1B3ED456",
"address": "849CB2C877F87A20925F35D00AE6688342D25B47",
"pub_key": {
"type": "ed25519",
"data": "3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8"
"type": "AC26791624DE60",
"value": "AT/+aaL1eB0477Mud9JMm8Sh8BIvOYlPGC9KkIUmFaE="
},
"priv_key": {
"type": "ed25519",
"data": "27F82582AEFAE7AB151CFB01C48BB6C1A0DA78F9BDDA979A9F70A84D074EB07D3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8"
"type": "954568A3288910",
"value": "EVkqJO/jIXp3rkASXfh9YnyToYXRXhBr6g9cQVxPFnQBP/5povV4HTjvsy530kybxKHwEi85iU8YL0qQhSYVoQ=="
},
"last_height": 0,
"last_round": 0,

View File

@@ -1,18 +1 @@
# The core consensus algorithm.
* state.go - The state machine as detailed in the whitepaper
* reactor.go - A reactor that connects the state machine to the gossip network
# Go-routine summary
The reactor runs 2 go-routines for each added peer: gossipDataRoutine and gossipVotesRoutine.
The consensus state runs two persistent go-routines: timeoutRoutine and receiveRoutine.
Go-routines are also started to trigger timeouts and to avoid blocking when the internalMsgQueue is really backed up.
# Replay/WAL
A write-ahead log is used to record all messages processed by the receiveRoutine,
which amounts to all inputs to the consensus state machine:
messages from peers, messages from ourselves, and timeouts.
They can be played back deterministically at startup or using the replay console.
See the [consensus spec](https://github.com/tendermint/tendermint/tree/master/docs/spec/consensus) and the [reactor consensus spec](https://github.com/tendermint/tendermint/tree/master/docs/spec/reactors/consensus) for more information.

View File

@@ -7,7 +7,6 @@ import (
"time"
"github.com/stretchr/testify/require"
crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common"
@@ -28,7 +27,7 @@ func init() {
// Heal partition and ensure A sees the commit
func TestByzantine(t *testing.T) {
N := 4
logger := consensusLogger()
logger := consensusLogger().With("test", "byzantine")
css := randConsensusNet(N, "consensus_byzantine_test", newMockTickerFunc(false), newCounter)
// give the byzantine validator a normal ticker
@@ -48,7 +47,9 @@ func TestByzantine(t *testing.T) {
for i := 0; i < N; i++ {
// make first val byzantine
if i == 0 {
css[i].privValidator = NewByzantinePrivValidator(css[i].privValidator)
// NOTE: Now, test validators are MockPV, which by default doesn't
// do any safety checks.
css[i].privValidator.(*types.MockPV).DisableChecks()
css[i].decideProposal = func(j int) func(int64, int) {
return func(height int64, round int) {
byzantineDecideProposalFunc(t, height, round, css[j], switches[j])
@@ -203,7 +204,7 @@ func byzantineDecideProposalFunc(t *testing.T, height int64, round int, cs *Cons
func sendProposalAndParts(height int64, round int, cs *ConsensusState, peer p2p.Peer, proposal *types.Proposal, blockHash []byte, parts *types.PartSet) {
// proposal
msg := &ProposalMessage{Proposal: proposal}
peer.Send(DataChannel, struct{ ConsensusMessage }{msg})
peer.Send(DataChannel, cdc.MustMarshalBinaryBare(msg))
// parts
for i := 0; i < parts.Total(); i++ {
@@ -213,7 +214,7 @@ func sendProposalAndParts(height int64, round int, cs *ConsensusState, peer p2p.
Round: round, // This tells peer that this part applies to us.
Part: part,
}
peer.Send(DataChannel, struct{ ConsensusMessage }{msg})
peer.Send(DataChannel, cdc.MustMarshalBinaryBare(msg))
}
// votes
@@ -222,8 +223,8 @@ func sendProposalAndParts(height int64, round int, cs *ConsensusState, peer p2p.
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}})
peer.Send(VoteChannel, cdc.MustMarshalBinaryBare(&VoteMessage{prevote}))
peer.Send(VoteChannel, cdc.MustMarshalBinaryBare(&VoteMessage{precommit}))
}
//----------------------------------------
@@ -264,47 +265,3 @@ func (br *ByzantineReactor) RemovePeer(peer p2p.Peer, reason interface{}) {
func (br *ByzantineReactor) Receive(chID byte, peer p2p.Peer, msgBytes []byte) {
br.reactor.Receive(chID, peer, msgBytes)
}
//----------------------------------------
// byzantine privValidator
type ByzantinePrivValidator struct {
types.Signer
pv types.PrivValidator
}
// Return a priv validator that will sign anything
func NewByzantinePrivValidator(pv types.PrivValidator) *ByzantinePrivValidator {
return &ByzantinePrivValidator{
Signer: pv.(*types.PrivValidatorFS).Signer,
pv: pv,
}
}
func (privVal *ByzantinePrivValidator) GetAddress() types.Address {
return privVal.pv.GetAddress()
}
func (privVal *ByzantinePrivValidator) GetPubKey() crypto.PubKey {
return privVal.pv.GetPubKey()
}
func (privVal *ByzantinePrivValidator) SignVote(chainID string, vote *types.Vote) (err error) {
vote.Signature, err = privVal.Sign(vote.SignBytes(chainID))
return err
}
func (privVal *ByzantinePrivValidator) SignProposal(chainID string, proposal *types.Proposal) (err error) {
proposal.Signature, _ = privVal.Sign(proposal.SignBytes(chainID))
return nil
}
func (privVal *ByzantinePrivValidator) SignHeartbeat(chainID string, heartbeat *types.Heartbeat) (err error) {
heartbeat.Signature, _ = privVal.Sign(heartbeat.SignBytes(chainID))
return nil
}
func (privVal *ByzantinePrivValidator) String() string {
return cmn.Fmt("PrivValidator{%X}", privVal.GetAddress())
}

View File

@@ -21,6 +21,7 @@ import (
"github.com/tendermint/tendermint/p2p"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
pvm "github.com/tendermint/tendermint/types/priv_validator"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
@@ -222,7 +223,7 @@ func subscribeToVoter(cs *ConsensusState, addr []byte) chan interface{} {
voteCh := make(chan interface{})
go func() {
for v := range voteCh0 {
vote := v.(types.TMEventData).Unwrap().(types.EventDataVote)
vote := v.(types.EventDataVote)
// we only fire for our own votes
if bytes.Equal(addr, vote.Vote.ValidatorAddress) {
voteCh <- v
@@ -263,7 +264,7 @@ func newConsensusStateWithConfigAndBlockStore(thisConfig *cfg.Config, state sm.S
// mock the evidence pool
evpool := types.MockEvidencePool{}
// Make ConsensusReactor
// Make ConsensusState
stateDB := dbm.NewMemDB()
blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyAppConnCon, mempool, evpool)
cs := NewConsensusState(thisConfig.Consensus, state, blockExec, blockStore, mempool, evpool)
@@ -277,10 +278,10 @@ func newConsensusStateWithConfigAndBlockStore(thisConfig *cfg.Config, state sm.S
return cs
}
func loadPrivValidator(config *cfg.Config) *types.PrivValidatorFS {
func loadPrivValidator(config *cfg.Config) *pvm.FilePV {
privValidatorFile := config.PrivValidatorFile()
ensureDir(path.Dir(privValidatorFile), 0700)
privValidator := types.LoadOrGenPrivValidatorFS(privValidatorFile)
privValidator := pvm.LoadOrGenFilePV(privValidatorFile)
privValidator.Reset()
return privValidator
}
@@ -378,7 +379,7 @@ func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerF
privVal = privVals[i]
} else {
_, tempFilePath := cmn.Tempfile("priv_validator_")
privVal = types.GenPrivValidatorFS(tempFilePath)
privVal = pvm.GenFilePV(tempFilePath)
}
app := appFunc()
@@ -394,7 +395,7 @@ func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerF
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()) {
if peer.NodeInfo().ID == s.NodeInfo().ID {
return i
}
}
@@ -405,9 +406,9 @@ func getSwitchIndex(switches []*p2p.Switch, peer p2p.Peer) int {
//-------------------------------------------------------------------------------
// genesis
func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.GenesisDoc, []*types.PrivValidatorFS) {
func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.GenesisDoc, []types.PrivValidator) {
validators := make([]types.GenesisValidator, numValidators)
privValidators := make([]*types.PrivValidatorFS, numValidators)
privValidators := make([]types.PrivValidator, numValidators)
for i := 0; i < numValidators; i++ {
val, privVal := types.RandValidator(randPower, minPower)
validators[i] = types.GenesisValidator{
@@ -425,7 +426,7 @@ func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.G
}, privValidators
}
func randGenesisState(numValidators int, randPower bool, minPower int64) (sm.State, []*types.PrivValidatorFS) {
func randGenesisState(numValidators int, randPower bool, minPower int64) (sm.State, []types.PrivValidator) {
genDoc, privValidators := randGenesisDoc(numValidators, randPower, minPower)
s0, _ := sm.MakeGenesisState(genDoc)
db := dbm.NewMemDB()

View File

@@ -108,7 +108,7 @@ func TestMempoolTxConcurrentWithCommit(t *testing.T) {
ticker := time.NewTicker(time.Second * 30)
select {
case b := <-newBlockCh:
evt := b.(types.TMEventData).Unwrap().(types.EventDataNewBlock)
evt := b.(types.EventDataNewBlock)
nTxs += int(evt.Block.Header.NumTxs)
case <-ticker.C:
panic("Timed out waiting to commit blocks with transactions")

View File

@@ -1,8 +1,6 @@
package consensus
import (
"bytes"
"context"
"fmt"
"reflect"
"sync"
@@ -10,11 +8,12 @@ import (
"github.com/pkg/errors"
wire "github.com/tendermint/go-wire"
amino "github.com/tendermint/go-amino"
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/log"
cstypes "github.com/tendermint/tendermint/consensus/types"
tmevents "github.com/tendermint/tendermint/libs/events"
"github.com/tendermint/tendermint/p2p"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
@@ -26,7 +25,7 @@ const (
VoteChannel = byte(0x22)
VoteSetBitsChannel = byte(0x23)
maxConsensusMessageSize = 1048576 // 1MB; NOTE/TODO: keep in sync with types.PartSet sizes.
maxMsgSize = 1048576 // 1MB; NOTE/TODO: keep in sync with types.PartSet sizes.
blocksToContributeToBecomeGoodPeer = 10000
)
@@ -44,7 +43,8 @@ type ConsensusReactor struct {
eventBus *types.EventBus
}
// NewConsensusReactor returns a new ConsensusReactor with the given consensusState.
// NewConsensusReactor returns a new ConsensusReactor with the given
// consensusState.
func NewConsensusReactor(consensusState *ConsensusState, fastSync bool) *ConsensusReactor {
conR := &ConsensusReactor{
conS: consensusState,
@@ -54,17 +54,15 @@ func NewConsensusReactor(consensusState *ConsensusState, fastSync bool) *Consens
return conR
}
// OnStart implements BaseService.
// OnStart implements BaseService by subscribing to events, which later will be
// broadcasted to other peers and starting state if we're not in fast sync.
func (conR *ConsensusReactor) OnStart() error {
conR.Logger.Info("ConsensusReactor ", "fastSync", conR.FastSync())
if err := conR.BaseReactor.OnStart(); err != nil {
return err
}
err := conR.startBroadcastRoutine()
if err != nil {
return err
}
conR.subscribeToBroadcastEvents()
if !conR.FastSync() {
err := conR.conS.Start()
@@ -76,9 +74,11 @@ func (conR *ConsensusReactor) OnStart() error {
return nil
}
// OnStop implements BaseService
// OnStop implements BaseService by unsubscribing from events and stopping
// state.
func (conR *ConsensusReactor) OnStop() {
conR.BaseReactor.OnStop()
conR.unsubscribeFromBroadcastEvents()
conR.conS.Stop()
}
@@ -102,6 +102,7 @@ func (conR *ConsensusReactor) SwitchToConsensus(state sm.State, blocksSynced int
err := conR.conS.Start()
if err != nil {
conR.Logger.Error("Error starting conS", "err", err)
return
}
}
@@ -110,27 +111,31 @@ func (conR *ConsensusReactor) GetChannels() []*p2p.ChannelDescriptor {
// TODO optimize
return []*p2p.ChannelDescriptor{
{
ID: StateChannel,
Priority: 5,
SendQueueCapacity: 100,
ID: StateChannel,
Priority: 5,
SendQueueCapacity: 100,
RecvMessageCapacity: maxMsgSize,
},
{
ID: DataChannel, // maybe split between gossiping current block and catchup stuff
Priority: 10, // once we gossip the whole block there's nothing left to send until next height or round
SendQueueCapacity: 100,
RecvBufferCapacity: 50 * 4096,
ID: DataChannel, // maybe split between gossiping current block and catchup stuff
Priority: 10, // once we gossip the whole block there's nothing left to send until next height or round
SendQueueCapacity: 100,
RecvBufferCapacity: 50 * 4096,
RecvMessageCapacity: maxMsgSize,
},
{
ID: VoteChannel,
Priority: 5,
SendQueueCapacity: 100,
RecvBufferCapacity: 100 * 100,
ID: VoteChannel,
Priority: 5,
SendQueueCapacity: 100,
RecvBufferCapacity: 100 * 100,
RecvMessageCapacity: maxMsgSize,
},
{
ID: VoteSetBitsChannel,
Priority: 1,
SendQueueCapacity: 2,
RecvBufferCapacity: 1024,
ID: VoteSetBitsChannel,
Priority: 1,
SendQueueCapacity: 2,
RecvBufferCapacity: 1024,
RecvMessageCapacity: maxMsgSize,
},
}
}
@@ -178,7 +183,7 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
return
}
_, msg, err := DecodeMessage(msgBytes)
msg, err := DecodeMessage(msgBytes)
if err != nil {
conR.Logger.Error("Error decoding message", "src", src, "chId", chID, "msg", msg, "err", err, "bytes", msgBytes)
conR.Switch.StopPeerForError(src, err)
@@ -207,7 +212,7 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
return
}
// Peer claims to have a maj23 for some BlockID at H,R,S,
err := votes.SetPeerMaj23(msg.Round, msg.Type, ps.Peer.ID(), msg.BlockID)
err := votes.SetPeerMaj23(msg.Round, msg.Type, ps.peer.ID(), msg.BlockID)
if err != nil {
conR.Switch.StopPeerForError(src, err)
return
@@ -224,13 +229,13 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
conR.Logger.Error("Bad VoteSetBitsMessage field Type")
return
}
src.TrySend(VoteSetBitsChannel, struct{ ConsensusMessage }{&VoteSetBitsMessage{
src.TrySend(VoteSetBitsChannel, cdc.MustMarshalBinaryBare(&VoteSetBitsMessage{
Height: msg.Height,
Round: msg.Round,
Type: msg.Type,
BlockID: msg.BlockID,
Votes: ourVotes,
}})
}))
case *ProposalHeartbeatMessage:
hb := msg.Heartbeat
conR.Logger.Debug("Received proposal heartbeat message",
@@ -342,83 +347,46 @@ func (conR *ConsensusReactor) FastSync() bool {
//--------------------------------------
// startBroadcastRoutine subscribes for new round steps, votes and proposal
// heartbeats using the event bus and starts a go routine to broadcasts events
// to peers upon receiving them.
func (conR *ConsensusReactor) startBroadcastRoutine() error {
// subscribeToBroadcastEvents subscribes for new round steps, votes and
// proposal heartbeats using internal pubsub defined on state to broadcast
// them to peers upon receiving.
func (conR *ConsensusReactor) subscribeToBroadcastEvents() {
const subscriber = "consensus-reactor"
ctx := context.Background()
conR.conS.evsw.AddListenerForEvent(subscriber, types.EventNewRoundStep,
func(data tmevents.EventData) {
conR.broadcastNewRoundStepMessages(data.(*cstypes.RoundState))
})
// new round steps
stepsCh := make(chan interface{})
err := conR.eventBus.Subscribe(ctx, subscriber, types.EventQueryNewRoundStep, stepsCh)
if err != nil {
return errors.Wrapf(err, "failed to subscribe %s to %s", subscriber, types.EventQueryNewRoundStep)
}
conR.conS.evsw.AddListenerForEvent(subscriber, types.EventVote,
func(data tmevents.EventData) {
conR.broadcastHasVoteMessage(data.(*types.Vote))
})
// votes
votesCh := make(chan interface{})
err = conR.eventBus.Subscribe(ctx, subscriber, types.EventQueryVote, votesCh)
if err != nil {
return errors.Wrapf(err, "failed to subscribe %s to %s", subscriber, types.EventQueryVote)
}
// proposal heartbeats
heartbeatsCh := make(chan interface{})
err = conR.eventBus.Subscribe(ctx, subscriber, types.EventQueryProposalHeartbeat, heartbeatsCh)
if err != nil {
return errors.Wrapf(err, "failed to subscribe %s to %s", subscriber, types.EventQueryProposalHeartbeat)
}
go func() {
var data interface{}
var ok bool
for {
select {
case data, ok = <-stepsCh:
if ok { // a receive from a closed channel returns the zero value immediately
edrs := data.(types.TMEventData).Unwrap().(types.EventDataRoundState)
conR.broadcastNewRoundStep(edrs.RoundState.(*cstypes.RoundState))
}
case data, ok = <-votesCh:
if ok {
edv := data.(types.TMEventData).Unwrap().(types.EventDataVote)
conR.broadcastHasVoteMessage(edv.Vote)
}
case data, ok = <-heartbeatsCh:
if ok {
edph := data.(types.TMEventData).Unwrap().(types.EventDataProposalHeartbeat)
conR.broadcastProposalHeartbeatMessage(edph)
}
case <-conR.Quit():
conR.eventBus.UnsubscribeAll(ctx, subscriber)
return
}
if !ok {
conR.eventBus.UnsubscribeAll(ctx, subscriber)
return
}
}
}()
return nil
conR.conS.evsw.AddListenerForEvent(subscriber, types.EventProposalHeartbeat,
func(data tmevents.EventData) {
conR.broadcastProposalHeartbeatMessage(data.(*types.Heartbeat))
})
}
func (conR *ConsensusReactor) broadcastProposalHeartbeatMessage(heartbeat types.EventDataProposalHeartbeat) {
hb := heartbeat.Heartbeat
func (conR *ConsensusReactor) unsubscribeFromBroadcastEvents() {
const subscriber = "consensus-reactor"
conR.conS.evsw.RemoveListener(subscriber)
}
func (conR *ConsensusReactor) broadcastProposalHeartbeatMessage(hb *types.Heartbeat) {
conR.Logger.Debug("Broadcasting proposal heartbeat message",
"height", hb.Height, "round", hb.Round, "sequence", hb.Sequence)
msg := &ProposalHeartbeatMessage{hb}
conR.Switch.Broadcast(StateChannel, struct{ ConsensusMessage }{msg})
conR.Switch.Broadcast(StateChannel, cdc.MustMarshalBinaryBare(msg))
}
func (conR *ConsensusReactor) broadcastNewRoundStep(rs *cstypes.RoundState) {
func (conR *ConsensusReactor) broadcastNewRoundStepMessages(rs *cstypes.RoundState) {
nrsMsg, csMsg := makeRoundStepMessages(rs)
if nrsMsg != nil {
conR.Switch.Broadcast(StateChannel, struct{ ConsensusMessage }{nrsMsg})
conR.Switch.Broadcast(StateChannel, cdc.MustMarshalBinaryBare(nrsMsg))
}
if csMsg != nil {
conR.Switch.Broadcast(StateChannel, struct{ ConsensusMessage }{csMsg})
conR.Switch.Broadcast(StateChannel, cdc.MustMarshalBinaryBare(csMsg))
}
}
@@ -430,7 +398,7 @@ func (conR *ConsensusReactor) broadcastHasVoteMessage(vote *types.Vote) {
Type: vote.Type,
Index: vote.ValidatorIndex,
}
conR.Switch.Broadcast(StateChannel, struct{ ConsensusMessage }{msg})
conR.Switch.Broadcast(StateChannel, cdc.MustMarshalBinaryBare(msg))
/*
// TODO: Make this broadcast more selective.
for _, peer := range conR.Switch.Peers().List() {
@@ -470,10 +438,10 @@ func (conR *ConsensusReactor) sendNewRoundStepMessages(peer p2p.Peer) {
rs := conR.conS.GetRoundState()
nrsMsg, csMsg := makeRoundStepMessages(rs)
if nrsMsg != nil {
peer.Send(StateChannel, struct{ ConsensusMessage }{nrsMsg})
peer.Send(StateChannel, cdc.MustMarshalBinaryBare(nrsMsg))
}
if csMsg != nil {
peer.Send(StateChannel, struct{ ConsensusMessage }{csMsg})
peer.Send(StateChannel, cdc.MustMarshalBinaryBare(csMsg))
}
}
@@ -500,7 +468,7 @@ OUTER_LOOP:
Part: part,
}
logger.Debug("Sending block part", "height", prs.Height, "round", prs.Round)
if peer.Send(DataChannel, struct{ ConsensusMessage }{msg}) {
if peer.Send(DataChannel, cdc.MustMarshalBinaryBare(msg)) {
ps.SetHasProposalBlockPart(prs.Height, prs.Round, index)
}
continue OUTER_LOOP
@@ -544,7 +512,7 @@ OUTER_LOOP:
{
msg := &ProposalMessage{Proposal: rs.Proposal}
logger.Debug("Sending proposal", "height", prs.Height, "round", prs.Round)
if peer.Send(DataChannel, struct{ ConsensusMessage }{msg}) {
if peer.Send(DataChannel, cdc.MustMarshalBinaryBare(msg)) {
ps.SetHasProposal(rs.Proposal)
}
}
@@ -559,7 +527,7 @@ OUTER_LOOP:
ProposalPOL: rs.Votes.Prevotes(rs.Proposal.POLRound).BitArray(),
}
logger.Debug("Sending POL", "height", prs.Height, "round", prs.Round)
peer.Send(DataChannel, struct{ ConsensusMessage }{msg})
peer.Send(DataChannel, cdc.MustMarshalBinaryBare(msg))
}
continue OUTER_LOOP
}
@@ -602,7 +570,7 @@ func (conR *ConsensusReactor) gossipDataForCatchup(logger log.Logger, rs *cstype
Part: part,
}
logger.Debug("Sending block part for catchup", "round", prs.Round, "index", index)
if peer.Send(DataChannel, struct{ ConsensusMessage }{msg}) {
if peer.Send(DataChannel, cdc.MustMarshalBinaryBare(msg)) {
ps.SetHasProposalBlockPart(prs.Height, prs.Round, index)
} else {
logger.Debug("Sending block part for catchup failed")
@@ -693,20 +661,37 @@ func (conR *ConsensusReactor) gossipVotesForHeight(logger log.Logger, rs *cstype
return true
}
}
// If there are POL prevotes to send...
if prs.Step <= cstypes.RoundStepPropose && prs.Round != -1 && prs.Round <= rs.Round && prs.ProposalPOLRound != -1 {
if polPrevotes := rs.Votes.Prevotes(prs.ProposalPOLRound); polPrevotes != nil {
if ps.PickSendVote(polPrevotes) {
logger.Debug("Picked rs.Prevotes(prs.ProposalPOLRound) to send",
"round", prs.ProposalPOLRound)
return true
}
}
}
// If there are prevotes to send...
if prs.Step <= cstypes.RoundStepPrevote && prs.Round != -1 && prs.Round <= rs.Round {
if prs.Step <= cstypes.RoundStepPrevoteWait && prs.Round != -1 && prs.Round <= rs.Round {
if ps.PickSendVote(rs.Votes.Prevotes(prs.Round)) {
logger.Debug("Picked rs.Prevotes(prs.Round) to send", "round", prs.Round)
return true
}
}
// If there are precommits to send...
if prs.Step <= cstypes.RoundStepPrecommit && prs.Round != -1 && prs.Round <= rs.Round {
if prs.Step <= cstypes.RoundStepPrecommitWait && prs.Round != -1 && prs.Round <= rs.Round {
if ps.PickSendVote(rs.Votes.Precommits(prs.Round)) {
logger.Debug("Picked rs.Precommits(prs.Round) to send", "round", prs.Round)
return true
}
}
// If there are prevotes to send...Needed because of validBlock mechanism
if prs.Round != -1 && prs.Round <= rs.Round {
if ps.PickSendVote(rs.Votes.Prevotes(prs.Round)) {
logger.Debug("Picked rs.Prevotes(prs.Round) to send", "round", prs.Round)
return true
}
}
// If there are POLPrevotes to send...
if prs.ProposalPOLRound != -1 {
if polPrevotes := rs.Votes.Prevotes(prs.ProposalPOLRound); polPrevotes != nil {
@@ -717,6 +702,7 @@ func (conR *ConsensusReactor) gossipVotesForHeight(logger log.Logger, rs *cstype
}
}
}
return false
}
@@ -739,12 +725,12 @@ OUTER_LOOP:
prs := ps.GetRoundState()
if rs.Height == prs.Height {
if maj23, ok := rs.Votes.Prevotes(prs.Round).TwoThirdsMajority(); ok {
peer.TrySend(StateChannel, struct{ ConsensusMessage }{&VoteSetMaj23Message{
peer.TrySend(StateChannel, cdc.MustMarshalBinaryBare(&VoteSetMaj23Message{
Height: prs.Height,
Round: prs.Round,
Type: types.VoteTypePrevote,
BlockID: maj23,
}})
}))
time.Sleep(conR.conS.config.PeerQueryMaj23Sleep())
}
}
@@ -756,12 +742,12 @@ OUTER_LOOP:
prs := ps.GetRoundState()
if rs.Height == prs.Height {
if maj23, ok := rs.Votes.Precommits(prs.Round).TwoThirdsMajority(); ok {
peer.TrySend(StateChannel, struct{ ConsensusMessage }{&VoteSetMaj23Message{
peer.TrySend(StateChannel, cdc.MustMarshalBinaryBare(&VoteSetMaj23Message{
Height: prs.Height,
Round: prs.Round,
Type: types.VoteTypePrecommit,
BlockID: maj23,
}})
}))
time.Sleep(conR.conS.config.PeerQueryMaj23Sleep())
}
}
@@ -773,12 +759,12 @@ OUTER_LOOP:
prs := ps.GetRoundState()
if rs.Height == prs.Height && prs.ProposalPOLRound >= 0 {
if maj23, ok := rs.Votes.Prevotes(prs.ProposalPOLRound).TwoThirdsMajority(); ok {
peer.TrySend(StateChannel, struct{ ConsensusMessage }{&VoteSetMaj23Message{
peer.TrySend(StateChannel, cdc.MustMarshalBinaryBare(&VoteSetMaj23Message{
Height: prs.Height,
Round: prs.ProposalPOLRound,
Type: types.VoteTypePrevote,
BlockID: maj23,
}})
}))
time.Sleep(conR.conS.config.PeerQueryMaj23Sleep())
}
}
@@ -792,12 +778,12 @@ OUTER_LOOP:
prs := ps.GetRoundState()
if prs.CatchupCommitRound != -1 && 0 < prs.Height && prs.Height <= conR.conS.blockStore.Height() {
commit := conR.conS.LoadCommit(prs.Height)
peer.TrySend(StateChannel, struct{ ConsensusMessage }{&VoteSetMaj23Message{
peer.TrySend(StateChannel, cdc.MustMarshalBinaryBare(&VoteSetMaj23Message{
Height: prs.Height,
Round: commit.Round(),
Type: types.VoteTypePrecommit,
BlockID: commit.BlockID,
}})
}))
time.Sleep(conR.conS.config.PeerQueryMaj23Sleep())
}
}
@@ -835,67 +821,78 @@ var (
ErrPeerStateInvalidStartTime = errors.New("Error peer state invalid startTime")
)
// PeerState contains the known state of a peer, including its connection
// and threadsafe access to its PeerRoundState.
// PeerState contains the known state of a peer, including its connection and
// threadsafe access to its PeerRoundState.
// NOTE: THIS GETS DUMPED WITH rpc/core/consensus.go.
// Be mindful of what you Expose.
type PeerState struct {
Peer p2p.Peer
peer p2p.Peer
logger log.Logger
mtx sync.Mutex
cstypes.PeerRoundState
stats *peerStateStats
mtx sync.Mutex `json:"-"` // NOTE: Modify below using setters, never directly.
PRS cstypes.PeerRoundState `json:"round_state"` // Exposed.
Stats *peerStateStats `json:"stats"` // Exposed.
}
// peerStateStats holds internal statistics for a peer.
type peerStateStats struct {
lastVoteHeight int64
votes int
lastBlockPartHeight int64
blockParts int
LastVoteHeight int64 `json:"last_vote_height"`
Votes int `json:"votes"`
LastBlockPartHeight int64 `json:"last_block_part_height"`
BlockParts int `json:"block_parts"`
}
func (pss peerStateStats) String() string {
return fmt.Sprintf("peerStateStats{votes: %d, blockParts: %d}", pss.votes, pss.blockParts)
return fmt.Sprintf("peerStateStats{lvh: %d, votes: %d, lbph: %d, blockParts: %d}",
pss.LastVoteHeight, pss.Votes, pss.LastBlockPartHeight, pss.BlockParts)
}
// NewPeerState returns a new PeerState for the given Peer
func NewPeerState(peer p2p.Peer) *PeerState {
return &PeerState{
Peer: peer,
peer: peer,
logger: log.NewNopLogger(),
PeerRoundState: cstypes.PeerRoundState{
PRS: cstypes.PeerRoundState{
Round: -1,
ProposalPOLRound: -1,
LastCommitRound: -1,
CatchupCommitRound: -1,
},
stats: &peerStateStats{},
Stats: &peerStateStats{},
}
}
// SetLogger allows to set a logger on the peer state. Returns the peer state
// itself.
func (ps *PeerState) SetLogger(logger log.Logger) *PeerState {
ps.logger = logger
return ps
}
// GetRoundState returns an atomic snapshot of the PeerRoundState.
// GetRoundState returns an shallow copy of the PeerRoundState.
// There's no point in mutating it since it won't change PeerState.
func (ps *PeerState) GetRoundState() *cstypes.PeerRoundState {
ps.mtx.Lock()
defer ps.mtx.Unlock()
prs := ps.PeerRoundState // copy
prs := ps.PRS // copy
return &prs
}
// ToJSON returns a json of PeerState, marshalled using go-amino.
func (ps *PeerState) ToJSON() ([]byte, error) {
ps.mtx.Lock()
defer ps.mtx.Unlock()
return cdc.MarshalJSON(ps)
}
// GetHeight returns an atomic snapshot of the PeerRoundState's height
// used by the mempool to ensure peers are caught up before broadcasting new txs
func (ps *PeerState) GetHeight() int64 {
ps.mtx.Lock()
defer ps.mtx.Unlock()
return ps.PeerRoundState.Height
return ps.PRS.Height
}
// SetHasProposal sets the given proposal as known for the peer.
@@ -903,18 +900,18 @@ func (ps *PeerState) SetHasProposal(proposal *types.Proposal) {
ps.mtx.Lock()
defer ps.mtx.Unlock()
if ps.Height != proposal.Height || ps.Round != proposal.Round {
if ps.PRS.Height != proposal.Height || ps.PRS.Round != proposal.Round {
return
}
if ps.Proposal {
if ps.PRS.Proposal {
return
}
ps.Proposal = true
ps.ProposalBlockPartsHeader = proposal.BlockPartsHeader
ps.ProposalBlockParts = cmn.NewBitArray(proposal.BlockPartsHeader.Total)
ps.ProposalPOLRound = proposal.POLRound
ps.ProposalPOL = nil // Nil until ProposalPOLMessage received.
ps.PRS.Proposal = true
ps.PRS.ProposalBlockPartsHeader = proposal.BlockPartsHeader
ps.PRS.ProposalBlockParts = cmn.NewBitArray(proposal.BlockPartsHeader.Total)
ps.PRS.ProposalPOLRound = proposal.POLRound
ps.PRS.ProposalPOL = nil // Nil until ProposalPOLMessage received.
}
// InitProposalBlockParts initializes the peer's proposal block parts header and bit array.
@@ -922,12 +919,12 @@ func (ps *PeerState) InitProposalBlockParts(partsHeader types.PartSetHeader) {
ps.mtx.Lock()
defer ps.mtx.Unlock()
if ps.ProposalBlockParts != nil {
if ps.PRS.ProposalBlockParts != nil {
return
}
ps.ProposalBlockPartsHeader = partsHeader
ps.ProposalBlockParts = cmn.NewBitArray(partsHeader.Total)
ps.PRS.ProposalBlockPartsHeader = partsHeader
ps.PRS.ProposalBlockParts = cmn.NewBitArray(partsHeader.Total)
}
// SetHasProposalBlockPart sets the given block part index as known for the peer.
@@ -935,11 +932,11 @@ func (ps *PeerState) SetHasProposalBlockPart(height int64, round int, index int)
ps.mtx.Lock()
defer ps.mtx.Unlock()
if ps.Height != height || ps.Round != round {
if ps.PRS.Height != height || ps.PRS.Round != round {
return
}
ps.ProposalBlockParts.SetIndex(index, true)
ps.PRS.ProposalBlockParts.SetIndex(index, true)
}
// PickSendVote picks a vote and sends it to the peer.
@@ -948,7 +945,7 @@ func (ps *PeerState) PickSendVote(votes types.VoteSetReader) bool {
if vote, ok := ps.PickVoteToSend(votes); ok {
msg := &VoteMessage{vote}
ps.logger.Debug("Sending vote message", "ps", ps, "vote", vote)
return ps.Peer.Send(VoteChannel, struct{ ConsensusMessage }{msg})
return ps.peer.Send(VoteChannel, cdc.MustMarshalBinaryBare(msg))
}
return false
}
@@ -988,40 +985,40 @@ func (ps *PeerState) getVoteBitArray(height int64, round int, type_ byte) *cmn.B
return nil
}
if ps.Height == height {
if ps.Round == round {
if ps.PRS.Height == height {
if ps.PRS.Round == round {
switch type_ {
case types.VoteTypePrevote:
return ps.Prevotes
return ps.PRS.Prevotes
case types.VoteTypePrecommit:
return ps.Precommits
return ps.PRS.Precommits
}
}
if ps.CatchupCommitRound == round {
if ps.PRS.CatchupCommitRound == round {
switch type_ {
case types.VoteTypePrevote:
return nil
case types.VoteTypePrecommit:
return ps.CatchupCommit
return ps.PRS.CatchupCommit
}
}
if ps.ProposalPOLRound == round {
if ps.PRS.ProposalPOLRound == round {
switch type_ {
case types.VoteTypePrevote:
return ps.ProposalPOL
return ps.PRS.ProposalPOL
case types.VoteTypePrecommit:
return nil
}
}
return nil
}
if ps.Height == height+1 {
if ps.LastCommitRound == round {
if ps.PRS.Height == height+1 {
if ps.PRS.LastCommitRound == round {
switch type_ {
case types.VoteTypePrevote:
return nil
case types.VoteTypePrecommit:
return ps.LastCommit
return ps.PRS.LastCommit
}
}
return nil
@@ -1031,7 +1028,7 @@ func (ps *PeerState) getVoteBitArray(height int64, round int, type_ byte) *cmn.B
// 'round': A round for which we have a +2/3 commit.
func (ps *PeerState) ensureCatchupCommitRound(height int64, round int, numValidators int) {
if ps.Height != height {
if ps.PRS.Height != height {
return
}
/*
@@ -1041,18 +1038,18 @@ func (ps *PeerState) ensureCatchupCommitRound(height int64, round int, numValida
cmn.PanicSanity(cmn.Fmt("Conflicting CatchupCommitRound. Height: %v, Orig: %v, New: %v", height, ps.CatchupCommitRound, round))
}
*/
if ps.CatchupCommitRound == round {
if ps.PRS.CatchupCommitRound == round {
return // Nothing to do!
}
ps.CatchupCommitRound = round
if round == ps.Round {
ps.CatchupCommit = ps.Precommits
ps.PRS.CatchupCommitRound = round
if round == ps.PRS.Round {
ps.PRS.CatchupCommit = ps.PRS.Precommits
} else {
ps.CatchupCommit = cmn.NewBitArray(numValidators)
ps.PRS.CatchupCommit = cmn.NewBitArray(numValidators)
}
}
// EnsureVoteVitArrays ensures the bit-arrays have been allocated for tracking
// EnsureVoteBitArrays ensures the bit-arrays have been allocated for tracking
// what votes this peer has received.
// NOTE: It's important to make sure that numValidators actually matches
// what the node sees as the number of validators for height.
@@ -1063,22 +1060,22 @@ func (ps *PeerState) EnsureVoteBitArrays(height int64, numValidators int) {
}
func (ps *PeerState) ensureVoteBitArrays(height int64, numValidators int) {
if ps.Height == height {
if ps.Prevotes == nil {
ps.Prevotes = cmn.NewBitArray(numValidators)
if ps.PRS.Height == height {
if ps.PRS.Prevotes == nil {
ps.PRS.Prevotes = cmn.NewBitArray(numValidators)
}
if ps.Precommits == nil {
ps.Precommits = cmn.NewBitArray(numValidators)
if ps.PRS.Precommits == nil {
ps.PRS.Precommits = cmn.NewBitArray(numValidators)
}
if ps.CatchupCommit == nil {
ps.CatchupCommit = cmn.NewBitArray(numValidators)
if ps.PRS.CatchupCommit == nil {
ps.PRS.CatchupCommit = cmn.NewBitArray(numValidators)
}
if ps.ProposalPOL == nil {
ps.ProposalPOL = cmn.NewBitArray(numValidators)
if ps.PRS.ProposalPOL == nil {
ps.PRS.ProposalPOL = cmn.NewBitArray(numValidators)
}
} else if ps.Height == height+1 {
if ps.LastCommit == nil {
ps.LastCommit = cmn.NewBitArray(numValidators)
} else if ps.PRS.Height == height+1 {
if ps.PRS.LastCommit == nil {
ps.PRS.LastCommit = cmn.NewBitArray(numValidators)
}
}
}
@@ -1090,12 +1087,12 @@ func (ps *PeerState) RecordVote(vote *types.Vote) int {
ps.mtx.Lock()
defer ps.mtx.Unlock()
if ps.stats.lastVoteHeight >= vote.Height {
return ps.stats.votes
if ps.Stats.LastVoteHeight >= vote.Height {
return ps.Stats.Votes
}
ps.stats.lastVoteHeight = vote.Height
ps.stats.votes++
return ps.stats.votes
ps.Stats.LastVoteHeight = vote.Height
ps.Stats.Votes++
return ps.Stats.Votes
}
// VotesSent returns the number of blocks for which peer has been sending us
@@ -1104,7 +1101,7 @@ func (ps *PeerState) VotesSent() int {
ps.mtx.Lock()
defer ps.mtx.Unlock()
return ps.stats.votes
return ps.Stats.Votes
}
// RecordBlockPart updates internal statistics for this peer by recording the
@@ -1115,13 +1112,13 @@ func (ps *PeerState) RecordBlockPart(bp *BlockPartMessage) int {
ps.mtx.Lock()
defer ps.mtx.Unlock()
if ps.stats.lastBlockPartHeight >= bp.Height {
return ps.stats.blockParts
if ps.Stats.LastBlockPartHeight >= bp.Height {
return ps.Stats.BlockParts
}
ps.stats.lastBlockPartHeight = bp.Height
ps.stats.blockParts++
return ps.stats.blockParts
ps.Stats.LastBlockPartHeight = bp.Height
ps.Stats.BlockParts++
return ps.Stats.BlockParts
}
// BlockPartsSent returns the number of blocks for which peer has been sending
@@ -1130,7 +1127,7 @@ func (ps *PeerState) BlockPartsSent() int {
ps.mtx.Lock()
defer ps.mtx.Unlock()
return ps.stats.blockParts
return ps.Stats.BlockParts
}
// SetHasVote sets the given vote as known by the peer
@@ -1142,7 +1139,7 @@ func (ps *PeerState) SetHasVote(vote *types.Vote) {
}
func (ps *PeerState) setHasVote(height int64, round int, type_ byte, index int) {
logger := ps.logger.With("peerH/R", cmn.Fmt("%d/%d", ps.Height, ps.Round), "H/R", cmn.Fmt("%d/%d", height, round))
logger := ps.logger.With("peerH/R", cmn.Fmt("%d/%d", ps.PRS.Height, ps.PRS.Round), "H/R", cmn.Fmt("%d/%d", height, round))
logger.Debug("setHasVote", "type", type_, "index", index)
// NOTE: some may be nil BitArrays -> no side effects.
@@ -1158,51 +1155,51 @@ func (ps *PeerState) ApplyNewRoundStepMessage(msg *NewRoundStepMessage) {
defer ps.mtx.Unlock()
// Ignore duplicates or decreases
if CompareHRS(msg.Height, msg.Round, msg.Step, ps.Height, ps.Round, ps.Step) <= 0 {
if CompareHRS(msg.Height, msg.Round, msg.Step, ps.PRS.Height, ps.PRS.Round, ps.PRS.Step) <= 0 {
return
}
// Just remember these values.
psHeight := ps.Height
psRound := ps.Round
//psStep := ps.Step
psCatchupCommitRound := ps.CatchupCommitRound
psCatchupCommit := ps.CatchupCommit
psHeight := ps.PRS.Height
psRound := ps.PRS.Round
//psStep := ps.PRS.Step
psCatchupCommitRound := ps.PRS.CatchupCommitRound
psCatchupCommit := ps.PRS.CatchupCommit
startTime := time.Now().Add(-1 * time.Duration(msg.SecondsSinceStartTime) * time.Second)
ps.Height = msg.Height
ps.Round = msg.Round
ps.Step = msg.Step
ps.StartTime = startTime
ps.PRS.Height = msg.Height
ps.PRS.Round = msg.Round
ps.PRS.Step = msg.Step
ps.PRS.StartTime = startTime
if psHeight != msg.Height || psRound != msg.Round {
ps.Proposal = false
ps.ProposalBlockPartsHeader = types.PartSetHeader{}
ps.ProposalBlockParts = nil
ps.ProposalPOLRound = -1
ps.ProposalPOL = nil
ps.PRS.Proposal = false
ps.PRS.ProposalBlockPartsHeader = types.PartSetHeader{}
ps.PRS.ProposalBlockParts = nil
ps.PRS.ProposalPOLRound = -1
ps.PRS.ProposalPOL = nil
// We'll update the BitArray capacity later.
ps.Prevotes = nil
ps.Precommits = nil
ps.PRS.Prevotes = nil
ps.PRS.Precommits = nil
}
if psHeight == msg.Height && psRound != msg.Round && msg.Round == psCatchupCommitRound {
// Peer caught up to CatchupCommitRound.
// Preserve psCatchupCommit!
// NOTE: We prefer to use prs.Precommits if
// pr.Round matches pr.CatchupCommitRound.
ps.Precommits = psCatchupCommit
ps.PRS.Precommits = psCatchupCommit
}
if psHeight != msg.Height {
// Shift Precommits to LastCommit.
if psHeight+1 == msg.Height && psRound == msg.LastCommitRound {
ps.LastCommitRound = msg.LastCommitRound
ps.LastCommit = ps.Precommits
ps.PRS.LastCommitRound = msg.LastCommitRound
ps.PRS.LastCommit = ps.PRS.Precommits
} else {
ps.LastCommitRound = msg.LastCommitRound
ps.LastCommit = nil
ps.PRS.LastCommitRound = msg.LastCommitRound
ps.PRS.LastCommit = nil
}
// We'll update the BitArray capacity later.
ps.CatchupCommitRound = -1
ps.CatchupCommit = nil
ps.PRS.CatchupCommitRound = -1
ps.PRS.CatchupCommit = nil
}
}
@@ -1211,12 +1208,12 @@ func (ps *PeerState) ApplyCommitStepMessage(msg *CommitStepMessage) {
ps.mtx.Lock()
defer ps.mtx.Unlock()
if ps.Height != msg.Height {
if ps.PRS.Height != msg.Height {
return
}
ps.ProposalBlockPartsHeader = msg.BlockPartsHeader
ps.ProposalBlockParts = msg.BlockParts
ps.PRS.ProposalBlockPartsHeader = msg.BlockPartsHeader
ps.PRS.ProposalBlockParts = msg.BlockParts
}
// ApplyProposalPOLMessage updates the peer state for the new proposal POL.
@@ -1224,16 +1221,16 @@ func (ps *PeerState) ApplyProposalPOLMessage(msg *ProposalPOLMessage) {
ps.mtx.Lock()
defer ps.mtx.Unlock()
if ps.Height != msg.Height {
if ps.PRS.Height != msg.Height {
return
}
if ps.ProposalPOLRound != msg.ProposalPOLRound {
if ps.PRS.ProposalPOLRound != msg.ProposalPOLRound {
return
}
// TODO: Merge onto existing ps.ProposalPOL?
// TODO: Merge onto existing ps.PRS.ProposalPOL?
// We might have sent some prevotes in the meantime.
ps.ProposalPOL = msg.ProposalPOL
ps.PRS.ProposalPOL = msg.ProposalPOL
}
// ApplyHasVoteMessage updates the peer state for the new vote.
@@ -1241,7 +1238,7 @@ func (ps *PeerState) ApplyHasVoteMessage(msg *HasVoteMessage) {
ps.mtx.Lock()
defer ps.mtx.Unlock()
if ps.Height != msg.Height {
if ps.PRS.Height != msg.Height {
return
}
@@ -1279,58 +1276,43 @@ func (ps *PeerState) StringIndented(indent string) string {
ps.mtx.Lock()
defer ps.mtx.Unlock()
return fmt.Sprintf(`PeerState{
%s Key %v
%s PRS %v
%s Stats %v
%s Key %v
%s RoundState %v
%s Stats %v
%s}`,
indent, ps.Peer.ID(),
indent, ps.PeerRoundState.StringIndented(indent+" "),
indent, ps.stats,
indent, ps.peer.ID(),
indent, ps.PRS.StringIndented(indent+" "),
indent, ps.Stats,
indent)
}
//-----------------------------------------------------------------------------
// Messages
const (
msgTypeNewRoundStep = byte(0x01)
msgTypeCommitStep = byte(0x02)
msgTypeProposal = byte(0x11)
msgTypeProposalPOL = byte(0x12)
msgTypeBlockPart = byte(0x13) // both block & POL
msgTypeVote = byte(0x14)
msgTypeHasVote = byte(0x15)
msgTypeVoteSetMaj23 = byte(0x16)
msgTypeVoteSetBits = byte(0x17)
msgTypeProposalHeartbeat = byte(0x20)
)
// ConsensusMessage is a message that can be sent and received on the ConsensusReactor
type ConsensusMessage interface{}
var _ = wire.RegisterInterface(
struct{ ConsensusMessage }{},
wire.ConcreteType{&NewRoundStepMessage{}, msgTypeNewRoundStep},
wire.ConcreteType{&CommitStepMessage{}, msgTypeCommitStep},
wire.ConcreteType{&ProposalMessage{}, msgTypeProposal},
wire.ConcreteType{&ProposalPOLMessage{}, msgTypeProposalPOL},
wire.ConcreteType{&BlockPartMessage{}, msgTypeBlockPart},
wire.ConcreteType{&VoteMessage{}, msgTypeVote},
wire.ConcreteType{&HasVoteMessage{}, msgTypeHasVote},
wire.ConcreteType{&VoteSetMaj23Message{}, msgTypeVoteSetMaj23},
wire.ConcreteType{&VoteSetBitsMessage{}, msgTypeVoteSetBits},
wire.ConcreteType{&ProposalHeartbeatMessage{}, msgTypeProposalHeartbeat},
)
func RegisterConsensusMessages(cdc *amino.Codec) {
cdc.RegisterInterface((*ConsensusMessage)(nil), nil)
cdc.RegisterConcrete(&NewRoundStepMessage{}, "tendermint/NewRoundStepMessage", nil)
cdc.RegisterConcrete(&CommitStepMessage{}, "tendermint/CommitStep", nil)
cdc.RegisterConcrete(&ProposalMessage{}, "tendermint/Proposal", nil)
cdc.RegisterConcrete(&ProposalPOLMessage{}, "tendermint/ProposalPOL", nil)
cdc.RegisterConcrete(&BlockPartMessage{}, "tendermint/BlockPart", nil)
cdc.RegisterConcrete(&VoteMessage{}, "tendermint/Vote", nil)
cdc.RegisterConcrete(&HasVoteMessage{}, "tendermint/HasVote", nil)
cdc.RegisterConcrete(&VoteSetMaj23Message{}, "tendermint/VoteSetMaj23", nil)
cdc.RegisterConcrete(&VoteSetBitsMessage{}, "tendermint/VoteSetBits", nil)
cdc.RegisterConcrete(&ProposalHeartbeatMessage{}, "tendermint/ProposalHeartbeat", nil)
}
// DecodeMessage decodes the given bytes into a ConsensusMessage.
// TODO: check for unnecessary extra bytes at the end.
func DecodeMessage(bz []byte) (msgType byte, msg ConsensusMessage, err error) {
msgType = bz[0]
n := new(int)
r := bytes.NewReader(bz)
msgI := wire.ReadBinary(struct{ ConsensusMessage }{}, r, maxConsensusMessageSize, n, &err)
msg = msgI.(struct{ ConsensusMessage }).ConsensusMessage
func DecodeMessage(bz []byte) (msg ConsensusMessage, err error) {
if len(bz) > maxMsgSize {
return msg, fmt.Errorf("Msg exceeds max size (%d > %d)",
len(bz), maxMsgSize)
}
err = cdc.UnmarshalBinaryBare(bz, &msg)
return
}

View File

@@ -11,7 +11,6 @@ import (
"time"
"github.com/tendermint/abci/example/kvstore"
wire "github.com/tendermint/tendermint/wire"
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/log"
@@ -149,30 +148,30 @@ func TestReactorRecordsBlockParts(t *testing.T) {
Round: 0,
Part: parts.GetPart(0),
}
bz, err := wire.MarshalBinary(struct{ ConsensusMessage }{msg})
bz, err := cdc.MarshalBinaryBare(msg)
require.NoError(t, err)
reactor.Receive(DataChannel, peer, bz)
assert.Equal(t, 1, ps.BlockPartsSent(), "number of block parts sent should have increased by 1")
require.Equal(t, 1, ps.BlockPartsSent(), "number of block parts sent should have increased by 1")
// 2) block part with the same height, but different round
msg.Round = 1
bz, err = wire.MarshalBinary(struct{ ConsensusMessage }{msg})
bz, err = cdc.MarshalBinaryBare(msg)
require.NoError(t, err)
reactor.Receive(DataChannel, peer, bz)
assert.Equal(t, 1, ps.BlockPartsSent(), "number of block parts sent should stay the same")
require.Equal(t, 1, ps.BlockPartsSent(), "number of block parts sent should stay the same")
// 3) block part from earlier height
msg.Height = 1
msg.Round = 0
bz, err = wire.MarshalBinary(struct{ ConsensusMessage }{msg})
bz, err = cdc.MarshalBinaryBare(msg)
require.NoError(t, err)
reactor.Receive(DataChannel, peer, bz)
assert.Equal(t, 1, ps.BlockPartsSent(), "number of block parts sent should stay the same")
require.Equal(t, 1, ps.BlockPartsSent(), "number of block parts sent should stay the same")
}
// Test we record votes from other peers
@@ -204,7 +203,7 @@ func TestReactorRecordsVotes(t *testing.T) {
Type: types.VoteTypePrevote,
BlockID: types.BlockID{},
}
bz, err := wire.MarshalBinary(struct{ ConsensusMessage }{&VoteMessage{vote}})
bz, err := cdc.MarshalBinaryBare(&VoteMessage{vote})
require.NoError(t, err)
reactor.Receive(VoteChannel, peer, bz)
@@ -213,7 +212,7 @@ func TestReactorRecordsVotes(t *testing.T) {
// 2) vote with the same height, but different round
vote.Round = 1
bz, err = wire.MarshalBinary(struct{ ConsensusMessage }{&VoteMessage{vote}})
bz, err = cdc.MarshalBinaryBare(&VoteMessage{vote})
require.NoError(t, err)
reactor.Receive(VoteChannel, peer, bz)
@@ -223,7 +222,7 @@ func TestReactorRecordsVotes(t *testing.T) {
vote.Height = 1
vote.Round = 0
bz, err = wire.MarshalBinary(struct{ ConsensusMessage }{&VoteMessage{vote}})
bz, err = cdc.MarshalBinaryBare(&VoteMessage{vote})
require.NoError(t, err)
reactor.Receive(VoteChannel, peer, bz)
@@ -410,7 +409,7 @@ func waitForAndValidateBlock(t *testing.T, n int, activeVals map[string]struct{}
if !ok {
return
}
newBlock := newBlockI.(types.TMEventData).Unwrap().(types.EventDataNewBlock).Block
newBlock := newBlockI.(types.EventDataNewBlock).Block
css[j].Logger.Debug("waitForAndValidateBlock: Got block", "height", newBlock.Height)
err := validateBlock(newBlock, activeVals)
assert.Nil(t, err)
@@ -431,7 +430,7 @@ func waitForAndValidateBlockWithTx(t *testing.T, n int, activeVals map[string]st
if !ok {
return
}
newBlock := newBlockI.(types.TMEventData).Unwrap().(types.EventDataNewBlock).Block
newBlock := newBlockI.(types.EventDataNewBlock).Block
css[j].Logger.Debug("waitForAndValidateBlockWithTx: Got block", "height", newBlock.Height)
err := validateBlock(newBlock, activeVals)
assert.Nil(t, err)
@@ -463,7 +462,7 @@ func waitForBlockWithUpdatedValsAndValidateIt(t *testing.T, n int, updatedVals m
if !ok {
return
}
newBlock = newBlockI.(types.TMEventData).Unwrap().(types.EventDataNewBlock).Block
newBlock = newBlockI.(types.EventDataNewBlock).Block
if newBlock.LastCommit.Size() == len(updatedVals) {
css[j].Logger.Debug("waitForBlockWithUpdatedValsAndValidateIt: Got block", "height", newBlock.Height)
break LOOP

View File

@@ -26,20 +26,24 @@ import (
var crc32c = crc32.MakeTable(crc32.Castagnoli)
// 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.
// There are two general failure scenarios:
//
// 1. failure during consensus
// 2. 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
// 1. 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
// 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(msg *TimedWALMessage, newStepCh chan interface{}) error {
// skip meta messages
// Skip meta messages which exist for demarcating boundaries.
if _, ok := msg.Msg.(EndHeightMessage); ok {
return nil
}
@@ -89,17 +93,18 @@ func (cs *ConsensusState) readReplayMessage(msg *TimedWALMessage, newStepCh chan
return nil
}
// replay only those messages since the last block.
// timeoutRoutine should run concurrently to read off tickChan
// Replay only those messages since the last block. `timeoutRoutine` should
// run concurrently to read off tickChan.
func (cs *ConsensusState) catchupReplay(csHeight int64) error {
// set replayMode
// Set replayMode to true so we don't log signing errors.
cs.replayMode = true
defer func() { cs.replayMode = false }()
// Ensure that ENDHEIGHT for this height doesn't exist.
// 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).
// this check (since we can crash after writing #ENDHEIGHT).
//
// Ignore data corruption errors since this is a sanity check.
gr, found, err := cs.wal.SearchForEndHeight(csHeight, &WALSearchOptions{IgnoreDataCorruptionErrors: true})
@@ -115,7 +120,7 @@ func (cs *ConsensusState) catchupReplay(csHeight int64) error {
return fmt.Errorf("WAL should not contain #ENDHEIGHT %d", csHeight)
}
// Search for last height marker
// Search for last height marker.
//
// Ignore data corruption errors in previous heights because we only care about last height
gr, found, err = cs.wal.SearchForEndHeight(csHeight-1, &WALSearchOptions{IgnoreDataCorruptionErrors: true})
@@ -182,10 +187,11 @@ func makeHeightSearchFunc(height int64) auto.SearchFunc {
}
}*/
//----------------------------------------------
// 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
//---------------------------------------------------
// 2. Recover from failure while applying the block.
// (by handshaking with the app to figure out where
// we were last, and using the WAL to recover there.)
//---------------------------------------------------
type Handshaker struct {
stateDB dbm.DB
@@ -220,7 +226,8 @@ func (h *Handshaker) NBlocks() int {
// 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
// Handshake is done via ABCI Info on the query conn.
res, err := proxyApp.Query().InfoSync(abci.RequestInfo{version.Version})
if err != nil {
return fmt.Errorf("Error calling Info: %v", err)
@@ -234,15 +241,16 @@ func (h *Handshaker) Handshake(proxyApp proxy.AppConns) error {
h.logger.Info("ABCI Handshake", "appHeight", blockHeight, "appHash", fmt.Sprintf("%X", appHash))
// TODO: check version
// TODO: check app version.
// replay blocks up to the latest in the blockstore
// Replay blocks up to the latest in the blockstore.
_, err = h.ReplayBlocks(h.initialState, appHash, blockHeight, proxyApp)
if err != nil {
return fmt.Errorf("Error on replay: %v", err)
}
h.logger.Info("Completed ABCI Handshake - Tendermint and App are synced", "appHeight", blockHeight, "appHash", fmt.Sprintf("%X", appHash))
h.logger.Info("Completed ABCI Handshake - Tendermint and App are synced",
"appHeight", blockHeight, "appHash", fmt.Sprintf("%X", appHash))
// TODO: (on restart) replay mempool
@@ -250,7 +258,7 @@ func (h *Handshaker) Handshake(proxyApp proxy.AppConns) error {
}
// Replay all blocks since appBlockHeight and ensure the result matches the current state.
// Returns the final AppHash or an error
// Returns the final AppHash or an error.
func (h *Handshaker) ReplayBlocks(state sm.State, appHash []byte, appBlockHeight int64, proxyApp proxy.AppConns) ([]byte, error) {
storeBlockHeight := h.store.Height()
@@ -314,7 +322,7 @@ func (h *Handshaker) ReplayBlocks(state sm.State, appHash []byte, appBlockHeight
// 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
// 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")
state, err = h.replayBlock(state, storeBlockHeight, proxyApp.Consensus())
return state.AppHash, err

View File

@@ -29,6 +29,7 @@ const (
//--------------------------------------------------------
// replay messages interactively or all at once
// replay the wal file
func RunReplayFile(config cfg.BaseConfig, csConfig *cfg.ConsensusConfig, console bool) {
consensusState := newConsensusStateForReplay(config, csConfig)
@@ -262,7 +263,7 @@ func (pb *playback) replayConsoleLoop() int {
case "locked_block":
fmt.Printf("%v %v\n", rs.LockedBlockParts.StringShort(), rs.LockedBlock.StringShort())
case "votes":
fmt.Println(rs.Votes.StringIndented(" "))
fmt.Println(rs.Votes.StringIndented(" "))
default:
fmt.Println("Unknown option", tokens[1])

View File

@@ -18,7 +18,6 @@ import (
"github.com/tendermint/abci/example/kvstore"
abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto"
wire "github.com/tendermint/go-wire"
auto "github.com/tendermint/tmlibs/autofile"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
@@ -27,6 +26,7 @@ import (
"github.com/tendermint/tendermint/proxy"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
pvm "github.com/tendermint/tendermint/types/priv_validator"
"github.com/tendermint/tmlibs/log"
)
@@ -60,7 +60,7 @@ func startNewConsensusStateAndWaitForBlock(t *testing.T, lastBlockHeight int64,
bytes, _ := ioutil.ReadFile(cs.config.WalFile())
// fmt.Printf("====== WAL: \n\r%s\n", bytes)
t.Logf("====== WAL: \n\r%s\n", bytes)
t.Logf("====== WAL: \n\r%X\n", bytes)
err := cs.Start()
require.NoError(t, err)
@@ -218,15 +218,15 @@ func (e ReachedHeightToStopError) Error() string {
return fmt.Sprintf("reached height to stop %d", e.height)
}
// Save simulate WAL's crashing by sending an error to the panicCh and then
// Write simulate WAL's crashing by sending an error to the panicCh and then
// exiting the cs.receiveRoutine.
func (w *crashingWAL) Save(m WALMessage) {
func (w *crashingWAL) Write(m WALMessage) {
if endMsg, ok := m.(EndHeightMessage); ok {
if endMsg.Height == w.heightToStop {
w.panicCh <- ReachedHeightToStopError{endMsg.Height}
runtime.Goexit()
} else {
w.next.Save(m)
w.next.Write(m)
}
return
}
@@ -238,10 +238,14 @@ func (w *crashingWAL) Save(m WALMessage) {
runtime.Goexit()
} else {
w.msgIndex++
w.next.Save(m)
w.next.Write(m)
}
}
func (w *crashingWAL) WriteSync(m WALMessage) {
w.Write(m)
}
func (w *crashingWAL) Group() *auto.Group { return w.next.Group() }
func (w *crashingWAL) SearchForEndHeight(height int64, options *WALSearchOptions) (gr *auto.GroupReader, found bool, err error) {
return w.next.SearchForEndHeight(height, options)
@@ -325,9 +329,9 @@ func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) {
walFile := tempWALWithData(walBody)
config.Consensus.SetWalFile(walFile)
privVal := types.LoadPrivValidatorFS(config.PrivValidatorFile())
privVal := pvm.LoadFilePV(config.PrivValidatorFile())
wal, err := NewWAL(walFile, false)
wal, err := NewWAL(walFile)
if err != nil {
t.Fatal(err)
}
@@ -519,8 +523,8 @@ func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) {
case EndHeightMessage:
// if its not the first one, we have a full block
if thisBlockParts != nil {
var n int
block := wire.ReadBinary(&types.Block{}, thisBlockParts.GetReader(), 0, &n, &err).(*types.Block)
var block = new(types.Block)
_, err = cdc.UnmarshalBinaryReader(thisBlockParts.GetReader(), block, 0)
if err != nil {
panic(err)
}
@@ -538,7 +542,7 @@ func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) {
case *types.PartSetHeader:
thisBlockParts = types.NewPartSetFromHeader(*p)
case *types.Part:
_, err := thisBlockParts.AddPart(p, false)
_, err := thisBlockParts.AddPart(p)
if err != nil {
return nil, nil, err
}
@@ -552,8 +556,8 @@ func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) {
}
}
// grab the last block too
var n int
block := wire.ReadBinary(&types.Block{}, thisBlockParts.GetReader(), 0, &n, &err).(*types.Block)
var block = new(types.Block)
_, err = cdc.UnmarshalBinaryReader(thisBlockParts.GetReader(), block, 0)
if err != nil {
panic(err)
}

View File

@@ -10,13 +10,12 @@ import (
"time"
fail "github.com/ebuchman/fail-test"
wire "github.com/tendermint/go-wire"
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/log"
cfg "github.com/tendermint/tendermint/config"
cstypes "github.com/tendermint/tendermint/consensus/types"
tmevents "github.com/tendermint/tendermint/libs/events"
"github.com/tendermint/tendermint/p2p"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
@@ -112,6 +111,10 @@ type ConsensusState struct {
// closed when we finish shutting down
done chan struct{}
// synchronous pubsub between consensus state and reactor.
// state only emits EventNewRoundStep, EventVote and EventProposalHeartbeat
evsw tmevents.EventSwitch
}
// NewConsensusState returns a new ConsensusState.
@@ -128,6 +131,7 @@ func NewConsensusState(config *cfg.ConsensusConfig, state sm.State, blockExec *s
doWALCatchup: true,
wal: nilWAL{},
evpool: evpool,
evsw: tmevents.NewEventSwitch(),
}
// set function defaults (may be overwritten before calling Start)
cs.decideProposal = cs.defaultDecideProposal
@@ -170,18 +174,31 @@ func (cs *ConsensusState) GetState() sm.State {
return cs.state.Copy()
}
// GetRoundState returns a copy of the internal consensus state.
// GetRoundState returns a shallow copy of the internal consensus state.
func (cs *ConsensusState) GetRoundState() *cstypes.RoundState {
cs.mtx.Lock()
defer cs.mtx.Unlock()
return cs.getRoundState()
}
func (cs *ConsensusState) getRoundState() *cstypes.RoundState {
rs := cs.RoundState // copy
return &rs
}
// GetRoundStateJSON returns a json of RoundState, marshalled using go-amino.
func (cs *ConsensusState) GetRoundStateJSON() ([]byte, error) {
cs.mtx.Lock()
defer cs.mtx.Unlock()
return cdc.MarshalJSON(cs.RoundState)
}
// GetRoundStateSimpleJSON returns a json of RoundStateSimple, marshalled using go-amino.
func (cs *ConsensusState) GetRoundStateSimpleJSON() ([]byte, error) {
cs.mtx.Lock()
defer cs.mtx.Unlock()
return cdc.MarshalJSON(cs.RoundState.RoundStateSimple())
}
// GetValidators returns a copy of the current validators.
func (cs *ConsensusState) GetValidators() (int64, []*types.Validator) {
cs.mtx.Lock()
@@ -216,6 +233,10 @@ func (cs *ConsensusState) LoadCommit(height int64) *types.Commit {
// OnStart implements cmn.Service.
// It loads the latest state via the WAL, and starts the timeout and receive routines.
func (cs *ConsensusState) OnStart() error {
if err := cs.evsw.Start(); err != nil {
return err
}
// we may set the WAL in testing before calling Start,
// so only OpenWAL if its still the nilWAL
if _, ok := cs.wal.(nilWAL); ok {
@@ -233,8 +254,7 @@ func (cs *ConsensusState) OnStart() error {
// NOTE: we will get a build up of garbage go routines
// firing on the tockChan until the receiveRoutine is started
// to deal with them (by that point, at most one will be valid)
err := cs.timeoutTicker.Start()
if err != nil {
if err := cs.timeoutTicker.Start(); err != nil {
return err
}
@@ -273,6 +293,8 @@ func (cs *ConsensusState) startRoutines(maxSteps int) {
func (cs *ConsensusState) OnStop() {
cs.BaseService.OnStop()
cs.evsw.Stop()
cs.timeoutTicker.Stop()
// Make BaseService.Wait() wait until cs.wal.Wait()
@@ -290,7 +312,7 @@ func (cs *ConsensusState) Wait() {
// OpenWAL opens a file to log all consensus messages and timeouts for deterministic accountability
func (cs *ConsensusState) OpenWAL(walFile string) (WAL, error) {
wal, err := NewWAL(walFile, cs.config.WalLight)
wal, err := NewWAL(walFile)
if err != nil {
cs.Logger.Error("Failed to open WAL for consensus state", "wal", walFile, "err", err)
return nil, err
@@ -493,11 +515,12 @@ func (cs *ConsensusState) updateToState(state sm.State) {
func (cs *ConsensusState) newStep() {
rs := cs.RoundStateEvent()
cs.wal.Save(rs)
cs.wal.Write(rs)
cs.nSteps++
// newStep is called by updateToStep in NewConsensusState before the eventBus is set!
if cs.eventBus != nil {
cs.eventBus.PublishEventNewRoundStep(rs)
cs.evsw.FireEvent(types.EventNewRoundStep, &cs.RoundState)
}
}
@@ -531,16 +554,16 @@ func (cs *ConsensusState) receiveRoutine(maxSteps int) {
case height := <-cs.mempool.TxsAvailable():
cs.handleTxsAvailable(height)
case mi = <-cs.peerMsgQueue:
cs.wal.Save(mi)
cs.wal.Write(mi)
// handles proposals, block parts, votes
// may generate internal events (votes, complete proposals, 2/3 majorities)
cs.handleMsg(mi)
case mi = <-cs.internalMsgQueue:
cs.wal.Save(mi)
cs.wal.WriteSync(mi) // NOTE: fsync
// handles proposals, block parts, votes
cs.handleMsg(mi)
case ti := <-cs.timeoutTicker.Chan(): // tockChan:
cs.wal.Save(ti)
cs.wal.Write(ti)
// if the timeout is relevant to the rs
// go to the next step
cs.handleTimeout(ti, rs)
@@ -573,8 +596,9 @@ func (cs *ConsensusState) handleMsg(mi msgInfo) {
err = cs.setProposal(msg.Proposal)
case *BlockPartMessage:
// if the proposal is complete, we'll enterPrevote or tryFinalizeCommit
_, err = cs.addProposalBlockPart(msg.Height, msg.Part, peerID != "")
_, err = cs.addProposalBlockPart(msg.Height, msg.Part)
if err != nil && msg.Round != cs.Round {
cs.Logger.Debug("Received block part from wrong round", "height", cs.Height, "csRound", cs.Round, "blockRound", msg.Round)
err = nil
}
case *VoteMessage:
@@ -599,7 +623,7 @@ func (cs *ConsensusState) handleMsg(mi msgInfo) {
cs.Logger.Error("Unknown msg type", reflect.TypeOf(msg))
}
if err != nil {
cs.Logger.Error("Error with msg", "type", reflect.TypeOf(msg), "peer", peerID, "err", err, "msg", msg)
cs.Logger.Error("Error with msg", "height", cs.Height, "round", cs.Round, "type", reflect.TypeOf(msg), "peer", peerID, "err", err, "msg", msg)
}
}
@@ -656,16 +680,18 @@ func (cs *ConsensusState) handleTxsAvailable(height int64) {
// Enter: +2/3 prevotes any or +2/3 precommits for block or any from (height, round)
// NOTE: cs.StartTime was already set for height.
func (cs *ConsensusState) enterNewRound(height int64, round int) {
logger := cs.Logger.With("height", height, "round", round)
if cs.Height != height || round < cs.Round || (cs.Round == round && cs.Step != cstypes.RoundStepNewHeight) {
cs.Logger.Debug(cmn.Fmt("enterNewRound(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
logger.Debug(cmn.Fmt("enterNewRound(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
return
}
if now := time.Now(); cs.StartTime.After(now) {
cs.Logger.Info("Need to set a buffer and log message here for sanity.", "startTime", cs.StartTime, "now", now)
logger.Info("Need to set a buffer and log message here for sanity.", "startTime", cs.StartTime, "now", now)
}
cs.Logger.Info(cmn.Fmt("enterNewRound(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
logger.Info(cmn.Fmt("enterNewRound(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
// Increment validators if necessary
validators := cs.Validators
@@ -684,6 +710,7 @@ func (cs *ConsensusState) enterNewRound(height int64, round int) {
// and meanwhile we might have received a proposal
// for round 0.
} else {
logger.Info("Resetting Proposal info")
cs.Proposal = nil
cs.ProposalBlock = nil
cs.ProposalBlockParts = nil
@@ -737,6 +764,7 @@ func (cs *ConsensusState) proposalHeartbeat(height int64, round int) {
}
cs.privValidator.SignHeartbeat(chainID, heartbeat)
cs.eventBus.PublishEventProposalHeartbeat(types.EventDataProposalHeartbeat{heartbeat})
cs.evsw.FireEvent(types.EventProposalHeartbeat, heartbeat)
counter++
time.Sleep(proposalHeartbeatIntervalSeconds * time.Second)
}
@@ -746,11 +774,13 @@ func (cs *ConsensusState) proposalHeartbeat(height int64, round int) {
// Enter (CreateEmptyBlocks, CreateEmptyBlocksInterval > 0 ): after enterNewRound(height,round), after timeout of CreateEmptyBlocksInterval
// Enter (!CreateEmptyBlocks) : after enterNewRound(height,round), once txs are in the mempool
func (cs *ConsensusState) enterPropose(height int64, round int) {
logger := cs.Logger.With("height", height, "round", round)
if cs.Height != height || round < cs.Round || (cs.Round == round && cstypes.RoundStepPropose <= cs.Step) {
cs.Logger.Debug(cmn.Fmt("enterPropose(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
logger.Debug(cmn.Fmt("enterPropose(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
return
}
cs.Logger.Info(cmn.Fmt("enterPropose(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
logger.Info(cmn.Fmt("enterPropose(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
defer func() {
// Done enterPropose:
@@ -770,22 +800,22 @@ func (cs *ConsensusState) enterPropose(height int64, round int) {
// Nothing more to do if we're not a validator
if cs.privValidator == nil {
cs.Logger.Debug("This node is not a validator")
logger.Debug("This node is not a validator")
return
}
// if not a validator, we're done
if !cs.Validators.HasAddress(cs.privValidator.GetAddress()) {
cs.Logger.Debug("This node is not a validator")
logger.Debug("This node is not a validator", "addr", cs.privValidator.GetAddress(), "vals", cs.Validators)
return
}
cs.Logger.Debug("This node is a validator")
logger.Debug("This node is a validator")
if cs.isProposer() {
cs.Logger.Info("enterPropose: Our turn to propose", "proposer", cs.Validators.GetProposer().Address, "privValidator", cs.privValidator)
logger.Info("enterPropose: Our turn to propose", "proposer", cs.Validators.GetProposer().Address, "privValidator", cs.privValidator)
cs.decideProposal(height, round)
} else {
cs.Logger.Info("enterPropose: Not our turn to propose", "proposer", cs.Validators.GetProposer().Address, "privValidator", cs.privValidator)
logger.Info("enterPropose: Not our turn to propose", "proposer", cs.Validators.GetProposer().Address, "privValidator", cs.privValidator)
}
}
@@ -948,14 +978,16 @@ func (cs *ConsensusState) defaultDoPrevote(height int64, round int) {
// Enter: any +2/3 prevotes at next round.
func (cs *ConsensusState) enterPrevoteWait(height int64, round int) {
logger := cs.Logger.With("height", height, "round", round)
if cs.Height != height || round < cs.Round || (cs.Round == round && cstypes.RoundStepPrevoteWait <= cs.Step) {
cs.Logger.Debug(cmn.Fmt("enterPrevoteWait(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
logger.Debug(cmn.Fmt("enterPrevoteWait(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
return
}
if !cs.Votes.Prevotes(round).HasTwoThirdsAny() {
cmn.PanicSanity(cmn.Fmt("enterPrevoteWait(%v/%v), but Prevotes does not have any +2/3 votes", height, round))
}
cs.Logger.Info(cmn.Fmt("enterPrevoteWait(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
logger.Info(cmn.Fmt("enterPrevoteWait(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
defer func() {
// Done enterPrevoteWait:
@@ -974,12 +1006,14 @@ func (cs *ConsensusState) enterPrevoteWait(height int64, round int) {
// else, unlock an existing lock and precommit nil if +2/3 of prevotes were nil,
// else, precommit nil otherwise.
func (cs *ConsensusState) enterPrecommit(height int64, round int) {
logger := cs.Logger.With("height", height, "round", round)
if cs.Height != height || round < cs.Round || (cs.Round == round && cstypes.RoundStepPrecommit <= cs.Step) {
cs.Logger.Debug(cmn.Fmt("enterPrecommit(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
logger.Debug(cmn.Fmt("enterPrecommit(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
return
}
cs.Logger.Info(cmn.Fmt("enterPrecommit(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
logger.Info(cmn.Fmt("enterPrecommit(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
defer func() {
// Done enterPrecommit:
@@ -987,23 +1021,24 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) {
cs.newStep()
}()
// check for a polka
blockID, ok := cs.Votes.Prevotes(round).TwoThirdsMajority()
// If we don't have a polka, we must precommit nil
// If we don't have a polka, we must precommit nil.
if !ok {
if cs.LockedBlock != nil {
cs.Logger.Info("enterPrecommit: No +2/3 prevotes during enterPrecommit while we're locked. Precommitting nil")
logger.Info("enterPrecommit: No +2/3 prevotes during enterPrecommit while we're locked. Precommitting nil")
} else {
cs.Logger.Info("enterPrecommit: No +2/3 prevotes during enterPrecommit. Precommitting nil.")
logger.Info("enterPrecommit: No +2/3 prevotes during enterPrecommit. Precommitting nil.")
}
cs.signAddVote(types.VoteTypePrecommit, nil, types.PartSetHeader{})
return
}
// At this point +2/3 prevoted for a particular block or nil
// At this point +2/3 prevoted for a particular block or nil.
cs.eventBus.PublishEventPolka(cs.RoundStateEvent())
// the latest POLRound should be this round
// the latest POLRound should be this round.
polRound, _ := cs.Votes.POLInfo()
if polRound < round {
cmn.PanicSanity(cmn.Fmt("This POLRound should be %v but got %", round, polRound))
@@ -1012,9 +1047,9 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) {
// +2/3 prevoted nil. Unlock and precommit nil.
if len(blockID.Hash) == 0 {
if cs.LockedBlock == nil {
cs.Logger.Info("enterPrecommit: +2/3 prevoted for nil.")
logger.Info("enterPrecommit: +2/3 prevoted for nil.")
} else {
cs.Logger.Info("enterPrecommit: +2/3 prevoted for nil. Unlocking")
logger.Info("enterPrecommit: +2/3 prevoted for nil. Unlocking")
cs.LockedRound = 0
cs.LockedBlock = nil
cs.LockedBlockParts = nil
@@ -1028,7 +1063,7 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) {
// If we're already locked on that block, precommit it, and update the LockedRound
if cs.LockedBlock.HashesTo(blockID.Hash) {
cs.Logger.Info("enterPrecommit: +2/3 prevoted locked block. Relocking")
logger.Info("enterPrecommit: +2/3 prevoted locked block. Relocking")
cs.LockedRound = round
cs.eventBus.PublishEventRelock(cs.RoundStateEvent())
cs.signAddVote(types.VoteTypePrecommit, blockID.Hash, blockID.PartsHeader)
@@ -1037,7 +1072,7 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) {
// If +2/3 prevoted for proposal block, stage and precommit it
if cs.ProposalBlock.HashesTo(blockID.Hash) {
cs.Logger.Info("enterPrecommit: +2/3 prevoted proposal block. Locking", "hash", blockID.Hash)
logger.Info("enterPrecommit: +2/3 prevoted proposal block. Locking", "hash", blockID.Hash)
// Validate the block.
if err := cs.blockExec.ValidateBlock(cs.state, cs.ProposalBlock); err != nil {
cmn.PanicConsensus(cmn.Fmt("enterPrecommit: +2/3 prevoted for an invalid block: %v", err))
@@ -1067,14 +1102,16 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) {
// Enter: any +2/3 precommits for next round.
func (cs *ConsensusState) enterPrecommitWait(height int64, round int) {
logger := cs.Logger.With("height", height, "round", round)
if cs.Height != height || round < cs.Round || (cs.Round == round && cstypes.RoundStepPrecommitWait <= cs.Step) {
cs.Logger.Debug(cmn.Fmt("enterPrecommitWait(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
logger.Debug(cmn.Fmt("enterPrecommitWait(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
return
}
if !cs.Votes.Precommits(round).HasTwoThirdsAny() {
cmn.PanicSanity(cmn.Fmt("enterPrecommitWait(%v/%v), but Precommits does not have any +2/3 votes", height, round))
}
cs.Logger.Info(cmn.Fmt("enterPrecommitWait(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
logger.Info(cmn.Fmt("enterPrecommitWait(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
defer func() {
// Done enterPrecommitWait:
@@ -1089,11 +1126,13 @@ func (cs *ConsensusState) enterPrecommitWait(height int64, round int) {
// Enter: +2/3 precommits for block
func (cs *ConsensusState) enterCommit(height int64, commitRound int) {
logger := cs.Logger.With("height", height, "commitRound", commitRound)
if cs.Height != height || cstypes.RoundStepCommit <= cs.Step {
cs.Logger.Debug(cmn.Fmt("enterCommit(%v/%v): Invalid args. Current step: %v/%v/%v", height, commitRound, cs.Height, cs.Round, cs.Step))
logger.Debug(cmn.Fmt("enterCommit(%v/%v): Invalid args. Current step: %v/%v/%v", height, commitRound, cs.Height, cs.Round, cs.Step))
return
}
cs.Logger.Info(cmn.Fmt("enterCommit(%v/%v). Current: %v/%v/%v", height, commitRound, cs.Height, cs.Round, cs.Step))
logger.Info(cmn.Fmt("enterCommit(%v/%v). Current: %v/%v/%v", height, commitRound, cs.Height, cs.Round, cs.Step))
defer func() {
// Done enterCommit:
@@ -1116,6 +1155,7 @@ func (cs *ConsensusState) enterCommit(height int64, commitRound int) {
// Move them over to ProposalBlock if they match the commit hash,
// otherwise they'll be cleared in updateToState.
if cs.LockedBlock.HashesTo(blockID.Hash) {
logger.Info("Commit is for locked block. Set ProposalBlock=LockedBlock", "blockHash", blockID.Hash)
cs.ProposalBlock = cs.LockedBlock
cs.ProposalBlockParts = cs.LockedBlockParts
}
@@ -1123,6 +1163,7 @@ func (cs *ConsensusState) enterCommit(height int64, commitRound int) {
// If we don't have the block being committed, set up to get it.
if !cs.ProposalBlock.HashesTo(blockID.Hash) {
if !cs.ProposalBlockParts.HasHeader(blockID.PartsHeader) {
logger.Info("Commit is for a block we don't know about. Set ProposalBlock=nil", "proposal", cs.ProposalBlock.Hash(), "commit", blockID.Hash)
// We're getting the wrong block.
// Set up ProposalBlockParts and keep waiting.
cs.ProposalBlock = nil
@@ -1135,19 +1176,21 @@ func (cs *ConsensusState) enterCommit(height int64, commitRound int) {
// If we have the block AND +2/3 commits for it, finalize.
func (cs *ConsensusState) tryFinalizeCommit(height int64) {
logger := cs.Logger.With("height", height)
if cs.Height != height {
cmn.PanicSanity(cmn.Fmt("tryFinalizeCommit() cs.Height: %v vs height: %v", cs.Height, height))
}
blockID, ok := cs.Votes.Precommits(cs.CommitRound).TwoThirdsMajority()
if !ok || len(blockID.Hash) == 0 {
cs.Logger.Error("Attempt to finalize failed. There was no +2/3 majority, or +2/3 was for <nil>.", "height", height)
logger.Error("Attempt to finalize failed. There was no +2/3 majority, or +2/3 was for <nil>.")
return
}
if !cs.ProposalBlock.HashesTo(blockID.Hash) {
// TODO: this happens every time if we're not a validator (ugly logs)
// TODO: ^^ wait, why does it matter that we're a validator?
cs.Logger.Info("Attempt to finalize failed. We don't have the commit block.", "height", height, "proposal-block", cs.ProposalBlock.Hash(), "commit-block", blockID.Hash)
logger.Info("Attempt to finalize failed. We don't have the commit block.", "proposal-block", cs.ProposalBlock.Hash(), "commit-block", blockID.Hash)
return
}
@@ -1198,23 +1241,28 @@ func (cs *ConsensusState) finalizeCommit(height int64) {
fail.Fail() // XXX
// Finish writing to the WAL for this height.
// NOTE: If we fail before writing this, we'll never write it,
// and just recover by running ApplyBlock in the Handshake.
// If we moved it before persisting the block, we'd have to allow
// WAL replay for blocks with an #ENDHEIGHT
// As is, ConsensusState should not be started again
// until we successfully call ApplyBlock (ie. here or in Handshake after restart)
cs.wal.Save(EndHeightMessage{height})
// Write EndHeightMessage{} for this height, implying that the blockstore
// has saved the block.
//
// If we crash before writing this EndHeightMessage{}, we will recover by
// running ApplyBlock during the ABCI handshake when we restart. If we
// didn't save the block to the blockstore before writing
// EndHeightMessage{}, we'd have to change WAL replay -- currently it
// complains about replaying for heights where an #ENDHEIGHT entry already
// exists.
//
// Either way, the ConsensusState should not be resumed until we
// successfully call ApplyBlock (ie. later here, or in Handshake after
// restart).
cs.wal.WriteSync(EndHeightMessage{height}) // NOTE: fsync
fail.Fail() // XXX
// Create a copy of the state for staging
// and an event cache for txs
// Create a copy of the state for staging and an event cache for txs.
stateCopy := cs.state.Copy()
// Execute and commit the block, update and save the state, and update the mempool.
// NOTE: the block.AppHash wont reflect these txs until the next block
// NOTE The block.AppHash wont reflect these txs until the next block.
var err error
stateCopy, err = cs.blockExec.ApplyBlock(stateCopy, types.BlockID{block.Hash(), blockParts.Header()}, block)
if err != nil {
@@ -1275,34 +1323,56 @@ func (cs *ConsensusState) defaultSetProposal(proposal *types.Proposal) error {
cs.Proposal = proposal
cs.ProposalBlockParts = types.NewPartSetFromHeader(proposal.BlockPartsHeader)
cs.Logger.Info("Received proposal", "proposal", proposal)
return nil
}
// NOTE: block is not necessarily valid.
// Asynchronously triggers either enterPrevote (before we timeout of propose) or tryFinalizeCommit, once we have the full block.
func (cs *ConsensusState) addProposalBlockPart(height int64, part *types.Part, verify bool) (added bool, err error) {
func (cs *ConsensusState) addProposalBlockPart(height int64, part *types.Part) (added bool, err error) {
// Blocks might be reused, so round mismatch is OK
if cs.Height != height {
cs.Logger.Debug("Received block part from wrong height", "height", height)
return false, nil
}
// We're not expecting a block part.
if cs.ProposalBlockParts == nil {
cs.Logger.Info("Received a block part when we're not expecting any", "height", height)
return false, nil // TODO: bad peer? Return error?
}
added, err = cs.ProposalBlockParts.AddPart(part, verify)
added, err = cs.ProposalBlockParts.AddPart(part)
if err != nil {
return added, err
}
if added && cs.ProposalBlockParts.IsComplete() {
// Added and completed!
var n int
var err error
cs.ProposalBlock = wire.ReadBinary(&types.Block{}, cs.ProposalBlockParts.GetReader(),
cs.state.ConsensusParams.BlockSize.MaxBytes, &n, &err).(*types.Block)
_, err = cdc.UnmarshalBinaryReader(cs.ProposalBlockParts.GetReader(), &cs.ProposalBlock, int64(cs.state.ConsensusParams.BlockSize.MaxBytes))
if err != nil {
return true, err
}
// NOTE: it's possible to receive complete proposal blocks for future rounds without having the proposal
cs.Logger.Info("Received complete proposal block", "height", cs.ProposalBlock.Height, "hash", cs.ProposalBlock.Hash())
// Update Valid* if we can.
prevotes := cs.Votes.Prevotes(cs.Round)
blockID, hasTwoThirds := prevotes.TwoThirdsMajority()
if hasTwoThirds && !blockID.IsZero() && (cs.ValidRound < cs.Round) {
if cs.ProposalBlock.HashesTo(blockID.Hash) {
cs.Logger.Info("Updating valid block to new proposal block",
"valid-round", cs.Round, "valid-block-hash", cs.ProposalBlock.Hash())
cs.ValidRound = cs.Round
cs.ValidBlock = cs.ProposalBlock
cs.ValidBlockParts = cs.ProposalBlockParts
}
// TODO: In case there is +2/3 majority in Prevotes set for some
// block and cs.ProposalBlock contains different block, either
// proposer is faulty or voting power of faulty processes is more
// than 1/3. We should trigger in the future accountability
// procedure at this point.
}
if cs.Step == cstypes.RoundStepPropose && cs.isProposalComplete() {
// Move onto the next step
cs.enterPrevote(height, cs.Round)
@@ -1310,7 +1380,7 @@ func (cs *ConsensusState) addProposalBlockPart(height int64, part *types.Part, v
// If we're waiting on the proposal block...
cs.tryFinalizeCommit(height)
}
return true, err
return true, nil
}
return added, nil
}
@@ -1361,6 +1431,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
cs.Logger.Info(cmn.Fmt("Added to lastPrecommits: %v", cs.LastCommit.StringShort()))
cs.eventBus.PublishEventVote(types.EventDataVote{vote})
cs.evsw.FireEvent(types.EventVote, vote)
// if we can skip timeoutCommit and have all the votes now,
if cs.config.SkipTimeoutCommit && cs.LastCommit.HasAll() {
@@ -1388,38 +1459,50 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
}
cs.eventBus.PublishEventVote(types.EventDataVote{vote})
cs.evsw.FireEvent(types.EventVote, vote)
switch vote.Type {
case types.VoteTypePrevote:
prevotes := cs.Votes.Prevotes(vote.Round)
cs.Logger.Info("Added to prevote", "vote", vote, "prevotes", prevotes.StringShort())
blockID, ok := prevotes.TwoThirdsMajority()
// First, unlock if prevotes is a valid POL.
// >> lockRound < POLRound <= unlockOrChangeLockRound (see spec)
// NOTE: If (lockRound < POLRound) but !(POLRound <= unlockOrChangeLockRound),
// we'll still enterNewRound(H,vote.R) and enterPrecommit(H,vote.R) to process it
// there.
if (cs.LockedBlock != nil) && (cs.LockedRound < vote.Round) && (vote.Round <= cs.Round) {
if ok && !cs.LockedBlock.HashesTo(blockID.Hash) {
// If +2/3 prevotes for a block or nil for *any* round:
if blockID, ok := prevotes.TwoThirdsMajority(); ok {
// There was a polka!
// If we're locked but this is a recent polka, unlock.
// If it matches our ProposalBlock, update the ValidBlock
// Unlock if `cs.LockedRound < vote.Round <= cs.Round`
// NOTE: If vote.Round > cs.Round, we'll deal with it when we get to vote.Round
if (cs.LockedBlock != nil) &&
(cs.LockedRound < vote.Round) &&
(vote.Round <= cs.Round) &&
!cs.LockedBlock.HashesTo(blockID.Hash) {
cs.Logger.Info("Unlocking because of POL.", "lockedRound", cs.LockedRound, "POLRound", vote.Round)
cs.LockedRound = 0
cs.LockedBlock = nil
cs.LockedBlockParts = nil
cs.eventBus.PublishEventUnlock(cs.RoundStateEvent())
}
}
// Update ValidBlock
if ok && !blockID.IsZero() && !cs.ValidBlock.HashesTo(blockID.Hash) && vote.Round > cs.ValidRound {
// update valid value
if cs.ProposalBlock.HashesTo(blockID.Hash) {
// Update Valid* if we can.
// NOTE: our proposal block may be nil or not what received a polka..
// TODO: we may want to still update the ValidBlock and obtain it via gossipping
if !blockID.IsZero() &&
(cs.ValidRound < vote.Round) &&
(vote.Round <= cs.Round) &&
cs.ProposalBlock.HashesTo(blockID.Hash) {
cs.Logger.Info("Updating ValidBlock because of POL.", "validRound", cs.ValidRound, "POLRound", vote.Round)
cs.ValidRound = vote.Round
cs.ValidBlock = cs.ProposalBlock
cs.ValidBlockParts = cs.ProposalBlockParts
}
//TODO: We might want to update ValidBlock also in case we don't have that block yet,
// and obtain the required block using gossiping
}
// If +2/3 prevotes for *anything* for this or future round:
if cs.Round <= vote.Round && prevotes.HasTwoThirdsAny() {
// Round-skip over to PrevoteWait or goto Precommit.
cs.enterNewRound(height, vote.Round) // if the vote is ahead of us
@@ -1435,6 +1518,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
cs.enterPrevote(height, cs.Round)
}
}
case types.VoteTypePrecommit:
precommits := cs.Votes.Precommits(vote.Round)
cs.Logger.Info("Added to precommit", "vote", vote, "precommits", precommits.StringShort())

View File

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

View File

@@ -174,6 +174,26 @@ func (hvs *HeightVoteSet) getVoteSet(round int, type_ byte) *types.VoteSet {
}
}
// 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 p2p.ID, blockID types.BlockID) error {
hvs.mtx.Lock()
defer hvs.mtx.Unlock()
if !types.IsVoteTypeValid(type_) {
return fmt.Errorf("SetPeerMaj23: Invalid vote type %v", type_)
}
voteSet := hvs.getVoteSet(round, type_)
if voteSet == nil {
return nil // something we don't know about yet
}
return voteSet.SetPeerMaj23(types.P2PID(peerID), blockID)
}
//---------------------------------------------------------
// string and json
func (hvs *HeightVoteSet) String() string {
return hvs.StringIndented("")
}
@@ -207,19 +227,35 @@ func (hvs *HeightVoteSet) StringIndented(indent string) string {
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 p2p.ID, blockID types.BlockID) error {
func (hvs *HeightVoteSet) MarshalJSON() ([]byte, error) {
hvs.mtx.Lock()
defer hvs.mtx.Unlock()
if !types.IsVoteTypeValid(type_) {
return fmt.Errorf("SetPeerMaj23: Invalid vote type %v", type_)
}
voteSet := hvs.getVoteSet(round, type_)
if voteSet == nil {
return nil // something we don't know about yet
}
return voteSet.SetPeerMaj23(types.P2PID(peerID), blockID)
allVotes := hvs.toAllRoundVotes()
return cdc.MarshalJSON(allVotes)
}
func (hvs *HeightVoteSet) toAllRoundVotes() []roundVotes {
totalRounds := hvs.round + 1
allVotes := make([]roundVotes, totalRounds)
// rounds 0 ~ hvs.round inclusive
for round := 0; round < totalRounds; round++ {
allVotes[round] = roundVotes{
Round: round,
Prevotes: hvs.roundVoteSets[round].Prevotes.VoteStrings(),
PrevotesBitArray: hvs.roundVoteSets[round].Prevotes.BitArrayString(),
Precommits: hvs.roundVoteSets[round].Precommits.VoteStrings(),
PrecommitsBitArray: hvs.roundVoteSets[round].Precommits.BitArrayString(),
}
}
// TODO: all other peer catchup rounds
return allVotes
}
type roundVotes struct {
Round int `json:"round"`
Prevotes []string `json:"prevotes"`
PrevotesBitArray string `json:"prevotes_bit_array"`
Precommits []string `json:"precommits"`
PrecommitsBitArray string `json:"precommits_bit_array"`
}

View File

@@ -48,7 +48,7 @@ func TestPeerCatchupRounds(t *testing.T) {
}
func makeVoteHR(t *testing.T, height int64, round int, privVals []*types.PrivValidatorFS, valIndex int) *types.Vote {
func makeVoteHR(t *testing.T, height int64, round int, privVals []types.PrivValidator, valIndex int) *types.Vote {
privVal := privVals[valIndex]
vote := &types.Vote{
ValidatorAddress: privVal.GetAddress(),

View File

@@ -0,0 +1,57 @@
package types
import (
"fmt"
"time"
"github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common"
)
//-----------------------------------------------------------------------------
// PeerRoundState contains the known state of a peer.
// NOTE: Read-only when returned by PeerState.GetRoundState().
type PeerRoundState struct {
Height int64 `json:"height"` // Height peer is at
Round int `json:"round"` // Round peer is at, -1 if unknown.
Step RoundStepType `json:"step"` // Step peer is at
StartTime time.Time `json:"start_time"` // Estimated start of round 0 at this height
Proposal bool `json:"proposal"` // True if peer has proposal for this round
ProposalBlockPartsHeader types.PartSetHeader `json:"proposal_block_parts_header"` //
ProposalBlockParts *cmn.BitArray `json:"proposal_block_parts"` //
ProposalPOLRound int `json:"proposal_pol_round"` // Proposal's POL round. -1 if none.
ProposalPOL *cmn.BitArray `json:"proposal_pol"` // nil until ProposalPOLMessage received.
Prevotes *cmn.BitArray `json:"prevotes"` // All votes peer has for this round
Precommits *cmn.BitArray `json:"precommits"` // All precommits peer has for this round
LastCommitRound int `json:"last_commit_round"` // Round of commit for last height. -1 if none.
LastCommit *cmn.BitArray `json:"last_commit"` // All commit precommits of commit for last height.
CatchupCommitRound int `json:"catchup_commit_round"` // Round that we have commit for. Not necessarily unique. -1 if none.
CatchupCommit *cmn.BitArray `json:"catchup_commit"` // All commit precommits peer has for this height & CatchupCommitRound
}
// String returns a string representation of the PeerRoundState
func (prs PeerRoundState) String() string {
return prs.StringIndented("")
}
// StringIndented returns a string representation of the PeerRoundState
func (prs PeerRoundState) StringIndented(indent string) string {
return fmt.Sprintf(`PeerRoundState{
%s %v/%v/%v @%v
%s Proposal %v -> %v
%s POL %v (round %v)
%s Prevotes %v
%s Precommits %v
%s LastCommit %v (round %v)
%s Catchup %v (round %v)
%s}`,
indent, prs.Height, prs.Round, prs.Step, prs.StartTime,
indent, prs.ProposalBlockPartsHeader, prs.ProposalBlockParts,
indent, prs.ProposalPOL, prs.ProposalPOLRound,
indent, prs.Prevotes,
indent, prs.Precommits,
indent, prs.LastCommit, prs.LastCommitRound,
indent, prs.CatchupCommit, prs.CatchupCommitRound,
indent)
}

View File

@@ -1,57 +0,0 @@
package types
import (
"fmt"
"time"
"github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common"
)
//-----------------------------------------------------------------------------
// PeerRoundState contains the known state of a peer.
// NOTE: Read-only when returned by PeerState.GetRoundState().
type PeerRoundState struct {
Height int64 // Height peer is at
Round int // Round peer is at, -1 if unknown.
Step RoundStepType // Step peer is at
StartTime time.Time // Estimated start of round 0 at this height
Proposal bool // True if peer has proposal for this round
ProposalBlockPartsHeader types.PartSetHeader //
ProposalBlockParts *cmn.BitArray //
ProposalPOLRound int // Proposal's POL round. -1 if none.
ProposalPOL *cmn.BitArray // nil until ProposalPOLMessage received.
Prevotes *cmn.BitArray // All votes peer has for this round
Precommits *cmn.BitArray // All precommits peer has for this round
LastCommitRound int // Round of commit for last height. -1 if none.
LastCommit *cmn.BitArray // All commit precommits of commit for last height.
CatchupCommitRound int // Round that we have commit for. Not necessarily unique. -1 if none.
CatchupCommit *cmn.BitArray // All commit precommits peer has for this height & CatchupCommitRound
}
// String returns a string representation of the PeerRoundState
func (prs PeerRoundState) String() string {
return prs.StringIndented("")
}
// StringIndented returns a string representation of the PeerRoundState
func (prs PeerRoundState) StringIndented(indent string) string {
return fmt.Sprintf(`PeerRoundState{
%s %v/%v/%v @%v
%s Proposal %v -> %v
%s POL %v (round %v)
%s Prevotes %v
%s Precommits %v
%s LastCommit %v (round %v)
%s Catchup %v (round %v)
%s}`,
indent, prs.Height, prs.Round, prs.Step, prs.StartTime,
indent, prs.ProposalBlockPartsHeader, prs.ProposalBlockParts,
indent, prs.ProposalPOL, prs.ProposalPOLRound,
indent, prs.Prevotes,
indent, prs.Precommits,
indent, prs.LastCommit, prs.LastCommitRound,
indent, prs.CatchupCommit, prs.CatchupCommitRound,
indent)
}

View File

@@ -1,10 +1,12 @@
package types
import (
"encoding/json"
"fmt"
"time"
"github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common"
)
//-----------------------------------------------------------------------------
@@ -13,6 +15,7 @@ import (
// RoundStepType enumerates the state of the consensus state machine
type RoundStepType uint8 // These must be numeric, ordered.
// RoundStepType
const (
RoundStepNewHeight = RoundStepType(0x01) // Wait til CommitTime + timeoutCommit
RoundStepNewRound = RoundStepType(0x02) // Setup new round and go to RoundStepPropose
@@ -52,43 +55,66 @@ func (rs RoundStepType) String() string {
//-----------------------------------------------------------------------------
// RoundState defines the internal consensus state.
// It is Immutable when returned from ConsensusState.GetRoundState()
// TODO: Actually, only the top pointer is copied,
// so access to field pointers is still racey
// NOTE: Not thread safe. Should only be manipulated by functions downstream
// of the cs.receiveRoutine
type RoundState struct {
Height int64 // Height we are working on
Round int
Step RoundStepType
StartTime time.Time
CommitTime time.Time // Subjective time when +2/3 precommits for Block at Round were found
Validators *types.ValidatorSet
Proposal *types.Proposal
ProposalBlock *types.Block
ProposalBlockParts *types.PartSet
LockedRound int
LockedBlock *types.Block
LockedBlockParts *types.PartSet
ValidRound int
ValidBlock *types.Block
ValidBlockParts *types.PartSet
Votes *HeightVoteSet
CommitRound int //
LastCommit *types.VoteSet // Last precommits at Height-1
LastValidators *types.ValidatorSet
Height int64 `json:"height"` // Height we are working on
Round int `json:"round"`
Step RoundStepType `json:"step"`
StartTime time.Time `json:"start_time"`
CommitTime time.Time `json:"commit_time"` // Subjective time when +2/3 precommits for Block at Round were found
Validators *types.ValidatorSet `json:"validators"`
Proposal *types.Proposal `json:"proposal"`
ProposalBlock *types.Block `json:"proposal_block"`
ProposalBlockParts *types.PartSet `json:"proposal_block_parts"`
LockedRound int `json:"locked_round"`
LockedBlock *types.Block `json:"locked_block"`
LockedBlockParts *types.PartSet `json:"locked_block_parts"`
ValidRound int `json:"valid_round"` // Last known round with POL for non-nil valid block.
ValidBlock *types.Block `json:"valid_block"` // Last known block of POL mentioned above.
ValidBlockParts *types.PartSet `json:"valid_block_parts"` // Last known block parts of POL metnioned above.
Votes *HeightVoteSet `json:"votes"`
CommitRound int `json:"commit_round"` //
LastCommit *types.VoteSet `json:"last_commit"` // Last precommits at Height-1
LastValidators *types.ValidatorSet `json:"last_validators"`
}
// Compressed version of the RoundState for use in RPC
type RoundStateSimple struct {
HeightRoundStep string `json:"height/round/step"`
StartTime time.Time `json:"start_time"`
ProposalBlockHash cmn.HexBytes `json:"proposal_block_hash"`
LockedBlockHash cmn.HexBytes `json:"locked_block_hash"`
ValidBlockHash cmn.HexBytes `json:"valid_block_hash"`
Votes json.RawMessage `json:"height_vote_set"`
}
// Compress the RoundState to RoundStateSimple
func (rs *RoundState) RoundStateSimple() RoundStateSimple {
votesJSON, err := rs.Votes.MarshalJSON()
if err != nil {
panic(err)
}
return RoundStateSimple{
HeightRoundStep: fmt.Sprintf("%d/%d/%d", rs.Height, rs.Round, rs.Step),
StartTime: rs.StartTime,
ProposalBlockHash: rs.ProposalBlock.Hash(),
LockedBlockHash: rs.LockedBlock.Hash(),
ValidBlockHash: rs.ValidBlock.Hash(),
Votes: votesJSON,
}
}
// RoundStateEvent returns the H/R/S of the RoundState as an event.
func (rs *RoundState) RoundStateEvent() types.EventDataRoundState {
// XXX: copy the RoundState
// if we want to avoid this, we may need synchronous events after all
rs_ := *rs
rsCopy := *rs
edrs := types.EventDataRoundState{
Height: rs.Height,
Round: rs.Round,
Step: rs.Step.String(),
RoundState: &rs_,
RoundState: &rsCopy,
}
return edrs
}
@@ -118,16 +144,16 @@ func (rs *RoundState) StringIndented(indent string) string {
indent, rs.Height, rs.Round, rs.Step,
indent, rs.StartTime,
indent, rs.CommitTime,
indent, rs.Validators.StringIndented(indent+" "),
indent, rs.Validators.StringIndented(indent+" "),
indent, rs.Proposal,
indent, rs.ProposalBlockParts.StringShort(), rs.ProposalBlock.StringShort(),
indent, rs.LockedRound,
indent, rs.LockedBlockParts.StringShort(), rs.LockedBlock.StringShort(),
indent, rs.ValidRound,
indent, rs.ValidBlockParts.StringShort(), rs.ValidBlock.StringShort(),
indent, rs.Votes.StringIndented(indent+" "),
indent, rs.Votes.StringIndented(indent+" "),
indent, rs.LastCommit.StringShort(),
indent, rs.LastValidators.StringIndented(indent+" "),
indent, rs.LastValidators.StringIndented(indent+" "),
indent)
}

View File

@@ -0,0 +1,95 @@
package types
import (
"testing"
"time"
"github.com/tendermint/go-amino"
"github.com/tendermint/go-crypto"
"github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common"
)
func BenchmarkRoundStateDeepCopy(b *testing.B) {
b.StopTimer()
// Random validators
nval, ntxs := 100, 100
vset, _ := types.RandValidatorSet(nval, 1)
precommits := make([]*types.Vote, nval)
blockID := types.BlockID{
Hash: cmn.RandBytes(20),
PartsHeader: types.PartSetHeader{
Hash: cmn.RandBytes(20),
},
}
sig := crypto.SignatureEd25519{}
for i := 0; i < nval; i++ {
precommits[i] = &types.Vote{
ValidatorAddress: types.Address(cmn.RandBytes(20)),
Timestamp: time.Now(),
BlockID: blockID,
Signature: sig,
}
}
txs := make([]types.Tx, ntxs)
for i := 0; i < ntxs; i++ {
txs[i] = cmn.RandBytes(100)
}
// Random block
block := &types.Block{
Header: &types.Header{
ChainID: cmn.RandStr(12),
Time: time.Now(),
LastBlockID: blockID,
LastCommitHash: cmn.RandBytes(20),
DataHash: cmn.RandBytes(20),
ValidatorsHash: cmn.RandBytes(20),
ConsensusHash: cmn.RandBytes(20),
AppHash: cmn.RandBytes(20),
LastResultsHash: cmn.RandBytes(20),
EvidenceHash: cmn.RandBytes(20),
},
Data: &types.Data{
Txs: txs,
},
Evidence: types.EvidenceData{},
LastCommit: &types.Commit{
BlockID: blockID,
Precommits: precommits,
},
}
parts := block.MakePartSet(4096)
// Random Proposal
proposal := &types.Proposal{
Timestamp: time.Now(),
BlockPartsHeader: types.PartSetHeader{
Hash: cmn.RandBytes(20),
},
POLBlockID: blockID,
Signature: sig,
}
// Random HeightVoteSet
// TODO: hvs :=
rs := &RoundState{
StartTime: time.Now(),
CommitTime: time.Now(),
Validators: vset,
Proposal: proposal,
ProposalBlock: block,
ProposalBlockParts: parts,
LockedBlock: block,
LockedBlockParts: parts,
ValidBlock: block,
ValidBlockParts: parts,
Votes: nil, // TODO
LastCommit: nil, // TODO
LastValidators: vset,
}
b.StartTimer()
for i := 0; i < b.N; i++ {
amino.DeepCopy(rs)
}
}

12
consensus/types/wire.go Normal file
View File

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

View File

@@ -1,7 +1,6 @@
package consensus
import (
"bytes"
"encoding/binary"
"fmt"
"hash/crc32"
@@ -11,7 +10,7 @@ import (
"github.com/pkg/errors"
wire "github.com/tendermint/go-wire"
amino "github.com/tendermint/go-amino"
"github.com/tendermint/tendermint/types"
auto "github.com/tendermint/tmlibs/autofile"
cmn "github.com/tendermint/tmlibs/common"
@@ -38,20 +37,21 @@ type EndHeightMessage struct {
type WALMessage interface{}
var _ = wire.RegisterInterface(
struct{ WALMessage }{},
wire.ConcreteType{types.EventDataRoundState{}, 0x01},
wire.ConcreteType{msgInfo{}, 0x02},
wire.ConcreteType{timeoutInfo{}, 0x03},
wire.ConcreteType{EndHeightMessage{}, 0x04},
)
func RegisterWALMessages(cdc *amino.Codec) {
cdc.RegisterInterface((*WALMessage)(nil), nil)
cdc.RegisterConcrete(types.EventDataRoundState{}, "tendermint/wal/EventDataRoundState", nil)
cdc.RegisterConcrete(msgInfo{}, "tendermint/wal/MsgInfo", nil)
cdc.RegisterConcrete(timeoutInfo{}, "tendermint/wal/TimeoutInfo", nil)
cdc.RegisterConcrete(EndHeightMessage{}, "tendermint/wal/EndHeightMessage", nil)
}
//--------------------------------------------------------
// Simple write-ahead logger
// WAL is an interface for any write-ahead logger.
type WAL interface {
Save(WALMessage)
Write(WALMessage)
WriteSync(WALMessage)
Group() *auto.Group
SearchForEndHeight(height int64, options *WALSearchOptions) (gr *auto.GroupReader, found bool, err error)
@@ -68,12 +68,11 @@ type baseWAL struct {
cmn.BaseService
group *auto.Group
light bool // ignore block parts
enc *WALEncoder
}
func NewWAL(walFile string, light bool) (*baseWAL, error) {
func NewWAL(walFile string) (*baseWAL, error) {
err := cmn.EnsureDir(filepath.Dir(walFile), 0700)
if err != nil {
return nil, errors.Wrap(err, "failed to ensure WAL directory is in place")
@@ -85,7 +84,6 @@ func NewWAL(walFile string, light bool) (*baseWAL, error) {
}
wal := &baseWAL{
group: group,
light: light,
enc: NewWALEncoder(group),
}
wal.BaseService = *cmn.NewBaseService(nil, "baseWAL", wal)
@@ -101,7 +99,7 @@ func (wal *baseWAL) OnStart() error {
if err != nil {
return err
} else if size == 0 {
wal.Save(EndHeightMessage{0})
wal.WriteSync(EndHeightMessage{0})
}
err = wal.group.Start()
return err
@@ -112,29 +110,31 @@ func (wal *baseWAL) OnStop() {
wal.group.Stop()
}
// called in newStep and for each pass in receiveRoutine
func (wal *baseWAL) Save(msg WALMessage) {
// Write is called in newStep and for each receive on the
// peerMsgQueue and the timeoutTicker.
// NOTE: does not call fsync()
func (wal *baseWAL) Write(msg 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 := msg.(msgInfo); ok {
if mi.PeerID != "" {
return
}
}
}
// Write the wal message
if err := wal.enc.Encode(&TimedWALMessage{time.Now(), msg}); err != nil {
cmn.PanicQ(cmn.Fmt("Error writing msg to consensus wal: %v \n\nMessage: %v", err, msg))
panic(cmn.Fmt("Error writing msg to consensus wal: %v \n\nMessage: %v", err, msg))
}
}
// WriteSync is called when we receive a msg from ourselves
// so that we write to disk before sending signed messages.
// NOTE: calls fsync()
func (wal *baseWAL) WriteSync(msg WALMessage) {
if wal == nil {
return
}
// TODO: only flush when necessary
wal.Write(msg)
if err := wal.group.Flush(); err != nil {
cmn.PanicQ(cmn.Fmt("Error flushing consensus wal buf to file. Error: %v \n", err))
panic(cmn.Fmt("Error flushing consensus wal buf to file. Error: %v \n", err))
}
}
@@ -144,13 +144,14 @@ type WALSearchOptions struct {
IgnoreDataCorruptionErrors bool
}
// SearchForEndHeight searches for the EndHeightMessage with the height and
// returns an auto.GroupReader, whenever it was found or not and an error.
// SearchForEndHeight searches for the EndHeightMessage with the given height
// and returns an auto.GroupReader, whenever it was found or not and an error.
// Group reader will be nil if found equals false.
//
// CONTRACT: caller must close group reader.
func (wal *baseWAL) SearchForEndHeight(height int64, options *WALSearchOptions) (gr *auto.GroupReader, found bool, err error) {
var msg *TimedWALMessage
lastHeightFound := int64(-1)
// NOTE: starting from the last file in the group because we're usually
// searching for the last height. See replay.go
@@ -166,17 +167,25 @@ func (wal *baseWAL) SearchForEndHeight(height int64, options *WALSearchOptions)
for {
msg, err = dec.Decode()
if err == io.EOF {
// OPTIMISATION: no need to look for height in older files if we've seen h < height
if lastHeightFound > 0 && lastHeightFound < height {
gr.Close()
return nil, false, nil
}
// check next file
break
}
if options.IgnoreDataCorruptionErrors && IsDataCorruptionError(err) {
wal.Logger.Debug("Corrupted entry. Skipping...", "err", err)
// do nothing
continue
} else if err != nil {
gr.Close()
return nil, false, err
}
if m, ok := msg.Msg.(EndHeightMessage); ok {
lastHeightFound = m.Height
if m.Height == height { // found
wal.Logger.Debug("Found", "height", height, "index", index)
return gr, true, nil
@@ -193,7 +202,7 @@ func (wal *baseWAL) SearchForEndHeight(height int64, options *WALSearchOptions)
// A WALEncoder writes custom-encoded WAL messages to an output stream.
//
// Format: 4 bytes CRC sum + 4 bytes length + arbitrary-length value (go-wire encoded)
// Format: 4 bytes CRC sum + 4 bytes length + arbitrary-length value (go-amino encoded)
type WALEncoder struct {
wr io.Writer
}
@@ -205,7 +214,7 @@ func NewWALEncoder(wr io.Writer) *WALEncoder {
// Encode writes the custom encoding of v to the stream.
func (enc *WALEncoder) Encode(v *TimedWALMessage) error {
data := wire.BinaryBytes(v)
data := cdc.MustMarshalBinaryBare(v)
crc := crc32.Checksum(data, crc32c)
length := uint32(len(data))
@@ -271,23 +280,17 @@ func (dec *WALDecoder) Decode() (*TimedWALMessage, error) {
b = make([]byte, 4)
_, err = dec.rd.Read(b)
if err == io.EOF {
return nil, err
}
if err != nil {
return nil, fmt.Errorf("failed to read length: %v", err)
}
length := binary.BigEndian.Uint32(b)
if length > maxMsgSizeBytes {
return nil, DataCorruptionError{fmt.Errorf("length %d exceeded maximum possible value of %d bytes", length, maxMsgSizeBytes)}
return nil, fmt.Errorf("length %d exceeded maximum possible value of %d bytes", length, maxMsgSizeBytes)
}
data := make([]byte, length)
_, err = dec.rd.Read(data)
if err == io.EOF {
return nil, err
}
if err != nil {
return nil, fmt.Errorf("failed to read data: %v", err)
}
@@ -298,9 +301,8 @@ func (dec *WALDecoder) Decode() (*TimedWALMessage, error) {
return nil, DataCorruptionError{fmt.Errorf("checksums do not match: (read: %v, actual: %v)", crc, actualCRC)}
}
var nn int
var res *TimedWALMessage // nolint: gosimple
res = wire.ReadBinary(&TimedWALMessage{}, bytes.NewBuffer(data), int(length), &nn, &err).(*TimedWALMessage)
var res = new(TimedWALMessage) // nolint: gosimple
err = cdc.UnmarshalBinaryBare(data, res)
if err != nil {
return nil, DataCorruptionError{fmt.Errorf("failed to decode data: %v", err)}
}
@@ -310,8 +312,9 @@ func (dec *WALDecoder) Decode() (*TimedWALMessage, error) {
type nilWAL struct{}
func (nilWAL) Save(m WALMessage) {}
func (nilWAL) Group() *auto.Group { return nil }
func (nilWAL) Write(m WALMessage) {}
func (nilWAL) WriteSync(m WALMessage) {}
func (nilWAL) Group() *auto.Group { return nil }
func (nilWAL) SearchForEndHeight(height int64, options *WALSearchOptions) (gr *auto.GroupReader, found bool, err error) {
return nil, false, nil
}

View File

@@ -4,7 +4,6 @@ import (
"bufio"
"bytes"
"fmt"
"math/rand"
"os"
"path/filepath"
"strings"
@@ -17,6 +16,7 @@ import (
"github.com/tendermint/tendermint/proxy"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
pvm "github.com/tendermint/tendermint/types/priv_validator"
auto "github.com/tendermint/tmlibs/autofile"
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/db"
@@ -40,7 +40,7 @@ func WALWithNBlocks(numBlocks int) (data []byte, err error) {
// COPY PASTE FROM node.go WITH A FEW MODIFICATIONS
// NOTE: we can't import node package because of circular dependency
privValidatorFile := config.PrivValidatorFile()
privValidator := types.LoadOrGenPrivValidatorFS(privValidatorFile)
privValidator := pvm.LoadOrGenFilePV(privValidatorFile)
genDoc, err := types.GenesisDocFromFile(config.GenesisFile())
if err != nil {
return nil, errors.Wrap(err, "failed to read genesis file")
@@ -83,7 +83,7 @@ func WALWithNBlocks(numBlocks int) (data []byte, err error) {
numBlocksWritten := make(chan struct{})
wal := newByteBufferWAL(logger, NewWALEncoder(wr), int64(numBlocks), numBlocksWritten)
// see wal.go#103
wal.Save(EndHeightMessage{0})
wal.Write(EndHeightMessage{0})
consensusState.wal = wal
if err := consensusState.Start(); err != nil {
@@ -116,7 +116,7 @@ func makePathname() string {
func randPort() int {
// returns between base and base + spread
base, spread := 20000, 20000
return base + rand.Intn(spread)
return base + cmn.RandIntn(spread)
}
func makeAddrs() (string, string, string) {
@@ -166,7 +166,7 @@ func newByteBufferWAL(logger log.Logger, enc *WALEncoder, nBlocks int64, signalS
// Save writes message to the internal buffer except when heightToStop is
// reached, in which case it will signal the caller via signalWhenStopsTo and
// skip writing.
func (w *byteBufferWAL) Save(m WALMessage) {
func (w *byteBufferWAL) Write(m WALMessage) {
if w.stopped {
w.logger.Debug("WAL already stopped. Not writing message", "msg", m)
return
@@ -189,6 +189,10 @@ func (w *byteBufferWAL) Save(m WALMessage) {
}
}
func (w *byteBufferWAL) WriteSync(m WALMessage) {
w.Write(m)
}
func (w *byteBufferWAL) Group() *auto.Group {
panic("not implemented")
}

View File

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

14
consensus/wire.go Normal file
View File

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

68
docker-compose.yml Normal file
View File

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

View File

@@ -183,6 +183,7 @@ Try running these commands:
> commit
-> code: OK
-> data.hex: 0x0000000000000000
> deliver_tx "abc"
-> code: OK
@@ -194,7 +195,7 @@ Try running these commands:
> commit
-> code: OK
-> data.hex: 0x49DFD15CCDACDEAE9728CB01FBB5E8688CA58B91
-> data.hex: 0x0200000000000000
> query "abc"
-> code: OK
@@ -208,7 +209,7 @@ Try running these commands:
> commit
-> code: OK
-> data.hex: 0x70102DB32280373FBF3F9F89DA2A20CE2CD62B0B
-> data.hex: 0x0400000000000000
> query "def"
-> code: OK
@@ -301,6 +302,7 @@ In another window, start the ``abci-cli console``:
> set_option serial on
-> code: OK
-> log: OK (SetOption doesn't return anything.)
> check_tx 0x00
-> code: OK

View File

@@ -1,125 +1,42 @@
Application Architecture Guide
==============================
Overview
--------
Here we provide a brief guide on the recommended architecture of a Tendermint blockchain
application.
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.
The following diagram provides a superb example:
Security
--------
https://drive.google.com/open?id=1yR2XpRi9YCY9H9uMfcw8-RMJpvDyvjz9
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.
The end-user application here is the Cosmos Voyager, at the bottom left.
Voyager communicates with a REST API exposed by a local Light-Client Daemon.
The Light-Client Daemon is an application specific program that communicates with
Tendermint nodes and verifies Tendermint light-client proofs through the Tendermint Core RPC.
The Tendermint Core process communicates with a local ABCI application, where the
user query or transaction is actually processed.
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?)
The ABCI application must be a deterministic result of the Tendermint consensus - any external influence
on the application state that didn't come through Tendermint could cause a
consensus failure. Thus *nothing* should communicate with the application except Tendermint via ABCI.
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.
If the application is written in Go, it can be compiled into the Tendermint binary.
Otherwise, it should use a unix socket to communicate with Tendermint.
If it's necessary to use TCP, extra care must be taken to encrypt and authenticate the connection.
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>`__.
All reads from the app happen through the Tendermint `/abci_query` endpoint.
All writes to the app happen through the Tendermint `/broadcast_tx_*` endpoints.
Connecting your client to the blockchain
----------------------------------------
The Light-Client Daemon is what provides light clients (end users) with nearly all the security of a full node.
It formats and broadcasts transactions, and verifies proofs of queries and transaction results.
Note that it need not be a daemon - the Light-Client logic could instead be implemented in the same process as the end-user application.
Tendermint Core RPC
~~~~~~~~~~~~~~~~~~~
Note for those ABCI applications with weaker security requirements, the functionality of the Light-Client Daemon can be moved
into the ABCI application process itself. That said, exposing the application process to anything besides Tendermint over ABCI
requires extreme caution, as all transactions, and possibly all queries, should still pass through Tendermint.
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 <./specification/rpc.html>`__. 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:
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).
See the following for more extensive documentation:
- [Interchain Standard for the Light-Client REST API](https://github.com/cosmos/cosmos-sdk/pull/1028)
- [Tendermint RPC Docs](https://tendermint.github.io/slate/)
- [Tendermint in Production](https://github.com/tendermint/tendermint/pull/1618)
- [Tendermint Basics](https://tendermint.readthedocs.io/en/master/using-tendermint.html)
- [ABCI spec](https://github.com/tendermint/abci/blob/master/specification.rst)

View File

@@ -178,21 +178,22 @@ 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.
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.
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 (this behaviour can be turned off with
``[mempool] recheck = false``).
.. container:: toggle
@@ -226,6 +227,23 @@ mempool state.
}
}
Replay Protection
^^^^^^^^^^^^^^^^^
To prevent old transactions from being replayed, CheckTx must
implement replay protection.
Tendermint provides the first defence layer by keeping a lightweight
in-memory cache of 100k (``[mempool] cache_size``) last transactions in
the mempool. If Tendermint is just started or the clients sent more
than 100k transactions, old transactions may be sent to the
application. So it is important CheckTx implements some logic to
handle them.
There are cases where a transaction will (or may) become valid in some
future state, in which case you probably want to disable Tendermint's
cache. You can do that by setting ``[mempool] cache_size = 0`` in the
config.
Consensus Connection
~~~~~~~~~~~~~~~~~~~~

View File

@@ -1,128 +1,29 @@
# ADR 008: PrivValidator
# ADR 008: SocketPV
## Context
Tendermint node's should support only two in-process PrivValidator
implementations:
The current PrivValidator is monolithic and isn't easily reuseable by alternative signers.
- FilePV uses an unencrypted private key in a "priv_validator.json" file - no
configuration required (just `tendermint init`).
- SocketPV uses a socket to send signing requests to another process - user is
responsible for starting that process themselves.
For instance, see https://github.com/tendermint/tendermint/issues/673
The SocketPV address can be provided via flags at the command line - doing so
will cause Tendermint to ignore any "priv_validator.json" file and to listen on
the given address for incoming connections from an external priv_validator
process. It will halt any operation until at least one external process
succesfully connected.
The goal is to have a clean PrivValidator interface like:
The external priv_validator process will dial the address to connect to
Tendermint, and then Tendermint will send requests on the ensuing connection to
sign votes and proposals. Thus the external process initiates the connection,
but the Tendermint process makes all requests. In a later stage we're going to
support multiple validators for fault tolerance. To prevent double signing they
need to be synced, which is deferred to an external solution (see #1185).
```
type PrivValidator interface {
Address() data.Bytes
PubKey() crypto.PubKey
SignVote(chainID string, vote *types.Vote) error
SignProposal(chainID string, proposal *types.Proposal) error
SignHeartbeat(chainID string, heartbeat *types.Heartbeat) error
}
```
It should also be easy to re-use the LastSignedInfo logic to avoid double signing.
## Decision
Tendermint node's should support only two in-process PrivValidator implementations:
- PrivValidatorUnencrypted uses an unencrypted private key in a "priv_validator.json" file - no configuration required (just `tendermint init`).
- PrivValidatorSocket uses a socket to send signing requests to another process - user is responsible for starting that process themselves.
The PrivValidatorSocket address can be provided via flags at the command line -
doing so will cause Tendermint to ignore any "priv_validator.json" file and to listen
on the given address for incoming connections from an external priv_validator process.
It will halt any operation until at least one external process succesfully
connected.
The external priv_validator process will dial the address to connect to Tendermint,
and then Tendermint will send requests on the ensuing connection to sign votes and proposals.
Thus the external process initiates the connection, but the Tendermint process makes all requests.
In a later stage we're going to support multiple validators for fault
tolerance. To prevent double signing they need to be synced, which is deferred
to an external solution (see #1185).
In addition, Tendermint will provide implementations that can be run in that external process.
These include:
- PrivValidatorEncrypted uses an encrypted private key persisted to disk - user must enter password to decrypt key when process is started.
- PrivValidatorLedger uses a Ledger Nano S to handle all signing.
What follows are descriptions of useful types
### Signer
```
type Signer interface {
Sign(msg []byte) (crypto.Signature, error)
}
```
Signer signs a message. It can also return an error.
### ValidatorID
ValidatorID is just the Address and PubKey
```
type ValidatorID struct {
Address data.Bytes `json:"address"`
PubKey crypto.PubKey `json:"pub_key"`
}
```
### LastSignedInfo
LastSignedInfo tracks the last thing we signed:
```
type LastSignedInfo struct {
Height int64 `json:"height"`
Round int `json:"round"`
Step int8 `json:"step"`
Signature crypto.Signature `json:"signature,omitempty"` // so we dont lose signatures
SignBytes data.Bytes `json:"signbytes,omitempty"` // so we dont lose signatures
}
```
It exposes methods for signing votes and proposals using a `Signer`.
This allows it to easily be reused by developers implemented their own PrivValidator.
### PrivValidatorUnencrypted
```
type PrivValidatorUnencrypted struct {
ID types.ValidatorID `json:"id"`
PrivKey PrivKey `json:"priv_key"`
LastSignedInfo *LastSignedInfo `json:"last_signed_info"`
}
```
Has the same structure as currently, but broken up into sub structs.
Note the LastSignedInfo is mutated in place every time we sign.
### PrivValidatorJSON
The "priv_validator.json" file supports only the PrivValidatorUnencrypted type.
It unmarshals into PrivValidatorJSON, which is used as the default PrivValidator type.
It wraps the PrivValidatorUnencrypted and persists it to disk after every signature.
## Status
Accepted.
## Consequences
### Positive
- Cleaner separation of components enabling re-use.
### Negative
- More files - led to creation of new directory.
### Neutral
In addition, Tendermint will provide implementations that can be run in that
external process. These include:
- FilePV will encrypt the private key, and the user must enter password to
decrypt key when process is started.
- LedgerPV uses a Ledger Nano S to handle all signing.

BIN
docs/assets/a_plus_t.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -71,7 +71,7 @@ language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'architecture', 'specification/new-spec', 'examples']
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'architecture', 'spec', 'examples']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
@@ -184,20 +184,10 @@ if os.path.isdir(tools_dir) != True:
if os.path.isdir(assets_dir) != True:
os.mkdir(assets_dir)
urllib.urlretrieve(tools_repo+tools_branch+'/ansible/README.rst', filename=tools_dir+'/ansible.rst')
urllib.urlretrieve(tools_repo+tools_branch+'/ansible/assets/a_plus_t.png', filename=assets_dir+'/a_plus_t.png')
urllib.urlretrieve(tools_repo+tools_branch+'/docker/README.rst', filename=tools_dir+'/docker.rst')
urllib.urlretrieve(tools_repo+tools_branch+'/mintnet-kubernetes/README.rst', filename=tools_dir+'/mintnet-kubernetes.rst')
urllib.urlretrieve(tools_repo+tools_branch+'/mintnet-kubernetes/assets/gce1.png', filename=assets_dir+'/gce1.png')
urllib.urlretrieve(tools_repo+tools_branch+'/mintnet-kubernetes/assets/gce2.png', filename=assets_dir+'/gce2.png')
urllib.urlretrieve(tools_repo+tools_branch+'/mintnet-kubernetes/assets/statefulset.png', filename=assets_dir+'/statefulset.png')
urllib.urlretrieve(tools_repo+tools_branch+'/mintnet-kubernetes/assets/t_plus_k.png', filename=assets_dir+'/t_plus_k.png')
urllib.urlretrieve(tools_repo+tools_branch+'/terraform-digitalocean/README.rst', filename=tools_dir+'/terraform-digitalocean.rst')
urllib.urlretrieve(tools_repo+tools_branch+'/tm-bench/README.rst', filename=tools_dir+'/benchmarking.rst')
urllib.urlretrieve('https://raw.githubusercontent.com/tendermint/tools/master/tm-monitor/README.rst', filename='tools/monitoring.rst')
urllib.urlretrieve(tools_repo+tools_branch+'/tm-monitor/README.rst', filename='tools/monitoring.rst')
#### abci spec #################################

View File

@@ -3,88 +3,62 @@ 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.
network to four validator nodes.
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/config``, or wherever the ``$TMHOME`` variable
might be set to.
validator, stored as ``priv_validator.json``, a node key, stored as
``node_key.json`` and a list of the public keys of all validators, stored as
``genesis.json``. These files should be stored in ``~/.tendermint/config``, or
wherever the ``$TMHOME`` 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``
3) Generate a private key and a node key for each validator using
``tendermint init``
4) Compile a list of public keys for each validator into a
``genesis.json`` file.
5) Run ``tendermint node --p2p.persistent_peers=< peer addresses >`` on each node,
``genesis.json`` file and replace the existing file with it.
5) Run ``tendermint node --proxy_app=kvstore --p2p.persistent_peers=< peer addresses >`` on each node,
where ``< peer 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.persistent_peers=192.168.0.1:46656,192.168.0.2:46656,192.168.0.3:46656,192.168.0.4:46656``.
::
tendermint node --proxy_app=kvstore --p2p.persistent_peers=96663a3dd0d7b9d17d4c8211b191af259621c693@192.168.0.1:46656, 429fcf25974313b95673f58d77eacdd434402665@192.168.0.2:46656, 0491d373a8e0fcf1023aaf18c51d6a1d0d4f31bd@192.168.0.3:46656, f9baeaa15fedf5e1ef7448dd60f46c01f1a9e9c4@192.168.0.4:46656
After a few seconds, all the nodes should connect to each other and start
making blocks! For more information, see the Tendermint Networks section
of `the guide to using Tendermint <using-tendermint.html>`__.
But wait! Steps 3 and 4 are quite manual. Instead, use `this script <https://github.com/tendermint/tendermint/blob/develop/docs/examples/init_testnet.sh>`__, which does the heavy lifting for you. And it gets better.
Instead of the previously linked script to initialize the files required for a testnet, we have the ``tendermint testnet`` command. By default, running ``tendermint testnet`` will create all the required files, just like the script. Of course, you'll still need to manually edit some fields in the ``config.toml``. Alternatively, see the available flags to auto-populate the ``config.toml`` with the fields that would otherwise be passed in via flags when running ``tendermint node``. As you might imagine, this command is useful for manual or automated deployments.
Automated Deployments
---------------------
While the manual deployment is easy enough, an automated deployment is
usually quicker. The below examples show different tools that can be used
for automated deployments.
The easiest and fastest way to get a testnet up in less than 5 minutes.
Automated Deployment using Kubernetes
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Local
^^^^^
The `mintnet-kubernetes tool <https://github.com/tendermint/tools/tree/master/mintnet-kubernetes>`__
allows automating the deployment of a Tendermint network on an already
provisioned Kubernetes cluster. For simple provisioning of a Kubernetes
cluster, check out the `Google Cloud Platform <https://cloud.google.com/>`__.
Automated Deployment using Terraform and Ansible
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The `terraform-digitalocean tool <https://github.com/tendermint/tools/tree/master/terraform-digitalocean>`__
allows creating a set of servers on the DigitalOcean cloud.
The `ansible playbooks <https://github.com/tendermint/tools/tree/master/ansible>`__
allow creating and managing a ``basecoin`` or ``ethermint`` testnet on provisioned servers.
Package Deployment on Linux for developers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The ``tendermint`` and ``basecoin`` applications can be installed from RPM or DEB packages on
Linux machines for development purposes. The packages are configured to be validators on the
one-node network that the machine represents. The services are not started after installation,
this way giving an opportunity to reconfigure the applications before starting.
The Ansible playbooks in the previous section use this repository to install ``basecoin``.
After installation, additional steps are executed to make sure that the multi-node testnet has
the right configuration before start.
Install from the CentOS/RedHat repository:
With ``docker`` and ``docker-compose`` installed, run the command:
::
rpm --import https://tendermint-packages.interblock.io/centos/7/os/x86_64/RPM-GPG-KEY-Tendermint
wget -O /etc/yum.repos.d/tendermint.repo https://tendermint-packages.interblock.io/centos/7/os/x86_64/tendermint.repo
yum install basecoin
make localnet-start
Install from the Debian/Ubuntu repository:
from the root of the tendermint repository. This will spin up a 4-node local testnet.
::
wget -O - https://tendermint-packages.interblock.io/centos/7/os/x86_64/RPM-GPG-KEY-Tendermint | apt-key add -
wget -O /etc/apt/sources.list.d/tendermint.list https://tendermint-packages.interblock.io/debian/tendermint.list
apt-get update && apt-get install basecoin
Cloud
^^^^^
See the `next section <./terraform-and-ansible.html>`__ for details.

View File

@@ -2,17 +2,18 @@
## Overview
This is a quick start guide. If you have a vague idea about how Tendermint works
and want to get started right away, continue. Otherwise, [review the documentation](http://tendermint.readthedocs.io/en/master/)
This is a quick start guide. If you have a vague idea about how Tendermint
works and want to get started right away, continue. Otherwise, [review the
documentation](http://tendermint.readthedocs.io/en/master/).
## Install
### Quick Install
On a fresh Ubuntu 16.04 machine can be done with [this script](https://git.io/vNLfY), like so:
On a fresh Ubuntu 16.04 machine can be done with [this script](https://git.io/vpgEI), like so:
```
curl -L https://git.io/vxWlX | bash
curl -L https://git.io/vpgEI | bash
source ~/.profile
```
@@ -23,7 +24,7 @@ The script is also used to facilitate cluster deployment below.
### Manual Install
Requires:
- `go` minimum version 1.9
- `go` minimum version 1.10
- `$GOPATH` environment variable must be set
- `$GOPATH/bin` must be on your `$PATH` (see https://github.com/tendermint/tendermint/wiki/Setting-GOPATH)
@@ -42,7 +43,7 @@ Confirm installation:
```
$ tendermint version
0.15.0-381fe19
0.18.0-XXXXXXX
```
## Initialization
@@ -117,12 +118,14 @@ where the value is returned in hex.
## Cluster of Nodes
First create four Ubuntu cloud machines. The following was tested on Digital Ocean Ubuntu 16.04 x64 (3GB/1CPU, 20GB SSD). We'll refer to their respective IP addresses below as IP1, IP2, IP3, IP4.
First create four Ubuntu cloud machines. The following was tested on Digital
Ocean Ubuntu 16.04 x64 (3GB/1CPU, 20GB SSD). We'll refer to their respective IP
addresses below as IP1, IP2, IP3, IP4.
Then, `ssh` into each machine, and execute [this script](https://git.io/vNLfY):
```
curl -L https://git.io/vNLfY | bash
curl -L https://git.io/vpgEI | bash
source ~/.profile
```
@@ -131,12 +134,16 @@ This will install `go` and other dependencies, get the Tendermint source code, t
Next, `cd` into `docs/examples`. Each command below should be run from each node, in sequence:
```
tendermint node --home ./node1 --proxy_app=kvstore --p2p.seeds IP1:46656,IP2:46656,IP3:46656,IP4:46656
tendermint node --home ./node2 --proxy_app=kvstore --p2p.seeds IP1:46656,IP2:46656,IP3:46656,IP4:46656
tendermint node --home ./node3 --proxy_app=kvstore --p2p.seeds IP1:46656,IP2:46656,IP3:46656,IP4:46656
tendermint node --home ./node4 --proxy_app=kvstore --p2p.seeds IP1:46656,IP2:46656,IP3:46656,IP4:46656
tendermint node --home ./node0 --proxy_app=kvstore --p2p.persistent_peers="167b80242c300bf0ccfb3ced3dec60dc2a81776e@IP1:46656,3c7a5920811550c04bf7a0b2f1e02ab52317b5e6@IP2:46656,303a1a4312c30525c99ba66522dd81cca56a361a@IP3:46656,b686c2a7f4b1b46dca96af3a0f31a6a7beae0be4@IP4:46656"
tendermint node --home ./node1 --proxy_app=kvstore --p2p.persistent_peers="167b80242c300bf0ccfb3ced3dec60dc2a81776e@IP1:46656,3c7a5920811550c04bf7a0b2f1e02ab52317b5e6@IP2:46656,303a1a4312c30525c99ba66522dd81cca56a361a@IP3:46656,b686c2a7f4b1b46dca96af3a0f31a6a7beae0be4@IP4:46656"
tendermint node --home ./node2 --proxy_app=kvstore --p2p.persistent_peers="167b80242c300bf0ccfb3ced3dec60dc2a81776e@IP1:46656,3c7a5920811550c04bf7a0b2f1e02ab52317b5e6@IP2:46656,303a1a4312c30525c99ba66522dd81cca56a361a@IP3:46656,b686c2a7f4b1b46dca96af3a0f31a6a7beae0be4@IP4:46656"
tendermint node --home ./node3 --proxy_app=kvstore --p2p.persistent_peers="167b80242c300bf0ccfb3ced3dec60dc2a81776e@IP1:46656,3c7a5920811550c04bf7a0b2f1e02ab52317b5e6@IP2:46656,303a1a4312c30525c99ba66522dd81cca56a361a@IP3:46656,b686c2a7f4b1b46dca96af3a0f31a6a7beae0be4@IP4:46656"
```
Note that after the third node is started, blocks will start to stream in because >2/3 of validators (defined in the `genesis.json`) have come online. Seeds can also be specified in the `config.toml`. See [this PR](https://github.com/tendermint/tendermint/pull/792) for more information about configuration options.
Note that after the third node is started, blocks will start to stream in
because >2/3 of validators (defined in the `genesis.json`) have come online.
Seeds can also be specified in the `config.toml`. See [this
PR](https://github.com/tendermint/tendermint/pull/792) for more information
about configuration options.
Transactions can then be sent as covered in the single, local node example above.

View File

@@ -0,0 +1,69 @@
#!/usr/bin/env bash
# make all the files
tendermint init --home ./tester/node0
tendermint init --home ./tester/node1
tendermint init --home ./tester/node2
tendermint init --home ./tester/node3
file0=./tester/node0/config/genesis.json
file1=./tester/node1/config/genesis.json
file2=./tester/node2/config/genesis.json
file3=./tester/node3/config/genesis.json
genesis_time=`cat $file0 | jq '.genesis_time'`
chain_id=`cat $file0 | jq '.chain_id'`
value0=`cat $file0 | jq '.validators[0].pub_key.value'`
value1=`cat $file1 | jq '.validators[0].pub_key.value'`
value2=`cat $file2 | jq '.validators[0].pub_key.value'`
value3=`cat $file3 | jq '.validators[0].pub_key.value'`
rm $file0
rm $file1
rm $file2
rm $file3
echo "{
\"genesis_time\": $genesis_time,
\"chain_id\": $chain_id,
\"validators\": [
{
\"pub_key\": {
\"type\": \"AC26791624DE60\",
\"value\": $value0
},
\"power:\": 10,
\"name\":, \"\"
},
{
\"pub_key\": {
\"type\": \"AC26791624DE60\",
\"value\": $value1
},
\"power:\": 10,
\"name\":, \"\"
},
{
\"pub_key\": {
\"type\": \"AC26791624DE60\",
\"value\": $value2
},
\"power:\": 10,
\"name\":, \"\"
},
{
\"pub_key\": {
\"type\": \"AC26791624DE60\",
\"value\": $value3
},
\"power:\": 10,
\"name\":, \"\"
}
],
\"app_hash\": \"\"
}" >> $file0
cp $file0 $file1
cp $file0 $file2
cp $file2 $file3

View File

@@ -26,7 +26,7 @@ go get $REPO
cd $GOPATH/src/$REPO
## build
git checkout v0.17.0
git checkout master
make get_tools
make get_vendor_deps
make install

View File

@@ -0,0 +1,169 @@
# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml
##### main base config options #####
# TCP or UNIX socket address of the ABCI application,
# or the name of an ABCI application compiled in with the Tendermint binary
proxy_app = "tcp://127.0.0.1:46658"
# A custom human readable name for this node
moniker = "alpha"
# If this node is many blocks behind the tip of the chain, FastSync
# allows them to catchup quickly by downloading blocks in parallel
# and verifying their commits
fast_sync = true
# Database backend: leveldb | memdb
db_backend = "leveldb"
# Database directory
db_path = "data"
# Output level for logging, including package level options
log_level = "main:info,state:info,*:error"
##### additional base config options #####
# Path to the JSON file containing the initial validator set and other meta data
genesis_file = "config/genesis.json"
# Path to the JSON file containing the private key to use as a validator in the consensus protocol
priv_validator_file = "config/priv_validator.json"
# Path to the JSON file containing the private key to use for node authentication in the p2p protocol
node_key_file = "config/node_key.json"
# Mechanism to connect to the ABCI application: socket | grpc
abci = "socket"
# TCP or UNIX socket address for the profiling server to listen on
prof_laddr = ""
# If true, query the ABCI app on connecting to a new peer
# so the app can decide if we should keep the connection or not
filter_peers = false
##### advanced configuration options #####
##### rpc server configuration options #####
[rpc]
# TCP or UNIX socket address for the RPC server to listen on
laddr = "tcp://0.0.0.0:46657"
# TCP or UNIX socket address for the gRPC server to listen on
# NOTE: This server only supports /broadcast_tx_commit
grpc_laddr = ""
# Activate unsafe RPC commands like /dial_seeds and /unsafe_flush_mempool
unsafe = false
##### peer to peer configuration options #####
[p2p]
# Address to listen for incoming connections
laddr = "tcp://0.0.0.0:46656"
# Comma separated list of seed nodes to connect to
seeds = ""
# Comma separated list of nodes to keep persistent connections to
# Do not add private peers to this list if you don't want them advertised
persistent_peers = ""
# Path to address book
addr_book_file = "config/addrbook.json"
# Set true for strict address routability rules
addr_book_strict = true
# Time to wait before flushing messages out on the connection, in ms
flush_throttle_timeout = 100
# Maximum number of peers to connect to
max_num_peers = 50
# Maximum size of a message packet payload, in bytes
max_packet_msg_payload_size = 1024
# Rate at which packets can be sent, in bytes/second
send_rate = 512000
# Rate at which packets can be received, in bytes/second
recv_rate = 512000
# Set true to enable the peer-exchange reactor
pex = true
# Seed mode, in which node constantly crawls the network and looks for
# peers. If another node asks it for addresses, it responds and disconnects.
#
# Does not work if the peer-exchange reactor is disabled.
seed_mode = false
# Authenticated encryption
auth_enc = true
# Comma separated list of peer IDs to keep private (will not be gossiped to other peers)
private_peer_ids = ""
##### mempool configuration options #####
[mempool]
recheck = true
recheck_empty = true
broadcast = true
wal_dir = "data/mempool.wal"
##### consensus configuration options #####
[consensus]
wal_file = "data/cs.wal/wal"
# All timeouts are in milliseconds
timeout_propose = 3000
timeout_propose_delta = 500
timeout_prevote = 1000
timeout_prevote_delta = 500
timeout_precommit = 1000
timeout_precommit_delta = 500
timeout_commit = 1000
# Make progress as soon as we have all the precommits (as if TimeoutCommit = 0)
skip_timeout_commit = false
# BlockSize
max_block_size_txs = 10000
max_block_size_bytes = 1
# EmptyBlocks mode and possible interval between empty blocks in seconds
create_empty_blocks = true
create_empty_blocks_interval = 0
# Reactor sleep duration parameters are in milliseconds
peer_gossip_sleep_duration = 100
peer_query_maj23_sleep_duration = 2000
##### transactions indexer configuration options #####
[tx_index]
# What indexer to use for transactions
#
# Options:
# 1) "null" (default)
# 2) "kv" - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend).
indexer = "kv"
# Comma-separated list of tags to index (by default the only tag is tx hash)
#
# It's recommended to index only a subset of tags due to possible memory
# bloat. This is, of course, depends on the indexer's DB and the volume of
# transactions.
index_tags = ""
# When set to true, tells indexer to index all tags. Note this may be not
# desirable (see the comment above). IndexTags has a precedence over
# IndexAllTags (i.e. when given both, IndexTags will be indexed).
index_all_tags = false

View File

@@ -0,0 +1,39 @@
{
"genesis_time": "0001-01-01T00:00:00Z",
"chain_id": "test-chain-A2i3OZ",
"validators": [
{
"pub_key": {
"type": "AC26791624DE60",
"value": "D+k4AdjnYPWbB9wmad137Bdpo/kAulOoTRQrLy/Qc4k="
},
"power": 10,
"name": ""
},
{
"pub_key": {
"type": "AC26791624DE60",
"value": "b56N5GCR1adcVRuENjfKw/mrm2dkhT7wNZXV/SDsKsU="
},
"power": 10,
"name": ""
},
{
"pub_key": {
"type": "AC26791624DE60",
"value": "IgZDpJvGA0TAamicA8ircy+RX/BkUlj6DXwM791ywIU="
},
"power": 10,
"name": ""
},
{
"pub_key": {
"type": "AC26791624DE60",
"value": "KGAZfxZvIZ7abbeIQ85U1ECG6+I62KSdaH8ulc0+OiU="
},
"power": 10,
"name": ""
}
],
"app_hash": ""
}

View File

@@ -0,0 +1 @@
{"priv_key":{"type":"954568A3288910","value":"7lY+k6EDllG8Q9gVbF5313t/ag2YGkBVKdVa0YHJ9xO5k0w3Q/hke0Z7UFT1KgVDGRUEKzwAwwjwFQUvgF0ZWg=="}}

View File

@@ -0,0 +1,14 @@
{
"address": "122A9414774A2FCAD026201DA477EF3F41970EF0",
"pub_key": {
"type": "AC26791624DE60",
"value": "D+k4AdjnYPWbB9wmad137Bdpo/kAulOoTRQrLy/Qc4k="
},
"last_height": 0,
"last_round": 0,
"last_step": 0,
"priv_key": {
"type": "954568A3288910",
"value": "YLxp3ho+kySgAnzjBptbxDzSGw2ntGZLsIHQsaVxY/cP6TgB2Odg9ZsH3CZp3XfsF2mj+QC6U6hNFCsvL9BziQ=="
}
}

View File

@@ -1,15 +0,0 @@
# 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 = "penguin"
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 = ""

View File

@@ -0,0 +1,169 @@
# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml
##### main base config options #####
# TCP or UNIX socket address of the ABCI application,
# or the name of an ABCI application compiled in with the Tendermint binary
proxy_app = "tcp://127.0.0.1:46658"
# A custom human readable name for this node
moniker = "bravo"
# If this node is many blocks behind the tip of the chain, FastSync
# allows them to catchup quickly by downloading blocks in parallel
# and verifying their commits
fast_sync = true
# Database backend: leveldb | memdb
db_backend = "leveldb"
# Database directory
db_path = "data"
# Output level for logging, including package level options
log_level = "main:info,state:info,*:error"
##### additional base config options #####
# Path to the JSON file containing the initial validator set and other meta data
genesis_file = "config/genesis.json"
# Path to the JSON file containing the private key to use as a validator in the consensus protocol
priv_validator_file = "config/priv_validator.json"
# Path to the JSON file containing the private key to use for node authentication in the p2p protocol
node_key_file = "config/node_key.json"
# Mechanism to connect to the ABCI application: socket | grpc
abci = "socket"
# TCP or UNIX socket address for the profiling server to listen on
prof_laddr = ""
# If true, query the ABCI app on connecting to a new peer
# so the app can decide if we should keep the connection or not
filter_peers = false
##### advanced configuration options #####
##### rpc server configuration options #####
[rpc]
# TCP or UNIX socket address for the RPC server to listen on
laddr = "tcp://0.0.0.0:46657"
# TCP or UNIX socket address for the gRPC server to listen on
# NOTE: This server only supports /broadcast_tx_commit
grpc_laddr = ""
# Activate unsafe RPC commands like /dial_seeds and /unsafe_flush_mempool
unsafe = false
##### peer to peer configuration options #####
[p2p]
# Address to listen for incoming connections
laddr = "tcp://0.0.0.0:46656"
# Comma separated list of seed nodes to connect to
seeds = ""
# Comma separated list of nodes to keep persistent connections to
# Do not add private peers to this list if you don't want them advertised
persistent_peers = ""
# Path to address book
addr_book_file = "config/addrbook.json"
# Set true for strict address routability rules
addr_book_strict = true
# Time to wait before flushing messages out on the connection, in ms
flush_throttle_timeout = 100
# Maximum number of peers to connect to
max_num_peers = 50
# Maximum size of a message packet payload, in bytes
max_packet_msg_payload_size = 1024
# Rate at which packets can be sent, in bytes/second
send_rate = 512000
# Rate at which packets can be received, in bytes/second
recv_rate = 512000
# Set true to enable the peer-exchange reactor
pex = true
# Seed mode, in which node constantly crawls the network and looks for
# peers. If another node asks it for addresses, it responds and disconnects.
#
# Does not work if the peer-exchange reactor is disabled.
seed_mode = false
# Authenticated encryption
auth_enc = true
# Comma separated list of peer IDs to keep private (will not be gossiped to other peers)
private_peer_ids = ""
##### mempool configuration options #####
[mempool]
recheck = true
recheck_empty = true
broadcast = true
wal_dir = "data/mempool.wal"
##### consensus configuration options #####
[consensus]
wal_file = "data/cs.wal/wal"
# All timeouts are in milliseconds
timeout_propose = 3000
timeout_propose_delta = 500
timeout_prevote = 1000
timeout_prevote_delta = 500
timeout_precommit = 1000
timeout_precommit_delta = 500
timeout_commit = 1000
# Make progress as soon as we have all the precommits (as if TimeoutCommit = 0)
skip_timeout_commit = false
# BlockSize
max_block_size_txs = 10000
max_block_size_bytes = 1
# EmptyBlocks mode and possible interval between empty blocks in seconds
create_empty_blocks = true
create_empty_blocks_interval = 0
# Reactor sleep duration parameters are in milliseconds
peer_gossip_sleep_duration = 100
peer_query_maj23_sleep_duration = 2000
##### transactions indexer configuration options #####
[tx_index]
# What indexer to use for transactions
#
# Options:
# 1) "null" (default)
# 2) "kv" - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend).
indexer = "kv"
# Comma-separated list of tags to index (by default the only tag is tx hash)
#
# It's recommended to index only a subset of tags due to possible memory
# bloat. This is, of course, depends on the indexer's DB and the volume of
# transactions.
index_tags = ""
# When set to true, tells indexer to index all tags. Note this may be not
# desirable (see the comment above). IndexTags has a precedence over
# IndexAllTags (i.e. when given both, IndexTags will be indexed).
index_all_tags = false

View File

@@ -0,0 +1,39 @@
{
"genesis_time": "0001-01-01T00:00:00Z",
"chain_id": "test-chain-A2i3OZ",
"validators": [
{
"pub_key": {
"type": "AC26791624DE60",
"value": "D+k4AdjnYPWbB9wmad137Bdpo/kAulOoTRQrLy/Qc4k="
},
"power": 10,
"name": ""
},
{
"pub_key": {
"type": "AC26791624DE60",
"value": "b56N5GCR1adcVRuENjfKw/mrm2dkhT7wNZXV/SDsKsU="
},
"power": 10,
"name": ""
},
{
"pub_key": {
"type": "AC26791624DE60",
"value": "IgZDpJvGA0TAamicA8ircy+RX/BkUlj6DXwM791ywIU="
},
"power": 10,
"name": ""
},
{
"pub_key": {
"type": "AC26791624DE60",
"value": "KGAZfxZvIZ7abbeIQ85U1ECG6+I62KSdaH8ulc0+OiU="
},
"power": 10,
"name": ""
}
],
"app_hash": ""
}

View File

@@ -0,0 +1 @@
{"priv_key":{"type":"954568A3288910","value":"H71dc/TIG7nTselfa9nG0WRArXLKYnm7P5eFCk2lk8ASKQ3sIHpbdxCSHQD/RcdHe7TiabJeuOssNPvPWiyQEQ=="}}

View File

@@ -0,0 +1,14 @@
{
"address": "BEA1B57F5806CF9AC4D54C8CF806DED5C0F102E1",
"pub_key": {
"type": "AC26791624DE60",
"value": "b56N5GCR1adcVRuENjfKw/mrm2dkhT7wNZXV/SDsKsU="
},
"last_height": 0,
"last_round": 0,
"last_step": 0,
"priv_key": {
"type": "954568A3288910",
"value": "o0IqrHSPtd5YqGefodWxpJuRzvuVBjgbH785vbMgk7Vvno3kYJHVp1xVG4Q2N8rD+aubZ2SFPvA1ldX9IOwqxQ=="
}
}

View File

@@ -1,42 +0,0 @@
{
"genesis_time":"0001-01-01T00:00:00Z",
"chain_id":"test-chain-wt7apy",
"validators":[
{
"pub_key":{
"type":"ed25519",
"data":"F08446C80A33E10D620E21450821B58D053778528F2B583D423B3E46EC647D30"
},
"power":10,
"name":"node1"
}
,
{
"pub_key":{
"type":"ed25519",
"data": "A8423F70A9E512643B4B00F7C3701ECAD1F31B0A1FAA45852C41046353B9A07F"
},
"power":10,
"name":"node2"
}
,
{
"pub_key":{
"type":"ed25519",
"data": "E52EFFAEDFE1D618ECDA71DE3B23592B3612CAABA0C10826E4C3120B2198C29A"
},
"power":10,
"name":"node3"
}
,
{
"pub_key":{
"type":"ed25519",
"data": "2B8FC09C07955A02998DFE5AF1AAD1C44115ECA7635FF51A867CF4265D347C07"
},
"power":10,
"name":"node4"
}
],
"app_hash":""
}

View File

@@ -1,15 +0,0 @@
{
"address":"4DC2756029CE0D8F8C6C3E4C3CE6EE8C30AF352F",
"pub_key":{
"type":"ed25519",
"data":"F08446C80A33E10D620E21450821B58D053778528F2B583D423B3E46EC647D30"
},
"last_height":0,
"last_round":0,
"last_step":0,
"last_signature":null,
"priv_key":{
"type":"ed25519",
"data":"4D3648E1D93C8703E436BFF814728B6BD270CFDFD686DF5385E8ACBEB7BE2D7DF08446C80A33E10D620E21450821B58D053778528F2B583D423B3E46EC647D30"
}
}

View File

@@ -1,15 +0,0 @@
# 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 = "penguin"
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 = ""

View File

@@ -0,0 +1,169 @@
# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml
##### main base config options #####
# TCP or UNIX socket address of the ABCI application,
# or the name of an ABCI application compiled in with the Tendermint binary
proxy_app = "tcp://127.0.0.1:46658"
# A custom human readable name for this node
moniker = "charlie"
# If this node is many blocks behind the tip of the chain, FastSync
# allows them to catchup quickly by downloading blocks in parallel
# and verifying their commits
fast_sync = true
# Database backend: leveldb | memdb
db_backend = "leveldb"
# Database directory
db_path = "data"
# Output level for logging, including package level options
log_level = "main:info,state:info,*:error"
##### additional base config options #####
# Path to the JSON file containing the initial validator set and other meta data
genesis_file = "config/genesis.json"
# Path to the JSON file containing the private key to use as a validator in the consensus protocol
priv_validator_file = "config/priv_validator.json"
# Path to the JSON file containing the private key to use for node authentication in the p2p protocol
node_key_file = "config/node_key.json"
# Mechanism to connect to the ABCI application: socket | grpc
abci = "socket"
# TCP or UNIX socket address for the profiling server to listen on
prof_laddr = ""
# If true, query the ABCI app on connecting to a new peer
# so the app can decide if we should keep the connection or not
filter_peers = false
##### advanced configuration options #####
##### rpc server configuration options #####
[rpc]
# TCP or UNIX socket address for the RPC server to listen on
laddr = "tcp://0.0.0.0:46657"
# TCP or UNIX socket address for the gRPC server to listen on
# NOTE: This server only supports /broadcast_tx_commit
grpc_laddr = ""
# Activate unsafe RPC commands like /dial_seeds and /unsafe_flush_mempool
unsafe = false
##### peer to peer configuration options #####
[p2p]
# Address to listen for incoming connections
laddr = "tcp://0.0.0.0:46656"
# Comma separated list of seed nodes to connect to
seeds = ""
# Comma separated list of nodes to keep persistent connections to
# Do not add private peers to this list if you don't want them advertised
persistent_peers = ""
# Path to address book
addr_book_file = "config/addrbook.json"
# Set true for strict address routability rules
addr_book_strict = true
# Time to wait before flushing messages out on the connection, in ms
flush_throttle_timeout = 100
# Maximum number of peers to connect to
max_num_peers = 50
# Maximum size of a message packet payload, in bytes
max_packet_msg_payload_size = 1024
# Rate at which packets can be sent, in bytes/second
send_rate = 512000
# Rate at which packets can be received, in bytes/second
recv_rate = 512000
# Set true to enable the peer-exchange reactor
pex = true
# Seed mode, in which node constantly crawls the network and looks for
# peers. If another node asks it for addresses, it responds and disconnects.
#
# Does not work if the peer-exchange reactor is disabled.
seed_mode = false
# Authenticated encryption
auth_enc = true
# Comma separated list of peer IDs to keep private (will not be gossiped to other peers)
private_peer_ids = ""
##### mempool configuration options #####
[mempool]
recheck = true
recheck_empty = true
broadcast = true
wal_dir = "data/mempool.wal"
##### consensus configuration options #####
[consensus]
wal_file = "data/cs.wal/wal"
# All timeouts are in milliseconds
timeout_propose = 3000
timeout_propose_delta = 500
timeout_prevote = 1000
timeout_prevote_delta = 500
timeout_precommit = 1000
timeout_precommit_delta = 500
timeout_commit = 1000
# Make progress as soon as we have all the precommits (as if TimeoutCommit = 0)
skip_timeout_commit = false
# BlockSize
max_block_size_txs = 10000
max_block_size_bytes = 1
# EmptyBlocks mode and possible interval between empty blocks in seconds
create_empty_blocks = true
create_empty_blocks_interval = 0
# Reactor sleep duration parameters are in milliseconds
peer_gossip_sleep_duration = 100
peer_query_maj23_sleep_duration = 2000
##### transactions indexer configuration options #####
[tx_index]
# What indexer to use for transactions
#
# Options:
# 1) "null" (default)
# 2) "kv" - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend).
indexer = "kv"
# Comma-separated list of tags to index (by default the only tag is tx hash)
#
# It's recommended to index only a subset of tags due to possible memory
# bloat. This is, of course, depends on the indexer's DB and the volume of
# transactions.
index_tags = ""
# When set to true, tells indexer to index all tags. Note this may be not
# desirable (see the comment above). IndexTags has a precedence over
# IndexAllTags (i.e. when given both, IndexTags will be indexed).
index_all_tags = false

View File

@@ -0,0 +1,39 @@
{
"genesis_time": "0001-01-01T00:00:00Z",
"chain_id": "test-chain-A2i3OZ",
"validators": [
{
"pub_key": {
"type": "AC26791624DE60",
"value": "D+k4AdjnYPWbB9wmad137Bdpo/kAulOoTRQrLy/Qc4k="
},
"power": 10,
"name": ""
},
{
"pub_key": {
"type": "AC26791624DE60",
"value": "b56N5GCR1adcVRuENjfKw/mrm2dkhT7wNZXV/SDsKsU="
},
"power": 10,
"name": ""
},
{
"pub_key": {
"type": "AC26791624DE60",
"value": "IgZDpJvGA0TAamicA8ircy+RX/BkUlj6DXwM791ywIU="
},
"power": 10,
"name": ""
},
{
"pub_key": {
"type": "AC26791624DE60",
"value": "KGAZfxZvIZ7abbeIQ85U1ECG6+I62KSdaH8ulc0+OiU="
},
"power": 10,
"name": ""
}
],
"app_hash": ""
}

View File

@@ -0,0 +1 @@
{"priv_key":{"type":"954568A3288910","value":"COHZ/Y2cWGWxJNkRwtpQBt5sYvOnb6Gpz0lO46XERRJFBIdSWD5x1UMGRSTmnvW1ec5G4bMdg6zUZKOZD+vVPg=="}}

View File

@@ -0,0 +1,14 @@
{
"address": "F0AA266949FB29ADA0B679C27889ED930BD1BDA1",
"pub_key": {
"type": "AC26791624DE60",
"value": "IgZDpJvGA0TAamicA8ircy+RX/BkUlj6DXwM791ywIU="
},
"last_height": 0,
"last_round": 0,
"last_step": 0,
"priv_key": {
"type": "954568A3288910",
"value": "khADeZ5K/8u/L99DFaZNRq8V5g+EHWbwfqFjhCrppaAiBkOkm8YDRMBqaJwDyKtzL5Ff8GRSWPoNfAzv3XLAhQ=="
}
}

View File

@@ -1,42 +0,0 @@
{
"genesis_time":"0001-01-01T00:00:00Z",
"chain_id":"test-chain-wt7apy",
"validators":[
{
"pub_key":{
"type":"ed25519",
"data":"F08446C80A33E10D620E21450821B58D053778528F2B583D423B3E46EC647D30"
},
"power":10,
"name":"node1"
}
,
{
"pub_key":{
"type":"ed25519",
"data": "A8423F70A9E512643B4B00F7C3701ECAD1F31B0A1FAA45852C41046353B9A07F"
},
"power":10,
"name":"node2"
}
,
{
"pub_key":{
"type":"ed25519",
"data": "E52EFFAEDFE1D618ECDA71DE3B23592B3612CAABA0C10826E4C3120B2198C29A"
},
"power":10,
"name":"node3"
}
,
{
"pub_key":{
"type":"ed25519",
"data": "2B8FC09C07955A02998DFE5AF1AAD1C44115ECA7635FF51A867CF4265D347C07"
},
"power":10,
"name":"node4"
}
],
"app_hash":""
}

View File

@@ -1,15 +0,0 @@
{
"address": "DD6C63A762608A9DDD4A845657743777F63121D6",
"pub_key": {
"type": "ed25519",
"data": "A8423F70A9E512643B4B00F7C3701ECAD1F31B0A1FAA45852C41046353B9A07F"
},
"last_height": 0,
"last_round": 0,
"last_step": 0,
"last_signature": null,
"priv_key": {
"type": "ed25519",
"data": "7B0DE666FF5E9B437D284BCE767F612381890C018B93B0A105D2E829A568DA6FA8423F70A9E512643B4B00F7C3701ECAD1F31B0A1FAA45852C41046353B9A07F"
}
}

View File

@@ -1,15 +0,0 @@
# 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 = "penguin"
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 = ""

View File

@@ -0,0 +1,169 @@
# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml
##### main base config options #####
# TCP or UNIX socket address of the ABCI application,
# or the name of an ABCI application compiled in with the Tendermint binary
proxy_app = "tcp://127.0.0.1:46658"
# A custom human readable name for this node
moniker = "delta"
# If this node is many blocks behind the tip of the chain, FastSync
# allows them to catchup quickly by downloading blocks in parallel
# and verifying their commits
fast_sync = true
# Database backend: leveldb | memdb
db_backend = "leveldb"
# Database directory
db_path = "data"
# Output level for logging, including package level options
log_level = "main:info,state:info,*:error"
##### additional base config options #####
# Path to the JSON file containing the initial validator set and other meta data
genesis_file = "config/genesis.json"
# Path to the JSON file containing the private key to use as a validator in the consensus protocol
priv_validator_file = "config/priv_validator.json"
# Path to the JSON file containing the private key to use for node authentication in the p2p protocol
node_key_file = "config/node_key.json"
# Mechanism to connect to the ABCI application: socket | grpc
abci = "socket"
# TCP or UNIX socket address for the profiling server to listen on
prof_laddr = ""
# If true, query the ABCI app on connecting to a new peer
# so the app can decide if we should keep the connection or not
filter_peers = false
##### advanced configuration options #####
##### rpc server configuration options #####
[rpc]
# TCP or UNIX socket address for the RPC server to listen on
laddr = "tcp://0.0.0.0:46657"
# TCP or UNIX socket address for the gRPC server to listen on
# NOTE: This server only supports /broadcast_tx_commit
grpc_laddr = ""
# Activate unsafe RPC commands like /dial_seeds and /unsafe_flush_mempool
unsafe = false
##### peer to peer configuration options #####
[p2p]
# Address to listen for incoming connections
laddr = "tcp://0.0.0.0:46656"
# Comma separated list of seed nodes to connect to
seeds = ""
# Comma separated list of nodes to keep persistent connections to
# Do not add private peers to this list if you don't want them advertised
persistent_peers = ""
# Path to address book
addr_book_file = "config/addrbook.json"
# Set true for strict address routability rules
addr_book_strict = true
# Time to wait before flushing messages out on the connection, in ms
flush_throttle_timeout = 100
# Maximum number of peers to connect to
max_num_peers = 50
# Maximum size of a message packet payload, in bytes
max_packet_msg_payload_size = 1024
# Rate at which packets can be sent, in bytes/second
send_rate = 512000
# Rate at which packets can be received, in bytes/second
recv_rate = 512000
# Set true to enable the peer-exchange reactor
pex = true
# Seed mode, in which node constantly crawls the network and looks for
# peers. If another node asks it for addresses, it responds and disconnects.
#
# Does not work if the peer-exchange reactor is disabled.
seed_mode = false
# Authenticated encryption
auth_enc = true
# Comma separated list of peer IDs to keep private (will not be gossiped to other peers)
private_peer_ids = ""
##### mempool configuration options #####
[mempool]
recheck = true
recheck_empty = true
broadcast = true
wal_dir = "data/mempool.wal"
##### consensus configuration options #####
[consensus]
wal_file = "data/cs.wal/wal"
# All timeouts are in milliseconds
timeout_propose = 3000
timeout_propose_delta = 500
timeout_prevote = 1000
timeout_prevote_delta = 500
timeout_precommit = 1000
timeout_precommit_delta = 500
timeout_commit = 1000
# Make progress as soon as we have all the precommits (as if TimeoutCommit = 0)
skip_timeout_commit = false
# BlockSize
max_block_size_txs = 10000
max_block_size_bytes = 1
# EmptyBlocks mode and possible interval between empty blocks in seconds
create_empty_blocks = true
create_empty_blocks_interval = 0
# Reactor sleep duration parameters are in milliseconds
peer_gossip_sleep_duration = 100
peer_query_maj23_sleep_duration = 2000
##### transactions indexer configuration options #####
[tx_index]
# What indexer to use for transactions
#
# Options:
# 1) "null" (default)
# 2) "kv" - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend).
indexer = "kv"
# Comma-separated list of tags to index (by default the only tag is tx hash)
#
# It's recommended to index only a subset of tags due to possible memory
# bloat. This is, of course, depends on the indexer's DB and the volume of
# transactions.
index_tags = ""
# When set to true, tells indexer to index all tags. Note this may be not
# desirable (see the comment above). IndexTags has a precedence over
# IndexAllTags (i.e. when given both, IndexTags will be indexed).
index_all_tags = false

View File

@@ -0,0 +1,39 @@
{
"genesis_time": "0001-01-01T00:00:00Z",
"chain_id": "test-chain-A2i3OZ",
"validators": [
{
"pub_key": {
"type": "AC26791624DE60",
"value": "D+k4AdjnYPWbB9wmad137Bdpo/kAulOoTRQrLy/Qc4k="
},
"power": 10,
"name": ""
},
{
"pub_key": {
"type": "AC26791624DE60",
"value": "b56N5GCR1adcVRuENjfKw/mrm2dkhT7wNZXV/SDsKsU="
},
"power": 10,
"name": ""
},
{
"pub_key": {
"type": "AC26791624DE60",
"value": "IgZDpJvGA0TAamicA8ircy+RX/BkUlj6DXwM791ywIU="
},
"power": 10,
"name": ""
},
{
"pub_key": {
"type": "AC26791624DE60",
"value": "KGAZfxZvIZ7abbeIQ85U1ECG6+I62KSdaH8ulc0+OiU="
},
"power": 10,
"name": ""
}
],
"app_hash": ""
}

View File

@@ -0,0 +1 @@
{"priv_key":{"type":"954568A3288910","value":"9Y9xp/tUJJ6pHTF5SUV0bGKYSdVbFtMHu+Lr8S0JBSZAwneaejnfOEU1LMKOnQ07skrDUaJcj5di3jAyjxJzqg=="}}

View File

@@ -0,0 +1,14 @@
{
"address": "9A1A6914EB5F4FF0269C7EEEE627C27310CC64F9",
"pub_key": {
"type": "AC26791624DE60",
"value": "KGAZfxZvIZ7abbeIQ85U1ECG6+I62KSdaH8ulc0+OiU="
},
"last_height": 0,
"last_round": 0,
"last_step": 0,
"priv_key": {
"type": "954568A3288910",
"value": "jb52LZ5gp+eQ8nJlFK1z06nBMp1gD8ICmyzdM1icGOgoYBl/Fm8hntptt4hDzlTUQIbr4jrYpJ1ofy6VzT46JQ=="
}
}

View File

@@ -1,42 +0,0 @@
{
"genesis_time":"0001-01-01T00:00:00Z",
"chain_id":"test-chain-wt7apy",
"validators":[
{
"pub_key":{
"type":"ed25519",
"data":"F08446C80A33E10D620E21450821B58D053778528F2B583D423B3E46EC647D30"
},
"power":10,
"name":"node1"
}
,
{
"pub_key":{
"type":"ed25519",
"data": "A8423F70A9E512643B4B00F7C3701ECAD1F31B0A1FAA45852C41046353B9A07F"
},
"power":10,
"name":"node2"
}
,
{
"pub_key":{
"type":"ed25519",
"data": "E52EFFAEDFE1D618ECDA71DE3B23592B3612CAABA0C10826E4C3120B2198C29A"
},
"power":10,
"name":"node3"
}
,
{
"pub_key":{
"type":"ed25519",
"data": "2B8FC09C07955A02998DFE5AF1AAD1C44115ECA7635FF51A867CF4265D347C07"
},
"power":10,
"name":"node4"
}
],
"app_hash":""
}

View File

@@ -1,15 +0,0 @@
{
"address": "6D6A1E313B407B5474106CA8759C976B777AB659",
"pub_key": {
"type": "ed25519",
"data": "E52EFFAEDFE1D618ECDA71DE3B23592B3612CAABA0C10826E4C3120B2198C29A"
},
"last_height": 0,
"last_round": 0,
"last_step": 0,
"last_signature": null,
"priv_key": {
"type": "ed25519",
"data": "622432A370111A5C25CFE121E163FE709C9D5C95F551EDBD7A2C69A8545C9B76E52EFFAEDFE1D618ECDA71DE3B23592B3612CAABA0C10826E4C3120B2198C29A"
}
}

View File

@@ -1,15 +0,0 @@
# 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 = "penguin"
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 = ""

View File

@@ -1,42 +0,0 @@
{
"genesis_time":"0001-01-01T00:00:00Z",
"chain_id":"test-chain-wt7apy",
"validators":[
{
"pub_key":{
"type":"ed25519",
"data":"F08446C80A33E10D620E21450821B58D053778528F2B583D423B3E46EC647D30"
},
"power":10,
"name":"node1"
}
,
{
"pub_key":{
"type":"ed25519",
"data": "A8423F70A9E512643B4B00F7C3701ECAD1F31B0A1FAA45852C41046353B9A07F"
},
"power":10,
"name":"node2"
}
,
{
"pub_key":{
"type":"ed25519",
"data": "E52EFFAEDFE1D618ECDA71DE3B23592B3612CAABA0C10826E4C3120B2198C29A"
},
"power":10,
"name":"node3"
}
,
{
"pub_key":{
"type":"ed25519",
"data": "2B8FC09C07955A02998DFE5AF1AAD1C44115ECA7635FF51A867CF4265D347C07"
},
"power":10,
"name":"node4"
}
],
"app_hash":""
}

View File

@@ -1,15 +0,0 @@
{
"address": "829A9663611D3DD88A3D84EA0249679D650A0755",
"pub_key": {
"type": "ed25519",
"data": "2B8FC09C07955A02998DFE5AF1AAD1C44115ECA7635FF51A867CF4265D347C07"
},
"last_height": 0,
"last_round": 0,
"last_step": 0,
"last_signature": null,
"priv_key": {
"type": "ed25519",
"data": "0A604D1C9AE94A50150BF39E603239092F9392E4773F4D8F4AC1D86E6438E89E2B8FC09C07955A02998DFE5AF1AAD1C44115ECA7635FF51A867CF4265D347C07"
}
}

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