github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/examples/gno.land/p/demo/rand/rand.gno (about)

     1  package rand
     2  
     3  // Disclaimer: this package is unsafe and won't prevent others to
     4  //             guess values in advance.
     5  //
     6  // the goal of this package is to implement a random library that
     7  // is fully deterministic for validators while being hard to guess.
     8  //
     9  // We use the Bernstein's hash djb2 to be CPU-cycle efficient.
    10  
    11  import (
    12  	"math/rand"
    13  	"std"
    14  	"time"
    15  )
    16  
    17  type Instance struct {
    18  	seed int64
    19  }
    20  
    21  func New() *Instance {
    22  	r := Instance{seed: 5381}
    23  	r.addEntropy()
    24  	return &r
    25  }
    26  
    27  func FromSeed(seed int64) *Instance {
    28  	r := Instance{seed: seed}
    29  	r.addEntropy()
    30  	return &r
    31  }
    32  
    33  func (i *Instance) Seed() int64 {
    34  	return i.seed
    35  }
    36  
    37  func (i *Instance) djb2String(input string) {
    38  	for _, c := range input {
    39  		i.djb2Int64(int64(c))
    40  	}
    41  }
    42  
    43  // super fast random algorithm.
    44  // http://www.cse.yorku.ca/~oz/hash.html
    45  func (i *Instance) djb2Int64(input int64) {
    46  	i.seed = (i.seed << 5) + i.seed + input
    47  }
    48  
    49  // AddEntropy uses various runtime variables to add entropy to the existing seed.
    50  func (i *Instance) addEntropy() {
    51  	// FIXME: reapply the 5381 initial value?
    52  
    53  	// inherit previous entropy
    54  	// nothing to do
    55  
    56  	// handle callers
    57  	{
    58  		caller1 := std.GetCallerAt(1).String()
    59  		i.djb2String(caller1)
    60  		caller2 := std.GetCallerAt(2).String()
    61  		i.djb2String(caller2)
    62  	}
    63  
    64  	// height
    65  	{
    66  		height := std.GetHeight()
    67  		i.djb2Int64(height)
    68  	}
    69  
    70  	// time
    71  	{
    72  		secs := time.Now().Second()
    73  		i.djb2Int64(int64(secs))
    74  		nsecs := time.Now().Nanosecond()
    75  		i.djb2Int64(int64(nsecs))
    76  	}
    77  
    78  	// FIXME: compute other hard-to-guess but deterministic variables, like real gas?
    79  }
    80  
    81  func (i *Instance) Float32() float32 {
    82  	i.addEntropy()
    83  	return rand.New(rand.NewSource(i.seed)).Float32()
    84  }
    85  
    86  func (i *Instance) Float64() float64 {
    87  	i.addEntropy()
    88  	return rand.New(rand.NewSource(i.seed)).Float64()
    89  }
    90  
    91  func (i *Instance) Int() int {
    92  	i.addEntropy()
    93  	return rand.New(rand.NewSource(i.seed)).Int()
    94  }
    95  
    96  func (i *Instance) Intn(n int) int {
    97  	i.addEntropy()
    98  	return rand.New(rand.NewSource(i.seed)).Intn(n)
    99  }
   100  
   101  func (i *Instance) Int63() int64 {
   102  	i.addEntropy()
   103  	return rand.New(rand.NewSource(i.seed)).Int63()
   104  }
   105  
   106  func (i *Instance) Int63n(n int64) int64 {
   107  	i.addEntropy()
   108  	return rand.New(rand.NewSource(i.seed)).Int63n(n)
   109  }
   110  
   111  func (i *Instance) Int31() int32 {
   112  	i.addEntropy()
   113  	return rand.New(rand.NewSource(i.seed)).Int31()
   114  }
   115  
   116  func (i *Instance) Int31n(n int32) int32 {
   117  	i.addEntropy()
   118  	return rand.New(rand.NewSource(i.seed)).Int31n(n)
   119  }
   120  
   121  func (i *Instance) Uint32() uint32 {
   122  	i.addEntropy()
   123  	return rand.New(rand.NewSource(i.seed)).Uint32()
   124  }
   125  
   126  func (i *Instance) Uint64() uint64 {
   127  	i.addEntropy()
   128  	return rand.New(rand.NewSource(i.seed)).Uint64()
   129  }
   130  
   131  func (i *Instance) Read(p []byte) (n int, err error) {
   132  	i.addEntropy()
   133  	return rand.New(rand.NewSource(i.seed)).Read(p)
   134  }
   135  
   136  func (i *Instance) Shuffle(n int, swap func(i, j int)) {
   137  	i.addEntropy()
   138  	rand.New(rand.NewSource(i.seed)).Shuffle(n, swap)
   139  }