Skip to content

Commit

Permalink
feat: add p/demo/entropy (gnolang#2487)
Browse files Browse the repository at this point in the history
- Revert partially "feat(stdlibs): add math/rand (gnolang#2455)
(f547d7d)
- Update p/demo/rand and rename to p/demo/entropy

---------

Signed-off-by: moul <94029+moul@users.noreply.github.com>
Co-authored-by: Marc Vertes <mvertes@free.fr>
  • Loading branch information
moul and mvertes authored Aug 9, 2024
1 parent 36c8b03 commit bd99fbe
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 2 deletions.
89 changes: 89 additions & 0 deletions examples/gno.land/p/demo/entropy/entropy.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Entropy generates fully deterministic, cost-effective, and hard to guess
// numbers.
//
// It is designed both for single-usage, like seeding math/rand or for being
// reused which increases the entropy and its cost effectiveness.
//
// Disclaimer: this package is unsafe and won't prevent others to guess values
// in advance.
//
// It uses the Bernstein's hash djb2 to be CPU-cycle efficient.
package entropy

import (
"math"
"std"
"time"
)

type Instance struct {
value uint32
}

func New() *Instance {
r := Instance{value: 5381}
r.addEntropy()
return &r
}

func FromSeed(seed uint32) *Instance {
r := Instance{value: seed}
r.addEntropy()
return &r
}

func (i *Instance) Seed() uint32 {
return i.value
}

func (i *Instance) djb2String(input string) {
for _, c := range input {
i.djb2Uint32(uint32(c))
}
}

// super fast random algorithm.
// http://www.cse.yorku.ca/~oz/hash.html
func (i *Instance) djb2Uint32(input uint32) {
i.value = (i.value << 5) + i.value + input
}

// AddEntropy uses various runtime variables to add entropy to the existing seed.
func (i *Instance) addEntropy() {
// FIXME: reapply the 5381 initial value?

// inherit previous entropy
// nothing to do

// handle callers
{
caller1 := std.GetCallerAt(1).String()
i.djb2String(caller1)
caller2 := std.GetCallerAt(2).String()
i.djb2String(caller2)
}

// height
{
height := std.GetHeight()
if height >= math.MaxUint32 {
height -= math.MaxUint32
}
i.djb2Uint32(uint32(height))
}

// time
{
secs := time.Now().Second()
i.djb2Uint32(uint32(secs))
nsecs := time.Now().Nanosecond()
i.djb2Uint32(uint32(nsecs))
}

// FIXME: compute other hard-to-guess but deterministic variables, like real gas?
}

func (i *Instance) Value() uint32 {
i.addEntropy()
return i.value
}
46 changes: 46 additions & 0 deletions examples/gno.land/p/demo/entropy/entropy_test.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package entropy

import (
"std"
"strconv"
"testing"
)

func TestInstance(t *testing.T) {
instance := New()
if instance == nil {
t.Errorf("instance should not be nil")
}
}

func TestInstanceValue(t *testing.T) {
baseEntropy := New()
baseResult := computeValue(t, baseEntropy)

sameHeightEntropy := New()
sameHeightResult := computeValue(t, sameHeightEntropy)

if baseResult != sameHeightResult {
t.Errorf("should have the same result: new=%s, base=%s", sameHeightResult, baseResult)
}

std.TestSkipHeights(1)
differentHeightEntropy := New()
differentHeightResult := computeValue(t, differentHeightEntropy)

if baseResult == differentHeightResult {
t.Errorf("should have different result: new=%s, base=%s", differentHeightResult, baseResult)
}
}

func computeValue(t *testing.T, r *Instance) string {
t.Helper()

out := ""
for i := 0; i < 10; i++ {
val := int(r.Value())
out += strconv.Itoa(val) + " "
}

return out
}
1 change: 1 addition & 0 deletions examples/gno.land/p/demo/entropy/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module gno.land/p/demo/entropy
56 changes: 56 additions & 0 deletions examples/gno.land/p/demo/entropy/z_filetest.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package main

import (
"std"

"gno.land/p/demo/entropy"
)

func main() {
// initial
println("---")
r := entropy.New()
println(r.Value())
println(r.Value())
println(r.Value())
println(r.Value())
println(r.Value())

// should be the same
println("---")
r = entropy.New()
println(r.Value())
println(r.Value())
println(r.Value())
println(r.Value())
println(r.Value())

std.TestSkipHeights(1)
println("---")
r = entropy.New()
println(r.Value())
println(r.Value())
println(r.Value())
println(r.Value())
println(r.Value())
}

// Output:
// ---
// 4129293727
// 2141104956
// 1950222777
// 3348280598
// 438354259
// ---
// 4129293727
// 2141104956
// 1950222777
// 3348280598
// 438354259
// ---
// 49506731
// 1539580078
// 2695928529
// 1895482388
// 3462727799
1 change: 1 addition & 0 deletions examples/gno.land/r/demo/art/gnoface/gno.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module gno.land/r/demo/art/gnoface

require (
gno.land/p/demo/entropy v0.0.0-latest
gno.land/p/demo/uassert v0.0.0-latest
gno.land/p/demo/ufmt v0.0.0-latest
)
4 changes: 2 additions & 2 deletions examples/gno.land/r/demo/art/gnoface/gnoface.gno
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ package gnoface

import (
"math/rand"
"std"
"strconv"
"strings"

"gno.land/p/demo/entropy"
"gno.land/p/demo/ufmt"
)

func Render(path string) string {
seed := uint64(std.GetHeight())
seed := uint64(entropy.New().Value())

path = strings.TrimSpace(path)
if path != "" {
Expand Down

0 comments on commit bd99fbe

Please sign in to comment.