entropy.gno
1.96 Kb · 98 lines
1// Entropy generates fully deterministic, cost-effective, and hard to guess
2// numbers.
3//
4// It is designed both for single-usage, like seeding math/rand or for being
5// reused which increases the entropy and its cost effectiveness.
6//
7// Disclaimer: this package is unsafe and won't prevent others to guess values
8// in advance.
9//
10// It uses the Bernstein's hash djb2 to be CPU-cycle efficient.
11package entropy
12
13import (
14 "chain/runtime"
15 "chain/runtime/unsafe"
16 "math"
17 "time"
18)
19
20type Instance struct {
21 value uint32
22}
23
24func New() *Instance {
25 r := Instance{value: 5381}
26 r.addEntropy()
27 return &r
28}
29
30func FromSeed(seed uint32) *Instance {
31 r := Instance{value: seed}
32 r.addEntropy()
33 return &r
34}
35
36func (i *Instance) Seed() uint32 {
37 return i.value
38}
39
40func (i *Instance) djb2String(input string) {
41 for _, c := range input {
42 i.djb2Uint32(uint32(c))
43 }
44}
45
46// super fast random algorithm.
47// http://www.cse.yorku.ca/~oz/hash.html
48func (i *Instance) djb2Uint32(input uint32) {
49 i.value = (i.value << 5) + i.value + input
50}
51
52// AddEntropy uses various runtime variables to add entropy to the existing seed.
53func (i *Instance) addEntropy() {
54 // FIXME: reapply the 5381 initial value?
55
56 // inherit previous entropy
57 // nothing to do
58
59 // handle callers
60 {
61 currentRealm := unsafe.CurrentRealm().Address().String()
62 i.djb2String(currentRealm)
63 originCaller := unsafe.OriginCaller().String()
64 i.djb2String(originCaller)
65 }
66
67 // height
68 {
69 height := runtime.ChainHeight()
70 if height >= math.MaxUint32 {
71 height -= math.MaxUint32
72 }
73 i.djb2Uint32(uint32(height))
74 }
75
76 // time
77 {
78 secs := time.Now().Second()
79 i.djb2Uint32(uint32(secs))
80 nsecs := time.Now().Nanosecond()
81 i.djb2Uint32(uint32(nsecs))
82 }
83
84 // FIXME: compute other hard-to-guess but deterministic variables, like real gas?
85}
86
87func (i *Instance) Value() uint32 {
88 i.addEntropy()
89 return i.value
90}
91
92func (i *Instance) Value64() uint64 {
93 i.addEntropy()
94 high := i.value
95 i.addEntropy()
96
97 return (uint64(high) << 32) | uint64(i.value)
98}