github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/x/simulation/mock_tendermint.go (about)

     1  package simulation
     2  
     3  import (
     4  	"fmt"
     5  	"math/rand"
     6  	"sort"
     7  	"testing"
     8  	"time"
     9  
    10  	abci "github.com/fibonacci-chain/fbc/libs/tendermint/abci/types"
    11  	tmbytes "github.com/fibonacci-chain/fbc/libs/tendermint/libs/bytes"
    12  	tmtypes "github.com/fibonacci-chain/fbc/libs/tendermint/types"
    13  )
    14  
    15  type mockValidator struct {
    16  	val           abci.ValidatorUpdate
    17  	livenessState int
    18  }
    19  
    20  func (mv mockValidator) String() string {
    21  	return fmt.Sprintf("mockValidator{%s:%X power:%v state:%v}",
    22  		mv.val.PubKey.Type,
    23  		mv.val.PubKey.Data,
    24  		mv.val.Power,
    25  		mv.livenessState)
    26  }
    27  
    28  type mockValidators map[string]mockValidator
    29  
    30  // get mockValidators from abci validators
    31  func newMockValidators(r *rand.Rand, abciVals []abci.ValidatorUpdate,
    32  	params Params) mockValidators {
    33  
    34  	validators := make(mockValidators)
    35  	for _, validator := range abciVals {
    36  		str := fmt.Sprintf("%v", validator.PubKey)
    37  		liveliness := GetMemberOfInitialState(r,
    38  			params.InitialLivenessWeightings)
    39  
    40  		validators[str] = mockValidator{
    41  			val:           validator,
    42  			livenessState: liveliness,
    43  		}
    44  	}
    45  
    46  	return validators
    47  }
    48  
    49  // TODO describe usage
    50  func (vals mockValidators) getKeys() []string {
    51  	keys := make([]string, len(vals))
    52  	i := 0
    53  	for key := range vals {
    54  		keys[i] = key
    55  		i++
    56  	}
    57  	sort.Strings(keys)
    58  	return keys
    59  }
    60  
    61  //_________________________________________________________________________________
    62  
    63  // randomProposer picks a random proposer from the current validator set
    64  func (vals mockValidators) randomProposer(r *rand.Rand) tmbytes.HexBytes {
    65  	keys := vals.getKeys()
    66  	if len(keys) == 0 {
    67  		return nil
    68  	}
    69  	key := keys[r.Intn(len(keys))]
    70  	proposer := vals[key].val
    71  	pk, err := tmtypes.PB2TM.PubKey(proposer.PubKey)
    72  	if err != nil {
    73  		panic(err)
    74  	}
    75  	return pk.Address()
    76  }
    77  
    78  // updateValidators mimicks Tendermint's update logic
    79  // nolint: unparam
    80  func updateValidators(tb testing.TB, r *rand.Rand, params Params,
    81  	current map[string]mockValidator, updates []abci.ValidatorUpdate,
    82  	event func(route, op, evResult string)) map[string]mockValidator {
    83  
    84  	for _, update := range updates {
    85  		str := fmt.Sprintf("%v", update.PubKey)
    86  
    87  		if update.Power == 0 {
    88  			if _, ok := current[str]; !ok {
    89  				tb.Fatalf("tried to delete a nonexistent validator")
    90  			}
    91  			event("end_block", "validator_updates", "kicked")
    92  			delete(current, str)
    93  
    94  		} else if mVal, ok := current[str]; ok {
    95  			// validator already exists
    96  			mVal.val = update
    97  			event("end_block", "validator_updates", "updated")
    98  
    99  		} else {
   100  			// Set this new validator
   101  			current[str] = mockValidator{
   102  				update,
   103  				GetMemberOfInitialState(r, params.InitialLivenessWeightings),
   104  			}
   105  			event("end_block", "validator_updates", "added")
   106  		}
   107  	}
   108  
   109  	return current
   110  }
   111  
   112  // RandomRequestBeginBlock generates a list of signing validators according to
   113  // the provided list of validators, signing fraction, and evidence fraction
   114  func RandomRequestBeginBlock(r *rand.Rand, params Params,
   115  	validators mockValidators, pastTimes []time.Time,
   116  	pastVoteInfos [][]abci.VoteInfo,
   117  	event func(route, op, evResult string), header abci.Header) abci.RequestBeginBlock {
   118  
   119  	if len(validators) == 0 {
   120  		return abci.RequestBeginBlock{
   121  			Header: header,
   122  		}
   123  	}
   124  
   125  	voteInfos := make([]abci.VoteInfo, len(validators))
   126  	for i, key := range validators.getKeys() {
   127  		mVal := validators[key]
   128  		mVal.livenessState = params.LivenessTransitionMatrix.NextState(r, mVal.livenessState)
   129  		signed := true
   130  
   131  		if mVal.livenessState == 1 {
   132  			// spotty connection, 50% probability of success
   133  			// See https://github.com/golang/go/issues/23804#issuecomment-365370418
   134  			// for reasoning behind computing like this
   135  			signed = r.Int63()%2 == 0
   136  		} else if mVal.livenessState == 2 {
   137  			// offline
   138  			signed = false
   139  		}
   140  
   141  		if signed {
   142  			event("begin_block", "signing", "signed")
   143  		} else {
   144  			event("begin_block", "signing", "missed")
   145  		}
   146  
   147  		pubkey, err := tmtypes.PB2TM.PubKey(mVal.val.PubKey)
   148  		if err != nil {
   149  			panic(err)
   150  		}
   151  		voteInfos[i] = abci.VoteInfo{
   152  			Validator: abci.Validator{
   153  				Address: pubkey.Address(),
   154  				Power:   mVal.val.Power,
   155  			},
   156  			SignedLastBlock: signed,
   157  		}
   158  	}
   159  
   160  	// return if no past times
   161  	if len(pastTimes) == 0 {
   162  		return abci.RequestBeginBlock{
   163  			Header: header,
   164  			LastCommitInfo: abci.LastCommitInfo{
   165  				Votes: voteInfos,
   166  			},
   167  		}
   168  	}
   169  
   170  	// TODO: Determine capacity before allocation
   171  	evidence := make([]abci.Evidence, 0)
   172  	for r.Float64() < params.EvidenceFraction {
   173  
   174  		height := header.Height
   175  		time := header.Time
   176  		vals := voteInfos
   177  
   178  		if r.Float64() < params.PastEvidenceFraction && header.Height > 1 {
   179  			height = int64(r.Intn(int(header.Height)-1)) + 1 // Tendermint starts at height 1
   180  			// array indices offset by one
   181  			time = pastTimes[height-1]
   182  			vals = pastVoteInfos[height-1]
   183  		}
   184  		validator := vals[r.Intn(len(vals))].Validator
   185  
   186  		var totalVotingPower int64
   187  		for _, val := range vals {
   188  			totalVotingPower += val.Validator.Power
   189  		}
   190  
   191  		evidence = append(evidence,
   192  			abci.Evidence{
   193  				Type:             tmtypes.ABCIEvidenceTypeDuplicateVote,
   194  				Validator:        validator,
   195  				Height:           height,
   196  				Time:             time,
   197  				TotalVotingPower: totalVotingPower,
   198  			},
   199  		)
   200  		event("begin_block", "evidence", "ok")
   201  	}
   202  
   203  	return abci.RequestBeginBlock{
   204  		Header: header,
   205  		LastCommitInfo: abci.LastCommitInfo{
   206  			Votes: voteInfos,
   207  		},
   208  		ByzantineValidators: evidence,
   209  	}
   210  }