## Description The `init` method of the `Rand` struct was called twice: in the constructor and in the `init` function. Closes: #XXXX --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... * [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title * [ ] added `!` to the type prefix if API or client breaking change * [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#pr-targeting)) * [ ] provided a link to the relevant issue or specification * [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/main/docs/docs/building-modules) * [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#testing) * [ ] added a changelog entry to `CHANGELOG.md` * [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) * [ ] updated the relevant documentation or specification * [ ] reviewed "Files changed" and left comments if necessary * [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... * [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title * [ ] confirmed `!` in the type prefix if API or client breaking change * [ ] confirmed all author checklist items have been addressed * [ ] reviewed state machine logic * [ ] reviewed API design and naming * [ ] reviewed documentation is accurate * [ ] reviewed tests and test coverage * [ ] manually tested (if applicable)
148 lines
2.7 KiB
Go
148 lines
2.7 KiB
Go
package unsafe
|
|
|
|
import (
|
|
crand "crypto/rand"
|
|
mrand "math/rand"
|
|
"sync"
|
|
)
|
|
|
|
const (
|
|
strChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" // 62 characters
|
|
)
|
|
|
|
// Rand is a prng, that is seeded with OS randomness.
|
|
// The OS randomness is obtained from crypto/rand, however none of the provided
|
|
// methods are suitable for cryptographic usage.
|
|
// They all utilize math/rand's prng internally.
|
|
//
|
|
// All of the methods here are suitable for concurrent use.
|
|
// This is achieved by using a mutex lock on all of the provided methods.
|
|
type Rand struct {
|
|
sync.Mutex
|
|
rand *mrand.Rand
|
|
}
|
|
|
|
var grand *Rand
|
|
|
|
func init() {
|
|
grand = NewRand()
|
|
}
|
|
|
|
func NewRand() *Rand {
|
|
rand := &Rand{}
|
|
rand.init()
|
|
return rand
|
|
}
|
|
|
|
func (r *Rand) init() {
|
|
bz := cRandBytes(8)
|
|
var seed uint64
|
|
for i := 0; i < 8; i++ {
|
|
seed |= uint64(bz[i])
|
|
seed <<= 8
|
|
}
|
|
r.reset(int64(seed))
|
|
}
|
|
|
|
func (r *Rand) reset(seed int64) {
|
|
r.rand = mrand.New(mrand.NewSource(seed))
|
|
}
|
|
|
|
//----------------------------------------
|
|
// Global functions
|
|
|
|
func Seed(seed int64) {
|
|
grand.Seed(seed)
|
|
}
|
|
|
|
func Str(length int) string {
|
|
return grand.Str(length)
|
|
}
|
|
|
|
func Int63() int64 {
|
|
return grand.Int63()
|
|
}
|
|
|
|
func Int() int {
|
|
return grand.Int()
|
|
}
|
|
|
|
func Bytes(n int) []byte {
|
|
return grand.Bytes(n)
|
|
}
|
|
|
|
//----------------------------------------
|
|
// Rand methods
|
|
|
|
func (r *Rand) Seed(seed int64) {
|
|
r.Lock()
|
|
r.reset(seed)
|
|
r.Unlock()
|
|
}
|
|
|
|
// Str constructs a random alphanumeric string of given length.
|
|
func (r *Rand) Str(length int) string {
|
|
if length <= 0 {
|
|
return ""
|
|
}
|
|
|
|
chars := []byte{}
|
|
MAIN_LOOP:
|
|
for {
|
|
val := r.Int63()
|
|
for i := 0; i < 10; i++ {
|
|
v := int(val & 0x3f) // rightmost 6 bits
|
|
if v >= 62 { // only 62 characters in strChars
|
|
val >>= 6
|
|
continue
|
|
} else {
|
|
chars = append(chars, strChars[v])
|
|
if len(chars) == length {
|
|
break MAIN_LOOP
|
|
}
|
|
val >>= 6
|
|
}
|
|
}
|
|
}
|
|
|
|
return string(chars)
|
|
}
|
|
|
|
func (r *Rand) Int63() int64 {
|
|
r.Lock()
|
|
i63 := r.rand.Int63()
|
|
r.Unlock()
|
|
return i63
|
|
}
|
|
|
|
func (r *Rand) Int() int {
|
|
r.Lock()
|
|
i := r.rand.Int()
|
|
r.Unlock()
|
|
return i
|
|
}
|
|
|
|
// Bytes returns n random bytes generated from the internal
|
|
// prng.
|
|
func (r *Rand) Bytes(n int) []byte {
|
|
// cRandBytes isn't guaranteed to be fast so instead
|
|
// use random bytes generated from the internal PRNG
|
|
bs := make([]byte, n)
|
|
for i := 0; i < len(bs); i++ {
|
|
bs[i] = byte(r.Int() & 0xFF)
|
|
}
|
|
return bs
|
|
}
|
|
|
|
// NOTE: This relies on the os's random number generator.
|
|
// For real security, we should salt that with some seed.
|
|
// See github.com/cometbft/cometbft/crypto for a more secure reader.
|
|
func cRandBytes(numBytes int) []byte {
|
|
b := make([]byte, numBytes)
|
|
_, err := crand.Read(b)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return b
|
|
}
|