From 5138bcb1c79014a22fb76d52bcc7a5a1f595ccaa Mon Sep 17 00:00:00 2001 From: Emmanuel Odeke Date: Fri, 15 Sep 2017 14:02:06 -0600 Subject: [PATCH 01/54] p2p: delete unused and untested *IPRangeCount functions Fixes #602 Delete unused and untested functions: - AddToIPRangeCounts - CheckIPRangeCounts --- p2p/ip_range_counter.go | 29 ----------------------------- 1 file changed, 29 deletions(-) delete mode 100644 p2p/ip_range_counter.go diff --git a/p2p/ip_range_counter.go b/p2p/ip_range_counter.go deleted file mode 100644 index 85d9d407..00000000 --- a/p2p/ip_range_counter.go +++ /dev/null @@ -1,29 +0,0 @@ -package p2p - -import ( - "strings" -) - -// TODO Test -func AddToIPRangeCounts(counts map[string]int, ip string) map[string]int { - changes := make(map[string]int) - ipParts := strings.Split(ip, ":") - for i := 1; i < len(ipParts); i++ { - prefix := strings.Join(ipParts[:i], ":") - counts[prefix] += 1 - changes[prefix] = counts[prefix] - } - return changes -} - -// TODO Test -func CheckIPRangeCounts(counts map[string]int, limits []int) bool { - for prefix, count := range counts { - ipParts := strings.Split(prefix, ":") - numParts := len(ipParts) - if limits[numParts] < count { - return false - } - } - return true -} From f9479b34cb0ef1a27c7c34c27c064198cb483ad1 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 27 Sep 2017 15:44:19 +0400 Subject: [PATCH 02/54] sleep time should be greater than readTimeout (5 sec) otherwise, we're not testing ping/pongs. see https://github.com/tendermint/tendermint/pull/687#issuecomment-332494735 --- rpc/lib/rpc_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpc/lib/rpc_test.go b/rpc/lib/rpc_test.go index 7415cb36..3ceaaa39 100644 --- a/rpc/lib/rpc_test.go +++ b/rpc/lib/rpc_test.go @@ -362,7 +362,7 @@ func TestWSClientPingPong(t *testing.T) { require.Nil(t, err) defer cl.Stop() - time.Sleep(3 * time.Second) + time.Sleep(6 * time.Second) } func randBytes(t *testing.T) []byte { From 382bead5482e8e379aea3bf7857cf8e28d6da850 Mon Sep 17 00:00:00 2001 From: Alexandre Thibault Date: Fri, 29 Sep 2017 11:32:30 +0200 Subject: [PATCH 03/54] rpc: fix client websocket timeout (#687) --- rpc/lib/server/handlers.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rpc/lib/server/handlers.go b/rpc/lib/server/handlers.go index 0fafd2a8..9de6564d 100644 --- a/rpc/lib/server/handlers.go +++ b/rpc/lib/server/handlers.go @@ -488,6 +488,10 @@ func (wsc *wsConnection) readRoutine() { wsc.baseConn.Close() }() + wsc.baseConn.SetPongHandler(func(m string) error { + return wsc.baseConn.SetReadDeadline(time.Now().Add(wsc.readWait)) + }) + for { select { case <-wsc.Quit: From a221736eeeebafec3ec1d687f11859997392da2a Mon Sep 17 00:00:00 2001 From: James Pettit Date: Fri, 29 Sep 2017 23:18:45 -0700 Subject: [PATCH 04/54] Fix typo in using-tendermint.rst configutation -> configuration --- docs/using-tendermint.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/using-tendermint.rst b/docs/using-tendermint.rst index 1cb3ad86..6e870851 100644 --- a/docs/using-tendermint.rst +++ b/docs/using-tendermint.rst @@ -116,7 +116,7 @@ new blockchain will not make any blocks. Configuration ------------- -Tendermint uses a ``config.toml`` for configutation. For details, see +Tendermint uses a ``config.toml`` for configuration. For details, see `the documentation <./specification/configuration.html>`__. Notable options include the socket address of the application From 2d6bc8d7d781d9ac05fdc8a8b493ccc6d304d1b3 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 25 Sep 2017 18:53:17 +0300 Subject: [PATCH 05/54] bump up Golang version to 1.9.0 --- Vagrantfile | 6 +++--- scripts/tendermint-builder/Dockerfile | 2 +- test/docker/Dockerfile | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Vagrantfile b/Vagrantfile index c465ed73..0f69feed 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -17,11 +17,11 @@ Vagrant.configure("2") do |config| usermod -a -G docker vagrant apt-get autoremove -y - curl -O https://storage.googleapis.com/golang/go1.8.linux-amd64.tar.gz - tar -xvf go1.8.linux-amd64.tar.gz + curl -O https://storage.googleapis.com/golang/go1.9.linux-amd64.tar.gz + tar -xvf go1.9.linux-amd64.tar.gz rm -rf /usr/local/go mv go /usr/local - rm -f go1.8.linux-amd64.tar.gz + rm -f go1.9.linux-amd64.tar.gz mkdir -p /home/vagrant/go/bin echo 'export PATH=$PATH:/usr/local/go/bin:/home/vagrant/go/bin' >> /home/vagrant/.bash_profile echo 'export GOPATH=/home/vagrant/go' >> /home/vagrant/.bash_profile diff --git a/scripts/tendermint-builder/Dockerfile b/scripts/tendermint-builder/Dockerfile index daf93171..0c5130c5 100644 --- a/scripts/tendermint-builder/Dockerfile +++ b/scripts/tendermint-builder/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.8.3 +FROM golang:1.9.0 RUN apt-get update && apt-get install -y --no-install-recommends \ zip \ diff --git a/test/docker/Dockerfile b/test/docker/Dockerfile index 8a2702fc..7e5cecef 100644 --- a/test/docker/Dockerfile +++ b/test/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.8.3 +FROM golang:1.9.0 # Add testing deps for curl RUN echo 'deb http://httpredir.debian.org/debian testing main non-free contrib' >> /etc/apt/sources.list From 17238360141420b0e9f51ca0b6f97dfdbacaf7c3 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 25 Sep 2017 18:57:18 +0300 Subject: [PATCH 06/54] update Dockerfile [ci skip] --- DOCKER/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DOCKER/Dockerfile b/DOCKER/Dockerfile index 55f2e14a..019e1f30 100644 --- a/DOCKER/Dockerfile +++ b/DOCKER/Dockerfile @@ -1,8 +1,8 @@ FROM alpine:3.6 # This is the release of tendermint to pull in. -ENV TM_VERSION 0.10.0 -ENV TM_SHA256SUM a29852b8d51c00db93c87c3d148fa419a047abd38f32b2507a905805131acc19 +ENV TM_VERSION 0.11.0 +ENV TM_SHA256SUM 7e443bac4d42f12e7beaf9cee63b4a565dad8c58895291fdedde8057088b70c5 # 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 From c8789492dc5181b9fb01b59ea90515ff29d43b34 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 25 Sep 2017 20:06:49 +0300 Subject: [PATCH 07/54] update docker readme [ci skip] --- DOCKER/README.md | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/DOCKER/README.md b/DOCKER/README.md index e191abc3..e5c6fee3 100644 --- a/DOCKER/README.md +++ b/DOCKER/README.md @@ -1,6 +1,7 @@ # Supported tags and respective `Dockerfile` links -- `0.10.0`, `latest` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/e5342f4054ab784b2cd6150e14f01053d7c8deb2/DOCKER/Dockerfile) +- `0.11.0`, `latest` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/9177cc1f64ca88a4a0243c5d1773d10fba67e201/DOCKER/Dockerfile) +- `0.10.0` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/e5342f4054ab784b2cd6150e14f01053d7c8deb2/DOCKER/Dockerfile) - `0.9.1`, `0.9`, [(Dockerfile)](https://github.com/tendermint/tendermint/blob/809e0e8c5933604ba8b2d096803ada7c5ec4dfd3/DOCKER/Dockerfile) - `0.9.0` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/d474baeeea6c22b289e7402449572f7c89ee21da/DOCKER/Dockerfile) - `0.8.0`, `0.8` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/bf64dd21fdb193e54d8addaaaa2ecf7ac371de8c/DOCKER/Dockerfile) @@ -8,13 +9,24 @@ `develop` tag points to the [develop](https://github.com/tendermint/tendermint/tree/develop) branch. +# Quick reference + +* **Where to get help:** + [Chat on Rocket](https://cosmos.rocket.chat/) + +* **Where to file issues:** + https://github.com/tendermint/tendermint/issues + +* **Supported Docker versions:** + [the latest release](https://github.com/moby/moby/releases) (down to 1.6 on a best-effort basis) + # Tendermint Tendermint Core is Byzantine Fault Tolerant (BFT) middleware that takes a state transition machine, written in any programming language, and securely replicates it on many machines. -For more background, see the [introduction](https://tendermint.com/intro). +For more background, see the [introduction](https://tendermint.readthedocs.io/en/master/introduction.html). -To get started developing applications, see the [application developers guide](https://tendermint.com/docs/guides/app-development). +To get started developing applications, see the [application developers guide](https://tendermint.readthedocs.io/en/master/getting-started.html). # How to use this image @@ -31,26 +43,12 @@ docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint node --proxy_app If you want to see many containers talking to each other, consider using [mintnet-kubernetes](https://github.com/tendermint/tools/tree/master/mintnet-kubernetes), which is a tool for running Tendermint-based applications on a Kubernetes cluster. -# Supported Docker versions - -This image is officially supported on Docker version 1.13.1. - -Support for older versions (down to 1.6) is provided on a best-effort basis. - -Please see [the Docker installation documentation](https://docs.docker.com/installation/) for details on how to upgrade your Docker daemon. - # License View [license information](https://raw.githubusercontent.com/tendermint/tendermint/master/LICENSE) for the software contained in this image. # User Feedback -## Issues - -If you have any problems with or questions about this image, please contact us through a [GitHub](https://github.com/tendermint/tendermint/issues) issue. If the issue is related to a CVE, please check for [a `cve-tracker` issue on the `official-images` repository](https://github.com/docker-library/official-images/issues?q=label%3Acve-tracker) first. - -You can also reach the image maintainers via [Slack](http://forum.tendermint.com:3000/). - ## Contributing You are invited to contribute new features, fixes, or updates, large or small; we are always thrilled to receive pull requests, and do our best to process them as fast as we can. From 498ff803db6e16227e104e0c04c8fe41258992c2 Mon Sep 17 00:00:00 2001 From: Tino Rusch Date: Mon, 25 Sep 2017 19:38:33 +0200 Subject: [PATCH 08/54] [README] added passchain to application list; This adds passchain to the 'applications' part of the toplevel README.md file. Passchain is a distributed password sharing system built on top of tendermint. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 42005797..b703fd43 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ https://camo.githubusercontent.com/915b7be44ada53c290eb157634330494ebe3e30a/6874 [![](https://tokei.rs/b1/github/tendermint/tendermint?category=lines)](https://github.com/tendermint/tendermint) -Branch | Tests | Coverage +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) @@ -56,6 +56,7 @@ All resources involving the use of, building application on, or developing for, * [Ethermint](http://github.com/tendermint/ethermint); Ethereum on Tendermint * [Cosmos SDK](http://github.com/cosmos/cosmos-sdk); a cryptocurrency application framework +* [Passchain](http://github.com/trusch/passchain); a secret sharing system for teams ### More From bfdad916a2f16b71f4e5bebd40b35dbc655f4e45 Mon Sep 17 00:00:00 2001 From: Adrian Brink Date: Mon, 25 Sep 2017 20:19:27 +0200 Subject: [PATCH 09/54] Update ecosystem.rst --- docs/ecosystem.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/ecosystem.rst b/docs/ecosystem.rst index 188f5009..66646fea 100644 --- a/docs/ecosystem.rst +++ b/docs/ecosystem.rst @@ -85,6 +85,8 @@ ABCI Servers +-------------------------------------------------------------+--------------------+--------------+ | `abci_server `__ | Krzysztof Jurewicz | Erlang | +-------------------------------------------------------------+--------------------+--------------+ +| `abci_server `__   | Adrian Brink | Rust       | ++-------------------------------------------------------------+--------------------+--------------+ Deployment Tools ---------------- From 60a2867af21f330e71f7b66fa2eeae008bb02b55 Mon Sep 17 00:00:00 2001 From: Zach Ramsay Date: Mon, 25 Sep 2017 14:50:18 -0400 Subject: [PATCH 10/54] docs/ecosystem: add ABCI implementations --- docs/ecosystem.rst | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/docs/ecosystem.rst b/docs/ecosystem.rst index 66646fea..b71ac18d 100644 --- a/docs/ecosystem.rst +++ b/docs/ecosystem.rst @@ -63,10 +63,14 @@ Passwerk Encrypted storage web-utility backed by Tendermint, written in Go, `authored by Rigel Rozanski `__. +Py-Tendermint +^^^^^^^^^^^^^ + +A Python microframework for building blockchain applications with Tendermint, written in Python, `authored by Dave Bryson `__. + ABCI Servers ------------ - +-------------------------------------------------------------+--------------------+--------------+ | **Name** | **Author** | **Language** | | | | | @@ -75,7 +79,9 @@ ABCI Servers +-------------------------------------------------------------+--------------------+--------------+ | `js abci `__ | Tendermint | Javascript | +-------------------------------------------------------------+--------------------+--------------+ -| `cpp-tmsp `__ | Martin Dyring | C++ | +| `cpp-tmsp `__ | Martin Dyring | C++ | ++-------------------------------------------------------------+--------------------+--------------+ +| `c-abci` `__ | ChainX | C | +-------------------------------------------------------------+--------------------+--------------+ | `jabci `__ | jTendermint | Java | +-------------------------------------------------------------+--------------------+--------------+ @@ -87,6 +93,10 @@ ABCI Servers +-------------------------------------------------------------+--------------------+--------------+ | `abci_server `__   | Adrian Brink | Rust       | +-------------------------------------------------------------+--------------------+--------------+ +| `hs-abci `__ | Alberto Gonzalez | Haskell | ++-------------------------------------------------------------+--------------------+--------------+ +| `haskell-abci `__ | Christoper Goes | Haskell | ++-------------------------------------------------------------+--------------------+--------------+ Deployment Tools ---------------- From e40d1b36f7c94534d7b774f6db845770baf32de2 Mon Sep 17 00:00:00 2001 From: Tino Rusch Date: Tue, 26 Sep 2017 09:03:13 +0200 Subject: [PATCH 11/54] docs: added passchain to the ecosystem.rst in the applications section; --- docs/ecosystem.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/ecosystem.rst b/docs/ecosystem.rst index b71ac18d..3137f50b 100644 --- a/docs/ecosystem.rst +++ b/docs/ecosystem.rst @@ -68,6 +68,11 @@ Py-Tendermint A Python microframework for building blockchain applications with Tendermint, written in Python, `authored by Dave Bryson `__. +Passchain +^^^^^^^^^ + +Passchain is a tool to securely store and share passwords, tokens and other short secrets, `authored by trusch `__. + ABCI Servers ------------ From 40b5defe18be7249f84e6603ef66adb0ade741fc Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 26 Sep 2017 13:46:36 +0300 Subject: [PATCH 12/54] release script [ci skip] --- Makefile | 1 + scripts/publish.sh | 1 + scripts/release.sh | 53 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100755 scripts/release.sh diff --git a/Makefile b/Makefile index 8c9c5214..a557dd86 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ GOTOOLS = \ github.com/mitchellh/gox \ + github.com/tcnksm/ghr \ github.com/Masterminds/glide \ honnef.co/go/tools/cmd/megacheck diff --git a/scripts/publish.sh b/scripts/publish.sh index 3091575f..ba944087 100755 --- a/scripts/publish.sh +++ b/scripts/publish.sh @@ -12,6 +12,7 @@ if [ -z "$VERSION" ]; then echo "Please specify a version." exit 1 fi +echo "==> Copying ${DIST_DIR} to S3..." # copy to s3 aws s3 cp --recursive ${DIST_DIR} s3://tendermint/binaries/tendermint/v${VERSION} --acl public-read diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100755 index 00000000..02899ad5 --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +set -e + +# Get the version from the environment, or try to figure it out. +if [ -z $VERSION ]; then + VERSION=$(awk -F\" '/Version =/ { print $2; exit }' < version/version.go) +fi +if [ -z "$VERSION" ]; then + echo "Please specify a version." + exit 1 +fi +echo "==> Releasing version $VERSION..." + +# Get the parent directory of where this script is. +SOURCE="${BASH_SOURCE[0]}" +while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done +DIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )" + +# Change into that dir because we expect that. +cd "$DIR" + +# Building binaries +sh -c "'$DIR/scripts/dist.sh'" + +# Pushing binaries to S3 +sh -c "'$DIR/scripts/publish.sh'" + +echo "==> Crafting a Github release" +today=$(date +"%B-%d-%Y") +ghr -b "https://github.com/tendermint/tendermint/blob/master/CHANGELOG.md#${VERSION//.}-${today,}" "v$VERSION" "$DIR/build/dist" + +# Build and push Docker image + +## Get SHA256SUM of the linux archive +SHA256SUM=$(shasum -a256 "${DIR}/build/dist/tendermint_${VERSION}_linux_amd64.zip" | awk '{print $1;}') + +## Replace TM_VERSION and TM_SHA256SUM with the new values +sed -i -e "s/TM_VERSION .*/TM_VERSION $VERSION/g" "$DIR/DOCKER/Dockerfile" +sed -i -e "s/TM_SHA256SUM .*/TM_SHA256SUM $SHA256SUM/g" "$DIR/DOCKER/Dockerfile" +git commit -m "update Dockerfile" -a "$DIR/DOCKER/Dockerfile" +echo "==> TODO: update DOCKER/README.md (latest Dockerfile's hash is $(git rev-parse HEAD)) and copy it's content to https://store.docker.com/community/images/tendermint/tendermint" + +pushd "$DIR/DOCKER" + +## Build Docker image +TAG=$VERSION sh -c "'./build.sh'" + +## Push Docker image +TAG=$VERSION sh -c "'./push.sh'" + +popd + +exit 0 From b61f5482d4f192dd784e9d8360938704670ce8d1 Mon Sep 17 00:00:00 2001 From: Martin Dyring-Andersen Date: Tue, 26 Sep 2017 15:06:21 +0200 Subject: [PATCH 13/54] Fix broken reference to ABCI --- rpc/grpc/types.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpc/grpc/types.proto b/rpc/grpc/types.proto index b32393a0..a7d18dae 100644 --- a/rpc/grpc/types.proto +++ b/rpc/grpc/types.proto @@ -1,7 +1,7 @@ syntax = "proto3"; package core_grpc; -import "github.com/tendermint/abci/types/types.proto"; +import "github.com/tendermint/abci/blob/master/types/types.proto"; //---------------------------------------- // Message types From ce36a0111a9771882f7196aecab63e1c0a9804ff Mon Sep 17 00:00:00 2001 From: Alexandre Thibault Date: Fri, 29 Sep 2017 11:31:39 +0200 Subject: [PATCH 14/54] rpc: subscribe on reconnection (#689) * rpc: subscribe on reconnection * rpc: fix unit tests --- rpc/client/httpclient.go | 10 ++++++++++ rpc/lib/client/ws_client.go | 7 +++++-- rpc/lib/client/ws_client_test.go | 2 ++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/rpc/client/httpclient.go b/rpc/client/httpclient.go index 7f29183d..d068ee95 100644 --- a/rpc/client/httpclient.go +++ b/rpc/client/httpclient.go @@ -305,6 +305,14 @@ func (w *WSEvents) RemoveListener(listenerID string) { w.EventSwitch.RemoveListener(listenerID) } +// After being reconnected, it is necessary to redo subscription +// to server otherwise no data will be automatically received +func (w *WSEvents) redoSubscriptions() { + for event, _ := range w.evtCount { + w.subscribe(event) + } +} + // eventListener is an infinite loop pulling all websocket events // and pushing them to the EventSwitch. // @@ -327,6 +335,8 @@ func (w *WSEvents) eventListener() { // before cleaning up the w.ws stuff w.done <- true return + case <-w.ws.ReconnectCh: + w.redoSubscriptions() } } } diff --git a/rpc/lib/client/ws_client.go b/rpc/lib/client/ws_client.go index 1407073a..788cb860 100644 --- a/rpc/lib/client/ws_client.go +++ b/rpc/lib/client/ws_client.go @@ -41,8 +41,9 @@ type WSClient struct { PingPongLatencyTimer metrics.Timer // user facing channels, closed only when the client is being stopped. - ResultsCh chan json.RawMessage - ErrorsCh chan error + ResultsCh chan json.RawMessage + ErrorsCh chan error + ReconnectCh chan bool // internal channels send chan types.RPCRequest // user requests @@ -139,6 +140,7 @@ func (c *WSClient) OnStart() error { c.ResultsCh = make(chan json.RawMessage) c.ErrorsCh = make(chan error) + c.ReconnectCh = make(chan bool) c.send = make(chan types.RPCRequest) // 1 additional error may come from the read/write @@ -254,6 +256,7 @@ func (c *WSClient) reconnect() error { c.Logger.Error("failed to redial", "err", err) } else { c.Logger.Info("reconnected") + c.ReconnectCh <- true return nil } diff --git a/rpc/lib/client/ws_client_test.go b/rpc/lib/client/ws_client_test.go index f5aa027f..e90fc29d 100644 --- a/rpc/lib/client/ws_client_test.go +++ b/rpc/lib/client/ws_client_test.go @@ -186,6 +186,8 @@ func callWgDoneOnResult(t *testing.T, c *WSClient, wg *sync.WaitGroup) { if err != nil { t.Fatalf("unexpected error: %v", err) } + case <-c.ReconnectCh: + t.Log("Reconnected") case <-c.Quit: return } From 45ff7cdd0c1ed4a4cfd6e63f19097e9245efa2ad Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Fri, 29 Sep 2017 14:11:46 +0400 Subject: [PATCH 15/54] rewrite ws client to expose a callback instead of a channel callback gives more power to the publisher. plus it is optional comparing to a channel, which will block the whole client if you won't read from it. --- rpc/client/httpclient.go | 6 +++--- rpc/lib/client/ws_client.go | 21 ++++++++++++++++----- rpc/lib/client/ws_client_test.go | 2 -- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/rpc/client/httpclient.go b/rpc/client/httpclient.go index d068ee95..7b09b5bd 100644 --- a/rpc/client/httpclient.go +++ b/rpc/client/httpclient.go @@ -226,7 +226,9 @@ func (w *WSEvents) Start() (bool, error) { st, err := w.EventSwitch.Start() // if we did start, then OnStart here... if st && err == nil { - ws := rpcclient.NewWSClient(w.remote, w.endpoint) + ws := rpcclient.NewWSClient(w.remote, w.endpoint, rpcclient.OnReconnect(func() { + w.redoSubscriptions() + })) _, err = ws.Start() if err == nil { w.ws = ws @@ -335,8 +337,6 @@ func (w *WSEvents) eventListener() { // before cleaning up the w.ws stuff w.done <- true return - case <-w.ws.ReconnectCh: - w.redoSubscriptions() } } } diff --git a/rpc/lib/client/ws_client.go b/rpc/lib/client/ws_client.go index 788cb860..2bdfa5c9 100644 --- a/rpc/lib/client/ws_client.go +++ b/rpc/lib/client/ws_client.go @@ -41,9 +41,11 @@ type WSClient struct { PingPongLatencyTimer metrics.Timer // user facing channels, closed only when the client is being stopped. - ResultsCh chan json.RawMessage - ErrorsCh chan error - ReconnectCh chan bool + ResultsCh chan json.RawMessage + ErrorsCh chan error + + // Callback, which will be called each time after successful reconnect. + onReconnect func() // internal channels send chan types.RPCRequest // user requests @@ -125,6 +127,14 @@ func PingPeriod(pingPeriod time.Duration) func(*WSClient) { } } +// OnReconnect sets the callback, which will be called every time after +// successful reconnect. +func OnReconnect(cb func()) func(*WSClient) { + return func(c *WSClient) { + c.onReconnect = cb + } +} + // String returns WS client full address. func (c *WSClient) String() string { return fmt.Sprintf("%s (%s)", c.Address, c.Endpoint) @@ -140,7 +150,6 @@ func (c *WSClient) OnStart() error { c.ResultsCh = make(chan json.RawMessage) c.ErrorsCh = make(chan error) - c.ReconnectCh = make(chan bool) c.send = make(chan types.RPCRequest) // 1 additional error may come from the read/write @@ -256,7 +265,9 @@ func (c *WSClient) reconnect() error { c.Logger.Error("failed to redial", "err", err) } else { c.Logger.Info("reconnected") - c.ReconnectCh <- true + if c.onReconnect != nil { + go c.onReconnect() + } return nil } diff --git a/rpc/lib/client/ws_client_test.go b/rpc/lib/client/ws_client_test.go index e90fc29d..f5aa027f 100644 --- a/rpc/lib/client/ws_client_test.go +++ b/rpc/lib/client/ws_client_test.go @@ -186,8 +186,6 @@ func callWgDoneOnResult(t *testing.T, c *WSClient, wg *sync.WaitGroup) { if err != nil { t.Fatalf("unexpected error: %v", err) } - case <-c.ReconnectCh: - t.Log("Reconnected") case <-c.Quit: return } From c2f6ff759bb3a01b011ea534e70cbdd8bc914d7a Mon Sep 17 00:00:00 2001 From: Zach Ramsay Date: Mon, 2 Oct 2017 13:02:45 -0400 Subject: [PATCH 16/54] typo --- docs/ecosystem.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/ecosystem.rst b/docs/ecosystem.rst index 3137f50b..b6df3060 100644 --- a/docs/ecosystem.rst +++ b/docs/ecosystem.rst @@ -86,7 +86,7 @@ ABCI Servers +-------------------------------------------------------------+--------------------+--------------+ | `cpp-tmsp `__ | Martin Dyring | C++ | +-------------------------------------------------------------+--------------------+--------------+ -| `c-abci` `__ | ChainX | C | +| `c-abci `__ | ChainX | C | +-------------------------------------------------------------+--------------------+--------------+ | `jabci `__ | jTendermint | Java | +-------------------------------------------------------------+--------------------+--------------+ @@ -96,7 +96,7 @@ ABCI Servers +-------------------------------------------------------------+--------------------+--------------+ | `abci_server `__ | Krzysztof Jurewicz | Erlang | +-------------------------------------------------------------+--------------------+--------------+ -| `abci_server `__   | Adrian Brink | Rust       | +| `rust-tsp `__   | Adrian Brink | Rust       | +-------------------------------------------------------------+--------------------+--------------+ | `hs-abci `__ | Alberto Gonzalez | Haskell | +-------------------------------------------------------------+--------------------+--------------+ From aa57e89e216e886ce6927cb6989692f0165971d0 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Mon, 2 Oct 2017 14:28:04 -0400 Subject: [PATCH 17/54] changelog: add genesis amount->power --- CHANGELOG.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ccc7e0a..e911e073 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,14 +31,16 @@ BUG FIXES: ## 0.11.0 (September 22, 2017) BREAKING: - - state: every validator set change is persisted to disk, which required some changes to the `State` structure. - - cmd: if there is no genesis, exit immediately instead of waiting around for one to show. - - p2p: new `p2p.Peer` interface used for all reactor methods (instead of `*p2p.Peer` struct). - - types: `Signer.Sign` returns an error. + - genesis file: validator `amount` is now `power` + - abci: Info, BeginBlock, InitChain all take structs - rpc: various changes to match JSONRPC spec (http://www.jsonrpc.org/specification), including breaking ones: - requests that previously returned HTTP code 4XX now return 200 with an error code in the JSONRPC. - `rpctypes.RPCResponse` uses new `RPCError` type instead of `string`. - - abci: Info, BeginBlock, InitChain all take structs + + - cmd: if there is no genesis, exit immediately instead of waiting around for one to show. + - types: `Signer.Sign` returns an error. + - state: every validator set change is persisted to disk, which required some changes to the `State` structure. + - p2p: new `p2p.Peer` interface used for all reactor methods (instead of `*p2p.Peer` struct). FEATURES: - rpc: `/validators?height=X` allows querying of validators at previous heights. From ed5511dc089d37de3d33baf0e87f0c1de5db457b Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Mon, 2 Oct 2017 14:28:43 -0400 Subject: [PATCH 18/54] glide: update for autofile fix --- glide.lock | 70 ++++++++++++++++++++++++++---------------------------- glide.yaml | 2 +- 2 files changed, 35 insertions(+), 37 deletions(-) diff --git a/glide.lock b/glide.lock index 6e295a12..a7a2bb5a 100644 --- a/glide.lock +++ b/glide.lock @@ -1,16 +1,18 @@ -hash: e3649cac7b1b9a23c024a9d1bbebd5a147861d55da2bca77c95129b6021850b4 -updated: 2017-09-22T13:24:29.443800586-04:00 +hash: 816d84782ab66637e02bd0a3c7f652a9a31f9b88e3ae11438c5bf641cf585f19 +updated: 2017-10-02T23:32:49.162422718-04:00 imports: - name: github.com/btcsuite/btcd - version: 4803a8291c92a1d2d41041b942a9a9e37deab065 + version: b8df516b4b267acf2de46be593a9d948d1d2c420 subpackages: - btcec +- name: github.com/btcsuite/fastsha256 + version: 637e656429416087660c84436a2a035d69d54e2e - name: github.com/ebuchman/fail-test version: 95f809107225be108efcf10a3509e4ea6ceef3c4 - name: github.com/fsnotify/fsnotify version: 4da3e2cfbabc9f751898f250b49f2439785783a1 - name: github.com/go-kit/kit - version: 0d313fb5fb3a94d87d61e6434785264e87a5d740 + version: d67bb4c202e3b91377d1079b110a6c9ce23ab2f8 subpackages: - log - log/level @@ -24,25 +26,22 @@ imports: - name: github.com/go-playground/universal-translator version: 71201497bace774495daed26a3874fd339e0b538 - name: github.com/go-stack/stack - version: 817915b46b97fd7bb80e8ab6b69f01a53ac3eebf + version: 100eb0c0a9c5b306ca2fb4f165df21d80ada4b82 - name: github.com/gogo/protobuf - version: 2adc21fd136931e0388e278825291678e1d98309 + version: f7f1376d9d231a646d4e62fe1075623ced6db327 subpackages: - proto - name: github.com/golang/protobuf - version: 130e6b02ab059e7b717a096f397c5b60111cae74 + version: 18c9bb3261723cd5401db4d0c9fbc5c3b6c70fe8 subpackages: - proto - - ptypes - ptypes/any - - ptypes/duration - - ptypes/timestamp - name: github.com/golang/snappy version: 553a641470496b2327abcac10b36396bd98e45c9 - name: github.com/gorilla/websocket - version: 6f34763140ed8887aed6a044912009832b4733d7 + version: a91eba7f97777409bc2c443f5534d41dd20c5720 - name: github.com/hashicorp/hcl - version: 68e816d1c783414e79bc65b3994d9ab6b0a722ab + version: 392dba7d905ed5d04a5794ba89f558b27e2ba1ca subpackages: - hcl/ast - hcl/parser @@ -59,31 +58,33 @@ imports: - name: github.com/kr/logfmt version: b84e30acd515aadc4b783ad4ff83aff3299bdfe0 - name: github.com/magiconair/properties - version: 8d7837e64d3c1ee4e54a880c5a920ab4316fc90a + version: 51463bfca2576e06c62a8504b5c0f06d61312647 - name: github.com/mitchellh/mapstructure - version: d0303fe809921458f417bcf828397a65db30a7e4 + version: cc8532a8e9a55ea36402aa21efdf403a60d34096 +- name: github.com/pelletier/go-buffruneio + version: c37440a7cf42ac63b919c752ca73a85067e05992 - name: github.com/pelletier/go-toml - version: 1d6b12b7cb290426e27e6b4e38b89fcda3aeef03 + version: 13d49d4606eb801b8f01ae542b4afc4c6ee3d84a - name: github.com/pkg/errors version: 645ef00459ed84a119197bfb8d8205042c6df63d - name: github.com/rcrowley/go-metrics version: 1f30fe9094a513ce4c700b9a54458bbb0c96996c - name: github.com/spf13/afero - version: ee1bd8ee15a1306d1f9201acc41ef39cd9f99a1b + version: 9be650865eab0c12963d8753212f4f9c66cdcf12 subpackages: - mem - name: github.com/spf13/cast version: acbeb36b902d72a7a4c18e8f3241075e7ab763e4 - name: github.com/spf13/cobra - version: b78744579491c1ceeaaa3b40205e56b0591b93a3 + version: 4cdb38c072b86bf795d2c81de50784d9fdd6eb77 - name: github.com/spf13/jwalterweatherman - version: 12bd96e66386c1960ab0f74ced1362f66f552f7b + version: 8f07c835e5cc1450c082fe3a439cf87b0cbb2d99 - name: github.com/spf13/pflag - version: 7aff26db30c1be810f9de5038ec5ef96ac41fd7c + version: e57e3eeb33f795204c1ca35f56c44f83227c6e66 - name: github.com/spf13/viper - version: 25b30aa063fc18e48662b86996252eabdcf2f0c7 + version: 0967fc9aceab2ce9da34061253ac10fb99bba5b2 - name: github.com/syndtr/goleveldb - version: b89cc31ef7977104127d34c1bd31ebd1a9db2199 + version: 8c81ea47d4c41a385645e133e15510fc6a2a74b4 subpackages: - leveldb - leveldb/cache @@ -122,7 +123,7 @@ imports: subpackages: - iavl - name: github.com/tendermint/tmlibs - version: 9997e3a3b46db1d2f88aa9816ed0e7915dad6ac1 + version: 096dcb90e60aa00b748b3fe49a4b95e48ebf1e13 subpackages: - autofile - cli @@ -136,7 +137,7 @@ imports: - merkle - test - name: golang.org/x/crypto - version: 7d9177d70076375b9a59c8fde23d52d9c4a7ecd5 + version: c7af5bf2638a1164f2eb5467c39c6cffbd13a02e subpackages: - curve25519 - nacl/box @@ -147,7 +148,7 @@ imports: - ripemd160 - salsa20/salsa - name: golang.org/x/net - version: 0744d001aa8470aaa53df28d32e5ceeb8af9bd70 + version: feeb485667d1fdabe727840fe00adc22431bc86e subpackages: - context - http2 @@ -157,46 +158,43 @@ imports: - lex/httplex - trace - name: golang.org/x/sys - version: 429f518978ab01db8bb6f44b66785088e7fba58b + version: e62c3de784db939836898e5c19ffd41bece347da subpackages: - unix - name: golang.org/x/text - version: 1cbadb444a806fd9430d14ad08967ed91da4fa0a + version: 470f45bf29f4147d6fbd7dfd0a02a848e49f5bf4 subpackages: - secure/bidirule - transform - unicode/bidi - unicode/norm - name: google.golang.org/genproto - version: 1e559d0a00eef8a9a43151db4665280bd8dd5886 + version: 411e09b969b1170a9f0c467558eb4c4c110d9c77 subpackages: - googleapis/rpc/status - name: google.golang.org/grpc - version: d4b75ebd4f9f8c4a2b1cdadbdbe0d7920431ccca + version: 844f573616520565fdc6fb4db242321b5456fd6d subpackages: - - balancer - codes - - connectivity - credentials - - grpclb/grpc_lb_v1/messages + - grpclb/grpc_lb_v1 - grpclog - internal - keepalive - metadata - naming - peer - - resolver - stats - status - tap - transport - name: gopkg.in/go-playground/validator.v9 - version: a021b2ec9a8a8bb970f3f15bc42617cb520e8a64 + version: 6d8c18553ea1ac493d049edd6f102f52e618f085 - name: gopkg.in/yaml.v2 - version: eb3733d160e74a9c7e442f435eb3bea458e1d19f + version: cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b testImports: - name: github.com/davecgh/go-spew - version: 04cdfd42973bb9c8589fd6a731800cf222fde1a9 + version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9 subpackages: - spew - name: github.com/pmezard/go-difflib @@ -204,7 +202,7 @@ testImports: subpackages: - difflib - name: github.com/stretchr/testify - version: 890a5c3458b43e6104ff5da8dfa139d013d77544 + version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0 subpackages: - assert - require diff --git a/glide.yaml b/glide.yaml index 58f25711..27b6cc39 100644 --- a/glide.yaml +++ b/glide.yaml @@ -30,7 +30,7 @@ import: subpackages: - iavl - package: github.com/tendermint/tmlibs - version: ~0.3.1 + version: ~0.3.2 subpackages: - autofile - cli From 8c6bd449296f89853a4e17585f052d1cafe32ac0 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Mon, 2 Oct 2017 15:24:30 -0400 Subject: [PATCH 19/54] log stack trace on consensus failure --- blockchain/reactor.go | 2 +- consensus/state.go | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/blockchain/reactor.go b/blockchain/reactor.go index fb68aadd..7ebf3179 100644 --- a/blockchain/reactor.go +++ b/blockchain/reactor.go @@ -235,7 +235,7 @@ FOR_LOOP: err := bcR.state.Validators.VerifyCommit( bcR.state.ChainID, types.BlockID{first.Hash(), firstPartsHeader}, first.Height, second.LastCommit) if err != nil { - bcR.Logger.Info("error in validation", "err", err) + bcR.Logger.Error("Error in validation", "err", err) bcR.pool.RedoRequest(first.Height) break SYNC_LOOP } else { diff --git a/consensus/state.go b/consensus/state.go index 648fc055..bed40eb1 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -6,6 +6,7 @@ import ( "fmt" "path/filepath" "reflect" + "runtime/debug" "sync" "time" @@ -609,7 +610,7 @@ func (cs *ConsensusState) newStep() { func (cs *ConsensusState) receiveRoutine(maxSteps int) { defer func() { if r := recover(); r != nil { - cs.Logger.Error("CONSENSUS FAILURE!!!", "err", r) + cs.Logger.Error("CONSENSUS FAILURE!!!", "err", r, "stack", string(debug.Stack())) } }() From 97e980225530133c7b7e2b45e5e65c1f78ace89b Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Mon, 2 Oct 2017 20:59:46 -0400 Subject: [PATCH 20/54] fix out of range error in VoteSet.addVote --- types/validator_set.go | 5 +++++ types/vote.go | 6 +++--- types/vote_set.go | 6 ++++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/types/validator_set.go b/types/validator_set.go index 3ec389a4..0e20417a 100644 --- a/types/validator_set.go +++ b/types/validator_set.go @@ -99,7 +99,12 @@ func (valSet *ValidatorSet) GetByAddress(address []byte) (index int, val *Valida } } +// GetByIndex returns the validator by index. +// It returns nil values if index >= len(ValidatorSet.Validators) func (valSet *ValidatorSet) GetByIndex(index int) (address []byte, val *Validator) { + if index >= len(valSet.Validators) { + return nil, nil + } val = valSet.Validators[index] return val.Address, val.Copy() } diff --git a/types/vote.go b/types/vote.go index 164293c5..65841568 100644 --- a/types/vote.go +++ b/types/vote.go @@ -13,9 +13,9 @@ import ( var ( ErrVoteUnexpectedStep = errors.New("Unexpected step") - ErrVoteInvalidValidatorIndex = errors.New("Invalid round vote validator index") - ErrVoteInvalidValidatorAddress = errors.New("Invalid round vote validator address") - ErrVoteInvalidSignature = errors.New("Invalid round vote signature") + ErrVoteInvalidValidatorIndex = errors.New("Invalid validator index") + ErrVoteInvalidValidatorAddress = errors.New("Invalid validator address") + ErrVoteInvalidSignature = errors.New("Invalid signature") ErrVoteInvalidBlockHash = errors.New("Invalid block hash") ) diff --git a/types/vote_set.go b/types/vote_set.go index 938dbcb6..7a5d5267 100644 --- a/types/vote_set.go +++ b/types/vote_set.go @@ -140,8 +140,10 @@ func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) { blockKey := vote.BlockID.Key() // Ensure that validator index was set - if valIndex < 0 || len(valAddr) == 0 { - panic("Validator index or address was not set in vote.") + if valIndex < 0 { + return false, ErrVoteInvalidValidatorIndex + } else if len(valAddr) == 0 { + return false, ErrVoteInvalidValidatorAddress } // Make sure the step matches. From 84e39203bb7f61f528bdd22a1d3f59736b12b82d Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Mon, 2 Oct 2017 23:46:35 -0400 Subject: [PATCH 21/54] readme points to ecosystem doc; add lotion, clean up --- README.md | 2 +- docs/ecosystem.rst | 26 ++++++++++++-------------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index b703fd43..6aa4d878 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ All resources involving the use of, building application on, or developing for, * [Ethermint](http://github.com/tendermint/ethermint); Ethereum on Tendermint * [Cosmos SDK](http://github.com/cosmos/cosmos-sdk); a cryptocurrency application framework -* [Passchain](http://github.com/trusch/passchain); a secret sharing system for teams +* [Many more](https://tendermint.readthedocs.io/en/master/ecosystem.html#abci-applications) ### More diff --git a/docs/ecosystem.rst b/docs/ecosystem.rst index b6df3060..8290a005 100644 --- a/docs/ecosystem.rst +++ b/docs/ecosystem.rst @@ -36,28 +36,31 @@ Ethermint The go-ethereum state machine run as a ABCI app, written in Go, `authored by Tendermint `__. +Lotion +^^^^^^^^^^^^^ -Merkle AVL Tree -^^^^^^^^^^^^^^^ +A Javascript microframework for building blockchain applications with Tendermint, written in Javascript, `authored by Judd Keppel of Tendermint `__. See also `lotion-chat ` and `lotion-coin ` apps written using Lotion. -The following are implementations of the Tendermint IAVL tree as an ABCI application - -Merkleeyes +IAVL ~~~~~~~~~~ -Written in Go, `authored by Tendermint `__. +Immutable AVL+ tree with Merkle proofs, Written in Go, `authored by Tendermint `__. MerkleTree ~~~~~~~~~~ -Written in Java, `authored by jTendermint `__. - +Immutable AVL+ tree with Merkle proofs, Written in Java, `authored by jTendermint `__. TMChat ^^^^^^ -P2P chat using Tendermint, written in Java, `authored by woldposd `__. +P2P chat using Tendermint, written in Java, `authored by wolfposd `__. +Passchain +^^^^^^^^^ + +Passchain is a tool to securely store and share passwords, tokens and other short secrets, `authored by trusch `__. + Passwerk ^^^^^^^^ @@ -68,11 +71,6 @@ Py-Tendermint A Python microframework for building blockchain applications with Tendermint, written in Python, `authored by Dave Bryson `__. -Passchain -^^^^^^^^^ - -Passchain is a tool to securely store and share passwords, tokens and other short secrets, `authored by trusch `__. - ABCI Servers ------------ From 8727bfc265bac9c8490d3efaaeea07a5e182e821 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 3 Oct 2017 16:21:48 +0400 Subject: [PATCH 22/54] remove unnecessary args in abci_query call in getting-started [ci skip] Since 0.10.0, RPC does not require all args (default values will be used). --- docs/getting-started.rst | 82 +++++++++++++++++++++++++++++++++++----- 1 file changed, 73 insertions(+), 9 deletions(-) diff --git a/docs/getting-started.rst b/docs/getting-started.rst index 6745b482..4a7e4b1b 100644 --- a/docs/getting-started.rst +++ b/docs/getting-started.rst @@ -73,7 +73,7 @@ Tendermint before, use: :: - tendermint init + tendermint init tendermint node If you have used Tendermint, you may want to reset the data for a new @@ -107,7 +107,24 @@ like: :: - {"jsonrpc":"2.0","id":"","result":[98,{"check_tx":{},"deliver_tx":{}}],"error":""} + { + "jsonrpc": "2.0", + "id": "", + "result": { + "check_tx": { + "code": 0, + "data": "", + "log": "" + }, + "deliver_tx": { + "code": 0, + "data": "", + "log": "" + }, + "hash": "2B8EC32BA2579B3B8606E42C06DE2F7AFA2556EF", + "height": 154 + } + } The ``98`` is a type-byte, and can be ignored (it's useful for serializing and deserializing arbitrary json). Otherwise, this result is @@ -118,14 +135,27 @@ querying the app: :: - curl -s 'localhost:46657/abci_query?data="abcd"&path=""&prove=false' + curl -s 'localhost:46657/abci_query?data="abcd"' -The ``path`` and ``prove`` arguments can be ignored for now, and in a -future release can be left out. The result should look like: +The result should look like: :: - {"jsonrpc":"2.0","id":"","result":[112,{"response":{"value":"61626364","log":"exists"}}],"error":""} + { + "jsonrpc": "2.0", + "id": "", + "result": { + "response": { + "code": 0, + "index": 0, + "key": "", + "value": "61626364", + "proof": "", + "height": 0, + "log": "exists" + } + } + } Again, the ``112`` is the type-byte. Note the ``value`` in the result (``61626364``); this is the hex-encoding of the ASCII of ``abcd``. You @@ -144,7 +174,7 @@ Now if we query for ``name``, we should get ``satoshi``, or :: - curl -s 'localhost:46657/abci_query?data="name"&path=""&prove=false' + curl -s 'localhost:46657/abci_query?data="name"' Try some other transactions and queries to make sure everything is working! @@ -204,14 +234,48 @@ the number ``1``. If instead, we try to send a ``5``, we get an error: :: > curl localhost:46657/broadcast_tx_commit?tx=0x05 - {"jsonrpc":"2.0","id":"","result":[98,{"check_tx":{},"deliver_tx":{"code":3,"log":"Invalid nonce. Expected 1, got 5"}}],"error":""} + { + "jsonrpc": "2.0", + "id": "", + "result": { + "check_tx": { + "code": 0, + "data": "", + "log": "" + }, + "deliver_tx": { + "code": 3, + "data": "", + "log": "Invalid nonce. Expected 1, got 5" + }, + "hash": "33B93DFF98749B0D6996A70F64071347060DC19C", + "height": 38 + } + } But if we send a ``1``, it works again: :: > curl localhost:46657/broadcast_tx_commit?tx=0x01 - {"jsonrpc":"2.0","id":"","result":[98,{"check_tx":{},"deliver_tx":{}}],"error":""} + { + "jsonrpc": "2.0", + "id": "", + "result": { + "check_tx": { + "code": 0, + "data": "", + "log": "" + }, + "deliver_tx": { + "code": 0, + "data": "", + "log": "" + }, + "hash": "F17854A977F6FA7EEA1BD758E296710B86F72F3D", + "height": 87 + } + } For more details on the ``broadcast_tx`` API, see `the guide on using Tendermint <./using-tendermint.html>`__. From edd718c580c7b7065a29bd0208d1b69a1420614d Mon Sep 17 00:00:00 2001 From: Zach Date: Tue, 3 Oct 2017 11:14:24 -0400 Subject: [PATCH 23/54] Update ecosystem.rst --- docs/ecosystem.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ecosystem.rst b/docs/ecosystem.rst index 8290a005..ce9a0a01 100644 --- a/docs/ecosystem.rst +++ b/docs/ecosystem.rst @@ -39,7 +39,7 @@ The go-ethereum state machine run as a ABCI app, written in Go, `authored by Ten Lotion ^^^^^^^^^^^^^ -A Javascript microframework for building blockchain applications with Tendermint, written in Javascript, `authored by Judd Keppel of Tendermint `__. See also `lotion-chat ` and `lotion-coin ` apps written using Lotion. +A Javascript microframework for building blockchain applications with Tendermint, written in Javascript, `authored by Judd Keppel of Tendermint `__. See also `lotion-chat `__ and `lotion-coin `__ apps written using Lotion. IAVL ~~~~~~~~~~ From 031eb23dc8df7a119ed757db578a7d8ab06eb75a Mon Sep 17 00:00:00 2001 From: Zach Ramsay Date: Tue, 3 Oct 2017 11:23:08 -0400 Subject: [PATCH 24/54] docs: fix build warnings --- docs/ecosystem.rst | 2 +- docs/specification.rst | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/ecosystem.rst b/docs/ecosystem.rst index ce9a0a01..ae723bfd 100644 --- a/docs/ecosystem.rst +++ b/docs/ecosystem.rst @@ -84,7 +84,7 @@ ABCI Servers +-------------------------------------------------------------+--------------------+--------------+ | `cpp-tmsp `__ | Martin Dyring | C++ | +-------------------------------------------------------------+--------------------+--------------+ -| `c-abci `__ | ChainX | C | +| `c-abci `__ | ChainX | C | +-------------------------------------------------------------+--------------------+--------------+ | `jabci `__ | jTendermint | Java | +-------------------------------------------------------------+--------------------+--------------+ diff --git a/docs/specification.rst b/docs/specification.rst index 2e8b3566..3afa9c65 100644 --- a/docs/specification.rst +++ b/docs/specification.rst @@ -2,19 +2,19 @@ Specification ############# -Here you'll find details of the Tendermint specification. See `the spec repo `__ for upcoming material. Tendermint's types are produced by `godoc `__ +Here you'll find details of the Tendermint specification. See `the spec repo `__ for upcoming material. Tendermint's types are produced by `godoc `__. .. toctree:: :maxdepth: 2 - specification/block-structure.rst - specification/byzantine-consensus-algorithm.rst - specification/configuration.rst - specification/fast-sync.rst - specification/genesis.rst - specification/light-client-protocol.rst - specification/merkle.rst - specification/rpc.rst - specification/secure-p2p.rst - specification/validators.rst - specification/wire-protocol.rst + specification/block-structure.rst + specification/byzantine-consensus-algorithm.rst + specification/configuration.rst + specification/fast-sync.rst + specification/genesis.rst + specification/light-client-protocol.rst + specification/merkle.rst + specification/rpc.rst + specification/secure-p2p.rst + specification/validators.rst + specification/wire-protocol.rst From 2e598a7caf5c3877671343ab0693c8c0aa0e2a00 Mon Sep 17 00:00:00 2001 From: Zach Ramsay Date: Tue, 3 Oct 2017 11:24:56 -0400 Subject: [PATCH 25/54] one more fix --- docs/ecosystem.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/ecosystem.rst b/docs/ecosystem.rst index ae723bfd..00210c4a 100644 --- a/docs/ecosystem.rst +++ b/docs/ecosystem.rst @@ -37,17 +37,17 @@ Ethermint The go-ethereum state machine run as a ABCI app, written in Go, `authored by Tendermint `__. Lotion -^^^^^^^^^^^^^ +^^^^^^ A Javascript microframework for building blockchain applications with Tendermint, written in Javascript, `authored by Judd Keppel of Tendermint `__. See also `lotion-chat `__ and `lotion-coin `__ apps written using Lotion. IAVL -~~~~~~~~~~ +^^^^ Immutable AVL+ tree with Merkle proofs, Written in Go, `authored by Tendermint `__. MerkleTree -~~~~~~~~~~ +^^^^^^^^^^ Immutable AVL+ tree with Merkle proofs, Written in Java, `authored by jTendermint `__. From 4fa4e617b779832da692eb039596728e655c6ca7 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 3 Oct 2017 12:47:01 -0400 Subject: [PATCH 26/54] docs/ecosystem: add stratumn --- docs/ecosystem.rst | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/docs/ecosystem.rst b/docs/ecosystem.rst index 00210c4a..c0c38630 100644 --- a/docs/ecosystem.rst +++ b/docs/ecosystem.rst @@ -36,26 +36,21 @@ Ethermint The go-ethereum state machine run as a ABCI app, written in Go, `authored by Tendermint `__. -Lotion -^^^^^^ - -A Javascript microframework for building blockchain applications with Tendermint, written in Javascript, `authored by Judd Keppel of Tendermint `__. See also `lotion-chat `__ and `lotion-coin `__ apps written using Lotion. - IAVL ^^^^ Immutable AVL+ tree with Merkle proofs, Written in Go, `authored by Tendermint `__. +Lotion +^^^^^^ + +A Javascript microframework for building blockchain applications with Tendermint, written in Javascript, `authored by Judd Keppel of Tendermint `__. See also `lotion-chat `__ and `lotion-coin `__ apps written using Lotion. + MerkleTree ^^^^^^^^^^ Immutable AVL+ tree with Merkle proofs, Written in Java, `authored by jTendermint `__. -TMChat -^^^^^^ - -P2P chat using Tendermint, written in Java, `authored by wolfposd `__. - Passchain ^^^^^^^^^ @@ -71,6 +66,17 @@ Py-Tendermint A Python microframework for building blockchain applications with Tendermint, written in Python, `authored by Dave Bryson `__. +Stratumn +^^^^^^^^ + +SDK for "Proof-of-Process" networks, written in Go, `authored by the Stratumn team `__. + +TMChat +^^^^^^ + +P2P chat using Tendermint, written in Java, `authored by wolfposd `__. + + ABCI Servers ------------ From 85370705755705c57fe25f5ea613a7979b519bdb Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 3 Oct 2017 18:07:20 +0400 Subject: [PATCH 27/54] [docs] restructure sentence [ci skip] --- docs/app-development.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/app-development.rst b/docs/app-development.rst index 011fb0f3..8447d2b2 100644 --- a/docs/app-development.rst +++ b/docs/app-development.rst @@ -228,7 +228,7 @@ Commit, or there will be deadlock. Note also that all remaining transactions in the mempool are replayed on the mempool connection (CheckTx) following a commit. -The Commit response includes a byte array, which is the deterministic +The app should respond 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. From 4a0ae17401b4867686341b3aea53a00b18a31ece Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 3 Oct 2017 18:07:59 +0400 Subject: [PATCH 28/54] [docs] include examples from the persistent_dummy app [ci skip] --- docs/app-development.rst | 97 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/docs/app-development.rst b/docs/app-development.rst index 8447d2b2..5a5b1b82 100644 --- a/docs/app-development.rst +++ b/docs/app-development.rst @@ -142,6 +142,10 @@ It is unlikely that you will need to implement a client. For details of our client, see `here `__. +All examples below are from `persistent-dummy application +`__, +which is a part of the abci repo. + Blockchain Protocol ------------------- @@ -187,6 +191,12 @@ 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. +:: + + func (app *PersistentDummyApplication) CheckTx(tx []byte) types.Result { + return app.app.CheckTx(tx) + } + Consensus Connection ~~~~~~~~~~~~~~~~~~~~ @@ -215,6 +225,22 @@ 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. +:: + + // tx is either "key=value" or just arbitrary bytes + func (app *PersistentDummyApplication) DeliverTx(tx []byte) types.Result { + // if it starts with "val:", update the validator set + // format is "val:pubkey/power" + if isValidatorTx(tx) { + // update validators in the merkle tree + // and in app.changes + return app.execValidatorTx(tx) + } + + // otherwise, update the key-value store + return app.app.DeliverTx(tx) + } + Commit ^^^^^^ @@ -237,6 +263,24 @@ 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>`__. +:: + + func (app *PersistentDummyApplication) Commit() types.Result { + // Save + appHash := app.app.state.Save() + app.logger.Info("Saved state", "root", appHash) + + lastBlock := LastBlockInfo{ + Height: app.blockHeader.Height, + AppHash: appHash, // this hash will be in the next block header + } + + app.logger.Info("Saving block", "height", lastBlock.Height, "root", lastBlock.AppHash) + SaveLastBlock(app.db, lastBlock) + + return types.NewResultOK(appHash, "") + } + BeginBlock ^^^^^^^^^^ @@ -248,6 +292,17 @@ 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. +:: + + // Track the block hash and header information + func (app *PersistentDummyApplication) BeginBlock(params types.RequestBeginBlock) { + // update latest block info + app.blockHeader = params.Header + + // reset valset changes + app.changes = make([]*types.Validator, 0) + } + EndBlock ^^^^^^^^ @@ -260,6 +315,13 @@ EndBlock response. To remove one, include it in the list with a validator set. Note validator set changes are only available in v0.8.0 and up. +:: + + // Update the validator set + func (app *PersistentDummyApplication) EndBlock(height uint64) (resEndBlock types.ResponseEndBlock) { + return types.ResponseEndBlock{Diffs: app.changes} + } + Query Connection ~~~~~~~~~~~~~~~~ @@ -281,6 +343,12 @@ cause Tendermint to not connect to the corresponding peer: Note: these query formats are subject to change! +:: + + func (app *PersistentDummyApplication) Query(reqQuery types.RequestQuery) types.ResponseQuery { + return app.app.Query(reqQuery) + } + Handshake ~~~~~~~~~ @@ -297,3 +365,32 @@ the app are synced to the latest block height. If the app returns a LastBlockHeight of 0, Tendermint will just replay all blocks. + +:: + + func (app *PersistentDummyApplication) Info(req types.RequestInfo) (resInfo types.ResponseInfo) { + resInfo = app.app.Info(req) + lastBlock := LoadLastBlock(app.db) + resInfo.LastBlockHeight = lastBlock.Height + resInfo.LastBlockAppHash = lastBlock.AppHash + return resInfo + } + +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. + +:: + + // Save the validators in the merkle tree + func (app *PersistentDummyApplication) InitChain(params types.RequestInitChain) { + for _, v := range params.Validators { + r := app.updateValidator(v) + if r.IsErr() { + app.logger.Error("Error updating validators", "r", r) + } + } + } From 10f361fcd08001656fed62fbf6863fd12481470b Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 4 Oct 2017 00:03:42 +0400 Subject: [PATCH 29/54] [docs] use persistent_dummy only when needed [ci skip] --- docs/app-development.rst | 81 ++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/docs/app-development.rst b/docs/app-development.rst index 5a5b1b82..84654391 100644 --- a/docs/app-development.rst +++ b/docs/app-development.rst @@ -142,9 +142,12 @@ It is unlikely that you will need to implement a client. For details of our client, see `here `__. -All examples below are from `persistent-dummy application -`__, -which is a part of the abci repo. +Most of the examples below are from `dummy application +`__, +which is a part of the abci repo. `persistent_dummy application +`__ +application is used to show ``BeginBlock``, ``EndBlock`` and ``InitChain`` +example implementations. Blockchain Protocol ------------------- @@ -193,8 +196,8 @@ mempool state. :: - func (app *PersistentDummyApplication) CheckTx(tx []byte) types.Result { - return app.app.CheckTx(tx) + func (app *DummyApplication) CheckTx(tx []byte) types.Result { + return types.OK } Consensus Connection @@ -228,17 +231,14 @@ merkle root of the data returned by the DeliverTx requests, or both. :: // tx is either "key=value" or just arbitrary bytes - func (app *PersistentDummyApplication) DeliverTx(tx []byte) types.Result { - // if it starts with "val:", update the validator set - // format is "val:pubkey/power" - if isValidatorTx(tx) { - // update validators in the merkle tree - // and in app.changes - return app.execValidatorTx(tx) + func (app *DummyApplication) 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) } - - // otherwise, update the key-value store - return app.app.DeliverTx(tx) + return types.OK } Commit @@ -265,20 +265,9 @@ job of the `Handshake <#handshake>`__. :: - func (app *PersistentDummyApplication) Commit() types.Result { - // Save - appHash := app.app.state.Save() - app.logger.Info("Saved state", "root", appHash) - - lastBlock := LastBlockInfo{ - Height: app.blockHeader.Height, - AppHash: appHash, // this hash will be in the next block header - } - - app.logger.Info("Saving block", "height", lastBlock.Height, "root", lastBlock.AppHash) - SaveLastBlock(app.db, lastBlock) - - return types.NewResultOK(appHash, "") + func (app *DummyApplication) Commit() types.Result { + hash := app.state.Hash() + return types.NewResultOK(hash, "") } BeginBlock @@ -345,8 +334,30 @@ Note: these query formats are subject to change! :: - func (app *PersistentDummyApplication) Query(reqQuery types.RequestQuery) types.ResponseQuery { - return app.app.Query(reqQuery) + func (app *DummyApplication) 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 + } } Handshake @@ -368,12 +379,8 @@ all blocks. :: - func (app *PersistentDummyApplication) Info(req types.RequestInfo) (resInfo types.ResponseInfo) { - resInfo = app.app.Info(req) - lastBlock := LoadLastBlock(app.db) - resInfo.LastBlockHeight = lastBlock.Height - resInfo.LastBlockAppHash = lastBlock.AppHash - return resInfo + func (app *DummyApplication) Info(req types.RequestInfo) (resInfo types.ResponseInfo) { + return types.ResponseInfo{Data: cmn.Fmt("{\"size\":%v}", app.state.Size())} } Genesis From 65501997518b59de12e35cbc11017aa54d162164 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 3 Oct 2017 23:39:28 -0400 Subject: [PATCH 30/54] [docs] minor fixes from review [ci skip] --- docs/app-development.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/app-development.rst b/docs/app-development.rst index 84654391..770572e1 100644 --- a/docs/app-development.rst +++ b/docs/app-development.rst @@ -146,7 +146,7 @@ Most of the examples below are from `dummy application `__, which is a part of the abci repo. `persistent_dummy application `__ -application is used to show ``BeginBlock``, ``EndBlock`` and ``InitChain`` +is used to show ``BeginBlock``, ``EndBlock`` and ``InitChain`` example implementations. Blockchain Protocol @@ -254,7 +254,7 @@ 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 with a byte array, which is the deterministic +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. From 00ab3daa0cf23c0ffca265a9b2dfd958458b4c3e Mon Sep 17 00:00:00 2001 From: Zach Ramsay Date: Tue, 3 Oct 2017 22:00:48 -0400 Subject: [PATCH 31/54] document no empty blocks, closes #605 [ci skip] --- docs/index.rst | 21 +++++++++--------- docs/specification/configuration.rst | 8 ++++--- docs/using-tendermint.rst | 32 ++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 14 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index ee1307bf..0ede0e4e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -15,8 +15,6 @@ Welcome to Tendermint! Tendermint 101 -------------- -.. maxdepth set to 2 for sexinesss -.. but use 4 to upgrade overall documentation .. toctree:: :maxdepth: 2 @@ -25,9 +23,19 @@ Tendermint 101 getting-started.rst using-tendermint.rst +Tendermint Ecosystem +-------------------- + +.. toctree:: + :maxdepth: 2 + + ecosystem.rst + Tendermint Tools ---------------- +.. the tools/ files are pulled in from the tools repo +.. see the bottom of conf.py .. toctree:: :maxdepth: 2 @@ -38,15 +46,6 @@ Tendermint Tools tools/terraform-digitalocean.rst tools/benchmarking-and-monitoring.rst - -Tendermint Ecosystem --------------------- - -.. toctree:: - :maxdepth: 2 - - ecosystem.rst - Tendermint 102 -------------- diff --git a/docs/specification/configuration.rst b/docs/specification/configuration.rst index 81d1c41d..94801136 100644 --- a/docs/specification/configuration.rst +++ b/docs/specification/configuration.rst @@ -30,14 +30,16 @@ The main config parameters are defined - ``consensus.max_block_size_txs``: Maximum number of block txs. *Default*: ``10000`` +- ``consensus.create_empty_blocks``: Create empty blocks w/o txs. + *Default*: ``true`` +- ``consensus.create_empty_blocks_interval``: Block creation interval, even if empty. - ``consensus.timeout_*``: Various consensus timeout parameters - **TODO** - ``consensus.wal_file``: Consensus state WAL. *Default*: - ``"$TMHOME/data/cswal"`` + ``"$TMHOME/data/cs.wal/wal"`` - ``consensus.wal_light``: Whether to use light-mode for Consensus state WAL. *Default*: ``false`` -- ``mempool.*``: Various mempool parameters **TODO** +- ``mempool.*``: Various mempool parameters - ``p2p.addr_book_file``: Peer address book. *Default*: ``"$TMHOME/addrbook.json"``. **NOT USED** diff --git a/docs/using-tendermint.rst b/docs/using-tendermint.rst index 6e870851..cc94245c 100644 --- a/docs/using-tendermint.rst +++ b/docs/using-tendermint.rst @@ -126,6 +126,38 @@ Notable options include the socket address of the application Some fields from the config file can be overwritten with flags. +No Empty Blocks +--------------- + +This much requested feature was implemented in version 0.10.3. While the default behaviour of ``tendermint`` is still to create blocks approximately once per second, it is possible to disable empty blocks or set a block creation interval. In the former case, blocks will be created when there are new transactions or when the AppHash changes. + +To configure tendermint to not produce empty blocks unless there are txs or the app hash changes, +run tendermint with this additional flag: + +:: + + tendermint node --consensus.create_empty_blocks=false + +or set the configuration via the ``config.toml`` file: + +:: + + [consensus] + create_empty_blocks = false + +Remember: because the default is to *create empty blocks*, avoiding empty blocks requires the config option to be set to ``false``. + +The block interval setting allows for a delay (in seconds) between the creation of each new empty block. It is set via the ``config.toml``: + +:: + + [consensus] + create_empty_blocks_interval = 5 + +With this setting, empty blocks will be produced every 5s if no block has been produced otherwise, +regardless of the value of `create_empty_blocks`. + + Broadcast API ------------- From 54f2cc9709e764310365ab1619660c116202193c Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 4 Oct 2017 13:14:00 +0400 Subject: [PATCH 32/54] [docs] add how to read logs guide [ci skip] --- docs/how-to-read-logs.rst | 165 ++++++++++++++++++++++++++++++++++++++ docs/index.rst | 1 + mempool/mempool.go | 2 +- 3 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 docs/how-to-read-logs.rst diff --git a/docs/how-to-read-logs.rst b/docs/how-to-read-logs.rst new file mode 100644 index 00000000..275b2c0b --- /dev/null +++ b/docs/how-to-read-logs.rst @@ -0,0 +1,165 @@ +How to read logs +================ + +Walk through example +-------------------- + +We first create three connections (mempool, consensus and query) to the +application (locally running dummy in this case). + +:: + + I[10-04|13:54:27.364] Starting multiAppConn module=proxy impl=multiAppConn + I[10-04|13:54:27.366] Starting localClient module=abci-client connection=query impl=localClient + I[10-04|13:54:27.366] Starting localClient module=abci-client connection=mempool impl=localClient + I[10-04|13:54:27.367] Starting localClient module=abci-client connection=consensus impl=localClient + +Then Tendermint Core and the application perform a handshake. + +:: + + I[10-04|13:54:27.367] ABCI Handshake module=consensus appHeight=90 appHash=E0FBAFBF6FCED8B9786DDFEB1A0D4FA2501BADAD + I[10-04|13:54:27.368] ABCI Replay Blocks module=consensus appHeight=90 storeHeight=90 stateHeight=90 + I[10-04|13:54:27.368] Completed ABCI Handshake - Tendermint and App are synced module=consensus appHeight=90 appHash=E0FBAFBF6FCED8B9786DDFEB1A0D4FA2501BADAD + +After that, we start a few more things like the event switch, reactors, and +perform UPNP discover in order to detect the IP address. + +:: + + I[10-04|13:54:27.374] Starting EventSwitch module=types impl=EventSwitch + I[10-04|13:54:27.375] This node is a validator module=consensus + I[10-04|13:54:27.379] Starting Node module=main impl=Node + I[10-04|13:54:27.381] Local listener module=p2p ip=:: port=46656 + I[10-04|13:54:27.382] Getting UPNP external address module=p2p + I[10-04|13:54:30.386] Could not perform UPNP discover module=p2p err="write udp4 0.0.0.0:38238->239.255.255.250:1900: i/o timeout" + I[10-04|13:54:30.386] Starting DefaultListener module=p2p impl=Listener(@10.0.2.15:46656) + I[10-04|13:54:30.387] Starting P2P Switch module=p2p impl="P2P Switch" + I[10-04|13:54:30.387] Starting MempoolReactor module=mempool impl=MempoolReactor + I[10-04|13:54:30.387] Starting BlockchainReactor module=blockchain impl=BlockchainReactor + I[10-04|13:54:30.387] Starting ConsensusReactor module=consensus impl=ConsensusReactor + I[10-04|13:54:30.387] ConsensusReactor module=consensus fastSync=false + I[10-04|13:54:30.387] Starting ConsensusState module=consensus impl=ConsensusState + I[10-04|13:54:30.387] Starting WAL module=consensus wal=/home/vagrant/.tendermint/data/cs.wal/wal impl=WAL + I[10-04|13:54:30.388] Starting TimeoutTicker module=consensus impl=TimeoutTicker + +Notice the second row where Tendermint Core reports that "This node is a +validator". It also could be just an observer (regular node). + +Next we replay all the messages from the WAL. + +:: + + I[10-04|13:54:30.390] Catchup by replaying consensus messages module=consensus height=91 + I[10-04|13:54:30.390] Replay: New Step module=consensus height=91 round=0 step=RoundStepNewHeight + I[10-04|13:54:30.390] Replay: Done module=consensus + +"Started node" message signals that everything is ready for work. + +:: + + I[10-04|13:54:30.391] Starting RPC HTTP server on tcp socket 0.0.0.0:46657 module=rpc-server + I[10-04|13:54:30.392] Started node module=main nodeInfo="NodeInfo{pk: PubKeyEd25519{DF22D7C92C91082324A1312F092AA1DA197FA598DBBFB6526E177003C4D6FD66}, moniker: anonymous, network: test-chain-3MNw2N [remote , listen 10.0.2.15:46656], version: 0.11.0-10f361fc ([wire_version=0.6.2 p2p_version=0.5.0 consensus_version=v1/0.2.2 rpc_version=0.7.0/3 tx_index=on rpc_addr=tcp://0.0.0.0:46657])}" + +Next follows a standard block creation cycle, where we enter a new round, +propose a block, receive more than 2/3 of prevotes, than precommits and finally +have a chance to commit a block. For details, please refer to `Consensus +Overview +__` +or `Byzantine Consensus Algorithm +__`. + +:: + + I[10-04|13:54:30.393] enterNewRound(91/0). Current: 91/0/RoundStepNewHeight module=consensus + I[10-04|13:54:30.393] enterPropose(91/0). Current: 91/0/RoundStepNewRound module=consensus + I[10-04|13:54:30.393] enterPropose: Our turn to propose module=consensus proposer=125B0E3C5512F5C2B0E1109E31885C4511570C42 privValidator="PrivValidator{125B0E3C5512F5C2B0E1109E31885C4511570C42 LH:90, LR:0, LS:3}" + I[10-04|13:54:30.394] Signed proposal module=consensus height=91 round=0 proposal="Proposal{91/0 1:21B79872514F (-1,:0:000000000000) {/10EDEDD7C84E.../}}" + I[10-04|13:54:30.397] Received complete proposal block module=consensus height=91 hash=F671D562C7B9242900A286E1882EE64E5556FE9E + I[10-04|13:54:30.397] enterPrevote(91/0). Current: 91/0/RoundStepPropose module=consensus + I[10-04|13:54:30.397] enterPrevote: ProposalBlock is valid module=consensus height=91 round=0 + I[10-04|13:54:30.398] Signed and pushed vote module=consensus height=91 round=0 vote="Vote{0:125B0E3C5512 91/00/1(Prevote) F671D562C7B9 {/89047FFC21D8.../}}" err=null + I[10-04|13:54:30.401] Added to prevote module=consensus vote="Vote{0:125B0E3C5512 91/00/1(Prevote) F671D562C7B9 {/89047FFC21D8.../}}" prevotes="VoteSet{H:91 R:0 T:1 +2/3:F671D562C7B9242900A286E1882EE64E5556FE9E:1:21B79872514F BA{1:X} map[]}" + I[10-04|13:54:30.401] enterPrecommit(91/0). Current: 91/0/RoundStepPrevote module=consensus + I[10-04|13:54:30.401] enterPrecommit: +2/3 prevoted proposal block. Locking module=consensus hash=F671D562C7B9242900A286E1882EE64E5556FE9E + I[10-04|13:54:30.402] Signed and pushed vote module=consensus height=91 round=0 vote="Vote{0:125B0E3C5512 91/00/2(Precommit) F671D562C7B9 {/80533478E41A.../}}" err=null + I[10-04|13:54:30.404] Added to precommit module=consensus vote="Vote{0:125B0E3C5512 91/00/2(Precommit) F671D562C7B9 {/80533478E41A.../}}" precommits="VoteSet{H:91 R:0 T:2 +2/3:F671D562C7B9242900A286E1882EE64E5556FE9E:1:21B79872514F BA{1:X} map[]}" + I[10-04|13:54:30.404] enterCommit(91/0). Current: 91/0/RoundStepPrecommit module=consensus + I[10-04|13:54:30.405] Finalizing commit of block with 0 txs module=consensus height=91 hash=F671D562C7B9242900A286E1882EE64E5556FE9E root=E0FBAFBF6FCED8B9786DDFEB1A0D4FA2501BADAD + I[10-04|13:54:30.405] Block{ + Header{ + ChainID: test-chain-3MNw2N + Height: 91 + Time: 2017-10-04 13:54:30.393 +0000 UTC + NumTxs: 0 + LastBlockID: F15AB8BEF9A6AAB07E457A6E16BC410546AA4DC6:1:D505DA273544 + LastCommit: 56FEF2EFDB8B37E9C6E6D635749DF3169D5F005D + Data: + Validators: CE25FBFF2E10C0D51AA1A07C064A96931BC8B297 + App: E0FBAFBF6FCED8B9786DDFEB1A0D4FA2501BADAD + }#F671D562C7B9242900A286E1882EE64E5556FE9E + Data{ + + }# + Commit{ + BlockID: F15AB8BEF9A6AAB07E457A6E16BC410546AA4DC6:1:D505DA273544 + Precommits: Vote{0:125B0E3C5512 90/00/2(Precommit) F15AB8BEF9A6 {/FE98E2B956F0.../}} + }#56FEF2EFDB8B37E9C6E6D635749DF3169D5F005D + }#F671D562C7B9242900A286E1882EE64E5556FE9E module=consensus + I[10-04|13:54:30.408] Executed block module=state height=91 validTxs=0 invalidTxs=0 + I[10-04|13:54:30.410] Committed state module=state height=91 txs=0 hash=E0FBAFBF6FCED8B9786DDFEB1A0D4FA2501BADAD + I[10-04|13:54:30.410] Recheck txs module=mempool numtxs=0 height=91 + +List of modules +--------------- + +Here is the list of modules you may encounter in Tendermint's log and a little +overview what they do. + +- ``abci-client`` As mentioned in `Application Development Guide + __`, + Tendermint acts as an ABCI client with respect to the application and + maintains 3 connections: mempool, consensus and query. The code used by + Tendermint Core can be found `here + __`. + +- ``blockchain`` + Provides storage, pool (a group of peers), and reactor for both storing and + exchanging blocks between peers. + +- ``consensus`` + The heart of Tendermint core, which is the implementation of the consensus + algorithm. Includes two "submodules": ``wal`` (write-ahead logging) for + ensuring data integrity and ``replay`` to replay blocks and messages on + recovery from a crash. + +- ``events`` + Simple event notification system. The list of events can be found + `here + __`. + You can subscribe to them by calling ``subscribe`` RPC method. + Refer to `RPC docs + __` + for additional information. + +- ``mempool`` + Mempool module handles all incoming transactions, whenever they are + coming from peers or the application. + +- ``p2p`` + Provides an abstraction around peer-to-peer communication. For more details, + please check out the `README + __`. + +- ``rpc`` + `Tendermint's RPC __`. + +- ``rpc-server`` + RPC server. For implementation details, please read the `README __` . + +- ``state`` + Represents the latest state and execution submodule, which executes + blocks against the application. + +- ``types`` + A collection of the publicly exposed types and methods to work with them. diff --git a/docs/index.rst b/docs/index.rst index 0ede0e4e..4c226516 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -55,6 +55,7 @@ Tendermint 102 abci-cli.rst app-architecture.rst app-development.rst + how-to-read-logs.rst Tendermint 201 -------------- diff --git a/mempool/mempool.go b/mempool/mempool.go index fc6e40c1..07b267c4 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -24,7 +24,7 @@ import ( The mempool pushes new txs onto the proxyAppConn. It gets a stream of (req, res) tuples from the proxy. -The memool stores good txs in a concurrent linked-list. +The mempool stores good txs in a concurrent linked-list. Multiple concurrent go-routines can traverse this linked-list safely by calling .NextWait() on each element. From 9e4c25761c549d92ce7fb4a4602fbc2d4edfdeb1 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 4 Oct 2017 22:59:16 +0400 Subject: [PATCH 33/54] relative links [ci skip] --- .gitignore | 2 ++ docs/how-to-read-logs.rst | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index cc6e1d67..23ae616e 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ test/p2p/data/ test/logs .glide coverage.txt +docs/_build +docs/tools diff --git a/docs/how-to-read-logs.rst b/docs/how-to-read-logs.rst index 275b2c0b..890057a2 100644 --- a/docs/how-to-read-logs.rst +++ b/docs/how-to-read-logs.rst @@ -62,12 +62,12 @@ Next we replay all the messages from the WAL. I[10-04|13:54:30.392] Started node module=main nodeInfo="NodeInfo{pk: PubKeyEd25519{DF22D7C92C91082324A1312F092AA1DA197FA598DBBFB6526E177003C4D6FD66}, moniker: anonymous, network: test-chain-3MNw2N [remote , listen 10.0.2.15:46656], version: 0.11.0-10f361fc ([wire_version=0.6.2 p2p_version=0.5.0 consensus_version=v1/0.2.2 rpc_version=0.7.0/3 tx_index=on rpc_addr=tcp://0.0.0.0:46657])}" Next follows a standard block creation cycle, where we enter a new round, -propose a block, receive more than 2/3 of prevotes, than precommits and finally +propose a block, receive more than 2/3 of prevotes, then precommits and finally have a chance to commit a block. For details, please refer to `Consensus Overview -__` +`__ or `Byzantine Consensus Algorithm -__`. +`__. :: @@ -117,11 +117,11 @@ Here is the list of modules you may encounter in Tendermint's log and a little overview what they do. - ``abci-client`` As mentioned in `Application Development Guide - __`, + `__, Tendermint acts as an ABCI client with respect to the application and maintains 3 connections: mempool, consensus and query. The code used by Tendermint Core can be found `here - __`. + `__. - ``blockchain`` Provides storage, pool (a group of peers), and reactor for both storing and @@ -136,10 +136,10 @@ overview what they do. - ``events`` Simple event notification system. The list of events can be found `here - __`. + `__. You can subscribe to them by calling ``subscribe`` RPC method. Refer to `RPC docs - __` + `__ for additional information. - ``mempool`` @@ -149,13 +149,13 @@ overview what they do. - ``p2p`` Provides an abstraction around peer-to-peer communication. For more details, please check out the `README - __`. + `__. - ``rpc`` - `Tendermint's RPC __`. + `Tendermint's RPC `__. - ``rpc-server`` - RPC server. For implementation details, please read the `README __` . + RPC server. For implementation details, please read the `README `__. - ``state`` Represents the latest state and execution submodule, which executes From d56b44f3a55ba9c59f41633061bf36aacfa6be43 Mon Sep 17 00:00:00 2001 From: Zach Ramsay Date: Wed, 4 Oct 2017 16:40:45 -0400 Subject: [PATCH 34/54] all: no more anonymous imports --- benchmarks/map_test.go | 7 ++--- benchmarks/os_test.go | 4 +-- benchmarks/simu/counter.go | 3 +-- blockchain/pool.go | 24 +++++++++--------- blockchain/pool_test.go | 4 +-- cmd/tendermint/commands/root_test.go | 4 +-- consensus/byzantine_test.go | 10 ++++---- consensus/common_test.go | 10 ++++---- consensus/height_vote_set.go | 10 ++++---- consensus/height_vote_set_test.go | 4 +-- consensus/mempool_test.go | 8 +++--- consensus/state_test.go | 8 +++--- consensus/version.go | 4 +-- consensus/wal.go | 14 +++++----- node/id.go | 3 ++- p2p/peer.go | 1 + p2p/upnp/README.md | 5 ---- rpc/client/httpclient.go | 1 + rpc/client/mock/abci_test.go | 4 +-- rpc/client/mock/status_test.go | 4 +-- rpc/client/rpc_test.go | 3 ++- rpc/core/blocks.go | 8 +++--- rpc/core/pipe.go | 3 +-- rpc/core/types/responses.go | 1 - rpc/core/types/responses_test.go | 1 + rpc/grpc/api.go | 5 ++-- rpc/grpc/client_server.go | 4 +-- rpc/lib/rpc_test.go | 1 + state/execution_test.go | 1 + state/state_test.go | 8 +++--- types/genesis_test.go | 1 + types/part_set_test.go | 6 ++--- types/proposal.go | 1 - types/signable.go | 4 +-- types/tx_test.go | 1 + types/vote_set.go | 38 ++++++++++++++-------------- types/vote_set_test.go | 23 ++++++++--------- 37 files changed, 119 insertions(+), 122 deletions(-) delete mode 100644 p2p/upnp/README.md diff --git a/benchmarks/map_test.go b/benchmarks/map_test.go index 80edaff7..2d978902 100644 --- a/benchmarks/map_test.go +++ b/benchmarks/map_test.go @@ -1,8 +1,9 @@ package benchmarks import ( - . "github.com/tendermint/tmlibs/common" "testing" + + cmn "github.com/tendermint/tmlibs/common" ) func BenchmarkSomething(b *testing.B) { @@ -11,11 +12,11 @@ func BenchmarkSomething(b *testing.B) { numChecks := 100000 keys := make([]string, numItems) for i := 0; i < numItems; i++ { - keys[i] = RandStr(100) + keys[i] = cmn.RandStr(100) } txs := make([]string, numChecks) for i := 0; i < numChecks; i++ { - txs[i] = RandStr(100) + txs[i] = cmn.RandStr(100) } b.StartTimer() diff --git a/benchmarks/os_test.go b/benchmarks/os_test.go index 2c4611c8..9c8fae65 100644 --- a/benchmarks/os_test.go +++ b/benchmarks/os_test.go @@ -4,7 +4,7 @@ import ( "os" "testing" - . "github.com/tendermint/tmlibs/common" + cmn "github.com/tendermint/tmlibs/common" ) func BenchmarkFileWrite(b *testing.B) { @@ -14,7 +14,7 @@ func BenchmarkFileWrite(b *testing.B) { if err != nil { b.Error(err) } - testString := RandStr(200) + "\n" + testString := cmn.RandStr(200) + "\n" b.StartTimer() for i := 0; i < b.N; i++ { diff --git a/benchmarks/simu/counter.go b/benchmarks/simu/counter.go index d26f5e6f..ff5b14c0 100644 --- a/benchmarks/simu/counter.go +++ b/benchmarks/simu/counter.go @@ -3,9 +3,8 @@ package main import ( "context" "encoding/binary" - "time" - //"encoding/hex" "fmt" + "time" rpcclient "github.com/tendermint/tendermint/rpc/lib/client" cmn "github.com/tendermint/tmlibs/common" diff --git a/blockchain/pool.go b/blockchain/pool.go index 924880c0..bd52e280 100644 --- a/blockchain/pool.go +++ b/blockchain/pool.go @@ -6,7 +6,7 @@ import ( "time" "github.com/tendermint/tendermint/types" - . "github.com/tendermint/tmlibs/common" + cmn "github.com/tendermint/tmlibs/common" flow "github.com/tendermint/tmlibs/flowrate" "github.com/tendermint/tmlibs/log" ) @@ -33,7 +33,7 @@ var peerTimeoutSeconds = time.Duration(15) // not const so we can override with */ type BlockPool struct { - BaseService + cmn.BaseService startTime time.Time mtx sync.Mutex @@ -59,7 +59,7 @@ func NewBlockPool(start int, requestsCh chan<- BlockRequest, timeoutsCh chan<- s requestsCh: requestsCh, timeoutsCh: timeoutsCh, } - bp.BaseService = *NewBaseService(nil, "BlockPool", bp) + bp.BaseService = *cmn.NewBaseService(nil, "BlockPool", bp) return bp } @@ -137,14 +137,14 @@ func (pool *BlockPool) IsCaughtUp() bool { maxPeerHeight := 0 for _, peer := range pool.peers { - maxPeerHeight = MaxInt(maxPeerHeight, peer.height) + maxPeerHeight = cmn.MaxInt(maxPeerHeight, peer.height) } // some conditions to determine if we're caught up receivedBlockOrTimedOut := (pool.height > 0 || time.Since(pool.startTime) > 5*time.Second) ourChainIsLongestAmongPeers := maxPeerHeight == 0 || pool.height >= maxPeerHeight isCaughtUp := receivedBlockOrTimedOut && ourChainIsLongestAmongPeers - pool.Logger.Info(Fmt("IsCaughtUp: %v", isCaughtUp), "height", pool.height, "maxPeerHeight", maxPeerHeight) + pool.Logger.Info(cmn.Fmt("IsCaughtUp: %v", isCaughtUp), "height", pool.height, "maxPeerHeight", maxPeerHeight) return isCaughtUp } @@ -180,7 +180,7 @@ func (pool *BlockPool) PopRequest() { delete(pool.requesters, pool.height) pool.height++ } else { - PanicSanity(Fmt("Expected requester to pop, got nothing at height %v", pool.height)) + cmn.PanicSanity(cmn.Fmt("Expected requester to pop, got nothing at height %v", pool.height)) } } @@ -192,7 +192,7 @@ func (pool *BlockPool) RedoRequest(height int) { pool.mtx.Unlock() if request.block == nil { - PanicSanity("Expected block to be non-nil") + cmn.PanicSanity("Expected block to be non-nil") } // RemovePeer will redo all requesters associated with this peer. // TODO: record this malfeasance @@ -311,10 +311,10 @@ func (pool *BlockPool) debug() string { str := "" for h := pool.height; h < pool.height+len(pool.requesters); h++ { if pool.requesters[h] == nil { - str += Fmt("H(%v):X ", h) + str += cmn.Fmt("H(%v):X ", h) } else { - str += Fmt("H(%v):", h) - str += Fmt("B?(%v) ", pool.requesters[h].block != nil) + str += cmn.Fmt("H(%v):", h) + str += cmn.Fmt("B?(%v) ", pool.requesters[h].block != nil) } } return str @@ -394,7 +394,7 @@ func (peer *bpPeer) onTimeout() { //------------------------------------- type bpRequester struct { - BaseService + cmn.BaseService pool *BlockPool height int gotBlockCh chan struct{} @@ -415,7 +415,7 @@ func newBPRequester(pool *BlockPool, height int) *bpRequester { peerID: "", block: nil, } - bpr.BaseService = *NewBaseService(nil, "bpRequester", bpr) + bpr.BaseService = *cmn.NewBaseService(nil, "bpRequester", bpr) return bpr } diff --git a/blockchain/pool_test.go b/blockchain/pool_test.go index 43ddbadd..a1fce2da 100644 --- a/blockchain/pool_test.go +++ b/blockchain/pool_test.go @@ -6,7 +6,7 @@ import ( "time" "github.com/tendermint/tendermint/types" - . "github.com/tendermint/tmlibs/common" + cmn "github.com/tendermint/tmlibs/common" "github.com/tendermint/tmlibs/log" ) @@ -22,7 +22,7 @@ type testPeer struct { func makePeers(numPeers int, minHeight, maxHeight int) map[string]testPeer { peers := make(map[string]testPeer, numPeers) for i := 0; i < numPeers; i++ { - peerID := RandStr(12) + peerID := cmn.RandStr(12) height := minHeight + rand.Intn(maxHeight-minHeight) peers[peerID] = testPeer{peerID, height} } diff --git a/cmd/tendermint/commands/root_test.go b/cmd/tendermint/commands/root_test.go index ae0f3827..b89e7a19 100644 --- a/cmd/tendermint/commands/root_test.go +++ b/cmd/tendermint/commands/root_test.go @@ -3,15 +3,15 @@ package commands import ( "os" "strconv" + "testing" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tmlibs/cli" - - "testing" ) var ( diff --git a/consensus/byzantine_test.go b/consensus/byzantine_test.go index 0f2d7b04..38dff406 100644 --- a/consensus/byzantine_test.go +++ b/consensus/byzantine_test.go @@ -9,7 +9,7 @@ import ( data "github.com/tendermint/go-wire/data" "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/types" - . "github.com/tendermint/tmlibs/common" + cmn "github.com/tendermint/tmlibs/common" "github.com/tendermint/tmlibs/events" ) @@ -149,8 +149,8 @@ func TestByzantine(t *testing.T) { case <-done: case <-tick.C: for i, reactor := range reactors { - t.Log(Fmt("Consensus Reactor %v", i)) - t.Log(Fmt("%v", reactor)) + t.Log(cmn.Fmt("Consensus Reactor %v", i)) + t.Log(cmn.Fmt("%v", reactor)) } t.Fatalf("Timed out waiting for all validators to commit first block") } @@ -220,7 +220,7 @@ func sendProposalAndParts(height, round int, cs *ConsensusState, peer p2p.Peer, // byzantine consensus reactor type ByzantineReactor struct { - Service + cmn.Service reactor *ConsensusReactor } @@ -296,5 +296,5 @@ func (privVal *ByzantinePrivValidator) SignHeartbeat(chainID string, heartbeat * } func (privVal *ByzantinePrivValidator) String() string { - return Fmt("PrivValidator{%X}", privVal.GetAddress()) + return cmn.Fmt("PrivValidator{%X}", privVal.GetAddress()) } diff --git a/consensus/common_test.go b/consensus/common_test.go index b16afc3d..e614bf37 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -19,7 +19,7 @@ import ( "github.com/tendermint/tendermint/p2p" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" - . "github.com/tendermint/tmlibs/common" + cmn "github.com/tendermint/tmlibs/common" dbm "github.com/tendermint/tmlibs/db" "github.com/tendermint/tmlibs/log" @@ -34,7 +34,7 @@ var config *cfg.Config // NOTE: must be reset for each _test.go file var ensureTimeout = time.Second * 2 func ensureDir(dir string, mode os.FileMode) { - if err := EnsureDir(dir, mode); err != nil { + if err := cmn.EnsureDir(dir, mode); err != nil { panic(err) } } @@ -341,7 +341,7 @@ func randConsensusNet(nValidators int, testName string, tickerFunc func() Timeou state, _ := sm.MakeGenesisState(db, genDoc) state.SetLogger(logger.With("module", "state", "validator", i)) state.Save() - thisConfig := ResetConfig(Fmt("%s_%d", testName, i)) + thisConfig := ResetConfig(cmn.Fmt("%s_%d", testName, i)) for _, opt := range configOpts { opt(thisConfig) } @@ -362,13 +362,13 @@ func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerF state, _ := sm.MakeGenesisState(db, genDoc) state.SetLogger(log.TestingLogger().With("module", "state")) state.Save() - thisConfig := ResetConfig(Fmt("%s_%d", testName, i)) + thisConfig := ResetConfig(cmn.Fmt("%s_%d", testName, i)) ensureDir(path.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal var privVal types.PrivValidator if i < nValidators { privVal = privVals[i] } else { - _, tempFilePath := Tempfile("priv_validator_") + _, tempFilePath := cmn.Tempfile("priv_validator_") privVal = types.GenPrivValidatorFS(tempFilePath) } diff --git a/consensus/height_vote_set.go b/consensus/height_vote_set.go index 455004f9..019b2642 100644 --- a/consensus/height_vote_set.go +++ b/consensus/height_vote_set.go @@ -5,7 +5,7 @@ import ( "sync" "github.com/tendermint/tendermint/types" - . "github.com/tendermint/tmlibs/common" + cmn "github.com/tendermint/tmlibs/common" ) type RoundVoteSet struct { @@ -76,7 +76,7 @@ func (hvs *HeightVoteSet) SetRound(round int) { hvs.mtx.Lock() defer hvs.mtx.Unlock() if hvs.round != 0 && (round < hvs.round+1) { - PanicSanity("SetRound() must increment hvs.round") + cmn.PanicSanity("SetRound() must increment hvs.round") } for r := hvs.round + 1; r <= round; r++ { if _, ok := hvs.roundVoteSets[r]; ok { @@ -89,7 +89,7 @@ func (hvs *HeightVoteSet) SetRound(round int) { func (hvs *HeightVoteSet) addRound(round int) { if _, ok := hvs.roundVoteSets[round]; ok { - PanicSanity("addRound() for an existing round") + cmn.PanicSanity("addRound() for an existing round") } // log.Debug("addRound(round)", "round", round) prevotes := types.NewVoteSet(hvs.chainID, hvs.height, round, types.VoteTypePrevote, hvs.valSet) @@ -164,7 +164,7 @@ func (hvs *HeightVoteSet) getVoteSet(round int, type_ byte) *types.VoteSet { case types.VoteTypePrecommit: return rvs.Precommits default: - PanicSanity(Fmt("Unexpected vote type %X", type_)) + cmn.PanicSanity(cmn.Fmt("Unexpected vote type %X", type_)) return nil } } @@ -194,7 +194,7 @@ func (hvs *HeightVoteSet) StringIndented(indent string) string { voteSetString = roundVoteSet.Precommits.StringShort() vsStrings = append(vsStrings, voteSetString) } - return Fmt(`HeightVoteSet{H:%v R:0~%v + return cmn.Fmt(`HeightVoteSet{H:%v R:0~%v %s %v %s}`, hvs.height, hvs.round, diff --git a/consensus/height_vote_set_test.go b/consensus/height_vote_set_test.go index 7e03e40f..30eab5ae 100644 --- a/consensus/height_vote_set_test.go +++ b/consensus/height_vote_set_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/tendermint/tendermint/types" - . "github.com/tendermint/tmlibs/common" + cmn "github.com/tendermint/tmlibs/common" ) func init() { @@ -57,7 +57,7 @@ func makeVoteHR(t *testing.T, height, round int, privVals []*types.PrivValidator chainID := config.ChainID err := privVal.SignVote(chainID, vote) if err != nil { - panic(Fmt("Error signing vote: %v", err)) + panic(cmn.Fmt("Error signing vote: %v", err)) return nil } return vote diff --git a/consensus/mempool_test.go b/consensus/mempool_test.go index f17d19f5..3a430ef2 100644 --- a/consensus/mempool_test.go +++ b/consensus/mempool_test.go @@ -8,7 +8,7 @@ import ( abci "github.com/tendermint/abci/types" "github.com/tendermint/tendermint/types" - . "github.com/tendermint/tmlibs/common" + cmn "github.com/tendermint/tmlibs/common" ) func init() { @@ -86,7 +86,7 @@ func deliverTxsRange(cs *ConsensusState, start, end int) { binary.BigEndian.PutUint64(txBytes, uint64(i)) err := cs.mempool.CheckTx(txBytes, nil) if err != nil { - panic(Fmt("Error after CheckTx: %v", err)) + panic(cmn.Fmt("Error after CheckTx: %v", err)) } } } @@ -184,7 +184,7 @@ func NewCounterApplication() *CounterApplication { } func (app *CounterApplication) Info(req abci.RequestInfo) abci.ResponseInfo { - return abci.ResponseInfo{Data: Fmt("txs:%v", app.txCount)} + return abci.ResponseInfo{Data: cmn.Fmt("txs:%v", app.txCount)} } func (app *CounterApplication) DeliverTx(tx []byte) abci.Result { @@ -201,7 +201,7 @@ func runTx(tx []byte, countPtr *int) abci.Result { copy(tx8[len(tx8)-len(tx):], tx) txValue := binary.BigEndian.Uint64(tx8) if txValue != uint64(count) { - return abci.ErrBadNonce.AppendLog(Fmt("Invalid nonce. Expected %v, got %v", count, txValue)) + return abci.ErrBadNonce.AppendLog(cmn.Fmt("Invalid nonce. Expected %v, got %v", count, txValue)) } *countPtr += 1 return abci.OK diff --git a/consensus/state_test.go b/consensus/state_test.go index c4a6769e..246fd879 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -7,7 +7,7 @@ import ( "time" "github.com/tendermint/tendermint/types" - . "github.com/tendermint/tmlibs/common" + cmn "github.com/tendermint/tmlibs/common" ) func init() { @@ -80,7 +80,7 @@ func TestProposerSelection0(t *testing.T) { prop = cs1.GetRoundState().Validators.GetProposer() if !bytes.Equal(prop.Address, vss[1].GetAddress()) { - panic(Fmt("expected proposer to be validator %d. Got %X", 1, prop.Address)) + panic(cmn.Fmt("expected proposer to be validator %d. Got %X", 1, prop.Address)) } } @@ -101,7 +101,7 @@ func TestProposerSelection2(t *testing.T) { for i := 0; i < len(vss); i++ { prop := cs1.GetRoundState().Validators.GetProposer() if !bytes.Equal(prop.Address, vss[(i+2)%len(vss)].GetAddress()) { - panic(Fmt("expected proposer to be validator %d. Got %X", (i+2)%len(vss), prop.Address)) + panic(cmn.Fmt("expected proposer to be validator %d. Got %X", (i+2)%len(vss), prop.Address)) } rs := cs1.GetRoundState() @@ -432,7 +432,7 @@ func TestLockNoPOL(t *testing.T) { // now we're on a new round and are the proposer if !bytes.Equal(rs.ProposalBlock.Hash(), rs.LockedBlock.Hash()) { - panic(Fmt("Expected proposal block to be locked block. Got %v, Expected %v", rs.ProposalBlock, rs.LockedBlock)) + panic(cmn.Fmt("Expected proposal block to be locked block. Got %v, Expected %v", rs.ProposalBlock, rs.LockedBlock)) } <-voteCh // prevote diff --git a/consensus/version.go b/consensus/version.go index 84f1ec81..2c137bf7 100644 --- a/consensus/version.go +++ b/consensus/version.go @@ -1,7 +1,7 @@ package consensus import ( - . "github.com/tendermint/tmlibs/common" + cmn "github.com/tendermint/tmlibs/common" ) // kind of arbitrary @@ -10,4 +10,4 @@ var Major = "0" // var Minor = "2" // replay refactor var Revision = "2" // validation -> commit -var Version = Fmt("v%s/%s.%s.%s", Spec, Major, Minor, Revision) +var Version = cmn.Fmt("v%s/%s.%s.%s", Spec, Major, Minor, Revision) diff --git a/consensus/wal.go b/consensus/wal.go index a2ac470a..f9a2a801 100644 --- a/consensus/wal.go +++ b/consensus/wal.go @@ -6,7 +6,7 @@ import ( wire "github.com/tendermint/go-wire" "github.com/tendermint/tendermint/types" auto "github.com/tendermint/tmlibs/autofile" - . "github.com/tendermint/tmlibs/common" + cmn "github.com/tendermint/tmlibs/common" ) //-------------------------------------------------------- @@ -34,7 +34,7 @@ var _ = wire.RegisterInterface( // TODO: currently the wal is overwritten during replay catchup // give it a mode so it's either reading or appending - must read to end to start appending again type WAL struct { - BaseService + cmn.BaseService group *auto.Group light bool // ignore block parts @@ -49,7 +49,7 @@ func NewWAL(walFile string, light bool) (*WAL, error) { group: group, light: light, } - wal.BaseService = *NewBaseService(nil, "WAL", wal) + wal.BaseService = *cmn.NewBaseService(nil, "WAL", wal) return wal, nil } @@ -86,19 +86,19 @@ func (wal *WAL) Save(wmsg WALMessage) { var wmsgBytes = wire.JSONBytes(TimedWALMessage{time.Now(), wmsg}) err := wal.group.WriteLine(string(wmsgBytes)) if err != nil { - PanicQ(Fmt("Error writing msg to consensus wal. Error: %v \n\nMessage: %v", err, wmsg)) + cmn.PanicQ(cmn.Fmt("Error writing msg to consensus wal. Error: %v \n\nMessage: %v", err, wmsg)) } // TODO: only flush when necessary if err := wal.group.Flush(); err != nil { - PanicQ(Fmt("Error flushing consensus wal buf to file. Error: %v \n", err)) + cmn.PanicQ(cmn.Fmt("Error flushing consensus wal buf to file. Error: %v \n", err)) } } func (wal *WAL) writeEndHeight(height int) { - wal.group.WriteLine(Fmt("#ENDHEIGHT: %v", height)) + wal.group.WriteLine(cmn.Fmt("#ENDHEIGHT: %v", height)) // TODO: only flush when necessary if err := wal.group.Flush(); err != nil { - PanicQ(Fmt("Error flushing consensus wal buf to file. Error: %v \n", err)) + cmn.PanicQ(cmn.Fmt("Error flushing consensus wal buf to file. Error: %v \n", err)) } } diff --git a/node/id.go b/node/id.go index c521aa4a..fa391f94 100644 --- a/node/id.go +++ b/node/id.go @@ -1,8 +1,9 @@ package node import ( - "github.com/tendermint/go-crypto" "time" + + "github.com/tendermint/go-crypto" ) type NodeID struct { diff --git a/p2p/peer.go b/p2p/peer.go index 2b986a7a..1bdb8210 100644 --- a/p2p/peer.go +++ b/p2p/peer.go @@ -7,6 +7,7 @@ import ( "time" "github.com/pkg/errors" + crypto "github.com/tendermint/go-crypto" wire "github.com/tendermint/go-wire" cmn "github.com/tendermint/tmlibs/common" diff --git a/p2p/upnp/README.md b/p2p/upnp/README.md deleted file mode 100644 index 557d05bd..00000000 --- a/p2p/upnp/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# `tendermint/p2p/upnp` - -## Resources - -* http://www.upnp-hacks.org/upnp.html diff --git a/rpc/client/httpclient.go b/rpc/client/httpclient.go index 7b09b5bd..b00c97d2 100644 --- a/rpc/client/httpclient.go +++ b/rpc/client/httpclient.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/pkg/errors" + data "github.com/tendermint/go-wire/data" ctypes "github.com/tendermint/tendermint/rpc/core/types" rpcclient "github.com/tendermint/tendermint/rpc/lib/client" diff --git a/rpc/client/mock/abci_test.go b/rpc/client/mock/abci_test.go index 935f9ff9..245db6c6 100644 --- a/rpc/client/mock/abci_test.go +++ b/rpc/client/mock/abci_test.go @@ -8,13 +8,13 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/tendermint/abci/example/dummy" abci "github.com/tendermint/abci/types" data "github.com/tendermint/go-wire/data" + "github.com/tendermint/tendermint/rpc/client/mock" ctypes "github.com/tendermint/tendermint/rpc/core/types" "github.com/tendermint/tendermint/types" - - "github.com/tendermint/tendermint/rpc/client/mock" ) func TestABCIMock(t *testing.T) { diff --git a/rpc/client/mock/status_test.go b/rpc/client/mock/status_test.go index e4adf52b..80ffe7b7 100644 --- a/rpc/client/mock/status_test.go +++ b/rpc/client/mock/status_test.go @@ -5,10 +5,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - data "github.com/tendermint/go-wire/data" - ctypes "github.com/tendermint/tendermint/rpc/core/types" + data "github.com/tendermint/go-wire/data" "github.com/tendermint/tendermint/rpc/client/mock" + ctypes "github.com/tendermint/tendermint/rpc/core/types" ) func TestStatus(t *testing.T) { diff --git a/rpc/client/rpc_test.go b/rpc/client/rpc_test.go index 77de9f6e..9bcd3de4 100644 --- a/rpc/client/rpc_test.go +++ b/rpc/client/rpc_test.go @@ -6,7 +6,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/tendermint/merkleeyes/iavl" + + "github.com/tendermint/merkleeyes/iavl" //TODO use tendermint/iavl ? "github.com/tendermint/tendermint/rpc/client" rpctest "github.com/tendermint/tendermint/rpc/test" "github.com/tendermint/tendermint/types" diff --git a/rpc/core/blocks.go b/rpc/core/blocks.go index 17b27303..ad00060f 100644 --- a/rpc/core/blocks.go +++ b/rpc/core/blocks.go @@ -5,7 +5,7 @@ import ( ctypes "github.com/tendermint/tendermint/rpc/core/types" "github.com/tendermint/tendermint/types" - . "github.com/tendermint/tmlibs/common" + cmn "github.com/tendermint/tmlibs/common" ) // Get block headers for minHeight <= height <= maxHeight. @@ -65,12 +65,12 @@ func BlockchainInfo(minHeight, maxHeight int) (*ctypes.ResultBlockchainInfo, err if maxHeight == 0 { maxHeight = blockStore.Height() } else { - maxHeight = MinInt(blockStore.Height(), maxHeight) + maxHeight = cmn.MinInt(blockStore.Height(), maxHeight) } if minHeight == 0 { - minHeight = MaxInt(1, maxHeight-20) + minHeight = cmn.MaxInt(1, maxHeight-20) } else { - minHeight = MaxInt(minHeight, maxHeight-20) + minHeight = cmn.MaxInt(minHeight, maxHeight-20) } logger.Debug("BlockchainInfoHandler", "maxHeight", maxHeight, "minHeight", minHeight) diff --git a/rpc/core/pipe.go b/rpc/core/pipe.go index 86c6c65e..0602689b 100644 --- a/rpc/core/pipe.go +++ b/rpc/core/pipe.go @@ -2,14 +2,13 @@ package core import ( crypto "github.com/tendermint/go-crypto" - "github.com/tendermint/tmlibs/log" - "github.com/tendermint/tendermint/consensus" p2p "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/proxy" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/state/txindex" "github.com/tendermint/tendermint/types" + "github.com/tendermint/tmlibs/log" ) //---------------------------------------------- diff --git a/rpc/core/types/responses.go b/rpc/core/types/responses.go index 6b66f116..22c9f9ab 100644 --- a/rpc/core/types/responses.go +++ b/rpc/core/types/responses.go @@ -6,7 +6,6 @@ import ( abci "github.com/tendermint/abci/types" "github.com/tendermint/go-crypto" "github.com/tendermint/go-wire/data" - "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/types" ) diff --git a/rpc/core/types/responses_test.go b/rpc/core/types/responses_test.go index 8eef1979..fa0da3fd 100644 --- a/rpc/core/types/responses_test.go +++ b/rpc/core/types/responses_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/tendermint/tendermint/p2p" ) diff --git a/rpc/grpc/api.go b/rpc/grpc/api.go index 7cfda158..b08a7833 100644 --- a/rpc/grpc/api.go +++ b/rpc/grpc/api.go @@ -1,11 +1,10 @@ package core_grpc import ( - core "github.com/tendermint/tendermint/rpc/core" + context "golang.org/x/net/context" abci "github.com/tendermint/abci/types" - - context "golang.org/x/net/context" + core "github.com/tendermint/tendermint/rpc/core" ) type broadcastAPI struct { diff --git a/rpc/grpc/client_server.go b/rpc/grpc/client_server.go index e6055ede..1c6498df 100644 --- a/rpc/grpc/client_server.go +++ b/rpc/grpc/client_server.go @@ -8,7 +8,7 @@ import ( "google.golang.org/grpc" - . "github.com/tendermint/tmlibs/common" + cmn "github.com/tendermint/tmlibs/common" ) // Start the grpcServer in a go routine @@ -40,5 +40,5 @@ func StartGRPCClient(protoAddr string) BroadcastAPIClient { } func dialerFunc(addr string, timeout time.Duration) (net.Conn, error) { - return Connect(addr) + return cmn.Connect(addr) } diff --git a/rpc/lib/rpc_test.go b/rpc/lib/rpc_test.go index 3ceaaa39..4e83d23e 100644 --- a/rpc/lib/rpc_test.go +++ b/rpc/lib/rpc_test.go @@ -16,6 +16,7 @@ import ( "github.com/go-kit/kit/log/term" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/tendermint/go-wire/data" client "github.com/tendermint/tendermint/rpc/lib/client" server "github.com/tendermint/tendermint/rpc/lib/server" diff --git a/state/execution_test.go b/state/execution_test.go index 2987667c..425f59ed 100644 --- a/state/execution_test.go +++ b/state/execution_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/tendermint/abci/example/dummy" crypto "github.com/tendermint/go-crypto" "github.com/tendermint/tendermint/proxy" diff --git a/state/state_test.go b/state/state_test.go index 2c19a416..2ab2e934 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -7,16 +7,14 @@ import ( "github.com/stretchr/testify/assert" + cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/types" + abci "github.com/tendermint/abci/types" - crypto "github.com/tendermint/go-crypto" - cmn "github.com/tendermint/tmlibs/common" dbm "github.com/tendermint/tmlibs/db" "github.com/tendermint/tmlibs/log" - - cfg "github.com/tendermint/tendermint/config" - "github.com/tendermint/tendermint/types" ) // setupTestCase does setup common to all test cases diff --git a/types/genesis_test.go b/types/genesis_test.go index fe997b69..0ffce4b5 100644 --- a/types/genesis_test.go +++ b/types/genesis_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + crypto "github.com/tendermint/go-crypto" ) diff --git a/types/part_set_test.go b/types/part_set_test.go index 7088ef31..76b538c1 100644 --- a/types/part_set_test.go +++ b/types/part_set_test.go @@ -5,7 +5,7 @@ import ( "io/ioutil" "testing" - . "github.com/tendermint/tmlibs/common" + cmn "github.com/tendermint/tmlibs/common" ) const ( @@ -15,7 +15,7 @@ const ( func TestBasicPartSet(t *testing.T) { // Construct random data of size partSize * 100 - data := RandBytes(testPartSize * 100) + data := cmn.RandBytes(testPartSize * 100) partSet := NewPartSetFromData(data, testPartSize) if len(partSet.Hash()) == 0 { @@ -65,7 +65,7 @@ func TestBasicPartSet(t *testing.T) { func TestWrongProof(t *testing.T) { // Construct random data of size partSize * 100 - data := RandBytes(testPartSize * 100) + data := cmn.RandBytes(testPartSize * 100) partSet := NewPartSetFromData(data, testPartSize) // Test adding a part with wrong data. diff --git a/types/proposal.go b/types/proposal.go index 8fe95980..8efa91b6 100644 --- a/types/proposal.go +++ b/types/proposal.go @@ -5,7 +5,6 @@ import ( "fmt" "io" - //. "github.com/tendermint/tmlibs/common" "github.com/tendermint/go-crypto" "github.com/tendermint/go-wire" ) diff --git a/types/signable.go b/types/signable.go index 13389fef..2134f630 100644 --- a/types/signable.go +++ b/types/signable.go @@ -4,7 +4,7 @@ import ( "bytes" "io" - . "github.com/tendermint/tmlibs/common" + cmn "github.com/tendermint/tmlibs/common" "github.com/tendermint/tmlibs/merkle" ) @@ -19,7 +19,7 @@ func SignBytes(chainID string, o Signable) []byte { buf, n, err := new(bytes.Buffer), new(int), new(error) o.WriteSignBytes(chainID, buf, n, err) if *err != nil { - PanicCrisis(err) + cmn.PanicCrisis(err) } return buf.Bytes() } diff --git a/types/tx_test.go b/types/tx_test.go index 91cddecf..dc99509d 100644 --- a/types/tx_test.go +++ b/types/tx_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + wire "github.com/tendermint/go-wire" cmn "github.com/tendermint/tmlibs/common" ctest "github.com/tendermint/tmlibs/test" diff --git a/types/vote_set.go b/types/vote_set.go index 7a5d5267..dcfb0088 100644 --- a/types/vote_set.go +++ b/types/vote_set.go @@ -6,7 +6,7 @@ import ( "strings" "sync" - . "github.com/tendermint/tmlibs/common" + cmn "github.com/tendermint/tmlibs/common" ) /* @@ -51,7 +51,7 @@ type VoteSet struct { mtx sync.Mutex valSet *ValidatorSet - votesBitArray *BitArray + votesBitArray *cmn.BitArray votes []*Vote // Primary votes to share sum int64 // Sum of voting power for seen votes, discounting conflicts maj23 *BlockID // First 2/3 majority seen @@ -62,7 +62,7 @@ type VoteSet struct { // Constructs a new VoteSet struct used to accumulate votes for given height/round. func NewVoteSet(chainID string, height int, round int, type_ byte, valSet *ValidatorSet) *VoteSet { if height == 0 { - PanicSanity("Cannot make VoteSet for height == 0, doesn't make sense.") + cmn.PanicSanity("Cannot make VoteSet for height == 0, doesn't make sense.") } return &VoteSet{ chainID: chainID, @@ -70,7 +70,7 @@ func NewVoteSet(chainID string, height int, round int, type_ byte, valSet *Valid round: round, type_: type_, valSet: valSet, - votesBitArray: NewBitArray(valSet.Size()), + votesBitArray: cmn.NewBitArray(valSet.Size()), votes: make([]*Vote, valSet.Size()), sum: 0, maj23: nil, @@ -125,7 +125,7 @@ func (voteSet *VoteSet) Size() int { // NOTE: VoteSet must not be nil func (voteSet *VoteSet) AddVote(vote *Vote) (added bool, err error) { if voteSet == nil { - PanicSanity("AddVote() on nil VoteSet") + cmn.PanicSanity("AddVote() on nil VoteSet") } voteSet.mtx.Lock() defer voteSet.mtx.Unlock() @@ -188,7 +188,7 @@ func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) { } } else { if !added { - PanicSanity("Expected to add non-conflicting vote") + cmn.PanicSanity("Expected to add non-conflicting vote") } return added, nil } @@ -214,7 +214,7 @@ func (voteSet *VoteSet) addVerifiedVote(vote *Vote, blockKey string, votingPower // Already exists in voteSet.votes? if existing := voteSet.votes[valIndex]; existing != nil { if existing.BlockID.Equals(vote.BlockID) { - PanicSanity("addVerifiedVote does not expect duplicate votes") + cmn.PanicSanity("addVerifiedVote does not expect duplicate votes") } else { conflicting = existing } @@ -285,7 +285,7 @@ func (voteSet *VoteSet) addVerifiedVote(vote *Vote, blockKey string, votingPower // NOTE: VoteSet must not be nil func (voteSet *VoteSet) SetPeerMaj23(peerID string, blockID BlockID) { if voteSet == nil { - PanicSanity("SetPeerMaj23() on nil VoteSet") + cmn.PanicSanity("SetPeerMaj23() on nil VoteSet") } voteSet.mtx.Lock() defer voteSet.mtx.Unlock() @@ -318,7 +318,7 @@ func (voteSet *VoteSet) SetPeerMaj23(peerID string, blockID BlockID) { } } -func (voteSet *VoteSet) BitArray() *BitArray { +func (voteSet *VoteSet) BitArray() *cmn.BitArray { if voteSet == nil { return nil } @@ -327,7 +327,7 @@ func (voteSet *VoteSet) BitArray() *BitArray { return voteSet.votesBitArray.Copy() } -func (voteSet *VoteSet) BitArrayByBlockID(blockID BlockID) *BitArray { +func (voteSet *VoteSet) BitArrayByBlockID(blockID BlockID) *cmn.BitArray { if voteSet == nil { return nil } @@ -358,7 +358,7 @@ func (voteSet *VoteSet) GetByAddress(address []byte) *Vote { defer voteSet.mtx.Unlock() valIndex, val := voteSet.valSet.GetByAddress(address) if val == nil { - PanicSanity("GetByAddress(address) returned nil") + cmn.PanicSanity("GetByAddress(address) returned nil") } return voteSet.votes[valIndex] } @@ -456,14 +456,14 @@ func (voteSet *VoteSet) StringShort() string { func (voteSet *VoteSet) MakeCommit() *Commit { if voteSet.type_ != VoteTypePrecommit { - PanicSanity("Cannot MakeCommit() unless VoteSet.Type is VoteTypePrecommit") + cmn.PanicSanity("Cannot MakeCommit() unless VoteSet.Type is VoteTypePrecommit") } voteSet.mtx.Lock() defer voteSet.mtx.Unlock() // Make sure we have a 2/3 majority if voteSet.maj23 == nil { - PanicSanity("Cannot MakeCommit() unless a blockhash has +2/3") + cmn.PanicSanity("Cannot MakeCommit() unless a blockhash has +2/3") } // For every validator, get the precommit @@ -484,16 +484,16 @@ func (voteSet *VoteSet) MakeCommit() *Commit { 2. A peer claims to have a 2/3 majority w/ blockKey (peerMaj23=true) */ type blockVotes struct { - peerMaj23 bool // peer claims to have maj23 - bitArray *BitArray // valIndex -> hasVote? - votes []*Vote // valIndex -> *Vote - sum int64 // vote sum + peerMaj23 bool // peer claims to have maj23 + bitArray *cmn.BitArray // valIndex -> hasVote? + votes []*Vote // valIndex -> *Vote + sum int64 // vote sum } func newBlockVotes(peerMaj23 bool, numValidators int) *blockVotes { return &blockVotes{ peerMaj23: peerMaj23, - bitArray: NewBitArray(numValidators), + bitArray: cmn.NewBitArray(numValidators), votes: make([]*Vote, numValidators), sum: 0, } @@ -523,7 +523,7 @@ type VoteSetReader interface { Round() int Type() byte Size() int - BitArray() *BitArray + BitArray() *cmn.BitArray GetByIndex(int) *Vote IsCommit() bool } diff --git a/types/vote_set_test.go b/types/vote_set_test.go index 80ee6135..5a757a00 100644 --- a/types/vote_set_test.go +++ b/types/vote_set_test.go @@ -2,12 +2,11 @@ package types import ( "bytes" + "testing" "github.com/tendermint/go-crypto" - . "github.com/tendermint/tmlibs/common" - . "github.com/tendermint/tmlibs/test" - - "testing" + cmn "github.com/tendermint/tmlibs/common" + tst "github.com/tendermint/tmlibs/test" ) // NOTE: privValidators are in order @@ -137,7 +136,7 @@ func Test2_3Majority(t *testing.T) { // 7th validator voted for some blockhash { vote := withValidator(voteProto, privValidators[6].GetAddress(), 6) - signAddVote(privValidators[6], withBlockHash(vote, RandBytes(32)), voteSet) + signAddVote(privValidators[6], withBlockHash(vote, cmn.RandBytes(32)), voteSet) blockID, ok = voteSet.TwoThirdsMajority() if ok || !blockID.IsZero() { t.Errorf("There should be no 2/3 majority") @@ -217,7 +216,7 @@ func Test2_3MajorityRedux(t *testing.T) { // 70th validator voted for different BlockHash { vote := withValidator(voteProto, privValidators[69].GetAddress(), 69) - signAddVote(privValidators[69], withBlockHash(vote, RandBytes(32)), voteSet) + signAddVote(privValidators[69], withBlockHash(vote, cmn.RandBytes(32)), voteSet) blockID, ok = voteSet.TwoThirdsMajority() if ok || !blockID.IsZero() { t.Errorf("There should be no 2/3 majority: last vote added had different BlockHash") @@ -260,7 +259,7 @@ func TestBadVotes(t *testing.T) { // val0 votes again for some block. { vote := withValidator(voteProto, privValidators[0].GetAddress(), 0) - added, err := signAddVote(privValidators[0], withBlockHash(vote, RandBytes(32)), voteSet) + added, err := signAddVote(privValidators[0], withBlockHash(vote, cmn.RandBytes(32)), voteSet) if added || err == nil { t.Errorf("Expected VoteSet.Add to fail, conflicting vote.") } @@ -297,8 +296,8 @@ func TestBadVotes(t *testing.T) { func TestConflicts(t *testing.T) { height, round := 1, 0 voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrevote, 4, 1) - blockHash1 := RandBytes(32) - blockHash2 := RandBytes(32) + blockHash1 := cmn.RandBytes(32) + blockHash2 := cmn.RandBytes(32) voteProto := &Vote{ ValidatorAddress: nil, @@ -444,13 +443,13 @@ func TestMakeCommit(t *testing.T) { } // MakeCommit should fail. - AssertPanics(t, "Doesn't have +2/3 majority", func() { voteSet.MakeCommit() }) + tst.AssertPanics(t, "Doesn't have +2/3 majority", func() { voteSet.MakeCommit() }) // 7th voted for some other block. { vote := withValidator(voteProto, privValidators[6].GetAddress(), 6) - vote = withBlockHash(vote, RandBytes(32)) - vote = withBlockPartsHeader(vote, PartSetHeader{123, RandBytes(32)}) + vote = withBlockHash(vote, cmn.RandBytes(32)) + vote = withBlockPartsHeader(vote, PartSetHeader{123, cmn.RandBytes(32)}) signAddVote(privValidators[6], vote, voteSet) } From f23d47e5d27996b438e950335342c9ce0eabf22e Mon Sep 17 00:00:00 2001 From: Zach Ramsay Date: Wed, 4 Oct 2017 17:19:49 -0400 Subject: [PATCH 35/54] upnp: keep a link --- p2p/upnp/upnp.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/p2p/upnp/upnp.go b/p2p/upnp/upnp.go index 29abbe79..7d44d1e3 100644 --- a/p2p/upnp/upnp.go +++ b/p2p/upnp/upnp.go @@ -1,11 +1,9 @@ -/* -Taken from taipei-torrent - -Just enough UPnP to be able to forward ports -*/ +// Taken from taipei-torrent. +// Just enough UPnP to be able to forward ports +// For more information, see: http://www.upnp-hacks.org/upnp.html package upnp -// BUG(jae): TODO: use syscalls to get actual ourIP. http://pastebin.com/9exZG4rh +// TODO: use syscalls to get actual ourIP, see issue #712 import ( "bytes" From 068f01368f11db7fa3f5f48c8eab16837a9d319c Mon Sep 17 00:00:00 2001 From: Emmanuel Odeke Date: Sat, 26 Aug 2017 02:33:19 -0600 Subject: [PATCH 36/54] blockchain/reactor: respondWithNoResponseMessage for missing height Fixes #514 Replaces #540 If a peer requests a block with a height that we don't have respond with a bcNoBlockResponseMessage. However, according to the Tendermint spec, if all nodes are honest this condition shouldn't occur, so this is a possible hint of an dishonest node. --- blockchain/reactor.go | 48 +++++--- blockchain/reactor_test.go | 218 +++++++++++++++++++++++++++++++++++++ 2 files changed, 252 insertions(+), 14 deletions(-) create mode 100644 blockchain/reactor_test.go diff --git a/blockchain/reactor.go b/blockchain/reactor.go index 7ebf3179..91f0aded 100644 --- a/blockchain/reactor.go +++ b/blockchain/reactor.go @@ -121,6 +121,24 @@ func (bcR *BlockchainReactor) RemovePeer(peer p2p.Peer, reason interface{}) { bcR.pool.RemovePeer(peer.Key()) } +// respondToPeer loads a block and sends it to the requesting peer, +// if we have it. Otherwise, we'll respond saying we don't have it. +// According to the Tendermint spec, if all nodes are honest, +// no node should be requesting for a block that's non-existent. +func (bcR *BlockchainReactor) respondToPeer(msg *bcBlockRequestMessage, src p2p.Peer) (queued bool) { + block := bcR.store.LoadBlock(msg.Height) + if block != nil { + msg := &bcBlockResponseMessage{Block: block} + return src.TrySend(BlockchainChannel, struct{ BlockchainMessage }{msg}) + } + + bcR.Logger.Info("Peer asking for a block we don't have", "src", src, "height", msg.Height) + + return src.TrySend(BlockchainChannel, struct{ BlockchainMessage }{ + &bcNoBlockResponseMessage{Height: msg.Height}, + }) +} + // Receive implements Reactor by handling 4 types of messages (look below). func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) { _, msg, err := DecodeMessage(msgBytes, bcR.maxMsgSize()) @@ -134,16 +152,8 @@ func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) // TODO: improve logic to satisfy megacheck switch msg := msg.(type) { case *bcBlockRequestMessage: - // Got a request for a block. Respond with block if we have it. - block := bcR.store.LoadBlock(msg.Height) - if block != nil { - msg := &bcBlockResponseMessage{Block: block} - queued := src.TrySend(BlockchainChannel, struct{ BlockchainMessage }{msg}) - if !queued { - // queue is full, just ignore. - } - } else { - // TODO peer is asking for things we don't have. + if queued := bcR.respondToPeer(msg, src); !queued { + // Unfortunately not queued since the queue is full. } case *bcBlockResponseMessage: // Got a block. @@ -276,10 +286,11 @@ func (bcR *BlockchainReactor) SetEventSwitch(evsw types.EventSwitch) { // Messages const ( - msgTypeBlockRequest = byte(0x10) - msgTypeBlockResponse = byte(0x11) - msgTypeStatusResponse = byte(0x20) - msgTypeStatusRequest = byte(0x21) + msgTypeBlockRequest = byte(0x10) + msgTypeBlockResponse = byte(0x11) + msgTypeNoBlockResponse = byte(0x12) + msgTypeStatusResponse = byte(0x20) + msgTypeStatusRequest = byte(0x21) ) // BlockchainMessage is a generic message for this reactor. @@ -289,6 +300,7 @@ var _ = wire.RegisterInterface( struct{ BlockchainMessage }{}, wire.ConcreteType{&bcBlockRequestMessage{}, msgTypeBlockRequest}, wire.ConcreteType{&bcBlockResponseMessage{}, msgTypeBlockResponse}, + wire.ConcreteType{&bcNoBlockResponseMessage{}, msgTypeNoBlockResponse}, wire.ConcreteType{&bcStatusResponseMessage{}, msgTypeStatusResponse}, wire.ConcreteType{&bcStatusRequestMessage{}, msgTypeStatusRequest}, ) @@ -316,6 +328,14 @@ func (m *bcBlockRequestMessage) String() string { return cmn.Fmt("[bcBlockRequestMessage %v]", m.Height) } +type bcNoBlockResponseMessage struct { + Height int +} + +func (brm *bcNoBlockResponseMessage) String() string { + return cmn.Fmt("[bcNoBlockResponseMessage %d]", brm.Height) +} + //------------------------------------- // NOTE: keep up-to-date with maxBlockchainResponseSize diff --git a/blockchain/reactor_test.go b/blockchain/reactor_test.go new file mode 100644 index 00000000..a13c6aed --- /dev/null +++ b/blockchain/reactor_test.go @@ -0,0 +1,218 @@ +package blockchain + +import ( + "bytes" + "testing" + + cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/p2p" + sm "github.com/tendermint/tendermint/state" + "github.com/tendermint/tendermint/types" + cmn "github.com/tendermint/tmlibs/common" + "github.com/tendermint/tmlibs/db" + "github.com/tendermint/tmlibs/log" +) + +func newBlockchainReactor(logger log.Logger, maxBlockHeight int) *BlockchainReactor { + config := cfg.ResetTestRoot("node_node_test") + + blockStoreDB := db.NewDB("blockstore", config.DBBackend, config.DBDir()) + blockStore := NewBlockStore(blockStoreDB) + + stateLogger := logger.With("module", "state") + + // Get State + stateDB := db.NewDB("state", config.DBBackend, config.DBDir()) + state := sm.GetState(stateDB, config.GenesisFile()) + state.SetLogger(stateLogger) + state.Save() + + // Make the blockchainReactor itself + fastSync := true + bcReactor := NewBlockchainReactor(state.Copy(), nil, blockStore, fastSync) + + // Next: we need to set a switch in order for peers to be added in + bcReactor.Switch = p2p.NewSwitch(cfg.DefaultP2PConfig()) + bcReactor.SetLogger(logger.With("module", "blockchain")) + + // Lastly: let's add some blocks in + for blockHeight := 1; blockHeight <= maxBlockHeight; blockHeight++ { + firstBlock := makeBlock(blockHeight, state) + secondBlock := makeBlock(blockHeight+1, state) + firstParts := firstBlock.MakePartSet(types.DefaultBlockPartSize) + blockStore.SaveBlock(firstBlock, firstParts, secondBlock.LastCommit) + } + + return bcReactor +} + +func newbcrTestPeer(key string) *bcrTestPeer { + return &bcrTestPeer{ + key: key, + kvm: make(map[byte]interface{}), + ch: make(chan *keyValue, 2), + } +} + +func TestNoBlockMessageResponse(t *testing.T) { + logBuf := new(bytes.Buffer) + logger := log.NewTMLogger(logBuf) + maxBlockHeight := 20 + + bcr := newBlockchainReactor(logger, maxBlockHeight) + go bcr.OnStart() + defer bcr.Stop() + + // Add some peers in + peer := newbcrTestPeer(cmn.RandStr(12)) + bcr.AddPeer(peer) + + chID := byte(0x01) + + tests := [...]struct { + height int + existent bool + }{ + 0: { + height: maxBlockHeight + 2, + existent: false, + }, + 1: { + height: 10, + existent: true, // We should have this + }, + 2: { + height: 1, + existent: true, // We should have this + }, + 3: { + height: 100, + existent: false, + }, + } + + // Currently the repsonses take asynchronous paths so + // the results are skewed. However, we can ensure that + // the (expectedHeight, expectedExistence) all tally up. + heightTally := map[int]bool{} + var results []BlockchainMessage + + msgRequest := byte(0x10) + for _, tt := range tests { + reqBlockBytes := []byte{msgRequest, 0x02, 0x00, byte(tt.height)} + bcr.Receive(chID, peer, reqBlockBytes) + heightTally[tt.height] = tt.existent + kv := peer.lastKeyValue() + results = append(results, kv.value.(struct{ BlockchainMessage }).BlockchainMessage) + } + + for i, res := range results { + if bcRM, ok := res.(*bcBlockResponseMessage); ok { + block := bcRM.Block + if block == nil { + t.Errorf("result: #%d: expecting a non-nil block", i) + continue + } + mustExist, foundHeight := heightTally[block.Height] + if !foundHeight { + t.Errorf("result: #%d, missing height %d", i, block.Height) + continue + } + if !mustExist { + t.Errorf("result: #%d mustExist", i) + } else { + delete(heightTally, block.Height) + } + } else if bcNBRSM, ok := res.(*bcNoBlockResponseMessage); ok { + mustExist, foundHeight := heightTally[bcNBRSM.Height] + if !foundHeight { + t.Errorf("result: #%d, missing height %d", i, bcNBRSM.Height) + continue + } + if mustExist { + t.Errorf("result: #%d mustNotExist", i) + } else { + // Passes, remove this height from the tally + delete(heightTally, bcNBRSM.Height) + } + } else { + t.Errorf("result: #%d is an unexpected type: %T; data: %#v", i, res, res) + } + } + + if len(heightTally) > 0 { + t.Errorf("Untallied heights: %#v\n", heightTally) + } +} + +func makeTxs(blockNumber int) (txs []types.Tx) { + for i := 0; i < 10; i++ { + txs = append(txs, types.Tx([]byte{byte(blockNumber)})) + } + return txs +} + +const testPartSize = 65536 + +func makeBlock(blockNumber int, state *sm.State) *types.Block { + prevHash := state.LastBlockID.Hash + prevParts := types.PartSetHeader{} + valHash := state.Validators.Hash() + prevBlockID := types.BlockID{prevHash, prevParts} + block, _ := types.MakeBlock(blockNumber, "test_chain", makeTxs(blockNumber), + new(types.Commit), prevBlockID, valHash, state.AppHash, testPartSize) + return block +} + +// The Test peer +type bcrTestPeer struct { + key string + ch chan *keyValue + kvm map[byte]interface{} +} + +type keyValue struct { + key interface{} + value interface{} +} + +var _ p2p.Peer = (*bcrTestPeer)(nil) + +func (tp *bcrTestPeer) Key() string { return tp.key } +func (tp *bcrTestPeer) IsOutbound() bool { return false } +func (tp *bcrTestPeer) IsPersistent() bool { return true } +func (tp *bcrTestPeer) IsRunning() bool { return true } + +func (tp *bcrTestPeer) OnStop() {} + +func (tp *bcrTestPeer) OnReset() error { return nil } +func (tp *bcrTestPeer) Reset() (bool, error) { return false, nil } +func (tp *bcrTestPeer) OnStart() error { return nil } +func (tp *bcrTestPeer) SetLogger(l log.Logger) {} + +func (tp *bcrTestPeer) Start() (bool, error) { return true, nil } +func (tp *bcrTestPeer) Stop() bool { return true } +func (tp *bcrTestPeer) String() string { return tp.key } + +func (tp *bcrTestPeer) TrySend(chID byte, value interface{}) bool { + if _, ok := value.(struct{ BlockchainMessage }).BlockchainMessage.(*bcStatusResponseMessage); ok { + // Discard status response messages since they skew our results + // We only want to deal with: + // + bcBlockResponseMessage + // + bcNoBlockResponseMessage + } else { + tp.ch <- &keyValue{key: chID, value: value} + } + return true +} + +func (tp *bcrTestPeer) Send(chID byte, data interface{}) bool { + return tp.TrySend(chID, data) +} + +func (tp *bcrTestPeer) NodeInfo() *p2p.NodeInfo { return nil } +func (tp *bcrTestPeer) Status() p2p.ConnectionStatus { return p2p.ConnectionStatus{} } + +func (tp *bcrTestPeer) Get(s string) interface{} { return s } +func (tp *bcrTestPeer) Set(string, interface{}) {} +func (tp *bcrTestPeer) lastKeyValue() *keyValue { return <-tp.ch } From 136b6a767355c06e4872377d0a97f6e485b20580 Mon Sep 17 00:00:00 2001 From: Zach Ramsay Date: Wed, 4 Oct 2017 17:26:53 -0400 Subject: [PATCH 37/54] rpc/lib: remove dead files, closes #710 --- p2p/listener.go | 2 +- rpc/lib/Dockerfile | 12 ----- rpc/lib/Makefile | 18 ------- rpc/lib/README.md | 121 --------------------------------------------- rpc/lib/circle.yml | 21 -------- 5 files changed, 1 insertion(+), 173 deletions(-) delete mode 100644 rpc/lib/Dockerfile delete mode 100644 rpc/lib/Makefile delete mode 100644 rpc/lib/README.md delete mode 100644 rpc/lib/circle.yml diff --git a/p2p/listener.go b/p2p/listener.go index a382fbed..97139097 100644 --- a/p2p/listener.go +++ b/p2p/listener.go @@ -196,7 +196,7 @@ func getUPNPExternalAddress(externalPort, internalPort int, logger log.Logger) * return NewNetAddressIPPort(ext, uint16(externalPort)) } -// TODO: use syscalls: http://pastebin.com/9exZG4rh +// TODO: use syscalls: see issue #712 func getNaiveExternalAddress(port int, settleForLocal bool, logger log.Logger) *NetAddress { addrs, err := net.InterfaceAddrs() if err != nil { diff --git a/rpc/lib/Dockerfile b/rpc/lib/Dockerfile deleted file mode 100644 index a194711b..00000000 --- a/rpc/lib/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM golang:latest - -RUN mkdir -p /go/src/github.com/tendermint/tendermint/rpc/lib -WORKDIR /go/src/github.com/tendermint/tendermint/rpc/lib - -COPY Makefile /go/src/github.com/tendermint/tendermint/rpc/lib/ -# COPY glide.yaml /go/src/github.com/tendermint/tendermint/rpc/lib/ -# COPY glide.lock /go/src/github.com/tendermint/tendermint/rpc/lib/ - -COPY . /go/src/github.com/tendermint/tendermint/rpc/lib - -RUN make get_deps diff --git a/rpc/lib/Makefile b/rpc/lib/Makefile deleted file mode 100644 index 0937558a..00000000 --- a/rpc/lib/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -PACKAGES=$(shell go list ./... | grep -v "test") - -all: get_deps test - -test: - @echo "--> Running go test --race" - @go test --race $(PACKAGES) - @echo "--> Running integration tests" - @bash ./test/integration_test.sh - -get_deps: - @echo "--> Running go get" - @go get -v -d $(PACKAGES) - @go list -f '{{join .TestImports "\n"}}' ./... | \ - grep -v /vendor/ | sort | uniq | \ - xargs go get -v -d - -.PHONY: all test get_deps diff --git a/rpc/lib/README.md b/rpc/lib/README.md deleted file mode 100644 index de481c2f..00000000 --- a/rpc/lib/README.md +++ /dev/null @@ -1,121 +0,0 @@ -# tendermint/rpc/lib - -[![CircleCI](https://circleci.com/gh/tendermint/tendermint/rpc/lib.svg?style=svg)](https://circleci.com/gh/tendermint/tendermint/rpc/lib) - -HTTP RPC server supporting calls via uri params, jsonrpc, and jsonrpc over websockets - -# Client Requests - -Suppose we want to expose the rpc function `HelloWorld(name string, num int)`. - -## GET (URI) - -As a GET request, it would have URI encoded parameters, and look like: - -``` -curl 'http://localhost:8008/hello_world?name="my_world"&num=5' -``` - -Note the `'` around the url, which is just so bash doesn't ignore the quotes in `"my_world"`. -This should also work: - -``` -curl http://localhost:8008/hello_world?name=\"my_world\"&num=5 -``` - -A GET request to `/` returns a list of available endpoints. -For those which take arguments, the arguments will be listed in order, with `_` where the actual value should be. - -## POST (JSONRPC) - -As a POST request, we use JSONRPC. For instance, the same request would have this as the body: - -``` -{ - "jsonrpc": "2.0", - "id": "anything", - "method": "hello_world", - "params": { - "name": "my_world", - "num": 5 - } -} -``` - -With the above saved in file `data.json`, we can make the request with - -``` -curl --data @data.json http://localhost:8008 -``` - -## WebSocket (JSONRPC) - -All requests are exposed over websocket in the same form as the POST JSONRPC. -Websocket connections are available at their own endpoint, typically `/websocket`, -though this is configurable when starting the server. - -# Server Definition - -Define some types and routes: - -``` -type ResultStatus struct { - Value string -} - -// Define some routes -var Routes = map[string]*rpcserver.RPCFunc{ - "status": rpcserver.NewRPCFunc(Status, "arg"), -} - -// an rpc function -func Status(v string) (*ResultStatus, error) { - return &ResultStatus{v}, nil -} - -``` - -Now start the server: - -``` -mux := http.NewServeMux() -rpcserver.RegisterRPCFuncs(mux, Routes) -wm := rpcserver.NewWebsocketManager(Routes, nil) -mux.HandleFunc("/websocket", wm.WebsocketHandler) -logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) -go func() { - _, err := rpcserver.StartHTTPServer("0.0.0.0:8008", mux, logger) - if err != nil { - panic(err) - } -}() - -``` - -Note that unix sockets are supported as well (eg. `/path/to/socket` instead of `0.0.0.0:8008`) - -Now see all available endpoints by sending a GET request to `0.0.0.0:8008`. -Each route is available as a GET request, as a JSONRPCv2 POST request, and via JSONRPCv2 over websockets. - - -# Examples - -* [Tendermint](https://github.com/tendermint/tendermint/blob/master/rpc/core/routes.go) -* [tm-monitor](https://github.com/tendermint/tools/blob/master/tm-monitor/rpc.go) - -## CHANGELOG - -### 0.7.0 - -BREAKING CHANGES: - -- removed `Client` empty interface -- `ClientJSONRPC#Call` `params` argument became a map -- rename `ClientURI` -> `URIClient`, `ClientJSONRPC` -> `JSONRPCClient` - -IMPROVEMENTS: - -- added `HTTPClient` interface, which can be used for both `ClientURI` -and `ClientJSONRPC` -- all params are now optional (Golang's default will be used if some param is missing) -- added `Call` method to `WSClient` (see method's doc for details) diff --git a/rpc/lib/circle.yml b/rpc/lib/circle.yml deleted file mode 100644 index 0308a4e7..00000000 --- a/rpc/lib/circle.yml +++ /dev/null @@ -1,21 +0,0 @@ -machine: - environment: - GOPATH: /home/ubuntu/.go_workspace - REPO: $GOPATH/src/github.com/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME - hosts: - circlehost: 127.0.0.1 - localhost: 127.0.0.1 - -checkout: - post: - - rm -rf $REPO - - mkdir -p $HOME/.go_workspace/src/github.com/$CIRCLE_PROJECT_USERNAME - - mv $HOME/$CIRCLE_PROJECT_REPONAME $REPO - -dependencies: - override: - - "cd $REPO && make get_deps" - -test: - override: - - "cd $REPO && make test" From 659768783f3198525a5cca311954da27f17dbc6e Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 4 Oct 2017 17:27:07 -0400 Subject: [PATCH 38/54] blockchain: fixing reactor tests --- blockchain/reactor_test.go | 164 ++++++++++++------------------------- 1 file changed, 52 insertions(+), 112 deletions(-) diff --git a/blockchain/reactor_test.go b/blockchain/reactor_test.go index a13c6aed..492ea7a8 100644 --- a/blockchain/reactor_test.go +++ b/blockchain/reactor_test.go @@ -4,13 +4,15 @@ import ( "bytes" "testing" + wire "github.com/tendermint/go-wire" + cmn "github.com/tendermint/tmlibs/common" + "github.com/tendermint/tmlibs/db" + "github.com/tendermint/tmlibs/log" + cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/p2p" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" - cmn "github.com/tendermint/tmlibs/common" - "github.com/tendermint/tmlibs/db" - "github.com/tendermint/tmlibs/log" ) func newBlockchainReactor(logger log.Logger, maxBlockHeight int) *BlockchainReactor { @@ -23,7 +25,8 @@ func newBlockchainReactor(logger log.Logger, maxBlockHeight int) *BlockchainReac // Get State stateDB := db.NewDB("state", config.DBBackend, config.DBDir()) - state := sm.GetState(stateDB, config.GenesisFile()) + state, _ := sm.GetState(stateDB, config.GenesisFile()) + state.SetLogger(stateLogger) state.Save() @@ -39,21 +42,13 @@ func newBlockchainReactor(logger log.Logger, maxBlockHeight int) *BlockchainReac for blockHeight := 1; blockHeight <= maxBlockHeight; blockHeight++ { firstBlock := makeBlock(blockHeight, state) secondBlock := makeBlock(blockHeight+1, state) - firstParts := firstBlock.MakePartSet(types.DefaultBlockPartSize) + firstParts := firstBlock.MakePartSet(state.Params().BlockGossipParams.BlockPartSizeBytes) blockStore.SaveBlock(firstBlock, firstParts, secondBlock.LastCommit) } return bcReactor } -func newbcrTestPeer(key string) *bcrTestPeer { - return &bcrTestPeer{ - key: key, - kvm: make(map[byte]interface{}), - ch: make(chan *keyValue, 2), - } -} - func TestNoBlockMessageResponse(t *testing.T) { logBuf := new(bytes.Buffer) logger := log.NewTMLogger(logBuf) @@ -68,131 +63,78 @@ func TestNoBlockMessageResponse(t *testing.T) { bcr.AddPeer(peer) chID := byte(0x01) - - tests := [...]struct { + + tests := []struct { height int existent bool }{ - 0: { - height: maxBlockHeight + 2, - existent: false, - }, - 1: { - height: 10, - existent: true, // We should have this - }, - 2: { - height: 1, - existent: true, // We should have this - }, - 3: { - height: 100, - existent: false, - }, + {maxBlockHeight + 2, false}, + {10, true}, + {1, true}, + {100, false}, } - // Currently the repsonses take asynchronous paths so - // the results are skewed. However, we can ensure that - // the (expectedHeight, expectedExistence) all tally up. - heightTally := map[int]bool{} - var results []BlockchainMessage - - msgRequest := byte(0x10) for _, tt := range tests { - reqBlockBytes := []byte{msgRequest, 0x02, 0x00, byte(tt.height)} + reqBlockMsg := &bcBlockRequestMessage{tt.height} + reqBlockBytes := wire.BinaryBytes(struct{ BlockchainMessage }{reqBlockMsg}) bcr.Receive(chID, peer, reqBlockBytes) - heightTally[tt.height] = tt.existent - kv := peer.lastKeyValue() - results = append(results, kv.value.(struct{ BlockchainMessage }).BlockchainMessage) - } + value := peer.lastValue() + msg := value.(struct{ BlockchainMessage }).BlockchainMessage - for i, res := range results { - if bcRM, ok := res.(*bcBlockResponseMessage); ok { - block := bcRM.Block - if block == nil { - t.Errorf("result: #%d: expecting a non-nil block", i) - continue - } - mustExist, foundHeight := heightTally[block.Height] - if !foundHeight { - t.Errorf("result: #%d, missing height %d", i, block.Height) - continue - } - if !mustExist { - t.Errorf("result: #%d mustExist", i) - } else { - delete(heightTally, block.Height) - } - } else if bcNBRSM, ok := res.(*bcNoBlockResponseMessage); ok { - mustExist, foundHeight := heightTally[bcNBRSM.Height] - if !foundHeight { - t.Errorf("result: #%d, missing height %d", i, bcNBRSM.Height) - continue - } - if mustExist { - t.Errorf("result: #%d mustNotExist", i) - } else { - // Passes, remove this height from the tally - delete(heightTally, bcNBRSM.Height) + if tt.existent { + if blockMsg, ok := msg.(*bcBlockResponseMessage); !ok { + t.Fatalf("Expected to receive a block response for height %d", tt.height) + } else if blockMsg.Block.Height != tt.height { + t.Fatalf("Expected response to be for height %d, got %d", tt.height, blockMsg.Block.Height) } } else { - t.Errorf("result: #%d is an unexpected type: %T; data: %#v", i, res, res) + if noBlockMsg, ok := msg.(*bcNoBlockResponseMessage); !ok { + t.Fatalf("Expected to receive a no block response for height %d", tt.height) + } else if noBlockMsg.Height != tt.height { + t.Fatalf("Expected response to be for height %d, got %d", tt.height, noBlockMsg.Height) + } } } - - if len(heightTally) > 0 { - t.Errorf("Untallied heights: %#v\n", heightTally) - } } +//---------------------------------------------- +// utility funcs + func makeTxs(blockNumber int) (txs []types.Tx) { for i := 0; i < 10; i++ { - txs = append(txs, types.Tx([]byte{byte(blockNumber)})) + txs = append(txs, types.Tx([]byte{byte(blockNumber), byte(i)})) } return txs } -const testPartSize = 65536 - func makeBlock(blockNumber int, state *sm.State) *types.Block { prevHash := state.LastBlockID.Hash prevParts := types.PartSetHeader{} valHash := state.Validators.Hash() prevBlockID := types.BlockID{prevHash, prevParts} block, _ := types.MakeBlock(blockNumber, "test_chain", makeTxs(blockNumber), - new(types.Commit), prevBlockID, valHash, state.AppHash, testPartSize) + new(types.Commit), prevBlockID, valHash, state.AppHash, state.Params().BlockGossipParams.BlockPartSizeBytes) return block } // The Test peer type bcrTestPeer struct { + cmn.Service key string - ch chan *keyValue - kvm map[byte]interface{} -} - -type keyValue struct { - key interface{} - value interface{} + ch chan interface{} } var _ p2p.Peer = (*bcrTestPeer)(nil) -func (tp *bcrTestPeer) Key() string { return tp.key } -func (tp *bcrTestPeer) IsOutbound() bool { return false } -func (tp *bcrTestPeer) IsPersistent() bool { return true } -func (tp *bcrTestPeer) IsRunning() bool { return true } +func newbcrTestPeer(key string) *bcrTestPeer { + return &bcrTestPeer{ + Service: cmn.NewBaseService(nil, "bcrTestPeer", nil), + key: key, + ch: make(chan interface{}, 2), + } +} -func (tp *bcrTestPeer) OnStop() {} - -func (tp *bcrTestPeer) OnReset() error { return nil } -func (tp *bcrTestPeer) Reset() (bool, error) { return false, nil } -func (tp *bcrTestPeer) OnStart() error { return nil } -func (tp *bcrTestPeer) SetLogger(l log.Logger) {} - -func (tp *bcrTestPeer) Start() (bool, error) { return true, nil } -func (tp *bcrTestPeer) Stop() bool { return true } -func (tp *bcrTestPeer) String() string { return tp.key } +func (tp *bcrTestPeer) lastValue() interface{} { return <-tp.ch } func (tp *bcrTestPeer) TrySend(chID byte, value interface{}) bool { if _, ok := value.(struct{ BlockchainMessage }).BlockchainMessage.(*bcStatusResponseMessage); ok { @@ -201,18 +143,16 @@ func (tp *bcrTestPeer) TrySend(chID byte, value interface{}) bool { // + bcBlockResponseMessage // + bcNoBlockResponseMessage } else { - tp.ch <- &keyValue{key: chID, value: value} + tp.ch <- value } return true } -func (tp *bcrTestPeer) Send(chID byte, data interface{}) bool { - return tp.TrySend(chID, data) -} - -func (tp *bcrTestPeer) NodeInfo() *p2p.NodeInfo { return nil } -func (tp *bcrTestPeer) Status() p2p.ConnectionStatus { return p2p.ConnectionStatus{} } - -func (tp *bcrTestPeer) Get(s string) interface{} { return s } -func (tp *bcrTestPeer) Set(string, interface{}) {} -func (tp *bcrTestPeer) lastKeyValue() *keyValue { return <-tp.ch } +func (tp *bcrTestPeer) Send(chID byte, data interface{}) bool { return tp.TrySend(chID, data) } +func (tp *bcrTestPeer) NodeInfo() *p2p.NodeInfo { return nil } +func (tp *bcrTestPeer) Status() p2p.ConnectionStatus { return p2p.ConnectionStatus{} } +func (tp *bcrTestPeer) Key() string { return tp.key } +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{}) {} From 91a3cb0f219cc6a9fc811d3362ef4cee0829bcbf Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 4 Oct 2017 23:20:22 -0400 Subject: [PATCH 39/54] makefile: remove megacheck --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index a557dd86..dfb0dc3a 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,6 @@ GOTOOLS = \ github.com/mitchellh/gox \ github.com/tcnksm/ghr \ github.com/Masterminds/glide \ - honnef.co/go/tools/cmd/megacheck PACKAGES=$(shell go list ./... | grep -v '/vendor/') BUILD_TAGS?=tendermint From 49653d3e31e34a5da83e16e9ffdcd95a68acd9be Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 4 Oct 2017 23:44:55 -0400 Subject: [PATCH 40/54] CODEOWNERS file --- .github/CODEOWNERS | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..074c48c9 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,5 @@ +# CODEOWNERS: https://help.github.com/articles/about-codeowners/ + +# Everything goes through Bucky. For now. +* @ebuchman + From 3702cb7e7c01fb7eea2d2ccc089b4e65c92bbd78 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Thu, 5 Oct 2017 11:44:02 +0400 Subject: [PATCH 41/54] restore rpc/lib readme as doc.go (Refs #710) [ci skip] I don't want to lose any documentation. Correct me if I am wrong, but we don't have this docs anywhere else. --- rpc/lib/doc.go | 102 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 rpc/lib/doc.go diff --git a/rpc/lib/doc.go b/rpc/lib/doc.go new file mode 100644 index 00000000..9b13e230 --- /dev/null +++ b/rpc/lib/doc.go @@ -0,0 +1,102 @@ +/* +HTTP RPC server supporting calls via uri params, jsonrpc, and jsonrpc over websockets + +# Client Requests + +Suppose we want to expose the rpc function `HelloWorld(name string, num int)`. + +## GET (URI) + +As a GET request, it would have URI encoded parameters, and look like: + +``` +curl 'http://localhost:8008/hello_world?name="my_world"&num=5' +``` + +Note the `'` around the url, which is just so bash doesn't ignore the quotes in `"my_world"`. +This should also work: + +``` +curl http://localhost:8008/hello_world?name=\"my_world\"&num=5 +``` + +A GET request to `/` returns a list of available endpoints. +For those which take arguments, the arguments will be listed in order, with `_` where the actual value should be. + +## POST (JSONRPC) + +As a POST request, we use JSONRPC. For instance, the same request would have this as the body: + +``` +{ + "jsonrpc": "2.0", + "id": "anything", + "method": "hello_world", + "params": { + "name": "my_world", + "num": 5 + } +} +``` + +With the above saved in file `data.json`, we can make the request with + +``` +curl --data @data.json http://localhost:8008 +``` + +## WebSocket (JSONRPC) + +All requests are exposed over websocket in the same form as the POST JSONRPC. +Websocket connections are available at their own endpoint, typically `/websocket`, +though this is configurable when starting the server. + +# Server Definition + +Define some types and routes: + +``` +type ResultStatus struct { + Value string +} + +// Define some routes +var Routes = map[string]*rpcserver.RPCFunc{ + "status": rpcserver.NewRPCFunc(Status, "arg"), +} + +// an rpc function +func Status(v string) (*ResultStatus, error) { + return &ResultStatus{v}, nil +} + +``` + +Now start the server: + +``` +mux := http.NewServeMux() +rpcserver.RegisterRPCFuncs(mux, Routes) +wm := rpcserver.NewWebsocketManager(Routes, nil) +mux.HandleFunc("/websocket", wm.WebsocketHandler) +logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) +go func() { + _, err := rpcserver.StartHTTPServer("0.0.0.0:8008", mux, logger) + if err != nil { + panic(err) + } +}() + +``` + +Note that unix sockets are supported as well (eg. `/path/to/socket` instead of `0.0.0.0:8008`) + +Now see all available endpoints by sending a GET request to `0.0.0.0:8008`. +Each route is available as a GET request, as a JSONRPCv2 POST request, and via JSONRPCv2 over websockets. + + +# Examples + +* [Tendermint](https://github.com/tendermint/tendermint/blob/master/rpc/core/routes.go) +* [tm-monitor](https://github.com/tendermint/tools/blob/master/tm-monitor/rpc.go) +*/ From a3adac3787b5b52f64170d4e8925da18ffe70db9 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 9 Oct 2017 13:30:52 +0400 Subject: [PATCH 42/54] [rpc] do not try to parse params if they were not provided (Refs #708) --- rpc/lib/server/handlers.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/rpc/lib/server/handlers.go b/rpc/lib/server/handlers.go index 9de6564d..cb666b1e 100644 --- a/rpc/lib/server/handlers.go +++ b/rpc/lib/server/handlers.go @@ -534,9 +534,13 @@ func (wsc *wsConnection) readRoutine() { var args []reflect.Value if rpcFunc.ws { wsCtx := types.WSRPCContext{Request: request, WSRPCConnection: wsc} - args, err = jsonParamsToArgsWS(rpcFunc, request.Params, wsCtx) + if request.Params != nil { + args, err = jsonParamsToArgsWS(rpcFunc, request.Params, wsCtx) + } } else { - args, err = jsonParamsToArgsRPC(rpcFunc, request.Params) + if request.Params != nil { + args, err = jsonParamsToArgsRPC(rpcFunc, request.Params) + } } if err != nil { wsc.WriteRPCResponse(types.RPCInternalError(request.ID, errors.Wrap(err, "Error converting json params to arguments"))) From d6a87d3c437623895853acf59eb8f3a6321fa4b0 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 9 Oct 2017 14:09:26 +0400 Subject: [PATCH 43/54] [rpc] DumpConsensusState: output state as json rather than string Before: ``` { "jsonrpc": "2.0", "id": "", "result": { "round_state": "RoundState{\n H:10 R:0 S:RoundStepNewHeight\n StartTime: 2017-10-09 13:07:24.841134374 +0400 +04\n CommitTime: 2017-10-09 13:07:23.841134374 +0400 +04\n Validators: ValidatorSet{\n Proposer: Validator{EF243CC0E9B88D0161D24D733BDE9003518CEA27 {PubKeyEd25519{2E0B9301334FCDAB193D514022F81BA09BBEC028685C96602BE9DD0BD4F9E202}} VP:10 A:0}\n Validators:\n Validator{EF243CC0E9B88D0161D24D733BDE9003518CEA27 {PubKeyEd25519{2E0B9301334FCDAB193D514022F81BA09BBEC028685C96602BE9DD0BD4F9E202}} VP:10 A:0}\n }\n Proposal: \u003cnil\u003e\n ProposalBlock: nil-PartSet nil-Block\n LockedRound: 0\n LockedBlock: nil-PartSet nil-Block\n Votes: HeightVoteSet{H:10 R:0~0\n VoteSet{H:10 R:0 T:1 +2/3:\u003cnil\u003e BA{1:_} map[]}\n VoteSet{H:10 R:0 T:2 +2/3:\u003cnil\u003e BA{1:_} map[]}\n }\n LastCommit: VoteSet{H:9 R:0 T:2 +2/3:947F67A7B85439AF2CD5DFED376C51AC7BD67AEE:1:365E9983E466 BA{1:X} map[]}\n LastValidators: ValidatorSet{\n Proposer: Validator{EF243CC0E9B88D0161D24D733BDE9003518CEA27 {PubKeyEd25519{2E0B9301334FCDAB193D514022F81BA09BBEC028685C96602BE9DD0BD4F9E202}} VP:10 A:0}\n Validators:\n Validator{EF243CC0E9B88D0161D24D733BDE9003518CEA27 {PubKeyEd25519{2E0B9301334FCDAB193D514022F81BA09BBEC028685C96602BE9DD0BD4F9E202}} VP:10 A:0}\n }\n}", "peer_round_states": [] } } ``` After: ``` { "jsonrpc": "2.0", "id": "", "result": { "round_state": { "Height": 1691, "Round": 0, "Step": 1, "StartTime": "2017-10-09T14:08:09.129491764+04:00", "CommitTime": "2017-10-09T14:08:08.129491764+04:00", "Validators": { "validators": [ { "address": "EF243CC0E9B88D0161D24D733BDE9003518CEA27", "pub_key": { "type": "ed25519", "data": "2E0B9301334FCDAB193D514022F81BA09BBEC028685C96602BE9DD0BD4F9E202" }, "voting_power": 10, "accum": 0 } ], "proposer": { "address": "EF243CC0E9B88D0161D24D733BDE9003518CEA27", "pub_key": { "type": "ed25519", "data": "2E0B9301334FCDAB193D514022F81BA09BBEC028685C96602BE9DD0BD4F9E202" }, "voting_power": 10, "accum": 0 } }, "Proposal": null, "ProposalBlock": null, "ProposalBlockParts": null, "LockedRound": 0, "LockedBlock": null, "LockedBlockParts": null, "Votes": {}, "CommitRound": -1, "LastCommit": {}, "LastValidators": { "validators": [ { "address": "EF243CC0E9B88D0161D24D733BDE9003518CEA27", "pub_key": { "type": "ed25519", "data": "2E0B9301334FCDAB193D514022F81BA09BBEC028685C96602BE9DD0BD4F9E202" }, "voting_power": 10, "accum": 0 } ], "proposer": { "address": "EF243CC0E9B88D0161D24D733BDE9003518CEA27", "pub_key": { "type": "ed25519", "data": "2E0B9301334FCDAB193D514022F81BA09BBEC028685C96602BE9DD0BD4F9E202" }, "voting_power": 10, "accum": 0 } } }, "peer_round_states": { "75EC8F15D244A421202F9725CD4DE509EE50303670310CF7530EF25E2B7C524B": { "Height": 1691, "Round": 0, "Step": 1, "StartTime": "2017-10-09T14:08:08.563251997+04:00", "Proposal": false, "ProposalBlockPartsHeader": { "total": 0, "hash": "" }, "ProposalBlockParts": null, "ProposalPOLRound": -1, "ProposalPOL": null, "Prevotes": null, "Precommits": null, "LastCommitRound": 0, "LastCommit": null, "CatchupCommitRound": -1, "CatchupCommit": null } } } } ``` --- rpc/core/consensus.go | 25 ++++++++++++++++++------- rpc/core/types/responses.go | 7 ++++--- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/rpc/core/consensus.go b/rpc/core/consensus.go index 0429c8d4..54cac12e 100644 --- a/rpc/core/consensus.go +++ b/rpc/core/consensus.go @@ -1,10 +1,13 @@ package core import ( - wire "github.com/tendermint/go-wire" + "encoding/json" + cm "github.com/tendermint/tendermint/consensus" ctypes "github.com/tendermint/tendermint/rpc/core/types" "github.com/tendermint/tendermint/types" + + "github.com/pkg/errors" ) // Get the validator set at the given block height. @@ -82,14 +85,22 @@ func Validators(heightPtr *int) (*ctypes.ResultValidators, error) { // } // ``` func DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) { - roundState := consensusState.GetRoundState() - peerRoundStates := []string{} - for _, peer := range p2pSwitch.Peers().List() { + roundStateBytes, err := json.Marshal(consensusState.GetRoundState()) + if err != nil { + return nil, errors.Wrap(err, "failed to marshal round state") + } + + peerRoundStates := make(map[string]json.RawMessage) + for i, peer := range p2pSwitch.Peers().List() { // TODO: clean this up? peerState := peer.Get(types.PeerStateKey).(*cm.PeerState) peerRoundState := peerState.GetRoundState() - peerRoundStateStr := peer.Key() + ":" + string(wire.JSONBytes(peerRoundState)) - peerRoundStates = append(peerRoundStates, peerRoundStateStr) + peerRoundStateBytes, err := json.Marshal(peerRoundState) + if err != nil { + return nil, errors.Wrapf(err, "failed to marshal peer#%d round state", i) + } + peerRoundStates[peer.Key()] = json.RawMessage(peerRoundStateBytes) } - return &ctypes.ResultDumpConsensusState{roundState.String(), peerRoundStates}, nil + + return &ctypes.ResultDumpConsensusState{json.RawMessage(roundStateBytes), peerRoundStates}, nil } diff --git a/rpc/core/types/responses.go b/rpc/core/types/responses.go index 22c9f9ab..dc9b1a51 100644 --- a/rpc/core/types/responses.go +++ b/rpc/core/types/responses.go @@ -1,10 +1,11 @@ package core_types import ( + "encoding/json" "strings" abci "github.com/tendermint/abci/types" - "github.com/tendermint/go-crypto" + crypto "github.com/tendermint/go-crypto" "github.com/tendermint/go-wire/data" "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/types" @@ -75,8 +76,8 @@ type ResultValidators struct { } type ResultDumpConsensusState struct { - RoundState string `json:"round_state"` - PeerRoundStates []string `json:"peer_round_states"` + RoundState json.RawMessage `json:"round_state"` + PeerRoundStates map[string]json.RawMessage `json:"peer_round_states"` } type ResultBroadcastTx struct { From 4bd4d59af5dec5732299a60ecb6411056bbd0f92 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 10 Oct 2017 01:14:00 +0400 Subject: [PATCH 44/54] update changelog [ci skip] --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e911e073..aa7731b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,22 @@ BUG FIXES: - Graceful handling/recovery for apps that have non-determinism or fail to halt - Graceful handling/recovery for violations of safety, or liveness +## 0.11.1 (October 10, 2017) + +BREAKING CHANGES: + - p2p: removed IPRangeCount* functions + +IMPROVEMENTS: + - blockchain/reactor: respondWithNoResponseMessage for missing height + +BUG FIXES: + - rpc: fixed client WebSocket timeout + - rpc: client now resubscribes on reconnection + - types: fixed out of range error in VoteSet.addVote + +DEPENDENCIES: + - tmlibs: https://github.com/tendermint/tmlibs/blob/master/CHANGELOG.md#032-october-2-2017 + ## 0.11.0 (September 22, 2017) BREAKING: From 99c4e48038d02dc2867a08c85e36da2d6faa2e69 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 10 Oct 2017 01:14:42 +0400 Subject: [PATCH 45/54] return missing package declaration --- rpc/lib/doc.go | 1 + 1 file changed, 1 insertion(+) diff --git a/rpc/lib/doc.go b/rpc/lib/doc.go index 9b13e230..0ea4e5c6 100644 --- a/rpc/lib/doc.go +++ b/rpc/lib/doc.go @@ -100,3 +100,4 @@ Each route is available as a GET request, as a JSONRPCv2 POST request, and via J * [Tendermint](https://github.com/tendermint/tendermint/blob/master/rpc/core/routes.go) * [tm-monitor](https://github.com/tendermint/tools/blob/master/tm-monitor/rpc.go) */ +package rpc From 90a23352672206ff49ec56e89198f90323a41603 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 10 Oct 2017 01:18:33 +0400 Subject: [PATCH 46/54] bump version to 0.11.1 --- version/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version/version.go b/version/version.go index 504aee68..e44a662a 100644 --- a/version/version.go +++ b/version/version.go @@ -6,7 +6,7 @@ const Fix = "0" var ( // The full version string - Version = "0.11.0" + Version = "0.11.1" // GitCommit is set with --ldflags "-X main.gitCommit=$(git rev-parse HEAD)" GitCommit string From dc0e8de9b0671fc93d7479b20c2c9b5ea54c48c6 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 10 Oct 2017 12:39:21 +0400 Subject: [PATCH 47/54] extract some of the consensus types into ./types so they can be used in rpc/core/types/responses.go. ``` So, it seems like we could use the actual structs here, but we don't want to have to import consensus to get them, as then clients are importing too much crap. So probably we should move some types from consensus into consensus/types so we can import. Will these raw messages be identical to: type ResultDumpConsensusState struct { RoundState cstypes.RoundState PeerRoundStates map[string]cstypes.PeerRoundState } ``` https://github.com/tendermint/tendermint/pull/724#discussion_r143598193 --- consensus/common_test.go | 3 +- consensus/reactor.go | 77 ++---- consensus/state.go | 219 ++++-------------- consensus/state_test.go | 31 +-- consensus/{ => types}/height_vote_set.go | 2 +- consensus/{ => types}/height_vote_set_test.go | 7 +- consensus/types/reactor.go | 57 +++++ consensus/types/state.go | 126 ++++++++++ rpc/core/consensus.go | 23 +- rpc/core/pipe.go | 3 +- rpc/core/types/responses.go | 6 +- 11 files changed, 283 insertions(+), 271 deletions(-) rename consensus/{ => types}/height_vote_set.go (99%) rename consensus/{ => types}/height_vote_set_test.go (89%) create mode 100644 consensus/types/reactor.go create mode 100644 consensus/types/state.go diff --git a/consensus/common_test.go b/consensus/common_test.go index e614bf37..33b613a0 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -15,6 +15,7 @@ import ( abci "github.com/tendermint/abci/types" bc "github.com/tendermint/tendermint/blockchain" cfg "github.com/tendermint/tendermint/config" + cstypes "github.com/tendermint/tendermint/consensus/types" mempl "github.com/tendermint/tendermint/mempool" "github.com/tendermint/tendermint/p2p" sm "github.com/tendermint/tendermint/state" @@ -456,7 +457,7 @@ func (m *mockTicker) ScheduleTimeout(ti timeoutInfo) { if m.onlyOnce && m.fired { return } - if ti.Step == RoundStepNewHeight { + if ti.Step == cstypes.RoundStepNewHeight { m.c <- ti m.fired = true } diff --git a/consensus/reactor.go b/consensus/reactor.go index 59cbfea7..48041e2f 100644 --- a/consensus/reactor.go +++ b/consensus/reactor.go @@ -12,6 +12,7 @@ import ( cmn "github.com/tendermint/tmlibs/common" "github.com/tendermint/tmlibs/log" + cstypes "github.com/tendermint/tendermint/consensus/types" "github.com/tendermint/tendermint/p2p" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" @@ -321,7 +322,7 @@ func (conR *ConsensusReactor) FastSync() bool { func (conR *ConsensusReactor) registerEventCallbacks() { types.AddListenerForEvent(conR.evsw, "conR", types.EventStringNewRoundStep(), func(data types.TMEventData) { - rs := data.Unwrap().(types.EventDataRoundState).RoundState.(*RoundState) + rs := data.Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState) conR.broadcastNewRoundStep(rs) }) @@ -344,7 +345,7 @@ func (conR *ConsensusReactor) broadcastProposalHeartbeatMessage(heartbeat types. conR.Switch.Broadcast(StateChannel, struct{ ConsensusMessage }{msg}) } -func (conR *ConsensusReactor) broadcastNewRoundStep(rs *RoundState) { +func (conR *ConsensusReactor) broadcastNewRoundStep(rs *cstypes.RoundState) { nrsMsg, csMsg := makeRoundStepMessages(rs) if nrsMsg != nil { @@ -381,7 +382,7 @@ func (conR *ConsensusReactor) broadcastHasVoteMessage(vote *types.Vote) { */ } -func makeRoundStepMessages(rs *RoundState) (nrsMsg *NewRoundStepMessage, csMsg *CommitStepMessage) { +func makeRoundStepMessages(rs *cstypes.RoundState) (nrsMsg *NewRoundStepMessage, csMsg *CommitStepMessage) { nrsMsg = &NewRoundStepMessage{ Height: rs.Height, Round: rs.Round, @@ -389,7 +390,7 @@ func makeRoundStepMessages(rs *RoundState) (nrsMsg *NewRoundStepMessage, csMsg * SecondsSinceStartTime: int(time.Since(rs.StartTime).Seconds()), LastCommitRound: rs.LastCommit.Round(), } - if rs.Step == RoundStepCommit { + if rs.Step == cstypes.RoundStepCommit { csMsg = &CommitStepMessage{ Height: rs.Height, BlockPartsHeader: rs.ProposalBlockParts.Header(), @@ -491,8 +492,8 @@ OUTER_LOOP: } } -func (conR *ConsensusReactor) gossipDataForCatchup(logger log.Logger, rs *RoundState, - prs *PeerRoundState, ps *PeerState, peer p2p.Peer) { +func (conR *ConsensusReactor) gossipDataForCatchup(logger log.Logger, rs *cstypes.RoundState, + prs *cstypes.PeerRoundState, ps *PeerState, peer p2p.Peer) { if index, ok := prs.ProposalBlockParts.Not().PickRandom(); ok { // Ensure that the peer's PartSetHeader is correct @@ -606,24 +607,24 @@ OUTER_LOOP: } } -func (conR *ConsensusReactor) gossipVotesForHeight(logger log.Logger, rs *RoundState, prs *PeerRoundState, ps *PeerState) bool { +func (conR *ConsensusReactor) gossipVotesForHeight(logger log.Logger, rs *cstypes.RoundState, prs *cstypes.PeerRoundState, ps *PeerState) bool { // If there are lastCommits to send... - if prs.Step == RoundStepNewHeight { + if prs.Step == cstypes.RoundStepNewHeight { if ps.PickSendVote(rs.LastCommit) { logger.Debug("Picked rs.LastCommit to send") return true } } // If there are prevotes to send... - if prs.Step <= RoundStepPrevote && prs.Round != -1 && prs.Round <= rs.Round { + if prs.Step <= cstypes.RoundStepPrevote && 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 <= RoundStepPrecommit && prs.Round != -1 && prs.Round <= rs.Round { + if prs.Step <= cstypes.RoundStepPrecommit && 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 @@ -752,54 +753,6 @@ func (conR *ConsensusReactor) StringIndented(indent string) string { //----------------------------------------------------------------------------- -// PeerRoundState contains the known state of a peer. -// NOTE: Read-only when returned by PeerState.GetRoundState(). -type PeerRoundState struct { - Height int // 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) -} - -//----------------------------------------------------------------------------- - var ( ErrPeerStateHeightRegression = errors.New("Error peer state height regression") ErrPeerStateInvalidStartTime = errors.New("Error peer state invalid startTime") @@ -812,7 +765,7 @@ type PeerState struct { logger log.Logger mtx sync.Mutex - PeerRoundState + cstypes.PeerRoundState } // NewPeerState returns a new PeerState for the given Peer @@ -820,7 +773,7 @@ func NewPeerState(peer p2p.Peer) *PeerState { return &PeerState{ Peer: peer, logger: log.NewNopLogger(), - PeerRoundState: PeerRoundState{ + PeerRoundState: cstypes.PeerRoundState{ Round: -1, ProposalPOLRound: -1, LastCommitRound: -1, @@ -836,7 +789,7 @@ func (ps *PeerState) SetLogger(logger log.Logger) *PeerState { // GetRoundState returns an atomic snapshot of the PeerRoundState. // There's no point in mutating it since it won't change PeerState. -func (ps *PeerState) GetRoundState() *PeerRoundState { +func (ps *PeerState) GetRoundState() *cstypes.PeerRoundState { ps.mtx.Lock() defer ps.mtx.Unlock() @@ -1227,7 +1180,7 @@ func DecodeMessage(bz []byte) (msgType byte, msg ConsensusMessage, err error) { type NewRoundStepMessage struct { Height int Round int - Step RoundStepType + Step cstypes.RoundStepType SecondsSinceStartTime int LastCommitRound int } diff --git a/consensus/state.go b/consensus/state.go index bed40eb1..f0fbad81 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -17,6 +17,7 @@ import ( "github.com/tendermint/tmlibs/log" cfg "github.com/tendermint/tendermint/config" + cstypes "github.com/tendermint/tendermint/consensus/types" "github.com/tendermint/tendermint/proxy" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" @@ -39,124 +40,6 @@ var ( ErrVoteHeightMismatch = errors.New("Error vote height mismatch") ) -//----------------------------------------------------------------------------- -// RoundStepType enum type - -// RoundStepType enumerates the state of the consensus state machine -type RoundStepType uint8 // These must be numeric, ordered. - -const ( - RoundStepNewHeight = RoundStepType(0x01) // Wait til CommitTime + timeoutCommit - RoundStepNewRound = RoundStepType(0x02) // Setup new round and go to RoundStepPropose - RoundStepPropose = RoundStepType(0x03) // Did propose, gossip proposal - RoundStepPrevote = RoundStepType(0x04) // Did prevote, gossip prevotes - RoundStepPrevoteWait = RoundStepType(0x05) // Did receive any +2/3 prevotes, start timeout - RoundStepPrecommit = RoundStepType(0x06) // Did precommit, gossip precommits - RoundStepPrecommitWait = RoundStepType(0x07) // Did receive any +2/3 precommits, start timeout - RoundStepCommit = RoundStepType(0x08) // Entered commit state machine - // NOTE: RoundStepNewHeight acts as RoundStepCommitWait. -) - -// String returns a string -func (rs RoundStepType) String() string { - switch rs { - case RoundStepNewHeight: - return "RoundStepNewHeight" - case RoundStepNewRound: - return "RoundStepNewRound" - case RoundStepPropose: - return "RoundStepPropose" - case RoundStepPrevote: - return "RoundStepPrevote" - case RoundStepPrevoteWait: - return "RoundStepPrevoteWait" - case RoundStepPrecommit: - return "RoundStepPrecommit" - case RoundStepPrecommitWait: - return "RoundStepPrecommitWait" - case RoundStepCommit: - return "RoundStepCommit" - default: - return "RoundStepUnknown" // Cannot panic. - } -} - -//----------------------------------------------------------------------------- - -// RoundState defines the internal consensus state. -// It is Immutable when returned from ConsensusState.GetRoundState() -// TODO: Actually, only the top pointer is copied, -// so access to field pointers is still racey -type RoundState struct { - Height int // 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 - Votes *HeightVoteSet - CommitRound int // - LastCommit *types.VoteSet // Last precommits at Height-1 - LastValidators *types.ValidatorSet -} - -// RoundStateEvent returns the H/R/S of the RoundState as an event. -func (rs *RoundState) RoundStateEvent() types.EventDataRoundState { - edrs := types.EventDataRoundState{ - Height: rs.Height, - Round: rs.Round, - Step: rs.Step.String(), - RoundState: rs, - } - return edrs -} - -// String returns a string -func (rs *RoundState) String() string { - return rs.StringIndented("") -} - -// StringIndented returns a string -func (rs *RoundState) StringIndented(indent string) string { - return fmt.Sprintf(`RoundState{ -%s H:%v R:%v S:%v -%s StartTime: %v -%s CommitTime: %v -%s Validators: %v -%s Proposal: %v -%s ProposalBlock: %v %v -%s LockedRound: %v -%s LockedBlock: %v %v -%s Votes: %v -%s LastCommit: %v -%s LastValidators: %v -%s}`, - indent, rs.Height, rs.Round, rs.Step, - indent, rs.StartTime, - indent, rs.CommitTime, - 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.Votes.StringIndented(indent+" "), - indent, rs.LastCommit.StringShort(), - indent, rs.LastValidators.StringIndented(indent+" "), - indent) -} - -// StringShort returns a string -func (rs *RoundState) StringShort() string { - return fmt.Sprintf(`RoundState{H:%v R:%v S:%v ST:%v}`, - rs.Height, rs.Round, rs.Step, rs.StartTime) -} - //----------------------------------------------------------------------------- var ( @@ -171,10 +54,10 @@ type msgInfo struct { // internally generated messages which may update the state type timeoutInfo struct { - Duration time.Duration `json:"duration"` - Height int `json:"height"` - Round int `json:"round"` - Step RoundStepType `json:"step"` + Duration time.Duration `json:"duration"` + Height int `json:"height"` + Round int `json:"round"` + Step cstypes.RoundStepType `json:"step"` } func (ti *timeoutInfo) String() string { @@ -199,7 +82,7 @@ type ConsensusState struct { // internal state mtx sync.Mutex - RoundState + cstypes.RoundState state *sm.State // State until height-1. // state changes may be triggered by msgs from peers, @@ -282,13 +165,13 @@ func (cs *ConsensusState) GetState() *sm.State { } // GetRoundState returns a copy of the internal consensus state. -func (cs *ConsensusState) GetRoundState() *RoundState { +func (cs *ConsensusState) GetRoundState() *cstypes.RoundState { cs.mtx.Lock() defer cs.mtx.Unlock() return cs.getRoundState() } -func (cs *ConsensusState) getRoundState() *RoundState { +func (cs *ConsensusState) getRoundState() *cstypes.RoundState { rs := cs.RoundState // copy return &rs } @@ -469,20 +352,20 @@ func (cs *ConsensusState) updateHeight(height int) { cs.Height = height } -func (cs *ConsensusState) updateRoundStep(round int, step RoundStepType) { +func (cs *ConsensusState) updateRoundStep(round int, step cstypes.RoundStepType) { cs.Round = round cs.Step = step } // enterNewRound(height, 0) at cs.StartTime. -func (cs *ConsensusState) scheduleRound0(rs *RoundState) { +func (cs *ConsensusState) scheduleRound0(rs *cstypes.RoundState) { //cs.Logger.Info("scheduleRound0", "now", time.Now(), "startTime", cs.StartTime) sleepDuration := rs.StartTime.Sub(time.Now()) - cs.scheduleTimeout(sleepDuration, rs.Height, 0, RoundStepNewHeight) + cs.scheduleTimeout(sleepDuration, rs.Height, 0, cstypes.RoundStepNewHeight) } // Attempt to schedule a timeout (by sending timeoutInfo on the tickChan) -func (cs *ConsensusState) scheduleTimeout(duration time.Duration, height, round int, step RoundStepType) { +func (cs *ConsensusState) scheduleTimeout(duration time.Duration, height, round int, step cstypes.RoundStepType) { cs.timeoutTicker.ScheduleTimeout(timeoutInfo{duration, height, round, step}) } @@ -524,7 +407,7 @@ func (cs *ConsensusState) reconstructLastCommit(state *sm.State) { } // Updates ConsensusState and increments height to match that of state. -// The round becomes 0 and cs.Step becomes RoundStepNewHeight. +// The round becomes 0 and cs.Step becomes cstypes.RoundStepNewHeight. func (cs *ConsensusState) updateToState(state *sm.State) { if cs.CommitRound > -1 && 0 < cs.Height && cs.Height != state.LastBlockHeight { cmn.PanicSanity(cmn.Fmt("updateToState() expected state height of %v but found %v", @@ -560,7 +443,7 @@ func (cs *ConsensusState) updateToState(state *sm.State) { // RoundState fields cs.updateHeight(height) - cs.updateRoundStep(0, RoundStepNewHeight) + cs.updateRoundStep(0, cstypes.RoundStepNewHeight) if cs.CommitTime.IsZero() { // "Now" makes it easier to sync up dev nodes. // We add timeoutCommit to allow transactions @@ -578,7 +461,7 @@ func (cs *ConsensusState) updateToState(state *sm.State) { cs.LockedRound = 0 cs.LockedBlock = nil cs.LockedBlockParts = nil - cs.Votes = NewHeightVoteSet(state.ChainID, height, validators) + cs.Votes = cstypes.NewHeightVoteSet(state.ChainID, height, validators) cs.CommitRound = -1 cs.LastCommit = lastPrecommits cs.LastValidators = state.LastValidators @@ -699,7 +582,7 @@ func (cs *ConsensusState) handleMsg(mi msgInfo) { } } -func (cs *ConsensusState) handleTimeout(ti timeoutInfo, rs RoundState) { +func (cs *ConsensusState) handleTimeout(ti timeoutInfo, rs cstypes.RoundState) { cs.Logger.Debug("Received tock", "timeout", ti.Duration, "height", ti.Height, "round", ti.Round, "step", ti.Step) // timeouts must be for current height, round, step @@ -713,19 +596,19 @@ func (cs *ConsensusState) handleTimeout(ti timeoutInfo, rs RoundState) { defer cs.mtx.Unlock() switch ti.Step { - case RoundStepNewHeight: + case cstypes.RoundStepNewHeight: // NewRound event fired from enterNewRound. // XXX: should we fire timeout here (for timeout commit)? cs.enterNewRound(ti.Height, 0) - case RoundStepNewRound: + case cstypes.RoundStepNewRound: cs.enterPropose(ti.Height, 0) - case RoundStepPropose: + case cstypes.RoundStepPropose: types.FireEventTimeoutPropose(cs.evsw, cs.RoundStateEvent()) cs.enterPrevote(ti.Height, ti.Round) - case RoundStepPrevoteWait: + case cstypes.RoundStepPrevoteWait: types.FireEventTimeoutWait(cs.evsw, cs.RoundStateEvent()) cs.enterPrecommit(ti.Height, ti.Round) - case RoundStepPrecommitWait: + case cstypes.RoundStepPrecommitWait: types.FireEventTimeoutWait(cs.evsw, cs.RoundStateEvent()) cs.enterNewRound(ti.Height, ti.Round+1) default: @@ -752,7 +635,7 @@ func (cs *ConsensusState) handleTxsAvailable(height int) { // 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 int, round int) { - if cs.Height != height || round < cs.Round || (cs.Round == round && cs.Step != RoundStepNewHeight) { + 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)) return } @@ -773,7 +656,7 @@ func (cs *ConsensusState) enterNewRound(height int, round int) { // Setup new round // we don't fire newStep for this step, // but we fire an event, so update the round step first - cs.updateRoundStep(round, RoundStepNewRound) + cs.updateRoundStep(round, cstypes.RoundStepNewRound) cs.Validators = validators if round == 0 { // We've already reset these upon new height, @@ -794,7 +677,7 @@ func (cs *ConsensusState) enterNewRound(height int, round int) { waitForTxs := cs.config.WaitForTxs() && round == 0 && !cs.needProofBlock(height) if waitForTxs { if cs.config.CreateEmptyBlocksInterval > 0 { - cs.scheduleTimeout(cs.config.EmptyBlocksInterval(), height, round, RoundStepNewRound) + cs.scheduleTimeout(cs.config.EmptyBlocksInterval(), height, round, cstypes.RoundStepNewRound) } go cs.proposalHeartbeat(height, round) } else { @@ -827,7 +710,7 @@ func (cs *ConsensusState) proposalHeartbeat(height, round int) { for { rs := cs.GetRoundState() // if we've already moved on, no need to send more heartbeats - if rs.Step > RoundStepNewRound || rs.Round > round || rs.Height > height { + if rs.Step > cstypes.RoundStepNewRound || rs.Round > round || rs.Height > height { return } heartbeat := &types.Heartbeat{ @@ -849,7 +732,7 @@ func (cs *ConsensusState) proposalHeartbeat(height, 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 int, round int) { - if cs.Height != height || round < cs.Round || (cs.Round == round && RoundStepPropose <= cs.Step) { + 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)) return } @@ -857,7 +740,7 @@ func (cs *ConsensusState) enterPropose(height int, round int) { defer func() { // Done enterPropose: - cs.updateRoundStep(round, RoundStepPropose) + cs.updateRoundStep(round, cstypes.RoundStepPropose) cs.newStep() // If we have the whole proposal + POL, then goto Prevote now. @@ -869,7 +752,7 @@ func (cs *ConsensusState) enterPropose(height int, round int) { }() // If we don't get the proposal and all block parts quick enough, enterPrevote - cs.scheduleTimeout(cs.config.Propose(round), height, round, RoundStepPropose) + cs.scheduleTimeout(cs.config.Propose(round), height, round, cstypes.RoundStepPropose) // Nothing more to do if we're not a validator if cs.privValidator == nil { @@ -986,14 +869,14 @@ func (cs *ConsensusState) createProposalBlock() (block *types.Block, blockParts // Prevote for LockedBlock if we're locked, or ProposalBlock if valid. // Otherwise vote nil. func (cs *ConsensusState) enterPrevote(height int, round int) { - if cs.Height != height || round < cs.Round || (cs.Round == round && RoundStepPrevote <= cs.Step) { + if cs.Height != height || round < cs.Round || (cs.Round == round && cstypes.RoundStepPrevote <= cs.Step) { cs.Logger.Debug(cmn.Fmt("enterPrevote(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) return } defer func() { // Done enterPrevote: - cs.updateRoundStep(round, RoundStepPrevote) + cs.updateRoundStep(round, cstypes.RoundStepPrevote) cs.newStep() }() @@ -1048,7 +931,7 @@ func (cs *ConsensusState) defaultDoPrevote(height int, round int) { // Enter: any +2/3 prevotes at next round. func (cs *ConsensusState) enterPrevoteWait(height int, round int) { - if cs.Height != height || round < cs.Round || (cs.Round == round && RoundStepPrevoteWait <= cs.Step) { + 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)) return } @@ -1059,12 +942,12 @@ func (cs *ConsensusState) enterPrevoteWait(height int, round int) { defer func() { // Done enterPrevoteWait: - cs.updateRoundStep(round, RoundStepPrevoteWait) + cs.updateRoundStep(round, cstypes.RoundStepPrevoteWait) cs.newStep() }() // Wait for some more prevotes; enterPrecommit - cs.scheduleTimeout(cs.config.Prevote(round), height, round, RoundStepPrevoteWait) + cs.scheduleTimeout(cs.config.Prevote(round), height, round, cstypes.RoundStepPrevoteWait) } // Enter: `timeoutPrevote` after any +2/3 prevotes. @@ -1074,7 +957,7 @@ func (cs *ConsensusState) enterPrevoteWait(height int, 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 int, round int) { - if cs.Height != height || round < cs.Round || (cs.Round == round && RoundStepPrecommit <= cs.Step) { + 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)) return } @@ -1083,7 +966,7 @@ func (cs *ConsensusState) enterPrecommit(height int, round int) { defer func() { // Done enterPrecommit: - cs.updateRoundStep(round, RoundStepPrecommit) + cs.updateRoundStep(round, cstypes.RoundStepPrecommit) cs.newStep() }() @@ -1167,7 +1050,7 @@ func (cs *ConsensusState) enterPrecommit(height int, round int) { // Enter: any +2/3 precommits for next round. func (cs *ConsensusState) enterPrecommitWait(height int, round int) { - if cs.Height != height || round < cs.Round || (cs.Round == round && RoundStepPrecommitWait <= cs.Step) { + 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)) return } @@ -1178,18 +1061,18 @@ func (cs *ConsensusState) enterPrecommitWait(height int, round int) { defer func() { // Done enterPrecommitWait: - cs.updateRoundStep(round, RoundStepPrecommitWait) + cs.updateRoundStep(round, cstypes.RoundStepPrecommitWait) cs.newStep() }() // Wait for some more precommits; enterNewRound - cs.scheduleTimeout(cs.config.Precommit(round), height, round, RoundStepPrecommitWait) + cs.scheduleTimeout(cs.config.Precommit(round), height, round, cstypes.RoundStepPrecommitWait) } // Enter: +2/3 precommits for block func (cs *ConsensusState) enterCommit(height int, commitRound int) { - if cs.Height != height || RoundStepCommit <= cs.Step { + 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)) return } @@ -1198,7 +1081,7 @@ func (cs *ConsensusState) enterCommit(height int, commitRound int) { defer func() { // Done enterCommit: // keep cs.Round the same, commitRound points to the right Precommits set. - cs.updateRoundStep(cs.Round, RoundStepCommit) + cs.updateRoundStep(cs.Round, cstypes.RoundStepCommit) cs.CommitRound = commitRound cs.CommitTime = time.Now() cs.newStep() @@ -1255,9 +1138,9 @@ func (cs *ConsensusState) tryFinalizeCommit(height int) { cs.finalizeCommit(height) } -// Increment height and goto RoundStepNewHeight +// Increment height and goto cstypes.RoundStepNewHeight func (cs *ConsensusState) finalizeCommit(height int) { - if cs.Height != height || cs.Step != RoundStepCommit { + if cs.Height != height || cs.Step != cstypes.RoundStepCommit { cs.Logger.Debug(cmn.Fmt("finalizeCommit(%v): Invalid args. Current step: %v/%v/%v", height, cs.Height, cs.Round, cs.Step)) return } @@ -1351,7 +1234,7 @@ func (cs *ConsensusState) finalizeCommit(height int) { // By here, // * cs.Height has been increment to height+1 - // * cs.Step is now RoundStepNewHeight + // * cs.Step is now cstypes.RoundStepNewHeight // * cs.StartTime is set to when we will start round0. } @@ -1369,8 +1252,8 @@ func (cs *ConsensusState) defaultSetProposal(proposal *types.Proposal) error { return nil } - // We don't care about the proposal if we're already in RoundStepCommit. - if RoundStepCommit <= cs.Step { + // We don't care about the proposal if we're already in cstypes.RoundStepCommit. + if cstypes.RoundStepCommit <= cs.Step { return nil } @@ -1415,10 +1298,10 @@ func (cs *ConsensusState) addProposalBlockPart(height int, part *types.Part, ver cs.state.Params().BlockSizeParams.MaxBytes, &n, &err).(*types.Block) // 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()) - if cs.Step == RoundStepPropose && cs.isProposalComplete() { + if cs.Step == cstypes.RoundStepPropose && cs.isProposalComplete() { // Move onto the next step cs.enterPrevote(height, cs.Round) - } else if cs.Step == RoundStepCommit { + } else if cs.Step == cstypes.RoundStepCommit { // If we're waiting on the proposal block... cs.tryFinalizeCommit(height) } @@ -1463,7 +1346,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerKey string) (added bool, // A precommit for the previous height? // These come in while we wait timeoutCommit if vote.Height+1 == cs.Height { - if !(cs.Step == RoundStepNewHeight && vote.Type == types.VoteTypePrecommit) { + if !(cs.Step == cstypes.RoundStepNewHeight && vote.Type == types.VoteTypePrecommit) { // TODO: give the reason .. // fmt.Errorf("tryAddVote: Wrong height, not a LastCommit straggler commit.") return added, ErrVoteHeightMismatch @@ -1476,7 +1359,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerKey string) (added bool, // if we can skip timeoutCommit and have all the votes now, if cs.config.SkipTimeoutCommit && cs.LastCommit.HasAll() { // go straight to new round (skip timeout commit) - // cs.scheduleTimeout(time.Duration(0), cs.Height, 0, RoundStepNewHeight) + // cs.scheduleTimeout(time.Duration(0), cs.Height, 0, cstypes.RoundStepNewHeight) cs.enterNewRound(cs.Height, 0) } } @@ -1540,7 +1423,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerKey string) (added bool, if cs.config.SkipTimeoutCommit && precommits.HasAll() { // if we have all the votes now, // go straight to new round (skip timeout commit) - // cs.scheduleTimeout(time.Duration(0), cs.Height, 0, RoundStepNewHeight) + // cs.scheduleTimeout(time.Duration(0), cs.Height, 0, cstypes.RoundStepNewHeight) cs.enterNewRound(cs.Height, 0) } @@ -1601,7 +1484,7 @@ func (cs *ConsensusState) signAddVote(type_ byte, hash []byte, header types.Part //--------------------------------------------------------- -func CompareHRS(h1, r1 int, s1 RoundStepType, h2, r2 int, s2 RoundStepType) int { +func CompareHRS(h1, r1 int, s1 cstypes.RoundStepType, h2, r2 int, s2 cstypes.RoundStepType) int { if h1 < h2 { return -1 } else if h1 > h2 { diff --git a/consensus/state_test.go b/consensus/state_test.go index 246fd879..69b6d53c 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -6,6 +6,7 @@ import ( "testing" "time" + cstypes "github.com/tendermint/tendermint/consensus/types" "github.com/tendermint/tendermint/types" cmn "github.com/tendermint/tmlibs/common" ) @@ -247,7 +248,7 @@ func TestFullRound1(t *testing.T) { // grab proposal re := <-propCh - propBlockHash := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState).ProposalBlock.Hash() + propBlockHash := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState).ProposalBlock.Hash() <-voteCh // wait for prevote // NOTE: voteChan cap of 0 ensures we can complete this @@ -344,7 +345,7 @@ func TestLockNoPOL(t *testing.T) { cs1.startRoutines(0) re := <-proposalCh - rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState) + rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState) theBlockHash := rs.ProposalBlock.Hash() <-voteCh // prevote @@ -384,7 +385,7 @@ func TestLockNoPOL(t *testing.T) { // now we're on a new round and not the proposer, so wait for timeout re = <-timeoutProposeCh - rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState) + rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState) if rs.ProposalBlock != nil { panic("Expected proposal block to be nil") @@ -428,7 +429,7 @@ func TestLockNoPOL(t *testing.T) { incrementRound(vs2) re = <-proposalCh - rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState) + rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState) // now we're on a new round and are the proposer if !bytes.Equal(rs.ProposalBlock.Hash(), rs.LockedBlock.Hash()) { @@ -515,7 +516,7 @@ func TestLockPOLRelock(t *testing.T) { <-newRoundCh re := <-proposalCh - rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState) + rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState) theBlockHash := rs.ProposalBlock.Hash() <-voteCh // prevote @@ -591,7 +592,7 @@ func TestLockPOLRelock(t *testing.T) { be := <-newBlockCh b := be.(types.TMEventData).Unwrap().(types.EventDataNewBlockHeader) re = <-newRoundCh - rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState) + rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState) if rs.Height != 2 { panic("Expected height to increment") } @@ -627,7 +628,7 @@ func TestLockPOLUnlock(t *testing.T) { startTestRound(cs1, cs1.Height, 0) <-newRoundCh re := <-proposalCh - rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState) + rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState) theBlockHash := rs.ProposalBlock.Hash() <-voteCh // prevote @@ -653,7 +654,7 @@ func TestLockPOLUnlock(t *testing.T) { // timeout to new round re = <-timeoutWaitCh - rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState) + rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState) lockedBlockHash := rs.LockedBlock.Hash() //XXX: this isnt guaranteed to get there before the timeoutPropose ... @@ -713,7 +714,7 @@ func TestLockPOLSafety1(t *testing.T) { startTestRound(cs1, cs1.Height, 0) <-newRoundCh re := <-proposalCh - rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState) + rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState) propBlock := rs.ProposalBlock <-voteCh // prevote @@ -761,7 +762,7 @@ func TestLockPOLSafety1(t *testing.T) { re = <-proposalCh } - rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState) + rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState) if rs.LockedBlock != nil { panic("we should not be locked!") @@ -848,7 +849,7 @@ func TestLockPOLSafety2(t *testing.T) { incrementRound(vs2, vs3, vs4) - cs1.updateRoundStep(0, RoundStepPrecommitWait) + cs1.updateRoundStep(0, cstypes.RoundStepPrecommitWait) t.Log("### ONTO Round 1") // jump in at round 1 @@ -929,7 +930,7 @@ func TestSlashingPrevotes(t *testing.T) { re := <-proposalCh <-voteCh // prevote - rs := re.(types.EventDataRoundState).RoundState.(*RoundState) + rs := re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState) // we should now be stuck in limbo forever, waiting for more prevotes // add one for a different block should cause us to go into prevote wait @@ -1009,7 +1010,7 @@ func TestHalt1(t *testing.T) { startTestRound(cs1, cs1.Height, 0) <-newRoundCh re := <-proposalCh - rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState) + rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState) propBlock := rs.ProposalBlock propBlockParts := propBlock.MakePartSet(partSize) @@ -1032,7 +1033,7 @@ func TestHalt1(t *testing.T) { // timeout to new round <-timeoutWaitCh re = <-newRoundCh - rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState) + rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState) t.Log("### ONTO ROUND 1") /*Round2 @@ -1050,7 +1051,7 @@ func TestHalt1(t *testing.T) { // receiving that precommit should take us straight to commit <-newBlockCh re = <-newRoundCh - rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState) + rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState) if rs.Height != 2 { panic("expected height to increment") diff --git a/consensus/height_vote_set.go b/consensus/types/height_vote_set.go similarity index 99% rename from consensus/height_vote_set.go rename to consensus/types/height_vote_set.go index 019b2642..18c1c78a 100644 --- a/consensus/height_vote_set.go +++ b/consensus/types/height_vote_set.go @@ -1,4 +1,4 @@ -package consensus +package types import ( "strings" diff --git a/consensus/height_vote_set_test.go b/consensus/types/height_vote_set_test.go similarity index 89% rename from consensus/height_vote_set_test.go rename to consensus/types/height_vote_set_test.go index 30eab5ae..d5797368 100644 --- a/consensus/height_vote_set_test.go +++ b/consensus/types/height_vote_set_test.go @@ -1,14 +1,17 @@ -package consensus +package types import ( "testing" + cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/types" cmn "github.com/tendermint/tmlibs/common" ) +var config *cfg.Config // NOTE: must be reset for each _test.go file + func init() { - config = ResetConfig("consensus_height_vote_set_test") + config = cfg.ResetTestRoot("consensus_height_vote_set_test") } func TestPeerCatchupRounds(t *testing.T) { diff --git a/consensus/types/reactor.go b/consensus/types/reactor.go new file mode 100644 index 00000000..2306ee38 --- /dev/null +++ b/consensus/types/reactor.go @@ -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 int // 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) +} diff --git a/consensus/types/state.go b/consensus/types/state.go new file mode 100644 index 00000000..0e6b1577 --- /dev/null +++ b/consensus/types/state.go @@ -0,0 +1,126 @@ +package types + +import ( + "fmt" + "time" + + "github.com/tendermint/tendermint/types" +) + +//----------------------------------------------------------------------------- +// RoundStepType enum type + +// RoundStepType enumerates the state of the consensus state machine +type RoundStepType uint8 // These must be numeric, ordered. + +const ( + RoundStepNewHeight = RoundStepType(0x01) // Wait til CommitTime + timeoutCommit + RoundStepNewRound = RoundStepType(0x02) // Setup new round and go to RoundStepPropose + RoundStepPropose = RoundStepType(0x03) // Did propose, gossip proposal + RoundStepPrevote = RoundStepType(0x04) // Did prevote, gossip prevotes + RoundStepPrevoteWait = RoundStepType(0x05) // Did receive any +2/3 prevotes, start timeout + RoundStepPrecommit = RoundStepType(0x06) // Did precommit, gossip precommits + RoundStepPrecommitWait = RoundStepType(0x07) // Did receive any +2/3 precommits, start timeout + RoundStepCommit = RoundStepType(0x08) // Entered commit state machine + // NOTE: RoundStepNewHeight acts as RoundStepCommitWait. +) + +// String returns a string +func (rs RoundStepType) String() string { + switch rs { + case RoundStepNewHeight: + return "RoundStepNewHeight" + case RoundStepNewRound: + return "RoundStepNewRound" + case RoundStepPropose: + return "RoundStepPropose" + case RoundStepPrevote: + return "RoundStepPrevote" + case RoundStepPrevoteWait: + return "RoundStepPrevoteWait" + case RoundStepPrecommit: + return "RoundStepPrecommit" + case RoundStepPrecommitWait: + return "RoundStepPrecommitWait" + case RoundStepCommit: + return "RoundStepCommit" + default: + return "RoundStepUnknown" // Cannot panic. + } +} + +//----------------------------------------------------------------------------- + +// RoundState defines the internal consensus state. +// It is Immutable when returned from ConsensusState.GetRoundState() +// TODO: Actually, only the top pointer is copied, +// so access to field pointers is still racey +type RoundState struct { + Height int // 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 + Votes *HeightVoteSet + CommitRound int // + LastCommit *types.VoteSet // Last precommits at Height-1 + LastValidators *types.ValidatorSet +} + +// RoundStateEvent returns the H/R/S of the RoundState as an event. +func (rs *RoundState) RoundStateEvent() types.EventDataRoundState { + edrs := types.EventDataRoundState{ + Height: rs.Height, + Round: rs.Round, + Step: rs.Step.String(), + RoundState: rs, + } + return edrs +} + +// String returns a string +func (rs *RoundState) String() string { + return rs.StringIndented("") +} + +// StringIndented returns a string +func (rs *RoundState) StringIndented(indent string) string { + return fmt.Sprintf(`RoundState{ +%s H:%v R:%v S:%v +%s StartTime: %v +%s CommitTime: %v +%s Validators: %v +%s Proposal: %v +%s ProposalBlock: %v %v +%s LockedRound: %v +%s LockedBlock: %v %v +%s Votes: %v +%s LastCommit: %v +%s LastValidators: %v +%s}`, + indent, rs.Height, rs.Round, rs.Step, + indent, rs.StartTime, + indent, rs.CommitTime, + 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.Votes.StringIndented(indent+" "), + indent, rs.LastCommit.StringShort(), + indent, rs.LastValidators.StringIndented(indent+" "), + indent) +} + +// StringShort returns a string +func (rs *RoundState) StringShort() string { + return fmt.Sprintf(`RoundState{H:%v R:%v S:%v ST:%v}`, + rs.Height, rs.Round, rs.Step, rs.StartTime) +} diff --git a/rpc/core/consensus.go b/rpc/core/consensus.go index 54cac12e..1fe84f9a 100644 --- a/rpc/core/consensus.go +++ b/rpc/core/consensus.go @@ -1,13 +1,10 @@ package core import ( - "encoding/json" - cm "github.com/tendermint/tendermint/consensus" + cstypes "github.com/tendermint/tendermint/consensus/types" ctypes "github.com/tendermint/tendermint/rpc/core/types" "github.com/tendermint/tendermint/types" - - "github.com/pkg/errors" ) // Get the validator set at the given block height. @@ -85,22 +82,12 @@ func Validators(heightPtr *int) (*ctypes.ResultValidators, error) { // } // ``` func DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) { - roundStateBytes, err := json.Marshal(consensusState.GetRoundState()) - if err != nil { - return nil, errors.Wrap(err, "failed to marshal round state") - } - - peerRoundStates := make(map[string]json.RawMessage) - for i, peer := range p2pSwitch.Peers().List() { + peerRoundStates := make(map[string]*cstypes.PeerRoundState) + for _, peer := range p2pSwitch.Peers().List() { // TODO: clean this up? peerState := peer.Get(types.PeerStateKey).(*cm.PeerState) peerRoundState := peerState.GetRoundState() - peerRoundStateBytes, err := json.Marshal(peerRoundState) - if err != nil { - return nil, errors.Wrapf(err, "failed to marshal peer#%d round state", i) - } - peerRoundStates[peer.Key()] = json.RawMessage(peerRoundStateBytes) + peerRoundStates[peer.Key()] = peerRoundState } - - return &ctypes.ResultDumpConsensusState{json.RawMessage(roundStateBytes), peerRoundStates}, nil + return &ctypes.ResultDumpConsensusState{consensusState.GetRoundState(), peerRoundStates}, nil } diff --git a/rpc/core/pipe.go b/rpc/core/pipe.go index 0602689b..20141cb9 100644 --- a/rpc/core/pipe.go +++ b/rpc/core/pipe.go @@ -3,6 +3,7 @@ package core import ( crypto "github.com/tendermint/go-crypto" "github.com/tendermint/tendermint/consensus" + cstypes "github.com/tendermint/tendermint/consensus/types" p2p "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/proxy" sm "github.com/tendermint/tendermint/state" @@ -17,7 +18,7 @@ import ( type Consensus interface { GetState() *sm.State GetValidators() (int, []*types.Validator) - GetRoundState() *consensus.RoundState + GetRoundState() *cstypes.RoundState } type P2P interface { diff --git a/rpc/core/types/responses.go b/rpc/core/types/responses.go index dc9b1a51..a5ed6f5a 100644 --- a/rpc/core/types/responses.go +++ b/rpc/core/types/responses.go @@ -1,12 +1,12 @@ package core_types import ( - "encoding/json" "strings" abci "github.com/tendermint/abci/types" crypto "github.com/tendermint/go-crypto" "github.com/tendermint/go-wire/data" + cstypes "github.com/tendermint/tendermint/consensus/types" "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/types" ) @@ -76,8 +76,8 @@ type ResultValidators struct { } type ResultDumpConsensusState struct { - RoundState json.RawMessage `json:"round_state"` - PeerRoundStates map[string]json.RawMessage `json:"peer_round_states"` + RoundState *cstypes.RoundState `json:"round_state"` + PeerRoundStates map[string]*cstypes.PeerRoundState `json:"peer_round_states"` } type ResultBroadcastTx struct { From 13b9de677836b3881adbf004377a20098b69e87e Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 10 Oct 2017 12:48:36 +0400 Subject: [PATCH 48/54] return missing package declaration --- rpc/lib/doc.go | 1 + 1 file changed, 1 insertion(+) diff --git a/rpc/lib/doc.go b/rpc/lib/doc.go index 9b13e230..0ea4e5c6 100644 --- a/rpc/lib/doc.go +++ b/rpc/lib/doc.go @@ -100,3 +100,4 @@ Each route is available as a GET request, as a JSONRPCv2 POST request, and via J * [Tendermint](https://github.com/tendermint/tendermint/blob/master/rpc/core/routes.go) * [tm-monitor](https://github.com/tendermint/tools/blob/master/tm-monitor/rpc.go) */ +package rpc From 5c331d82768a2b675011db6d423a4159dbf532aa Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 10 Oct 2017 13:01:25 +0400 Subject: [PATCH 49/54] log a notification to help debug user issues --- rpc/lib/server/handlers.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rpc/lib/server/handlers.go b/rpc/lib/server/handlers.go index cb666b1e..d8ba7dba 100644 --- a/rpc/lib/server/handlers.go +++ b/rpc/lib/server/handlers.go @@ -116,6 +116,7 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han // A Notification is a Request object without an "id" member. // The Server MUST NOT reply to a Notification, including those that are within a batch request. if request.ID == "" { + logger.Debug("HTTPJSONRPC received a notification, skipping... (please send a non-empty ID if you want to call a method)") return } if len(r.URL.Path) > 1 { @@ -521,6 +522,7 @@ func (wsc *wsConnection) readRoutine() { // A Notification is a Request object without an "id" member. // The Server MUST NOT reply to a Notification, including those that are within a batch request. if request.ID == "" { + wsc.Logger.Debug("WSJSONRPC received a notification, skipping... (please send a non-empty ID if you want to call a method)") continue } From d935a4f0a80cc8acb2a067d7644ad807b1e0fa4d Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 10 Oct 2017 13:48:56 +0400 Subject: [PATCH 50/54] recover from panic in WS JSON RPC readRoutine https://github.com/tendermint/tendermint/pull/724#issuecomment-335316484 --- rpc/lib/server/handlers.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/rpc/lib/server/handlers.go b/rpc/lib/server/handlers.go index d8ba7dba..d3d1b504 100644 --- a/rpc/lib/server/handlers.go +++ b/rpc/lib/server/handlers.go @@ -8,6 +8,7 @@ import ( "io/ioutil" "net/http" "reflect" + "runtime/debug" "sort" "strings" "time" @@ -486,7 +487,17 @@ func (wsc *wsConnection) TryWriteRPCResponse(resp types.RPCResponse) bool { // Read from the socket and subscribe to or unsubscribe from events func (wsc *wsConnection) readRoutine() { defer func() { - wsc.baseConn.Close() + if r := recover(); r != nil { + err, ok := r.(error) + if !ok { + err = fmt.Errorf("WSJSONRPC: %v", r) + } + wsc.Logger.Error("Panic in WSJSONRPC handler", "err", err, "stack", string(debug.Stack())) + wsc.WriteRPCResponse(types.RPCInternalError("unknown", err)) + go wsc.readRoutine() + } else { + wsc.baseConn.Close() + } }() wsc.baseConn.SetPongHandler(func(m string) error { From aae4e949980f8eda9bc9bce3d4661afce454f312 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 10 Oct 2017 13:50:06 +0400 Subject: [PATCH 51/54] make RPCRequest params not a pointer https://github.com/tendermint/tendermint/pull/724#issuecomment-335362927 --- rpc/lib/server/handlers.go | 23 +++++++++++++---------- rpc/lib/types/types.go | 10 +++++----- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/rpc/lib/server/handlers.go b/rpc/lib/server/handlers.go index d3d1b504..3a3c48f0 100644 --- a/rpc/lib/server/handlers.go +++ b/rpc/lib/server/handlers.go @@ -129,10 +129,13 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han WriteRPCResponseHTTP(w, types.RPCMethodNotFoundError(request.ID)) return } - args, err := jsonParamsToArgsRPC(rpcFunc, request.Params) - if err != nil { - WriteRPCResponseHTTP(w, types.RPCInvalidParamsError(request.ID, errors.Wrap(err, "Error converting json params to arguments"))) - return + var args []reflect.Value + if len(request.Params) > 0 { + args, err = jsonParamsToArgsRPC(rpcFunc, request.Params) + if err != nil { + WriteRPCResponseHTTP(w, types.RPCInvalidParamsError(request.ID, errors.Wrap(err, "Error converting json params to arguments"))) + return + } } returns := rpcFunc.f.Call(args) logger.Info("HTTPJSONRPC", "method", request.Method, "args", args, "returns", returns) @@ -210,13 +213,13 @@ func jsonParamsToArgs(rpcFunc *RPCFunc, raw []byte, argsOffset int) ([]reflect.V } // Convert a []interface{} OR a map[string]interface{} to properly typed values -func jsonParamsToArgsRPC(rpcFunc *RPCFunc, params *json.RawMessage) ([]reflect.Value, error) { - return jsonParamsToArgs(rpcFunc, *params, 0) +func jsonParamsToArgsRPC(rpcFunc *RPCFunc, params json.RawMessage) ([]reflect.Value, error) { + return jsonParamsToArgs(rpcFunc, params, 0) } // Same as above, but with the first param the websocket connection -func jsonParamsToArgsWS(rpcFunc *RPCFunc, params *json.RawMessage, wsCtx types.WSRPCContext) ([]reflect.Value, error) { - values, err := jsonParamsToArgs(rpcFunc, *params, 1) +func jsonParamsToArgsWS(rpcFunc *RPCFunc, params json.RawMessage, wsCtx types.WSRPCContext) ([]reflect.Value, error) { + values, err := jsonParamsToArgs(rpcFunc, params, 1) if err != nil { return nil, err } @@ -547,11 +550,11 @@ func (wsc *wsConnection) readRoutine() { var args []reflect.Value if rpcFunc.ws { wsCtx := types.WSRPCContext{Request: request, WSRPCConnection: wsc} - if request.Params != nil { + if len(request.Params) > 0 { args, err = jsonParamsToArgsWS(rpcFunc, request.Params, wsCtx) } } else { - if request.Params != nil { + if len(request.Params) > 0 { args, err = jsonParamsToArgsRPC(rpcFunc, request.Params) } } diff --git a/rpc/lib/types/types.go b/rpc/lib/types/types.go index 4928db51..07a8e568 100644 --- a/rpc/lib/types/types.go +++ b/rpc/lib/types/types.go @@ -14,10 +14,10 @@ import ( // REQUEST type RPCRequest struct { - JSONRPC string `json:"jsonrpc"` - ID string `json:"id"` - Method string `json:"method"` - Params *json.RawMessage `json:"params"` // must be map[string]interface{} or []interface{} + JSONRPC string `json:"jsonrpc"` + ID string `json:"id"` + Method string `json:"method"` + Params json.RawMessage `json:"params"` // must be map[string]interface{} or []interface{} } func NewRPCRequest(id string, method string, params json.RawMessage) RPCRequest { @@ -25,7 +25,7 @@ func NewRPCRequest(id string, method string, params json.RawMessage) RPCRequest JSONRPC: "2.0", ID: id, Method: method, - Params: ¶ms, + Params: params, } } From 9fb45c5b5aff7207da0f8174ba3e91706b073c8b Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 10 Oct 2017 10:52:26 -0400 Subject: [PATCH 52/54] remove a stale comment --- rpc/core/consensus.go | 1 - 1 file changed, 1 deletion(-) diff --git a/rpc/core/consensus.go b/rpc/core/consensus.go index 1fe84f9a..75ce08a9 100644 --- a/rpc/core/consensus.go +++ b/rpc/core/consensus.go @@ -84,7 +84,6 @@ func Validators(heightPtr *int) (*ctypes.ResultValidators, error) { func DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) { peerRoundStates := make(map[string]*cstypes.PeerRoundState) for _, peer := range p2pSwitch.Peers().List() { - // TODO: clean this up? peerState := peer.Get(types.PeerStateKey).(*cm.PeerState) peerRoundState := peerState.GetRoundState() peerRoundStates[peer.Key()] = peerRoundState From 33565447064b466ef6b604c1dd9bea83923e7e78 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 10 Oct 2017 11:10:48 -0400 Subject: [PATCH 53/54] update changelog --- CHANGELOG.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa7731b9..c48d0850 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,19 +30,16 @@ BUG FIXES: ## 0.11.1 (October 10, 2017) -BREAKING CHANGES: - - p2p: removed IPRangeCount* functions - IMPROVEMENTS: - blockchain/reactor: respondWithNoResponseMessage for missing height BUG FIXES: - rpc: fixed client WebSocket timeout - rpc: client now resubscribes on reconnection + - rpc: fix panics on missing params + - rpc: fix `/dump_consensus_state` to have normal json output (NOTE: technically breaking, but worth a bug fix label) - types: fixed out of range error in VoteSet.addVote - -DEPENDENCIES: - - tmlibs: https://github.com/tendermint/tmlibs/blob/master/CHANGELOG.md#032-october-2-2017 + - consensus: fix wal autofile via https://github.com/tendermint/tmlibs/blob/master/CHANGELOG.md#032-october-2-2017 ## 0.11.0 (September 22, 2017) From 8c08fc671c28cf16921378fdb7717167253a6d0b Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 10 Oct 2017 18:47:28 -0400 Subject: [PATCH 54/54] fix version --- version/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version/version.go b/version/version.go index e44a662a..b874accc 100644 --- a/version/version.go +++ b/version/version.go @@ -2,7 +2,7 @@ package version const Maj = "0" const Min = "11" -const Fix = "0" +const Fix = "1" var ( // The full version string