Skip to content

Commit

Permalink
test: add random tree operation tests (#419)
Browse files Browse the repository at this point in the history
add new module

some fixes
  • Loading branch information
weiihann committed Nov 25, 2023
1 parent 1cb2ee6 commit d193f0b
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 0 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.19

require (
github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233
github.com/davecgh/go-spew v1.1.1
golang.org/x/sync v0.1.0
)

Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5U
github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ=
github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c=
github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY=
Expand Down
13 changes: 13 additions & 0 deletions proof_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,19 @@ import (
"github.com/crate-crypto/go-ipa/common"
)

func TestProofEmptyTree(t *testing.T) {
t.Parallel()

root := New()
root.Commit()

proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, [][]byte{ffx32KeyTest}, nil)
cfg := GetConfig()
if ok, err := VerifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil {
t.Fatalf("could not verify verkle proof: %s", ToDot(root))
}
}

func TestProofVerifyTwoLeaves(t *testing.T) {
t.Parallel()

Expand Down
156 changes: 156 additions & 0 deletions tree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,17 @@ import (
"encoding/hex"
"errors"
"fmt"
"io"
mRand "math/rand"
"reflect"
"runtime"
"sort"
"strings"
"testing"
"testing/quick"
"time"

"github.com/davecgh/go-spew/spew"
)

// a 32 byte value, as expected in the tree structure
Expand Down Expand Up @@ -1649,3 +1654,154 @@ func TestLeafNodeInsert(t *testing.T) {
t.Fatalf("hash should not be nil")
}
}

type randTest []randTestStep

type randTestStep struct {
op int
key []byte // for opInsert, opDelete, opGet
value []byte // for opInsert
err error // for debugging
}

const (
opInsert = iota
opDelete
opGet
opHash
opCommit
opProve
numOps
)

// Generate implements the quick.Generator interface from testing/quick
// to generate random test cases.
func (randTest) Generate(r *mRand.Rand, size int) reflect.Value {
var finishedFn = func() bool {
if size == 0 {
return true
}
size--
return false
}
return reflect.ValueOf(generateSteps(finishedFn, r))
}

func generateSteps(finished func() bool, r io.Reader) randTest {
var allKeys [][]byte
var tmp = []byte{0}
genKey := func() []byte {
_, err := r.Read(tmp)
if err != nil {
panic(err)
}

// first 2 operations always create a new key, then 10% of the time
// we create a new key or return an existing key otherwise.
if len(allKeys) < 2 || tmp[0]%100 > 90 {
// new key
key := make([]byte, 32)
_, err := r.Read(key)
if err != nil {
panic(err)
}

allKeys = append(allKeys, key)
return key
}
// use existing key
idx := int(tmp[0]) % len(allKeys)
return allKeys[idx]
}
var steps randTest
for !finished() {
_, err := r.Read(tmp)
if err != nil {
panic(err)
}

step := randTestStep{op: int(tmp[0]) % numOps}
switch step.op {
case opInsert:
step.key = genKey()
step.value = make([]byte, 32)
_, err := r.Read(step.value)
if err != nil {
panic(err)
}
case opGet, opDelete, opProve:
step.key = genKey()
}
steps = append(steps, step)
}
return steps
}

// runRandTestBool coerces error to boolean, for use in quick.Check
func runRandTestBool(rt randTest) bool {
return runRandTest(rt) == nil
}

func runRandTest(rt randTest) error {
var (
root = New()
keys = [][]byte{}
values = make(map[string]string)
cfg = GetConfig()
)
for i, step := range rt {
switch step.op {
case opInsert:
if err := root.Insert(step.key, step.value, nil); err != nil {
rt[i].err = err
}
keys = append(keys, step.key)
values[string(step.key)] = string(step.value)
case opDelete:
if _, err := root.Delete(step.key, nil); err != nil {
rt[i].err = err
}
delete(values, string(step.key))
case opGet:
v, err := root.Get(step.key, nil)
want := values[string(step.key)]
if string(v) != want {
rt[i].err = fmt.Errorf("mismatch for key %#x, got %#x want %#x, err %v", step.key, v, want, err)
}
case opProve:
if len(keys) == 0 {
continue
}
root.Commit()
proof, cis, zis, yis, _ := MakeVerkleMultiProof(root, nil, keys, nil)
if ok, err := VerifyVerkleProof(proof, cis, zis, yis, cfg); !ok || err != nil {
rt[i].err = fmt.Errorf("could not verify verkle proof: %s, err %v", ToDot(root), err)
}
// TODO: reconsider if we should avoid returning pointers in Hash() and Commit()
case opHash:
if hash := root.Hash(); hash == nil {
rt[i].err = fmt.Errorf("hash is nil")
}
case opCommit:
if comm := root.Commit(); comm == nil {
rt[i].err = fmt.Errorf("commit is nil")
}
}
// Abort the test on error.
if rt[i].err != nil {
return rt[i].err
}
}
return nil
}

func TestRandom(t *testing.T) {
t.Parallel()

if err := quick.Check(runRandTestBool, nil); err != nil {
if cerr, ok := err.(*quick.CheckError); ok {
t.Fatalf("random test iteration %d failed: %s", cerr.Count, spew.Sdump(cerr.In))
}
t.Fatal(err)
}
}

0 comments on commit d193f0b

Please sign in to comment.