Browse Source

Merge pull request #2056 from tendermint/hotfix/v0.22.6

Hotfix/v0.22.6
pull/2065/head v0.22.6
Ethan Buchman 7 years ago
committed by GitHub
parent
commit
5fdbcd70df
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 181 additions and 74 deletions
  1. +15
    -1
      CHANGELOG.md
  2. +3
    -9
      Gopkg.lock
  3. +5
    -5
      Gopkg.toml
  4. +1
    -1
      blockchain/reactor_test.go
  5. +3
    -2
      consensus/state.go
  6. +3
    -1
      p2p/listener.go
  7. +2
    -0
      p2p/switch.go
  8. +7
    -1
      rpc/core/abci.go
  9. +36
    -15
      rpc/core/blocks.go
  10. +58
    -0
      rpc/core/blocks_test.go
  11. +3
    -3
      state/execution_test.go
  12. +16
    -8
      state/state.go
  13. +2
    -9
      types/block.go
  14. +25
    -17
      types/block_test.go
  15. +2
    -2
      version/version.go

+ 15
- 1
CHANGELOG.md View File

@ -1,6 +1,20 @@
# Changelog
## TBA
## 0.22.6
*July 24th, 2018*
BUG FIXES
- [rpc] Fix `/blockchain` endpoint
- (#2049) Fix OOM attack by returning error on negative input
- Fix result length to have max 20 (instead of 21) block metas
- [rpc] Validate height is non-negative in `/abci_query`
- [consensus] (#2050) Include evidence in proposal block parts (previously evidence was
not being included in blocks!)
- [p2p] (#2046) Close rejected inbound connections so file descriptor doesn't
leak
- [Gopkg] (#2053) Fix versions in the toml
## 0.22.5


+ 3
- 9
Gopkg.lock View File

@ -11,7 +11,6 @@
branch = "master"
name = "github.com/btcsuite/btcd"
packages = ["btcec"]
pruneopts = "UT"
revision = "f673a4b563b57b9a95832545c878669a7fa801d9"
[[projects]]
@ -81,8 +80,8 @@
"sortkeys",
"types"
]
revision = "7d68e886eac4f7e34d0d82241a6273d6c304c5cf"
version = "v1.1.0"
revision = "636bf0302bc95575d69441b25a2603156ffdddf1"
version = "v1.1.1"
[[projects]]
name = "github.com/golang/protobuf"
@ -156,10 +155,8 @@
[[projects]]
branch = "master"
digest = "1:5ab79470a1d0fb19b041a624415612f8236b3c06070161a910562f2b2d064355"
name = "github.com/mitchellh/mapstructure"
packages = ["."]
pruneopts = "UT"
revision = "f15292f7a699fcc1a38a80977f80a046874ba8ac"
[[projects]]
@ -190,10 +187,8 @@
[[projects]]
branch = "master"
digest = "1:0f37e09b3e92aaeda5991581311f8dbf38944b36a3edec61cc2d1991f527554a"
name = "github.com/prometheus/client_model"
packages = ["go"]
pruneopts = "UT"
revision = "5c3871d89910bfb32f5fcab2aa4b9ec68e65a99f"
[[projects]]
@ -347,7 +342,6 @@
"cpu",
"unix"
]
pruneopts = "UT"
revision = "ac767d655b305d4e9612f5f6e33120b9176c4ad4"
[[projects]]
@ -417,6 +411,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "9beb2d27dc19e3f9e2c7f416f312f7129f5441b1b53def42503fc6f7d3a54b16"
inputs-digest = "8e519c3716c259c6ecdb052889dd3602539fd98a3650dfbf50a4023e087a5d53"
solver-name = "gps-cdcl"
solver-version = 1

+ 5
- 5
Gopkg.toml View File

@ -31,7 +31,7 @@
[[constraint]]
name = "github.com/go-kit/kit"
version = "=0.7.0"
version = "=0.6.0"
[[constraint]]
name = "github.com/gogo/protobuf"
@ -51,19 +51,19 @@
[[constraint]]
name = "github.com/spf13/cobra"
version = "=0.0.3"
version = "=0.0.1"
[[constraint]]
name = "github.com/spf13/viper"
version = "=1.0.2"
version = "=1.0.0"
[[constraint]]
name = "github.com/stretchr/testify"
version = "=1.2.2"
version = "=1.2.1"
[[constraint]]
name = "github.com/tendermint/go-amino"
version = "=v0.11.1"
version = "=v0.10.1"
[[constraint]]
name = "google.golang.org/grpc"


+ 1
- 1
blockchain/reactor_test.go View File

@ -156,7 +156,7 @@ func makeTxs(height int64) (txs []types.Tx) {
}
func makeBlock(height int64, state sm.State) *types.Block {
block, _ := state.MakeBlock(height, makeTxs(height), new(types.Commit))
block, _ := state.MakeBlock(height, makeTxs(height), new(types.Commit), nil)
return block
}


+ 3
- 2
consensus/state.go View File

@ -907,6 +907,8 @@ func (cs *ConsensusState) isProposalComplete() bool {
}
// Create the next block to propose and return it.
// We really only need to return the parts, but the block
// is returned for convenience so we can log the proposal block.
// Returns nil block upon error.
// NOTE: keep it side-effect free for clarity.
func (cs *ConsensusState) createProposalBlock() (block *types.Block, blockParts *types.PartSet) {
@ -926,9 +928,8 @@ func (cs *ConsensusState) createProposalBlock() (block *types.Block, blockParts
// Mempool validated transactions
txs := cs.mempool.Reap(cs.state.ConsensusParams.BlockSize.MaxTxs)
block, parts := cs.state.MakeBlock(cs.Height, txs, commit)
evidence := cs.evpool.PendingEvidence()
block.AddEvidence(evidence)
block, parts := cs.state.MakeBlock(cs.Height, txs, commit, evidence)
return block, parts
}


+ 3
- 1
p2p/listener.go View File

@ -151,7 +151,7 @@ func (l *DefaultListener) OnStop() {
l.listener.Close() // nolint: errcheck
}
// Accept connections and pass on the channel
// Accept connections and pass on the channel.
func (l *DefaultListener) listenRoutine() {
for {
conn, err := l.listener.Accept()
@ -178,6 +178,8 @@ func (l *DefaultListener) listenRoutine() {
// Connections returns a channel of inbound connections.
// It gets closed when the listener closes.
// It is the callers responsibility to close any connections received
// over this channel.
func (l *DefaultListener) Connections() <-chan net.Conn {
return l.connections
}


+ 2
- 0
p2p/switch.go View File

@ -496,6 +496,7 @@ func (sw *Switch) listenerRoutine(l Listener) {
maxPeers := sw.config.MaxNumPeers - DefaultMinNumOutboundPeers
if maxPeers <= sw.peers.Size() {
sw.Logger.Info("Ignoring inbound connection: already have enough peers", "address", inConn.RemoteAddr().String(), "numPeers", sw.peers.Size(), "max", maxPeers)
inConn.Close()
continue
}
@ -510,6 +511,7 @@ func (sw *Switch) listenerRoutine(l Listener) {
// cleanup
}
// closes conn if err is returned
func (sw *Switch) addInboundPeerWithConfig(
conn net.Conn,
config *config.P2PConfig,


+ 7
- 1
rpc/core/abci.go View File

@ -1,10 +1,12 @@
package core
import (
"fmt"
abci "github.com/tendermint/tendermint/abci/types"
cmn "github.com/tendermint/tendermint/libs/common"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/tendermint/tendermint/version"
cmn "github.com/tendermint/tendermint/libs/common"
)
// Query the application for some information.
@ -48,6 +50,10 @@ import (
// | height | int64 | 0 | false | Height (0 means latest) |
// | trusted | bool | false | false | Does not include a proof of the data inclusion |
func ABCIQuery(path string, data cmn.HexBytes, height int64, trusted bool) (*ctypes.ResultABCIQuery, error) {
if height < 0 {
return nil, fmt.Errorf("height must be non-negative")
}
resQuery, err := proxyAppQuery.QuerySync(abci.RequestQuery{
Path: path,
Data: data,


+ 36
- 15
rpc/core/blocks.go View File

@ -64,25 +64,15 @@ import (
//
// <aside class="notice">Returns at most 20 items.</aside>
func BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) {
if minHeight == 0 {
minHeight = 1
}
if maxHeight == 0 {
maxHeight = blockStore.Height()
} else {
maxHeight = cmn.MinInt64(blockStore.Height(), maxHeight)
}
// maximum 20 block metas
const limit int64 = 20
minHeight = cmn.MaxInt64(minHeight, maxHeight-limit)
logger.Debug("BlockchainInfoHandler", "maxHeight", maxHeight, "minHeight", minHeight)
if minHeight > maxHeight {
return nil, fmt.Errorf("min height %d can't be greater than max height %d", minHeight, maxHeight)
var err error
minHeight, maxHeight, err = filterMinMax(blockStore.Height(), minHeight, maxHeight, limit)
if err != nil {
return nil, err
}
logger.Debug("BlockchainInfoHandler", "maxHeight", maxHeight, "minHeight", minHeight)
blockMetas := []*types.BlockMeta{}
for height := maxHeight; height >= minHeight; height-- {
@ -93,6 +83,37 @@ func BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, e
return &ctypes.ResultBlockchainInfo{blockStore.Height(), blockMetas}, nil
}
// error if either min or max are negative or min < max
// if 0, use 1 for min, latest block height for max
// enforce limit.
// error if min > max
func filterMinMax(height, min, max, limit int64) (int64, int64, error) {
// filter negatives
if min < 0 || max < 0 {
return min, max, fmt.Errorf("heights must be non-negative")
}
// adjust for default values
if min == 0 {
min = 1
}
if max == 0 {
max = height
}
// limit max to the height
max = cmn.MinInt64(height, max)
// limit min to within `limit` of max
// so the total number of blocks returned will be `limit`
min = cmn.MaxInt64(min, max-limit+1)
if min > max {
return min, max, fmt.Errorf("min height %d can't be greater than max height %d", min, max)
}
return min, max, nil
}
// Get block at a given height.
// If no height is provided, it will fetch the latest block.
//


+ 58
- 0
rpc/core/blocks_test.go View File

@ -0,0 +1,58 @@
package core
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
)
func TestBlockchainInfo(t *testing.T) {
cases := []struct {
min, max int64
height int64
limit int64
resultLength int64
wantErr bool
}{
// min > max
{0, 0, 0, 10, 0, true}, // min set to 1
{0, 1, 0, 10, 0, true}, // max set to height (0)
{0, 0, 1, 10, 1, false}, // max set to height (1)
{2, 0, 1, 10, 0, true}, // max set to height (1)
{2, 1, 5, 10, 0, true},
// negative
{1, 10, 14, 10, 10, false}, // control
{-1, 10, 14, 10, 0, true},
{1, -10, 14, 10, 0, true},
{-9223372036854775808, -9223372036854775788, 100, 20, 0, true},
// check limit and height
{1, 1, 1, 10, 1, false},
{1, 1, 5, 10, 1, false},
{2, 2, 5, 10, 1, false},
{1, 2, 5, 10, 2, false},
{1, 5, 1, 10, 1, false},
{1, 5, 10, 10, 5, false},
{1, 15, 10, 10, 10, false},
{1, 15, 15, 10, 10, false},
{1, 15, 15, 20, 15, false},
{1, 20, 15, 20, 15, false},
{1, 20, 20, 20, 20, false},
}
for i, c := range cases {
caseString := fmt.Sprintf("test %d failed", i)
min, max, err := filterMinMax(c.height, c.min, c.max, c.limit)
if c.wantErr {
require.Error(t, err, caseString)
} else {
require.NoError(t, err, caseString)
require.Equal(t, 1+max-min, c.resultLength, caseString)
}
}
}

+ 3
- 3
state/execution_test.go View File

@ -79,7 +79,7 @@ func TestBeginBlockValidators(t *testing.T) {
lastCommit := &types.Commit{BlockID: prevBlockID, Precommits: tc.lastCommitPrecommits}
// block for height 2
block, _ := state.MakeBlock(2, makeTxs(2), lastCommit)
block, _ := state.MakeBlock(2, makeTxs(2), lastCommit, nil)
_, err = ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), state.Validators, stateDB)
require.Nil(t, err, tc.desc)
@ -138,7 +138,7 @@ func TestBeginBlockByzantineValidators(t *testing.T) {
lastCommit := &types.Commit{BlockID: prevBlockID, Precommits: votes}
for _, tc := range testCases {
block, _ := state.MakeBlock(10, makeTxs(2), lastCommit)
block, _ := state.MakeBlock(10, makeTxs(2), lastCommit, nil)
block.Time = now
block.Evidence.Evidence = tc.evidence
_, err = ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), state.Validators, stateDB)
@ -269,7 +269,7 @@ func state(nVals, height int) (State, dbm.DB) {
}
func makeBlock(state State, height int64) *types.Block {
block, _ := state.MakeBlock(height, makeTxs(state.LastBlockHeight), new(types.Commit))
block, _ := state.MakeBlock(height, makeTxs(state.LastBlockHeight), new(types.Commit), nil)
return block
}


+ 16
- 8
state/state.go View File

@ -101,18 +101,26 @@ func (state State) GetValidators() (last *types.ValidatorSet, current *types.Val
//------------------------------------------------------------------------
// Create a block from the latest state
// MakeBlock builds a block with the given txs and commit from the current state.
func (state State) MakeBlock(height int64, txs []types.Tx, commit *types.Commit) (*types.Block, *types.PartSet) {
// build base block
block := types.MakeBlock(height, txs, commit)
// fill header with state data
// MakeBlock builds a block from the current state with the given txs, commit, and evidence.
func (state State) MakeBlock(
height int64,
txs []types.Tx,
commit *types.Commit,
evidence []types.Evidence,
) (*types.Block, *types.PartSet) {
// Build base block with block data.
block := types.MakeBlock(height, txs, commit, evidence)
// Fill rest of header with state data.
block.ChainID = state.ChainID
block.TotalTxs = state.LastBlockTotalTx + block.NumTxs
block.LastBlockID = state.LastBlockID
block.TotalTxs = state.LastBlockTotalTx + block.NumTxs
block.ValidatorsHash = state.Validators.Hash()
block.AppHash = state.AppHash
block.ConsensusHash = state.ConsensusParams.Hash()
block.AppHash = state.AppHash
block.LastResultsHash = state.LastResultsHash
return block, block.MakePartSet(state.ConsensusParams.BlockGossip.BlockPartSizeBytes)


+ 2
- 9
types/block.go View File

@ -25,7 +25,7 @@ type Block struct {
// MakeBlock returns a new block with an empty header, except what can be computed from itself.
// It populates the same set of fields validated by ValidateBasic
func MakeBlock(height int64, txs []Tx, commit *Commit) *Block {
func MakeBlock(height int64, txs []Tx, commit *Commit, evidence []Evidence) *Block {
block := &Block{
Header: Header{
Height: height,
@ -35,20 +35,13 @@ func MakeBlock(height int64, txs []Tx, commit *Commit) *Block {
Data: Data{
Txs: txs,
},
Evidence: EvidenceData{Evidence: evidence},
LastCommit: commit,
}
block.fillHeader()
return block
}
// AddEvidence appends the given evidence to the block
func (b *Block) AddEvidence(evidence []Evidence) {
if b == nil {
return
}
b.Evidence.Evidence = append(b.Evidence.Evidence, evidence...)
}
// ValidateBasic performs basic validation that doesn't involve state data.
// It checks the internal consistency of the block.
func (b *Block) ValidateBasic() error {


+ 25
- 17
types/block_test.go View File

@ -19,11 +19,13 @@ func TestBlockAddEvidence(t *testing.T) {
commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals)
require.NoError(t, err)
block := MakeBlock(h, txs, commit)
require.NotNil(t, block)
ev := NewMockGoodEvidence(h, 0, valSet.Validators[0].Address)
block.AddEvidence([]Evidence{ev})
evList := []Evidence{ev}
block := MakeBlock(h, txs, commit, evList)
require.NotNil(t, block)
require.Equal(t, 1, len(block.Evidence.Evidence))
require.NotNil(t, block.EvidenceHash)
}
func TestBlockValidateBasic(t *testing.T) {
@ -33,11 +35,14 @@ func TestBlockValidateBasic(t *testing.T) {
lastID := makeBlockIDRandom()
h := int64(3)
voteSet, _, vals := randVoteSet(h-1, 1, VoteTypePrecommit, 10, 1)
voteSet, valSet, vals := randVoteSet(h-1, 1, VoteTypePrecommit, 10, 1)
commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals)
require.NoError(t, err)
block := MakeBlock(h, txs, commit)
ev := NewMockGoodEvidence(h, 0, valSet.Validators[0].Address)
evList := []Evidence{ev}
block := MakeBlock(h, txs, commit, evList)
require.NotNil(t, block)
// proper block must pass
@ -45,39 +50,39 @@ func TestBlockValidateBasic(t *testing.T) {
require.NoError(t, err)
// tamper with NumTxs
block = MakeBlock(h, txs, commit)
block = MakeBlock(h, txs, commit, evList)
block.NumTxs++
err = block.ValidateBasic()
require.Error(t, err)
// remove 1/2 the commits
block = MakeBlock(h, txs, commit)
block = MakeBlock(h, txs, commit, evList)
block.LastCommit.Precommits = commit.Precommits[:commit.Size()/2]
block.LastCommit.hash = nil // clear hash or change wont be noticed
err = block.ValidateBasic()
require.Error(t, err)
// tamper with LastCommitHash
block = MakeBlock(h, txs, commit)
block = MakeBlock(h, txs, commit, evList)
block.LastCommitHash = []byte("something else")
err = block.ValidateBasic()
require.Error(t, err)
// tamper with data
block = MakeBlock(h, txs, commit)
block = MakeBlock(h, txs, commit, evList)
block.Data.Txs[0] = Tx("something else")
block.Data.hash = nil // clear hash or change wont be noticed
err = block.ValidateBasic()
require.Error(t, err)
// tamper with DataHash
block = MakeBlock(h, txs, commit)
block = MakeBlock(h, txs, commit, evList)
block.DataHash = cmn.RandBytes(len(block.DataHash))
err = block.ValidateBasic()
require.Error(t, err)
// tamper with evidence
block = MakeBlock(h, txs, commit)
block = MakeBlock(h, txs, commit, evList)
block.EvidenceHash = []byte("something else")
err = block.ValidateBasic()
require.Error(t, err)
@ -85,13 +90,13 @@ func TestBlockValidateBasic(t *testing.T) {
func TestBlockHash(t *testing.T) {
assert.Nil(t, (*Block)(nil).Hash())
assert.Nil(t, MakeBlock(int64(3), []Tx{Tx("Hello World")}, nil).Hash())
assert.Nil(t, MakeBlock(int64(3), []Tx{Tx("Hello World")}, nil, nil).Hash())
}
func TestBlockMakePartSet(t *testing.T) {
assert.Nil(t, (*Block)(nil).MakePartSet(2))
partSet := MakeBlock(int64(3), []Tx{Tx("Hello World")}, nil).MakePartSet(1024)
partSet := MakeBlock(int64(3), []Tx{Tx("Hello World")}, nil, nil).MakePartSet(1024)
assert.NotNil(t, partSet)
assert.Equal(t, 1, partSet.Total())
}
@ -105,7 +110,10 @@ func TestBlockHashesTo(t *testing.T) {
commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals)
require.NoError(t, err)
block := MakeBlock(h, []Tx{Tx("Hello World")}, commit)
ev := NewMockGoodEvidence(h, 0, valSet.Validators[0].Address)
evList := []Evidence{ev}
block := MakeBlock(h, []Tx{Tx("Hello World")}, commit, evList)
block.ValidatorsHash = valSet.Hash()
assert.False(t, block.HashesTo([]byte{}))
assert.False(t, block.HashesTo([]byte("something else")))
@ -113,7 +121,7 @@ func TestBlockHashesTo(t *testing.T) {
}
func TestBlockSize(t *testing.T) {
size := MakeBlock(int64(3), []Tx{Tx("Hello World")}, nil).Size()
size := MakeBlock(int64(3), []Tx{Tx("Hello World")}, nil, nil).Size()
if size <= 0 {
t.Fatal("Size of the block is zero or negative")
}
@ -124,7 +132,7 @@ func TestBlockString(t *testing.T) {
assert.Equal(t, "nil-Block", (*Block)(nil).StringIndented(""))
assert.Equal(t, "nil-Block", (*Block)(nil).StringShort())
block := MakeBlock(int64(3), []Tx{Tx("Hello World")}, nil)
block := MakeBlock(int64(3), []Tx{Tx("Hello World")}, nil, nil)
assert.NotEqual(t, "nil-Block", block.String())
assert.NotEqual(t, "nil-Block", block.StringIndented(""))
assert.NotEqual(t, "nil-Block", block.StringShort())


+ 2
- 2
version/version.go View File

@ -4,13 +4,13 @@ package version
const (
Maj = "0"
Min = "22"
Fix = "5"
Fix = "6"
)
var (
// Version is the current version of Tendermint
// Must be a string because scripts like dist.sh read this file.
Version = "0.22.5"
Version = "0.22.6"
// GitCommit is the current HEAD set using ldflags.
GitCommit string


Loading…
Cancel
Save