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

     1  package simulation
     2  
     3  import (
     4  	"errors"
     5  	"math/big"
     6  	"math/rand"
     7  	"time"
     8  
     9  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    10  )
    11  
    12  const (
    13  	letterBytes   = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
    14  	letterIdxBits = 6                    // 6 bits to represent a letter index
    15  	letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
    16  	letterIdxMax  = 63 / letterIdxBits   // # of letter indices fitting in 63 bits
    17  )
    18  
    19  // shamelessly copied from
    20  // https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-golang#31832326
    21  
    22  // RandStringOfLength generates a random string of a particular length
    23  func RandStringOfLength(r *rand.Rand, n int) string {
    24  	b := make([]byte, n)
    25  	// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
    26  	for i, cache, remain := n-1, r.Int63(), letterIdxMax; i >= 0; {
    27  		if remain == 0 {
    28  			cache, remain = r.Int63(), letterIdxMax
    29  		}
    30  		if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
    31  			b[i] = letterBytes[idx]
    32  			i--
    33  		}
    34  		cache >>= letterIdxBits
    35  		remain--
    36  	}
    37  	return string(b)
    38  }
    39  
    40  // RandPositiveInt get a rand positive sdk.Int
    41  func RandPositiveInt(r *rand.Rand, max sdk.Dec) (sdk.Int, error) {
    42  	if !max.GTE(sdk.OneDec()) {
    43  		return sdk.Int{}, errors.New("max too small")
    44  	}
    45  	max = max.Sub(sdk.OneDec())
    46  	return sdk.NewIntFromBigInt(new(big.Int).Rand(r, max.BigInt())).Add(sdk.OneInt()), nil
    47  }
    48  
    49  // RandomAmount generates a random amount
    50  // Note: The range of RandomAmount includes max, and is, in fact, biased to return max as well as 0.
    51  func RandomAmount(r *rand.Rand, max sdk.Int) sdk.Int {
    52  	var randInt = big.NewInt(0)
    53  	switch r.Intn(10) {
    54  	case 0:
    55  		// randInt = big.NewInt(0)
    56  	case 1:
    57  		randInt = max.BigInt()
    58  	default: // NOTE: there are 10 total cases.
    59  		randInt = big.NewInt(0).Rand(r, max.BigInt()) // up to max - 1
    60  	}
    61  	return sdk.NewIntFromBigInt(randInt)
    62  }
    63  
    64  // RandomDecAmount generates a random decimal amount
    65  // Note: The range of RandomDecAmount includes max, and is, in fact, biased to return max as well as 0.
    66  func RandomDecAmount(r *rand.Rand, max sdk.Dec) sdk.Dec {
    67  	var randInt = big.NewInt(0)
    68  	switch r.Intn(10) {
    69  	case 0:
    70  		// randInt = big.NewInt(0)
    71  	case 1:
    72  		randInt = max.Int // the underlying big int with all precision bits.
    73  	default: // NOTE: there are 10 total cases.
    74  		randInt = big.NewInt(0).Rand(r, max.Int)
    75  	}
    76  	return sdk.NewDecFromBigIntWithPrec(randInt, sdk.Precision)
    77  }
    78  
    79  // RandTimestamp generates a random timestamp
    80  func RandTimestamp(r *rand.Rand) time.Time {
    81  	// json.Marshal breaks for timestamps greater with year greater than 9999
    82  	unixTime := r.Int63n(253373529600)
    83  	return time.Unix(unixTime, 0)
    84  }
    85  
    86  // RandIntBetween returns a random int between two numbers inclusively.
    87  func RandIntBetween(r *rand.Rand, min, max int) int {
    88  	return r.Intn(max-min) + min
    89  }
    90  
    91  // returns random subset of the provided coins
    92  // will return at least one coin unless coins argument is empty or malformed
    93  // i.e. 0 amt in coins
    94  func RandSubsetCoins(r *rand.Rand, coins sdk.Coins) sdk.Coins {
    95  	if len(coins) == 0 {
    96  		return sdk.Coins{}
    97  	}
    98  	// make sure at least one coin added
    99  	denomIdx := r.Intn(len(coins))
   100  	coin := coins[denomIdx]
   101  	amt, err := RandPositiveInt(r, coin.Amount)
   102  	// malformed coin. 0 amt in coins
   103  	if err != nil {
   104  		return sdk.Coins{}
   105  	}
   106  	subset := sdk.Coins{sdk.NewCoin(coin.Denom, amt)}
   107  	for i, c := range coins {
   108  		// skip denom that we already chose earlier
   109  		if i == denomIdx {
   110  			continue
   111  		}
   112  		// coin flip if multiple coins
   113  		// if there is single coin then return random amount of it
   114  		if r.Intn(2) == 0 && len(coins) != 1 {
   115  			continue
   116  		}
   117  
   118  		amt, err := RandPositiveInt(r, c.Amount)
   119  		// ignore errors and try another denom
   120  		if err != nil {
   121  			continue
   122  		}
   123  		subset = append(subset, sdk.NewCoin(c.Denom, amt))
   124  	}
   125  	return subset.Sort()
   126  }
   127  
   128  // DeriveRand derives a new Rand deterministically from another random source.
   129  // Unlike rand.New(rand.NewSource(seed)), the result is "more random"
   130  // depending on the source and state of r.
   131  //
   132  // NOTE: not crypto safe.
   133  func DeriveRand(r *rand.Rand) *rand.Rand {
   134  	const num = 8 // TODO what's a good number?  Too large is too slow.
   135  	ms := multiSource(make([]rand.Source, num))
   136  	for i := 0; i < num; i++ {
   137  		ms[i] = rand.NewSource(r.Int63())
   138  	}
   139  	return rand.New(ms)
   140  }
   141  
   142  type multiSource []rand.Source
   143  
   144  func (ms multiSource) Int63() (r int64) {
   145  	for _, source := range ms {
   146  		r ^= source.Int63()
   147  	}
   148  	return r
   149  }
   150  
   151  func (ms multiSource) Seed(seed int64) {
   152  	panic("multiSource Seed should not be called")
   153  }