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