2018-07-16 18:25:15 -07:00
package simulation
2018-07-06 13:19:11 -07:00
import (
2018-07-17 11:33:53 -07:00
"encoding/json"
2018-07-06 13:19:11 -07:00
"fmt"
2018-08-26 18:04:52 -07:00
"math"
2018-07-06 13:19:11 -07:00
"math/rand"
2018-08-29 23:02:15 -07:00
"os"
2018-09-08 01:29:24 -07:00
"os/signal"
2018-09-11 02:18:58 -07:00
"runtime/debug"
2018-08-16 14:45:07 -07:00
"sort"
2018-09-09 08:34:09 -07:00
"strings"
2018-09-08 01:29:24 -07:00
"syscall"
2018-07-06 13:19:11 -07:00
"testing"
"time"
2018-08-16 08:36:15 -07:00
abci "github.com/tendermint/tendermint/abci/types"
2018-10-09 16:14:46 -07:00
common "github.com/tendermint/tendermint/libs/common"
2018-08-16 08:36:15 -07:00
tmtypes "github.com/tendermint/tendermint/types"
2018-07-16 18:15:50 -07:00
"github.com/cosmos/cosmos-sdk/baseapp"
2018-07-17 15:04:10 -07:00
sdk "github.com/cosmos/cosmos-sdk/types"
2018-07-06 13:19:11 -07:00
)
2018-07-17 11:33:53 -07:00
// Simulate tests application by sending random messages.
2018-09-12 21:53:55 -07:00
func Simulate ( t * testing . T , app * baseapp . BaseApp ,
2018-09-22 20:20:53 -07:00
appStateFn func ( r * rand . Rand , accs [ ] Account ) json . RawMessage ,
2018-09-12 21:53:55 -07:00
ops [ ] WeightedOperation , setups [ ] RandSetup ,
invariants [ ] Invariant , numBlocks int , blockSize int , commit bool ) error {
2018-07-06 13:19:11 -07:00
time := time . Now ( ) . UnixNano ( )
2018-09-11 02:18:58 -07:00
return SimulateFromSeed ( t , app , appStateFn , time , ops , setups , invariants , numBlocks , blockSize , commit )
2018-07-06 13:19:11 -07:00
}
2018-09-22 20:20:53 -07:00
func initChain ( r * rand . Rand , accounts [ ] Account , setups [ ] RandSetup , app * baseapp . BaseApp ,
appStateFn func ( r * rand . Rand , accounts [ ] Account ) json . RawMessage ) ( validators map [ string ] mockValidator ) {
res := app . InitChain ( abci . RequestInitChain { AppStateBytes : appStateFn ( r , accounts ) } )
2018-08-29 23:02:15 -07:00
validators = make ( map [ string ] mockValidator )
for _ , validator := range res . Validators {
2018-10-03 09:37:06 -07:00
str := fmt . Sprintf ( "%v" , validator . PubKey )
validators [ str ] = mockValidator { validator , GetMemberOfInitialState ( r , initialLivenessWeightings ) }
2018-08-29 23:02:15 -07:00
}
for i := 0 ; i < len ( setups ) ; i ++ {
2018-09-22 20:20:53 -07:00
setups [ i ] ( r , accounts )
2018-08-29 23:02:15 -07:00
}
return
}
func randTimestamp ( r * rand . Rand ) time . Time {
unixTime := r . Int63n ( int64 ( math . Pow ( 2 , 40 ) ) )
return time . Unix ( unixTime , 0 )
}
2018-07-17 11:33:53 -07:00
// SimulateFromSeed tests an application by running the provided
2018-07-06 13:19:11 -07:00
// operations, testing the provided invariants, but using the provided seed.
2018-09-12 21:53:55 -07:00
func SimulateFromSeed ( tb testing . TB , app * baseapp . BaseApp ,
2018-09-22 20:20:53 -07:00
appStateFn func ( r * rand . Rand , accs [ ] Account ) json . RawMessage ,
2018-09-12 21:53:55 -07:00
seed int64 , ops [ ] WeightedOperation , setups [ ] RandSetup , invariants [ ] Invariant ,
numBlocks int , blockSize int , commit bool ) ( simError error ) {
2018-09-11 02:18:58 -07:00
// in case we have to end early, don't os.Exit so that we can run cleanup code.
stopEarly := false
2018-09-01 12:32:24 -07:00
testingMode , t , b := getTestingMode ( tb )
2018-09-09 08:34:09 -07:00
fmt . Printf ( "Starting SimulateFromSeed with randomness created with seed %d\n" , int ( seed ) )
2018-07-06 13:19:11 -07:00
r := rand . New ( rand . NewSource ( seed ) )
2018-08-29 23:02:15 -07:00
timestamp := randTimestamp ( r )
2018-09-09 08:34:09 -07:00
fmt . Printf ( "Starting the simulation from time %v, unixtime %v\n" , timestamp . UTC ( ) . Format ( time . UnixDate ) , timestamp . Unix ( ) )
2018-08-26 18:04:52 -07:00
timeDiff := maxTimePerBlock - minTimePerBlock
2018-09-22 20:20:53 -07:00
accs := RandomAccounts ( r , numKeys )
2018-07-06 13:19:11 -07:00
2018-07-17 16:27:51 -07:00
// Setup event stats
events := make ( map [ string ] uint )
event := func ( what string ) {
2018-07-17 22:50:04 -07:00
events [ what ] ++
2018-07-17 16:27:51 -07:00
}
2018-09-22 20:20:53 -07:00
validators := initChain ( r , accs , setups , app , appStateFn )
2018-10-04 09:22:51 -07:00
// Second variable to keep pending validator set (delayed one block since TM 0.24)
// Initially this is the same as the initial validator set
nextValidators := validators
2018-07-06 13:19:11 -07:00
2018-10-19 11:00:27 -07:00
header := abci . Header { Height : 1 , Time : timestamp , ProposerAddress : randomProposer ( r , validators ) }
2018-08-16 08:36:15 -07:00
opCount := 0
2018-09-08 01:29:24 -07:00
// Setup code to catch SIGTERM's
c := make ( chan os . Signal )
2018-09-11 02:18:58 -07:00
signal . Notify ( c , os . Interrupt , syscall . SIGTERM , syscall . SIGINT )
2018-09-08 01:29:24 -07:00
go func ( ) {
2018-09-11 02:18:58 -07:00
receivedSignal := <- c
fmt . Printf ( "Exiting early due to %s, on block %d, operation %d\n" , receivedSignal , header . Height , opCount )
simError = fmt . Errorf ( "Exited due to %s" , receivedSignal )
stopEarly = true
2018-09-08 01:29:24 -07:00
} ( )
2018-09-01 12:32:24 -07:00
var pastTimes [ ] time . Time
2018-10-03 08:48:23 -07:00
var pastVoteInfos [ ] [ ] abci . VoteInfo
2018-08-31 17:01:23 -07:00
2018-10-03 08:48:23 -07:00
request := RandomRequestBeginBlock ( r , validators , livenessTransitionMatrix , evidenceFraction , pastTimes , pastVoteInfos , event , header )
2018-08-27 14:27:00 -07:00
// These are operations which have been queued by previous operations
operationQueue := make ( map [ int ] [ ] Operation )
2018-09-18 19:47:54 -07:00
timeOperationQueue := [ ] FutureOperation { }
2018-09-09 08:34:09 -07:00
var blockLogBuilders [ ] * strings . Builder
2018-07-06 13:19:11 -07:00
2018-09-11 02:18:58 -07:00
if testingMode {
2018-09-09 08:34:09 -07:00
blockLogBuilders = make ( [ ] * strings . Builder , numBlocks )
2018-08-30 00:13:31 -07:00
}
2018-09-09 08:34:09 -07:00
displayLogs := logPrinter ( testingMode , blockLogBuilders )
2018-10-28 21:37:19 -07:00
blockSimulator := createBlockSimulator ( testingMode , tb , t , event , invariants , ops , operationQueue , timeOperationQueue , numBlocks , blockSize , displayLogs )
2018-09-11 02:18:58 -07:00
if ! testingMode {
b . ResetTimer ( )
} else {
// Recover logs in case of panic
defer func ( ) {
if r := recover ( ) ; r != nil {
fmt . Println ( "Panic with err\n" , r )
stackTrace := string ( debug . Stack ( ) )
fmt . Println ( stackTrace )
displayLogs ( )
simError = fmt . Errorf ( "Simulation halted due to panic on block %d" , header . Height )
}
} ( )
}
2018-08-30 00:13:31 -07:00
2018-09-11 02:18:58 -07:00
for i := 0 ; i < numBlocks && ! stopEarly ; i ++ {
2018-09-01 12:32:24 -07:00
// Log the header time for future lookup
pastTimes = append ( pastTimes , header . Time )
2018-10-03 08:48:23 -07:00
pastVoteInfos = append ( pastVoteInfos , request . LastCommitInfo . Votes )
2018-09-01 12:32:24 -07:00
2018-10-18 18:55:14 -07:00
// Construct log writer
logWriter := addLogMessage ( testingMode , blockLogBuilders , i )
2018-08-16 08:36:15 -07:00
// Run the BeginBlock handler
2018-10-18 18:55:14 -07:00
logWriter ( "BeginBlock" )
2018-08-16 08:36:15 -07:00
app . BeginBlock ( request )
2018-09-01 12:32:24 -07:00
if testingMode {
2018-08-30 00:13:31 -07:00
// Make sure invariants hold at beginning of block
2018-10-26 04:42:53 -07:00
assertAllInvariants ( t , app , header , invariants , "BeginBlock" , displayLogs )
2018-08-30 00:13:31 -07:00
}
2018-07-06 13:19:11 -07:00
ctx := app . NewContext ( false , header )
2018-08-29 23:02:15 -07:00
2018-08-27 14:27:00 -07:00
// Run queued operations. Ignores blocksize if blocksize is too small
2018-10-18 18:55:14 -07:00
logWriter ( "Queued operations" )
2018-09-22 20:20:53 -07:00
numQueuedOpsRan := runQueuedOperations ( operationQueue , int ( header . Height ) , tb , r , app , ctx , accs , logWriter , displayLogs , event )
numQueuedTimeOpsRan := runQueuedTimeOperations ( timeOperationQueue , header . Time , tb , r , app , ctx , accs , logWriter , displayLogs , event )
2018-10-18 18:55:14 -07:00
if testingMode && onOperation {
// Make sure invariants hold at end of queued operations
2018-10-26 04:42:53 -07:00
assertAllInvariants ( t , app , header , invariants , "QueuedOperations" , displayLogs )
2018-10-18 18:55:14 -07:00
}
logWriter ( "Standard operations" )
2018-10-28 21:37:19 -07:00
operations := blockSimulator ( r , app , ctx , accs , header , logWriter )
2018-09-18 19:47:54 -07:00
opCount += operations + numQueuedOpsRan + numQueuedTimeOpsRan
2018-10-18 18:55:14 -07:00
if testingMode {
// Make sure invariants hold at end of block
2018-10-26 04:42:53 -07:00
assertAllInvariants ( t , app , header , invariants , "StandardOperations" , displayLogs )
2018-10-18 18:55:14 -07:00
}
2018-07-06 13:19:11 -07:00
2018-08-16 08:36:15 -07:00
res := app . EndBlock ( abci . RequestEndBlock { } )
2018-07-06 13:19:11 -07:00
header . Height ++
2018-08-16 08:36:15 -07:00
header . Time = header . Time . Add ( time . Duration ( minTimePerBlock ) * time . Second ) . Add ( time . Duration ( int64 ( r . Intn ( int ( timeDiff ) ) ) ) * time . Second )
2018-10-09 16:14:46 -07:00
header . ProposerAddress = randomProposer ( r , validators )
2018-09-09 08:34:09 -07:00
logWriter ( "EndBlock" )
2018-08-16 08:36:15 -07:00
2018-09-01 12:32:24 -07:00
if testingMode {
2018-08-30 00:13:31 -07:00
// Make sure invariants hold at end of block
2018-10-26 04:42:53 -07:00
assertAllInvariants ( t , app , header , invariants , "EndBlock" , displayLogs )
2018-08-30 00:13:31 -07:00
}
2018-08-17 07:19:33 -07:00
if commit {
app . Commit ( )
}
2018-10-22 20:55:49 -07:00
if header . ProposerAddress == nil {
fmt . Printf ( "\nSimulation stopped early as all validators have been unbonded, there is nobody left propose a block!\n" )
stopEarly = true
break
}
2018-08-17 07:19:33 -07:00
2018-08-16 08:36:15 -07:00
// Generate a random RequestBeginBlock with the current validator set for the next block
2018-10-03 08:48:23 -07:00
request = RandomRequestBeginBlock ( r , validators , livenessTransitionMatrix , evidenceFraction , pastTimes , pastVoteInfos , event , header )
2018-08-16 08:36:15 -07:00
2018-10-04 09:22:51 -07:00
// Update the validator set, which will be reflected in the application on the next block
validators = nextValidators
nextValidators = updateValidators ( tb , r , validators , res . ValidatorUpdates , event )
2018-07-06 13:19:11 -07:00
}
2018-09-11 02:18:58 -07:00
if stopEarly {
DisplayEvents ( events )
return
}
2018-08-30 11:43:56 -07:00
fmt . Printf ( "\nSimulation complete. Final height (blocks): %d, final time (seconds), : %v, operations ran %d\n" , header . Height , header . Time , opCount )
DisplayEvents ( events )
2018-09-11 02:18:58 -07:00
return nil
2018-08-30 11:43:56 -07:00
}
// Returns a function to simulate blocks. Written like this to avoid constant parameters being passed everytime, to minimize
// memory overhead
2018-10-29 09:37:42 -07:00
func createBlockSimulator ( testingMode bool , tb testing . TB , t * testing . T ,
event func ( string ) , invariants [ ] Invariant ,
ops [ ] WeightedOperation , operationQueue map [ int ] [ ] Operation , timeOperationQueue [ ] FutureOperation ,
totalNumBlocks int , avgBlockSize int , displayLogs func ( ) ) func (
r * rand . Rand ,
app * baseapp . BaseApp , ctx sdk . Context ,
accounts [ ] Account , header abci . Header , logWriter func ( string ) ) ( opCount int ) {
2018-10-28 21:37:19 -07:00
var (
lastBlocksizeState = 0 // state for [4 * uniform distribution]
totalOpWeight = 0
blocksize int
)
2018-09-12 00:16:52 -07:00
for i := 0 ; i < len ( ops ) ; i ++ {
totalOpWeight += ops [ i ] . Weight
}
selectOp := func ( r * rand . Rand ) Operation {
x := r . Intn ( totalOpWeight )
for i := 0 ; i < len ( ops ) ; i ++ {
if x <= ops [ i ] . Weight {
return ops [ i ] . Op
}
x -= ops [ i ] . Weight
}
// shouldn't happen
return ops [ 0 ] . Op
}
2018-10-28 21:37:19 -07:00
return func ( r * rand . Rand , app * baseapp . BaseApp , ctx sdk . Context ,
2018-09-22 20:20:53 -07:00
accounts [ ] Account , header abci . Header , logWriter func ( string ) ) ( opCount int ) {
2018-10-28 21:37:19 -07:00
lastBlocksizeState , blocksize = getBlockSize ( r , lastBlocksizeState , avgBlockSize )
2018-08-30 11:43:56 -07:00
for j := 0 ; j < blocksize ; j ++ {
2018-09-22 20:20:53 -07:00
logUpdate , futureOps , err := selectOp ( r ) ( r , app , ctx , accounts , event )
2018-08-30 11:43:56 -07:00
if err != nil {
2018-09-09 08:34:09 -07:00
displayLogs ( )
tb . Fatalf ( "error on operation %d within block %d, %v" , header . Height , opCount , err )
2018-08-30 11:43:56 -07:00
}
2018-09-09 08:34:09 -07:00
logWriter ( logUpdate )
2018-08-30 11:43:56 -07:00
2018-09-18 19:47:54 -07:00
queueOperations ( operationQueue , timeOperationQueue , futureOps )
2018-09-01 12:32:24 -07:00
if testingMode {
2018-08-30 11:43:56 -07:00
if onOperation {
2018-10-26 04:42:53 -07:00
assertAllInvariants ( t , app , header , invariants , fmt . Sprintf ( "operation: %v" , logUpdate ) , displayLogs )
2018-08-30 11:43:56 -07:00
}
if opCount % 50 == 0 {
2018-10-22 21:53:06 -07:00
fmt . Printf ( "\rSimulating... block %d/%d, operation %d/%d. " , header . Height , totalNumBlocks , opCount , blocksize )
2018-08-30 11:43:56 -07:00
}
}
opCount ++
}
2018-09-09 08:34:09 -07:00
return opCount
2018-08-30 11:43:56 -07:00
}
}
2018-09-01 12:32:24 -07:00
func getTestingMode ( tb testing . TB ) ( testingMode bool , t * testing . T , b * testing . B ) {
testingMode = false
2018-08-30 11:43:56 -07:00
if _t , ok := tb . ( * testing . T ) ; ok {
t = _t
2018-09-01 12:32:24 -07:00
testingMode = true
2018-08-30 00:35:02 -07:00
} else {
2018-08-30 11:43:56 -07:00
b = tb . ( * testing . B )
2018-08-30 00:13:31 -07:00
}
2018-08-30 11:43:56 -07:00
return
2018-07-06 13:19:11 -07:00
}
2018-10-29 09:37:42 -07:00
/ * * getBlockSize returns a block size as determined from the transition matrix .
It targets making average block size the provided parameter .
The three states it moves between are :
* "over stuffed" blocks with average size of 2 * avgblocksize ,
* normal sized blocks , hitting avgBlocksize on average ,
* and empty blocks , with no txs / only txs scheduled from the past . * /
2018-10-28 21:37:19 -07:00
func getBlockSize ( r * rand . Rand , lastBlockSizeState , avgBlockSize int ) ( state , blocksize int ) {
// TODO: Make blockSizeTransitionMatrix non-global
// TODO: Make default blocksize transitition matrix actually make the average blocksize equal to avgBlockSize
state = blockSizeTransitionMatrix . NextState ( r , lastBlockSizeState )
if state == 0 {
blocksize = r . Intn ( avgBlockSize * 4 )
} else if state == 1 {
blocksize = r . Intn ( avgBlockSize * 2 )
} else {
blocksize = 0
2018-08-29 23:02:15 -07:00
}
2018-10-28 21:37:19 -07:00
return
2018-08-29 23:02:15 -07:00
}
2018-08-27 14:27:00 -07:00
// adds all future operations into the operation queue.
2018-09-18 19:47:54 -07:00
func queueOperations ( queuedOperations map [ int ] [ ] Operation , queuedTimeOperations [ ] FutureOperation , futureOperations [ ] FutureOperation ) {
2018-08-27 14:27:00 -07:00
if futureOperations == nil {
return
}
for _ , futureOp := range futureOperations {
2018-09-18 19:47:54 -07:00
if futureOp . BlockHeight != 0 {
if val , ok := queuedOperations [ futureOp . BlockHeight ] ; ok {
queuedOperations [ futureOp . BlockHeight ] = append ( val , futureOp . Op )
} else {
queuedOperations [ futureOp . BlockHeight ] = [ ] Operation { futureOp . Op }
}
2018-08-27 14:27:00 -07:00
} else {
2018-09-18 19:47:54 -07:00
// TODO: Replace with proper sorted data structure, so don't have the copy entire slice
index := sort . Search ( len ( queuedTimeOperations ) , func ( i int ) bool { return queuedTimeOperations [ i ] . BlockTime . After ( futureOp . BlockTime ) } )
queuedTimeOperations = append ( queuedTimeOperations , FutureOperation { } )
copy ( queuedTimeOperations [ index + 1 : ] , queuedTimeOperations [ index : ] )
queuedTimeOperations [ index ] = futureOp
2018-08-27 14:27:00 -07:00
}
}
}
2018-09-03 09:47:24 -07:00
// nolint: errcheck
2018-08-29 23:02:15 -07:00
func runQueuedOperations ( queueOperations map [ int ] [ ] Operation , height int , tb testing . TB , r * rand . Rand , app * baseapp . BaseApp , ctx sdk . Context ,
2018-09-22 20:20:53 -07:00
accounts [ ] Account , logWriter func ( string ) , displayLogs func ( ) , event func ( string ) ) ( numOpsRan int ) {
2018-08-27 14:27:00 -07:00
if queuedOps , ok := queueOperations [ height ] ; ok {
numOps := len ( queuedOps )
for i := 0 ; i < numOps ; i ++ {
// For now, queued operations cannot queue more operations.
// If a need arises for us to support queued messages to queue more messages, this can
// be changed.
2018-09-22 20:20:53 -07:00
logUpdate , _ , err := queuedOps [ i ] ( r , app , ctx , accounts , event )
2018-09-09 08:34:09 -07:00
logWriter ( logUpdate )
2018-08-29 23:02:15 -07:00
if err != nil {
2018-09-09 08:34:09 -07:00
displayLogs ( )
2018-08-29 23:02:15 -07:00
tb . FailNow ( )
}
2018-08-27 14:27:00 -07:00
}
delete ( queueOperations , height )
2018-09-09 08:34:09 -07:00
return numOps
2018-08-27 14:27:00 -07:00
}
2018-09-09 08:34:09 -07:00
return 0
2018-08-27 14:27:00 -07:00
}
2018-09-18 19:47:54 -07:00
func runQueuedTimeOperations ( queueOperations [ ] FutureOperation , currentTime time . Time , tb testing . TB , r * rand . Rand , app * baseapp . BaseApp , ctx sdk . Context ,
2018-09-22 20:20:53 -07:00
accounts [ ] Account , logWriter func ( string ) , displayLogs func ( ) , event func ( string ) ) ( numOpsRan int ) {
2018-09-18 19:47:54 -07:00
numOpsRan = 0
for len ( queueOperations ) > 0 && currentTime . After ( queueOperations [ 0 ] . BlockTime ) {
// For now, queued operations cannot queue more operations.
// If a need arises for us to support queued messages to queue more messages, this can
// be changed.
2018-09-22 20:20:53 -07:00
logUpdate , _ , err := queueOperations [ 0 ] . Op ( r , app , ctx , accounts , event )
2018-09-18 19:47:54 -07:00
logWriter ( logUpdate )
if err != nil {
displayLogs ( )
tb . FailNow ( )
}
queueOperations = queueOperations [ 1 : ]
numOpsRan ++
}
return numOpsRan
}
2018-08-16 14:45:07 -07:00
func getKeys ( validators map [ string ] mockValidator ) [ ] string {
keys := make ( [ ] string , len ( validators ) )
i := 0
for key := range validators {
keys [ i ] = key
i ++
}
sort . Strings ( keys )
return keys
}
2018-10-09 16:14:46 -07:00
// randomProposer picks a random proposer from the current validator set
func randomProposer ( r * rand . Rand , validators map [ string ] mockValidator ) common . HexBytes {
keys := getKeys ( validators )
if len ( keys ) == 0 {
return nil
}
key := keys [ r . Intn ( len ( keys ) ) ]
proposer := validators [ key ] . val
pk , err := tmtypes . PB2TM . PubKey ( proposer . PubKey )
if err != nil {
panic ( err )
}
return pk . Address ( )
}
2018-08-16 08:36:15 -07:00
// RandomRequestBeginBlock generates a list of signing validators according to the provided list of validators, signing fraction, and evidence fraction
2018-08-31 15:22:37 -07:00
// nolint: unparam
2018-08-29 23:02:15 -07:00
func RandomRequestBeginBlock ( r * rand . Rand , validators map [ string ] mockValidator , livenessTransitions TransitionMatrix , evidenceFraction float64 ,
2018-10-03 08:48:23 -07:00
pastTimes [ ] time . Time , pastVoteInfos [ ] [ ] abci . VoteInfo , event func ( string ) , header abci . Header ) abci . RequestBeginBlock {
2018-08-16 08:36:15 -07:00
if len ( validators ) == 0 {
return abci . RequestBeginBlock { Header : header }
}
2018-10-03 08:48:23 -07:00
voteInfos := make ( [ ] abci . VoteInfo , len ( validators ) )
2018-08-16 08:36:15 -07:00
i := 0
2018-08-16 14:45:07 -07:00
for _ , key := range getKeys ( validators ) {
mVal := validators [ key ]
2018-08-16 08:36:15 -07:00
mVal . livenessState = livenessTransitions . NextState ( r , mVal . livenessState )
signed := true
if mVal . livenessState == 1 {
// spotty connection, 50% probability of success
// See https://github.com/golang/go/issues/23804#issuecomment-365370418
// for reasoning behind computing like this
signed = r . Int63 ( ) % 2 == 0
} else if mVal . livenessState == 2 {
// offline
signed = false
}
if signed {
event ( "beginblock/signing/signed" )
} else {
event ( "beginblock/signing/missed" )
}
2018-10-03 08:48:23 -07:00
pubkey , err := tmtypes . PB2TM . PubKey ( mVal . val . PubKey )
if err != nil {
panic ( err )
}
voteInfos [ i ] = abci . VoteInfo {
Validator : abci . Validator {
Address : pubkey . Address ( ) ,
2018-10-16 15:34:16 -07:00
Power : mVal . val . Power ,
2018-10-03 08:48:23 -07:00
} ,
2018-08-16 08:36:15 -07:00
SignedLastBlock : signed ,
}
i ++
}
2018-08-29 23:02:15 -07:00
// TODO: Determine capacity before allocation
2018-08-16 08:36:15 -07:00
evidence := make ( [ ] abci . Evidence , 0 )
2018-08-31 17:01:23 -07:00
// Anything but the first block
if len ( pastTimes ) > 0 {
for r . Float64 ( ) < evidenceFraction {
height := header . Height
time := header . Time
2018-10-03 08:48:23 -07:00
vals := voteInfos
2018-08-31 17:01:23 -07:00
if r . Float64 ( ) < pastEvidenceFraction {
2018-10-19 11:00:27 -07:00
height = int64 ( r . Intn ( int ( header . Height ) - 1 ) )
2018-08-31 17:01:23 -07:00
time = pastTimes [ height ]
2018-10-03 08:48:23 -07:00
vals = pastVoteInfos [ height ]
2018-08-31 17:01:23 -07:00
}
validator := vals [ r . Intn ( len ( vals ) ) ] . Validator
var totalVotingPower int64
for _ , val := range vals {
totalVotingPower += val . Validator . Power
}
evidence = append ( evidence , abci . Evidence {
Type : tmtypes . ABCIEvidenceTypeDuplicateVote ,
Validator : validator ,
Height : height ,
Time : time ,
TotalVotingPower : totalVotingPower ,
} )
event ( "beginblock/evidence" )
2018-08-16 08:36:15 -07:00
}
}
return abci . RequestBeginBlock {
Header : header ,
LastCommitInfo : abci . LastCommitInfo {
2018-10-03 08:48:23 -07:00
Votes : voteInfos ,
2018-08-16 08:36:15 -07:00
} ,
ByzantineValidators : evidence ,
}
}
// updateValidators mimicks Tendermint's update logic
2018-09-03 09:47:24 -07:00
// nolint: unparam
2018-10-03 08:48:23 -07:00
func updateValidators ( tb testing . TB , r * rand . Rand , current map [ string ] mockValidator , updates [ ] abci . ValidatorUpdate , event func ( string ) ) map [ string ] mockValidator {
2018-09-12 00:16:28 -07:00
2018-08-16 08:36:15 -07:00
for _ , update := range updates {
2018-10-03 09:37:06 -07:00
str := fmt . Sprintf ( "%v" , update . PubKey )
2018-08-16 08:36:15 -07:00
switch {
case update . Power == 0 :
2018-10-03 09:37:06 -07:00
if _ , ok := current [ str ] ; ! ok {
2018-09-12 00:16:28 -07:00
tb . Fatalf ( "tried to delete a nonexistent validator" )
}
2018-08-16 08:36:15 -07:00
event ( "endblock/validatorupdates/kicked" )
2018-10-03 09:37:06 -07:00
delete ( current , str )
2018-08-16 08:36:15 -07:00
default :
// Does validator already exist?
2018-10-03 09:37:06 -07:00
if mVal , ok := current [ str ] ; ok {
2018-08-16 08:36:15 -07:00
mVal . val = update
event ( "endblock/validatorupdates/updated" )
} else {
// Set this new validator
2018-10-03 09:37:06 -07:00
current [ str ] = mockValidator { update , GetMemberOfInitialState ( r , initialLivenessWeightings ) }
2018-08-16 08:36:15 -07:00
event ( "endblock/validatorupdates/added" )
}
}
}
2018-09-12 00:16:28 -07:00
2018-08-16 08:36:15 -07:00
return current
}