From f837252ff140186a52a80701929e53da2e6faa9a Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Sun, 26 Jun 2016 00:40:53 -0400 Subject: [PATCH] consensus: test reactor --- consensus/common.go | 2 +- consensus/common_test.go | 14 ++++++- consensus/reactor_test.go | 82 +++++++++++++++++++++++++++++++++++++++ consensus/state_test.go | 2 +- 4 files changed, 97 insertions(+), 3 deletions(-) create mode 100644 consensus/reactor_test.go diff --git a/consensus/common.go b/consensus/common.go index 2e4a4dba1..02b2c4a41 100644 --- a/consensus/common.go +++ b/consensus/common.go @@ -6,7 +6,7 @@ import ( // NOTE: this is blocking func subscribeToEvent(evsw types.EventSwitch, receiver, eventID string, chanCap int) chan interface{} { - // listen for new round + // listen for event ch := make(chan interface{}, chanCap) types.AddListenerForEvent(evsw, receiver, eventID, func(data types.TMEventData) { ch <- data diff --git a/consensus/common_test.go b/consensus/common_test.go index 23fa8fadc..3a509ff3d 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -246,6 +246,18 @@ func randConsensusState(nValidators int) (*ConsensusState, []*validatorStub) { return cs, vss } +func randConsensusNet(nValidators int) []*ConsensusState { + genDoc, privVals := randGenesisDoc(nValidators, false, 10) + css := make([]*ConsensusState, nValidators) + for i := 0; i < nValidators; i++ { + db := dbm.NewMemDB() // each state needs its own db + state := sm.MakeGenesisState(db, genDoc) + state.Save() + css[i] = newConsensusState(state, privVals[i], counter.NewCounterApplication(true)) + } + return css +} + func subscribeToVoter(cs *ConsensusState, addr []byte) chan interface{} { voteCh0 := subscribeToEvent(cs.evsw, "tester", types.EventStringVote(), 1) voteCh := make(chan interface{}) @@ -274,8 +286,8 @@ func readVotes(ch chan interface{}, reads int) chan struct{} { } func randGenesisState(numValidators int, randPower bool, minPower int64) (*sm.State, []*types.PrivValidator) { - db := dbm.NewMemDB() genDoc, privValidators := randGenesisDoc(numValidators, randPower, minPower) + db := dbm.NewMemDB() s0 := sm.MakeGenesisState(db, genDoc) s0.Save() return s0, privValidators diff --git a/consensus/reactor_test.go b/consensus/reactor_test.go new file mode 100644 index 000000000..3bd9ed922 --- /dev/null +++ b/consensus/reactor_test.go @@ -0,0 +1,82 @@ +package consensus + +import ( + "net" + "sync" + "testing" + "time" + + "github.com/tendermint/tendermint/config/tendermint_test" + + . "github.com/tendermint/go-common" + dbm "github.com/tendermint/go-db" + "github.com/tendermint/go-events" + "github.com/tendermint/go-p2p" + bc "github.com/tendermint/tendermint/blockchain" + "github.com/tendermint/tendermint/types" +) + +func init() { + config = tendermint_test.ResetConfig("consensus_reactor_test") +} + +func resetConfigTimeouts() { + config.Set("log_level", "notice") + config.Set("timeout_propose", 2000) + // config.Set("timeout_propose_delta", 500) + // config.Set("timeout_prevote", 1000) + // config.Set("timeout_prevote_delta", 500) + // config.Set("timeout_precommit", 1000) + // config.Set("timeout_precommit_delta", 500) + // config.Set("timeout_commit", 1000) +} + +func TestReactor(t *testing.T) { + resetConfigTimeouts() + N := 4 + css := randConsensusNet(N) + reactors := make([]*ConsensusReactor, N) + eventChans := make([]chan interface{}, N) + for i := 0; i < N; i++ { + blockStoreDB := dbm.NewDB(Fmt("blockstore%d", i), config.GetString("db_backend"), config.GetString("db_dir")) + blockStore := bc.NewBlockStore(blockStoreDB) + reactors[i] = NewConsensusReactor(css[i], blockStore, false) + reactors[i].SetPrivValidator(css[i].privValidator) + + eventSwitch := events.NewEventSwitch() + _, err := eventSwitch.Start() + if err != nil { + t.Fatalf("Failed to start switch: %v", err) + } + + reactors[i].SetEventSwitch(eventSwitch) + eventChans[i] = subscribeToEvent(eventSwitch, "tester", types.EventStringNewBlock(), 1) + } + p2p.MakeConnectedSwitches(N, func(i int, s *p2p.Switch) *p2p.Switch { + s.AddReactor("CONSENSUS", reactors[i]) + return s + }, net.Pipe) + + // wait till everyone makes the first new block + wg := new(sync.WaitGroup) + wg.Add(N) + for i := 0; i < N; i++ { + go func(j int) { + <-eventChans[j] + wg.Done() + }(i) + } + + done := make(chan struct{}) + go func() { + wg.Wait() + close(done) + }() + + tick := time.NewTicker(time.Second * 3) + select { + case <-done: + case <-tick.C: + t.Fatalf("Timed out waiting for all validators to commit first block") + } +} diff --git a/consensus/state_test.go b/consensus/state_test.go index 13a2a3fcd..b54839d4f 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -30,7 +30,7 @@ x * TestBadProposal - 2 vals, bad proposal (bad block state hash), should prevot FullRoundSuite x * TestFullRound1 - 1 val, full successful round x * TestFullRoundNil - 1 val, full round of nil -x * TestFullRound2 - 2 vals, both required for fuill round +x * TestFullRound2 - 2 vals, both required for full round LockSuite x * TestLockNoPOL - 2 vals, 4 rounds. one val locked, precommits nil every round except first. x * TestLockPOLRelock - 4 vals, one precommits, other 3 polka at next round, so we unlock and precomit the polka