diff --git a/CHANGELOG.md b/CHANGELOG.md index f3b92deb8..fce7d6ee3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## 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 *July 23th, 2018* diff --git a/Gopkg.lock b/Gopkg.lock index 3c305f02b..6ec13589f 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -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" @@ -155,10 +154,8 @@ [[projects]] branch = "master" - digest = "1:5ab79470a1d0fb19b041a624415612f8236b3c06070161a910562f2b2d064355" name = "github.com/mitchellh/mapstructure" packages = ["."] - pruneopts = "UT" revision = "f15292f7a699fcc1a38a80977f80a046874ba8ac" [[projects]] @@ -189,10 +186,8 @@ [[projects]] branch = "master" - digest = "1:0f37e09b3e92aaeda5991581311f8dbf38944b36a3edec61cc2d1991f527554a" name = "github.com/prometheus/client_model" packages = ["go"] - pruneopts = "UT" revision = "5c3871d89910bfb32f5fcab2aa4b9ec68e65a99f" [[projects]] @@ -346,7 +341,6 @@ "cpu", "unix" ] - pruneopts = "UT" revision = "ac767d655b305d4e9612f5f6e33120b9176c4ad4" [[projects]] @@ -416,6 +410,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "9beb2d27dc19e3f9e2c7f416f312f7129f5441b1b53def42503fc6f7d3a54b16" + inputs-digest = "8e519c3716c259c6ecdb052889dd3602539fd98a3650dfbf50a4023e087a5d53" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 3ddd36deb..9aea7f4a0 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -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" diff --git a/blockchain/reactor_test.go b/blockchain/reactor_test.go index 21eaae4ba..11991bda4 100644 --- a/blockchain/reactor_test.go +++ b/blockchain/reactor_test.go @@ -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 } diff --git a/consensus/state.go b/consensus/state.go index 634f10313..f66a872eb 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -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 } diff --git a/p2p/listener.go b/p2p/listener.go index 3509ec69c..d73b3cabf 100644 --- a/p2p/listener.go +++ b/p2p/listener.go @@ -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 } diff --git a/p2p/switch.go b/p2p/switch.go index 636ca6d8e..da94fa4b0 100644 --- a/p2p/switch.go +++ b/p2p/switch.go @@ -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, diff --git a/rpc/core/abci.go b/rpc/core/abci.go index a5eede3fc..100176195 100644 --- a/rpc/core/abci.go +++ b/rpc/core/abci.go @@ -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, diff --git a/rpc/core/blocks.go b/rpc/core/blocks.go index 5815b60e3..671250db8 100644 --- a/rpc/core/blocks.go +++ b/rpc/core/blocks.go @@ -64,25 +64,15 @@ import ( // // 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. // diff --git a/rpc/core/blocks_test.go b/rpc/core/blocks_test.go new file mode 100644 index 000000000..da3920c22 --- /dev/null +++ b/rpc/core/blocks_test.go @@ -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) + } + } + +} diff --git a/state/execution_test.go b/state/execution_test.go index 81510fb9f..d214ae4f2 100644 --- a/state/execution_test.go +++ b/state/execution_test.go @@ -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 } diff --git a/state/state.go b/state/state.go index 3bc08dae3..fb589a240 100644 --- a/state/state.go +++ b/state/state.go @@ -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) diff --git a/types/block.go b/types/block.go index 488570764..304e8bdee 100644 --- a/types/block.go +++ b/types/block.go @@ -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 { diff --git a/types/block_test.go b/types/block_test.go index 1d27a7746..714029bf2 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -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()) diff --git a/version/version.go b/version/version.go index 706264372..5ef4aadbf 100644 --- a/version/version.go +++ b/version/version.go @@ -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