entropy.gno

1.92 Kb ยท 97 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	"math"
15	"std"
16	"time"
17)
18
19type Instance struct {
20	value uint32
21}
22
23func New() *Instance {
24	r := Instance{value: 5381}
25	r.addEntropy()
26	return &r
27}
28
29func FromSeed(seed uint32) *Instance {
30	r := Instance{value: seed}
31	r.addEntropy()
32	return &r
33}
34
35func (i *Instance) Seed() uint32 {
36	return i.value
37}
38
39func (i *Instance) djb2String(input string) {
40	for _, c := range input {
41		i.djb2Uint32(uint32(c))
42	}
43}
44
45// super fast random algorithm.
46// http://www.cse.yorku.ca/~oz/hash.html
47func (i *Instance) djb2Uint32(input uint32) {
48	i.value = (i.value << 5) + i.value + input
49}
50
51// AddEntropy uses various runtime variables to add entropy to the existing seed.
52func (i *Instance) addEntropy() {
53	// FIXME: reapply the 5381 initial value?
54
55	// inherit previous entropy
56	// nothing to do
57
58	// handle callers
59	{
60		currentRealm := std.CurrentRealm().Address().String()
61		i.djb2String(currentRealm)
62		originCaller := std.OriginCaller().String()
63		i.djb2String(originCaller)
64	}
65
66	// height
67	{
68		height := std.ChainHeight()
69		if height >= math.MaxUint32 {
70			height -= math.MaxUint32
71		}
72		i.djb2Uint32(uint32(height))
73	}
74
75	// time
76	{
77		secs := time.Now().Second()
78		i.djb2Uint32(uint32(secs))
79		nsecs := time.Now().Nanosecond()
80		i.djb2Uint32(uint32(nsecs))
81	}
82
83	// FIXME: compute other hard-to-guess but deterministic variables, like real gas?
84}
85
86func (i *Instance) Value() uint32 {
87	i.addEntropy()
88	return i.value
89}
90
91func (i *Instance) Value64() uint64 {
92	i.addEntropy()
93	high := i.value
94	i.addEntropy()
95
96	return (uint64(high) << 32) | uint64(i.value)
97}