Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add p/demo/entropy #2487

Merged
merged 12 commits into from
Aug 9, 2024
85 changes: 85 additions & 0 deletions examples/gno.land/p/demo/entropy/entropy.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Entropy generates fully deterministic, cost-effective, and hard to guess
// numbers.
//
// It is designed both for for single-usage, like seeding math/rand or for being
moul marked this conversation as resolved.
Show resolved Hide resolved
// 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 (
"std"
"time"
)

type Instance struct {
value int64
}

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

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

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

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

// super fast random algorithm.
// http://www.cse.yorku.ca/~oz/hash.html
func (i *Instance) djb2Int64(input int64) {
mvertes marked this conversation as resolved.
Show resolved Hide resolved
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()
i.djb2Int64(height)
}

// time
{
secs := time.Now().Second()
i.djb2Int64(int64(secs))
nsecs := time.Now().Nanosecond()
i.djb2Int64(int64(nsecs))
}

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

func (i *Instance) Value() int64 {
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:
// ---
// 2206404796634629535
// 6369783909128776508
// -273967328977352263
// 3685013872379606294
// -321443877504368301
// ---
// 2206404796634629535
// 6369783909128776508
// -273967328977352263
// 3685013872379606294
// -321443877504368301
// ---
// 2389594832880495019
// 2483785090669486254
// 902493904282098385
// 5349860799650907156
// 3037689170578704503
5 changes: 4 additions & 1 deletion examples/gno.land/r/demo/art/gnoface/gno.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
module gno.land/r/demo/art/gnoface

require gno.land/p/demo/ufmt v0.0.0-latest
require (
gno.land/p/demo/entropy 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
Loading