github.com/cosmos/cosmos-sdk@v0.50.10/types/simulation/rand_util.go (about)

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