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"
2017-04-28 17:57:06 -04:00
"github.com/tendermint/abci/example/dummy"
2017-04-21 18:19:41 -04:00
"github.com/tendermint/tendermint/p2p"
2016-06-26 00:40:53 -04:00
"github.com/tendermint/tendermint/types"
2017-04-28 17:57:06 -04:00
"github.com/tendermint/tmlibs/events"
2016-06-26 00:40:53 -04:00
)
func init ( ) {
2017-05-02 00:43:49 -04:00
config = ResetConfig ( "consensus_reactor_test" )
2016-06-26 00:40:53 -04:00
}
2016-11-23 18:20:46 -05:00
//----------------------------------------------
// in-process testnets
2017-01-12 02:29:53 -05:00
func startConsensusNet ( t * testing . T , css [ ] * ConsensusState , N int , subscribeEventRespond bool ) ( [ ] * ConsensusReactor , [ ] chan interface { } ) {
2016-06-26 00:40:53 -04:00
reactors := make ( [ ] * ConsensusReactor , N )
eventChans := make ( [ ] chan interface { } , N )
2017-05-14 21:44:01 +02:00
logger := consensusLogger ( )
2016-06-26 00:40:53 -04:00
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
2017-05-14 21:44:01 +02:00
reactors [ i ] . SetLogger ( logger . With ( "validator" , i ) )
2016-06-26 00:40:53 -04:00
eventSwitch := events . NewEventSwitch ( )
2017-05-14 21:44:01 +02:00
eventSwitch . SetLogger ( logger . With ( "module" , "events" , "validator" , i ) )
2016-06-26 00:40:53 -04:00
_ , err := eventSwitch . Start ( )
if err != nil {
t . Fatalf ( "Failed to start switch: %v" , err )
}
reactors [ i ] . SetEventSwitch ( eventSwitch )
2017-01-12 02:29:53 -05:00
if subscribeEventRespond {
eventChans [ i ] = subscribeToEventRespond ( eventSwitch , "tester" , types . EventStringNewBlock ( ) )
} else {
eventChans [ i ] = subscribeToEvent ( eventSwitch , "tester" , types . EventStringNewBlock ( ) , 1 )
}
2016-06-26 00:40:53 -04:00
}
2016-11-23 18:20:46 -05:00
// make connected switches and start all reactors
2017-05-02 00:43:49 -04:00
p2p . MakeConnectedSwitches ( config . P2P , N , func ( i int , s * p2p . Switch ) * p2p . Switch {
2016-06-26 00:40:53 -04:00
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
2017-01-12 02:29:53 -05:00
// now that everyone is connected, start the state machines
// 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-22 22:02:58 -05:00
for i := 0 ; i < N ; i ++ {
s := reactors [ i ] . conS . GetState ( )
reactors [ i ] . SwitchToConsensus ( s )
}
2017-01-12 02:29:53 -05:00
return reactors , eventChans
}
func stopConsensusNet ( reactors [ ] * ConsensusReactor ) {
for _ , r := range reactors {
r . Switch . Stop ( )
}
}
2016-12-22 22:02:58 -05:00
2017-01-12 02:29:53 -05:00
// Ensure a testnet makes blocks
func TestReactor ( t * testing . T ) {
N := 4
css := randConsensusNet ( N , "consensus_reactor_test" , newMockTickerFunc ( true ) , newCounter )
reactors , eventChans := startConsensusNet ( t , css , N , false )
defer stopConsensusNet ( reactors )
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 ( )
2017-01-11 15:32:03 -05:00
} , css )
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
2017-01-11 15:32:03 -05:00
func TestVotingPowerChange ( t * testing . T ) {
nVals := 4
css := randConsensusNet ( nVals , "consensus_voting_power_changes_test" , newMockTickerFunc ( true ) , newPersistentDummy )
2017-01-12 02:29:53 -05:00
reactors , eventChans := startConsensusNet ( t , css , nVals , true )
defer stopConsensusNet ( reactors )
2017-01-11 15:32:03 -05:00
// map of active validators
activeVals := make ( map [ string ] struct { } )
for i := 0 ; i < nVals ; i ++ {
activeVals [ string ( css [ i ] . privValidator . GetAddress ( ) ) ] = struct { } { }
}
// wait till everyone makes block 1
timeoutWaitGroup ( t , nVals , func ( wg * sync . WaitGroup , j int ) {
<- eventChans [ j ]
eventChans [ j ] <- struct { } { }
wg . Done ( )
} , css )
//---------------------------------------------------------------------------
2017-05-02 11:53:32 +04:00
t . Log ( "---------------------------- Testing changing the voting power of one validator a few times" )
2017-01-11 15:32:03 -05:00
val1PubKey := css [ 0 ] . privValidator . ( * types . PrivValidator ) . PubKey
updateValidatorTx := dummy . MakeValSetChangeTx ( val1PubKey . Bytes ( ) , 25 )
previousTotalVotingPower := css [ 0 ] . GetRoundState ( ) . LastValidators . TotalVotingPower ( )
waitForAndValidateBlock ( t , nVals , activeVals , eventChans , css , updateValidatorTx )
waitForAndValidateBlock ( t , nVals , activeVals , eventChans , css )
waitForAndValidateBlock ( t , nVals , activeVals , eventChans , css )
waitForAndValidateBlock ( t , nVals , activeVals , eventChans , css )
if css [ 0 ] . GetRoundState ( ) . LastValidators . TotalVotingPower ( ) == previousTotalVotingPower {
t . Fatalf ( "expected voting power to change (before: %d, after: %d)" , previousTotalVotingPower , css [ 0 ] . GetRoundState ( ) . LastValidators . TotalVotingPower ( ) )
}
updateValidatorTx = dummy . MakeValSetChangeTx ( val1PubKey . Bytes ( ) , 2 )
previousTotalVotingPower = css [ 0 ] . GetRoundState ( ) . LastValidators . TotalVotingPower ( )
waitForAndValidateBlock ( t , nVals , activeVals , eventChans , css , updateValidatorTx )
waitForAndValidateBlock ( t , nVals , activeVals , eventChans , css )
waitForAndValidateBlock ( t , nVals , activeVals , eventChans , css )
waitForAndValidateBlock ( t , nVals , activeVals , eventChans , css )
if css [ 0 ] . GetRoundState ( ) . LastValidators . TotalVotingPower ( ) == previousTotalVotingPower {
t . Fatalf ( "expected voting power to change (before: %d, after: %d)" , previousTotalVotingPower , css [ 0 ] . GetRoundState ( ) . LastValidators . TotalVotingPower ( ) )
}
updateValidatorTx = dummy . MakeValSetChangeTx ( val1PubKey . Bytes ( ) , 100 )
previousTotalVotingPower = css [ 0 ] . GetRoundState ( ) . LastValidators . TotalVotingPower ( )
waitForAndValidateBlock ( t , nVals , activeVals , eventChans , css , updateValidatorTx )
waitForAndValidateBlock ( t , nVals , activeVals , eventChans , css )
waitForAndValidateBlock ( t , nVals , activeVals , eventChans , css )
waitForAndValidateBlock ( t , nVals , activeVals , eventChans , css )
if css [ 0 ] . GetRoundState ( ) . LastValidators . TotalVotingPower ( ) == previousTotalVotingPower {
t . Fatalf ( "expected voting power to change (before: %d, after: %d)" , previousTotalVotingPower , css [ 0 ] . GetRoundState ( ) . LastValidators . TotalVotingPower ( ) )
}
}
2016-11-23 18:20:46 -05:00
func TestValidatorSetChanges ( t * testing . T ) {
2017-01-12 02:29:53 -05:00
nPeers := 7
2016-11-23 18:20:46 -05:00
nVals := 4
2017-01-11 15:32:03 -05:00
css := randConsensusNetWithPeers ( nVals , nPeers , "consensus_val_set_changes_test" , newMockTickerFunc ( true ) , newPersistentDummy )
2017-01-12 02:29:53 -05:00
reactors , eventChans := startConsensusNet ( t , css , nPeers , true )
defer stopConsensusNet ( reactors )
2016-12-19 19:50:40 -05:00
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 ( )
2017-01-11 15:32:03 -05:00
} , css )
2016-09-13 22:25:11 -04:00
2016-12-23 20:08:12 +04:00
//---------------------------------------------------------------------------
2017-05-02 11:53:32 +04:00
t . Log ( "---------------------------- 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
//---------------------------------------------------------------------------
2017-05-02 11:53:32 +04:00
t . Log ( "---------------------------- Testing changing the voting power of one validator" )
2016-12-23 20:08:12 +04:00
2017-01-11 15:32:03 -05:00
updateValidatorPubKey1 := css [ nVals ] . privValidator . ( * types . PrivValidator ) . PubKey
updateValidatorTx1 := dummy . MakeValSetChangeTx ( updateValidatorPubKey1 . Bytes ( ) , 25 )
previousTotalVotingPower := css [ nVals ] . GetRoundState ( ) . LastValidators . TotalVotingPower ( )
2016-12-23 20:08:12 +04:00
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 )
2017-01-11 15:32:03 -05:00
if css [ nVals ] . GetRoundState ( ) . LastValidators . TotalVotingPower ( ) == previousTotalVotingPower {
t . Errorf ( "expected voting power to change (before: %d, after: %d)" , previousTotalVotingPower , css [ nVals ] . GetRoundState ( ) . LastValidators . TotalVotingPower ( ) )
2016-12-23 20:08:12 +04:00
}
//---------------------------------------------------------------------------
2017-05-02 11:53:32 +04:00
t . Log ( "---------------------------- 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 )
//---------------------------------------------------------------------------
2017-05-02 11:53:32 +04:00
t . Log ( "---------------------------- 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
}
2017-01-05 00:26:31 +04:00
// Check we can make blocks with skip_timeout_commit=false
func TestReactorWithTimeoutCommit ( t * testing . T ) {
N := 4
2017-01-11 18:37:36 -05:00
css := randConsensusNet ( N , "consensus_reactor_with_timeout_commit_test" , newMockTickerFunc ( false ) , newCounter )
2017-01-05 00:26:31 +04:00
// override default SkipTimeoutCommit == true for tests
for i := 0 ; i < N ; i ++ {
2017-05-02 00:43:49 -04:00
css [ i ] . config . SkipTimeoutCommit = false
2017-01-05 00:26:31 +04:00
}
2017-01-12 02:29:53 -05:00
reactors , eventChans := startConsensusNet ( t , css , N - 1 , false )
defer stopConsensusNet ( reactors )
2017-01-05 00:26:31 +04:00
// wait till everyone makes the first new block
timeoutWaitGroup ( t , N - 1 , func ( wg * sync . WaitGroup , j int ) {
<- eventChans [ j ]
wg . Done ( )
2017-01-11 18:37:36 -05:00
} , css )
2017-01-05 00:26:31 +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 ]
2017-04-28 17:57:06 -04:00
newBlock := newBlockI . ( types . TMEventData ) . Unwrap ( ) . ( types . EventDataNewBlock ) . Block
2017-05-02 11:53:32 +04:00
t . Logf ( "[WARN] Got block height=%v validator=%v" , newBlock . Height , j )
2016-12-17 13:24:54 -05:00
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 ( )
2017-01-11 15:32:03 -05:00
} , css )
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
}
2017-01-11 15:32:03 -05:00
func timeoutWaitGroup ( t * testing . T , n int , f func ( * sync . WaitGroup , int ) , css [ ] * ConsensusState ) {
2016-11-23 18:20:46 -05:00
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 :
2017-01-11 15:32:03 -05:00
case <- time . After ( time . Second * 10 ) :
for i , cs := range css {
fmt . Println ( "#################" )
fmt . Println ( "Validator" , i )
fmt . Println ( cs . GetRoundState ( ) )
fmt . Println ( "" )
}
panic ( "Timed out waiting for all validators to commit a block" )
2016-11-23 18:20:46 -05:00
}
2016-06-26 15:33:11 -04:00
}