tendermint/consensus/mempool_test.go

205 lines
5.7 KiB
Go
Raw Normal View History

package consensus
import (
"encoding/binary"
"testing"
"time"
2017-01-28 08:27:13 -08:00
abci "github.com/tendermint/abci/types"
"github.com/tendermint/tendermint/types"
2017-04-21 15:12:54 -07:00
. "github.com/tendermint/tmlibs/common"
)
func init() {
2017-05-01 21:43:49 -07:00
config = ResetConfig("consensus_mempool_test")
}
func TestNoProgressUntilTxsAvailable(t *testing.T) {
2017-07-13 12:03:19 -07:00
config := ResetConfig("consensus_mempool_txs_available_test")
config.Consensus.CreateEmptyBlocks = false
state, privVals := randGenesisState(1, false, 10)
2017-07-13 12:03:19 -07:00
cs := newConsensusStateWithConfig(config, state, privVals[0], NewCounterApplication())
cs.mempool.EnableTxsAvailable()
height, round := cs.Height, cs.Round
newBlockCh := subscribeToEvent(cs.evsw, "tester", types.EventStringNewBlock(), 1)
2017-07-13 12:03:19 -07:00
startTestRound(cs, height, round)
ensureNewStep(newBlockCh) // first block gets committed
ensureNoNewStep(newBlockCh)
deliverTxsRange(cs, 0, 2)
ensureNewStep(newBlockCh) // commit txs
ensureNewStep(newBlockCh) // commit updated app hash
2017-07-13 12:03:19 -07:00
ensureNoNewStep(newBlockCh)
2017-07-28 20:39:10 -07:00
}
func TestProgressInHigherRound(t *testing.T) {
config := ResetConfig("consensus_mempool_txs_available_test")
config.Consensus.CreateEmptyBlocks = false
2017-07-28 20:39:10 -07:00
state, privVals := randGenesisState(1, false, 10)
cs := newConsensusStateWithConfig(config, state, privVals[0], NewCounterApplication())
cs.mempool.EnableTxsAvailable()
height, round := cs.Height, cs.Round
newBlockCh := subscribeToEvent(cs.evsw, "tester", types.EventStringNewBlock(), 1)
newRoundCh := subscribeToEvent(cs.evsw, "tester", types.EventStringNewRound(), 1)
timeoutCh := subscribeToEvent(cs.evsw, "tester", types.EventStringTimeoutPropose(), 1)
cs.setProposal = func(proposal *types.Proposal) error {
if cs.Height == 2 && cs.Round == 0 {
// dont set the proposal in round 0 so we timeout and
// go to next round
cs.Logger.Info("Ignoring set proposal at height 2, round 0")
return nil
}
return cs.defaultSetProposal(proposal)
}
startTestRound(cs, height, round)
ensureNewStep(newRoundCh) // first round at first height
ensureNewStep(newBlockCh) // first block gets committed
ensureNewStep(newRoundCh) // first round at next height
deliverTxsRange(cs, 0, 2) // we deliver txs, but dont set a proposal so we get the next round
<-timeoutCh
ensureNewStep(newRoundCh) // wait for the next round
ensureNewStep(newBlockCh) // now we can commit the block
2017-07-13 12:03:19 -07:00
}
func deliverTxsRange(cs *ConsensusState, start, end int) {
// Deliver some txs.
for i := start; i < end; i++ {
txBytes := make([]byte, 8)
binary.BigEndian.PutUint64(txBytes, uint64(i))
err := cs.mempool.CheckTx(txBytes, nil)
if err != nil {
panic(Fmt("Error after CheckTx: %v", err))
}
}
2017-07-13 12:03:19 -07:00
}
func TestTxConcurrentWithCommit(t *testing.T) {
state, privVals := randGenesisState(1, false, 10)
cs := newConsensusState(state, privVals[0], NewCounterApplication())
height, round := cs.Height, cs.Round
newBlockCh := subscribeToEvent(cs.evsw, "tester", types.EventStringNewBlock(), 1)
NTxs := 10000
2017-07-13 12:03:19 -07:00
go deliverTxsRange(cs, 0, NTxs)
startTestRound(cs, height, round)
ticker := time.NewTicker(time.Second * 20)
for nTxs := 0; nTxs < NTxs; {
select {
case b := <-newBlockCh:
2017-04-28 14:57:06 -07:00
nTxs += b.(types.TMEventData).Unwrap().(types.EventDataNewBlock).Block.Header.NumTxs
case <-ticker.C:
2016-07-11 18:10:05 -07:00
panic("Timed out waiting to commit blocks with transactions")
}
}
}
2016-10-10 14:05:50 -07:00
func TestRmBadTx(t *testing.T) {
state, privVals := randGenesisState(1, false, 10)
app := NewCounterApplication()
cs := newConsensusState(state, privVals[0], app)
// increment the counter by 1
txBytes := make([]byte, 8)
binary.BigEndian.PutUint64(txBytes, uint64(0))
2017-01-12 12:55:03 -08:00
app.DeliverTx(txBytes)
2016-10-10 14:05:50 -07:00
app.Commit()
ch := make(chan struct{})
cbCh := make(chan struct{})
go func() {
// Try to send the tx through the mempool.
2017-01-12 12:53:32 -08:00
// CheckTx should not err, but the app should return a bad abci code
2016-10-10 14:05:50 -07:00
// and the tx should get removed from the pool
2017-01-12 12:53:32 -08:00
err := cs.mempool.CheckTx(txBytes, func(r *abci.Response) {
if r.GetCheckTx().Code != abci.CodeType_BadNonce {
2016-10-10 14:05:50 -07:00
t.Fatalf("expected checktx to return bad nonce, got %v", r)
}
cbCh <- struct{}{}
})
if err != nil {
t.Fatal("Error after CheckTx: %v", err)
}
// check for the tx
for {
time.Sleep(time.Second)
2017-03-05 00:40:36 -08:00
txs := cs.mempool.Reap(1)
if len(txs) == 0 {
ch <- struct{}{}
return
2016-10-10 14:05:50 -07:00
}
2017-03-05 00:40:36 -08:00
2016-10-10 14:05:50 -07:00
}
}()
// Wait until the tx returns
ticker := time.After(time.Second * 5)
select {
case <-cbCh:
// success
case <-ticker:
t.Fatalf("Timed out waiting for tx to return")
}
// Wait until the tx is removed
ticker = time.After(time.Second * 5)
select {
case <-ch:
// success
case <-ticker:
t.Fatalf("Timed out waiting for tx to be removed")
}
}
// CounterApplication that maintains a mempool state and resets it upon commit
type CounterApplication struct {
2017-02-13 18:07:26 -08:00
abci.BaseApplication
txCount int
mempoolTxCount int
}
func NewCounterApplication() *CounterApplication {
return &CounterApplication{}
}
2017-01-12 12:53:32 -08:00
func (app *CounterApplication) Info() abci.ResponseInfo {
return abci.ResponseInfo{Data: Fmt("txs:%v", app.txCount)}
}
2017-01-12 12:55:03 -08:00
func (app *CounterApplication) DeliverTx(tx []byte) abci.Result {
return runTx(tx, &app.txCount)
}
2017-01-12 12:53:32 -08:00
func (app *CounterApplication) CheckTx(tx []byte) abci.Result {
return runTx(tx, &app.mempoolTxCount)
}
2017-01-12 12:53:32 -08:00
func runTx(tx []byte, countPtr *int) abci.Result {
count := *countPtr
tx8 := make([]byte, 8)
copy(tx8[len(tx8)-len(tx):], tx)
txValue := binary.BigEndian.Uint64(tx8)
if txValue != uint64(count) {
2017-01-12 12:53:32 -08:00
return abci.ErrBadNonce.AppendLog(Fmt("Invalid nonce. Expected %v, got %v", count, txValue))
}
*countPtr += 1
2017-01-12 12:53:32 -08:00
return abci.OK
}
2017-01-12 12:53:32 -08:00
func (app *CounterApplication) Commit() abci.Result {
app.mempoolTxCount = app.txCount
if app.txCount == 0 {
2017-01-12 12:53:32 -08:00
return abci.OK
} else {
hash := make([]byte, 8)
binary.BigEndian.PutUint64(hash, uint64(app.txCount))
2017-01-12 12:53:32 -08:00
return abci.NewResultOK(hash, "")
}
}