commit
d4634dc683
|
@ -0,0 +1,5 @@
|
||||||
|
# CODEOWNERS: https://help.github.com/articles/about-codeowners/
|
||||||
|
|
||||||
|
# Everything goes through Bucky. For now.
|
||||||
|
* @ebuchman
|
||||||
|
|
|
@ -15,3 +15,5 @@ test/p2p/data/
|
||||||
test/logs
|
test/logs
|
||||||
.glide
|
.glide
|
||||||
coverage.txt
|
coverage.txt
|
||||||
|
docs/_build
|
||||||
|
docs/tools
|
||||||
|
|
25
CHANGELOG.md
25
CHANGELOG.md
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
2
Makefile
2
Makefile
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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++ {
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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{}) {}
|
|
@ -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 (
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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,
|
|
@ -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
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
----------------
|
----------------
|
||||||
|
|
|
@ -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>`__.
|
||||||
|
|
|
@ -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.
|
|
@ -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
|
||||||
--------------
|
--------------
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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**
|
||||||
|
|
|
@ -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
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
# `tendermint/p2p/upnp`
|
|
||||||
|
|
||||||
## Resources
|
|
||||||
|
|
||||||
* http://www.upnp-hacks.org/upnp.html
|
|
|
@ -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"
|
||||||
|
|
|
@ -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.
|
||||||
//
|
//
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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"
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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")))
|
||||||
|
|
|
@ -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: ¶ms,
|
Params: params,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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 \
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue