diff --git a/.circleci/config.yml b/.circleci/config.yml index 0de4a1791..0bbe76ffd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,6 +7,13 @@ defaults: &defaults environment: GOBIN: /tmp/workspace/bin +docs_update_config: &docs_update_config + working_directory: ~/repo + docker: + - image: tendermint/docs_deployment + environment: + AWS_REGION: us-east-1 + jobs: setup_dependencies: <<: *defaults @@ -339,10 +346,25 @@ jobs: name: upload command: bash .circleci/codecov.sh -f coverage.txt + deploy_docs: + <<: *docs_update_config + steps: + - checkout + - run: + name: Trigger website build + command: | + chamber exec tendermint -- start_website_build + workflows: version: 2 test-suite: jobs: + - deploy_docs: + filters: + branches: + only: + - master + - develop - setup_dependencies - lint: requires: diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 18978d7e6..ffc8e6fbd 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -8,10 +8,14 @@ Special thanks to external contributors on this release: * CLI/RPC/Config +- [privval] \#2926 split up `PubKeyMsg` into `PubKeyRequest` and `PubKeyResponse` to be consistent with other message types + * Apps -* Go API -- [types] \#2926 Return error on `PrivValidator.GetAddress()`/`PrivValidator.GetPubKey()` instead of panic'ing +* Go API +- [types] \#2926 memoize consensus public key on initialization of remote signer and return the memoized key on +`PrivValidator.GetPubKey()` instead of requesting it again +- [types] \#2981 Remove `PrivValidator.GetAddress()` * Blockchain Protocol @@ -20,7 +24,11 @@ Special thanks to external contributors on this release: ### FEATURES: ### IMPROVEMENTS: +- [rpc] Add `UnconfirmedTxs(limit)` and `NumUnconfirmedTxs()` methods to HTTP/Local clients (@danil-lashin) +- [ci/cd] Updated CircleCI job to trigger website build when docs are updated ### BUG FIXES: - [kv indexer] \#2912 don't ignore key when executing CONTAINS -- [types] \#2926 Return error on `PrivValidator.GetAddress()`/`PrivValidator.GetPubKey()` instead of panic'ing +- [types] \#2926 do not panic if retrieving the private validator's public key fails +- [mempool] \#2994 Don't allow txs with negative gas wanted +- [p2p] \#2715 fix a bug where seeds don't disconnect from a peer after 3h diff --git a/cmd/tendermint/commands/testnet.go b/cmd/tendermint/commands/testnet.go index 1c00844df..1353876f0 100644 --- a/cmd/tendermint/commands/testnet.go +++ b/cmd/tendermint/commands/testnet.go @@ -128,14 +128,31 @@ func testnetFiles(cmd *cobra.Command, args []string) error { } } + // Gather persistent peer addresses. + var ( + persistentPeers string + err error + ) if populatePersistentPeers { - err := populatePersistentPeersInConfigAndWriteIt(config) + persistentPeers, err = persistentPeersString(config) if err != nil { _ = os.RemoveAll(outputDir) return err } } + // Overwrite default config. + for i := 0; i < nValidators+nNonValidators; i++ { + nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i)) + config.SetRoot(nodeDir) + config.P2P.AddrBookStrict = false + if populatePersistentPeers { + config.P2P.PersistentPeers = persistentPeers + } + + cfg.WriteConfigFile(filepath.Join(nodeDir, "config", "config.toml"), config) + } + fmt.Printf("Successfully initialized %v node directories\n", nValidators+nNonValidators) return nil } @@ -158,28 +175,16 @@ func hostnameOrIP(i int) string { return fmt.Sprintf("%s%d", hostnamePrefix, i) } -func populatePersistentPeersInConfigAndWriteIt(config *cfg.Config) error { +func persistentPeersString(config *cfg.Config) (string, error) { persistentPeers := make([]string, nValidators+nNonValidators) for i := 0; i < nValidators+nNonValidators; i++ { nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i)) config.SetRoot(nodeDir) nodeKey, err := p2p.LoadNodeKey(config.NodeKeyFile()) if err != nil { - return err + return "", err } persistentPeers[i] = p2p.IDAddressString(nodeKey.ID(), fmt.Sprintf("%s:%d", hostnameOrIP(i), p2pPort)) } - persistentPeersList := strings.Join(persistentPeers, ",") - - for i := 0; i < nValidators+nNonValidators; i++ { - nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i)) - config.SetRoot(nodeDir) - config.P2P.PersistentPeers = persistentPeersList - config.P2P.AddrBookStrict = false - - // overwrite default config - cfg.WriteConfigFile(filepath.Join(nodeDir, "config", "config.toml"), config) - } - - return nil + return strings.Join(persistentPeers, ","), nil } diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 9f8ddbc73..a1e150c38 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -8,7 +8,12 @@ module.exports = { lineNumbers: true }, themeConfig: { - lastUpdated: "Last Updated", + lastUpdated: true, + algolia: { + apiKey: '59f0e2deb984aa9cdf2b3a5fd24ac501', + indexName: 'tendermint', + debug: false + }, nav: [{ text: "Back to Tendermint", link: "https://tendermint.com" }], sidebar: [ { diff --git a/docs/app-dev/app-architecture.md b/docs/app-dev/app-architecture.md index b141c0f30..943a3cd09 100644 --- a/docs/app-dev/app-architecture.md +++ b/docs/app-dev/app-architecture.md @@ -5,7 +5,7 @@ Tendermint blockchain application. The following diagram provides a superb example: - +![](../imgs/cosmos-tendermint-stack-4k.jpg) 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 diff --git a/docs/app-dev/ecosystem.json b/docs/app-dev/ecosystem.json index 3f0fa3342..570597013 100644 --- a/docs/app-dev/ecosystem.json +++ b/docs/app-dev/ecosystem.json @@ -181,5 +181,13 @@ "language": "Javascript", "author": "Dennis McKinnon" } + ], + "aminoLibraries": [ + { + "name": "JS-Amino", + "url": "https://github.com/TanNgocDo/Js-Amino", + "language": "Javascript", + "author": "TanNgocDo" + } ] } diff --git a/docs/app-dev/ecosystem.md b/docs/app-dev/ecosystem.md index e51ca430a..c87d3658b 100644 --- a/docs/app-dev/ecosystem.md +++ b/docs/app-dev/ecosystem.md @@ -1,11 +1,9 @@ # Ecosystem The growing list of applications built using various pieces of the -Tendermint stack can be found at: +Tendermint stack can be found at the [ecosystem page](https://tendermint.com/ecosystem). -- https://tendermint.com/ecosystem - -We thank the community for their contributions thus far and welcome the +We thank the community for their contributions and welcome the addition of new projects. A pull request can be submitted to [this file](https://github.com/tendermint/tendermint/blob/master/docs/app-dev/ecosystem.json) to include your project. diff --git a/docs/imgs/cosmos-tendermint-stack-4k.jpg b/docs/imgs/cosmos-tendermint-stack-4k.jpg new file mode 100644 index 000000000..b10ffa6a2 Binary files /dev/null and b/docs/imgs/cosmos-tendermint-stack-4k.jpg differ diff --git a/docs/introduction/what-is-tendermint.md b/docs/introduction/what-is-tendermint.md index 389bf9658..a35dd9ec1 100644 --- a/docs/introduction/what-is-tendermint.md +++ b/docs/introduction/what-is-tendermint.md @@ -70,10 +70,6 @@ Tendermint is in essence similar software, but with two key differences: the application logic that's right for them, from key-value store to cryptocurrency to e-voting platform and beyond. -The layout of this Tendermint website content is also ripped directly -and without shame from [consul.io](https://www.consul.io/) and the other -[Hashicorp sites](https://www.hashicorp.com/#tools). - ### Bitcoin, Ethereum, etc. Tendermint emerged in the tradition of cryptocurrencies like Bitcoin, diff --git a/docs/spec/README.md b/docs/spec/README.md index 3e2c2bcd4..7ec9387c2 100644 --- a/docs/spec/README.md +++ b/docs/spec/README.md @@ -14,31 +14,31 @@ please submit them to our [bug bounty](https://tendermint.com/security)! ### Data Structures -- [Encoding and Digests](https://github.com/tendermint/tendermint/blob/master/docs/spec/blockchain/encoding.md) -- [Blockchain](https://github.com/tendermint/tendermint/blob/master/docs/spec/blockchain/blockchain.md) -- [State](https://github.com/tendermint/tendermint/blob/master/docs/spec/blockchain/state.md) +- [Encoding and Digests](./blockchain/encoding.md) +- [Blockchain](./blockchain/blockchain.md) +- [State](./blockchain/state.md) ### Consensus Protocol -- [Consensus Algorithm](/docs/spec/consensus/consensus.md) -- [Creating a proposal](/docs/spec/consensus/creating-proposal.md) -- [Time](/docs/spec/consensus/bft-time.md) -- [Light-Client](/docs/spec/consensus/light-client.md) +- [Consensus Algorithm](./consensus/consensus.md) +- [Creating a proposal](./consensus/creating-proposal.md) +- [Time](./consensus/bft-time.md) +- [Light-Client](./consensus/light-client.md) ### P2P and Network Protocols -- [The Base P2P Layer](https://github.com/tendermint/tendermint/tree/master/docs/spec/p2p): multiplex the protocols ("reactors") on authenticated and encrypted TCP connections -- [Peer Exchange (PEX)](https://github.com/tendermint/tendermint/tree/master/docs/spec/reactors/pex): gossip known peer addresses so peers can find each other -- [Block Sync](https://github.com/tendermint/tendermint/tree/master/docs/spec/reactors/block_sync): gossip blocks so peers can catch up quickly -- [Consensus](https://github.com/tendermint/tendermint/tree/master/docs/spec/reactors/consensus): gossip votes and block parts so new blocks can be committed -- [Mempool](https://github.com/tendermint/tendermint/tree/master/docs/spec/reactors/mempool): gossip transactions so they get included in blocks -- Evidence: Forthcoming, see [this issue](https://github.com/tendermint/tendermint/issues/2329). +- [The Base P2P Layer](./p2p/): multiplex the protocols ("reactors") on authenticated and encrypted TCP connections +- [Peer Exchange (PEX)](./reactors/pex/): gossip known peer addresses so peers can find each other +- [Block Sync](./reactors/block_sync/): gossip blocks so peers can catch up quickly +- [Consensus](./reactors/consensus/): gossip votes and block parts so new blocks can be committed +- [Mempool](./reactors/mempool/): gossip transactions so they get included in blocks +- [Evidence](./reactors/evidence/): sending invalid evidence will stop the peer ### Software -- [ABCI](/docs/spec/software/abci.md): Details about interactions between the +- [ABCI](./software/abci.md): Details about interactions between the application and consensus engine over ABCI -- [Write-Ahead Log](/docs/spec/software/wal.md): Details about how the consensus +- [Write-Ahead Log](./software/wal.md): Details about how the consensus engine preserves data and recovers from crash failures ## Overview diff --git a/libs/log/testing_logger.go b/libs/log/testing_logger.go index 81482bef5..8914bd81f 100644 --- a/libs/log/testing_logger.go +++ b/libs/log/testing_logger.go @@ -1,6 +1,7 @@ package log import ( + "io" "os" "testing" @@ -19,12 +20,22 @@ var ( // inside a test (not in the init func) because // verbose flag only set at the time of testing. func TestingLogger() Logger { + return TestingLoggerWithOutput(os.Stdout) +} + +// TestingLoggerWOutput returns a TMLogger which writes to (w io.Writer) if testing being run +// with the verbose (-v) flag, NopLogger otherwise. +// +// Note that the call to TestingLoggerWithOutput(w io.Writer) must be made +// inside a test (not in the init func) because +// verbose flag only set at the time of testing. +func TestingLoggerWithOutput(w io.Writer) Logger { if _testingLogger != nil { return _testingLogger } if testing.Verbose() { - _testingLogger = NewTMLogger(NewSyncWriter(os.Stdout)) + _testingLogger = NewTMLogger(NewSyncWriter(w)) } else { _testingLogger = NewNopLogger() } diff --git a/mempool/mempool.go b/mempool/mempool.go index 8f70ec6c8..404683158 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -108,6 +108,10 @@ func PostCheckMaxGas(maxGas int64) PostCheckFunc { if maxGas == -1 { return nil } + if res.GasWanted < 0 { + return fmt.Errorf("gas wanted %d is negative", + res.GasWanted) + } if res.GasWanted > maxGas { return fmt.Errorf("gas wanted %d is greater than max gas %d", res.GasWanted, maxGas) @@ -486,11 +490,15 @@ func (mem *Mempool) ReapMaxBytesMaxGas(maxBytes, maxGas int64) types.Txs { return txs } totalBytes += int64(len(memTx.tx)) + aminoOverhead - // Check total gas requirement - if maxGas > -1 && totalGas+memTx.gasWanted > maxGas { + // Check total gas requirement. + // If maxGas is negative, skip this check. + // Since newTotalGas < masGas, which + // must be non-negative, it follows that this won't overflow. + newTotalGas := totalGas + memTx.gasWanted + if maxGas > -1 && newTotalGas > maxGas { return txs } - totalGas += memTx.gasWanted + totalGas = newTotalGas txs = append(txs, memTx.tx) } return txs diff --git a/p2p/conn/connection.go b/p2p/conn/connection.go index c6aad038b..fb20c4775 100644 --- a/p2p/conn/connection.go +++ b/p2p/conn/connection.go @@ -160,6 +160,7 @@ func NewMConnectionWithConfig(conn net.Conn, chDescs []*ChannelDescriptor, onRec onReceive: onReceive, onError: onError, config: config, + created: time.Now(), } // Create channels diff --git a/rpc/client/httpclient.go b/rpc/client/httpclient.go index a1b59ffa0..1a1d88c47 100644 --- a/rpc/client/httpclient.go +++ b/rpc/client/httpclient.go @@ -109,6 +109,24 @@ func (c *HTTP) broadcastTX(route string, tx types.Tx) (*ctypes.ResultBroadcastTx return result, nil } +func (c *HTTP) UnconfirmedTxs(limit int) (*ctypes.ResultUnconfirmedTxs, error) { + result := new(ctypes.ResultUnconfirmedTxs) + _, err := c.rpc.Call("unconfirmed_txs", map[string]interface{}{"limit": limit}, result) + if err != nil { + return nil, errors.Wrap(err, "unconfirmed_txs") + } + return result, nil +} + +func (c *HTTP) NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error) { + result := new(ctypes.ResultUnconfirmedTxs) + _, err := c.rpc.Call("num_unconfirmed_txs", map[string]interface{}{}, result) + if err != nil { + return nil, errors.Wrap(err, "num_unconfirmed_txs") + } + return result, nil +} + func (c *HTTP) NetInfo() (*ctypes.ResultNetInfo, error) { result := new(ctypes.ResultNetInfo) _, err := c.rpc.Call("net_info", map[string]interface{}{}, result) diff --git a/rpc/client/interface.go b/rpc/client/interface.go index f34410c5b..7477225e9 100644 --- a/rpc/client/interface.go +++ b/rpc/client/interface.go @@ -93,3 +93,9 @@ type NetworkClient interface { type EventsClient interface { types.EventBusSubscriber } + +// MempoolClient shows us data about current mempool state. +type MempoolClient interface { + UnconfirmedTxs(limit int) (*ctypes.ResultUnconfirmedTxs, error) + NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error) +} diff --git a/rpc/client/localclient.go b/rpc/client/localclient.go index 8d89b7150..ba8fb3f17 100644 --- a/rpc/client/localclient.go +++ b/rpc/client/localclient.go @@ -76,6 +76,14 @@ func (Local) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { return core.BroadcastTxSync(tx) } +func (Local) UnconfirmedTxs(limit int) (*ctypes.ResultUnconfirmedTxs, error) { + return core.UnconfirmedTxs(limit) +} + +func (Local) NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error) { + return core.NumUnconfirmedTxs() +} + func (Local) NetInfo() (*ctypes.ResultNetInfo, error) { return core.NetInfo() } diff --git a/rpc/client/rpc_test.go b/rpc/client/rpc_test.go index b07b74a39..fa5080f90 100644 --- a/rpc/client/rpc_test.go +++ b/rpc/client/rpc_test.go @@ -281,6 +281,42 @@ func TestBroadcastTxCommit(t *testing.T) { } } +func TestUnconfirmedTxs(t *testing.T) { + _, _, tx := MakeTxKV() + + mempool := node.MempoolReactor().Mempool + _ = mempool.CheckTx(tx, nil) + + for i, c := range GetClients() { + mc, ok := c.(client.MempoolClient) + require.True(t, ok, "%d", i) + txs, err := mc.UnconfirmedTxs(1) + require.Nil(t, err, "%d: %+v", i, err) + assert.Exactly(t, types.Txs{tx}, types.Txs(txs.Txs)) + } + + mempool.Flush() +} + +func TestNumUnconfirmedTxs(t *testing.T) { + _, _, tx := MakeTxKV() + + mempool := node.MempoolReactor().Mempool + _ = mempool.CheckTx(tx, nil) + mempoolSize := mempool.Size() + + for i, c := range GetClients() { + mc, ok := c.(client.MempoolClient) + require.True(t, ok, "%d", i) + res, err := mc.NumUnconfirmedTxs() + require.Nil(t, err, "%d: %+v", i, err) + + assert.Equal(t, mempoolSize, res.N) + } + + mempool.Flush() +} + func TestTx(t *testing.T) { // first we broadcast a tx c := getHTTPClient() diff --git a/types/vote_test.go b/types/vote_test.go index 03110fbfa..942f2d6b9 100644 --- a/types/vote_test.go +++ b/types/vote_test.go @@ -147,6 +147,7 @@ func TestVoteVerifySignature(t *testing.T) { // sign it err := privVal.SignVote("test_chain_id", vote) + require.NoError(t, err) // verify the same vote valid := pubkey.VerifyBytes(vote.SignBytes("test_chain_id"), vote.Signature)