Compare commits

...

399 Commits

Author SHA1 Message Date
Ethan Buchman
46369a1ab7 Merge pull request #1779 from tendermint/release/v0.21.0
Release/v0.21.0
2018-06-21 12:44:31 -07:00
Ethan Buchman
1e3951c61c update changelog 2018-06-21 12:47:29 -07:00
Ethan Buchman
d220a1ef13 changelog 2018-06-20 00:22:07 -07:00
Ethan Buchman
c6c468c341 update changelog 2018-06-20 00:13:23 -07:00
Ethan Buchman
1d86270e20 version 2018-06-20 00:09:55 -07:00
Ethan Buchman
43745c83db Merge branch 'release/v0.20.1' into develop 2018-06-20 00:08:51 -07:00
Ethan Buchman
c793a72ac5 Merge pull request #1769 from tendermint/1755-possible-memory-leak
Memory leak in Websocket
2018-06-19 23:57:29 -07:00
Anton Kaliaev
cfff83fa3d update changelog 2018-06-19 20:20:30 +04:00
Anton Kaliaev
4fc06e9d2a [libs/pubsub] fix memory leak
Refs #1755

I started with writing a test for wsConnection (WebsocketManager) where
I:

- create a WS connection
- do a simple echo call
- close it

No leaking goroutines, nor any leaking memory were detected.

For useful shortcuts see my blog post
https://blog.cosmos.network/debugging-the-memory-leak-in-tendermint-210186711420

Then I went to the rpc tests to see if calling Subscribe results in
memory growth. It did.

I used a slightly modified version of TestHeaderEvents function:

```
func TestHeaderEvents(t *testing.T) {
	// memory heap before
	f, err := os.Create("/tmp/mem1.mprof")
	if err != nil {
		t.Fatal(err)
	}
	pprof.WriteHeapProfile(f)
	f.Close()

	for i := 0; i < 100; i++ {
		c := getHTTPClient()
		err = c.Start()
		require.Nil(t, err)

		evtTyp := types.EventNewBlockHeader
		evt, err := client.WaitForOneEvent(c, evtTyp, waitForEventTimeout)
		require.Nil(t, err)
		_, ok := evt.(types.EventDataNewBlockHeader)
		require.True(t, ok)

		c.Stop()
		c = nil
	}

	runtime.GC()

	// memory heap before
	f, err = os.Create("/tmp/mem2.mprof")
	if err != nil {
		t.Fatal(err)
	}
	pprof.WriteHeapProfile(f)
	f.Close()

	// dump all running goroutines
	time.Sleep(10 * time.Second)
	pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
}
```

```
Showing nodes accounting for 35159.16kB, 100% of 35159.16kB total
Showing top 10 nodes out of 48
      flat  flat%   sum%        cum   cum%
32022.23kB 91.08% 91.08% 32022.23kB 91.08%  github.com/tendermint/tendermint/libs/pubsub/query.(*QueryParser).Init
 1056.33kB  3.00% 94.08%  1056.33kB  3.00%  bufio.NewReaderSize
  528.17kB  1.50% 95.58%   528.17kB  1.50%  bufio.NewWriterSize
  528.17kB  1.50% 97.09%   528.17kB  1.50%  github.com/tendermint/tendermint/consensus.NewConsensusState
  512.19kB  1.46% 98.54%   512.19kB  1.46%  runtime.malg
  512.08kB  1.46%   100%   512.08kB  1.46%  syscall.ByteSliceFromString
         0     0%   100%   512.08kB  1.46%  github.com/tendermint/tendermint/consensus.(*ConsensusState).(github.com/tendermint/tendermint/consensus.defaultDecideProposal)-fm
         0     0%   100%   512.08kB  1.46%  github.com/tendermint/tendermint/consensus.(*ConsensusState).addVote
         0     0%   100%   512.08kB  1.46%  github.com/tendermint/tendermint/consensus.(*ConsensusState).defaultDecideProposal
         0     0%   100%   512.08kB  1.46%  github.com/tendermint/tendermint/consensus.(*ConsensusState).enterNewRound
```

100 subscriptions produce 32MB.

Again, no additional goroutines are running after the end of the test
(wsConnection readRoutine and writeRoutine both finishes). **It means
that some exiting goroutine or object is holding a reference to the
*Query objects, which are leaking.**

One of them is pubsub#loop. It's using state.queries to map queries to
clients and state.clients to map clients to queries.

Before this commit, we're not thoroughly cleaning state.queries, which
was the reason for memory leakage.
2018-06-19 19:59:21 +04:00
Anton Kaliaev
aaddf5d32f set pubsub default capacity to 0
Refs #951

Jae: I don't know a good way to catch these errors in general, but
forcing pubsub's internal channel to have a capacity of 0 will reveal
bugs sooner, if the subscriber also has a 0 or small capacity ch to pull
from.
2018-06-19 17:07:21 +04:00
Anton Kaliaev
26b2e808f7 [rpc/lib/server] wrote a basic test for WebsocketManager 2018-06-19 17:06:48 +04:00
Anton Kaliaev
3d30a42943 add config to issue template 2018-06-19 11:59:14 +04:00
Anton Kaliaev
4f5492c831 add nopTxCache (Nil Object Pattern)
to better handle zero cache size
2018-06-19 11:59:07 +04:00
Anton Kaliaev
70d973016e output msg only once during start 2018-06-19 11:40:40 +04:00
Ethan Buchman
4b2348f697 mempool: fix cache_size==0. closes #1761 2018-06-18 18:21:19 -07:00
Ethan Buchman
6a324764ac fix circle 2018-06-18 17:18:35 -07:00
Ethan Buchman
3470e5d7b3 changelog and version 2018-06-18 17:15:41 -07:00
Ethan Buchman
a519825bf8 consensus: fixes #1754
* updateToState exits early if the state isn't new, which happens after
* fast syncing. This results in not sending a NewRoundStep message. The mempool
* reactor depends on PeerState, which is updated by NewRoundStep
* messages. If the peer never sends a NewRoundStep, the mempool reactor
* will think they're behind, and never forward transactions. Note this
* only happens when `create_empty_blocks = false`, because otherwise
* peers will move through the consensus state and send a NewRoundStep
* for a new step soon anyways. Simple fix is just to send the
* NewRoundStep message during updateToState even if exit early
2018-06-18 17:08:09 -07:00
Ethan Buchman
d457887dd6 Merge pull request #1759 from tendermint/bucky/readme
update README
2018-06-16 22:56:35 -07:00
Ethan Buchman
adb6a94f18 contact us at riot 2018-06-16 23:05:24 -07:00
Ethan Buchman
b8bfc041d3 update README 2018-06-16 19:00:42 -07:00
Ethan Buchman
9bad770f21 Merge pull request #1757 from tendermint/bucky/update-spec
docs/spec: some organizational cleanup
2018-06-16 13:03:00 -07:00
Ethan Buchman
d3b53e62a5 fix circle 2018-06-15 23:46:43 -07:00
Ethan Buchman
506cf6c9c7 docs/spec: DuplicateVoteEvidence 2018-06-15 23:19:42 -07:00
Ethan Buchman
b8f340afd0 docs/spec: some organizational cleanup 2018-06-15 22:56:26 -07:00
Ethan Buchman
c84be3b8dd Merge pull request #1751 from tendermint/bucky/codeowner-alex
add xla as codeowner
2018-06-14 20:22:04 -07:00
Ethan Buchman
050636d5ce add xla as codeowner 2018-06-14 17:04:45 -07:00
ia
b5775b56c6 all: gofmt (#1743)
* all: gofmt

Run 'gofmt -w .' from project root.

* Update changelog to say that I ran gofmt

* Revert "Update changelog to say that I ran gofmt"

This reverts commit 956f133ff0354fd7338e7df7c823e6f98b655da6.
2018-06-15 02:03:50 +02:00
Alexander Simmerl
19af3e9733 Merge pull request #1738 from tendermint/zarko/add-blockchain-reactor-algorithm-spec
Add algorithm for Blockchain Reactor
2018-06-15 01:47:36 +02:00
Ethan Buchman
917bf4d428 Merge pull request #1732 from maxim-levy/patch-2
typo fix
2018-06-13 17:42:29 -07:00
Anton Kaliaev
696e8c6f9e [docs] write about addr_book_strict in production notes (#1741)
Refs #1736
2018-06-13 18:24:12 +04:00
Zarko Milosevic
ce73884857 Add spec for Blockchain Reactor algorithm 2018-06-13 14:05:17 +02:00
Alexander Simmerl
fa32dc5181 Merge pull request #1739 from tendermint/dev/bump_version
Bump abci version
2018-06-13 01:48:13 +02:00
ValarDragon
ec0c901bec Bump abci version 2018-06-12 16:30:24 -07:00
Dev Ojha
b84f788f36 Switch ports 466xx to 266xx (#1735)
* Switch ports 466xx to be 266xx
This is done so the default ports aren't in the linux kernel's default ephemeral port range.

* Update ABCI import

* Bump cache on circleci

* Get more verbose output for debugging

* Bump abci dependency

* Fix accidental change of a block header's hash

* pin abci release
2018-06-12 13:25:52 +04:00
Adrian Brink
ac80b93b60 unsafe_reset_all also resets addrbook.json (#1731)
* `unsafe_reset_all` also resets addrbook.json

When executing `unsafe_reset_all` it also clear all IP addresses from
addrbook.json. This is the expected behaviour of `unsafe_reset_all`.

* Fix tests

* improve logging statements

* use correct file
2018-06-12 12:55:10 +04:00
Alexander Simmerl
dfc5aefd5f Merge pull request #1726 from tendermint/1717-panic-in-netaddress
return an error if we fail to parse external IP
2018-06-12 01:08:16 +02:00
Alexander Simmerl
eb4a8e0e7a Merge pull request #1719 from Slamper/develop
Escape paths in config template
2018-06-12 01:06:22 +02:00
Max Levy
708ddb30f7 typo fix
And typo fix
2018-06-11 22:53:48 +09:00
Anton Kaliaev
cd3a240c9f return an error if we fail to parse external IP
```
I[06-08|11:51:57.234] Getting UPNP external address                module=p2p
I[06-08|11:51:58.867] Got UPNP external address                    module=p2p address=
```

Fixes #1717

```
I[06-08|11:51:56.952] Starting multiAppConn                        module=proxy impl=multiAppConn
I[06-08|11:51:56.952] Starting localClient                         module=abci-client connection=query impl=localClient
I[06-08|11:51:56.952] Starting localClient                         module=abci-client connection=mempool impl=localClient
I[06-08|11:51:56.952] Starting localClient                         module=abci-client connection=consensus impl=localClient
I[06-08|11:51:56.952] ABCI Handshake                               module=consensus appHeight=0 appHash=
I[06-08|11:51:56.952] ABCI Replay Blocks                           module=consensus appHeight=0 storeHeight=0 stateHeight=0
I[06-08|11:51:57.053] Completed ABCI Handshake - Tendermint and App are synced module=consensus appHeight=0 appHash=
I[06-08|11:51:57.053] This node is a validator                     module=consensus addr=6816B5D9BAC32A3CDF07884D9D3D2650694C371D pubKey=PubKeyEd25519{27A40CD032DD2467342D0CF27C5EC92052D966FEC714B6CF2F3BF3146AFD0D51}
I[06-08|11:51:57.234] Starting Node                                module=main impl=Node
I[06-08|11:51:57.234] Starting EventBus                            module=events impl=EventBus
I[06-08|11:51:57.234] Local listener                               module=p2p ip=:: port=46656
I[06-08|11:51:57.234] Getting UPNP external address                module=p2p
I[06-08|11:51:58.867] Got UPNP external address                    module=p2p address=
I[06-08|11:51:58.867] Starting DefaultListener                     module=p2p impl=Listener(@<nil>:46656)
I[06-08|11:51:58.867] P2P Node ID                                  module=main ID=3629b516392e494ae717ac4c6a1ea7eb0fe421c3 file=/home/tpb/.tendermint/config/node_key.json
I[06-08|11:51:58.868] Add our address to book                      module=p2p book=/home/tpb/.tendermint/config/addrbook.json addr=null
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x38 pc=0x89fb86]

goroutine 1 [running]:
github.com/tendermint/tendermint/p2p.(*NetAddress).String(0x0, 0xc96e24, 0x17)
	/home/tpb/code/go/src/github.com/tendermint/tendermint/p2p/netaddress.go:171 +0x26
github.com/tendermint/tendermint/p2p/pex.(*addrBook).AddOurAddress(0xc420190620, 0x0)
	/home/tpb/code/go/src/github.com/tendermint/tendermint/p2p/pex/addrbook.go:160 +0x116
github.com/tendermint/tendermint/node.(*Node).OnStart(0xc420286d00, 0xc4201b8010, 0xd)
	/home/tpb/code/go/src/github.com/tendermint/tendermint/node/node.go:402 +0x547
github.com/tendermint/tendermint/vendor/github.com/tendermint/tmlibs/common.(*BaseService).Start(0xc420286d00, 0xe51c40, 0xc42000bd40)
	/home/tpb/code/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/tmlibs/common/service.go:130 +0x3bd
github.com/tendermint/tendermint/cmd/tendermint/commands.NewRunNodeCmd.func1(0xc42022e000, 0xc4200acdc0, 0x0, 0x1, 0x0, 0x0)
	/home/tpb/code/go/src/github.com/tendermint/tendermint/cmd/tendermint/commands/run_node.go:58 +0xfe
github.com/tendermint/tendermint/vendor/github.com/spf13/cobra.(*Command).execute(0xc42022e000, 0xc4200acda0, 0x1, 0x1, 0xc42022e000, 0xc4200acda0)
	/home/tpb/code/go/src/github.com/tendermint/tendermint/vendor/github.com/spf13/cobra/command.go:762 +0x468
github.com/tendermint/tendermint/vendor/github.com/spf13/cobra.(*Command).ExecuteC(0x1289280, 0xbbdda0, 0xc420015e01, 0xc4201bc640)
	/home/tpb/code/go/src/github.com/tendermint/tendermint/vendor/github.com/spf13/cobra/command.go:852 +0x30a
github.com/tendermint/tendermint/vendor/github.com/spf13/cobra.(*Command).Execute(0x1289280, 0xc4201bc640, 0xc420015e98)
	/home/tpb/code/go/src/github.com/tendermint/tendermint/vendor/github.com/spf13/cobra/command.go:800 +0x2b
github.com/tendermint/tendermint/vendor/github.com/tendermint/tmlibs/cli.Executor.Execute(0x1289280, 0xde5798, 0x2, 0xc4200332c0)
	/home/tpb/code/go/src/github.com/tendermint/tendermint/vendor/github.com/tendermint/tmlibs/cli/setup.go:89 +0x4e
main.main()
	/home/tpb/code/go/src/github.com/tendermint/tendermint/cmd/tendermint/main.go:45 +0x24d
```
2018-06-09 15:03:38 +04:00
Hendrik Hofstadt
e93865f7de escape paths in config template 2018-06-08 20:44:54 -07:00
Alexander Simmerl
2e0466d1c8 Merge pull request #1722 from tendermint/dev/document_peer_types
Update description of seed and persistent peer nodes
2018-06-09 02:14:48 +02:00
ValarDragon
fd9375c35b Docs: Update description of seeds and persistent peers 2018-06-09 01:25:46 +02:00
Ethan Buchman
5ebf9b816b Merge pull request #1698 from tendermint/zach/rst-2-md
docs to markdown
2018-06-07 21:46:18 -07:00
Zach Ramsay
724b6c39b8 style fixes/typos/etc from PR review 2018-06-07 10:01:31 -04:00
Zach Ramsay
9aef3fa610 docs: pretty fixes 2018-06-07 09:41:57 -04:00
Zach Ramsay
e82ab1c374 moar fixes 2018-06-07 09:41:57 -04:00
Zach Ramsay
ffa8b5f620 oomph 2018-06-07 09:41:57 -04:00
Zach Ramsay
df07f8b36a moar 2018-06-07 09:41:57 -04:00
Zach Ramsay
d4d91d7781 running in prod 2018-06-07 09:41:57 -04:00
Zach Ramsay
3039aa1e67 intro 2018-06-07 09:41:57 -04:00
Zach Ramsay
d4d79886b2 indexing txns 2018-06-07 09:41:57 -04:00
Zach Ramsay
b429d65f9f how to read logs 2018-06-07 09:41:57 -04:00
Zach Ramsay
20e1eadcf1 getting started 2018-06-07 09:41:57 -04:00
Zach Ramsay
2e18a4e633 determinism & ecosystem 2018-06-07 09:41:57 -04:00
Zach Ramsay
b860018975 deploy tesnets 2018-06-07 09:41:57 -04:00
Zach Ramsay
44c88805a7 app devel 2018-06-07 09:41:57 -04:00
Zach Ramsay
a9b6fcdbc4 docs: cleanup/clarify build process 2018-06-07 09:41:57 -04:00
Zach Ramsay
14a5dfd945 docs: start move back to md 2018-06-07 09:41:57 -04:00
Ethan Buchman
24fa2a62b0 dev version bump 2018-06-06 20:57:19 -07:00
Ethan Buchman
27bd1deabe Merge pull request #1703 from tendermint/release/v0.20.0
Release/v0.20.0
2018-06-06 20:47:17 -07:00
Ethan Buchman
76c82fd433 add more tests 2018-06-06 20:49:00 -07:00
Ethan Buchman
9481cabd50 fixes from review 2018-06-06 20:45:20 -07:00
Ethan Buchman
46b957929c Merge branch 'develop' into release/v0.20.0 2018-06-06 19:24:36 -07:00
Ethan Buchman
512e563d4b Merge pull request #1694 from tendermint/dev/bsd_install_script
docs: Add BSD install script
2018-06-06 19:14:46 -07:00
ValarDragon
a6a4fc7784 docs: Add BSD install script 2018-06-06 17:57:25 -07:00
Ethan Buchman
bfcec02423 Merge pull request #1702 from tendermint/xla/fix-racy-config
Fix race when mutating MConnConfig
2018-06-06 17:15:53 -07:00
Ethan Buchman
fcf61b8088 check addrs match pubkeys in abci Validator. version bump 2018-06-06 16:35:35 -07:00
Ethan Buchman
46fb179605 Merge pull request #1701 from tendermint/bucky/abci-fixes
Bucky/abci fixes
2018-06-06 16:12:12 -07:00
Ethan Buchman
89925501f3 p2p/filter/pubkey -> p2p/filter/id 2018-06-06 16:13:51 -07:00
Ethan Buchman
6b8613b3e7 ResponseEndBlock: ensure Address matches PubKey if provided 2018-06-06 16:12:14 -07:00
Ethan Buchman
8be27494bb update abci spec. add address spec 2018-06-06 16:11:58 -07:00
Alexander Simmerl
c661a3ec21 Fix race when mutating MConnConfig
Instead of mutating the passed in MConnConfig part of P2PConfig we just
use the default and override the values, the same as before as it was
always the default version. This is yet another good reason to not embed
information and access to config structs in our components and will go
away with the ongoing refactoring in #1325.
2018-06-07 01:09:13 +02:00
Ethan Buchman
8e45348737 update for abci v0.11.0 release. let InitChain update validators 2018-06-06 15:47:04 -07:00
Ethan Buchman
7dfc74a6b6 Merge pull request #1667 from tendermint/bucky/abci-11
Update ABCI for new PubKey type and changes to BeginBlock and InitChain
2018-06-06 13:16:09 -07:00
Ethan Buchman
2edc68c59b use all fields in abci types 2018-06-06 13:07:17 -07:00
Dev Ojha
fe4123684d Change reset messages (#1699) 2018-06-06 22:07:10 +04:00
Ethan Buchman
2897685c57 abci header takes ValidatorsHash 2018-06-06 00:28:12 -07:00
Ethan Buchman
71556c62eb fixes from rebase 2018-06-05 22:14:37 -07:00
Ethan Buchman
54e61468d4 fixes from review 2018-06-05 22:04:38 -07:00
Ethan Buchman
5c7ccbd4a7 use const for abci type strings 2018-06-05 22:04:38 -07:00
Ethan Buchman
e2f5a6fbe4 update abci 2018-06-05 22:04:38 -07:00
Ethan Buchman
aa8be33da1 fix fmt 2018-06-05 22:04:27 -07:00
Ethan Buchman
909f66e841 remove extra eventBus 2018-06-05 22:04:27 -07:00
Ethan Buchman
3d2c4fd309 update Evidence type - requires pubkey and valset to verify and convert to abci.Evidence 2018-06-05 22:04:26 -07:00
Ethan Buchman
e5bca1df6f update godep for abci 2018-06-05 22:00:43 -07:00
Ethan Buchman
866bcceb35 fix consensus tests 2018-06-05 22:00:25 -07:00
Ethan Buchman
e1e6878a4d fix state tests 2018-06-05 22:00:25 -07:00
Ethan Buchman
e4147b6f1a state test runs 2018-06-05 22:00:25 -07:00
Ethan Buchman
7606b7595f compiles 2018-06-05 22:00:25 -07:00
Ethan Buchman
485b4a0c6f revert gogo 2018-06-05 21:59:52 -07:00
Ethan Buchman
575d94dbb9 state compiles 2018-06-05 21:59:52 -07:00
Ethan Buchman
ebd2fe7a68 more types 2018-06-05 21:59:52 -07:00
Ethan Buchman
f28eae7816 update types 2018-06-05 21:59:52 -07:00
Ethan Buchman
e13c1ab735 update for new abci 2018-06-05 21:59:52 -07:00
Ethan Buchman
0e0461d9bc dev version bump 2018-06-05 18:54:30 -07:00
Ethan Buchman
057e076ca9 Merge pull request #1693 from tendermint/release/v0.19.9
Release/v0.19.9
2018-06-05 18:00:02 -07:00
Ethan Buchman
775fef31c2 remove go-wire from test/app/grpc_client 2018-06-05 17:43:39 -07:00
Ethan Buchman
9cb079dcc6 dep, version, changelog 2018-06-05 17:38:54 -07:00
Ethan Buchman
67180344b7 Merge pull request #1663 from tendermint/zach/quick-install
quick install script
2018-06-05 17:17:47 -07:00
Ethan Buchman
825fdf2c24 Merge pull request #1679 from tendermint/flush-wal-on-stop
Flush cs.wal on stop
2018-06-05 17:14:19 -07:00
Ethan Buchman
61002ad264 Merge pull request #1628 from tendermint/bucky/selective-evidence-broadcast
evidence: dont send evidence to unsynced peers
2018-06-05 17:07:29 -07:00
Ethan Buchman
41e847ec97 linter 2018-06-05 17:02:05 -07:00
Ethan Buchman
55bae62d71 fix test 2018-06-05 16:54:58 -07:00
Ethan Buchman
d2259696af Merge pull request #1689 from tendermint/1688-tx-search-panic
validate per_page before page
2018-06-05 07:52:49 -07:00
Ethan Buchman
32719123d9 Merge pull request #1687 from tendermint/sdk-1045-memory-leak
do not drain the channel because there is no channel, duh
2018-06-05 07:51:21 -07:00
Anton Kaliaev
2ce8179c8b validate per_page before page
plus an additional check just in case
Closes #1688
2018-06-05 16:20:48 +04:00
Anton Kaliaev
b8c076ca79 do not drain the channel because there is no channel, duh
Fixes https://github.com/cosmos/cosmos-sdk/issues/1045
2018-06-05 14:57:20 +04:00
Ethan Buchman
1b2e34738a checkSendEvidenceMessage 2018-06-05 00:01:01 -07:00
Ethan Buchman
566024b64f use Hash as map key 2018-06-04 21:50:29 -07:00
Ethan Buchman
932381effa evidence: give each peer a go-routine 2018-06-04 21:20:23 -07:00
Ethan Buchman
2007c66091 fix test 2018-06-04 21:20:23 -07:00
Ethan Buchman
97c5533c35 update some comments 2018-06-04 21:20:23 -07:00
Ethan Buchman
3d33226e80 move types/services.go to state pkg. pass State to evpool.Update 2018-06-04 21:20:23 -07:00
Ethan Buchman
edb851280a state: b -> block 2018-06-04 21:20:23 -07:00
Ethan Buchman
dd62f06994 state: s -> state 2018-06-04 21:20:23 -07:00
Ethan Buchman
19d95b5410 evidence: check peerstate exists; dont send old evidence 2018-06-04 21:20:23 -07:00
Ethan Buchman
53937a8129 changelog 2018-06-04 21:20:23 -07:00
Ethan Buchman
f1c53c7358 evidence: dont send evidence to unsynced peers
* only send evidence to peers that are synced enough to validate it all
* closes #1624
2018-06-04 21:20:23 -07:00
Ethan Buchman
3445f1206e Merge pull request #1683 from tendermint/bucky/test
fix byz-test
2018-06-04 21:09:10 -07:00
Ethan Buchman
097f778c1e fix byz-test 2018-06-04 20:48:35 -07:00
Ethan Buchman
c85c21d1bc Merge pull request #1670 from tendermint/xla/extract-privval
Extract priv_validator into first class package
2018-06-04 18:57:09 -07:00
Ethan Buchman
fd4db8dfdc Merge pull request #1676 from tendermint/xla/collapse-peerconfig
Collapse PeerConfig into P2PConfig
2018-06-04 18:50:41 -07:00
Alexander Simmerl
1318bd18cd Merge pull request #1680 from Slamper/develop
Return 404 for unknown RPC endpoints
2018-06-05 02:14:16 +02:00
Alexander Simmerl
ea896865a7 Collapse PeerConfig into P2PConfig
As both configs are concerned with the p2p packaage and PeerConfig is
only used inside of the package there is no good reason to keep the
couple of fields separate, therefore it is collapsed into the more
general P2PConifg. This is a stepping stone towards a setup where the
components inside of p2p do not have any knowledge about the config.

follow-up to #1325
2018-06-05 02:07:56 +02:00
Ethan Buchman
aeb91dfc22 dev version bump 2018-06-04 15:57:57 -07:00
Ethan Buchman
5727916c5b Merge pull request #1681 from tendermint/release/v0.19.8
Release/v0.19.8
2018-06-04 15:48:22 -07:00
Ethan Buchman
876c8f14e7 changelog and version 2018-06-04 14:16:58 -07:00
Hendrik Hofstadt
67416feb3a return 404 for unknown RPC endpoints 2018-06-04 22:14:20 +02:00
Zach Ramsay
8706ae765c docs: a link to quick install script 2018-06-04 10:42:28 -04:00
Zach Ramsay
954a8941ff scripts: quickest/easiest fresh install 2018-06-04 10:42:28 -04:00
Anton Kaliaev
1f22f34edf flush wal group on stop
Refs #1659
Refs https://github.com/tendermint/tmlibs/pull/217
2018-06-04 16:47:44 +04:00
Anton Kaliaev
0562009275 bring back assert 2018-06-04 16:33:57 +04:00
idoor88
fedd07c522 removed assertion to avoid confusion (#1626) 2018-06-04 14:30:46 +04:00
Anton Kaliaev
3fa734ef5a recheck only if there are txs left in the mempool (#1645) 2018-06-04 14:28:47 +04:00
Ethan Buchman
cd6bfdc42f Merge pull request #1673 from tendermint/bucky/mempool
fix possible mempool deadlock
2018-06-03 16:14:58 -04:00
Ethan Buchman
98b0c51b5f fix possible mempool deadlock 2018-06-03 16:11:52 -04:00
Ethan Buchman
c777be256a changelog, version 2018-06-03 16:11:21 -04:00
Ethan Buchman
d66f8bf829 Merge branch 'master' into develop 2018-06-03 16:10:04 -04:00
Alexander Simmerl
bf370d36c2 Extract priv_validator into first class package
This is a maintenance change to move the private validator package out
of the types and to a top-level location. There is no good reason to
keep it under the types and it will more clearly coommunicate where
additions related to the privval belong. It leaves the interface and the
mock in types for now as it would introduce circular dependency between
privval and types, this should be resolved eventually.

* mv priv_validator to privval pkg
* use consistent `privval` as import

Follow-up to #1255
2018-06-03 13:51:58 +02:00
Ethan Buchman
1c643701f5 Merge pull request #1662 from Liamsi/develop
WIP: simplify & update documentation, fix typo
2018-06-02 18:39:36 -04:00
Alexander Simmerl
0b0290bdb2 Merge pull request #1675 from tendermint/xla/disable-slate-ci
Disable slate step in CI workflow
2018-06-02 15:56:43 +02:00
Alexander Simmerl
a4779fdf51 Disable slate step in CI workflow
It's currently breaking for unknown reasons, until fixed we going to
disable it, to not block on it for unrelated PRs.
2018-06-02 15:49:25 +02:00
Liamsi
7030d5c2a7 remove notes column
according to: https://github.com/tendermint/go-crypto/pull/110#issuecomment-394048086
2018-06-02 13:04:40 +01:00
Ethan Buchman
f34d1009c4 Merge pull request #1671 from tendermint/1518-xla/remove-auth_enc-option
Remove auth_enc config option
2018-06-01 21:07:14 -04:00
Alexander Simmerl
0e3dc32b3d Merge pull request #1672 from tendermint/xla/store-ci-test-logs
Store CI test logs
2018-06-02 02:44:01 +02:00
Alexander Simmerl
d292fa4541 Store CI test logs
For post-mortem introspection it is helpful to have the full logs of
test runs available for download.
2018-06-02 02:33:31 +02:00
Alexander Simmerl
3255c076e5 Remove auth_enc config option
As we didn't hear any voices requesting this feature, we removed the
option to disable it and always have peer connection auth encrypted.

closes #1518
follow-up #1325
2018-06-01 21:07:20 +02:00
Liamsi
978277a4c1 make slightly more readable 2018-05-31 20:40:01 +01:00
Liamsi
58eb76f34d simplify & update documentation, fiy typo 2018-05-31 20:13:41 +01:00
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
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
e88f74bb9b remove wal_light setting
Closes #1428
2018-04-11 10:08:03 +02: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
Javed Khan
5ef639fcbe p2p: persistent - redial if first dial fails
Fixes #1401
2018-04-03 09:27:06 +05:30
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
337 changed files with 14801 additions and 5596 deletions

View File

@@ -3,7 +3,7 @@ version: 2
defaults: &defaults
working_directory: /go/src/github.com/tendermint/tendermint
docker:
- image: circleci/golang:1.10.0
- image: circleci/golang:1.10.3
environment:
GOBIN: /tmp/workspace/bin
@@ -16,7 +16,7 @@ jobs:
- checkout
- restore_cache:
keys:
- v1-pkg-cache
- v2-pkg-cache
- run:
name: tools
command: |
@@ -38,11 +38,11 @@ jobs:
- bin
- profiles
- save_cache:
key: v1-pkg-cache
key: v2-pkg-cache
paths:
- /go/pkg
- save_cache:
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
key: v2-tree-{{ .Environment.CIRCLE_SHA1 }}
paths:
- /go/src/github.com/tendermint/tendermint
@@ -52,9 +52,9 @@ jobs:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v1-pkg-cache
key: v2-pkg-cache
- restore_cache:
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
key: v2-tree-{{ .Environment.CIRCLE_SHA1 }}
- run:
name: Checkout abci
command: |
@@ -77,15 +77,31 @@ jobs:
paths:
- "bin/abci*"
build_slate:
<<: *defaults
steps:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v2-pkg-cache
- restore_cache:
key: v2-tree-{{ .Environment.CIRCLE_SHA1 }}
- run:
name: slate docs
command: |
set -ex
export PATH="$GOBIN:$PATH"
make build-slate
lint:
<<: *defaults
steps:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v1-pkg-cache
key: v2-pkg-cache
- restore_cache:
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
key: v2-tree-{{ .Environment.CIRCLE_SHA1 }}
- run:
name: metalinter
command: |
@@ -99,9 +115,9 @@ jobs:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v1-pkg-cache
key: v2-pkg-cache
- restore_cache:
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
key: v2-tree-{{ .Environment.CIRCLE_SHA1 }}
- run: sudo apt-get update && sudo apt-get install -y --no-install-recommends bsdmainutils
- run:
name: Run tests
@@ -114,21 +130,24 @@ jobs:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v1-pkg-cache
key: v2-pkg-cache
- restore_cache:
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
key: v2-tree-{{ .Environment.CIRCLE_SHA1 }}
- run: mkdir -p /tmp/logs
- run:
name: Run tests
command: |
for pkg in $(go list github.com/tendermint/tendermint/... | grep -v /vendor/ | circleci tests split --split-by=timings); do
id=$(basename "$pkg")
go test -timeout 5m -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg"
GOCACHE=off go test -v -timeout 5m -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg" | tee "/tmp/logs/$id-$RANDOM.log"
done
- persist_to_workspace:
root: /tmp/workspace
paths:
- "profiles/*"
- store_artifacts:
path: /tmp/logs
test_persistence:
<<: *defaults
@@ -136,9 +155,9 @@ jobs:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v1-pkg-cache
key: v2-pkg-cache
- restore_cache:
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
key: v2-tree-{{ .Environment.CIRCLE_SHA1 }}
- run:
name: Run tests
command: bash test/persist/test_failure_indices.sh
@@ -153,7 +172,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
@@ -161,7 +180,7 @@ jobs:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
key: v2-tree-{{ .Environment.CIRCLE_SHA1 }}
- run:
name: gather
command: |

4
.github/CODEOWNERS vendored
View File

@@ -1,4 +1,4 @@
# CODEOWNERS: https://help.github.com/articles/about-codeowners/
# Everything goes through Bucky and Anton. For now.
* @ebuchman @melekes
# Everything goes through Bucky, Anton, Alex. For now.
* @ebuchman @melekes @xla

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**:
@@ -39,6 +35,8 @@ in a case of bug.
**Logs (you can paste a part showing an error or attach the whole file)**:
**Config (you can paste only the changes you've made)**:
**`/dump_consensus_state` output for consensus bugs**
**Anything else do we need to know**:

6
.gitignore vendored
View File

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

View File

@@ -1,28 +1,224 @@
# Changelog
## Roadmap
## 0.21.0
BREAKING CHANGES:
- Better support for injecting randomness
- Upgrade consensus for more real-time use of evidence
*June 21th, 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 CHANGES
- [config] Change default ports from 4665X to 2665X. Ports over 32768 are
ephemeral and reserved for use by the kernel.
- [cmd] `unsafe_reset_all` removes the addrbook.json
IMPROVEMENT
- [pubsub] Set default capacity to 0
- [docs] Various improvements
BUG FIXES
- [consensus] Fix an issue where we don't make blocks after `fast_sync` when `create_empty_blocks=false`
- [mempool] Fix #1761 where we don't process txs if `cache_size=0`
- [rpc] Fix memory leak in Websocket (when using `/subscribe` method)
- [config] Escape paths in config - fixes config paths on Windows
## 0.20.0
*June 6th, 2018*
This is the first in a series of breaking releases coming to Tendermint after
soliciting developer feedback and conducting security audits.
This release does not break any blockchain data structures or
protocols other than the ABCI messages between Tendermint and the application.
Applications that upgrade for ABCI v0.11.0 should be able to continue running Tendermint
v0.20.0 on blockchains created with v0.19.X
BREAKING CHANGES
- [abci] Upgrade to
[v0.11.0](https://github.com/tendermint/abci/blob/master/CHANGELOG.md#0110)
- [abci] Change Query path for filtering peers by node ID from
`p2p/filter/pubkey/<id>` to `p2p/filter/id/<id>`
## 0.19.9
*June 5th, 2018*
BREAKING CHANGES
- [types/priv_validator] Moved to top level `privval` package
FEATURES
- [config] Collapse PeerConfig into P2PConfig
- [docs] Add quick-install script
- [docs/spec] Add table of Amino prefixes
BUG FIXES
- [rpc] Return 404 for unknown endpoints
- [consensus] Flush WAL on stop
- [evidence] Don't send evidence to peers that are behind
- [p2p] Fix memory leak on peer disconnects
- [rpc] Fix panic when `per_page=0`
## 0.19.8
*June 4th, 2018*
BREAKING:
- [p2p] Remove `auth_enc` config option, peer connections are always auth
encrypted. Technically a breaking change but seems no one was using it and
arguably a bug fix :)
BUG FIXES
- [mempool] Fix deadlock under high load when `skip_timeout_commit=true` and
`create_empty_blocks=false`
## 0.19.7
*May 31st, 2018*
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 synchronous 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
BUG FIX:
- [evidence] Dont send peers evidence from heights they haven't synced to yet
- [p2p] Refuse connections to more than one peer with the same IP
- [docs] Various fixes
## 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)
@@ -39,8 +235,9 @@ See github.com/tendermint/go-amino for details on the new format.
See `scripts/wire2amino.go` for a tool to upgrade
genesis/priv_validator/node_key JSON files.
FEATURES:
- [cmd] added `gen_node_key` command
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 26656 26657
ENTRYPOINT ["/usr/bin/tendermint"]
CMD ["node", "--moniker=`hostname`"]
STOPSIGNAL SIGTERM
ARG BINARY=tendermint
COPY $BINARY /usr/bin/tendermint

View File

@@ -27,8 +27,8 @@ RUN mkdir -p /go/src/github.com/tendermint/tendermint && \
VOLUME $DATA_ROOT
EXPOSE 46656
EXPOSE 46657
EXPOSE 26656
EXPOSE 26657
ENTRYPOINT ["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 26656
EXPOSE 26657

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

@@ -1,4 +1,6 @@
# Supported tags and respective `Dockerfile` links
# Docker
## Supported tags and respective `Dockerfile` links
- `0.17.1`, `latest` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/208ac32fa266657bd6c304e84ec828aa252bb0b8/DOCKER/Dockerfile)
- `0.15.0` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/170777300ea92dc21a8aec1abc16cb51812513a4/DOCKER/Dockerfile)
@@ -14,10 +16,10 @@
`develop` tag points to the [develop](https://github.com/tendermint/tendermint/tree/develop) branch.
# Quick reference
## Quick reference
* **Where to get help:**
https://tendermint.com/community
https://cosmos.network/community
* **Where to file issues:**
https://github.com/tendermint/tendermint/issues
@@ -25,7 +27,7 @@
* **Supported Docker versions:**
[the latest release](https://github.com/moby/moby/releases) (down to 1.6 on a best-effort basis)
# Tendermint
## Tendermint
Tendermint Core is Byzantine Fault Tolerant (BFT) middleware that takes a state transition machine, written in any programming language, and securely replicates it on many machines.
@@ -33,29 +35,33 @@ For more background, see the [introduction](https://tendermint.readthedocs.io/en
To get started developing applications, see the [application developers guide](https://tendermint.readthedocs.io/en/master/getting-started.html).
# How to use this image
## How to use this image
## Start one instance of the Tendermint core with the `kvstore` app
### Start one instance of the Tendermint core with the `kvstore` app
A very simple example of a built-in app and Tendermint core in one container.
A 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:
# License
```
make build-linux
make build-docker-localnode
make localnet-start
```
View [license information](https://raw.githubusercontent.com/tendermint/tendermint/master/LICENSE) for the software contained in this image.
Note that this will build and use a different image than the ones provided here.
# User Feedback
## License
- Tendermint's license is [Apache 2.0](https://github.com/tendermint/tendermint/master/LICENSE).
## 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.

63
Gopkg.lock generated
View File

@@ -5,7 +5,7 @@
branch = "master"
name = "github.com/btcsuite/btcd"
packages = ["btcec"]
revision = "2be2f12b358dc57d70b8f501b00be450192efbc3"
revision = "86fed781132ac890ee03e906e4ecd5d6fa180c64"
[[projects]]
name = "github.com/davecgh/go-spew"
@@ -82,7 +82,7 @@
branch = "master"
name = "github.com/golang/snappy"
packages = ["."]
revision = "553a641470496b2327abcac10b36396bd98e45c9"
revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a"
[[projects]]
name = "github.com/gorilla/websocket"
@@ -128,20 +128,20 @@
[[projects]]
name = "github.com/magiconair/properties"
packages = ["."]
revision = "c3beff4c2358b44d0493c7dda585e7db7ff28ae6"
version = "v1.7.6"
revision = "c2353362d570a7bfa228149c62842019201cfb71"
version = "v1.8.0"
[[projects]]
branch = "master"
name = "github.com/mitchellh/mapstructure"
packages = ["."]
revision = "00c29f56e2386353d58c599509e8dc3801b0d716"
revision = "bb74f1db0675b241733089d5a1faa5dd8b0ef57b"
[[projects]]
name = "github.com/pelletier/go-toml"
packages = ["."]
revision = "acdc4509485b587f5e675510c4f2c63e90ff68a8"
version = "v1.1.0"
revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194"
version = "v1.2.0"
[[projects]]
name = "github.com/pkg/errors"
@@ -159,7 +159,7 @@
branch = "master"
name = "github.com/rcrowley/go-metrics"
packages = ["."]
revision = "d932a24a8ccb8fcadc993e5c6c58f93dac168294"
revision = "e2704e165165ec55d062f5919b4b29494e9fa790"
[[projects]]
name = "github.com/spf13/afero"
@@ -167,8 +167,8 @@
".",
"mem"
]
revision = "63644898a8da0bc22138abf860edaf5277b6102e"
version = "v1.1.0"
revision = "787d034dfe70e44075ccc060d346146ef53270ad"
version = "v1.1.1"
[[projects]]
name = "github.com/spf13/cast"
@@ -179,8 +179,8 @@
[[projects]]
name = "github.com/spf13/cobra"
packages = ["."]
revision = "a1f051bc3eba734da4772d60e2d677f47cf93ef4"
version = "v0.0.2"
revision = "ef82de70bb3f60c65fb8eebacbb2d122ef517385"
version = "v0.0.3"
[[projects]]
branch = "master"
@@ -206,8 +206,8 @@
"assert",
"require"
]
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
version = "v1.2.1"
revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686"
version = "v1.2.2"
[[projects]]
branch = "master"
@@ -226,7 +226,7 @@
"leveldb/table",
"leveldb/util"
]
revision = "714f901b98fdb3aa954b4193d8cbd64a28d80cad"
revision = "e2150783cd35f5b607daca48afd8c57ec54cc995"
[[projects]]
name = "github.com/tendermint/abci"
@@ -238,8 +238,8 @@
"server",
"types"
]
revision = "78a8905690ef54f9d57e3b2b0ee7ad3a04ef3f1f"
version = "v0.10.3"
revision = "198dccf0ddfd1bb176f87657e3286a05a6ed9540"
version = "v0.12.0"
[[projects]]
branch = "master"
@@ -254,8 +254,8 @@
[[projects]]
name = "github.com/tendermint/go-amino"
packages = ["."]
revision = "42246108ff925a457fb709475070a03dfd3e2b5c"
version = "0.9.6"
revision = "ed62928576cfcaf887209dc96142cd79cdfff389"
version = "0.9.9"
[[projects]]
name = "github.com/tendermint/go-crypto"
@@ -263,12 +263,6 @@
revision = "915416979bf70efa4bcbf1c6cd5d64c5fff9fc19"
version = "v0.6.2"
[[projects]]
name = "github.com/tendermint/go-wire"
packages = ["."]
revision = "fa721242b042ecd4c6ed1a934ee740db4f74e45c"
version = "v0.7.3"
[[projects]]
name = "github.com/tendermint/tmlibs"
packages = [
@@ -281,12 +275,10 @@
"flowrate",
"log",
"merkle",
"pubsub",
"pubsub/query",
"test"
]
revision = "97e1f1ad3f510048929a51475811a18686c894df"
version = "0.8.2-rc0"
revision = "692f1d86a6e2c0efa698fd1e4541b68c74ffaf38"
version = "v0.8.4"
[[projects]]
branch = "master"
@@ -301,27 +293,27 @@
"ripemd160",
"salsa20/salsa"
]
revision = "d6449816ce06963d9d136eee5a56fca5b0616e7e"
revision = "8ac0e0d97ce45cd83d1d7243c060cb8461dda5e9"
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = [
"context",
"http/httpguts",
"http2",
"http2/hpack",
"idna",
"internal/timeseries",
"lex/httplex",
"trace"
]
revision = "61147c48b25b599e5b561d2e9c4f3e1ef489ca41"
revision = "db08ff08e8622530d9ed3a0e8ac279f6d4c02196"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["unix"]
revision = "3b87a42e500a6dc65dae1a55d0b641295971163e"
revision = "a9e25c09b96b8870693763211309e213c6ef299d"
[[projects]]
name = "golang.org/x/text"
@@ -345,10 +337,9 @@
version = "v0.3.0"
[[projects]]
branch = "master"
name = "google.golang.org/genproto"
packages = ["googleapis/rpc/status"]
revision = "51d0944304c3cbce4afe9e5247e21100037bff78"
revision = "7fd901a49ba6a7f87732eb344f6e3c5b19d1b200"
[[projects]]
name = "google.golang.org/grpc"
@@ -383,6 +374,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "e70f8692c825e80ae8510546e297840b9560d00e11b2272749a55cc2ffd147f0"
inputs-digest = "62d0626fa963b25411234da915fb3b8bfc7aefffd76824d27ac1647ca2b5d489"
solver-name = "gps-cdcl"
solver-version = 1

View File

@@ -71,7 +71,7 @@
[[constraint]]
name = "github.com/tendermint/abci"
version = "~0.10.3"
version = "~0.12.0"
[[constraint]]
name = "github.com/tendermint/go-crypto"
@@ -79,16 +79,21 @@
[[constraint]]
name = "github.com/tendermint/go-amino"
version = "~0.9.6"
version = "=0.9.9"
[[constraint]]
[[override]]
name = "github.com/tendermint/tmlibs"
version = "~0.8.2-rc0"
version = "~0.8.4"
[[constraint]]
name = "google.golang.org/grpc"
version = "~1.7.3"
# this got updated and broke, so locked to an old working commit ...
[[override]]
name = "google.golang.org/genproto"
revision = "7fd901a49ba6a7f87732eb344f6e3c5b19d1b200"
[prune]
go-tests = true
unused-packages = true

46
Makefile Normal file → Executable file
View File

@@ -178,6 +178,14 @@ 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
@@ -185,18 +193,44 @@ metalinter_all:
build-linux:
GOOS=linux GOARCH=amd64 $(MAKE) build
build-docker-localnode:
cd networks/local
make
# Run a 4-node testnet locally
docker-start:
@echo "Wait until 'Attaching to node0, node1, node2, node3' message appears"
@if ! [ -f build/node0/config/genesis.json ]; then docker run --rm -v `pwd`/build:/tendermint:Z tendermint/localnode testnet --v 4 --o . --populate-persistent-peers --starting-ip-address 192.167.10.2 ; fi
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
docker-stop:
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 build-linux docker-start docker-stop
.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

@@ -9,7 +9,7 @@ Or [Blockchain](https://en.wikipedia.org/wiki/Blockchain_(database)) for short.
https://camo.githubusercontent.com/915b7be44ada53c290eb157634330494ebe3e30a/68747470733a2f2f676f646f632e6f72672f6769746875622e636f6d2f676f6c616e672f6764646f3f7374617475732e737667
)](https://godoc.org/github.com/tendermint/tendermint)
[![Go version](https://img.shields.io/badge/go-1.9.2-blue.svg)](https://github.com/moovweb/gvm)
[![Rocket.Chat](https://demo.rocket.chat/images/join-chat.svg)](https://cosmos.rocket.chat/)
[![riot.im](https://img.shields.io/badge/riot.im-JOIN%20CHAT-green.svg)](https://riot.im/app/#/room/#tendermint:matrix.org)
[![license](https://img.shields.io/github/license/tendermint/tendermint.svg)](https://github.com/tendermint/tendermint/blob/master/LICENSE)
[![](https://tokei.rs/b1/github/tendermint/tendermint?category=lines)](https://github.com/tendermint/tendermint)
@@ -19,12 +19,28 @@ Branch | Tests | Coverage
master | [![CircleCI](https://circleci.com/gh/tendermint/tendermint/tree/master.svg?style=shield)](https://circleci.com/gh/tendermint/tendermint/tree/master) | [![codecov](https://codecov.io/gh/tendermint/tendermint/branch/master/graph/badge.svg)](https://codecov.io/gh/tendermint/tendermint)
develop | [![CircleCI](https://circleci.com/gh/tendermint/tendermint/tree/develop.svg?style=shield)](https://circleci.com/gh/tendermint/tendermint/tree/develop) | [![codecov](https://codecov.io/gh/tendermint/tendermint/branch/develop/graph/badge.svg)](https://codecov.io/gh/tendermint/tendermint)
_NOTE: This is alpha software. Please contact us if you intend to run it in production._
Tendermint Core is Byzantine Fault Tolerant (BFT) middleware that takes a state transition machine - written in any programming language -
and 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).
## A Note on Production Readiness
While Tendermint is being used in production in private, permissioned
environments, we are still working actively to harden and audit it in preparation
for use in public blockchains, such as the [Cosmos Network](https://cosmos.network/).
We are also still making breaking changes to the protocol and the APIs.
Thus we tag the releases as *alpha software*.
In any case, if you intend to run Tendermint in production,
please [contact us](https://riot.im/app/#/room/#tendermint:matrix.org) :)
## 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 +50,25 @@ 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 details about the blockchain data structures and the p2p protocols, see the
the [Tendermint specification](/docs/spec).
For details on using the software, [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 +83,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 +107,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

@@ -32,7 +32,7 @@ func BenchmarkEncodeStatusWire(b *testing.B) {
LatestBlockTime: time.Unix(0, 1234),
},
ValidatorInfo: ctypes.ValidatorInfo{
PubKey: nodeKey.PubKey(),
PubKey: nodeKey.PubKey(),
},
}
b.StartTimer()

View File

@@ -11,7 +11,7 @@ import (
)
func main() {
wsc := rpcclient.NewWSClient("127.0.0.1:46657", "/websocket")
wsc := rpcclient.NewWSClient("127.0.0.1:26657", "/websocket")
err := wsc.Start()
if err != nil {
cmn.Exit(err.Error())

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,6 +1,7 @@
package blockchain
import (
"net"
"testing"
cmn "github.com/tendermint/tmlibs/common"
@@ -35,7 +36,7 @@ func newBlockchainReactor(logger log.Logger, maxBlockHeight int64) *BlockchainRe
fastSync := true
var nilApp proxy.AppConnConsensus
blockExec := sm.NewBlockExecutor(dbm.NewMemDB(), log.TestingLogger(), nilApp,
types.MockMempool{}, types.MockEvidencePool{})
sm.MockMempool{}, sm.MockEvidencePool{})
bcReactor := NewBlockchainReactor(state.Copy(), blockExec, blockStore, fastSync)
bcReactor.SetLogger(logger.With("module", "blockchain"))
@@ -204,3 +205,4 @@ 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

@@ -97,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,

View File

@@ -8,12 +8,12 @@ import (
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/log"
priv_val "github.com/tendermint/tendermint/types/priv_validator"
"github.com/tendermint/tendermint/privval"
)
func main() {
var (
addr = flag.String("addr", ":46659", "Address of client to connect to")
addr = flag.String("addr", ":26659", "Address of client to connect to")
chainID = flag.String("chain-id", "mychain", "chain id")
privValPath = flag.String("priv", "", "priv val file path")
@@ -30,13 +30,13 @@ func main() {
"privPath", *privValPath,
)
privVal := priv_val.LoadFilePV(*privValPath)
pv := privval.LoadFilePV(*privValPath)
rs := priv_val.NewRemoteSigner(
rs := privval.NewRemoteSigner(
logger,
*chainID,
*addr,
privVal,
pv,
crypto.GenPrivKeyEd25519(),
)
err := rs.Start()

View File

@@ -5,7 +5,7 @@ import (
"github.com/spf13/cobra"
pvm "github.com/tendermint/tendermint/types/priv_validator"
"github.com/tendermint/tendermint/privval"
)
// GenValidatorCmd allows the generation of a keypair for a
@@ -17,7 +17,7 @@ var GenValidatorCmd = &cobra.Command{
}
func genValidator(cmd *cobra.Command, args []string) {
pv := pvm.GenFilePV("")
pv := privval.GenFilePV("")
jsbz, err := cdc.MarshalJSON(pv)
if err != nil {
panic(err)

View File

@@ -1,12 +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/privval"
"github.com/tendermint/tendermint/types"
pvm "github.com/tendermint/tendermint/types/priv_validator"
cmn "github.com/tendermint/tmlibs/common"
)
@@ -24,12 +26,12 @@ func initFiles(cmd *cobra.Command, args []string) error {
func initFilesWithConfig(config *cfg.Config) error {
// private validator
privValFile := config.PrivValidatorFile()
var pv *pvm.FilePV
var pv *privval.FilePV
if cmn.FileExists(privValFile) {
pv = pvm.LoadFilePV(privValFile)
pv = privval.LoadFilePV(privValFile)
logger.Info("Found private validator", "path", privValFile)
} else {
pv = pvm.GenFilePV(privValFile)
pv = privval.GenFilePV(privValFile)
pv.Save()
logger.Info("Generated private validator", "path", privValFile)
}
@@ -50,7 +52,8 @@ func initFilesWithConfig(config *cfg.Config) error {
logger.Info("Found genesis file", "path", genFile)
} else {
genDoc := types.GenesisDoc{
ChainID: cmn.Fmt("test-chain-%v", cmn.RandStr(6)),
ChainID: cmn.Fmt("test-chain-%v", cmn.RandStr(6)),
GenesisTime: time.Now(),
}
genDoc.Validators = []types.GenesisValidator{{
PubKey: pv.GetPubKey(),

View File

@@ -35,7 +35,7 @@ var (
func init() {
LiteCmd.Flags().StringVar(&listenAddr, "laddr", "tcp://localhost:8888", "Serve the proxy on the given address")
LiteCmd.Flags().StringVar(&nodeAddr, "node", "tcp://localhost:46657", "Connect to a Tendermint node at this address")
LiteCmd.Flags().StringVar(&nodeAddr, "node", "tcp://localhost:26657", "Connect to a Tendermint node at this address")
LiteCmd.Flags().StringVar(&chainID, "chain-id", "tendermint", "Specify the Tendermint chain ID")
LiteCmd.Flags().StringVar(&home, "home-dir", ".tendermint-lite", "Specify the home directory")
}

View File

@@ -5,7 +5,7 @@ import (
"github.com/spf13/cobra"
pvm "github.com/tendermint/tendermint/types/priv_validator"
"github.com/tendermint/tendermint/privval"
"github.com/tendermint/tmlibs/log"
)
@@ -13,32 +13,21 @@ import (
// instance.
var ResetAllCmd = &cobra.Command{
Use: "unsafe_reset_all",
Short: "(unsafe) Remove all the data and WAL, reset this node's validator",
Short: "(unsafe) Remove all the data and WAL, reset this node's validator to genesis state",
Run: resetAll,
}
// ResetPrivValidatorCmd resets the private validator files.
var ResetPrivValidatorCmd = &cobra.Command{
Use: "unsafe_reset_priv_validator",
Short: "(unsafe) Reset this node's validator",
Short: "(unsafe) Reset this node's validator to genesis state",
Run: resetPrivValidator,
}
// ResetAll removes the privValidator files.
// Exported so other CLI tools can use it.
func ResetAll(dbDir, privValFile string, logger log.Logger) {
resetFilePV(privValFile, logger)
if err := os.RemoveAll(dbDir); err != nil {
logger.Error("Error removing directory", "err", err)
return
}
logger.Info("Removed all data", "dir", dbDir)
}
// XXX: this is totally unsafe.
// it's only suitable for testnets.
func resetAll(cmd *cobra.Command, args []string) {
ResetAll(config.DBDir(), config.PrivValidatorFile(), logger)
ResetAll(config.DBDir(), config.P2P.AddrBookFile(), config.PrivValidatorFile(), logger)
}
// XXX: this is totally unsafe.
@@ -47,15 +36,34 @@ func resetPrivValidator(cmd *cobra.Command, args []string) {
resetFilePV(config.PrivValidatorFile(), logger)
}
func resetFilePV(privValFile string, logger log.Logger) {
// Get PrivValidator
if _, err := os.Stat(privValFile); err == nil {
pv := pvm.LoadFilePV(privValFile)
pv.Reset()
logger.Info("Reset PrivValidator", "file", privValFile)
// ResetAll removes the privValidator and address book files plus all data.
// Exported so other CLI tools can use it.
func ResetAll(dbDir, addrBookFile, privValFile string, logger log.Logger) {
resetFilePV(privValFile, logger)
removeAddrBook(addrBookFile, logger)
if err := os.RemoveAll(dbDir); err == nil {
logger.Info("Removed all blockchain history", "dir", dbDir)
} else {
pv := pvm.GenFilePV(privValFile)
pv.Save()
logger.Info("Generated PrivValidator", "file", privValFile)
logger.Error("Error removing all blockchain history", "dir", dbDir, "err", err)
}
}
func resetFilePV(privValFile string, logger log.Logger) {
if _, err := os.Stat(privValFile); err == nil {
pv := privval.LoadFilePV(privValFile)
pv.Reset()
logger.Info("Reset private validator file to genesis state", "file", privValFile)
} else {
pv := privval.GenFilePV(privValFile)
pv.Save()
logger.Info("Generated private validator file", "file", privValFile)
}
}
func removeAddrBook(addrBookFile string, logger log.Logger) {
if err := os.Remove(addrBookFile); err == nil {
logger.Info("Removed existing address book", "file", addrBookFile)
} else if !os.IsNotExist(err) {
logger.Info("Error removing address book", "file", addrBookFile, "err", err)
}
}

View File

@@ -6,7 +6,6 @@ import (
"github.com/spf13/cobra"
"github.com/tendermint/tendermint/p2p"
)
// ShowNodeIDCmd dumps node's ID to the standard output.

View File

@@ -2,9 +2,10 @@ package commands
import (
"fmt"
"github.com/spf13/cobra"
privval "github.com/tendermint/tendermint/types/priv_validator"
"github.com/tendermint/tendermint/privval"
)
// ShowValidatorCmd adds capabilities for showing the validator info.

View File

@@ -12,8 +12,8 @@ import (
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/privval"
"github.com/tendermint/tendermint/types"
pvm "github.com/tendermint/tendermint/types/priv_validator"
cmn "github.com/tendermint/tmlibs/common"
)
@@ -46,10 +46,10 @@ func init() {
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, ...)")
"Hostname prefix (node results in persistent peers list ID0@node0:26656, ID1@node1:26656, ...)")
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,
"Starting IP address (192.168.0.1 results in persistent peers list ID0@192.168.0.1:26656, ID1@192.168.0.2:26656, ...)")
TestnetFilesCmd.Flags().IntVar(&p2pPort, "p2p-port", 26656,
"P2P Port")
}
@@ -57,7 +57,18 @@ func init() {
var TestnetFilesCmd = &cobra.Command{
Use: "testnet",
Short: "Initialize files for a Tendermint testnet",
RunE: 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) error {
@@ -78,7 +89,7 @@ func testnetFiles(cmd *cobra.Command, args []string) error {
initFilesWithConfig(config)
pvFile := filepath.Join(nodeDir, config.BaseConfig.PrivValidator)
pv := pvm.LoadFilePV(pvFile)
pv := privval.LoadFilePV(pvFile)
genVals[i] = types.GenesisValidator{
PubKey: pv.GetPubKey(),
Power: 1,
@@ -162,6 +173,7 @@ func populatePersistentPeersInConfigAndWriteIt(config *cfg.Config) error {
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)

View File

@@ -7,6 +7,13 @@ import (
"time"
)
const (
// FuzzModeDrop is a mode in which we randomly drop reads/writes, connections or sleep
FuzzModeDrop = iota
// FuzzModeDelay is a mode in which we randomly sleep
FuzzModeDelay
)
// NOTE: Most of the structs & relevant comments + the
// default configuration options were used to manually
// generate the config.toml. Please reflect any changes
@@ -144,7 +151,7 @@ func DefaultBaseConfig() BaseConfig {
PrivValidator: defaultPrivValPath,
NodeKey: defaultNodeKeyPath,
Moniker: defaultMoniker,
ProxyApp: "tcp://127.0.0.1:46658",
ProxyApp: "tcp://127.0.0.1:26658",
ABCI: "socket",
LogLevel: DefaultPackageLogLevels(),
ProfListenAddress: "",
@@ -221,7 +228,7 @@ type RPCConfig struct {
// DefaultRPCConfig returns a default configuration for the RPC server
func DefaultRPCConfig() *RPCConfig {
return &RPCConfig{
ListenAddress: "tcp://0.0.0.0:46657",
ListenAddress: "tcp://0.0.0.0:26657",
GRPCListenAddress: "",
Unsafe: false,
}
@@ -287,17 +294,29 @@ type P2PConfig struct {
// Does not work if the peer-exchange reactor is disabled.
SeedMode bool `mapstructure:"seed_mode"`
// Authenticated encryption
AuthEnc bool `mapstructure:"auth_enc"`
// Comma separated list of peer IDs to keep private (will not be gossiped to other peers)
// 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"`
// Peer connection configuration.
HandshakeTimeout time.Duration `mapstructure:"handshake_timeout"`
DialTimeout time.Duration `mapstructure:"dial_timeout"`
// Testing params.
// Force dial to fail
TestDialFail bool `mapstructure:"test_dial_fail"`
// FUzz connection
TestFuzz bool `mapstructure:"test_fuzz"`
TestFuzzConfig *FuzzConnConfig `mapstructure:"test_fuzz_config"`
}
// DefaultP2PConfig returns a default configuration for the peer-to-peer layer
func DefaultP2PConfig() *P2PConfig {
return &P2PConfig{
ListenAddress: "tcp://0.0.0.0:46656",
ListenAddress: "tcp://0.0.0.0:26656",
AddrBook: defaultAddrBookPath,
AddrBookStrict: true,
MaxNumPeers: 50,
@@ -307,7 +326,12 @@ func DefaultP2PConfig() *P2PConfig {
RecvRate: 512000, // 500 kB/s
PexReactor: true,
SeedMode: false,
AuthEnc: true,
AllowDuplicateIP: true, // so non-breaking yet
HandshakeTimeout: 20 * time.Second,
DialTimeout: 3 * time.Second,
TestDialFail: false,
TestFuzz: false,
TestFuzzConfig: DefaultFuzzConnConfig(),
}
}
@@ -317,6 +341,7 @@ func TestP2PConfig() *P2PConfig {
cfg.ListenAddress = "tcp://0.0.0.0:36656"
cfg.SkipUPNP = true
cfg.FlushThrottleTimeout = 10
cfg.AllowDuplicateIP = true
return cfg
}
@@ -325,6 +350,26 @@ func (cfg *P2PConfig) AddrBookFile() string {
return rootify(cfg.AddrBook, cfg.RootDir)
}
// FuzzConnConfig is a FuzzedConnection configuration.
type FuzzConnConfig struct {
Mode int
MaxDelay time.Duration
ProbDropRW float64
ProbDropConn float64
ProbSleep float64
}
// DefaultFuzzConnConfig returns the default config.
func DefaultFuzzConnConfig() *FuzzConnConfig {
return &FuzzConnConfig{
Mode: FuzzModeDrop,
MaxDelay: 3 * time.Second,
ProbDropRW: 0.2,
ProbDropConn: 0.00,
ProbSleep: 0.00,
}
}
//-----------------------------------------------------------------------------
// MempoolConfig
@@ -335,6 +380,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 +391,7 @@ func DefaultMempoolConfig() *MempoolConfig {
RecheckEmpty: true,
Broadcast: true,
WalPath: filepath.Join(defaultDataDir, "mempool.wal"),
Size: 100000,
CacheSize: 100000,
}
}
@@ -367,10 +414,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 +447,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,13 +37,13 @@ func EnsureRoot(rootDir string) {
// Write default config file if missing.
if !cmn.FileExists(configFilePath) {
writeDefaultCondigFile(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 writeDefaultCondigFile(configFilePath string) {
func writeDefaultConfigFile(configFilePath string) {
WriteConfigFile(configFilePath, DefaultConfig())
}
@@ -81,7 +81,7 @@ fast_sync = {{ .BaseConfig.FastSync }}
db_backend = "{{ .BaseConfig.DBBackend }}"
# Database directory
db_path = "{{ .BaseConfig.DBPath }}"
db_path = "{{ js .BaseConfig.DBPath }}"
# Output level for logging, including package level options
log_level = "{{ .BaseConfig.LogLevel }}"
@@ -89,13 +89,13 @@ log_level = "{{ .BaseConfig.LogLevel }}"
##### additional base config options #####
# Path to the JSON file containing the initial validator set and other meta data
genesis_file = "{{ .BaseConfig.Genesis }}"
genesis_file = "{{ js .BaseConfig.Genesis }}"
# Path to the JSON file containing the private key to use as a validator in the consensus protocol
priv_validator_file = "{{ .BaseConfig.PrivValidator }}"
priv_validator_file = "{{ js .BaseConfig.PrivValidator }}"
# Path to the JSON file containing the private key to use for node authentication in the p2p protocol
node_key_file = "{{ .BaseConfig.NodeKey}}"
node_key_file = "{{ js .BaseConfig.NodeKey}}"
# Mechanism to connect to the ABCI application: socket | grpc
abci = "{{ .BaseConfig.ABCI }}"
@@ -136,7 +136,7 @@ seeds = "{{ .P2P.Seeds }}"
persistent_peers = "{{ .P2P.PersistentPeers }}"
# Path to address book
addr_book_file = "{{ .P2P.AddrBook }}"
addr_book_file = "{{ js .P2P.AddrBook }}"
# Set true for strict address routability rules
addr_book_strict = {{ .P2P.AddrBookStrict }}
@@ -165,9 +165,6 @@ pex = {{ .P2P.PexReactor }}
# Does not work if the peer-exchange reactor is disabled.
seed_mode = {{ .P2P.SeedMode }}
# Authenticated encryption
auth_enc = {{ .P2P.AuthEnc }}
# Comma separated list of peer IDs to keep private (will not be gossiped to other peers)
private_peer_ids = "{{ .P2P.PrivatePeerIDs }}"
@@ -177,13 +174,18 @@ private_peer_ids = "{{ .P2P.PrivatePeerIDs }}"
recheck = {{ .Mempool.Recheck }}
recheck_empty = {{ .Mempool.RecheckEmpty }}
broadcast = {{ .Mempool.Broadcast }}
wal_dir = "{{ .Mempool.WalPath }}"
wal_dir = "{{ js .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 }}
wal_file = "{{ js .Consensus.WalPath }}"
# All timeouts are in milliseconds
timeout_propose = {{ .Consensus.TimeoutPropose }}
@@ -267,7 +269,7 @@ func ResetTestRoot(testName string) *Config {
// Write default config file if missing.
if !cmn.FileExists(configFilePath) {
writeDefaultCondigFile(configFilePath)
writeDefaultConfigFile(configFilePath)
}
if !cmn.FileExists(genesisFilePath) {
cmn.MustWriteFile(genesisFilePath, []byte(testGenesis), 0644)

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

@@ -27,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
@@ -58,14 +58,11 @@ func TestByzantine(t *testing.T) {
css[i].doPrevote = func(height int64, round int) {}
}
eventBus := types.NewEventBus()
eventBus := css[i].eventBus
eventBus.SetLogger(logger.With("module", "events", "validator", i))
err := eventBus.Start()
require.NoError(t, err)
defer eventBus.Stop()
eventChans[i] = make(chan interface{}, 1)
err = eventBus.Subscribe(context.Background(), testSubscriber, types.EventQueryNewBlock, eventChans[i])
err := eventBus.Subscribe(context.Background(), testSubscriber, types.EventQueryNewBlock, eventChans[i])
require.NoError(t, err)
conR := NewConsensusReactor(css[i], true) // so we dont start the consensus states
@@ -105,15 +102,18 @@ func TestByzantine(t *testing.T) {
p2p.Connect2Switches(sws, i, j)
})
// start the state machines
byzR := reactors[0].(*ByzantineReactor)
s := byzR.reactor.conS.GetState()
byzR.reactor.SwitchToConsensus(s, 0)
// start the non-byz state machines.
// note these must be started before the byz
for i := 1; i < N; i++ {
cr := reactors[i].(*ConsensusReactor)
cr.SwitchToConsensus(cr.conS.GetState(), 0)
}
// start the byzantine state machine
byzR := reactors[0].(*ByzantineReactor)
s := byzR.reactor.conS.GetState()
byzR.reactor.SwitchToConsensus(s, 0)
// byz proposer sends one block to peers[0]
// and the other block to peers[1] and peers[2].
// note peers and switches order don't match.

View File

@@ -19,9 +19,9 @@ import (
cstypes "github.com/tendermint/tendermint/consensus/types"
mempl "github.com/tendermint/tendermint/mempool"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/privval"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/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"
@@ -262,9 +262,9 @@ func newConsensusStateWithConfigAndBlockStore(thisConfig *cfg.Config, state sm.S
}
// mock the evidence pool
evpool := types.MockEvidencePool{}
evpool := sm.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)
@@ -278,10 +278,10 @@ func newConsensusStateWithConfigAndBlockStore(thisConfig *cfg.Config, state sm.S
return cs
}
func loadPrivValidator(config *cfg.Config) *pvm.FilePV {
func loadPrivValidator(config *cfg.Config) *privval.FilePV {
privValidatorFile := config.PrivValidatorFile()
ensureDir(path.Dir(privValidatorFile), 0700)
privValidator := pvm.LoadOrGenFilePV(privValidatorFile)
privValidator := privval.LoadOrGenFilePV(privValidatorFile)
privValidator.Reset()
return privValidator
}
@@ -379,7 +379,7 @@ func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerF
privVal = privVals[i]
} else {
_, tempFilePath := cmn.Tempfile("priv_validator_")
privVal = pvm.GenFilePV(tempFilePath)
privVal = privval.GenFilePV(tempFilePath)
}
app := appFunc()

View File

@@ -1,7 +1,6 @@
package consensus
import (
"context"
"fmt"
"reflect"
"sync"
@@ -14,6 +13,7 @@ import (
"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"
@@ -43,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,
@@ -53,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()
@@ -75,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()
}
@@ -101,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
}
}
@@ -210,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
@@ -345,77 +347,40 @@ 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.EventDataRoundState)
conR.broadcastNewRoundStep(edrs.RoundState.(*cstypes.RoundState))
}
case data, ok = <-votesCh:
if ok {
edv := data.(types.EventDataVote)
conR.broadcastHasVoteMessage(edv.Vote)
}
case data, ok = <-heartbeatsCh:
if ok {
edph := data.(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, 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, cdc.MustMarshalBinaryBare(nrsMsg))
@@ -696,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 {
@@ -720,6 +702,7 @@ func (conR *ConsensusReactor) gossipVotesForHeight(logger log.Logger, rs *cstype
}
}
}
return false
}
@@ -840,41 +823,42 @@ var (
// 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{},
}
}
@@ -891,16 +875,16 @@ func (ps *PeerState) GetRoundState() *cstypes.PeerRoundState {
ps.mtx.Lock()
defer ps.mtx.Unlock()
prs := ps.PeerRoundState // copy
prs := ps.PRS // copy
return &prs
}
// GetRoundStateJSON returns a json of PeerRoundState, marshalled using go-amino.
func (ps *PeerState) GetRoundStateJSON() ([]byte, error) {
// 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.PeerRoundState)
return cdc.MarshalJSON(ps)
}
// GetHeight returns an atomic snapshot of the PeerRoundState's height
@@ -908,7 +892,7 @@ func (ps *PeerState) GetRoundStateJSON() ([]byte, error) {
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.
@@ -916,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.
@@ -935,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.
@@ -948,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.
@@ -961,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, cdc.MustMarshalBinaryBare(msg))
return ps.peer.Send(VoteChannel, cdc.MustMarshalBinaryBare(msg))
}
return false
}
@@ -1001,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
@@ -1044,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
}
/*
@@ -1054,14 +1038,14 @@ 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)
}
}
@@ -1076,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)
}
}
}
@@ -1103,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
@@ -1117,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
@@ -1128,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
@@ -1143,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
@@ -1155,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.
@@ -1171,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
}
}
@@ -1224,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.
@@ -1237,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.
@@ -1254,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
}
@@ -1292,13 +1276,13 @@ 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)
}

View File

@@ -254,7 +254,8 @@ func TestReactorVotingPowerChange(t *testing.T) {
logger.Debug("---------------------------- Testing changing the voting power of one validator a few times")
val1PubKey := css[0].privValidator.GetPubKey()
updateValidatorTx := kvstore.MakeValSetChangeTx(val1PubKey.Bytes(), 25)
val1PubKeyABCI := types.TM2PB.PubKey(val1PubKey)
updateValidatorTx := kvstore.MakeValSetChangeTx(val1PubKeyABCI, 25)
previousTotalVotingPower := css[0].GetRoundState().LastValidators.TotalVotingPower()
waitForAndValidateBlock(t, nVals, activeVals, eventChans, css, updateValidatorTx)
@@ -266,7 +267,7 @@ func TestReactorVotingPowerChange(t *testing.T) {
t.Fatalf("expected voting power to change (before: %d, after: %d)", previousTotalVotingPower, css[0].GetRoundState().LastValidators.TotalVotingPower())
}
updateValidatorTx = kvstore.MakeValSetChangeTx(val1PubKey.Bytes(), 2)
updateValidatorTx = kvstore.MakeValSetChangeTx(val1PubKeyABCI, 2)
previousTotalVotingPower = css[0].GetRoundState().LastValidators.TotalVotingPower()
waitForAndValidateBlock(t, nVals, activeVals, eventChans, css, updateValidatorTx)
@@ -278,7 +279,7 @@ func TestReactorVotingPowerChange(t *testing.T) {
t.Fatalf("expected voting power to change (before: %d, after: %d)", previousTotalVotingPower, css[0].GetRoundState().LastValidators.TotalVotingPower())
}
updateValidatorTx = kvstore.MakeValSetChangeTx(val1PubKey.Bytes(), 26)
updateValidatorTx = kvstore.MakeValSetChangeTx(val1PubKeyABCI, 26)
previousTotalVotingPower = css[0].GetRoundState().LastValidators.TotalVotingPower()
waitForAndValidateBlock(t, nVals, activeVals, eventChans, css, updateValidatorTx)
@@ -316,7 +317,8 @@ func TestReactorValidatorSetChanges(t *testing.T) {
logger.Info("---------------------------- Testing adding one validator")
newValidatorPubKey1 := css[nVals].privValidator.GetPubKey()
newValidatorTx1 := kvstore.MakeValSetChangeTx(newValidatorPubKey1.Bytes(), testMinPower)
valPubKey1ABCI := types.TM2PB.PubKey(newValidatorPubKey1)
newValidatorTx1 := kvstore.MakeValSetChangeTx(valPubKey1ABCI, testMinPower)
// wait till everyone makes block 2
// ensure the commit includes all validators
@@ -342,7 +344,8 @@ func TestReactorValidatorSetChanges(t *testing.T) {
logger.Info("---------------------------- Testing changing the voting power of one validator")
updateValidatorPubKey1 := css[nVals].privValidator.GetPubKey()
updateValidatorTx1 := kvstore.MakeValSetChangeTx(updateValidatorPubKey1.Bytes(), 25)
updatePubKey1ABCI := types.TM2PB.PubKey(updateValidatorPubKey1)
updateValidatorTx1 := kvstore.MakeValSetChangeTx(updatePubKey1ABCI, 25)
previousTotalVotingPower := css[nVals].GetRoundState().LastValidators.TotalVotingPower()
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css, updateValidatorTx1)
@@ -358,10 +361,12 @@ func TestReactorValidatorSetChanges(t *testing.T) {
logger.Info("---------------------------- Testing adding two validators at once")
newValidatorPubKey2 := css[nVals+1].privValidator.GetPubKey()
newValidatorTx2 := kvstore.MakeValSetChangeTx(newValidatorPubKey2.Bytes(), testMinPower)
newVal2ABCI := types.TM2PB.PubKey(newValidatorPubKey2)
newValidatorTx2 := kvstore.MakeValSetChangeTx(newVal2ABCI, testMinPower)
newValidatorPubKey3 := css[nVals+2].privValidator.GetPubKey()
newValidatorTx3 := kvstore.MakeValSetChangeTx(newValidatorPubKey3.Bytes(), testMinPower)
newVal3ABCI := types.TM2PB.PubKey(newValidatorPubKey3)
newValidatorTx3 := kvstore.MakeValSetChangeTx(newVal3ABCI, testMinPower)
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css, newValidatorTx2, newValidatorTx3)
waitForAndValidateBlockWithTx(t, nPeers, activeVals, eventChans, css, newValidatorTx2, newValidatorTx3)
@@ -373,8 +378,8 @@ func TestReactorValidatorSetChanges(t *testing.T) {
//---------------------------------------------------------------------------
logger.Info("---------------------------- Testing removing two validators at once")
removeValidatorTx2 := kvstore.MakeValSetChangeTx(newValidatorPubKey2.Bytes(), 0)
removeValidatorTx3 := kvstore.MakeValSetChangeTx(newValidatorPubKey3.Bytes(), 0)
removeValidatorTx2 := kvstore.MakeValSetChangeTx(newVal2ABCI, 0)
removeValidatorTx3 := kvstore.MakeValSetChangeTx(newVal3ABCI, 0)
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css, removeValidatorTx2, removeValidatorTx3)
waitForAndValidateBlockWithTx(t, nPeers, activeVals, eventChans, css, removeValidatorTx2, removeValidatorTx3)

View File

@@ -2,7 +2,6 @@ package consensus
import (
"bytes"
"encoding/json"
"fmt"
"hash/crc32"
"io"
@@ -26,20 +25,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 +92,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 +119,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,29 +186,30 @@ 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
initialState sm.State
store types.BlockStore
appState json.RawMessage
store sm.BlockStore
genDoc *types.GenesisDoc
logger log.Logger
nBlocks int // number of blocks applied to the state
}
func NewHandshaker(stateDB dbm.DB, state sm.State,
store types.BlockStore, appState json.RawMessage) *Handshaker {
store sm.BlockStore, genDoc *types.GenesisDoc) *Handshaker {
return &Handshaker{
stateDB: stateDB,
initialState: state,
store: store,
appState: appState,
genDoc: genDoc,
logger: log.NewNopLogger(),
nBlocks: 0,
}
@@ -220,7 +225,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 +240,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 +257,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()
@@ -260,14 +267,33 @@ func (h *Handshaker) ReplayBlocks(state sm.State, appHash []byte, appBlockHeight
// If appBlockHeight == 0 it means that we are at genesis and hence should send InitChain
if appBlockHeight == 0 {
validators := types.TM2PB.Validators(state.Validators)
csParams := types.TM2PB.ConsensusParams(h.genDoc.ConsensusParams)
req := abci.RequestInitChain{
Validators: validators,
AppStateBytes: h.appState,
Time: h.genDoc.GenesisTime.Unix(), // TODO
ChainId: h.genDoc.ChainID,
ConsensusParams: csParams,
Validators: validators,
AppStateBytes: h.genDoc.AppStateJSON,
}
_, err := proxyApp.Consensus().InitChainSync(req)
res, err := proxyApp.Consensus().InitChainSync(req)
if err != nil {
return nil, err
}
// if the app returned validators
// or consensus params, update the state
// with the them
if len(res.Validators) > 0 {
vals, err := types.PB2TM.Validators(res.Validators)
if err != nil {
return nil, err
}
state.Validators = types.NewValidatorSet(vals)
}
if res.ConsensusParams != nil {
state.ConsensusParams = types.PB2TM.ConsensusParams(res.ConsensusParams)
}
sm.SaveState(h.stateDB, state)
}
// First handle edge cases and constraints on the storeBlockHeight
@@ -314,7 +340,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
@@ -357,7 +383,7 @@ func (h *Handshaker) replayBlocks(state sm.State, proxyApp proxy.AppConns, appBl
for i := appBlockHeight + 1; i <= finalBlock; i++ {
h.logger.Info("Applying block", "height", i)
block := h.store.LoadBlock(i)
appHash, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, h.logger)
appHash, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, h.logger, state.LastValidators, h.stateDB)
if err != nil {
return nil, err
}
@@ -382,7 +408,7 @@ func (h *Handshaker) replayBlock(state sm.State, height int64, proxyApp proxy.Ap
block := h.store.LoadBlock(height)
meta := h.store.LoadBlockMeta(height)
blockExec := sm.NewBlockExecutor(h.stateDB, h.logger, proxyApp, types.MockMempool{}, types.MockEvidencePool{})
blockExec := sm.NewBlockExecutor(h.stateDB, h.logger, proxyApp, sm.MockMempool{}, sm.MockEvidencePool{})
var err error
state, err = blockExec.ApplyBlock(state, meta.BlockID, block)

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])
@@ -298,7 +299,7 @@ func newConsensusStateForReplay(config cfg.BaseConfig, csConfig *cfg.ConsensusCo
// Create proxyAppConn connection (consensus, mempool, query)
clientCreator := proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir())
proxyApp := proxy.NewAppConns(clientCreator,
NewHandshaker(stateDB, state, blockStore, gdoc.AppState()))
NewHandshaker(stateDB, state, blockStore, gdoc))
err = proxyApp.Start()
if err != nil {
cmn.Exit(cmn.Fmt("Error starting proxy app conns: %v", err))
@@ -309,7 +310,7 @@ func newConsensusStateForReplay(config cfg.BaseConfig, csConfig *cfg.ConsensusCo
cmn.Exit(cmn.Fmt("Failed to start event bus: %v", err))
}
mempool, evpool := types.MockMempool{}, types.MockEvidencePool{}
mempool, evpool := sm.MockMempool{}, sm.MockEvidencePool{}
blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mempool, evpool)
consensusState := NewConsensusState(csConfig, state.Copy(), blockExec,

View File

@@ -13,20 +13,21 @@ import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/abci/example/kvstore"
abci "github.com/tendermint/abci/types"
"github.com/tendermint/go-crypto"
crypto "github.com/tendermint/go-crypto"
auto "github.com/tendermint/tmlibs/autofile"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/privval"
"github.com/tendermint/tendermint/proxy"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
pvm "github.com/tendermint/tendermint/types/priv_validator"
"github.com/tendermint/tmlibs/log"
)
@@ -218,15 +219,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 +239,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)
@@ -259,8 +264,8 @@ const (
)
var (
mempool = types.MockMempool{}
evpool = types.MockEvidencePool{}
mempool = sm.MockMempool{}
evpool = sm.MockEvidencePool{}
)
//---------------------------------------
@@ -325,9 +330,9 @@ func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) {
walFile := tempWALWithData(walBody)
config.Consensus.SetWalFile(walFile)
privVal := pvm.LoadFilePV(config.PrivValidatorFile())
privVal := privval.LoadFilePV(config.PrivValidatorFile())
wal, err := NewWAL(walFile, false)
wal, err := NewWAL(walFile)
if err != nil {
t.Fatal(err)
}
@@ -362,7 +367,8 @@ func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) {
}
// now start the app using the handshake - it should sync
handshaker := NewHandshaker(stateDB, state, store, nil)
genDoc, _ := sm.MakeGenesisDocFromFile(config.GenesisFile())
handshaker := NewHandshaker(stateDB, state, store, genDoc)
proxyApp := proxy.NewAppConns(clientCreator2, handshaker)
if err := proxyApp.Start(); err != nil {
t.Fatalf("Error starting proxy app connections: %v", err)
@@ -412,10 +418,10 @@ func buildAppStateFromChain(proxyApp proxy.AppConns, stateDB dbm.DB,
}
defer proxyApp.Stop()
// TODO: get the genesis bytes (https://github.com/tendermint/tendermint/issues/1224)
var genesisBytes []byte
validators := types.TM2PB.Validators(state.Validators)
if _, err := proxyApp.Consensus().InitChainSync(abci.RequestInitChain{validators, genesisBytes}); err != nil {
if _, err := proxyApp.Consensus().InitChainSync(abci.RequestInitChain{
Validators: validators,
}); err != nil {
panic(err)
}
@@ -449,10 +455,10 @@ func buildTMStateFromChain(config *cfg.Config, stateDB dbm.DB, state sm.State, c
}
defer proxyApp.Stop()
// TODO: get the genesis bytes (https://github.com/tendermint/tendermint/issues/1224)
var genesisBytes []byte
validators := types.TM2PB.Validators(state.Validators)
if _, err := proxyApp.Consensus().InitChainSync(abci.RequestInitChain{validators, genesisBytes}); err != nil {
if _, err := proxyApp.Consensus().InitChainSync(abci.RequestInitChain{
Validators: validators,
}); err != nil {
panic(err)
}
@@ -538,7 +544,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
}
@@ -629,3 +635,53 @@ func (bs *mockBlockStore) LoadBlockCommit(height int64) *types.Commit {
func (bs *mockBlockStore) LoadSeenCommit(height int64) *types.Commit {
return bs.commits[height-1]
}
//----------------------------------------
func TestInitChainUpdateValidators(t *testing.T) {
val, _ := types.RandValidator(true, 10)
vals := types.NewValidatorSet([]*types.Validator{val})
app := &initChainApp{vals: types.TM2PB.Validators(vals)}
clientCreator := proxy.NewLocalClientCreator(app)
config := ResetConfig("proxy_test_")
privVal := privval.LoadFilePV(config.PrivValidatorFile())
stateDB, state, store := stateAndStore(config, privVal.GetPubKey())
oldValAddr := state.Validators.Validators[0].Address
// now start the app using the handshake - it should sync
genDoc, _ := sm.MakeGenesisDocFromFile(config.GenesisFile())
handshaker := NewHandshaker(stateDB, state, store, genDoc)
proxyApp := proxy.NewAppConns(clientCreator, handshaker)
if err := proxyApp.Start(); err != nil {
t.Fatalf("Error starting proxy app connections: %v", err)
}
defer proxyApp.Stop()
// reload the state, check the validator set was updated
state = sm.LoadState(stateDB)
newValAddr := state.Validators.Validators[0].Address
expectValAddr := val.Address
assert.NotEqual(t, oldValAddr, newValAddr)
assert.Equal(t, newValAddr, expectValAddr)
}
func newInitChainApp(vals []abci.Validator) *initChainApp {
return &initChainApp{
vals: vals,
}
}
// returns the vals on InitChain
type initChainApp struct {
abci.BaseApplication
vals []abci.Validator
}
func (ica *initChainApp) InitChain(req abci.RequestInitChain) abci.ResponseInitChain {
return abci.ResponseInitChain{
Validators: ica.vals,
}
}

View File

@@ -15,6 +15,7 @@ import (
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"
@@ -75,9 +76,9 @@ type ConsensusState struct {
// services for creating and executing blocks
// TODO: encapsulate all of this in one "BlockManager"
blockExec *sm.BlockExecutor
blockStore types.BlockStore
mempool types.Mempool
evpool types.EvidencePool
blockStore sm.BlockStore
mempool sm.Mempool
evpool sm.EvidencePool
// internal state
mtx sync.Mutex
@@ -110,10 +111,14 @@ 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.
func NewConsensusState(config *cfg.ConsensusConfig, state sm.State, blockExec *sm.BlockExecutor, blockStore types.BlockStore, mempool types.Mempool, evpool types.EvidencePool) *ConsensusState {
func NewConsensusState(config *cfg.ConsensusConfig, state sm.State, blockExec *sm.BlockExecutor, blockStore sm.BlockStore, mempool sm.Mempool, evpool sm.EvidencePool) *ConsensusState {
cs := &ConsensusState{
config: config,
blockExec: blockExec,
@@ -126,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
@@ -185,6 +191,14 @@ func (cs *ConsensusState) GetRoundStateJSON() ([]byte, error) {
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()
@@ -219,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 {
@@ -236,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
}
@@ -276,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()
@@ -293,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
@@ -441,9 +460,12 @@ func (cs *ConsensusState) updateToState(state sm.State) {
// If state isn't further out than cs.state, just ignore.
// This happens when SwitchToConsensus() is called in the reactor.
// We don't want to reset e.g. the Votes.
// We don't want to reset e.g. the Votes, but we still want to
// signal the new round step, because other services (eg. mempool)
// depend on having an up-to-date peer state!
if !cs.state.IsEmpty() && (state.LastBlockHeight <= cs.state.LastBlockHeight) {
cs.Logger.Info("Ignoring updateToState()", "newHeight", state.LastBlockHeight+1, "oldHeight", cs.state.LastBlockHeight+1)
cs.newStep()
return
}
@@ -473,6 +495,7 @@ func (cs *ConsensusState) updateToState(state sm.State) {
} else {
cs.StartTime = cs.config.Commit(cs.CommitTime)
}
cs.Validators = validators
cs.Proposal = nil
cs.ProposalBlock = nil
@@ -496,11 +519,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!
// newStep is called by updateToState in NewConsensusState before the eventBus is set!
if cs.eventBus != nil {
cs.eventBus.PublishEventNewRoundStep(rs)
cs.evsw.FireEvent(types.EventNewRoundStep, &cs.RoundState)
}
}
@@ -534,16 +558,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)
@@ -576,8 +600,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:
@@ -602,7 +627,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)
}
}
@@ -659,16 +684,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
@@ -687,6 +714,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
@@ -740,6 +768,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)
}
@@ -749,11 +778,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:
@@ -773,22 +804,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", "addr", cs.privValidator.GetAddress(), "vals", cs.Validators)
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)
}
}
@@ -951,14 +982,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:
@@ -977,12 +1010,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:
@@ -990,23 +1025,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))
@@ -1015,9 +1051,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
@@ -1031,7 +1067,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)
@@ -1040,7 +1076,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))
@@ -1070,14 +1106,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:
@@ -1092,11 +1130,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:
@@ -1119,6 +1159,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
}
@@ -1126,6 +1167,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
@@ -1138,19 +1180,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
}
@@ -1201,23 +1245,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 {
@@ -1278,23 +1327,26 @@ 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
}
@@ -1306,6 +1358,25 @@ func (cs *ConsensusState) addProposalBlockPart(height int64, part *types.Part, v
}
// 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)
@@ -1364,6 +1435,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() {
@@ -1391,38 +1463,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
@@ -1438,6 +1522,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

@@ -8,10 +8,10 @@ import (
"time"
cstypes "github.com/tendermint/tendermint/consensus/types"
tmpubsub "github.com/tendermint/tendermint/libs/pubsub"
"github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/log"
tmpubsub "github.com/tendermint/tmlibs/pubsub"
)
func init() {

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

@@ -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
@@ -55,37 +58,63 @@ func (rs RoundStepType) String() string {
// 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
}
@@ -115,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

@@ -10,7 +10,7 @@ import (
"github.com/pkg/errors"
"github.com/tendermint/go-amino"
amino "github.com/tendermint/go-amino"
"github.com/tendermint/tendermint/types"
auto "github.com/tendermint/tmlibs/autofile"
cmn "github.com/tendermint/tmlibs/common"
@@ -50,7 +50,8 @@ func RegisterWALMessages(cdc *amino.Codec) {
// 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)
@@ -67,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")
@@ -84,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)
@@ -100,40 +99,42 @@ 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
}
func (wal *baseWAL) OnStop() {
wal.BaseService.OnStop()
wal.group.Stop()
wal.group.Close()
}
// 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))
}
}
@@ -143,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
@@ -165,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
@@ -270,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)
}
@@ -308,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

@@ -13,10 +13,10 @@ import (
"github.com/tendermint/abci/example/kvstore"
bc "github.com/tendermint/tendermint/blockchain"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/privval"
"github.com/tendermint/tendermint/proxy"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
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 := pvm.LoadOrGenFilePV(privValidatorFile)
privValidator := privval.LoadOrGenFilePV(privValidatorFile)
genDoc, err := types.GenesisDocFromFile(config.GenesisFile())
if err != nil {
return nil, errors.Wrap(err, "failed to read genesis file")
@@ -52,7 +52,7 @@ func WALWithNBlocks(numBlocks int) (data []byte, err error) {
return nil, errors.Wrap(err, "failed to make genesis state")
}
blockStore := bc.NewBlockStore(blockStoreDB)
handshaker := NewHandshaker(stateDB, state, blockStore, genDoc.AppState())
handshaker := NewHandshaker(stateDB, state, blockStore, genDoc)
proxyApp := proxy.NewAppConns(proxy.NewLocalClientCreator(app), handshaker)
proxyApp.SetLogger(logger.With("module", "proxy"))
if err := proxyApp.Start(); err != nil {
@@ -65,8 +65,8 @@ func WALWithNBlocks(numBlocks int) (data []byte, err error) {
return nil, errors.Wrap(err, "failed to start event bus")
}
defer eventBus.Stop()
mempool := types.MockMempool{}
evpool := types.MockEvidencePool{}
mempool := sm.MockMempool{}
evpool := sm.MockEvidencePool{}
blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mempool, evpool)
consensusState := NewConsensusState(config.Consensus, state.Copy(), blockExec, blockStore, mempool, evpool)
consensusState.SetLogger(logger)
@@ -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 {
@@ -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

@@ -47,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)
}

View File

@@ -5,12 +5,12 @@ services:
container_name: node0
image: "tendermint/localnode"
ports:
- "46656-46657:46656-46657"
- "26656-26657:26656-26657"
environment:
- ID=0
- LOG=${LOG:-tendermint.log}
- LOG=$${LOG:-tendermint.log}
volumes:
- ${FOLDER:-./build}:/tendermint:Z
- ./build:/tendermint:Z
networks:
localnet:
ipv4_address: 192.167.10.2
@@ -19,12 +19,12 @@ services:
container_name: node1
image: "tendermint/localnode"
ports:
- "46659-46660:46656-46657"
- "26659-26660:26656-26657"
environment:
- ID=1
- LOG=${LOG:-tendermint.log}
- LOG=$${LOG:-tendermint.log}
volumes:
- ${FOLDER:-./build}:/tendermint:Z
- ./build:/tendermint:Z
networks:
localnet:
ipv4_address: 192.167.10.3
@@ -34,11 +34,11 @@ services:
image: "tendermint/localnode"
environment:
- ID=2
- LOG=${LOG:-tendermint.log}
- LOG=$${LOG:-tendermint.log}
ports:
- "46661-46662:46656-46657"
- "26661-26662:26656-26657"
volumes:
- ${FOLDER:-./build}:/tendermint:Z
- ./build:/tendermint:Z
networks:
localnet:
ipv4_address: 192.167.10.4
@@ -48,11 +48,11 @@ services:
image: "tendermint/localnode"
environment:
- ID=3
- LOG=${LOG:-tendermint.log}
- LOG=$${LOG:-tendermint.log}
ports:
- "46663-46664:46656-46657"
- "26663-26664:26656-26657"
volumes:
- ${FOLDER:-./build}:/tendermint:Z
- ./build:/tendermint:Z
networks:
localnet:
ipv4_address: 192.167.10.5

View File

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

View File

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

329
docs/abci-cli.md Normal file
View File

@@ -0,0 +1,329 @@
# Using ABCI-CLI
To facilitate testing and debugging of ABCI servers and simple apps, we
built a CLI, the `abci-cli`, for sending ABCI messages from the command
line.
## Install
Make sure you [have Go installed](https://golang.org/doc/install).
Next, install the `abci-cli` tool and example applications:
go get -u github.com/tendermint/abci/cmd/abci-cli
If this fails, you may need to use [dep](https://github.com/golang/dep)
to get vendored dependencies:
cd $GOPATH/src/github.com/tendermint/abci
make get_tools
make get_vendor_deps
make install
Now run `abci-cli` to see the list of commands:
Usage:
abci-cli [command]
Available Commands:
batch Run a batch of abci commands against an application
check_tx Validate a tx
commit Commit the application state and return the Merkle root hash
console Start an interactive abci console for multiple commands
counter ABCI demo example
deliver_tx Deliver a new tx to the application
kvstore ABCI demo example
echo Have the application echo a message
help Help about any command
info Get some info about the application
query Query the application state
set_option Set an options on the application
Flags:
--abci string socket or grpc (default "socket")
--address string address of application socket (default "tcp://127.0.0.1:26658")
-h, --help help for abci-cli
-v, --verbose print the command and results as if it were a console session
Use "abci-cli [command] --help" for more information about a command.
## KVStore - First Example
The `abci-cli` tool lets us send ABCI messages to our application, to
help build and debug them.
The most important messages are `deliver_tx`, `check_tx`, and `commit`,
but there are others for convenience, configuration, and information
purposes.
We'll start a kvstore application, which was installed at the same time
as `abci-cli` above. The kvstore just stores transactions in a merkle
tree.
Its code can be found
[here](https://github.com/tendermint/abci/blob/master/cmd/abci-cli/abci-cli.go)
and looks like:
func cmdKVStore(cmd *cobra.Command, args []string) error {
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
// Create the application - in memory or persisted to disk
var app types.Application
if flagPersist == "" {
app = kvstore.NewKVStoreApplication()
} else {
app = kvstore.NewPersistentKVStoreApplication(flagPersist)
app.(*kvstore.PersistentKVStoreApplication).SetLogger(logger.With("module", "kvstore"))
}
// Start the listener
srv, err := server.NewServer(flagAddrD, flagAbci, app)
if err != nil {
return err
}
srv.SetLogger(logger.With("module", "abci-server"))
if err := srv.Start(); err != nil {
return err
}
// Wait forever
cmn.TrapSignal(func() {
// Cleanup
srv.Stop()
})
return nil
}
Start by running:
abci-cli kvstore
And in another terminal, run
abci-cli echo hello
abci-cli info
You'll see something like:
-> data: hello
-> data.hex: 68656C6C6F
and:
-> data: {"size":0}
-> data.hex: 7B2273697A65223A307D
An ABCI application must provide two things:
- a socket server
- a handler for ABCI messages
When we run the `abci-cli` tool we open a new connection to the
application's socket server, send the given ABCI message, and wait for a
response.
The server may be generic for a particular language, and we provide a
[reference implementation in
Golang](https://github.com/tendermint/abci/tree/master/server). See the
[list of other ABCI implementations](./ecosystem.html) for servers in
other languages.
The handler is specific to the application, and may be arbitrary, so
long as it is deterministic and conforms to the ABCI interface
specification.
So when we run `abci-cli info`, we open a new connection to the ABCI
server, which calls the `Info()` method on the application, which tells
us the number of transactions in our Merkle tree.
Now, since every command opens a new connection, we provide the
`abci-cli console` and `abci-cli batch` commands, to allow multiple ABCI
messages to be sent over a single connection.
Running `abci-cli console` should drop you in an interactive console for
speaking ABCI messages to your application.
Try running these commands:
> echo hello
-> code: OK
-> data: hello
-> data.hex: 0x68656C6C6F
> info
-> code: OK
-> data: {"size":0}
-> data.hex: 0x7B2273697A65223A307D
> commit
-> code: OK
-> data.hex: 0x0000000000000000
> deliver_tx "abc"
-> code: OK
> info
-> code: OK
-> data: {"size":1}
-> data.hex: 0x7B2273697A65223A317D
> commit
-> code: OK
-> data.hex: 0x0200000000000000
> query "abc"
-> code: OK
-> log: exists
-> height: 0
-> value: abc
-> value.hex: 616263
> deliver_tx "def=xyz"
-> code: OK
> commit
-> code: OK
-> data.hex: 0x0400000000000000
> query "def"
-> code: OK
-> log: exists
-> height: 0
-> value: xyz
-> value.hex: 78797A
Note that if we do `deliver_tx "abc"` it will store `(abc, abc)`, but if
we do `deliver_tx "abc=efg"` it will store `(abc, efg)`.
Similarly, you could put the commands in a file and run
`abci-cli --verbose batch < myfile`.
## Counter - Another Example
Now that we've got the hang of it, let's try another application, the
"counter" app.
Like the kvstore app, its code can be found
[here](https://github.com/tendermint/abci/blob/master/cmd/abci-cli/abci-cli.go)
and looks like:
func cmdCounter(cmd *cobra.Command, args []string) error {
app := counter.NewCounterApplication(flagSerial)
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
// Start the listener
srv, err := server.NewServer(flagAddrC, flagAbci, app)
if err != nil {
return err
}
srv.SetLogger(logger.With("module", "abci-server"))
if err := srv.Start(); err != nil {
return err
}
// Wait forever
cmn.TrapSignal(func() {
// Cleanup
srv.Stop()
})
return nil
}
The counter app doesn't use a Merkle tree, it just counts how many times
we've sent a transaction, asked for a hash, or committed the state. The
result of `commit` is just the number of transactions sent.
This application has two modes: `serial=off` and `serial=on`.
When `serial=on`, transactions must be a big-endian encoded incrementing
integer, starting at 0.
If `serial=off`, there are no restrictions on transactions.
We can toggle the value of `serial` using the `set_option` ABCI message.
When `serial=on`, some transactions are invalid. In a live blockchain,
transactions collect in memory before they are committed into blocks. To
avoid wasting resources on invalid transactions, ABCI provides the
`check_tx` message, which application developers can use to accept or
reject transactions, before they are stored in memory or gossipped to
other peers.
In this instance of the counter app, `check_tx` only allows transactions
whose integer is greater than the last committed one.
Let's kill the console and the kvstore application, and start the
counter app:
abci-cli counter
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
> check_tx 0xff
-> code: OK
> deliver_tx 0x00
-> code: OK
> check_tx 0x00
-> code: BadNonce
-> log: Invalid nonce. Expected >= 1, got 0
> deliver_tx 0x01
-> code: OK
> deliver_tx 0x04
-> code: BadNonce
-> log: Invalid nonce. Expected 2, got 4
> info
-> code: OK
-> data: {"hashes":0,"txs":2}
-> data.hex: 0x7B22686173686573223A302C22747873223A327D
This is a very simple application, but between `counter` and `kvstore`,
its easy to see how you can build out arbitrary application states on
top of the ABCI. [Hyperledger's
Burrow](https://github.com/hyperledger/burrow) also runs atop ABCI,
bringing with it Ethereum-like accounts, the Ethereum virtual-machine,
Monax's permissioning scheme, and native contracts extensions.
But the ultimate flexibility comes from being able to write the
application easily in any language.
We have implemented the counter in a number of languages [see the
example directory](https://github.com/tendermint/abci/tree/master/example).
To run the Node JS version, `cd` to `example/js` and run
node app.js
(you'll have to kill the other counter application process). In another
window, run the console and those previous ABCI commands. You should get
the same results as for the Go version.
## Bounties
Want to write the counter app in your favorite language?! We'd be happy
to add you to our [ecosystem](https://tendermint.com/ecosystem)! We're
also offering [bounties](https://hackerone.com/tendermint/) for
implementations in new languages!
The `abci-cli` is designed strictly for testing and debugging. In a real
deployment, the role of sending messages is taken by Tendermint, which
connects to the app using three separate connections, each with its own
pattern of messages.
For more information, see the [application developers
guide](./app-development.html). For examples of running an ABCI app with
Tendermint, see the [getting started guide](./getting-started.html).
Next is the ABCI specification.

View File

@@ -1,369 +0,0 @@
Using ABCI-CLI
==============
To facilitate testing and debugging of ABCI servers and simple apps, we
built a CLI, the ``abci-cli``, for sending ABCI messages from the
command line.
Install
-------
Make sure you `have Go installed <https://golang.org/doc/install>`__.
Next, install the ``abci-cli`` tool and example applications:
::
go get -u github.com/tendermint/abci/cmd/abci-cli
If this fails, you may need to use `dep <https://github.com/golang/dep>`__ to get vendored
dependencies:
::
cd $GOPATH/src/github.com/tendermint/abci
make get_tools
make get_vendor_deps
make install
Now run ``abci-cli`` to see the list of commands:
::
Usage:
abci-cli [command]
Available Commands:
batch Run a batch of abci commands against an application
check_tx Validate a tx
commit Commit the application state and return the Merkle root hash
console Start an interactive abci console for multiple commands
counter ABCI demo example
deliver_tx Deliver a new tx to the application
kvstore ABCI demo example
echo Have the application echo a message
help Help about any command
info Get some info about the application
query Query the application state
set_option Set an options on the application
Flags:
--abci string socket or grpc (default "socket")
--address string address of application socket (default "tcp://127.0.0.1:46658")
-h, --help help for abci-cli
-v, --verbose print the command and results as if it were a console session
Use "abci-cli [command] --help" for more information about a command.
KVStore - First Example
-----------------------
The ``abci-cli`` tool lets us send ABCI messages to our application, to
help build and debug them.
The most important messages are ``deliver_tx``, ``check_tx``, and
``commit``, but there are others for convenience, configuration, and
information purposes.
We'll start a kvstore application, which was installed at the same time as
``abci-cli`` above. The kvstore just stores transactions in a merkle tree.
Its code can be found `here <https://github.com/tendermint/abci/blob/master/cmd/abci-cli/abci-cli.go>`__ and looks like:
.. container:: toggle
.. container:: header
**Show/Hide KVStore Example**
.. code-block:: go
func cmdKVStore(cmd *cobra.Command, args []string) error {
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
// Create the application - in memory or persisted to disk
var app types.Application
if flagPersist == "" {
app = kvstore.NewKVStoreApplication()
} else {
app = kvstore.NewPersistentKVStoreApplication(flagPersist)
app.(*kvstore.PersistentKVStoreApplication).SetLogger(logger.With("module", "kvstore"))
}
// Start the listener
srv, err := server.NewServer(flagAddrD, flagAbci, app)
if err != nil {
return err
}
srv.SetLogger(logger.With("module", "abci-server"))
if err := srv.Start(); err != nil {
return err
}
// Wait forever
cmn.TrapSignal(func() {
// Cleanup
srv.Stop()
})
return nil
}
Start by running:
::
abci-cli kvstore
And in another terminal, run
::
abci-cli echo hello
abci-cli info
You'll see something like:
::
-> data: hello
-> data.hex: 68656C6C6F
and:
::
-> data: {"size":0}
-> data.hex: 7B2273697A65223A307D
An ABCI application must provide two things:
- a socket server
- a handler for ABCI messages
When we run the ``abci-cli`` tool we open a new connection to the
application's socket server, send the given ABCI message, and wait for a
response.
The server may be generic for a particular language, and we provide a
`reference implementation in
Golang <https://github.com/tendermint/abci/tree/master/server>`__. See
the `list of other ABCI
implementations <./ecosystem.html>`__ for servers in
other languages.
The handler is specific to the application, and may be arbitrary, so
long as it is deterministic and conforms to the ABCI interface
specification.
So when we run ``abci-cli info``, we open a new connection to the ABCI
server, which calls the ``Info()`` method on the application, which
tells us the number of transactions in our Merkle tree.
Now, since every command opens a new connection, we provide the
``abci-cli console`` and ``abci-cli batch`` commands, to allow multiple
ABCI messages to be sent over a single connection.
Running ``abci-cli console`` should drop you in an interactive console
for speaking ABCI messages to your application.
Try running these commands:
::
> echo hello
-> code: OK
-> data: hello
-> data.hex: 0x68656C6C6F
> info
-> code: OK
-> data: {"size":0}
-> data.hex: 0x7B2273697A65223A307D
> commit
-> code: OK
> deliver_tx "abc"
-> code: OK
> info
-> code: OK
-> data: {"size":1}
-> data.hex: 0x7B2273697A65223A317D
> commit
-> code: OK
-> data.hex: 0x49DFD15CCDACDEAE9728CB01FBB5E8688CA58B91
> query "abc"
-> code: OK
-> log: exists
-> height: 0
-> value: abc
-> value.hex: 616263
> deliver_tx "def=xyz"
-> code: OK
> commit
-> code: OK
-> data.hex: 0x70102DB32280373FBF3F9F89DA2A20CE2CD62B0B
> query "def"
-> code: OK
-> log: exists
-> height: 0
-> value: xyz
-> value.hex: 78797A
Note that if we do ``deliver_tx "abc"`` it will store ``(abc, abc)``,
but if we do ``deliver_tx "abc=efg"`` it will store ``(abc, efg)``.
Similarly, you could put the commands in a file and run
``abci-cli --verbose batch < myfile``.
Counter - Another Example
-------------------------
Now that we've got the hang of it, let's try another application, the
"counter" app.
Like the kvstore app, its code can be found `here <https://github.com/tendermint/abci/blob/master/cmd/abci-cli/abci-cli.go>`__ and looks like:
.. container:: toggle
.. container:: header
**Show/Hide Counter Example**
.. code-block:: go
func cmdCounter(cmd *cobra.Command, args []string) error {
app := counter.NewCounterApplication(flagSerial)
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
// Start the listener
srv, err := server.NewServer(flagAddrC, flagAbci, app)
if err != nil {
return err
}
srv.SetLogger(logger.With("module", "abci-server"))
if err := srv.Start(); err != nil {
return err
}
// Wait forever
cmn.TrapSignal(func() {
// Cleanup
srv.Stop()
})
return nil
}
The counter app doesn't use a Merkle tree, it just counts how many times
we've sent a transaction, asked for a hash, or committed the state. The
result of ``commit`` is just the number of transactions sent.
This application has two modes: ``serial=off`` and ``serial=on``.
When ``serial=on``, transactions must be a big-endian encoded
incrementing integer, starting at 0.
If ``serial=off``, there are no restrictions on transactions.
We can toggle the value of ``serial`` using the ``set_option`` ABCI
message.
When ``serial=on``, some transactions are invalid. In a live blockchain,
transactions collect in memory before they are committed into blocks. To
avoid wasting resources on invalid transactions, ABCI provides the
``check_tx`` message, which application developers can use to accept or
reject transactions, before they are stored in memory or gossipped to
other peers.
In this instance of the counter app, ``check_tx`` only allows
transactions whose integer is greater than the last committed one.
Let's kill the console and the kvstore application, and start the counter
app:
::
abci-cli counter
In another window, start the ``abci-cli console``:
::
> set_option serial on
-> code: OK
> check_tx 0x00
-> code: OK
> check_tx 0xff
-> code: OK
> deliver_tx 0x00
-> code: OK
> check_tx 0x00
-> code: BadNonce
-> log: Invalid nonce. Expected >= 1, got 0
> deliver_tx 0x01
-> code: OK
> deliver_tx 0x04
-> code: BadNonce
-> log: Invalid nonce. Expected 2, got 4
> info
-> code: OK
-> data: {"hashes":0,"txs":2}
-> data.hex: 0x7B22686173686573223A302C22747873223A327D
This is a very simple application, but between ``counter`` and
``kvstore``, its easy to see how you can build out arbitrary application
states on top of the ABCI. `Hyperledger's
Burrow <https://github.com/hyperledger/burrow>`__ also runs atop ABCI,
bringing with it Ethereum-like accounts, the Ethereum virtual-machine,
Monax's permissioning scheme, and native contracts extensions.
But the ultimate flexibility comes from being able to write the
application easily in any language.
We have implemented the counter in a number of languages (see the
`example directory <https://github.com/tendermint/abci/tree/master/example`__).
To run the Node JS version, ``cd`` to ``example/js`` and run
::
node app.js
(you'll have to kill the other counter application process). In another
window, run the console and those previous ABCI commands. You should get
the same results as for the Go version.
Bounties
--------
Want to write the counter app in your favorite language?! We'd be happy
to add you to our `ecosystem <https://tendermint.com/ecosystem>`__!
We're also offering `bounties <https://tendermint.com/bounties>`__ for
implementations in new languages!
The ``abci-cli`` is designed strictly for testing and debugging. In a
real deployment, the role of sending messages is taken by Tendermint,
which connects to the app using three separate connections, each with
its own pattern of messages.
For more information, see the `application developers
guide <./app-development.html>`__. For examples of running an ABCI
app with Tendermint, see the `getting started
guide <./getting-started.html>`__. Next is the ABCI specification.

50
docs/app-architecture.md Normal file
View File

@@ -0,0 +1,50 @@
# Application Architecture Guide
Here we provide a brief guide on the recommended architecture of a
Tendermint blockchain application.
The following diagram provides a superb example:
<https://drive.google.com/open?id=1yR2XpRi9YCY9H9uMfcw8-RMJpvDyvjz9>
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.
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.
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.
All reads from the app happen through the Tendermint `/abci_query`
endpoint. All writes to the app happen through the Tendermint
`/broadcast_tx_*` endpoints.
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.
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.
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/develop/specification.md)

View File

@@ -1,125 +0,0 @@
Application Architecture Guide
==============================
Overview
--------
A blockchain application is more than the consensus engine and the
transaction logic (eg. smart contracts, business logic) as implemented
in the ABCI app. There are also (mobile, web, desktop) clients that will
need to connect and make use of the app. We will assume for now that you
have a well designed transactions and database model, but maybe this
will be the topic of another article. This article is more interested in
various ways of setting up the "plumbing" and connecting these pieces,
and demonstrating some evolving best practices.
Security
--------
A very important aspect when constructing a blockchain is security. The
consensus model can be DoSed (no consensus possible) by corrupting 1/3
of the validators and exploited (writing arbitrary blocks) by corrupting
2/3 of the validators. So, while the security is not that of the
"weakest link", you should take care that the "average link" is
sufficiently hardened.
One big attack surface on the validators is the communication between
the ABCI app and the tendermint core. This should be highly protected.
Ideally, the app and the core are running on the same machine, so no
external agent can target the communication channel. You can use unix
sockets (with permissions preventing access from other users), or even
compile the two apps into one binary if the ABCI app is also writen in
go. If you are unable to do that due to language support, then the ABCI
app should bind a TCP connection to localhost (127.0.0.1), which is less
efficient and secure, but still not reachable from outside. If you must
run the ABCI app and tendermint core on separate machines, make sure you
have a secure communication channel (ssh tunnel?)
Now assuming, you have linked together your app and the core securely,
you must also make sure no one can get on the machine it is hosted on.
At this point it is basic network security. Run on a secure operating
system (SELinux?). Limit who has access to the machine (user accounts,
but also where the physical machine is hosted). Turn off all services
except for ssh, which should only be accessible by some well-guarded
public/private key pairs (no password). And maybe even firewall off
access to the ports used by the validators, so only known validators can
connect.
There was also a suggestion on slack from @jhon about compiling
everything together with a unikernel for more security, such as
`Mirage <https://mirage.io>`__ or
`UNIK <https://github.com/emc-advanced-dev/unik>`__.
Connecting your client to the blockchain
----------------------------------------
Tendermint Core RPC
~~~~~~~~~~~~~~~~~~~
The concept is that the ABCI app is completely hidden from the outside
world and only communicated through a tested and secured `interface
exposed by the tendermint core <./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).

527
docs/app-development.md Normal file
View File

@@ -0,0 +1,527 @@
# Application Development Guide
## ABCI Design
The purpose of ABCI is to provide a clean interface between state
transition machines on one computer and the mechanics of their
replication across multiple computers. The former we call 'application
logic' and the latter the 'consensus engine'. Application logic
validates transactions and optionally executes transactions against some
persistent state. A consensus engine ensures all transactions are
replicated in the same order on every machine. We call each machine in a
consensus engine a 'validator', and each validator runs the same
transactions through the same application logic. In particular, we are
interested in blockchain-style consensus engines, where transactions are
committed in hash-linked blocks.
The ABCI design has a few distinct components:
- message protocol
- pairs of request and response messages
- consensus makes requests, application responds
- defined using protobuf
- server/client
- consensus engine runs the client
- application runs the server
- two implementations:
- async raw bytes
- grpc
- blockchain protocol
- abci is connection oriented
- Tendermint Core maintains three connections:
- [mempool connection](#mempool-connection): for checking if
transactions should be relayed before they are committed;
only uses `CheckTx`
- [consensus connection](#consensus-connection): for executing
transactions that have been committed. Message sequence is
-for every block
-`BeginBlock, [DeliverTx, ...], EndBlock, Commit`
- [query connection](#query-connection): for querying the
application state; only uses Query and Info
The mempool and consensus logic act as clients, and each maintains an
open ABCI connection with the application, which hosts an ABCI server.
Shown are the request and response types sent on each connection.
## Message Protocol
The message protocol consists of pairs of requests and responses. Some
messages have no fields, while others may include byte-arrays, strings,
or integers. See the `message Request` and `message Response`
definitions in [the protobuf definition
file](https://github.com/tendermint/abci/blob/master/types/types.proto),
and the [protobuf
documentation](https://developers.google.com/protocol-buffers/docs/overview)
for more details.
For each request, a server should respond with the corresponding
response, where order of requests is preserved in the order of
responses.
## Server
To use ABCI in your programming language of choice, there must be a ABCI
server in that language. Tendermint supports two kinds of implementation
of the server:
- Asynchronous, raw socket server (Tendermint Socket Protocol, also
known as TSP or Teaspoon)
- GRPC
Both can be tested using the `abci-cli` by setting the `--abci` flag
appropriately (ie. to `socket` or `grpc`).
See examples, in various stages of maintenance, in
[Go](https://github.com/tendermint/abci/tree/master/server),
[JavaScript](https://github.com/tendermint/js-abci),
[Python](https://github.com/tendermint/abci/tree/master/example/python3/abci),
[C++](https://github.com/mdyring/cpp-tmsp), and
[Java](https://github.com/jTendermint/jabci).
### GRPC
If GRPC is available in your language, this is the easiest approach,
though it will have significant performance overhead.
To get started with GRPC, copy in the [protobuf
file](https://github.com/tendermint/abci/blob/master/types/types.proto)
and compile it using the GRPC plugin for your language. For instance,
for golang, the command is `protoc --go_out=plugins=grpc:. types.proto`.
See the [grpc documentation for more details](http://www.grpc.io/docs/).
`protoc` will autogenerate all the necessary code for ABCI client and
server in your language, including whatever interface your application
must satisfy to be used by the ABCI server for handling requests.
### TSP
If GRPC is not available in your language, or you require higher
performance, or otherwise enjoy programming, you may implement your own
ABCI server using the Tendermint Socket Protocol, known affectionately
as Teaspoon. The first step is still to auto-generate the relevant data
types and codec in your language using `protoc`. Messages coming over
the socket are Protobuf3 encoded, but additionally length-prefixed to
facilitate use as a streaming protocol. Protobuf3 doesn't have an
official length-prefix standard, so we use our own. The first byte in
the prefix represents the length of the Big Endian encoded length. The
remaining bytes in the prefix are the Big Endian encoded length.
For example, if the Protobuf3 encoded ABCI message is 0xDEADBEEF (4
bytes), the length-prefixed message is 0x0104DEADBEEF. If the Protobuf3
encoded ABCI message is 65535 bytes long, the length-prefixed message
would be like 0x02FFFF....
Note this prefixing does not apply for grpc.
An ABCI server must also be able to support multiple connections, as
Tendermint uses three connections.
## Client
There are currently two use-cases for an ABCI client. One is a testing
tool, as in the `abci-cli`, which allows ABCI requests to be sent via
command line. The other is a consensus engine, such as Tendermint Core,
which makes requests to the application every time a new transaction is
received or a block is committed.
It is unlikely that you will need to implement a client. For details of
our client, see
[here](https://github.com/tendermint/abci/tree/master/client).
Most of the examples below are from [kvstore
application](https://github.com/tendermint/abci/blob/master/example/kvstore/kvstore.go),
which is a part of the abci repo. [persistent_kvstore
application](https://github.com/tendermint/abci/blob/master/example/kvstore/persistent_kvstore.go)
is used to show `BeginBlock`, `EndBlock` and `InitChain` example
implementations.
## Blockchain Protocol
In ABCI, a transaction is simply an arbitrary length byte-array. It is
the application's responsibility to define the transaction codec as they
please, and to use it for both CheckTx and DeliverTx.
Note that there are two distinct means for running transactions,
corresponding to stages of 'awareness' of the transaction in the
network. The first stage is when a transaction is received by a
validator from a client into the so-called mempool or transaction pool
-this is where we use CheckTx. The second is when the transaction is
successfully committed on more than 2/3 of validators - where we use
DeliverTx. In the former case, it may not be necessary to run all the
state transitions associated with the transaction, as the transaction
may not ultimately be committed until some much later time, when the
result of its execution will be different. For instance, an Ethereum
ABCI app would check signatures and amounts in CheckTx, but would not
actually execute any contract code until the DeliverTx, so as to avoid
executing state transitions that have not been finalized.
To formalize the distinction further, two explicit ABCI connections are
made between Tendermint Core and the application: the mempool connection
and the consensus connection. We also make a third connection, the query
connection, to query the local state of the app.
### Mempool Connection
The mempool connection is used *only* for CheckTx requests. Transactions
are run using CheckTx in the same order they were received by the
validator. If the CheckTx returns `OK`, the transaction is kept in
memory and relayed to other peers in the same order it was received.
Otherwise, it is discarded.
CheckTx requests run concurrently with block processing; so they should
run against a copy of the main application state which is reset after
every block. This copy is necessary to track transitions made by a
sequence of CheckTx requests before they are included in a block. When a
block is committed, the application must ensure to reset the mempool
state to the latest committed state. Tendermint Core will then filter
through all transactions in the mempool, removing any that were included
in the block, and re-run the rest using CheckTx against the post-Commit
mempool state (this behaviour can be turned off with
`[mempool] recheck = false`).
In go:
func (app *KVStoreApplication) CheckTx(tx []byte) types.Result {
return types.OK
}
In Java:
ResponseCheckTx requestCheckTx(RequestCheckTx req) {
byte[] transaction = req.getTx().toByteArray();
// validate transaction
if (notValid) {
return ResponseCheckTx.newBuilder().setCode(CodeType.BadNonce).setLog("invalid tx").build();
} else {
return ResponseCheckTx.newBuilder().setCode(CodeType.OK).build();
}
}
### 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
The consensus connection is used only when a new block is committed, and
communicates all information from the block in a series of requests:
`BeginBlock, [DeliverTx, ...], EndBlock, Commit`. That is, when a block
is committed in the consensus, we send a list of DeliverTx requests (one
for each transaction) sandwiched by BeginBlock and EndBlock requests,
and followed by a Commit.
### DeliverTx
DeliverTx is the workhorse of the blockchain. Tendermint sends the
DeliverTx requests asynchronously but in order, and relies on the
underlying socket protocol (ie. TCP) to ensure they are received by the
app in order. They have already been ordered in the global consensus by
the Tendermint protocol.
DeliverTx returns a abci.Result, which includes a Code, Data, and Log.
The code may be non-zero (non-OK), meaning the corresponding transaction
should have been rejected by the mempool, but may have been included in
a block by a Byzantine proposer.
The block header will be updated (TODO) to include some commitment to
the results of DeliverTx, be it a bitarray of non-OK transactions, or a
merkle root of the data returned by the DeliverTx requests, or both.
In go:
// tx is either "key=value" or just arbitrary bytes
func (app *KVStoreApplication) DeliverTx(tx []byte) types.Result {
parts := strings.Split(string(tx), "=")
if len(parts) == 2 {
app.state.Set([]byte(parts[0]), []byte(parts[1]))
} else {
app.state.Set(tx, tx)
}
return types.OK
}
In Java:
/**
* Using Protobuf types from the protoc compiler, we always start with a byte[]
*/
ResponseDeliverTx deliverTx(RequestDeliverTx request) {
byte[] transaction = request.getTx().toByteArray();
// validate your transaction
if (notValid) {
return ResponseDeliverTx.newBuilder().setCode(CodeType.BadNonce).setLog("transaction was invalid").build();
} else {
ResponseDeliverTx.newBuilder().setCode(CodeType.OK).build();
}
}
### Commit
Once all processing of the block is complete, Tendermint sends the
Commit request and blocks waiting for a response. While the mempool may
run concurrently with block processing (the BeginBlock, DeliverTxs, and
EndBlock), it is locked for the Commit request so that its state can be
safely reset during Commit. This means the app *MUST NOT* do any
blocking communication with the mempool (ie. broadcast\_tx) during
Commit, or there will be deadlock. Note also that all remaining
transactions in the mempool are replayed on the mempool connection
(CheckTx) following a commit.
The app should respond to the Commit request with a byte array, which is
the deterministic state root of the application. It is included in the
header of the next block. It can be used to provide easily verified
Merkle-proofs of the state of the application.
It is expected that the app will persist state to disk on Commit. The
option to have all transactions replayed from some previous block is the
job of the [Handshake](#handshake).
In go:
func (app *KVStoreApplication) Commit() types.Result {
hash := app.state.Hash()
return types.NewResultOK(hash, "")
}
In Java:
ResponseCommit requestCommit(RequestCommit requestCommit) {
// update the internal app-state
byte[] newAppState = calculateAppState();
// and return it to the node
return ResponseCommit.newBuilder().setCode(CodeType.OK).setData(ByteString.copyFrom(newAppState)).build();
}
### BeginBlock
The BeginBlock request can be used to run some code at the beginning of
every block. It also allows Tendermint to send the current block hash
and header to the application, before it sends any of the transactions.
The app should remember the latest height and header (ie. from which it
has run a successful Commit) so that it can tell Tendermint where to
pick up from when it restarts. See information on the Handshake, below.
In go:
// Track the block hash and header information
func (app *PersistentKVStoreApplication) BeginBlock(params types.RequestBeginBlock) {
// update latest block info
app.blockHeader = params.Header
// reset valset changes
app.changes = make([]*types.Validator, 0)
}
In Java:
/*
* all types come from protobuf definition
*/
ResponseBeginBlock requestBeginBlock(RequestBeginBlock req) {
Header header = req.getHeader();
byte[] prevAppHash = header.getAppHash().toByteArray();
long prevHeight = header.getHeight();
long numTxs = header.getNumTxs();
// run your pre-block logic. Maybe prepare a state snapshot, message components, etc
return ResponseBeginBlock.newBuilder().build();
}
### EndBlock
The EndBlock request can be used to run some code at the end of every
block. Additionally, the response may contain a list of validators,
which can be used to update the validator set. To add a new validator or
update an existing one, simply include them in the list returned in the
EndBlock response. To remove one, include it in the list with a `power`
equal to `0`. Tendermint core will take care of updating the validator
set. Note the change in voting power must be strictly less than 1/3 per
block if you want a light client to be able to prove the transition
externally. See the [light client
docs](https://godoc.org/github.com/tendermint/tendermint/lite#hdr-How_We_Track_Validators)
for details on how it tracks validators.
In go:
// Update the validator set
func (app *PersistentKVStoreApplication) EndBlock(req types.RequestEndBlock) types.ResponseEndBlock {
return types.ResponseEndBlock{ValidatorUpdates: app.ValUpdates}
}
In Java:
/*
* Assume that one validator changes. The new validator has a power of 10
*/
ResponseEndBlock requestEndBlock(RequestEndBlock req) {
final long currentHeight = req.getHeight();
final byte[] validatorPubKey = getValPubKey();
ResponseEndBlock.Builder builder = ResponseEndBlock.newBuilder();
builder.addDiffs(1, Types.Validator.newBuilder().setPower(10L).setPubKey(ByteString.copyFrom(validatorPubKey)).build());
return builder.build();
}
### Query Connection
This connection is used to query the application without engaging
consensus. It's exposed over the tendermint core rpc, so clients can
query the app without exposing a server on the app itself, but they must
serialize each query as a single byte array. Additionally, certain
"standardized" queries may be used to inform local decisions, for
instance about which peers to connect to.
Tendermint Core currently uses the Query connection to filter peers upon
connecting, according to IP address or public key. For instance,
returning non-OK ABCI response to either of the following queries will
cause Tendermint to not connect to the corresponding peer:
- `p2p/filter/addr/<addr>`, where `<addr>` is an IP address.
- `p2p/filter/pubkey/<pubkey>`, where `<pubkey>` is the hex-encoded
ED25519 key of the node (not it's validator key)
Note: these query formats are subject to change!
In go:
func (app *KVStoreApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) {
if reqQuery.Prove {
value, proof, exists := app.state.Proof(reqQuery.Data)
resQuery.Index = -1 // TODO make Proof return index
resQuery.Key = reqQuery.Data
resQuery.Value = value
resQuery.Proof = proof
if exists {
resQuery.Log = "exists"
} else {
resQuery.Log = "does not exist"
}
return
} else {
index, value, exists := app.state.Get(reqQuery.Data)
resQuery.Index = int64(index)
resQuery.Value = value
if exists {
resQuery.Log = "exists"
} else {
resQuery.Log = "does not exist"
}
return
}
}
In Java:
ResponseQuery requestQuery(RequestQuery req) {
final boolean isProveQuery = req.getProve();
final ResponseQuery.Builder responseBuilder = ResponseQuery.newBuilder();
if (isProveQuery) {
com.app.example.ProofResult proofResult = generateProof(req.getData().toByteArray());
final byte[] proofAsByteArray = proofResult.getAsByteArray();
responseBuilder.setProof(ByteString.copyFrom(proofAsByteArray));
responseBuilder.setKey(req.getData());
responseBuilder.setValue(ByteString.copyFrom(proofResult.getData()));
responseBuilder.setLog(result.getLogValue());
} else {
byte[] queryData = req.getData().toByteArray();
final com.app.example.QueryResult result = generateQueryResult(queryData);
responseBuilder.setIndex(result.getIndex());
responseBuilder.setValue(ByteString.copyFrom(result.getValue()));
responseBuilder.setLog(result.getLogValue());
}
return responseBuilder.build();
}
### Handshake
When the app or tendermint restarts, they need to sync to a common
height. When an ABCI connection is first established, Tendermint will
call `Info` on the Query connection. The response should contain the
LastBlockHeight and LastBlockAppHash - the former is the last block for
which the app ran Commit successfully, the latter is the response from
that Commit.
Using this information, Tendermint will determine what needs to be
replayed, if anything, against the app, to ensure both Tendermint and
the app are synced to the latest block height.
If the app returns a LastBlockHeight of 0, Tendermint will just replay
all blocks.
In go:
func (app *KVStoreApplication) Info(req types.RequestInfo) (resInfo types.ResponseInfo) {
return types.ResponseInfo{Data: cmn.Fmt("{\"size\":%v}", app.state.Size())}
}
In Java:
ResponseInfo requestInfo(RequestInfo req) {
final byte[] lastAppHash = getLastAppHash();
final long lastHeight = getLastHeight();
return ResponseInfo.newBuilder().setLastBlockAppHash(ByteString.copyFrom(lastAppHash)).setLastBlockHeight(lastHeight).build();
}
### Genesis
`InitChain` will be called once upon the genesis. `params` includes the
initial validator set. Later on, it may be extended to take parts of the
consensus params.
In go:
// Save the validators in the merkle tree
func (app *PersistentKVStoreApplication) InitChain(params types.RequestInitChain) {
for _, v := range params.Validators {
r := app.updateValidator(v)
if r.IsErr() {
app.logger.Error("Error updating validators", "r", r)
}
}
}
In Java:
/*
* all types come from protobuf definition
*/
ResponseInitChain requestInitChain(RequestInitChain req) {
final int validatorsCount = req.getValidatorsCount();
final List<Types.Validator> validatorsList = req.getValidatorsList();
validatorsList.forEach((validator) -> {
long power = validator.getPower();
byte[] validatorPubKey = validator.getPubKey().toByteArray();
// do somehing for validator setup in app
});
return ResponseInitChain.newBuilder().build();
}

View File

@@ -1,630 +0,0 @@
Application Development Guide
=============================
ABCI Design
-----------
The purpose of ABCI is to provide a clean interface between state
transition machines on one computer and the mechanics of their
replication across multiple computers. The former we call 'application
logic' and the latter the 'consensus engine'. Application logic
validates transactions and optionally executes transactions against some
persistent state. A consensus engine ensures all transactions are
replicated in the same order on every machine. We call each machine in a
consensus engine a 'validator', and each validator runs the same
transactions through the same application logic. In particular, we are
interested in blockchain-style consensus engines, where transactions are
committed in hash-linked blocks.
The ABCI design has a few distinct components:
- message protocol
- pairs of request and response messages
- consensus makes requests, application responds
- defined using protobuf
- server/client
- consensus engine runs the client
- application runs the server
- two implementations:
- async raw bytes
- grpc
- blockchain protocol
- abci is connection oriented
- Tendermint Core maintains three connections:
- `mempool connection <#mempool-connection>`__: for checking if
transactions should be relayed before they are committed; only
uses ``CheckTx``
- `consensus connection <#consensus-connection>`__: for executing
transactions that have been committed. Message sequence is -
for every block -
``BeginBlock, [DeliverTx, ...], EndBlock, Commit``
- `query connection <#query-connection>`__: for querying the
application state; only uses Query and Info
The mempool and consensus logic act as clients, and each maintains an
open ABCI connection with the application, which hosts an ABCI server.
Shown are the request and response types sent on each connection.
Message Protocol
----------------
The message protocol consists of pairs of requests and responses. Some
messages have no fields, while others may include byte-arrays, strings,
or integers. See the ``message Request`` and ``message Response``
definitions in `the protobuf definition
file <https://github.com/tendermint/abci/blob/master/types/types.proto>`__,
and the `protobuf
documentation <https://developers.google.com/protocol-buffers/docs/overview>`__
for more details.
For each request, a server should respond with the corresponding
response, where order of requests is preserved in the order of
responses.
Server
------
To use ABCI in your programming language of choice, there must be a ABCI
server in that language. Tendermint supports two kinds of implementation
of the server:
- Asynchronous, raw socket server (Tendermint Socket Protocol, also
known as TSP or Teaspoon)
- GRPC
Both can be tested using the ``abci-cli`` by setting the ``--abci`` flag
appropriately (ie. to ``socket`` or ``grpc``).
See examples, in various stages of maintenance, in
`Go <https://github.com/tendermint/abci/tree/master/server>`__,
`JavaScript <https://github.com/tendermint/js-abci>`__,
`Python <https://github.com/tendermint/abci/tree/master/example/python3/abci>`__,
`C++ <https://github.com/mdyring/cpp-tmsp>`__, and
`Java <https://github.com/jTendermint/jabci>`__.
GRPC
~~~~
If GRPC is available in your language, this is the easiest approach,
though it will have significant performance overhead.
To get started with GRPC, copy in the `protobuf
file <https://github.com/tendermint/abci/blob/master/types/types.proto>`__
and compile it using the GRPC plugin for your language. For instance,
for golang, the command is
``protoc --go_out=plugins=grpc:. types.proto``. See the `grpc
documentation for more details <http://www.grpc.io/docs/>`__. ``protoc``
will autogenerate all the necessary code for ABCI client and server in
your language, including whatever interface your application must
satisfy to be used by the ABCI server for handling requests.
TSP
~~~
If GRPC is not available in your language, or you require higher
performance, or otherwise enjoy programming, you may implement your own
ABCI server using the Tendermint Socket Protocol, known affectionately
as Teaspoon. The first step is still to auto-generate the relevant data
types and codec in your language using ``protoc``. Messages coming over
the socket are Protobuf3 encoded, but additionally length-prefixed to
facilitate use as a streaming protocol. Protobuf3 doesn't have an
official length-prefix standard, so we use our own. The first byte in
the prefix represents the length of the Big Endian encoded length. The
remaining bytes in the prefix are the Big Endian encoded length.
For example, if the Protobuf3 encoded ABCI message is 0xDEADBEEF (4
bytes), the length-prefixed message is 0x0104DEADBEEF. If the Protobuf3
encoded ABCI message is 65535 bytes long, the length-prefixed message
would be like 0x02FFFF....
Note this prefixing does not apply for grpc.
An ABCI server must also be able to support multiple connections, as
Tendermint uses three connections.
Client
------
There are currently two use-cases for an ABCI client. One is a testing
tool, as in the ``abci-cli``, which allows ABCI requests to be sent via
command line. The other is a consensus engine, such as Tendermint Core,
which makes requests to the application every time a new transaction is
received or a block is committed.
It is unlikely that you will need to implement a client. For details of
our client, see
`here <https://github.com/tendermint/abci/tree/master/client>`__.
Most of the examples below are from `kvstore application
<https://github.com/tendermint/abci/blob/master/example/kvstore/kvstore.go>`__,
which is a part of the abci repo. `persistent_kvstore application
<https://github.com/tendermint/abci/blob/master/example/kvstore/persistent_kvstore.go>`__
is used to show ``BeginBlock``, ``EndBlock`` and ``InitChain``
example implementations.
Blockchain Protocol
-------------------
In ABCI, a transaction is simply an arbitrary length byte-array. It is
the application's responsibility to define the transaction codec as they
please, and to use it for both CheckTx and DeliverTx.
Note that there are two distinct means for running transactions,
corresponding to stages of 'awareness' of the transaction in the
network. The first stage is when a transaction is received by a
validator from a client into the so-called mempool or transaction pool -
this is where we use CheckTx. The second is when the transaction is
successfully committed on more than 2/3 of validators - where we use
DeliverTx. In the former case, it may not be necessary to run all the
state transitions associated with the transaction, as the transaction
may not ultimately be committed until some much later time, when the
result of its execution will be different. For instance, an Ethereum
ABCI app would check signatures and amounts in CheckTx, but would not
actually execute any contract code until the DeliverTx, so as to avoid
executing state transitions that have not been finalized.
To formalize the distinction further, two explicit ABCI connections are
made between Tendermint Core and the application: the mempool connection
and the consensus connection. We also make a third connection, the query
connection, to query the local state of the app.
Mempool Connection
~~~~~~~~~~~~~~~~~~
The mempool connection is used *only* for CheckTx requests. Transactions
are run using CheckTx in the same order they were received by the
validator. If the CheckTx returns ``OK``, the transaction is kept in
memory and relayed to other peers in the same order it was received.
Otherwise, it is discarded.
CheckTx requests run concurrently with block processing; so they should
run against a copy of the main application state which is reset after
every block. This copy is necessary to track transitions made by a
sequence of CheckTx requests before they are included in a block. When a
block is committed, the application must ensure to reset the mempool
state to the latest committed state. Tendermint Core will then filter
through all transactions in the mempool, removing any that were included
in the block, and re-run the rest using CheckTx against the post-Commit
mempool state.
.. container:: toggle
.. container:: header
**Show/Hide Go Example**
.. code-block:: go
func (app *KVStoreApplication) CheckTx(tx []byte) types.Result {
return types.OK
}
.. container:: toggle
.. container:: header
**Show/Hide Java Example**
.. code-block:: java
ResponseCheckTx requestCheckTx(RequestCheckTx req) {
byte[] transaction = req.getTx().toByteArray();
// validate transaction
if (notValid) {
return ResponseCheckTx.newBuilder().setCode(CodeType.BadNonce).setLog("invalid tx").build();
} else {
return ResponseCheckTx.newBuilder().setCode(CodeType.OK).build();
}
}
Consensus Connection
~~~~~~~~~~~~~~~~~~~~
The consensus connection is used only when a new block is committed, and
communicates all information from the block in a series of requests:
``BeginBlock, [DeliverTx, ...], EndBlock, Commit``. That is, when a
block is committed in the consensus, we send a list of DeliverTx
requests (one for each transaction) sandwiched by BeginBlock and
EndBlock requests, and followed by a Commit.
DeliverTx
^^^^^^^^^
DeliverTx is the workhorse of the blockchain. Tendermint sends the
DeliverTx requests asynchronously but in order, and relies on the
underlying socket protocol (ie. TCP) to ensure they are received by the
app in order. They have already been ordered in the global consensus by
the Tendermint protocol.
DeliverTx returns a abci.Result, which includes a Code, Data, and Log.
The code may be non-zero (non-OK), meaning the corresponding transaction
should have been rejected by the mempool, but may have been included in
a block by a Byzantine proposer.
The block header will be updated (TODO) to include some commitment to
the results of DeliverTx, be it a bitarray of non-OK transactions, or a
merkle root of the data returned by the DeliverTx requests, or both.
.. container:: toggle
.. container:: header
**Show/Hide Go Example**
.. code-block:: go
// tx is either "key=value" or just arbitrary bytes
func (app *KVStoreApplication) DeliverTx(tx []byte) types.Result {
parts := strings.Split(string(tx), "=")
if len(parts) == 2 {
app.state.Set([]byte(parts[0]), []byte(parts[1]))
} else {
app.state.Set(tx, tx)
}
return types.OK
}
.. container:: toggle
.. container:: header
**Show/Hide Java Example**
.. code-block:: java
/**
* Using Protobuf types from the protoc compiler, we always start with a byte[]
*/
ResponseDeliverTx deliverTx(RequestDeliverTx request) {
byte[] transaction = request.getTx().toByteArray();
// validate your transaction
if (notValid) {
return ResponseDeliverTx.newBuilder().setCode(CodeType.BadNonce).setLog("transaction was invalid").build();
} else {
ResponseDeliverTx.newBuilder().setCode(CodeType.OK).build();
}
}
Commit
^^^^^^
Once all processing of the block is complete, Tendermint sends the
Commit request and blocks waiting for a response. While the mempool may
run concurrently with block processing (the BeginBlock, DeliverTxs, and
EndBlock), it is locked for the Commit request so that its state can be
safely reset during Commit. This means the app *MUST NOT* do any
blocking communication with the mempool (ie. broadcast\_tx) during
Commit, or there will be deadlock. Note also that all remaining
transactions in the mempool are replayed on the mempool connection
(CheckTx) following a commit.
The app should respond to the Commit request with a byte array, which is the deterministic
state root of the application. It is included in the header of the next
block. It can be used to provide easily verified Merkle-proofs of the
state of the application.
It is expected that the app will persist state to disk on Commit. The
option to have all transactions replayed from some previous block is the
job of the `Handshake <#handshake>`__.
.. container:: toggle
.. container:: header
**Show/Hide Go Example**
.. code-block:: go
func (app *KVStoreApplication) Commit() types.Result {
hash := app.state.Hash()
return types.NewResultOK(hash, "")
}
.. container:: toggle
.. container:: header
**Show/Hide Java Example**
.. code-block:: java
ResponseCommit requestCommit(RequestCommit requestCommit) {
// update the internal app-state
byte[] newAppState = calculateAppState();
// and return it to the node
return ResponseCommit.newBuilder().setCode(CodeType.OK).setData(ByteString.copyFrom(newAppState)).build();
}
BeginBlock
^^^^^^^^^^
The BeginBlock request can be used to run some code at the beginning of
every block. It also allows Tendermint to send the current block hash
and header to the application, before it sends any of the transactions.
The app should remember the latest height and header (ie. from which it
has run a successful Commit) so that it can tell Tendermint where to
pick up from when it restarts. See information on the Handshake, below.
.. container:: toggle
.. container:: header
**Show/Hide Go Example**
.. code-block:: go
// Track the block hash and header information
func (app *PersistentKVStoreApplication) BeginBlock(params types.RequestBeginBlock) {
// update latest block info
app.blockHeader = params.Header
// reset valset changes
app.changes = make([]*types.Validator, 0)
}
.. container:: toggle
.. container:: header
**Show/Hide Java Example**
.. code-block:: java
/*
* all types come from protobuf definition
*/
ResponseBeginBlock requestBeginBlock(RequestBeginBlock req) {
Header header = req.getHeader();
byte[] prevAppHash = header.getAppHash().toByteArray();
long prevHeight = header.getHeight();
long numTxs = header.getNumTxs();
// run your pre-block logic. Maybe prepare a state snapshot, message components, etc
return ResponseBeginBlock.newBuilder().build();
}
EndBlock
^^^^^^^^
The EndBlock request can be used to run some code at the end of every block.
Additionally, the response may contain a list of validators, which can be used
to update the validator set. To add a new validator or update an existing one,
simply include them in the list returned in the EndBlock response. To remove
one, include it in the list with a ``power`` equal to ``0``. Tendermint core
will take care of updating the validator set. Note the change in voting power
must be strictly less than 1/3 per block if you want a light client to be able
to prove the transition externally. See the `light client docs
<https://godoc.org/github.com/tendermint/tendermint/lite#hdr-How_We_Track_Validators>`__
for details on how it tracks validators.
.. container:: toggle
.. container:: header
**Show/Hide Go Example**
.. code-block:: go
// Update the validator set
func (app *PersistentKVStoreApplication) EndBlock(req types.RequestEndBlock) types.ResponseEndBlock {
return types.ResponseEndBlock{ValidatorUpdates: app.ValUpdates}
}
.. container:: toggle
.. container:: header
**Show/Hide Java Example**
.. code-block:: java
/*
* Assume that one validator changes. The new validator has a power of 10
*/
ResponseEndBlock requestEndBlock(RequestEndBlock req) {
final long currentHeight = req.getHeight();
final byte[] validatorPubKey = getValPubKey();
ResponseEndBlock.Builder builder = ResponseEndBlock.newBuilder();
builder.addDiffs(1, Types.Validator.newBuilder().setPower(10L).setPubKey(ByteString.copyFrom(validatorPubKey)).build());
return builder.build();
}
Query Connection
~~~~~~~~~~~~~~~~
This connection is used to query the application without engaging
consensus. It's exposed over the tendermint core rpc, so clients can
query the app without exposing a server on the app itself, but they must
serialize each query as a single byte array. Additionally, certain
"standardized" queries may be used to inform local decisions, for
instance about which peers to connect to.
Tendermint Core currently uses the Query connection to filter peers upon
connecting, according to IP address or public key. For instance,
returning non-OK ABCI response to either of the following queries will
cause Tendermint to not connect to the corresponding peer:
- ``p2p/filter/addr/<addr>``, where ``<addr>`` is an IP address.
- ``p2p/filter/pubkey/<pubkey>``, where ``<pubkey>`` is the hex-encoded
ED25519 key of the node (not it's validator key)
Note: these query formats are subject to change!
.. container:: toggle
.. container:: header
**Show/Hide Go Example**
.. code-block:: go
func (app *KVStoreApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) {
if reqQuery.Prove {
value, proof, exists := app.state.Proof(reqQuery.Data)
resQuery.Index = -1 // TODO make Proof return index
resQuery.Key = reqQuery.Data
resQuery.Value = value
resQuery.Proof = proof
if exists {
resQuery.Log = "exists"
} else {
resQuery.Log = "does not exist"
}
return
} else {
index, value, exists := app.state.Get(reqQuery.Data)
resQuery.Index = int64(index)
resQuery.Value = value
if exists {
resQuery.Log = "exists"
} else {
resQuery.Log = "does not exist"
}
return
}
}
.. container:: toggle
.. container:: header
**Show/Hide Java Example**
.. code-block:: java
ResponseQuery requestQuery(RequestQuery req) {
final boolean isProveQuery = req.getProve();
final ResponseQuery.Builder responseBuilder = ResponseQuery.newBuilder();
if (isProveQuery) {
com.app.example.ProofResult proofResult = generateProof(req.getData().toByteArray());
final byte[] proofAsByteArray = proofResult.getAsByteArray();
responseBuilder.setProof(ByteString.copyFrom(proofAsByteArray));
responseBuilder.setKey(req.getData());
responseBuilder.setValue(ByteString.copyFrom(proofResult.getData()));
responseBuilder.setLog(result.getLogValue());
} else {
byte[] queryData = req.getData().toByteArray();
final com.app.example.QueryResult result = generateQueryResult(queryData);
responseBuilder.setIndex(result.getIndex());
responseBuilder.setValue(ByteString.copyFrom(result.getValue()));
responseBuilder.setLog(result.getLogValue());
}
return responseBuilder.build();
}
Handshake
~~~~~~~~~
When the app or tendermint restarts, they need to sync to a common
height. When an ABCI connection is first established, Tendermint will
call ``Info`` on the Query connection. The response should contain the
LastBlockHeight and LastBlockAppHash - the former is the last block for
which the app ran Commit successfully, the latter is the response
from that Commit.
Using this information, Tendermint will determine what needs to be
replayed, if anything, against the app, to ensure both Tendermint and
the app are synced to the latest block height.
If the app returns a LastBlockHeight of 0, Tendermint will just replay
all blocks.
.. container:: toggle
.. container:: header
**Show/Hide Go Example**
.. code-block:: go
func (app *KVStoreApplication) Info(req types.RequestInfo) (resInfo types.ResponseInfo) {
return types.ResponseInfo{Data: cmn.Fmt("{\"size\":%v}", app.state.Size())}
}
.. container:: toggle
.. container:: header
**Show/Hide Java Example**
.. code-block:: java
ResponseInfo requestInfo(RequestInfo req) {
final byte[] lastAppHash = getLastAppHash();
final long lastHeight = getLastHeight();
return ResponseInfo.newBuilder().setLastBlockAppHash(ByteString.copyFrom(lastAppHash)).setLastBlockHeight(lastHeight).build();
}
Genesis
~~~~~~~
``InitChain`` will be called once upon the genesis. ``params`` includes the
initial validator set. Later on, it may be extended to take parts of the
consensus params.
.. container:: toggle
.. container:: header
**Show/Hide Go Example**
.. code-block:: go
// Save the validators in the merkle tree
func (app *PersistentKVStoreApplication) InitChain(params types.RequestInitChain) {
for _, v := range params.Validators {
r := app.updateValidator(v)
if r.IsErr() {
app.logger.Error("Error updating validators", "r", r)
}
}
}
.. container:: toggle
.. container:: header
**Show/Hide Java Example**
.. code-block:: java
/*
* all types come from protobuf definition
*/
ResponseInitChain requestInitChain(RequestInitChain req) {
final int validatorsCount = req.getValidatorsCount();
final List<Types.Validator> validatorsList = req.getValidatorsList();
validatorsList.forEach((validator) -> {
long power = validator.getPower();
byte[] validatorPubKey = validator.getPubKey().toByteArray();
// do somehing for validator setup in app
});
return ResponseInitChain.newBuilder().build();
}

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

@@ -41,8 +41,15 @@ templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
#source_suffix = ['.rst', '.md']
source_suffix = '.rst'
from recommonmark.parser import CommonMarkParser
source_parsers = {
'.md': CommonMarkParser,
}
source_suffix = ['.rst', '.md']
#source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
@@ -71,7 +78,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'
@@ -171,37 +178,29 @@ texinfo_documents = [
'Database'),
]
# ---- customization -------------------------
# ---------------- customizations ----------------------
# for Docker README, below
from shutil import copyfile
# tm-bench and tm-monitor
tools_repo = "https://raw.githubusercontent.com/tendermint/tools/"
tools_branch = "master"
tools_dir = "./tools"
assets_dir = tools_dir + "/assets"
if os.path.isdir(tools_dir) != True:
os.mkdir(tools_dir)
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')
copyfile('../DOCKER/README.md', tools_dir+'/docker.md')
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-bench/README.md', filename=tools_dir+'/benchmarking.md')
urllib.urlretrieve(tools_repo+tools_branch+'/tm-monitor/README.md', filename=tools_dir+'/monitoring.md')
#### abci spec #################################
abci_repo = "https://raw.githubusercontent.com/tendermint/abci/"
abci_branch = "develop"
urllib.urlretrieve(abci_repo+abci_branch+'/specification.rst', filename='abci-spec.rst')
urllib.urlretrieve(abci_repo+abci_branch+'/specification.md', filename='abci-spec.md')

68
docs/deploy-testnets.md Normal file
View File

@@ -0,0 +1,68 @@
# 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.
## 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`, 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 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 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 `26656`. 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 --proxy_app=kvstore --p2p.persistent_peers=96663a3dd0d7b9d17d4c8211b191af259621c693@192.168.0.1:26656, 429fcf25974313b95673f58d77eacdd434402665@192.168.0.2:26656, 0491d373a8e0fcf1023aaf18c51d6a1d0d4f31bd@192.168.0.3:26656, f9baeaa15fedf5e1ef7448dd60f46c01f1a9e9c4@192.168.0.4:26656
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
The easiest and fastest way to get a testnet up in less than 5 minutes.
### Local
With `docker` and `docker-compose` installed, run the command:
make localnet-start
from the root of the tendermint repository. This will spin up a 4-node
local testnet. Review the target in the Makefile to debug any problems.
### Cloud
See the [next section](./terraform-and-ansible.html) for details.

View File

@@ -1,90 +0,0 @@
Deploy a Testnet
================
Now that we've seen how ABCI works, and even played with a few
applications on a single validator node, it's time to deploy a test
network to four validator nodes. For this deployment, we'll use the
``basecoin`` application.
Manual Deployments
------------------
It's relatively easy to setup a Tendermint cluster manually. The only
requirements for a particular Tendermint node are a private key for the
validator, stored as ``priv_validator.json``, 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 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 and replace the existing file with it.
5) Run ``tendermint node --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=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>`__.
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.
Automated Deployment using Kubernetes
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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:
::
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
Install from the Debian/Ubuntu repository:
::
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

View File

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

21
docs/ecosystem.md Normal file
View File

@@ -0,0 +1,21 @@
# Ecosystem
The growing list of applications built using various pieces of the
Tendermint stack can be found at:
- https://tendermint.com/ecosystem
We thank the community for their contributions thus far and welcome the
addition of new projects. A pull request can be submitted to [this
file](https://github.com/tendermint/aib-data/blob/master/json/ecosystem.json)
to include your project.
## Other Tools
See [deploy testnets](./deploy-testnets.html) for information about all
the tools built by Tendermint. We have Kubernetes, Ansible, and
Terraform integrations.
For upgrading from older to newer versions of tendermint and to migrate
your chain data, see [tm-migrator](https://github.com/hxzqlh/tm-tools)
written by @hxzqlh.

View File

@@ -1,15 +0,0 @@
Tendermint Ecosystem
====================
The growing list of applications built using various pieces of the Tendermint stack can be found at:
* https://tendermint.com/ecosystem
We thank the community for their contributions thus far and welcome the addition of new projects. A pull request can be submitted to `this file <https://github.com/tendermint/aib-data/blob/master/json/ecosystem.json>`__ to include your project.
Other Tools
-----------
See `deploy testnets <./deploy-testnets.html>`__ for information about all the tools built by Tendermint. We have Kubernetes, Ansible, and Terraform integrations.
For upgrading from older to newer versions of tendermint and to migrate your chain data, see `tm-migrator <https://github.com/hxzqlh/tm-tools>`__ written by @hxzqlh.

View File

@@ -10,10 +10,10 @@ documentation](http://tendermint.readthedocs.io/en/master/).
### 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
```
@@ -24,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)
@@ -85,7 +85,7 @@ I[01-06|01:45:15.624] Committed state module=state
Check the status with:
```
curl -s localhost:46657/status
curl -s localhost:26657/status
```
### Sending Transactions
@@ -93,25 +93,25 @@ curl -s localhost:46657/status
With the kvstore app running, we can send transactions:
```
curl -s 'localhost:46657/broadcast_tx_commit?tx="abcd"'
curl -s 'localhost:26657/broadcast_tx_commit?tx="abcd"'
```
and check that it worked with:
```
curl -s 'localhost:46657/abci_query?data="abcd"'
curl -s 'localhost:26657/abci_query?data="abcd"'
```
We can send transactions with a key and value too:
```
curl -s 'localhost:46657/broadcast_tx_commit?tx="name=satoshi"'
curl -s 'localhost:26657/broadcast_tx_commit?tx="name=satoshi"'
```
and query the key:
```
curl -s 'localhost:46657/abci_query?data="name"'
curl -s 'localhost:26657/abci_query?data="name"'
```
where the value is returned in hex.
@@ -125,7 +125,7 @@ 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
```
@@ -134,10 +134,10 @@ 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.persistent_peers="3a558bd6f8c97453aa6c2372bb800e8b6ed8e6db@IP1:46656,ccf30d873fddda10a495f42687c8f33472a6569f@IP2:46656,9a4c3de5d6788a76c6ee3cd9ff41e3b45b4cfd14@IP3:46656,58e6f2ab297b3ceae107ba4c8c2898da5c009ff4@IP4:46656"
tendermint node --home ./node2 --proxy_app=kvstore --p2p.persistent_peers="3a558bd6f8c97453aa6c2372bb800e8b6ed8e6db@IP1:46656,ccf30d873fddda10a495f42687c8f33472a6569f@IP2:46656,9a4c3de5d6788a76c6ee3cd9ff41e3b45b4cfd14@IP3:46656,58e6f2ab297b3ceae107ba4c8c2898da5c009ff4@IP4:46656"
tendermint node --home ./node3 --proxy_app=kvstore --p2p.persistent_peers="3a558bd6f8c97453aa6c2372bb800e8b6ed8e6db@IP1:46656,ccf30d873fddda10a495f42687c8f33472a6569f@IP2:46656,9a4c3de5d6788a76c6ee3cd9ff41e3b45b4cfd14@IP3:46656,58e6f2ab297b3ceae107ba4c8c2898da5c009ff4@IP4:46656"
tendermint node --home ./node4 --proxy_app=kvstore --p2p.persistent_peers="3a558bd6f8c97453aa6c2372bb800e8b6ed8e6db@IP1:46656,ccf30d873fddda10a495f42687c8f33472a6569f@IP2:46656,9a4c3de5d6788a76c6ee3cd9ff41e3b45b4cfd14@IP3:46656,58e6f2ab297b3ceae107ba4c8c2898da5c009ff4@IP4:46656"
tendermint node --home ./node0 --proxy_app=kvstore --p2p.persistent_peers="167b80242c300bf0ccfb3ced3dec60dc2a81776e@IP1:26656,3c7a5920811550c04bf7a0b2f1e02ab52317b5e6@IP2:26656,303a1a4312c30525c99ba66522dd81cca56a361a@IP3:26656,b686c2a7f4b1b46dca96af3a0f31a6a7beae0be4@IP4:26656"
tendermint node --home ./node1 --proxy_app=kvstore --p2p.persistent_peers="167b80242c300bf0ccfb3ced3dec60dc2a81776e@IP1:26656,3c7a5920811550c04bf7a0b2f1e02ab52317b5e6@IP2:26656,303a1a4312c30525c99ba66522dd81cca56a361a@IP3:26656,b686c2a7f4b1b46dca96af3a0f31a6a7beae0be4@IP4:26656"
tendermint node --home ./node2 --proxy_app=kvstore --p2p.persistent_peers="167b80242c300bf0ccfb3ced3dec60dc2a81776e@IP1:26656,3c7a5920811550c04bf7a0b2f1e02ab52317b5e6@IP2:26656,303a1a4312c30525c99ba66522dd81cca56a361a@IP3:26656,b686c2a7f4b1b46dca96af3a0f31a6a7beae0be4@IP4:26656"
tendermint node --home ./node3 --proxy_app=kvstore --p2p.persistent_peers="167b80242c300bf0ccfb3ced3dec60dc2a81776e@IP1:26656,3c7a5920811550c04bf7a0b2f1e02ab52317b5e6@IP2:26656,303a1a4312c30525c99ba66522dd81cca56a361a@IP3:26656,b686c2a7f4b1b46dca96af3a0f31a6a7beae0be4@IP4:26656"
```
Note that after the third node is started, blocks will start to stream in

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.18.0
git checkout master
make get_tools
make get_vendor_deps
make install

View File

@@ -0,0 +1,166 @@
# 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:26658"
# 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:26657"
# 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:26656"
# 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
# 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,166 @@
# 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:26658"
# 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:26657"
# 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:26656"
# 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
# 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,6 +0,0 @@
{
"priv_key" : {
"data" : "DA9BAABEA7211A6D93D9A1986B4279EAB3021FAA1653D459D53E6AB4D1CFB4C69BF7D52E48CF00AC5779AA0A6D3C368955D5636A677F72370B8ED19989714CFC",
"type" : "ed25519"
}
}

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,166 @@
# 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:26658"
# 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:26657"
# 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:26656"
# 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
# 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,6 +0,0 @@
{
"priv_key" : {
"data" : "F7BCABA165DFC0DDD50AE563EFB285BAA236EA805D35612504238A36EFA105958756442B1D9F942D7ABD259F2D59671657B6378E9C7194342A7AAA47A66D1E95",
"type" : "ed25519"
}
}

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,166 @@
# 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:26658"
# 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:26657"
# 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:26656"
# 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
# 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,6 +0,0 @@
{
"priv_key" : {
"data" : "95136FCC97E4446B3141EDF9841078107ECE755E99925D79CCBF91085492680B3CA1034D9917DF1DED4E4AB2D9BC225919F6CB2176F210D2368697CC339DF4E7",
"type" : "ed25519"
}
}

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"
}
}

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