Merge pull request #729 from tendermint/release/0.11.1

Release/0.11.1
This commit is contained in:
Ethan Buchman 2017-10-10 18:50:15 -04:00 committed by GitHub
commit d4634dc683
81 changed files with 1273 additions and 658 deletions

5
.github/CODEOWNERS vendored Normal file
View File

@ -0,0 +1,5 @@
# CODEOWNERS: https://help.github.com/articles/about-codeowners/
# Everything goes through Bucky. For now.
* @ebuchman

2
.gitignore vendored
View File

@ -15,3 +15,5 @@ test/p2p/data/
test/logs test/logs
.glide .glide
coverage.txt coverage.txt
docs/_build
docs/tools

View File

@ -28,17 +28,32 @@ BUG FIXES:
- Graceful handling/recovery for apps that have non-determinism or fail to halt - Graceful handling/recovery for apps that have non-determinism or fail to halt
- Graceful handling/recovery for violations of safety, or liveness - Graceful handling/recovery for violations of safety, or liveness
## 0.11.1 (October 10, 2017)
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
- consensus: fix wal autofile via https://github.com/tendermint/tmlibs/blob/master/CHANGELOG.md#032-october-2-2017
## 0.11.0 (September 22, 2017) ## 0.11.0 (September 22, 2017)
BREAKING: BREAKING:
- state: every validator set change is persisted to disk, which required some changes to the `State` structure. - genesis file: validator `amount` is now `power`
- cmd: if there is no genesis, exit immediately instead of waiting around for one to show. - abci: Info, BeginBlock, InitChain all take structs
- p2p: new `p2p.Peer` interface used for all reactor methods (instead of `*p2p.Peer` struct).
- types: `Signer.Sign` returns an error.
- rpc: various changes to match JSONRPC spec (http://www.jsonrpc.org/specification), including breaking ones: - 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. - 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`. - `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: FEATURES:
- rpc: `/validators?height=X` allows querying of validators at previous heights. - rpc: `/validators?height=X` allows querying of validators at previous heights.

View File

@ -1,8 +1,8 @@
FROM alpine:3.6 FROM alpine:3.6
# This is the release of tendermint to pull in. # This is the release of tendermint to pull in.
ENV TM_VERSION 0.10.0 ENV TM_VERSION 0.11.0
ENV TM_SHA256SUM a29852b8d51c00db93c87c3d148fa419a047abd38f32b2507a905805131acc19 ENV TM_SHA256SUM 7e443bac4d42f12e7beaf9cee63b4a565dad8c58895291fdedde8057088b70c5
# Tendermint will be looking for genesis file in /tendermint (unless you change # 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 # `genesis_file` in config.toml). You can put your config.toml and private

View File

@ -1,6 +1,7 @@
# Supported tags and respective `Dockerfile` links # 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.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.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) - `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. `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
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. 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 # 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. 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 # License
View [license information](https://raw.githubusercontent.com/tendermint/tendermint/master/LICENSE) for the software contained in this image. View [license information](https://raw.githubusercontent.com/tendermint/tendermint/master/LICENSE) for the software contained in this image.
# User Feedback # 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 ## 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. 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.

View File

@ -1,7 +1,7 @@
GOTOOLS = \ GOTOOLS = \
github.com/mitchellh/gox \ github.com/mitchellh/gox \
github.com/tcnksm/ghr \
github.com/Masterminds/glide \ github.com/Masterminds/glide \
honnef.co/go/tools/cmd/megacheck
PACKAGES=$(shell go list ./... | grep -v '/vendor/') PACKAGES=$(shell go list ./... | grep -v '/vendor/')
BUILD_TAGS?=tendermint BUILD_TAGS?=tendermint

View File

@ -13,7 +13,7 @@ https://camo.githubusercontent.com/915b7be44ada53c290eb157634330494ebe3e30a/6874
[![](https://tokei.rs/b1/github/tendermint/tendermint?category=lines)](https://github.com/tendermint/tendermint) [![](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) 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) 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 * [Ethermint](http://github.com/tendermint/ethermint); Ethereum on Tendermint
* [Cosmos SDK](http://github.com/cosmos/cosmos-sdk); a cryptocurrency application framework * [Cosmos SDK](http://github.com/cosmos/cosmos-sdk); a cryptocurrency application framework
* [Many more](https://tendermint.readthedocs.io/en/master/ecosystem.html#abci-applications)
### More ### More

6
Vagrantfile vendored
View File

@ -17,11 +17,11 @@ Vagrant.configure("2") do |config|
usermod -a -G docker vagrant usermod -a -G docker vagrant
apt-get autoremove -y apt-get autoremove -y
curl -O https://storage.googleapis.com/golang/go1.8.linux-amd64.tar.gz curl -O https://storage.googleapis.com/golang/go1.9.linux-amd64.tar.gz
tar -xvf go1.8.linux-amd64.tar.gz tar -xvf go1.9.linux-amd64.tar.gz
rm -rf /usr/local/go rm -rf /usr/local/go
mv go /usr/local 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 mkdir -p /home/vagrant/go/bin
echo 'export PATH=$PATH:/usr/local/go/bin:/home/vagrant/go/bin' >> /home/vagrant/.bash_profile 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 echo 'export GOPATH=/home/vagrant/go' >> /home/vagrant/.bash_profile

View File

@ -1,8 +1,9 @@
package benchmarks package benchmarks
import ( import (
. "github.com/tendermint/tmlibs/common"
"testing" "testing"
cmn "github.com/tendermint/tmlibs/common"
) )
func BenchmarkSomething(b *testing.B) { func BenchmarkSomething(b *testing.B) {
@ -11,11 +12,11 @@ func BenchmarkSomething(b *testing.B) {
numChecks := 100000 numChecks := 100000
keys := make([]string, numItems) keys := make([]string, numItems)
for i := 0; i < numItems; i++ { for i := 0; i < numItems; i++ {
keys[i] = RandStr(100) keys[i] = cmn.RandStr(100)
} }
txs := make([]string, numChecks) txs := make([]string, numChecks)
for i := 0; i < numChecks; i++ { for i := 0; i < numChecks; i++ {
txs[i] = RandStr(100) txs[i] = cmn.RandStr(100)
} }
b.StartTimer() b.StartTimer()

View File

@ -4,7 +4,7 @@ import (
"os" "os"
"testing" "testing"
. "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
) )
func BenchmarkFileWrite(b *testing.B) { func BenchmarkFileWrite(b *testing.B) {
@ -14,7 +14,7 @@ func BenchmarkFileWrite(b *testing.B) {
if err != nil { if err != nil {
b.Error(err) b.Error(err)
} }
testString := RandStr(200) + "\n" testString := cmn.RandStr(200) + "\n"
b.StartTimer() b.StartTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {

View File

@ -3,9 +3,8 @@ package main
import ( import (
"context" "context"
"encoding/binary" "encoding/binary"
"time"
//"encoding/hex"
"fmt" "fmt"
"time"
rpcclient "github.com/tendermint/tendermint/rpc/lib/client" rpcclient "github.com/tendermint/tendermint/rpc/lib/client"
cmn "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"

View File

@ -6,7 +6,7 @@ import (
"time" "time"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
. "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
flow "github.com/tendermint/tmlibs/flowrate" flow "github.com/tendermint/tmlibs/flowrate"
"github.com/tendermint/tmlibs/log" "github.com/tendermint/tmlibs/log"
) )
@ -33,7 +33,7 @@ var peerTimeoutSeconds = time.Duration(15) // not const so we can override with
*/ */
type BlockPool struct { type BlockPool struct {
BaseService cmn.BaseService
startTime time.Time startTime time.Time
mtx sync.Mutex mtx sync.Mutex
@ -59,7 +59,7 @@ func NewBlockPool(start int, requestsCh chan<- BlockRequest, timeoutsCh chan<- s
requestsCh: requestsCh, requestsCh: requestsCh,
timeoutsCh: timeoutsCh, timeoutsCh: timeoutsCh,
} }
bp.BaseService = *NewBaseService(nil, "BlockPool", bp) bp.BaseService = *cmn.NewBaseService(nil, "BlockPool", bp)
return bp return bp
} }
@ -137,14 +137,14 @@ func (pool *BlockPool) IsCaughtUp() bool {
maxPeerHeight := 0 maxPeerHeight := 0
for _, peer := range pool.peers { 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 // some conditions to determine if we're caught up
receivedBlockOrTimedOut := (pool.height > 0 || time.Since(pool.startTime) > 5*time.Second) receivedBlockOrTimedOut := (pool.height > 0 || time.Since(pool.startTime) > 5*time.Second)
ourChainIsLongestAmongPeers := maxPeerHeight == 0 || pool.height >= maxPeerHeight ourChainIsLongestAmongPeers := maxPeerHeight == 0 || pool.height >= maxPeerHeight
isCaughtUp := receivedBlockOrTimedOut && ourChainIsLongestAmongPeers 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 return isCaughtUp
} }
@ -180,7 +180,7 @@ func (pool *BlockPool) PopRequest() {
delete(pool.requesters, pool.height) delete(pool.requesters, pool.height)
pool.height++ pool.height++
} else { } 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() pool.mtx.Unlock()
if request.block == nil { 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. // RemovePeer will redo all requesters associated with this peer.
// TODO: record this malfeasance // TODO: record this malfeasance
@ -311,10 +311,10 @@ func (pool *BlockPool) debug() string {
str := "" str := ""
for h := pool.height; h < pool.height+len(pool.requesters); h++ { for h := pool.height; h < pool.height+len(pool.requesters); h++ {
if pool.requesters[h] == nil { if pool.requesters[h] == nil {
str += Fmt("H(%v):X ", h) str += cmn.Fmt("H(%v):X ", h)
} else { } else {
str += Fmt("H(%v):", h) str += cmn.Fmt("H(%v):", h)
str += Fmt("B?(%v) ", pool.requesters[h].block != nil) str += cmn.Fmt("B?(%v) ", pool.requesters[h].block != nil)
} }
} }
return str return str
@ -394,7 +394,7 @@ func (peer *bpPeer) onTimeout() {
//------------------------------------- //-------------------------------------
type bpRequester struct { type bpRequester struct {
BaseService cmn.BaseService
pool *BlockPool pool *BlockPool
height int height int
gotBlockCh chan struct{} gotBlockCh chan struct{}
@ -415,7 +415,7 @@ func newBPRequester(pool *BlockPool, height int) *bpRequester {
peerID: "", peerID: "",
block: nil, block: nil,
} }
bpr.BaseService = *NewBaseService(nil, "bpRequester", bpr) bpr.BaseService = *cmn.NewBaseService(nil, "bpRequester", bpr)
return bpr return bpr
} }

View File

@ -6,7 +6,7 @@ import (
"time" "time"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
. "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/log" "github.com/tendermint/tmlibs/log"
) )
@ -22,7 +22,7 @@ type testPeer struct {
func makePeers(numPeers int, minHeight, maxHeight int) map[string]testPeer { func makePeers(numPeers int, minHeight, maxHeight int) map[string]testPeer {
peers := make(map[string]testPeer, numPeers) peers := make(map[string]testPeer, numPeers)
for i := 0; i < numPeers; i++ { for i := 0; i < numPeers; i++ {
peerID := RandStr(12) peerID := cmn.RandStr(12)
height := minHeight + rand.Intn(maxHeight-minHeight) height := minHeight + rand.Intn(maxHeight-minHeight)
peers[peerID] = testPeer{peerID, height} peers[peerID] = testPeer{peerID, height}
} }

View File

@ -121,6 +121,24 @@ func (bcR *BlockchainReactor) RemovePeer(peer p2p.Peer, reason interface{}) {
bcR.pool.RemovePeer(peer.Key()) 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). // Receive implements Reactor by handling 4 types of messages (look below).
func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) { func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) {
_, msg, err := DecodeMessage(msgBytes, bcR.maxMsgSize()) _, 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 // TODO: improve logic to satisfy megacheck
switch msg := msg.(type) { switch msg := msg.(type) {
case *bcBlockRequestMessage: case *bcBlockRequestMessage:
// Got a request for a block. Respond with block if we have it. if queued := bcR.respondToPeer(msg, src); !queued {
block := bcR.store.LoadBlock(msg.Height) // Unfortunately not queued since the queue is full.
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.
} }
case *bcBlockResponseMessage: case *bcBlockResponseMessage:
// Got a block. // Got a block.
@ -235,7 +245,7 @@ FOR_LOOP:
err := bcR.state.Validators.VerifyCommit( err := bcR.state.Validators.VerifyCommit(
bcR.state.ChainID, types.BlockID{first.Hash(), firstPartsHeader}, first.Height, second.LastCommit) bcR.state.ChainID, types.BlockID{first.Hash(), firstPartsHeader}, first.Height, second.LastCommit)
if err != nil { if err != nil {
bcR.Logger.Info("error in validation", "err", err) bcR.Logger.Error("Error in validation", "err", err)
bcR.pool.RedoRequest(first.Height) bcR.pool.RedoRequest(first.Height)
break SYNC_LOOP break SYNC_LOOP
} else { } else {
@ -276,10 +286,11 @@ func (bcR *BlockchainReactor) SetEventSwitch(evsw types.EventSwitch) {
// Messages // Messages
const ( const (
msgTypeBlockRequest = byte(0x10) msgTypeBlockRequest = byte(0x10)
msgTypeBlockResponse = byte(0x11) msgTypeBlockResponse = byte(0x11)
msgTypeStatusResponse = byte(0x20) msgTypeNoBlockResponse = byte(0x12)
msgTypeStatusRequest = byte(0x21) msgTypeStatusResponse = byte(0x20)
msgTypeStatusRequest = byte(0x21)
) )
// BlockchainMessage is a generic message for this reactor. // BlockchainMessage is a generic message for this reactor.
@ -289,6 +300,7 @@ var _ = wire.RegisterInterface(
struct{ BlockchainMessage }{}, struct{ BlockchainMessage }{},
wire.ConcreteType{&bcBlockRequestMessage{}, msgTypeBlockRequest}, wire.ConcreteType{&bcBlockRequestMessage{}, msgTypeBlockRequest},
wire.ConcreteType{&bcBlockResponseMessage{}, msgTypeBlockResponse}, wire.ConcreteType{&bcBlockResponseMessage{}, msgTypeBlockResponse},
wire.ConcreteType{&bcNoBlockResponseMessage{}, msgTypeNoBlockResponse},
wire.ConcreteType{&bcStatusResponseMessage{}, msgTypeStatusResponse}, wire.ConcreteType{&bcStatusResponseMessage{}, msgTypeStatusResponse},
wire.ConcreteType{&bcStatusRequestMessage{}, msgTypeStatusRequest}, wire.ConcreteType{&bcStatusRequestMessage{}, msgTypeStatusRequest},
) )
@ -316,6 +328,14 @@ func (m *bcBlockRequestMessage) String() string {
return cmn.Fmt("[bcBlockRequestMessage %v]", m.Height) 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 // NOTE: keep up-to-date with maxBlockchainResponseSize

158
blockchain/reactor_test.go Normal file
View File

@ -0,0 +1,158 @@
package blockchain
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"
)
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(state.Params().BlockGossipParams.BlockPartSizeBytes)
blockStore.SaveBlock(firstBlock, firstParts, secondBlock.LastCommit)
}
return bcReactor
}
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
}{
{maxBlockHeight + 2, false},
{10, true},
{1, true},
{100, false},
}
for _, tt := range tests {
reqBlockMsg := &bcBlockRequestMessage{tt.height}
reqBlockBytes := wire.BinaryBytes(struct{ BlockchainMessage }{reqBlockMsg})
bcr.Receive(chID, peer, reqBlockBytes)
value := peer.lastValue()
msg := value.(struct{ BlockchainMessage }).BlockchainMessage
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 {
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)
}
}
}
}
//----------------------------------------------
// utility funcs
func makeTxs(blockNumber int) (txs []types.Tx) {
for i := 0; i < 10; i++ {
txs = append(txs, types.Tx([]byte{byte(blockNumber), byte(i)}))
}
return txs
}
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, state.Params().BlockGossipParams.BlockPartSizeBytes)
return block
}
// The Test peer
type bcrTestPeer struct {
cmn.Service
key string
ch chan interface{}
}
var _ p2p.Peer = (*bcrTestPeer)(nil)
func newbcrTestPeer(key string) *bcrTestPeer {
return &bcrTestPeer{
Service: cmn.NewBaseService(nil, "bcrTestPeer", nil),
key: key,
ch: make(chan interface{}, 2),
}
}
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 {
// Discard status response messages since they skew our results
// We only want to deal with:
// + bcBlockResponseMessage
// + bcNoBlockResponseMessage
} else {
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) 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{}) {}

View File

@ -3,15 +3,15 @@ package commands
import ( import (
"os" "os"
"strconv" "strconv"
"testing"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
cfg "github.com/tendermint/tendermint/config" cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tmlibs/cli" "github.com/tendermint/tmlibs/cli"
"testing"
) )
var ( var (

View File

@ -9,7 +9,7 @@ import (
data "github.com/tendermint/go-wire/data" data "github.com/tendermint/go-wire/data"
"github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
. "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/events" "github.com/tendermint/tmlibs/events"
) )
@ -149,8 +149,8 @@ func TestByzantine(t *testing.T) {
case <-done: case <-done:
case <-tick.C: case <-tick.C:
for i, reactor := range reactors { for i, reactor := range reactors {
t.Log(Fmt("Consensus Reactor %v", i)) t.Log(cmn.Fmt("Consensus Reactor %v", i))
t.Log(Fmt("%v", reactor)) t.Log(cmn.Fmt("%v", reactor))
} }
t.Fatalf("Timed out waiting for all validators to commit first block") 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 // byzantine consensus reactor
type ByzantineReactor struct { type ByzantineReactor struct {
Service cmn.Service
reactor *ConsensusReactor reactor *ConsensusReactor
} }
@ -296,5 +296,5 @@ func (privVal *ByzantinePrivValidator) SignHeartbeat(chainID string, heartbeat *
} }
func (privVal *ByzantinePrivValidator) String() string { func (privVal *ByzantinePrivValidator) String() string {
return Fmt("PrivValidator{%X}", privVal.GetAddress()) return cmn.Fmt("PrivValidator{%X}", privVal.GetAddress())
} }

View File

@ -15,11 +15,12 @@ import (
abci "github.com/tendermint/abci/types" abci "github.com/tendermint/abci/types"
bc "github.com/tendermint/tendermint/blockchain" bc "github.com/tendermint/tendermint/blockchain"
cfg "github.com/tendermint/tendermint/config" cfg "github.com/tendermint/tendermint/config"
cstypes "github.com/tendermint/tendermint/consensus/types"
mempl "github.com/tendermint/tendermint/mempool" mempl "github.com/tendermint/tendermint/mempool"
"github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/p2p"
sm "github.com/tendermint/tendermint/state" sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
. "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db" dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log" "github.com/tendermint/tmlibs/log"
@ -34,7 +35,7 @@ var config *cfg.Config // NOTE: must be reset for each _test.go file
var ensureTimeout = time.Second * 2 var ensureTimeout = time.Second * 2
func ensureDir(dir string, mode os.FileMode) { func ensureDir(dir string, mode os.FileMode) {
if err := EnsureDir(dir, mode); err != nil { if err := cmn.EnsureDir(dir, mode); err != nil {
panic(err) panic(err)
} }
} }
@ -341,7 +342,7 @@ func randConsensusNet(nValidators int, testName string, tickerFunc func() Timeou
state, _ := sm.MakeGenesisState(db, genDoc) state, _ := sm.MakeGenesisState(db, genDoc)
state.SetLogger(logger.With("module", "state", "validator", i)) state.SetLogger(logger.With("module", "state", "validator", i))
state.Save() state.Save()
thisConfig := ResetConfig(Fmt("%s_%d", testName, i)) thisConfig := ResetConfig(cmn.Fmt("%s_%d", testName, i))
for _, opt := range configOpts { for _, opt := range configOpts {
opt(thisConfig) opt(thisConfig)
} }
@ -362,13 +363,13 @@ func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerF
state, _ := sm.MakeGenesisState(db, genDoc) state, _ := sm.MakeGenesisState(db, genDoc)
state.SetLogger(log.TestingLogger().With("module", "state")) state.SetLogger(log.TestingLogger().With("module", "state"))
state.Save() 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 ensureDir(path.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal
var privVal types.PrivValidator var privVal types.PrivValidator
if i < nValidators { if i < nValidators {
privVal = privVals[i] privVal = privVals[i]
} else { } else {
_, tempFilePath := Tempfile("priv_validator_") _, tempFilePath := cmn.Tempfile("priv_validator_")
privVal = types.GenPrivValidatorFS(tempFilePath) privVal = types.GenPrivValidatorFS(tempFilePath)
} }
@ -456,7 +457,7 @@ func (m *mockTicker) ScheduleTimeout(ti timeoutInfo) {
if m.onlyOnce && m.fired { if m.onlyOnce && m.fired {
return return
} }
if ti.Step == RoundStepNewHeight { if ti.Step == cstypes.RoundStepNewHeight {
m.c <- ti m.c <- ti
m.fired = true m.fired = true
} }

View File

@ -8,7 +8,7 @@ import (
abci "github.com/tendermint/abci/types" abci "github.com/tendermint/abci/types"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
. "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
) )
func init() { func init() {
@ -86,7 +86,7 @@ func deliverTxsRange(cs *ConsensusState, start, end int) {
binary.BigEndian.PutUint64(txBytes, uint64(i)) binary.BigEndian.PutUint64(txBytes, uint64(i))
err := cs.mempool.CheckTx(txBytes, nil) err := cs.mempool.CheckTx(txBytes, nil)
if err != 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 { 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 { 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) copy(tx8[len(tx8)-len(tx):], tx)
txValue := binary.BigEndian.Uint64(tx8) txValue := binary.BigEndian.Uint64(tx8)
if txValue != uint64(count) { 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 *countPtr += 1
return abci.OK return abci.OK

View File

@ -12,6 +12,7 @@ import (
cmn "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/log" "github.com/tendermint/tmlibs/log"
cstypes "github.com/tendermint/tendermint/consensus/types"
"github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/p2p"
sm "github.com/tendermint/tendermint/state" sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
@ -321,7 +322,7 @@ func (conR *ConsensusReactor) FastSync() bool {
func (conR *ConsensusReactor) registerEventCallbacks() { func (conR *ConsensusReactor) registerEventCallbacks() {
types.AddListenerForEvent(conR.evsw, "conR", types.EventStringNewRoundStep(), func(data types.TMEventData) { 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) conR.broadcastNewRoundStep(rs)
}) })
@ -344,7 +345,7 @@ func (conR *ConsensusReactor) broadcastProposalHeartbeatMessage(heartbeat types.
conR.Switch.Broadcast(StateChannel, struct{ ConsensusMessage }{msg}) conR.Switch.Broadcast(StateChannel, struct{ ConsensusMessage }{msg})
} }
func (conR *ConsensusReactor) broadcastNewRoundStep(rs *RoundState) { func (conR *ConsensusReactor) broadcastNewRoundStep(rs *cstypes.RoundState) {
nrsMsg, csMsg := makeRoundStepMessages(rs) nrsMsg, csMsg := makeRoundStepMessages(rs)
if nrsMsg != nil { 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{ nrsMsg = &NewRoundStepMessage{
Height: rs.Height, Height: rs.Height,
Round: rs.Round, Round: rs.Round,
@ -389,7 +390,7 @@ func makeRoundStepMessages(rs *RoundState) (nrsMsg *NewRoundStepMessage, csMsg *
SecondsSinceStartTime: int(time.Since(rs.StartTime).Seconds()), SecondsSinceStartTime: int(time.Since(rs.StartTime).Seconds()),
LastCommitRound: rs.LastCommit.Round(), LastCommitRound: rs.LastCommit.Round(),
} }
if rs.Step == RoundStepCommit { if rs.Step == cstypes.RoundStepCommit {
csMsg = &CommitStepMessage{ csMsg = &CommitStepMessage{
Height: rs.Height, Height: rs.Height,
BlockPartsHeader: rs.ProposalBlockParts.Header(), BlockPartsHeader: rs.ProposalBlockParts.Header(),
@ -491,8 +492,8 @@ OUTER_LOOP:
} }
} }
func (conR *ConsensusReactor) gossipDataForCatchup(logger log.Logger, rs *RoundState, func (conR *ConsensusReactor) gossipDataForCatchup(logger log.Logger, rs *cstypes.RoundState,
prs *PeerRoundState, ps *PeerState, peer p2p.Peer) { prs *cstypes.PeerRoundState, ps *PeerState, peer p2p.Peer) {
if index, ok := prs.ProposalBlockParts.Not().PickRandom(); ok { if index, ok := prs.ProposalBlockParts.Not().PickRandom(); ok {
// Ensure that the peer's PartSetHeader is correct // 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 there are lastCommits to send...
if prs.Step == RoundStepNewHeight { if prs.Step == cstypes.RoundStepNewHeight {
if ps.PickSendVote(rs.LastCommit) { if ps.PickSendVote(rs.LastCommit) {
logger.Debug("Picked rs.LastCommit to send") logger.Debug("Picked rs.LastCommit to send")
return true return true
} }
} }
// If there are prevotes to send... // 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)) { if ps.PickSendVote(rs.Votes.Prevotes(prs.Round)) {
logger.Debug("Picked rs.Prevotes(prs.Round) to send", "round", prs.Round) logger.Debug("Picked rs.Prevotes(prs.Round) to send", "round", prs.Round)
return true return true
} }
} }
// If there are precommits to send... // 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)) { if ps.PickSendVote(rs.Votes.Precommits(prs.Round)) {
logger.Debug("Picked rs.Precommits(prs.Round) to send", "round", prs.Round) logger.Debug("Picked rs.Precommits(prs.Round) to send", "round", prs.Round)
return true 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 ( var (
ErrPeerStateHeightRegression = errors.New("Error peer state height regression") ErrPeerStateHeightRegression = errors.New("Error peer state height regression")
ErrPeerStateInvalidStartTime = errors.New("Error peer state invalid startTime") ErrPeerStateInvalidStartTime = errors.New("Error peer state invalid startTime")
@ -812,7 +765,7 @@ type PeerState struct {
logger log.Logger logger log.Logger
mtx sync.Mutex mtx sync.Mutex
PeerRoundState cstypes.PeerRoundState
} }
// NewPeerState returns a new PeerState for the given Peer // NewPeerState returns a new PeerState for the given Peer
@ -820,7 +773,7 @@ func NewPeerState(peer p2p.Peer) *PeerState {
return &PeerState{ return &PeerState{
Peer: peer, Peer: peer,
logger: log.NewNopLogger(), logger: log.NewNopLogger(),
PeerRoundState: PeerRoundState{ PeerRoundState: cstypes.PeerRoundState{
Round: -1, Round: -1,
ProposalPOLRound: -1, ProposalPOLRound: -1,
LastCommitRound: -1, LastCommitRound: -1,
@ -836,7 +789,7 @@ func (ps *PeerState) SetLogger(logger log.Logger) *PeerState {
// GetRoundState returns an atomic snapshot of the PeerRoundState. // GetRoundState returns an atomic snapshot of the PeerRoundState.
// There's no point in mutating it since it won't change PeerState. // 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() ps.mtx.Lock()
defer ps.mtx.Unlock() defer ps.mtx.Unlock()
@ -1227,7 +1180,7 @@ func DecodeMessage(bz []byte) (msgType byte, msg ConsensusMessage, err error) {
type NewRoundStepMessage struct { type NewRoundStepMessage struct {
Height int Height int
Round int Round int
Step RoundStepType Step cstypes.RoundStepType
SecondsSinceStartTime int SecondsSinceStartTime int
LastCommitRound int LastCommitRound int
} }

View File

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"path/filepath" "path/filepath"
"reflect" "reflect"
"runtime/debug"
"sync" "sync"
"time" "time"
@ -16,6 +17,7 @@ import (
"github.com/tendermint/tmlibs/log" "github.com/tendermint/tmlibs/log"
cfg "github.com/tendermint/tendermint/config" cfg "github.com/tendermint/tendermint/config"
cstypes "github.com/tendermint/tendermint/consensus/types"
"github.com/tendermint/tendermint/proxy" "github.com/tendermint/tendermint/proxy"
sm "github.com/tendermint/tendermint/state" sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
@ -38,124 +40,6 @@ var (
ErrVoteHeightMismatch = errors.New("Error vote height mismatch") 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 ( var (
@ -170,10 +54,10 @@ type msgInfo struct {
// internally generated messages which may update the state // internally generated messages which may update the state
type timeoutInfo struct { type timeoutInfo struct {
Duration time.Duration `json:"duration"` Duration time.Duration `json:"duration"`
Height int `json:"height"` Height int `json:"height"`
Round int `json:"round"` Round int `json:"round"`
Step RoundStepType `json:"step"` Step cstypes.RoundStepType `json:"step"`
} }
func (ti *timeoutInfo) String() string { func (ti *timeoutInfo) String() string {
@ -198,7 +82,7 @@ type ConsensusState struct {
// internal state // internal state
mtx sync.Mutex mtx sync.Mutex
RoundState cstypes.RoundState
state *sm.State // State until height-1. state *sm.State // State until height-1.
// state changes may be triggered by msgs from peers, // state changes may be triggered by msgs from peers,
@ -281,13 +165,13 @@ func (cs *ConsensusState) GetState() *sm.State {
} }
// GetRoundState returns a copy of the internal consensus state. // GetRoundState returns a copy of the internal consensus state.
func (cs *ConsensusState) GetRoundState() *RoundState { func (cs *ConsensusState) GetRoundState() *cstypes.RoundState {
cs.mtx.Lock() cs.mtx.Lock()
defer cs.mtx.Unlock() defer cs.mtx.Unlock()
return cs.getRoundState() return cs.getRoundState()
} }
func (cs *ConsensusState) getRoundState() *RoundState { func (cs *ConsensusState) getRoundState() *cstypes.RoundState {
rs := cs.RoundState // copy rs := cs.RoundState // copy
return &rs return &rs
} }
@ -468,20 +352,20 @@ func (cs *ConsensusState) updateHeight(height int) {
cs.Height = height cs.Height = height
} }
func (cs *ConsensusState) updateRoundStep(round int, step RoundStepType) { func (cs *ConsensusState) updateRoundStep(round int, step cstypes.RoundStepType) {
cs.Round = round cs.Round = round
cs.Step = step cs.Step = step
} }
// enterNewRound(height, 0) at cs.StartTime. // 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) //cs.Logger.Info("scheduleRound0", "now", time.Now(), "startTime", cs.StartTime)
sleepDuration := rs.StartTime.Sub(time.Now()) 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) // 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}) cs.timeoutTicker.ScheduleTimeout(timeoutInfo{duration, height, round, step})
} }
@ -523,7 +407,7 @@ func (cs *ConsensusState) reconstructLastCommit(state *sm.State) {
} }
// Updates ConsensusState and increments height to match that of 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) { func (cs *ConsensusState) updateToState(state *sm.State) {
if cs.CommitRound > -1 && 0 < cs.Height && cs.Height != state.LastBlockHeight { if cs.CommitRound > -1 && 0 < cs.Height && cs.Height != state.LastBlockHeight {
cmn.PanicSanity(cmn.Fmt("updateToState() expected state height of %v but found %v", cmn.PanicSanity(cmn.Fmt("updateToState() expected state height of %v but found %v",
@ -559,7 +443,7 @@ func (cs *ConsensusState) updateToState(state *sm.State) {
// RoundState fields // RoundState fields
cs.updateHeight(height) cs.updateHeight(height)
cs.updateRoundStep(0, RoundStepNewHeight) cs.updateRoundStep(0, cstypes.RoundStepNewHeight)
if cs.CommitTime.IsZero() { if cs.CommitTime.IsZero() {
// "Now" makes it easier to sync up dev nodes. // "Now" makes it easier to sync up dev nodes.
// We add timeoutCommit to allow transactions // We add timeoutCommit to allow transactions
@ -577,7 +461,7 @@ func (cs *ConsensusState) updateToState(state *sm.State) {
cs.LockedRound = 0 cs.LockedRound = 0
cs.LockedBlock = nil cs.LockedBlock = nil
cs.LockedBlockParts = nil cs.LockedBlockParts = nil
cs.Votes = NewHeightVoteSet(state.ChainID, height, validators) cs.Votes = cstypes.NewHeightVoteSet(state.ChainID, height, validators)
cs.CommitRound = -1 cs.CommitRound = -1
cs.LastCommit = lastPrecommits cs.LastCommit = lastPrecommits
cs.LastValidators = state.LastValidators cs.LastValidators = state.LastValidators
@ -609,7 +493,7 @@ func (cs *ConsensusState) newStep() {
func (cs *ConsensusState) receiveRoutine(maxSteps int) { func (cs *ConsensusState) receiveRoutine(maxSteps int) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
cs.Logger.Error("CONSENSUS FAILURE!!!", "err", r) cs.Logger.Error("CONSENSUS FAILURE!!!", "err", r, "stack", string(debug.Stack()))
} }
}() }()
@ -698,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) 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 // timeouts must be for current height, round, step
@ -712,19 +596,19 @@ func (cs *ConsensusState) handleTimeout(ti timeoutInfo, rs RoundState) {
defer cs.mtx.Unlock() defer cs.mtx.Unlock()
switch ti.Step { switch ti.Step {
case RoundStepNewHeight: case cstypes.RoundStepNewHeight:
// NewRound event fired from enterNewRound. // NewRound event fired from enterNewRound.
// XXX: should we fire timeout here (for timeout commit)? // XXX: should we fire timeout here (for timeout commit)?
cs.enterNewRound(ti.Height, 0) cs.enterNewRound(ti.Height, 0)
case RoundStepNewRound: case cstypes.RoundStepNewRound:
cs.enterPropose(ti.Height, 0) cs.enterPropose(ti.Height, 0)
case RoundStepPropose: case cstypes.RoundStepPropose:
types.FireEventTimeoutPropose(cs.evsw, cs.RoundStateEvent()) types.FireEventTimeoutPropose(cs.evsw, cs.RoundStateEvent())
cs.enterPrevote(ti.Height, ti.Round) cs.enterPrevote(ti.Height, ti.Round)
case RoundStepPrevoteWait: case cstypes.RoundStepPrevoteWait:
types.FireEventTimeoutWait(cs.evsw, cs.RoundStateEvent()) types.FireEventTimeoutWait(cs.evsw, cs.RoundStateEvent())
cs.enterPrecommit(ti.Height, ti.Round) cs.enterPrecommit(ti.Height, ti.Round)
case RoundStepPrecommitWait: case cstypes.RoundStepPrecommitWait:
types.FireEventTimeoutWait(cs.evsw, cs.RoundStateEvent()) types.FireEventTimeoutWait(cs.evsw, cs.RoundStateEvent())
cs.enterNewRound(ti.Height, ti.Round+1) cs.enterNewRound(ti.Height, ti.Round+1)
default: default:
@ -751,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) // Enter: +2/3 prevotes any or +2/3 precommits for block or any from (height, round)
// NOTE: cs.StartTime was already set for height. // NOTE: cs.StartTime was already set for height.
func (cs *ConsensusState) enterNewRound(height int, round int) { 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)) cs.Logger.Debug(cmn.Fmt("enterNewRound(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
return return
} }
@ -772,7 +656,7 @@ func (cs *ConsensusState) enterNewRound(height int, round int) {
// Setup new round // Setup new round
// we don't fire newStep for this step, // we don't fire newStep for this step,
// but we fire an event, so update the round step first // but we fire an event, so update the round step first
cs.updateRoundStep(round, RoundStepNewRound) cs.updateRoundStep(round, cstypes.RoundStepNewRound)
cs.Validators = validators cs.Validators = validators
if round == 0 { if round == 0 {
// We've already reset these upon new height, // We've already reset these upon new height,
@ -793,7 +677,7 @@ func (cs *ConsensusState) enterNewRound(height int, round int) {
waitForTxs := cs.config.WaitForTxs() && round == 0 && !cs.needProofBlock(height) waitForTxs := cs.config.WaitForTxs() && round == 0 && !cs.needProofBlock(height)
if waitForTxs { if waitForTxs {
if cs.config.CreateEmptyBlocksInterval > 0 { 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) go cs.proposalHeartbeat(height, round)
} else { } else {
@ -826,7 +710,7 @@ func (cs *ConsensusState) proposalHeartbeat(height, round int) {
for { for {
rs := cs.GetRoundState() rs := cs.GetRoundState()
// if we've already moved on, no need to send more heartbeats // 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 return
} }
heartbeat := &types.Heartbeat{ heartbeat := &types.Heartbeat{
@ -848,7 +732,7 @@ func (cs *ConsensusState) proposalHeartbeat(height, round int) {
// Enter (CreateEmptyBlocks, CreateEmptyBlocksInterval > 0 ): after enterNewRound(height,round), after timeout of CreateEmptyBlocksInterval // Enter (CreateEmptyBlocks, CreateEmptyBlocksInterval > 0 ): after enterNewRound(height,round), after timeout of CreateEmptyBlocksInterval
// Enter (!CreateEmptyBlocks) : after enterNewRound(height,round), once txs are in the mempool // Enter (!CreateEmptyBlocks) : after enterNewRound(height,round), once txs are in the mempool
func (cs *ConsensusState) enterPropose(height int, round int) { 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)) cs.Logger.Debug(cmn.Fmt("enterPropose(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
return return
} }
@ -856,7 +740,7 @@ func (cs *ConsensusState) enterPropose(height int, round int) {
defer func() { defer func() {
// Done enterPropose: // Done enterPropose:
cs.updateRoundStep(round, RoundStepPropose) cs.updateRoundStep(round, cstypes.RoundStepPropose)
cs.newStep() cs.newStep()
// If we have the whole proposal + POL, then goto Prevote now. // If we have the whole proposal + POL, then goto Prevote now.
@ -868,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 // 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 // Nothing more to do if we're not a validator
if cs.privValidator == nil { if cs.privValidator == nil {
@ -985,14 +869,14 @@ func (cs *ConsensusState) createProposalBlock() (block *types.Block, blockParts
// Prevote for LockedBlock if we're locked, or ProposalBlock if valid. // Prevote for LockedBlock if we're locked, or ProposalBlock if valid.
// Otherwise vote nil. // Otherwise vote nil.
func (cs *ConsensusState) enterPrevote(height int, round int) { 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)) cs.Logger.Debug(cmn.Fmt("enterPrevote(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
return return
} }
defer func() { defer func() {
// Done enterPrevote: // Done enterPrevote:
cs.updateRoundStep(round, RoundStepPrevote) cs.updateRoundStep(round, cstypes.RoundStepPrevote)
cs.newStep() cs.newStep()
}() }()
@ -1047,7 +931,7 @@ func (cs *ConsensusState) defaultDoPrevote(height int, round int) {
// Enter: any +2/3 prevotes at next round. // Enter: any +2/3 prevotes at next round.
func (cs *ConsensusState) enterPrevoteWait(height int, round int) { 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)) cs.Logger.Debug(cmn.Fmt("enterPrevoteWait(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
return return
} }
@ -1058,12 +942,12 @@ func (cs *ConsensusState) enterPrevoteWait(height int, round int) {
defer func() { defer func() {
// Done enterPrevoteWait: // Done enterPrevoteWait:
cs.updateRoundStep(round, RoundStepPrevoteWait) cs.updateRoundStep(round, cstypes.RoundStepPrevoteWait)
cs.newStep() cs.newStep()
}() }()
// Wait for some more prevotes; enterPrecommit // 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. // Enter: `timeoutPrevote` after any +2/3 prevotes.
@ -1073,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, unlock an existing lock and precommit nil if +2/3 of prevotes were nil,
// else, precommit nil otherwise. // else, precommit nil otherwise.
func (cs *ConsensusState) enterPrecommit(height int, round int) { 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)) cs.Logger.Debug(cmn.Fmt("enterPrecommit(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
return return
} }
@ -1082,7 +966,7 @@ func (cs *ConsensusState) enterPrecommit(height int, round int) {
defer func() { defer func() {
// Done enterPrecommit: // Done enterPrecommit:
cs.updateRoundStep(round, RoundStepPrecommit) cs.updateRoundStep(round, cstypes.RoundStepPrecommit)
cs.newStep() cs.newStep()
}() }()
@ -1166,7 +1050,7 @@ func (cs *ConsensusState) enterPrecommit(height int, round int) {
// Enter: any +2/3 precommits for next round. // Enter: any +2/3 precommits for next round.
func (cs *ConsensusState) enterPrecommitWait(height int, round int) { 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)) cs.Logger.Debug(cmn.Fmt("enterPrecommitWait(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
return return
} }
@ -1177,18 +1061,18 @@ func (cs *ConsensusState) enterPrecommitWait(height int, round int) {
defer func() { defer func() {
// Done enterPrecommitWait: // Done enterPrecommitWait:
cs.updateRoundStep(round, RoundStepPrecommitWait) cs.updateRoundStep(round, cstypes.RoundStepPrecommitWait)
cs.newStep() cs.newStep()
}() }()
// Wait for some more precommits; enterNewRound // 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 // Enter: +2/3 precommits for block
func (cs *ConsensusState) enterCommit(height int, commitRound int) { 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)) cs.Logger.Debug(cmn.Fmt("enterCommit(%v/%v): Invalid args. Current step: %v/%v/%v", height, commitRound, cs.Height, cs.Round, cs.Step))
return return
} }
@ -1197,7 +1081,7 @@ func (cs *ConsensusState) enterCommit(height int, commitRound int) {
defer func() { defer func() {
// Done enterCommit: // Done enterCommit:
// keep cs.Round the same, commitRound points to the right Precommits set. // 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.CommitRound = commitRound
cs.CommitTime = time.Now() cs.CommitTime = time.Now()
cs.newStep() cs.newStep()
@ -1254,9 +1138,9 @@ func (cs *ConsensusState) tryFinalizeCommit(height int) {
cs.finalizeCommit(height) cs.finalizeCommit(height)
} }
// Increment height and goto RoundStepNewHeight // Increment height and goto cstypes.RoundStepNewHeight
func (cs *ConsensusState) finalizeCommit(height int) { 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)) cs.Logger.Debug(cmn.Fmt("finalizeCommit(%v): Invalid args. Current step: %v/%v/%v", height, cs.Height, cs.Round, cs.Step))
return return
} }
@ -1350,7 +1234,7 @@ func (cs *ConsensusState) finalizeCommit(height int) {
// By here, // By here,
// * cs.Height has been increment to height+1 // * 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. // * cs.StartTime is set to when we will start round0.
} }
@ -1368,8 +1252,8 @@ func (cs *ConsensusState) defaultSetProposal(proposal *types.Proposal) error {
return nil return nil
} }
// We don't care about the proposal if we're already in RoundStepCommit. // We don't care about the proposal if we're already in cstypes.RoundStepCommit.
if RoundStepCommit <= cs.Step { if cstypes.RoundStepCommit <= cs.Step {
return nil return nil
} }
@ -1414,10 +1298,10 @@ func (cs *ConsensusState) addProposalBlockPart(height int, part *types.Part, ver
cs.state.Params().BlockSizeParams.MaxBytes, &n, &err).(*types.Block) 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 // 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()) 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 // Move onto the next step
cs.enterPrevote(height, cs.Round) cs.enterPrevote(height, cs.Round)
} else if cs.Step == RoundStepCommit { } else if cs.Step == cstypes.RoundStepCommit {
// If we're waiting on the proposal block... // If we're waiting on the proposal block...
cs.tryFinalizeCommit(height) cs.tryFinalizeCommit(height)
} }
@ -1462,7 +1346,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerKey string) (added bool,
// A precommit for the previous height? // A precommit for the previous height?
// These come in while we wait timeoutCommit // These come in while we wait timeoutCommit
if vote.Height+1 == cs.Height { 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 .. // TODO: give the reason ..
// fmt.Errorf("tryAddVote: Wrong height, not a LastCommit straggler commit.") // fmt.Errorf("tryAddVote: Wrong height, not a LastCommit straggler commit.")
return added, ErrVoteHeightMismatch return added, ErrVoteHeightMismatch
@ -1475,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 we can skip timeoutCommit and have all the votes now,
if cs.config.SkipTimeoutCommit && cs.LastCommit.HasAll() { if cs.config.SkipTimeoutCommit && cs.LastCommit.HasAll() {
// go straight to new round (skip timeout commit) // 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) cs.enterNewRound(cs.Height, 0)
} }
} }
@ -1539,7 +1423,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerKey string) (added bool,
if cs.config.SkipTimeoutCommit && precommits.HasAll() { if cs.config.SkipTimeoutCommit && precommits.HasAll() {
// if we have all the votes now, // if we have all the votes now,
// go straight to new round (skip timeout commit) // 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) cs.enterNewRound(cs.Height, 0)
} }
@ -1600,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 { if h1 < h2 {
return -1 return -1
} else if h1 > h2 { } else if h1 > h2 {

View File

@ -6,8 +6,9 @@ import (
"testing" "testing"
"time" "time"
cstypes "github.com/tendermint/tendermint/consensus/types"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
. "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
) )
func init() { func init() {
@ -80,7 +81,7 @@ func TestProposerSelection0(t *testing.T) {
prop = cs1.GetRoundState().Validators.GetProposer() prop = cs1.GetRoundState().Validators.GetProposer()
if !bytes.Equal(prop.Address, vss[1].GetAddress()) { 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 +102,7 @@ func TestProposerSelection2(t *testing.T) {
for i := 0; i < len(vss); i++ { for i := 0; i < len(vss); i++ {
prop := cs1.GetRoundState().Validators.GetProposer() prop := cs1.GetRoundState().Validators.GetProposer()
if !bytes.Equal(prop.Address, vss[(i+2)%len(vss)].GetAddress()) { 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() rs := cs1.GetRoundState()
@ -247,7 +248,7 @@ func TestFullRound1(t *testing.T) {
// grab proposal // grab proposal
re := <-propCh 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 <-voteCh // wait for prevote
// NOTE: voteChan cap of 0 ensures we can complete this // NOTE: voteChan cap of 0 ensures we can complete this
@ -344,7 +345,7 @@ func TestLockNoPOL(t *testing.T) {
cs1.startRoutines(0) cs1.startRoutines(0)
re := <-proposalCh 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() theBlockHash := rs.ProposalBlock.Hash()
<-voteCh // prevote <-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 // now we're on a new round and not the proposer, so wait for timeout
re = <-timeoutProposeCh 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 { if rs.ProposalBlock != nil {
panic("Expected proposal block to be nil") panic("Expected proposal block to be nil")
@ -428,11 +429,11 @@ func TestLockNoPOL(t *testing.T) {
incrementRound(vs2) incrementRound(vs2)
re = <-proposalCh 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 // now we're on a new round and are the proposer
if !bytes.Equal(rs.ProposalBlock.Hash(), rs.LockedBlock.Hash()) { 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 <-voteCh // prevote
@ -515,7 +516,7 @@ func TestLockPOLRelock(t *testing.T) {
<-newRoundCh <-newRoundCh
re := <-proposalCh 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() theBlockHash := rs.ProposalBlock.Hash()
<-voteCh // prevote <-voteCh // prevote
@ -591,7 +592,7 @@ func TestLockPOLRelock(t *testing.T) {
be := <-newBlockCh be := <-newBlockCh
b := be.(types.TMEventData).Unwrap().(types.EventDataNewBlockHeader) b := be.(types.TMEventData).Unwrap().(types.EventDataNewBlockHeader)
re = <-newRoundCh 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 { if rs.Height != 2 {
panic("Expected height to increment") panic("Expected height to increment")
} }
@ -627,7 +628,7 @@ func TestLockPOLUnlock(t *testing.T) {
startTestRound(cs1, cs1.Height, 0) startTestRound(cs1, cs1.Height, 0)
<-newRoundCh <-newRoundCh
re := <-proposalCh 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() theBlockHash := rs.ProposalBlock.Hash()
<-voteCh // prevote <-voteCh // prevote
@ -653,7 +654,7 @@ func TestLockPOLUnlock(t *testing.T) {
// timeout to new round // timeout to new round
re = <-timeoutWaitCh 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() lockedBlockHash := rs.LockedBlock.Hash()
//XXX: this isnt guaranteed to get there before the timeoutPropose ... //XXX: this isnt guaranteed to get there before the timeoutPropose ...
@ -713,7 +714,7 @@ func TestLockPOLSafety1(t *testing.T) {
startTestRound(cs1, cs1.Height, 0) startTestRound(cs1, cs1.Height, 0)
<-newRoundCh <-newRoundCh
re := <-proposalCh re := <-proposalCh
rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState) rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
propBlock := rs.ProposalBlock propBlock := rs.ProposalBlock
<-voteCh // prevote <-voteCh // prevote
@ -761,7 +762,7 @@ func TestLockPOLSafety1(t *testing.T) {
re = <-proposalCh 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 { if rs.LockedBlock != nil {
panic("we should not be locked!") panic("we should not be locked!")
@ -848,7 +849,7 @@ func TestLockPOLSafety2(t *testing.T) {
incrementRound(vs2, vs3, vs4) incrementRound(vs2, vs3, vs4)
cs1.updateRoundStep(0, RoundStepPrecommitWait) cs1.updateRoundStep(0, cstypes.RoundStepPrecommitWait)
t.Log("### ONTO Round 1") t.Log("### ONTO Round 1")
// jump in at round 1 // jump in at round 1
@ -929,7 +930,7 @@ func TestSlashingPrevotes(t *testing.T) {
re := <-proposalCh re := <-proposalCh
<-voteCh // prevote <-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 // 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 // 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) startTestRound(cs1, cs1.Height, 0)
<-newRoundCh <-newRoundCh
re := <-proposalCh re := <-proposalCh
rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*RoundState) rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
propBlock := rs.ProposalBlock propBlock := rs.ProposalBlock
propBlockParts := propBlock.MakePartSet(partSize) propBlockParts := propBlock.MakePartSet(partSize)
@ -1032,7 +1033,7 @@ func TestHalt1(t *testing.T) {
// timeout to new round // timeout to new round
<-timeoutWaitCh <-timeoutWaitCh
re = <-newRoundCh 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") t.Log("### ONTO ROUND 1")
/*Round2 /*Round2
@ -1050,7 +1051,7 @@ func TestHalt1(t *testing.T) {
// receiving that precommit should take us straight to commit // receiving that precommit should take us straight to commit
<-newBlockCh <-newBlockCh
re = <-newRoundCh 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 { if rs.Height != 2 {
panic("expected height to increment") panic("expected height to increment")

View File

@ -1,11 +1,11 @@
package consensus package types
import ( import (
"strings" "strings"
"sync" "sync"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
. "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
) )
type RoundVoteSet struct { type RoundVoteSet struct {
@ -76,7 +76,7 @@ func (hvs *HeightVoteSet) SetRound(round int) {
hvs.mtx.Lock() hvs.mtx.Lock()
defer hvs.mtx.Unlock() defer hvs.mtx.Unlock()
if hvs.round != 0 && (round < hvs.round+1) { 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++ { for r := hvs.round + 1; r <= round; r++ {
if _, ok := hvs.roundVoteSets[r]; ok { if _, ok := hvs.roundVoteSets[r]; ok {
@ -89,7 +89,7 @@ func (hvs *HeightVoteSet) SetRound(round int) {
func (hvs *HeightVoteSet) addRound(round int) { func (hvs *HeightVoteSet) addRound(round int) {
if _, ok := hvs.roundVoteSets[round]; ok { 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) // log.Debug("addRound(round)", "round", round)
prevotes := types.NewVoteSet(hvs.chainID, hvs.height, round, types.VoteTypePrevote, hvs.valSet) 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: case types.VoteTypePrecommit:
return rvs.Precommits return rvs.Precommits
default: default:
PanicSanity(Fmt("Unexpected vote type %X", type_)) cmn.PanicSanity(cmn.Fmt("Unexpected vote type %X", type_))
return nil return nil
} }
} }
@ -194,7 +194,7 @@ func (hvs *HeightVoteSet) StringIndented(indent string) string {
voteSetString = roundVoteSet.Precommits.StringShort() voteSetString = roundVoteSet.Precommits.StringShort()
vsStrings = append(vsStrings, voteSetString) vsStrings = append(vsStrings, voteSetString)
} }
return Fmt(`HeightVoteSet{H:%v R:0~%v return cmn.Fmt(`HeightVoteSet{H:%v R:0~%v
%s %v %s %v
%s}`, %s}`,
hvs.height, hvs.round, hvs.height, hvs.round,

View File

@ -1,14 +1,17 @@
package consensus package types
import ( import (
"testing" "testing"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
. "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
) )
var config *cfg.Config // NOTE: must be reset for each _test.go file
func init() { func init() {
config = ResetConfig("consensus_height_vote_set_test") config = cfg.ResetTestRoot("consensus_height_vote_set_test")
} }
func TestPeerCatchupRounds(t *testing.T) { func TestPeerCatchupRounds(t *testing.T) {
@ -57,7 +60,7 @@ func makeVoteHR(t *testing.T, height, round int, privVals []*types.PrivValidator
chainID := config.ChainID chainID := config.ChainID
err := privVal.SignVote(chainID, vote) err := privVal.SignVote(chainID, vote)
if err != nil { if err != nil {
panic(Fmt("Error signing vote: %v", err)) panic(cmn.Fmt("Error signing vote: %v", err))
return nil return nil
} }
return vote return vote

View File

@ -0,0 +1,57 @@
package types
import (
"fmt"
"time"
"github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tmlibs/common"
)
//-----------------------------------------------------------------------------
// PeerRoundState contains the known state of a peer.
// NOTE: Read-only when returned by PeerState.GetRoundState().
type PeerRoundState struct {
Height 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)
}

126
consensus/types/state.go Normal file
View File

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

View File

@ -1,7 +1,7 @@
package consensus package consensus
import ( import (
. "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
) )
// kind of arbitrary // kind of arbitrary
@ -10,4 +10,4 @@ var Major = "0" //
var Minor = "2" // replay refactor var Minor = "2" // replay refactor
var Revision = "2" // validation -> commit 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)

View File

@ -6,7 +6,7 @@ import (
wire "github.com/tendermint/go-wire" wire "github.com/tendermint/go-wire"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
auto "github.com/tendermint/tmlibs/autofile" 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 // 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 // give it a mode so it's either reading or appending - must read to end to start appending again
type WAL struct { type WAL struct {
BaseService cmn.BaseService
group *auto.Group group *auto.Group
light bool // ignore block parts light bool // ignore block parts
@ -49,7 +49,7 @@ func NewWAL(walFile string, light bool) (*WAL, error) {
group: group, group: group,
light: light, light: light,
} }
wal.BaseService = *NewBaseService(nil, "WAL", wal) wal.BaseService = *cmn.NewBaseService(nil, "WAL", wal)
return wal, nil return wal, nil
} }
@ -86,19 +86,19 @@ func (wal *WAL) Save(wmsg WALMessage) {
var wmsgBytes = wire.JSONBytes(TimedWALMessage{time.Now(), wmsg}) var wmsgBytes = wire.JSONBytes(TimedWALMessage{time.Now(), wmsg})
err := wal.group.WriteLine(string(wmsgBytes)) err := wal.group.WriteLine(string(wmsgBytes))
if err != nil { 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 // TODO: only flush when necessary
if err := wal.group.Flush(); err != nil { 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) { 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 // TODO: only flush when necessary
if err := wal.group.Flush(); err != nil { 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))
} }
} }

View File

@ -142,6 +142,13 @@ It is unlikely that you will need to implement a client. For details of
our client, see our client, see
`here <https://github.com/tendermint/abci/tree/master/client>`__. `here <https://github.com/tendermint/abci/tree/master/client>`__.
Most of the examples below are from `dummy application
<https://github.com/tendermint/abci/blob/master/example/dummy/dummy.go>`__,
which is a part of the abci repo. `persistent_dummy application
<https://github.com/tendermint/abci/blob/master/example/dummy/persistent_dummy.go>`__
is used to show ``BeginBlock``, ``EndBlock`` and ``InitChain``
example implementations.
Blockchain Protocol Blockchain Protocol
------------------- -------------------
@ -187,6 +194,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 in the block, and re-run the rest using CheckTx against the post-Commit
mempool state. mempool state.
::
func (app *DummyApplication) CheckTx(tx []byte) types.Result {
return types.OK
}
Consensus Connection Consensus Connection
~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
@ -215,6 +228,19 @@ 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 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. merkle root of the data returned by the DeliverTx requests, or both.
::
// tx is either "key=value" or just arbitrary bytes
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)
}
return types.OK
}
Commit Commit
^^^^^^ ^^^^^^
@ -228,7 +254,7 @@ Commit, or there will be deadlock. Note also that all remaining
transactions in the mempool are replayed on the mempool connection transactions in the mempool are replayed on the mempool connection
(CheckTx) following a commit. (CheckTx) following a commit.
The Commit response includes 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 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 block. It can be used to provide easily verified Merkle-proofs of the
state of the application. state of the application.
@ -237,6 +263,13 @@ 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 option to have all transactions replayed from some previous block is the
job of the `Handshake <#handshake>`__. job of the `Handshake <#handshake>`__.
::
func (app *DummyApplication) Commit() types.Result {
hash := app.state.Hash()
return types.NewResultOK(hash, "")
}
BeginBlock BeginBlock
^^^^^^^^^^ ^^^^^^^^^^
@ -248,6 +281,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 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. 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 EndBlock
^^^^^^^^ ^^^^^^^^
@ -260,6 +304,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 validator set. Note validator set changes are only available in v0.8.0
and up. and up.
::
// Update the validator set
func (app *PersistentDummyApplication) EndBlock(height uint64) (resEndBlock types.ResponseEndBlock) {
return types.ResponseEndBlock{Diffs: app.changes}
}
Query Connection Query Connection
~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
@ -281,6 +332,34 @@ cause Tendermint to not connect to the corresponding peer:
Note: these query formats are subject to change! Note: these query formats are subject to change!
::
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 Handshake
~~~~~~~~~ ~~~~~~~~~
@ -297,3 +376,28 @@ the app are synced to the latest block height.
If the app returns a LastBlockHeight of 0, Tendermint will just replay If the app returns a LastBlockHeight of 0, Tendermint will just replay
all blocks. all blocks.
::
func (app *DummyApplication) Info(req types.RequestInfo) (resInfo types.ResponseInfo) {
return types.ResponseInfo{Data: cmn.Fmt("{\"size\":%v}", app.state.Size())}
}
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)
}
}
}

View File

@ -36,37 +36,50 @@ Ethermint
The go-ethereum state machine run as a ABCI app, written in Go, `authored by Tendermint <https://github.com/tendermint/ethermint>`__. The go-ethereum state machine run as a ABCI app, written in Go, `authored by Tendermint <https://github.com/tendermint/ethermint>`__.
IAVL
^^^^
Merkle AVL Tree Immutable AVL+ tree with Merkle proofs, Written in Go, `authored by Tendermint <https://github.com/tendermint/iavl>`__.
^^^^^^^^^^^^^^^
The following are implementations of the Tendermint IAVL tree as an ABCI application Lotion
Merkleeyes
~~~~~~~~~~
Written in Go, `authored by Tendermint <https://github.com/tendermint/merkleeyes>`__.
MerkleTree
~~~~~~~~~~
Written in Java, `authored by jTendermint <https://github.com/jTendermint/MerkleTree>`__.
TMChat
^^^^^^ ^^^^^^
P2P chat using Tendermint, written in Java, `authored by woldposd <https://github.com/wolfposd/TMChat>`__. A Javascript microframework for building blockchain applications with Tendermint, written in Javascript, `authored by Judd Keppel of Tendermint <https://github.com/keppel/lotion>`__. See also `lotion-chat <https://github.com/keppel/lotion-chat>`__ and `lotion-coin <https://github.com/keppel/lotion-coin>`__ apps written using Lotion.
MerkleTree
^^^^^^^^^^
Immutable AVL+ tree with Merkle proofs, Written in Java, `authored by jTendermint <https://github.com/jTendermint/MerkleTree>`__.
Passchain
^^^^^^^^^
Passchain is a tool to securely store and share passwords, tokens and other short secrets, `authored by trusch <https://github.com/trusch/passchain>`__.
Passwerk Passwerk
^^^^^^^^ ^^^^^^^^
Encrypted storage web-utility backed by Tendermint, written in Go, `authored by Rigel Rozanski <https://github.com/rigelrozanski/passwerk>`__. Encrypted storage web-utility backed by Tendermint, written in Go, `authored by Rigel Rozanski <https://github.com/rigelrozanski/passwerk>`__.
Py-Tendermint
^^^^^^^^^^^^^
A Python microframework for building blockchain applications with Tendermint, written in Python, `authored by Dave Bryson <https://github.com/davebryson/py-tendermint>`__.
Stratumn
^^^^^^^^
SDK for "Proof-of-Process" networks, written in Go, `authored by the Stratumn team <https://github.com/stratumn/sdk>`__.
TMChat
^^^^^^
P2P chat using Tendermint, written in Java, `authored by wolfposd <https://github.com/wolfposd/TMChat>`__.
ABCI Servers ABCI Servers
------------ ------------
+-------------------------------------------------------------+--------------------+--------------+ +-------------------------------------------------------------+--------------------+--------------+
| **Name** | **Author** | **Language** | | **Name** | **Author** | **Language** |
| | | | | | | |
@ -75,7 +88,9 @@ ABCI Servers
+-------------------------------------------------------------+--------------------+--------------+ +-------------------------------------------------------------+--------------------+--------------+
| `js abci <https://github.com/tendermint/js-abci>`__ | Tendermint | Javascript | | `js abci <https://github.com/tendermint/js-abci>`__ | Tendermint | Javascript |
+-------------------------------------------------------------+--------------------+--------------+ +-------------------------------------------------------------+--------------------+--------------+
| `cpp-tmsp <https://github.com/mdyring/cpp-tmsp>`__ | Martin Dyring | C++ | | `cpp-tmsp <https://github.com/block-finance/cpp-abci>`__ | Martin Dyring | C++ |
+-------------------------------------------------------------+--------------------+--------------+
| `c-abci <https://github.com/chainx-org/c-abci>`__ | ChainX | C |
+-------------------------------------------------------------+--------------------+--------------+ +-------------------------------------------------------------+--------------------+--------------+
| `jabci <https://github.com/jTendermint/jabci>`__ | jTendermint | Java | | `jabci <https://github.com/jTendermint/jabci>`__ | jTendermint | Java |
+-------------------------------------------------------------+--------------------+--------------+ +-------------------------------------------------------------+--------------------+--------------+
@ -85,6 +100,12 @@ ABCI Servers
+-------------------------------------------------------------+--------------------+--------------+ +-------------------------------------------------------------+--------------------+--------------+
| `abci_server <https://github.com/KrzysiekJ/abci_server>`__ | Krzysztof Jurewicz | Erlang | | `abci_server <https://github.com/KrzysiekJ/abci_server>`__ | Krzysztof Jurewicz | Erlang |
+-------------------------------------------------------------+--------------------+--------------+ +-------------------------------------------------------------+--------------------+--------------+
| `rust-tsp <https://github.com/tendermint/rust-tsp>`__   | Adrian Brink | Rust       |
+-------------------------------------------------------------+--------------------+--------------+
| `hs-abci <https://github.com/albertov/hs-abci>`__ | Alberto Gonzalez | Haskell |
+-------------------------------------------------------------+--------------------+--------------+
| `haskell-abci <https://github.com/cwgoes/haskell-abci>`__ | Christoper Goes | Haskell |
+-------------------------------------------------------------+--------------------+--------------+
Deployment Tools Deployment Tools
---------------- ----------------

View File

@ -73,7 +73,7 @@ Tendermint before, use:
:: ::
tendermint init tendermint init
tendermint node tendermint node
If you have used Tendermint, you may want to reset the data for a new 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 The ``98`` is a type-byte, and can be ignored (it's useful for
serializing and deserializing arbitrary json). Otherwise, this result is 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 The result should look like:
future release can be left out. 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 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 (``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 Try some other transactions and queries to make sure everything is
working! 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 > 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: But if we send a ``1``, it works again:
:: ::
> curl localhost:46657/broadcast_tx_commit?tx=0x01 > 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 For more details on the ``broadcast_tx`` API, see `the guide on using
Tendermint <./using-tendermint.html>`__. Tendermint <./using-tendermint.html>`__.

165
docs/how-to-read-logs.rst Normal file
View File

@ -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, then precommits and finally
have a chance to commit a block. For details, please refer to `Consensus
Overview
<introduction.html#consensus-overview>`__
or `Byzantine Consensus Algorithm
<specification.html>`__.
::
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
<app-development.html#abci-design>`__,
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
<https://github.com/tendermint/abci/tree/master/client>`__.
- ``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
<https://github.com/tendermint/tendermint/blob/master/types/events.go>`__.
You can subscribe to them by calling ``subscribe`` RPC method.
Refer to `RPC docs
<specification/rpc.html>`__
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
<https://github.com/tendermint/tendermint/blob/56c60fba2381e4ac41d2ae38a1eb6569acfbc095/p2p/README.md>`__.
- ``rpc``
`Tendermint's RPC <specification/rpc.html>`__.
- ``rpc-server``
RPC server. For implementation details, please read the `README <https://github.com/tendermint/tendermint/blob/master/rpc/lib/README.md>`__.
- ``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.

View File

@ -15,8 +15,6 @@ Welcome to Tendermint!
Tendermint 101 Tendermint 101
-------------- --------------
.. maxdepth set to 2 for sexinesss
.. but use 4 to upgrade overall documentation
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
@ -25,9 +23,19 @@ Tendermint 101
getting-started.rst getting-started.rst
using-tendermint.rst using-tendermint.rst
Tendermint Ecosystem
--------------------
.. toctree::
:maxdepth: 2
ecosystem.rst
Tendermint Tools Tendermint Tools
---------------- ----------------
.. the tools/ files are pulled in from the tools repo
.. see the bottom of conf.py
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
@ -38,15 +46,6 @@ Tendermint Tools
tools/terraform-digitalocean.rst tools/terraform-digitalocean.rst
tools/benchmarking-and-monitoring.rst tools/benchmarking-and-monitoring.rst
Tendermint Ecosystem
--------------------
.. toctree::
:maxdepth: 2
ecosystem.rst
Tendermint 102 Tendermint 102
-------------- --------------
@ -56,6 +55,7 @@ Tendermint 102
abci-cli.rst abci-cli.rst
app-architecture.rst app-architecture.rst
app-development.rst app-development.rst
how-to-read-logs.rst
Tendermint 201 Tendermint 201
-------------- --------------

View File

@ -2,19 +2,19 @@
Specification Specification
############# #############
Here you'll find details of the Tendermint specification. See `the spec repo <https://github.com/tendermint/spec>`__ for upcoming material. Tendermint's types are produced by `godoc <https://godoc.org/github.com/tendermint/tendermint/types>`__ Here you'll find details of the Tendermint specification. See `the spec repo <https://github.com/tendermint/spec>`__ for upcoming material. Tendermint's types are produced by `godoc <https://godoc.org/github.com/tendermint/tendermint/types>`__.
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
specification/block-structure.rst specification/block-structure.rst
specification/byzantine-consensus-algorithm.rst specification/byzantine-consensus-algorithm.rst
specification/configuration.rst specification/configuration.rst
specification/fast-sync.rst specification/fast-sync.rst
specification/genesis.rst specification/genesis.rst
specification/light-client-protocol.rst specification/light-client-protocol.rst
specification/merkle.rst specification/merkle.rst
specification/rpc.rst specification/rpc.rst
specification/secure-p2p.rst specification/secure-p2p.rst
specification/validators.rst specification/validators.rst
specification/wire-protocol.rst specification/wire-protocol.rst

View File

@ -30,14 +30,16 @@ The main config parameters are defined
- ``consensus.max_block_size_txs``: Maximum number of block txs. - ``consensus.max_block_size_txs``: Maximum number of block txs.
*Default*: ``10000`` *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 - ``consensus.timeout_*``: Various consensus timeout parameters
**TODO**
- ``consensus.wal_file``: Consensus state WAL. *Default*: - ``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 - ``consensus.wal_light``: Whether to use light-mode for Consensus
state WAL. *Default*: ``false`` state WAL. *Default*: ``false``
- ``mempool.*``: Various mempool parameters **TODO** - ``mempool.*``: Various mempool parameters
- ``p2p.addr_book_file``: Peer address book. *Default*: - ``p2p.addr_book_file``: Peer address book. *Default*:
``"$TMHOME/addrbook.json"``. **NOT USED** ``"$TMHOME/addrbook.json"``. **NOT USED**

View File

@ -116,7 +116,7 @@ new blockchain will not make any blocks.
Configuration 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>`__. `the documentation <./specification/configuration.html>`__.
Notable options include the socket address of the application Notable options include the socket address of the application
@ -126,6 +126,38 @@ Notable options include the socket address of the application
Some fields from the config file can be overwritten with flags. 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 Broadcast API
------------- -------------

70
glide.lock generated
View File

@ -1,16 +1,18 @@
hash: e3649cac7b1b9a23c024a9d1bbebd5a147861d55da2bca77c95129b6021850b4 hash: 816d84782ab66637e02bd0a3c7f652a9a31f9b88e3ae11438c5bf641cf585f19
updated: 2017-09-22T13:24:29.443800586-04:00 updated: 2017-10-02T23:32:49.162422718-04:00
imports: imports:
- name: github.com/btcsuite/btcd - name: github.com/btcsuite/btcd
version: 4803a8291c92a1d2d41041b942a9a9e37deab065 version: b8df516b4b267acf2de46be593a9d948d1d2c420
subpackages: subpackages:
- btcec - btcec
- name: github.com/btcsuite/fastsha256
version: 637e656429416087660c84436a2a035d69d54e2e
- name: github.com/ebuchman/fail-test - name: github.com/ebuchman/fail-test
version: 95f809107225be108efcf10a3509e4ea6ceef3c4 version: 95f809107225be108efcf10a3509e4ea6ceef3c4
- name: github.com/fsnotify/fsnotify - name: github.com/fsnotify/fsnotify
version: 4da3e2cfbabc9f751898f250b49f2439785783a1 version: 4da3e2cfbabc9f751898f250b49f2439785783a1
- name: github.com/go-kit/kit - name: github.com/go-kit/kit
version: 0d313fb5fb3a94d87d61e6434785264e87a5d740 version: d67bb4c202e3b91377d1079b110a6c9ce23ab2f8
subpackages: subpackages:
- log - log
- log/level - log/level
@ -24,25 +26,22 @@ imports:
- name: github.com/go-playground/universal-translator - name: github.com/go-playground/universal-translator
version: 71201497bace774495daed26a3874fd339e0b538 version: 71201497bace774495daed26a3874fd339e0b538
- name: github.com/go-stack/stack - name: github.com/go-stack/stack
version: 817915b46b97fd7bb80e8ab6b69f01a53ac3eebf version: 100eb0c0a9c5b306ca2fb4f165df21d80ada4b82
- name: github.com/gogo/protobuf - name: github.com/gogo/protobuf
version: 2adc21fd136931e0388e278825291678e1d98309 version: f7f1376d9d231a646d4e62fe1075623ced6db327
subpackages: subpackages:
- proto - proto
- name: github.com/golang/protobuf - name: github.com/golang/protobuf
version: 130e6b02ab059e7b717a096f397c5b60111cae74 version: 18c9bb3261723cd5401db4d0c9fbc5c3b6c70fe8
subpackages: subpackages:
- proto - proto
- ptypes
- ptypes/any - ptypes/any
- ptypes/duration
- ptypes/timestamp
- name: github.com/golang/snappy - name: github.com/golang/snappy
version: 553a641470496b2327abcac10b36396bd98e45c9 version: 553a641470496b2327abcac10b36396bd98e45c9
- name: github.com/gorilla/websocket - name: github.com/gorilla/websocket
version: 6f34763140ed8887aed6a044912009832b4733d7 version: a91eba7f97777409bc2c443f5534d41dd20c5720
- name: github.com/hashicorp/hcl - name: github.com/hashicorp/hcl
version: 68e816d1c783414e79bc65b3994d9ab6b0a722ab version: 392dba7d905ed5d04a5794ba89f558b27e2ba1ca
subpackages: subpackages:
- hcl/ast - hcl/ast
- hcl/parser - hcl/parser
@ -59,31 +58,33 @@ imports:
- name: github.com/kr/logfmt - name: github.com/kr/logfmt
version: b84e30acd515aadc4b783ad4ff83aff3299bdfe0 version: b84e30acd515aadc4b783ad4ff83aff3299bdfe0
- name: github.com/magiconair/properties - name: github.com/magiconair/properties
version: 8d7837e64d3c1ee4e54a880c5a920ab4316fc90a version: 51463bfca2576e06c62a8504b5c0f06d61312647
- name: github.com/mitchellh/mapstructure - name: github.com/mitchellh/mapstructure
version: d0303fe809921458f417bcf828397a65db30a7e4 version: cc8532a8e9a55ea36402aa21efdf403a60d34096
- name: github.com/pelletier/go-buffruneio
version: c37440a7cf42ac63b919c752ca73a85067e05992
- name: github.com/pelletier/go-toml - name: github.com/pelletier/go-toml
version: 1d6b12b7cb290426e27e6b4e38b89fcda3aeef03 version: 13d49d4606eb801b8f01ae542b4afc4c6ee3d84a
- name: github.com/pkg/errors - name: github.com/pkg/errors
version: 645ef00459ed84a119197bfb8d8205042c6df63d version: 645ef00459ed84a119197bfb8d8205042c6df63d
- name: github.com/rcrowley/go-metrics - name: github.com/rcrowley/go-metrics
version: 1f30fe9094a513ce4c700b9a54458bbb0c96996c version: 1f30fe9094a513ce4c700b9a54458bbb0c96996c
- name: github.com/spf13/afero - name: github.com/spf13/afero
version: ee1bd8ee15a1306d1f9201acc41ef39cd9f99a1b version: 9be650865eab0c12963d8753212f4f9c66cdcf12
subpackages: subpackages:
- mem - mem
- name: github.com/spf13/cast - name: github.com/spf13/cast
version: acbeb36b902d72a7a4c18e8f3241075e7ab763e4 version: acbeb36b902d72a7a4c18e8f3241075e7ab763e4
- name: github.com/spf13/cobra - name: github.com/spf13/cobra
version: b78744579491c1ceeaaa3b40205e56b0591b93a3 version: 4cdb38c072b86bf795d2c81de50784d9fdd6eb77
- name: github.com/spf13/jwalterweatherman - name: github.com/spf13/jwalterweatherman
version: 12bd96e66386c1960ab0f74ced1362f66f552f7b version: 8f07c835e5cc1450c082fe3a439cf87b0cbb2d99
- name: github.com/spf13/pflag - name: github.com/spf13/pflag
version: 7aff26db30c1be810f9de5038ec5ef96ac41fd7c version: e57e3eeb33f795204c1ca35f56c44f83227c6e66
- name: github.com/spf13/viper - name: github.com/spf13/viper
version: 25b30aa063fc18e48662b86996252eabdcf2f0c7 version: 0967fc9aceab2ce9da34061253ac10fb99bba5b2
- name: github.com/syndtr/goleveldb - name: github.com/syndtr/goleveldb
version: b89cc31ef7977104127d34c1bd31ebd1a9db2199 version: 8c81ea47d4c41a385645e133e15510fc6a2a74b4
subpackages: subpackages:
- leveldb - leveldb
- leveldb/cache - leveldb/cache
@ -122,7 +123,7 @@ imports:
subpackages: subpackages:
- iavl - iavl
- name: github.com/tendermint/tmlibs - name: github.com/tendermint/tmlibs
version: 9997e3a3b46db1d2f88aa9816ed0e7915dad6ac1 version: 096dcb90e60aa00b748b3fe49a4b95e48ebf1e13
subpackages: subpackages:
- autofile - autofile
- cli - cli
@ -136,7 +137,7 @@ imports:
- merkle - merkle
- test - test
- name: golang.org/x/crypto - name: golang.org/x/crypto
version: 7d9177d70076375b9a59c8fde23d52d9c4a7ecd5 version: c7af5bf2638a1164f2eb5467c39c6cffbd13a02e
subpackages: subpackages:
- curve25519 - curve25519
- nacl/box - nacl/box
@ -147,7 +148,7 @@ imports:
- ripemd160 - ripemd160
- salsa20/salsa - salsa20/salsa
- name: golang.org/x/net - name: golang.org/x/net
version: 0744d001aa8470aaa53df28d32e5ceeb8af9bd70 version: feeb485667d1fdabe727840fe00adc22431bc86e
subpackages: subpackages:
- context - context
- http2 - http2
@ -157,46 +158,43 @@ imports:
- lex/httplex - lex/httplex
- trace - trace
- name: golang.org/x/sys - name: golang.org/x/sys
version: 429f518978ab01db8bb6f44b66785088e7fba58b version: e62c3de784db939836898e5c19ffd41bece347da
subpackages: subpackages:
- unix - unix
- name: golang.org/x/text - name: golang.org/x/text
version: 1cbadb444a806fd9430d14ad08967ed91da4fa0a version: 470f45bf29f4147d6fbd7dfd0a02a848e49f5bf4
subpackages: subpackages:
- secure/bidirule - secure/bidirule
- transform - transform
- unicode/bidi - unicode/bidi
- unicode/norm - unicode/norm
- name: google.golang.org/genproto - name: google.golang.org/genproto
version: 1e559d0a00eef8a9a43151db4665280bd8dd5886 version: 411e09b969b1170a9f0c467558eb4c4c110d9c77
subpackages: subpackages:
- googleapis/rpc/status - googleapis/rpc/status
- name: google.golang.org/grpc - name: google.golang.org/grpc
version: d4b75ebd4f9f8c4a2b1cdadbdbe0d7920431ccca version: 844f573616520565fdc6fb4db242321b5456fd6d
subpackages: subpackages:
- balancer
- codes - codes
- connectivity
- credentials - credentials
- grpclb/grpc_lb_v1/messages - grpclb/grpc_lb_v1
- grpclog - grpclog
- internal - internal
- keepalive - keepalive
- metadata - metadata
- naming - naming
- peer - peer
- resolver
- stats - stats
- status - status
- tap - tap
- transport - transport
- name: gopkg.in/go-playground/validator.v9 - name: gopkg.in/go-playground/validator.v9
version: a021b2ec9a8a8bb970f3f15bc42617cb520e8a64 version: 6d8c18553ea1ac493d049edd6f102f52e618f085
- name: gopkg.in/yaml.v2 - name: gopkg.in/yaml.v2
version: eb3733d160e74a9c7e442f435eb3bea458e1d19f version: cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b
testImports: testImports:
- name: github.com/davecgh/go-spew - name: github.com/davecgh/go-spew
version: 04cdfd42973bb9c8589fd6a731800cf222fde1a9 version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9
subpackages: subpackages:
- spew - spew
- name: github.com/pmezard/go-difflib - name: github.com/pmezard/go-difflib
@ -204,7 +202,7 @@ testImports:
subpackages: subpackages:
- difflib - difflib
- name: github.com/stretchr/testify - name: github.com/stretchr/testify
version: 890a5c3458b43e6104ff5da8dfa139d013d77544 version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0
subpackages: subpackages:
- assert - assert
- require - require

View File

@ -30,7 +30,7 @@ import:
subpackages: subpackages:
- iavl - iavl
- package: github.com/tendermint/tmlibs - package: github.com/tendermint/tmlibs
version: ~0.3.1 version: ~0.3.2
subpackages: subpackages:
- autofile - autofile
- cli - cli

View File

@ -24,7 +24,7 @@ import (
The mempool pushes new txs onto the proxyAppConn. The mempool pushes new txs onto the proxyAppConn.
It gets a stream of (req, res) tuples from the proxy. 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 Multiple concurrent go-routines can traverse this linked-list
safely by calling .NextWait() on each element. safely by calling .NextWait() on each element.

View File

@ -1,8 +1,9 @@
package node package node
import ( import (
"github.com/tendermint/go-crypto"
"time" "time"
"github.com/tendermint/go-crypto"
) )
type NodeID struct { type NodeID struct {

View File

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

View File

@ -196,7 +196,7 @@ func getUPNPExternalAddress(externalPort, internalPort int, logger log.Logger) *
return NewNetAddressIPPort(ext, uint16(externalPort)) 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 { func getNaiveExternalAddress(port int, settleForLocal bool, logger log.Logger) *NetAddress {
addrs, err := net.InterfaceAddrs() addrs, err := net.InterfaceAddrs()
if err != nil { if err != nil {

View File

@ -7,6 +7,7 @@ import (
"time" "time"
"github.com/pkg/errors" "github.com/pkg/errors"
crypto "github.com/tendermint/go-crypto" crypto "github.com/tendermint/go-crypto"
wire "github.com/tendermint/go-wire" wire "github.com/tendermint/go-wire"
cmn "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"

View File

@ -1,5 +0,0 @@
# `tendermint/p2p/upnp`
## Resources
* http://www.upnp-hacks.org/upnp.html

View File

@ -1,11 +1,9 @@
/* // Taken from taipei-torrent.
Taken from taipei-torrent // Just enough UPnP to be able to forward ports
// For more information, see: http://www.upnp-hacks.org/upnp.html
Just enough UPnP to be able to forward ports
*/
package upnp 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 ( import (
"bytes" "bytes"

View File

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"github.com/pkg/errors" "github.com/pkg/errors"
data "github.com/tendermint/go-wire/data" data "github.com/tendermint/go-wire/data"
ctypes "github.com/tendermint/tendermint/rpc/core/types" ctypes "github.com/tendermint/tendermint/rpc/core/types"
rpcclient "github.com/tendermint/tendermint/rpc/lib/client" rpcclient "github.com/tendermint/tendermint/rpc/lib/client"
@ -226,7 +227,9 @@ func (w *WSEvents) Start() (bool, error) {
st, err := w.EventSwitch.Start() st, err := w.EventSwitch.Start()
// if we did start, then OnStart here... // if we did start, then OnStart here...
if st && err == nil { 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() _, err = ws.Start()
if err == nil { if err == nil {
w.ws = ws w.ws = ws
@ -305,6 +308,14 @@ func (w *WSEvents) RemoveListener(listenerID string) {
w.EventSwitch.RemoveListener(listenerID) 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 // eventListener is an infinite loop pulling all websocket events
// and pushing them to the EventSwitch. // and pushing them to the EventSwitch.
// //

View File

@ -8,13 +8,13 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/tendermint/abci/example/dummy" "github.com/tendermint/abci/example/dummy"
abci "github.com/tendermint/abci/types" abci "github.com/tendermint/abci/types"
data "github.com/tendermint/go-wire/data" data "github.com/tendermint/go-wire/data"
"github.com/tendermint/tendermint/rpc/client/mock"
ctypes "github.com/tendermint/tendermint/rpc/core/types" ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
"github.com/tendermint/tendermint/rpc/client/mock"
) )
func TestABCIMock(t *testing.T) { func TestABCIMock(t *testing.T) {

View File

@ -5,10 +5,10 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "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" "github.com/tendermint/tendermint/rpc/client/mock"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
) )
func TestStatus(t *testing.T) { func TestStatus(t *testing.T) {

View File

@ -6,7 +6,8 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "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" "github.com/tendermint/tendermint/rpc/client"
rpctest "github.com/tendermint/tendermint/rpc/test" rpctest "github.com/tendermint/tendermint/rpc/test"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"

View File

@ -5,7 +5,7 @@ import (
ctypes "github.com/tendermint/tendermint/rpc/core/types" ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
. "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
) )
// Get block headers for minHeight <= height <= maxHeight. // Get block headers for minHeight <= height <= maxHeight.
@ -65,12 +65,12 @@ func BlockchainInfo(minHeight, maxHeight int) (*ctypes.ResultBlockchainInfo, err
if maxHeight == 0 { if maxHeight == 0 {
maxHeight = blockStore.Height() maxHeight = blockStore.Height()
} else { } else {
maxHeight = MinInt(blockStore.Height(), maxHeight) maxHeight = cmn.MinInt(blockStore.Height(), maxHeight)
} }
if minHeight == 0 { if minHeight == 0 {
minHeight = MaxInt(1, maxHeight-20) minHeight = cmn.MaxInt(1, maxHeight-20)
} else { } else {
minHeight = MaxInt(minHeight, maxHeight-20) minHeight = cmn.MaxInt(minHeight, maxHeight-20)
} }
logger.Debug("BlockchainInfoHandler", "maxHeight", maxHeight, "minHeight", minHeight) logger.Debug("BlockchainInfoHandler", "maxHeight", maxHeight, "minHeight", minHeight)

View File

@ -1,8 +1,8 @@
package core package core
import ( import (
wire "github.com/tendermint/go-wire"
cm "github.com/tendermint/tendermint/consensus" cm "github.com/tendermint/tendermint/consensus"
cstypes "github.com/tendermint/tendermint/consensus/types"
ctypes "github.com/tendermint/tendermint/rpc/core/types" ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
) )
@ -82,14 +82,11 @@ func Validators(heightPtr *int) (*ctypes.ResultValidators, error) {
// } // }
// ``` // ```
func DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) { func DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) {
roundState := consensusState.GetRoundState() peerRoundStates := make(map[string]*cstypes.PeerRoundState)
peerRoundStates := []string{}
for _, peer := range p2pSwitch.Peers().List() { for _, peer := range p2pSwitch.Peers().List() {
// TODO: clean this up?
peerState := peer.Get(types.PeerStateKey).(*cm.PeerState) peerState := peer.Get(types.PeerStateKey).(*cm.PeerState)
peerRoundState := peerState.GetRoundState() peerRoundState := peerState.GetRoundState()
peerRoundStateStr := peer.Key() + ":" + string(wire.JSONBytes(peerRoundState)) peerRoundStates[peer.Key()] = peerRoundState
peerRoundStates = append(peerRoundStates, peerRoundStateStr)
} }
return &ctypes.ResultDumpConsensusState{roundState.String(), peerRoundStates}, nil return &ctypes.ResultDumpConsensusState{consensusState.GetRoundState(), peerRoundStates}, nil
} }

View File

@ -2,14 +2,14 @@ package core
import ( import (
crypto "github.com/tendermint/go-crypto" crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/tmlibs/log"
"github.com/tendermint/tendermint/consensus" "github.com/tendermint/tendermint/consensus"
cstypes "github.com/tendermint/tendermint/consensus/types"
p2p "github.com/tendermint/tendermint/p2p" p2p "github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/proxy" "github.com/tendermint/tendermint/proxy"
sm "github.com/tendermint/tendermint/state" sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/state/txindex" "github.com/tendermint/tendermint/state/txindex"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
"github.com/tendermint/tmlibs/log"
) )
//---------------------------------------------- //----------------------------------------------
@ -18,7 +18,7 @@ import (
type Consensus interface { type Consensus interface {
GetState() *sm.State GetState() *sm.State
GetValidators() (int, []*types.Validator) GetValidators() (int, []*types.Validator)
GetRoundState() *consensus.RoundState GetRoundState() *cstypes.RoundState
} }
type P2P interface { type P2P interface {

View File

@ -4,9 +4,9 @@ import (
"strings" "strings"
abci "github.com/tendermint/abci/types" 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/go-wire/data"
cstypes "github.com/tendermint/tendermint/consensus/types"
"github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
) )
@ -76,8 +76,8 @@ type ResultValidators struct {
} }
type ResultDumpConsensusState struct { type ResultDumpConsensusState struct {
RoundState string `json:"round_state"` RoundState *cstypes.RoundState `json:"round_state"`
PeerRoundStates []string `json:"peer_round_states"` PeerRoundStates map[string]*cstypes.PeerRoundState `json:"peer_round_states"`
} }
type ResultBroadcastTx struct { type ResultBroadcastTx struct {

View File

@ -4,6 +4,7 @@ import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/p2p"
) )

View File

@ -1,11 +1,10 @@
package core_grpc package core_grpc
import ( import (
core "github.com/tendermint/tendermint/rpc/core" context "golang.org/x/net/context"
abci "github.com/tendermint/abci/types" abci "github.com/tendermint/abci/types"
core "github.com/tendermint/tendermint/rpc/core"
context "golang.org/x/net/context"
) )
type broadcastAPI struct { type broadcastAPI struct {

View File

@ -8,7 +8,7 @@ import (
"google.golang.org/grpc" "google.golang.org/grpc"
. "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
) )
// Start the grpcServer in a go routine // 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) { func dialerFunc(addr string, timeout time.Duration) (net.Conn, error) {
return Connect(addr) return cmn.Connect(addr)
} }

View File

@ -1,7 +1,7 @@
syntax = "proto3"; syntax = "proto3";
package core_grpc; package core_grpc;
import "github.com/tendermint/abci/types/types.proto"; import "github.com/tendermint/abci/blob/master/types/types.proto";
//---------------------------------------- //----------------------------------------
// Message types // Message types

View File

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

View File

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

View File

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

View File

@ -44,6 +44,9 @@ type WSClient struct {
ResultsCh chan json.RawMessage ResultsCh chan json.RawMessage
ErrorsCh chan error ErrorsCh chan error
// Callback, which will be called each time after successful reconnect.
onReconnect func()
// internal channels // internal channels
send chan types.RPCRequest // user requests send chan types.RPCRequest // user requests
backlog chan types.RPCRequest // stores a single user request received during a conn failure backlog chan types.RPCRequest // stores a single user request received during a conn failure
@ -124,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. // String returns WS client full address.
func (c *WSClient) String() string { func (c *WSClient) String() string {
return fmt.Sprintf("%s (%s)", c.Address, c.Endpoint) return fmt.Sprintf("%s (%s)", c.Address, c.Endpoint)
@ -254,6 +265,9 @@ func (c *WSClient) reconnect() error {
c.Logger.Error("failed to redial", "err", err) c.Logger.Error("failed to redial", "err", err)
} else { } else {
c.Logger.Info("reconnected") c.Logger.Info("reconnected")
if c.onReconnect != nil {
go c.onReconnect()
}
return nil return nil
} }

View File

@ -1,7 +1,4 @@
# 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 HTTP RPC server supporting calls via uri params, jsonrpc, and jsonrpc over websockets
# Client Requests # Client Requests
@ -102,20 +99,5 @@ 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) * [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) * [tm-monitor](https://github.com/tendermint/tools/blob/master/tm-monitor/rpc.go)
*/
## CHANGELOG package rpc
### 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)

View File

@ -16,6 +16,7 @@ import (
"github.com/go-kit/kit/log/term" "github.com/go-kit/kit/log/term"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/tendermint/go-wire/data" "github.com/tendermint/go-wire/data"
client "github.com/tendermint/tendermint/rpc/lib/client" client "github.com/tendermint/tendermint/rpc/lib/client"
server "github.com/tendermint/tendermint/rpc/lib/server" server "github.com/tendermint/tendermint/rpc/lib/server"
@ -362,7 +363,7 @@ func TestWSClientPingPong(t *testing.T) {
require.Nil(t, err) require.Nil(t, err)
defer cl.Stop() defer cl.Stop()
time.Sleep(3 * time.Second) time.Sleep(6 * time.Second)
} }
func randBytes(t *testing.T) []byte { func randBytes(t *testing.T) []byte {

View File

@ -8,6 +8,7 @@ import (
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"reflect" "reflect"
"runtime/debug"
"sort" "sort"
"strings" "strings"
"time" "time"
@ -116,6 +117,7 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han
// A Notification is a Request object without an "id" member. // 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. // The Server MUST NOT reply to a Notification, including those that are within a batch request.
if request.ID == "" { if request.ID == "" {
logger.Debug("HTTPJSONRPC received a notification, skipping... (please send a non-empty ID if you want to call a method)")
return return
} }
if len(r.URL.Path) > 1 { if len(r.URL.Path) > 1 {
@ -127,10 +129,13 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han
WriteRPCResponseHTTP(w, types.RPCMethodNotFoundError(request.ID)) WriteRPCResponseHTTP(w, types.RPCMethodNotFoundError(request.ID))
return return
} }
args, err := jsonParamsToArgsRPC(rpcFunc, request.Params) var args []reflect.Value
if err != nil { if len(request.Params) > 0 {
WriteRPCResponseHTTP(w, types.RPCInvalidParamsError(request.ID, errors.Wrap(err, "Error converting json params to arguments"))) args, err = jsonParamsToArgsRPC(rpcFunc, request.Params)
return if err != nil {
WriteRPCResponseHTTP(w, types.RPCInvalidParamsError(request.ID, errors.Wrap(err, "Error converting json params to arguments")))
return
}
} }
returns := rpcFunc.f.Call(args) returns := rpcFunc.f.Call(args)
logger.Info("HTTPJSONRPC", "method", request.Method, "args", args, "returns", returns) logger.Info("HTTPJSONRPC", "method", request.Method, "args", args, "returns", returns)
@ -208,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 // Convert a []interface{} OR a map[string]interface{} to properly typed values
func jsonParamsToArgsRPC(rpcFunc *RPCFunc, params *json.RawMessage) ([]reflect.Value, error) { func jsonParamsToArgsRPC(rpcFunc *RPCFunc, params json.RawMessage) ([]reflect.Value, error) {
return jsonParamsToArgs(rpcFunc, *params, 0) return jsonParamsToArgs(rpcFunc, params, 0)
} }
// Same as above, but with the first param the websocket connection // Same as above, but with the first param the websocket connection
func jsonParamsToArgsWS(rpcFunc *RPCFunc, params *json.RawMessage, wsCtx types.WSRPCContext) ([]reflect.Value, error) { func jsonParamsToArgsWS(rpcFunc *RPCFunc, params json.RawMessage, wsCtx types.WSRPCContext) ([]reflect.Value, error) {
values, err := jsonParamsToArgs(rpcFunc, *params, 1) values, err := jsonParamsToArgs(rpcFunc, params, 1)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -485,9 +490,23 @@ func (wsc *wsConnection) TryWriteRPCResponse(resp types.RPCResponse) bool {
// Read from the socket and subscribe to or unsubscribe from events // Read from the socket and subscribe to or unsubscribe from events
func (wsc *wsConnection) readRoutine() { func (wsc *wsConnection) readRoutine() {
defer func() { 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 {
return wsc.baseConn.SetReadDeadline(time.Now().Add(wsc.readWait))
})
for { for {
select { select {
case <-wsc.Quit: case <-wsc.Quit:
@ -517,6 +536,7 @@ func (wsc *wsConnection) readRoutine() {
// A Notification is a Request object without an "id" member. // 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. // The Server MUST NOT reply to a Notification, including those that are within a batch request.
if request.ID == "" { 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 continue
} }
@ -530,9 +550,13 @@ func (wsc *wsConnection) readRoutine() {
var args []reflect.Value var args []reflect.Value
if rpcFunc.ws { if rpcFunc.ws {
wsCtx := types.WSRPCContext{Request: request, WSRPCConnection: wsc} wsCtx := types.WSRPCContext{Request: request, WSRPCConnection: wsc}
args, err = jsonParamsToArgsWS(rpcFunc, request.Params, wsCtx) if len(request.Params) > 0 {
args, err = jsonParamsToArgsWS(rpcFunc, request.Params, wsCtx)
}
} else { } else {
args, err = jsonParamsToArgsRPC(rpcFunc, request.Params) if len(request.Params) > 0 {
args, err = jsonParamsToArgsRPC(rpcFunc, request.Params)
}
} }
if err != nil { if err != nil {
wsc.WriteRPCResponse(types.RPCInternalError(request.ID, errors.Wrap(err, "Error converting json params to arguments"))) wsc.WriteRPCResponse(types.RPCInternalError(request.ID, errors.Wrap(err, "Error converting json params to arguments")))

View File

@ -14,10 +14,10 @@ import (
// REQUEST // REQUEST
type RPCRequest struct { type RPCRequest struct {
JSONRPC string `json:"jsonrpc"` JSONRPC string `json:"jsonrpc"`
ID string `json:"id"` ID string `json:"id"`
Method string `json:"method"` Method string `json:"method"`
Params *json.RawMessage `json:"params"` // must be map[string]interface{} or []interface{} Params json.RawMessage `json:"params"` // must be map[string]interface{} or []interface{}
} }
func NewRPCRequest(id string, method string, params json.RawMessage) RPCRequest { 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", JSONRPC: "2.0",
ID: id, ID: id,
Method: method, Method: method,
Params: &params, Params: params,
} }
} }

View File

@ -12,6 +12,7 @@ if [ -z "$VERSION" ]; then
echo "Please specify a version." echo "Please specify a version."
exit 1 exit 1
fi fi
echo "==> Copying ${DIST_DIR} to S3..."
# copy to s3 # copy to s3
aws s3 cp --recursive ${DIST_DIR} s3://tendermint/binaries/tendermint/v${VERSION} --acl public-read aws s3 cp --recursive ${DIST_DIR} s3://tendermint/binaries/tendermint/v${VERSION} --acl public-read

53
scripts/release.sh Executable file
View File

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

View File

@ -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 \ RUN apt-get update && apt-get install -y --no-install-recommends \
zip \ zip \

View File

@ -5,6 +5,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/tendermint/abci/example/dummy" "github.com/tendermint/abci/example/dummy"
crypto "github.com/tendermint/go-crypto" crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/tendermint/proxy" "github.com/tendermint/tendermint/proxy"

View File

@ -7,16 +7,14 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/types"
abci "github.com/tendermint/abci/types" abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto" crypto "github.com/tendermint/go-crypto"
cmn "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db" dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log" "github.com/tendermint/tmlibs/log"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/types"
) )
// setupTestCase does setup common to all test cases // setupTestCase does setup common to all test cases

View File

@ -1,4 +1,4 @@
FROM golang:1.8.3 FROM golang:1.9.0
# Add testing deps for curl # Add testing deps for curl
RUN echo 'deb http://httpredir.debian.org/debian testing main non-free contrib' >> /etc/apt/sources.list RUN echo 'deb http://httpredir.debian.org/debian testing main non-free contrib' >> /etc/apt/sources.list

View File

@ -5,6 +5,7 @@ import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
crypto "github.com/tendermint/go-crypto" crypto "github.com/tendermint/go-crypto"
) )

View File

@ -5,7 +5,7 @@ import (
"io/ioutil" "io/ioutil"
"testing" "testing"
. "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
) )
const ( const (
@ -15,7 +15,7 @@ const (
func TestBasicPartSet(t *testing.T) { func TestBasicPartSet(t *testing.T) {
// Construct random data of size partSize * 100 // Construct random data of size partSize * 100
data := RandBytes(testPartSize * 100) data := cmn.RandBytes(testPartSize * 100)
partSet := NewPartSetFromData(data, testPartSize) partSet := NewPartSetFromData(data, testPartSize)
if len(partSet.Hash()) == 0 { if len(partSet.Hash()) == 0 {
@ -65,7 +65,7 @@ func TestBasicPartSet(t *testing.T) {
func TestWrongProof(t *testing.T) { func TestWrongProof(t *testing.T) {
// Construct random data of size partSize * 100 // Construct random data of size partSize * 100
data := RandBytes(testPartSize * 100) data := cmn.RandBytes(testPartSize * 100)
partSet := NewPartSetFromData(data, testPartSize) partSet := NewPartSetFromData(data, testPartSize)
// Test adding a part with wrong data. // Test adding a part with wrong data.

View File

@ -5,7 +5,6 @@ import (
"fmt" "fmt"
"io" "io"
//. "github.com/tendermint/tmlibs/common"
"github.com/tendermint/go-crypto" "github.com/tendermint/go-crypto"
"github.com/tendermint/go-wire" "github.com/tendermint/go-wire"
) )

View File

@ -4,7 +4,7 @@ import (
"bytes" "bytes"
"io" "io"
. "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/merkle" "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) buf, n, err := new(bytes.Buffer), new(int), new(error)
o.WriteSignBytes(chainID, buf, n, err) o.WriteSignBytes(chainID, buf, n, err)
if *err != nil { if *err != nil {
PanicCrisis(err) cmn.PanicCrisis(err)
} }
return buf.Bytes() return buf.Bytes()
} }

View File

@ -5,6 +5,7 @@ import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
wire "github.com/tendermint/go-wire" wire "github.com/tendermint/go-wire"
cmn "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
ctest "github.com/tendermint/tmlibs/test" ctest "github.com/tendermint/tmlibs/test"

View File

@ -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) { func (valSet *ValidatorSet) GetByIndex(index int) (address []byte, val *Validator) {
if index >= len(valSet.Validators) {
return nil, nil
}
val = valSet.Validators[index] val = valSet.Validators[index]
return val.Address, val.Copy() return val.Address, val.Copy()
} }

View File

@ -13,9 +13,9 @@ import (
var ( var (
ErrVoteUnexpectedStep = errors.New("Unexpected step") ErrVoteUnexpectedStep = errors.New("Unexpected step")
ErrVoteInvalidValidatorIndex = errors.New("Invalid round vote validator index") ErrVoteInvalidValidatorIndex = errors.New("Invalid validator index")
ErrVoteInvalidValidatorAddress = errors.New("Invalid round vote validator address") ErrVoteInvalidValidatorAddress = errors.New("Invalid validator address")
ErrVoteInvalidSignature = errors.New("Invalid round vote signature") ErrVoteInvalidSignature = errors.New("Invalid signature")
ErrVoteInvalidBlockHash = errors.New("Invalid block hash") ErrVoteInvalidBlockHash = errors.New("Invalid block hash")
) )

View File

@ -6,7 +6,7 @@ import (
"strings" "strings"
"sync" "sync"
. "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
) )
/* /*
@ -51,7 +51,7 @@ type VoteSet struct {
mtx sync.Mutex mtx sync.Mutex
valSet *ValidatorSet valSet *ValidatorSet
votesBitArray *BitArray votesBitArray *cmn.BitArray
votes []*Vote // Primary votes to share votes []*Vote // Primary votes to share
sum int64 // Sum of voting power for seen votes, discounting conflicts sum int64 // Sum of voting power for seen votes, discounting conflicts
maj23 *BlockID // First 2/3 majority seen 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. // 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 { func NewVoteSet(chainID string, height int, round int, type_ byte, valSet *ValidatorSet) *VoteSet {
if height == 0 { 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{ return &VoteSet{
chainID: chainID, chainID: chainID,
@ -70,7 +70,7 @@ func NewVoteSet(chainID string, height int, round int, type_ byte, valSet *Valid
round: round, round: round,
type_: type_, type_: type_,
valSet: valSet, valSet: valSet,
votesBitArray: NewBitArray(valSet.Size()), votesBitArray: cmn.NewBitArray(valSet.Size()),
votes: make([]*Vote, valSet.Size()), votes: make([]*Vote, valSet.Size()),
sum: 0, sum: 0,
maj23: nil, maj23: nil,
@ -125,7 +125,7 @@ func (voteSet *VoteSet) Size() int {
// NOTE: VoteSet must not be nil // NOTE: VoteSet must not be nil
func (voteSet *VoteSet) AddVote(vote *Vote) (added bool, err error) { func (voteSet *VoteSet) AddVote(vote *Vote) (added bool, err error) {
if voteSet == nil { if voteSet == nil {
PanicSanity("AddVote() on nil VoteSet") cmn.PanicSanity("AddVote() on nil VoteSet")
} }
voteSet.mtx.Lock() voteSet.mtx.Lock()
defer voteSet.mtx.Unlock() defer voteSet.mtx.Unlock()
@ -140,8 +140,10 @@ func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) {
blockKey := vote.BlockID.Key() blockKey := vote.BlockID.Key()
// Ensure that validator index was set // Ensure that validator index was set
if valIndex < 0 || len(valAddr) == 0 { if valIndex < 0 {
panic("Validator index or address was not set in vote.") return false, ErrVoteInvalidValidatorIndex
} else if len(valAddr) == 0 {
return false, ErrVoteInvalidValidatorAddress
} }
// Make sure the step matches. // Make sure the step matches.
@ -186,7 +188,7 @@ func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) {
} }
} else { } else {
if !added { if !added {
PanicSanity("Expected to add non-conflicting vote") cmn.PanicSanity("Expected to add non-conflicting vote")
} }
return added, nil return added, nil
} }
@ -212,7 +214,7 @@ func (voteSet *VoteSet) addVerifiedVote(vote *Vote, blockKey string, votingPower
// Already exists in voteSet.votes? // Already exists in voteSet.votes?
if existing := voteSet.votes[valIndex]; existing != nil { if existing := voteSet.votes[valIndex]; existing != nil {
if existing.BlockID.Equals(vote.BlockID) { if existing.BlockID.Equals(vote.BlockID) {
PanicSanity("addVerifiedVote does not expect duplicate votes") cmn.PanicSanity("addVerifiedVote does not expect duplicate votes")
} else { } else {
conflicting = existing conflicting = existing
} }
@ -283,7 +285,7 @@ func (voteSet *VoteSet) addVerifiedVote(vote *Vote, blockKey string, votingPower
// NOTE: VoteSet must not be nil // NOTE: VoteSet must not be nil
func (voteSet *VoteSet) SetPeerMaj23(peerID string, blockID BlockID) { func (voteSet *VoteSet) SetPeerMaj23(peerID string, blockID BlockID) {
if voteSet == nil { if voteSet == nil {
PanicSanity("SetPeerMaj23() on nil VoteSet") cmn.PanicSanity("SetPeerMaj23() on nil VoteSet")
} }
voteSet.mtx.Lock() voteSet.mtx.Lock()
defer voteSet.mtx.Unlock() defer voteSet.mtx.Unlock()
@ -316,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 { if voteSet == nil {
return nil return nil
} }
@ -325,7 +327,7 @@ func (voteSet *VoteSet) BitArray() *BitArray {
return voteSet.votesBitArray.Copy() return voteSet.votesBitArray.Copy()
} }
func (voteSet *VoteSet) BitArrayByBlockID(blockID BlockID) *BitArray { func (voteSet *VoteSet) BitArrayByBlockID(blockID BlockID) *cmn.BitArray {
if voteSet == nil { if voteSet == nil {
return nil return nil
} }
@ -356,7 +358,7 @@ func (voteSet *VoteSet) GetByAddress(address []byte) *Vote {
defer voteSet.mtx.Unlock() defer voteSet.mtx.Unlock()
valIndex, val := voteSet.valSet.GetByAddress(address) valIndex, val := voteSet.valSet.GetByAddress(address)
if val == nil { if val == nil {
PanicSanity("GetByAddress(address) returned nil") cmn.PanicSanity("GetByAddress(address) returned nil")
} }
return voteSet.votes[valIndex] return voteSet.votes[valIndex]
} }
@ -454,14 +456,14 @@ func (voteSet *VoteSet) StringShort() string {
func (voteSet *VoteSet) MakeCommit() *Commit { func (voteSet *VoteSet) MakeCommit() *Commit {
if voteSet.type_ != VoteTypePrecommit { if voteSet.type_ != VoteTypePrecommit {
PanicSanity("Cannot MakeCommit() unless VoteSet.Type is VoteTypePrecommit") cmn.PanicSanity("Cannot MakeCommit() unless VoteSet.Type is VoteTypePrecommit")
} }
voteSet.mtx.Lock() voteSet.mtx.Lock()
defer voteSet.mtx.Unlock() defer voteSet.mtx.Unlock()
// Make sure we have a 2/3 majority // Make sure we have a 2/3 majority
if voteSet.maj23 == nil { 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 // For every validator, get the precommit
@ -482,16 +484,16 @@ func (voteSet *VoteSet) MakeCommit() *Commit {
2. A peer claims to have a 2/3 majority w/ blockKey (peerMaj23=true) 2. A peer claims to have a 2/3 majority w/ blockKey (peerMaj23=true)
*/ */
type blockVotes struct { type blockVotes struct {
peerMaj23 bool // peer claims to have maj23 peerMaj23 bool // peer claims to have maj23
bitArray *BitArray // valIndex -> hasVote? bitArray *cmn.BitArray // valIndex -> hasVote?
votes []*Vote // valIndex -> *Vote votes []*Vote // valIndex -> *Vote
sum int64 // vote sum sum int64 // vote sum
} }
func newBlockVotes(peerMaj23 bool, numValidators int) *blockVotes { func newBlockVotes(peerMaj23 bool, numValidators int) *blockVotes {
return &blockVotes{ return &blockVotes{
peerMaj23: peerMaj23, peerMaj23: peerMaj23,
bitArray: NewBitArray(numValidators), bitArray: cmn.NewBitArray(numValidators),
votes: make([]*Vote, numValidators), votes: make([]*Vote, numValidators),
sum: 0, sum: 0,
} }
@ -521,7 +523,7 @@ type VoteSetReader interface {
Round() int Round() int
Type() byte Type() byte
Size() int Size() int
BitArray() *BitArray BitArray() *cmn.BitArray
GetByIndex(int) *Vote GetByIndex(int) *Vote
IsCommit() bool IsCommit() bool
} }

View File

@ -2,12 +2,11 @@ package types
import ( import (
"bytes" "bytes"
"testing"
"github.com/tendermint/go-crypto" "github.com/tendermint/go-crypto"
. "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
. "github.com/tendermint/tmlibs/test" tst "github.com/tendermint/tmlibs/test"
"testing"
) )
// NOTE: privValidators are in order // NOTE: privValidators are in order
@ -137,7 +136,7 @@ func Test2_3Majority(t *testing.T) {
// 7th validator voted for some blockhash // 7th validator voted for some blockhash
{ {
vote := withValidator(voteProto, privValidators[6].GetAddress(), 6) 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() blockID, ok = voteSet.TwoThirdsMajority()
if ok || !blockID.IsZero() { if ok || !blockID.IsZero() {
t.Errorf("There should be no 2/3 majority") 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 // 70th validator voted for different BlockHash
{ {
vote := withValidator(voteProto, privValidators[69].GetAddress(), 69) 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() blockID, ok = voteSet.TwoThirdsMajority()
if ok || !blockID.IsZero() { if ok || !blockID.IsZero() {
t.Errorf("There should be no 2/3 majority: last vote added had different BlockHash") 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. // val0 votes again for some block.
{ {
vote := withValidator(voteProto, privValidators[0].GetAddress(), 0) 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 { if added || err == nil {
t.Errorf("Expected VoteSet.Add to fail, conflicting vote.") t.Errorf("Expected VoteSet.Add to fail, conflicting vote.")
} }
@ -297,8 +296,8 @@ func TestBadVotes(t *testing.T) {
func TestConflicts(t *testing.T) { func TestConflicts(t *testing.T) {
height, round := 1, 0 height, round := 1, 0
voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrevote, 4, 1) voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrevote, 4, 1)
blockHash1 := RandBytes(32) blockHash1 := cmn.RandBytes(32)
blockHash2 := RandBytes(32) blockHash2 := cmn.RandBytes(32)
voteProto := &Vote{ voteProto := &Vote{
ValidatorAddress: nil, ValidatorAddress: nil,
@ -444,13 +443,13 @@ func TestMakeCommit(t *testing.T) {
} }
// MakeCommit should fail. // 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. // 7th voted for some other block.
{ {
vote := withValidator(voteProto, privValidators[6].GetAddress(), 6) vote := withValidator(voteProto, privValidators[6].GetAddress(), 6)
vote = withBlockHash(vote, RandBytes(32)) vote = withBlockHash(vote, cmn.RandBytes(32))
vote = withBlockPartsHeader(vote, PartSetHeader{123, RandBytes(32)}) vote = withBlockPartsHeader(vote, PartSetHeader{123, cmn.RandBytes(32)})
signAddVote(privValidators[6], vote, voteSet) signAddVote(privValidators[6], vote, voteSet)
} }

View File

@ -2,11 +2,11 @@ package version
const Maj = "0" const Maj = "0"
const Min = "11" const Min = "11"
const Fix = "0" const Fix = "1"
var ( var (
// The full version string // 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 is set with --ldflags "-X main.gitCommit=$(git rev-parse HEAD)"
GitCommit string GitCommit string