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.md b/CHANGELOG.md index 2ec6b1d94..d7675d296 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,31 @@ # Changelog +## v0.27.1 + +*December 15th, 2018* + +Special thanks to external contributors on this release: +@danil-lashin, @hleb-albau, @james-ray, @leo-xinwang + +### FEATURES: +- [rpc] [\#2964](https://github.com/tendermint/tendermint/issues/2964) Add `UnconfirmedTxs(limit)` and `NumUnconfirmedTxs()` methods to HTTP/Local clients (@danil-lashin) +- [docs] [\#3004](https://github.com/tendermint/tendermint/issues/3004) Enable full-text search on docs pages + +### IMPROVEMENTS: +- [consensus] [\#2971](https://github.com/tendermint/tendermint/issues/2971) Return error if ValidatorSet is empty after InitChain + (@leo-xinwang) +- [ci/cd] [\#3005](https://github.com/tendermint/tendermint/issues/3005) Updated CircleCI job to trigger website build when docs are updated +- [docs] Various updates + +### BUG FIXES: +- [cmd] [\#2983](https://github.com/tendermint/tendermint/issues/2983) `testnet` command always sets `addr_book_strict = false` +- [config] [\#2980](https://github.com/tendermint/tendermint/issues/2980) Fix CORS options formatting +- [kv indexer] [\#2912](https://github.com/tendermint/tendermint/issues/2912) Don't ignore key when executing CONTAINS +- [mempool] [\#2961](https://github.com/tendermint/tendermint/issues/2961) Call `notifyTxsAvailable` if there're txs left after committing a block, but recheck=false +- [mempool] [\#2994](https://github.com/tendermint/tendermint/issues/2994) Reject txs with negative GasWanted +- [p2p] [\#2990](https://github.com/tendermint/tendermint/issues/2990) Fix a bug where seeds don't disconnect from a peer after 3h +- [consensus] [\#3006](https://github.com/tendermint/tendermint/issues/3006) Save state after InitChain only when stateHeight is also 0 (@james-ray) + ## v0.27.0 *December 5th, 2018* diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 7063efc39..e9a1d52f8 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -1,4 +1,4 @@ -## v0.27.1 +## v0.27.2 *TBD* diff --git a/Makefile b/Makefile index 6f7874ee9..d0f8c4393 100644 --- a/Makefile +++ b/Makefile @@ -294,6 +294,7 @@ build-linux: build-docker-localnode: cd networks/local make + cd - # Run a 4-node testnet locally localnet-start: localnet-stop diff --git a/cmd/tendermint/commands/testnet.go b/cmd/tendermint/commands/testnet.go index 0f7dd79a4..7e5635ca6 100644 --- a/cmd/tendermint/commands/testnet.go +++ b/cmd/tendermint/commands/testnet.go @@ -127,14 +127,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 } @@ -157,28 +174,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/config/config.go b/config/config.go index 23b033994..2b9c8758a 100644 --- a/config/config.go +++ b/config/config.go @@ -283,7 +283,7 @@ type RPCConfig struct { // Maximum number of simultaneous connections. // Does not include RPC (HTTP&WebSocket) connections. See max_open_connections - // If you want to accept more significant number than the default, make sure + // If you want to accept a larger number than the default, make sure // you increase your OS limits. // 0 - unlimited. GRPCMaxOpenConnections int `mapstructure:"grpc_max_open_connections"` @@ -293,7 +293,7 @@ type RPCConfig struct { // Maximum number of simultaneous connections (including WebSocket). // Does not include gRPC connections. See grpc_max_open_connections - // If you want to accept more significant number than the default, make sure + // If you want to accept a larger number than the default, make sure // you increase your OS limits. // 0 - unlimited. // Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files} @@ -774,12 +774,12 @@ type InstrumentationConfig struct { PrometheusListenAddr string `mapstructure:"prometheus_listen_addr"` // Maximum number of simultaneous connections. - // If you want to accept more significant number than the default, make sure + // If you want to accept a larger number than the default, make sure // you increase your OS limits. // 0 - unlimited. MaxOpenConnections int `mapstructure:"max_open_connections"` - // Tendermint instrumentation namespace. + // Instrumentation namespace. Namespace string `mapstructure:"namespace"` } diff --git a/config/toml.go b/config/toml.go index 21e017b45..9aa30451d 100644 --- a/config/toml.go +++ b/config/toml.go @@ -125,13 +125,13 @@ laddr = "{{ .RPC.ListenAddress }}" # A list of origins a cross-domain request can be executed from # Default value '[]' disables cors support # Use '["*"]' to allow any origin -cors_allowed_origins = "{{ .RPC.CORSAllowedOrigins }}" +cors_allowed_origins = [{{ range .RPC.CORSAllowedOrigins }}{{ printf "%q, " . }}{{end}}] # A list of methods the client is allowed to use with cross-domain requests -cors_allowed_methods = "{{ .RPC.CORSAllowedMethods }}" +cors_allowed_methods = [{{ range .RPC.CORSAllowedMethods }}{{ printf "%q, " . }}{{end}}] # A list of non simple headers the client is allowed to use with cross-domain requests -cors_allowed_headers = "{{ .RPC.CORSAllowedHeaders }}" +cors_allowed_headers = [{{ range .RPC.CORSAllowedHeaders }}{{ printf "%q, " . }}{{end}}] # TCP or UNIX socket address for the gRPC server to listen on # NOTE: This server only supports /broadcast_tx_commit @@ -139,7 +139,7 @@ grpc_laddr = "{{ .RPC.GRPCListenAddress }}" # Maximum number of simultaneous connections. # Does not include RPC (HTTP&WebSocket) connections. See max_open_connections -# If you want to accept more significant number than the default, make sure +# If you want to accept a larger number than the default, make sure # you increase your OS limits. # 0 - unlimited. # Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files} @@ -151,7 +151,7 @@ unsafe = {{ .RPC.Unsafe }} # Maximum number of simultaneous connections (including WebSocket). # Does not include gRPC connections. See grpc_max_open_connections -# If you want to accept more significant number than the default, make sure +# If you want to accept a larger number than the default, make sure # you increase your OS limits. # 0 - unlimited. # Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files} @@ -269,8 +269,8 @@ blocktime_iota = "{{ .Consensus.BlockTimeIota }}" # 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). +# 1) "null" +# 2) "kv" (default) - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend). indexer = "{{ .TxIndex.Indexer }}" # Comma-separated list of tags to index (by default the only tag is "tx.hash") @@ -302,7 +302,7 @@ prometheus = {{ .Instrumentation.Prometheus }} prometheus_listen_addr = "{{ .Instrumentation.PrometheusListenAddr }}" # Maximum number of simultaneous connections. -# If you want to accept more significant number than the default, make sure +# If you want to accept a larger number than the default, make sure # you increase your OS limits. # 0 - unlimited. max_open_connections = {{ .Instrumentation.MaxOpenConnections }} diff --git a/consensus/replay.go b/consensus/replay.go index c9a779e34..16b66e86d 100644 --- a/consensus/replay.go +++ b/consensus/replay.go @@ -11,7 +11,6 @@ import ( "time" abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/version" //auto "github.com/tendermint/tendermint/libs/autofile" cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" @@ -20,6 +19,7 @@ import ( "github.com/tendermint/tendermint/proxy" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" + "github.com/tendermint/tendermint/version" ) var crc32c = crc32.MakeTable(crc32.Castagnoli) @@ -247,6 +247,7 @@ func (h *Handshaker) Handshake(proxyApp proxy.AppConns) error { // Set AppVersion on the state. h.initialState.Version.Consensus.App = version.Protocol(res.AppVersion) + sm.SaveState(h.stateDB, h.initialState) // Replay blocks up to the latest in the blockstore. _, err = h.ReplayBlocks(h.initialState, appHash, blockHeight, proxyApp) @@ -295,19 +296,27 @@ func (h *Handshaker) ReplayBlocks( return nil, err } - // If the app returned validators or consensus params, update the state. - if len(res.Validators) > 0 { - vals, err := types.PB2TM.ValidatorUpdates(res.Validators) - if err != nil { - return nil, err + if stateBlockHeight == 0 { //we only update state when we are in initial state + // If the app returned validators or consensus params, update the state. + if len(res.Validators) > 0 { + vals, err := types.PB2TM.ValidatorUpdates(res.Validators) + if err != nil { + return nil, err + } + state.Validators = types.NewValidatorSet(vals) + state.NextValidators = types.NewValidatorSet(vals) + } else { + // If validator set is not set in genesis and still empty after InitChain, exit. + if len(h.genDoc.Validators) == 0 { + return nil, fmt.Errorf("Validator set is nil in genesis and still empty after InitChain") + } } - state.Validators = types.NewValidatorSet(vals) - state.NextValidators = types.NewValidatorSet(vals) + + if res.ConsensusParams != nil { + state.ConsensusParams = types.PB2TM.ConsensusParams(res.ConsensusParams) + } + sm.SaveState(h.stateDB, state) } - 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. diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 9f8ddbc73..7c8aeee5e 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -8,7 +8,17 @@ module.exports = { lineNumbers: true }, themeConfig: { - lastUpdated: "Last Updated", + repo: "tendermint/tendermint", + editLinks: true, + docsDir: "docs", + docsBranch: "develop", + editLinkText: 'Edit this page on Github', + lastUpdated: true, + algolia: { + apiKey: '59f0e2deb984aa9cdf2b3a5fd24ac501', + indexName: 'tendermint', + debug: false + }, nav: [{ text: "Back to Tendermint", link: "https://tendermint.com" }], sidebar: [ { diff --git a/docs/DOCS_README.md b/docs/DOCS_README.md index a7671c360..c91d03911 100644 --- a/docs/DOCS_README.md +++ b/docs/DOCS_README.md @@ -12,10 +12,10 @@ respectively. ## How It Works -There is a Jenkins job listening for changes in the `/docs` directory, on both +There is a CircleCI job listening for changes in the `/docs` directory, on both the `master` and `develop` branches. Any updates to files in this directory on those branches will automatically trigger a website deployment. Under the hood, -a private website repository has make targets consumed by a standard Jenkins task. +the private website repository has a `make build-docs` target consumed by a CircleCI job in that repo. ## README @@ -93,6 +93,10 @@ python -m SimpleHTTPServer 8080 then navigate to localhost:8080 in your browser. +## Search + +We are using [Algolia](https://www.algolia.com) to power full-text search. This uses a public API search-only key in the `config.js` as well as a [tendermint.json](https://github.com/algolia/docsearch-configs/blob/master/configs/tendermint.json) configuration file that we can update with PRs. + ## Consistency Because the build processes are identical (as is the information contained herein), this file should be kept in sync as 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/networks/docker-compose.md b/docs/networks/docker-compose.md index a1924eb9d..52616b3d9 100644 --- a/docs/networks/docker-compose.md +++ b/docs/networks/docker-compose.md @@ -1,20 +1,17 @@ # Docker Compose -With Docker Compose, we can spin up local testnets in a single command: - -``` -make localnet-start -``` +With Docker Compose, you can spin up local testnets with a single command. ## Requirements -- [Install tendermint](/docs/install.md) -- [Install docker](https://docs.docker.com/engine/installation/) -- [Install docker-compose](https://docs.docker.com/compose/install/) +1. [Install tendermint](/docs/install.md) +2. [Install docker](https://docs.docker.com/engine/installation/) +3. [Install docker-compose](https://docs.docker.com/compose/install/) ## Build -Build the `tendermint` binary and the `tendermint/localnode` docker image. +Build the `tendermint` binary and, optionally, the `tendermint/localnode` +docker image. Note the binary will be mounted into the container so it can be updated without rebuilding the image. @@ -25,11 +22,10 @@ cd $GOPATH/src/github.com/tendermint/tendermint # Build the linux binary in ./build make build-linux -# Build tendermint/localnode image +# (optionally) Build tendermint/localnode image make build-docker-localnode ``` - ## Run a testnet To start a 4 node testnet run: @@ -38,9 +34,13 @@ To start a 4 node testnet run: make localnet-start ``` -The nodes bind their RPC servers to ports 26657, 26660, 26662, and 26664 on the host. +The nodes bind their RPC servers to ports 26657, 26660, 26662, and 26664 on the +host. + This file creates a 4-node network using the localnode image. -The nodes of the network expose their P2P and RPC endpoints to the host machine on ports 26656-26657, 26659-26660, 26661-26662, and 26663-26664 respectively. + +The nodes of the network expose their P2P and RPC endpoints to the host machine +on ports 26656-26657, 26659-26660, 26661-26662, and 26663-26664 respectively. To update the binary, just rebuild it and restart the nodes: @@ -52,34 +52,40 @@ make localnet-start ## Configuration -The `make localnet-start` creates files for a 4-node testnet in `./build` by calling the `tendermint testnet` command. +The `make localnet-start` creates files for a 4-node testnet in `./build` by +calling the `tendermint testnet` command. -The `./build` directory is mounted to the `/tendermint` mount point to attach the binary and config files to the container. +The `./build` directory is mounted to the `/tendermint` mount point to attach +the binary and config files to the container. -For instance, to create a single node testnet: +To change the number of validators / non-validators change the `localnet-start` Makefile target: + +``` +localnet-start: localnet-stop + @if ! [ -f build/node0/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/tendermint:Z tendermint/localnode testnet --v 5 --n 3 --o . --populate-persistent-peers --starting-ip-address 192.167.10.2 ; fi + docker-compose up +``` + +The command now will generate config files for 5 validators and 3 +non-validators network. + +Before running it, don't forget to cleanup the old files: ``` cd $GOPATH/src/github.com/tendermint/tendermint # Clear the build folder -rm -rf ./build - -# 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 - +rm -rf ./build/node* ``` ## 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. +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. - +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. 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/docs/tendermint-core/configuration.md b/docs/tendermint-core/configuration.md index 7d1a562ec..e66dcf511 100644 --- a/docs/tendermint-core/configuration.md +++ b/docs/tendermint-core/configuration.md @@ -36,22 +36,26 @@ db_backend = "leveldb" # Database directory db_dir = "data" -# Output level for logging -log_level = "state:info,*:error" +# Output level for logging, including package level options +log_level = "main:info,state:info,*:error" # Output format: 'plain' (colored text) or 'json' log_format = "plain" ##### additional base config options ##### -# The ID of the chain to join (should be signed with every transaction and vote) -chain_id = "" - # Path to the JSON file containing the initial validator set and other meta data -genesis_file = "genesis.json" +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 = "priv_validator.json" +priv_validator_file = "config/priv_validator.json" + +# TCP or UNIX socket address for Tendermint to listen on for +# connections from an external PrivValidator process +priv_validator_laddr = "" + +# 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" @@ -74,13 +78,13 @@ laddr = "tcp://0.0.0.0:26657" # A list of origins a cross-domain request can be executed from # Default value '[]' disables cors support # Use '["*"]' to allow any origin -cors_allowed_origins = "[]" +cors_allowed_origins = [] # A list of methods the client is allowed to use with cross-domain requests -cors_allowed_methods = "[HEAD GET POST]" +cors_allowed_methods = ["HEAD", "GET", "POST"] # A list of non simple headers the client is allowed to use with cross-domain requests -cors_allowed_headers = "[Origin Accept Content-Type X-Requested-With X-Server-Time]" +cors_allowed_headers = ["Origin", "Accept", "Content-Type", "X-Requested-With", "X-Server-Time"] # TCP or UNIX socket address for the gRPC server to listen on # NOTE: This server only supports /broadcast_tx_commit @@ -88,7 +92,7 @@ grpc_laddr = "" # Maximum number of simultaneous connections. # Does not include RPC (HTTP&WebSocket) connections. See max_open_connections -# If you want to accept more significant number than the default, make sure +# If you want to accept a larger number than the default, make sure # you increase your OS limits. # 0 - unlimited. # Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files} @@ -100,7 +104,7 @@ unsafe = false # Maximum number of simultaneous connections (including WebSocket). # Does not include gRPC connections. See grpc_max_open_connections -# If you want to accept more significant number than the default, make sure +# If you want to accept a larger number than the default, make sure # you increase your OS limits. # 0 - unlimited. # Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files} @@ -113,6 +117,12 @@ max_open_connections = 900 # Address to listen for incoming connections laddr = "tcp://0.0.0.0:26656" +# Address to advertise to peers for them to dial +# If empty, will use the same port as the laddr, +# and will introspect on the listener or use UPnP +# to figure out the address. +external_address = "" + # Comma separated list of seed nodes to connect to seeds = "" @@ -123,7 +133,7 @@ persistent_peers = "" upnp = false # Path to address book -addr_book_file = "addrbook.json" +addr_book_file = "config/addrbook.json" # Set true for strict address routability rules # Set false for private or local networks @@ -171,26 +181,26 @@ dial_timeout = "3s" recheck = true broadcast = true -wal_dir = "data/mempool.wal" +wal_dir = "" # size of the mempool -size = 100000 +size = 5000 # size of the cache (used to filter transactions we saw earlier) -cache_size = 100000 +cache_size = 10000 ##### consensus configuration options ##### [consensus] wal_file = "data/cs.wal/wal" -timeout_propose = "3000ms" +timeout_propose = "3s" timeout_propose_delta = "500ms" -timeout_prevote = "1000ms" +timeout_prevote = "1s" timeout_prevote_delta = "500ms" -timeout_precommit = "1000ms" +timeout_precommit = "1s" timeout_precommit_delta = "500ms" -timeout_commit = "1000ms" +timeout_commit = "1s" # Make progress as soon as we have all the precommits (as if TimeoutCommit = 0) skip_timeout_commit = false @@ -201,10 +211,10 @@ create_empty_blocks_interval = "0s" # Reactor sleep duration parameters peer_gossip_sleep_duration = "100ms" -peer_query_maj23_sleep_duration = "2000ms" +peer_query_maj23_sleep_duration = "2s" # Block time parameters. Corresponds to the minimum time increment between consecutive blocks. -blocktime_iota = "1000ms" +blocktime_iota = "1s" ##### transactions indexer configuration options ##### [tx_index] @@ -245,7 +255,7 @@ prometheus = false prometheus_listen_addr = ":26660" # Maximum number of simultaneous connections. -# If you want to accept a more significant number than the default, make sure +# If you want to accept a larger number than the default, make sure # you increase your OS limits. # 0 - unlimited. max_open_connections = 3 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..c5f966c4e 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 @@ -548,13 +556,18 @@ func (mem *Mempool) Update( // Remove committed transactions. txsLeft := mem.removeTxs(txs) - // Recheck mempool txs if any txs were committed in the block - if mem.config.Recheck && len(txsLeft) > 0 { - mem.logger.Info("Recheck txs", "numtxs", len(txsLeft), "height", height) - mem.recheckTxs(txsLeft) - // At this point, mem.txs are being rechecked. - // mem.recheckCursor re-scans mem.txs and possibly removes some txs. - // Before mem.Reap(), we should wait for mem.recheckCursor to be nil. + // Either recheck non-committed txs to see if they became invalid + // or just notify there're some txs left. + if len(txsLeft) > 0 { + if mem.config.Recheck { + mem.logger.Info("Recheck txs", "numtxs", len(txsLeft), "height", height) + mem.recheckTxs(txsLeft) + // At this point, mem.txs are being rechecked. + // mem.recheckCursor re-scans mem.txs and possibly removes some txs. + // Before mem.Reap(), we should wait for mem.recheckCursor to be nil. + } else { + mem.notifyTxsAvailable() + } } // Update metrics 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/state/txindex/kv/kv.go b/state/txindex/kv/kv.go index 1e3733ba8..93249b7f9 100644 --- a/state/txindex/kv/kv.go +++ b/state/txindex/kv/kv.go @@ -332,18 +332,18 @@ func isRangeOperation(op query.Operator) bool { } } -func (txi *TxIndex) match(c query.Condition, startKey []byte) (hashes [][]byte) { +func (txi *TxIndex) match(c query.Condition, startKeyBz []byte) (hashes [][]byte) { if c.Op == query.OpEqual { - it := dbm.IteratePrefix(txi.store, startKey) + it := dbm.IteratePrefix(txi.store, startKeyBz) defer it.Close() for ; it.Valid(); it.Next() { hashes = append(hashes, it.Value()) } } else if c.Op == query.OpContains { - // XXX: doing full scan because startKey does not apply here - // For example, if startKey = "account.owner=an" and search query = "accoutn.owner CONSISTS an" - // we can't iterate with prefix "account.owner=an" because we might miss keys like "account.owner=Ulan" - it := txi.store.Iterator(nil, nil) + // XXX: startKey does not apply here. + // For example, if startKey = "account.owner/an/" and search query = "accoutn.owner CONTAINS an" + // we can't iterate with prefix "account.owner/an/" because we might miss keys like "account.owner/Ulan/" + it := dbm.IteratePrefix(txi.store, startKey(c.Tag)) defer it.Close() for ; it.Valid(); it.Next() { if !isTagKey(it.Key()) { diff --git a/state/txindex/kv/kv_test.go b/state/txindex/kv/kv_test.go index 343cfbb55..0f2065146 100644 --- a/state/txindex/kv/kv_test.go +++ b/state/txindex/kv/kv_test.go @@ -89,8 +89,10 @@ func TestTxSearch(t *testing.T) { {"account.date >= TIME 2013-05-03T14:45:00Z", 0}, // search using CONTAINS {"account.owner CONTAINS 'an'", 1}, - // search using CONTAINS + // search for non existing value using CONTAINS {"account.owner CONTAINS 'Vlad'", 0}, + // search using the wrong tag (of numeric type) using CONTAINS + {"account.number CONTAINS 'Iv'", 0}, } for _, tc := range testCases { diff --git a/version/version.go b/version/version.go index 921e1430f..3c1e2f319 100644 --- a/version/version.go +++ b/version/version.go @@ -18,7 +18,7 @@ const ( // TMCoreSemVer is the current version of Tendermint Core. // It's the Semantic Version of the software. // Must be a string because scripts like dist.sh read this file. - TMCoreSemVer = "0.27.0" + TMCoreSemVer = "0.27.1" // ABCISemVer is the semantic version of the ABCI library ABCISemVer = "0.15.0"