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 }