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  }