2016-06-26 00:40:53 -04:00
|
|
|
package consensus
|
|
|
|
|
|
|
|
import (
|
2016-11-23 18:20:46 -05:00
|
|
|
"fmt"
|
2016-06-26 00:40:53 -04:00
|
|
|
"sync"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/tendermint/tendermint/config/tendermint_test"
|
|
|
|
|
|
|
|
"github.com/tendermint/go-events"
|
|
|
|
"github.com/tendermint/go-p2p"
|
|
|
|
"github.com/tendermint/tendermint/types"
|
2016-11-23 18:20:46 -05:00
|
|
|
"github.com/tendermint/tmsp/example/dummy"
|
2016-06-26 00:40:53 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
config = tendermint_test.ResetConfig("consensus_reactor_test")
|
|
|
|
}
|
|
|
|
|
2016-11-23 18:20:46 -05:00
|
|
|
//----------------------------------------------
|
|
|
|
// in-process testnets
|
|
|
|
|
|
|
|
// Ensure a testnet makes blocks
|
2016-09-13 16:50:13 -04:00
|
|
|
func TestReactor(t *testing.T) {
|
2016-06-26 00:40:53 -04:00
|
|
|
N := 4
|
2016-12-22 22:02:58 -05:00
|
|
|
css := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true))
|
2016-06-26 00:40:53 -04:00
|
|
|
reactors := make([]*ConsensusReactor, N)
|
|
|
|
eventChans := make([]chan interface{}, N)
|
|
|
|
for i := 0; i < N; i++ {
|
2016-12-22 22:02:58 -05:00
|
|
|
reactors[i] = NewConsensusReactor(css[i], true) // so we dont start the consensus states
|
2016-06-26 00:40:53 -04:00
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
2016-11-23 18:20:46 -05:00
|
|
|
// make connected switches and start all reactors
|
2016-06-26 00:40:53 -04:00
|
|
|
p2p.MakeConnectedSwitches(N, func(i int, s *p2p.Switch) *p2p.Switch {
|
|
|
|
s.AddReactor("CONSENSUS", reactors[i])
|
|
|
|
return s
|
2016-09-13 22:25:11 -04:00
|
|
|
}, p2p.Connect2Switches)
|
2016-06-26 00:40:53 -04:00
|
|
|
|
2016-12-22 22:02:58 -05:00
|
|
|
// start the state machines
|
|
|
|
for i := 0; i < N; i++ {
|
|
|
|
s := reactors[i].conS.GetState()
|
|
|
|
reactors[i].SwitchToConsensus(s)
|
|
|
|
}
|
|
|
|
|
2016-06-26 00:40:53 -04:00
|
|
|
// wait till everyone makes the first new block
|
2016-11-23 18:20:46 -05:00
|
|
|
timeoutWaitGroup(t, N, func(wg *sync.WaitGroup, j int) {
|
|
|
|
<-eventChans[j]
|
|
|
|
wg.Done()
|
|
|
|
})
|
2016-06-26 00:40:53 -04:00
|
|
|
}
|
2016-06-26 15:33:11 -04:00
|
|
|
|
2016-11-23 18:20:46 -05:00
|
|
|
//-------------------------------------------------------------
|
|
|
|
// ensure we can make blocks despite cycling a validator set
|
2016-06-26 15:33:11 -04:00
|
|
|
|
2016-11-23 18:20:46 -05:00
|
|
|
func TestValidatorSetChanges(t *testing.T) {
|
|
|
|
nPeers := 8
|
|
|
|
nVals := 4
|
2016-12-22 22:02:58 -05:00
|
|
|
css := randConsensusNetWithPeers(nVals, nPeers, "consensus_val_set_changes_test", newMockTickerFunc(true))
|
2016-11-23 18:20:46 -05:00
|
|
|
reactors := make([]*ConsensusReactor, nPeers)
|
|
|
|
eventChans := make([]chan interface{}, nPeers)
|
|
|
|
for i := 0; i < nPeers; i++ {
|
2016-12-19 19:50:40 -05:00
|
|
|
reactors[i] = NewConsensusReactor(css[i], true) // so we dont start the consensus states
|
2016-06-26 15:33:11 -04:00
|
|
|
|
|
|
|
eventSwitch := events.NewEventSwitch()
|
|
|
|
_, err := eventSwitch.Start()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Failed to start switch: %v", err)
|
|
|
|
}
|
2016-09-13 22:25:11 -04:00
|
|
|
|
2016-11-23 18:20:46 -05:00
|
|
|
reactors[i].SetEventSwitch(eventSwitch)
|
|
|
|
eventChans[i] = subscribeToEventRespond(eventSwitch, "tester", types.EventStringNewBlock())
|
|
|
|
}
|
|
|
|
p2p.MakeConnectedSwitches(nPeers, func(i int, s *p2p.Switch) *p2p.Switch {
|
|
|
|
s.AddReactor("CONSENSUS", reactors[i])
|
|
|
|
return s
|
|
|
|
}, p2p.Connect2Switches)
|
2016-09-13 22:25:11 -04:00
|
|
|
|
2016-12-19 19:50:40 -05:00
|
|
|
// now that everyone is connected, start the state machines
|
2016-12-22 21:51:58 -05:00
|
|
|
// If we started the state machines before everyone was connected,
|
|
|
|
// we'd block when the cs fires NewBlockEvent and the peers are trying to start their reactors
|
2016-12-19 19:50:40 -05:00
|
|
|
for i := 0; i < nPeers; i++ {
|
|
|
|
s := reactors[i].conS.GetState()
|
|
|
|
reactors[i].SwitchToConsensus(s)
|
|
|
|
}
|
|
|
|
|
2016-11-23 18:20:46 -05:00
|
|
|
// map of active validators
|
|
|
|
activeVals := make(map[string]struct{})
|
|
|
|
for i := 0; i < nVals; i++ {
|
|
|
|
activeVals[string(css[i].privValidator.GetAddress())] = struct{}{}
|
2016-06-26 15:33:11 -04:00
|
|
|
}
|
2016-09-13 22:25:11 -04:00
|
|
|
|
2016-11-23 18:20:46 -05:00
|
|
|
// wait till everyone makes block 1
|
|
|
|
timeoutWaitGroup(t, nPeers, func(wg *sync.WaitGroup, j int) {
|
|
|
|
<-eventChans[j]
|
|
|
|
eventChans[j] <- struct{}{}
|
|
|
|
wg.Done()
|
2016-09-13 22:25:11 -04:00
|
|
|
})
|
|
|
|
|
2016-12-23 20:08:12 +04:00
|
|
|
//---------------------------------------------------------------------------
|
2016-12-26 18:39:58 +04:00
|
|
|
log.Info("Testing adding one validator")
|
2016-12-23 20:08:12 +04:00
|
|
|
|
|
|
|
newValidatorPubKey1 := css[nVals].privValidator.(*types.PrivValidator).PubKey
|
|
|
|
newValidatorTx1 := dummy.MakeValSetChangeTx(newValidatorPubKey1.Bytes(), uint64(testMinPower))
|
2016-09-13 22:25:11 -04:00
|
|
|
|
2016-11-23 18:20:46 -05:00
|
|
|
// wait till everyone makes block 2
|
|
|
|
// ensure the commit includes all validators
|
|
|
|
// send newValTx to change vals in block 3
|
2016-12-23 20:08:12 +04:00
|
|
|
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css, newValidatorTx1)
|
2016-09-13 22:25:11 -04:00
|
|
|
|
2016-11-23 18:20:46 -05:00
|
|
|
// wait till everyone makes block 3.
|
|
|
|
// it includes the commit for block 2, which is by the original validator set
|
|
|
|
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css)
|
2016-09-13 22:25:11 -04:00
|
|
|
|
2016-11-23 18:20:46 -05:00
|
|
|
// wait till everyone makes block 4.
|
|
|
|
// it includes the commit for block 3, which is by the original validator set
|
|
|
|
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css)
|
2016-09-13 22:25:11 -04:00
|
|
|
|
2016-11-23 18:20:46 -05:00
|
|
|
// the commits for block 4 should be with the updated validator set
|
2016-12-23 20:08:12 +04:00
|
|
|
activeVals[string(newValidatorPubKey1.Address())] = struct{}{}
|
2016-09-13 22:25:11 -04:00
|
|
|
|
2016-11-23 18:20:46 -05:00
|
|
|
// wait till everyone makes block 5
|
|
|
|
// it includes the commit for block 4, which should have the updated validator set
|
|
|
|
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css)
|
2016-06-26 15:33:11 -04:00
|
|
|
|
2016-12-23 20:08:12 +04:00
|
|
|
//---------------------------------------------------------------------------
|
2016-12-26 18:39:58 +04:00
|
|
|
log.Info("Testing changing the voting power of one validator")
|
2016-12-23 20:08:12 +04:00
|
|
|
|
|
|
|
updateValidatorTx1 := dummy.MakeValSetChangeTx(newValidatorPubKey1.Bytes(), 25)
|
|
|
|
previousTotalVotingPower := css[nVals].LastValidators.TotalVotingPower()
|
|
|
|
|
|
|
|
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css, updateValidatorTx1)
|
|
|
|
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css)
|
|
|
|
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css)
|
|
|
|
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css)
|
|
|
|
if css[nVals].LastValidators.TotalVotingPower() == previousTotalVotingPower {
|
|
|
|
t.Errorf("expected voting power to change (before: %d, after: %d)", previousTotalVotingPower, css[nVals].LastValidators.TotalVotingPower())
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
2016-12-26 18:39:58 +04:00
|
|
|
log.Info("Testing adding two validators at once")
|
2016-12-23 20:08:12 +04:00
|
|
|
|
|
|
|
newValidatorPubKey2 := css[nVals+1].privValidator.(*types.PrivValidator).PubKey
|
|
|
|
newValidatorTx2 := dummy.MakeValSetChangeTx(newValidatorPubKey2.Bytes(), uint64(testMinPower))
|
|
|
|
|
|
|
|
newValidatorPubKey3 := css[nVals+2].privValidator.(*types.PrivValidator).PubKey
|
|
|
|
newValidatorTx3 := dummy.MakeValSetChangeTx(newValidatorPubKey3.Bytes(), uint64(testMinPower))
|
|
|
|
|
|
|
|
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css, newValidatorTx2, newValidatorTx3)
|
|
|
|
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css)
|
|
|
|
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css)
|
|
|
|
activeVals[string(newValidatorPubKey2.Address())] = struct{}{}
|
|
|
|
activeVals[string(newValidatorPubKey3.Address())] = struct{}{}
|
|
|
|
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css)
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
2016-12-26 18:39:58 +04:00
|
|
|
log.Info("Testing removing two validators at once")
|
2016-12-23 20:08:12 +04:00
|
|
|
|
|
|
|
removeValidatorTx2 := dummy.MakeValSetChangeTx(newValidatorPubKey2.Bytes(), 0)
|
|
|
|
removeValidatorTx3 := dummy.MakeValSetChangeTx(newValidatorPubKey3.Bytes(), 0)
|
|
|
|
|
|
|
|
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css, removeValidatorTx2, removeValidatorTx3)
|
|
|
|
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css)
|
|
|
|
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css)
|
|
|
|
delete(activeVals, string(newValidatorPubKey2.Address()))
|
|
|
|
delete(activeVals, string(newValidatorPubKey3.Address()))
|
|
|
|
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css)
|
|
|
|
|
2016-06-26 15:33:11 -04:00
|
|
|
}
|
|
|
|
|
2016-11-23 18:20:46 -05:00
|
|
|
func waitForAndValidateBlock(t *testing.T, n int, activeVals map[string]struct{}, eventChans []chan interface{}, css []*ConsensusState, txs ...[]byte) {
|
|
|
|
timeoutWaitGroup(t, n, func(wg *sync.WaitGroup, j int) {
|
2016-12-17 13:24:54 -05:00
|
|
|
newBlockI := <-eventChans[j]
|
|
|
|
newBlock := newBlockI.(types.EventDataNewBlock).Block
|
|
|
|
log.Info("Got block", "height", newBlock.Height, "validator", j)
|
|
|
|
err := validateBlock(newBlock, activeVals)
|
2016-11-23 18:20:46 -05:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
2016-09-13 22:25:11 -04:00
|
|
|
}
|
2016-11-23 18:20:46 -05:00
|
|
|
for _, tx := range txs {
|
|
|
|
css[j].mempool.CheckTx(tx, nil)
|
2016-06-26 15:33:11 -04:00
|
|
|
}
|
|
|
|
|
2016-11-23 18:20:46 -05:00
|
|
|
eventChans[j] <- struct{}{}
|
|
|
|
wg.Done()
|
|
|
|
})
|
2016-09-13 22:25:11 -04:00
|
|
|
}
|
|
|
|
|
2016-11-23 18:20:46 -05:00
|
|
|
// expects high synchrony!
|
|
|
|
func validateBlock(block *types.Block, activeVals map[string]struct{}) error {
|
|
|
|
if block.LastCommit.Size() != len(activeVals) {
|
|
|
|
return fmt.Errorf("Commit size doesn't match number of active validators. Got %d, expected %d", block.LastCommit.Size(), len(activeVals))
|
2016-09-13 22:25:11 -04:00
|
|
|
}
|
2016-06-26 15:33:11 -04:00
|
|
|
|
2016-11-23 18:20:46 -05:00
|
|
|
for _, vote := range block.LastCommit.Precommits {
|
|
|
|
if _, ok := activeVals[string(vote.ValidatorAddress)]; !ok {
|
|
|
|
return fmt.Errorf("Found vote for unactive validator %X", vote.ValidatorAddress)
|
|
|
|
}
|
2016-06-26 15:33:11 -04:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-11-23 18:20:46 -05:00
|
|
|
func timeoutWaitGroup(t *testing.T, n int, f func(*sync.WaitGroup, int)) {
|
|
|
|
wg := new(sync.WaitGroup)
|
|
|
|
wg.Add(n)
|
|
|
|
for i := 0; i < n; i++ {
|
|
|
|
go f(wg, i)
|
|
|
|
}
|
2016-06-26 15:33:11 -04:00
|
|
|
|
2016-11-23 18:20:46 -05:00
|
|
|
done := make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
wg.Wait()
|
|
|
|
close(done)
|
|
|
|
}()
|
2016-06-26 15:33:11 -04:00
|
|
|
|
2016-11-23 18:20:46 -05:00
|
|
|
select {
|
|
|
|
case <-done:
|
2016-12-26 18:39:58 +04:00
|
|
|
case <-time.After(time.Second * 3):
|
2016-11-23 18:20:46 -05:00
|
|
|
t.Fatalf("Timed out waiting for all validators to commit a block")
|
|
|
|
}
|
2016-06-26 15:33:11 -04:00
|
|
|
}
|