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