Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

provable_test.gno

4.70 Kb · 165 lines
  1package provable
  2
  3import (
  4	"strconv"
  5	"strings"
  6	"testing"
  7
  8	"gno.land/p/nym-moultest123/x/merkle/v0"
  9)
 10
 11func TestAppendAndVerify(cur realm, t *testing.T) {
 12	seed()
 13	i := Append(cross(cur), "a fourth commitment")
 14	if i != 3 || Size() != 4 {
 15		t.Fatalf("Append returned %d with Size %d, want 3 and 4", i, Size())
 16	}
 17	if Entry(i) != "a fourth commitment" {
 18		t.Errorf("Entry(%d) = %q", i, Entry(i))
 19	}
 20
 21	root := Root()
 22	for j := 0; j < Size(); j++ {
 23		path, before, after := ProofOf(j)
 24		if !Verify(root, j, Size(), Entry(j), path, before, after) {
 25			t.Errorf("entry %d: its own proof does not verify", j)
 26		}
 27	}
 28}
 29
 30// Append trims, so the proof must be over the trimmed entry.
 31func TestAppendTrims(cur realm, t *testing.T) {
 32	seed()
 33	i := Append(cross(cur), "  padded  ")
 34	if Entry(i) != "padded" {
 35		t.Fatalf("Entry = %q, want %q", Entry(i), "padded")
 36	}
 37	path, before, after := ProofOf(i)
 38	if !Verify(Root(), i, Size(), "padded", path, before, after) {
 39		t.Error("trimmed entry does not verify")
 40	}
 41	if Verify(Root(), i, Size(), "  padded  ", path, before, after) {
 42		t.Error("the untrimmed string verified, so the leaf is not what is stored")
 43	}
 44}
 45
 46func TestVerifyRejects(cur realm, t *testing.T) {
 47	seed()
 48	root := Root()
 49	path, before, after := ProofOf(1)
 50	other := "0000000000000000000000000000000000000000000000000000000000000000"
 51
 52	tests := []struct {
 53		name                       string
 54		root                       string
 55		index, total               int
 56		entry, path, before, after string
 57	}{
 58		{"wrong entry", root, 1, 3, "not in the log", path, before, after},
 59		{"another real entry", root, 1, 3, Entry(0), path, before, after},
 60		{"wrong index", root, 0, 3, Entry(1), path, before, after},
 61		{"wrong total", root, 1, 4, Entry(1), path, before, after},
 62		{"wrong root", other, 1, 3, Entry(1), path, before, after},
 63		{"root not hex", "zz", 1, 3, Entry(1), path, before, after},
 64		{"root wrong length", "abcd", 1, 3, Entry(1), path, before, after},
 65		{"empty path", root, 1, 3, Entry(1), "", before, after},
 66		{"peaks swapped", root, 1, 3, Entry(1), path, after, before},
 67		{"garbage path", root, 1, 3, Entry(1), "zz", before, after},
 68	}
 69	for _, tc := range tests {
 70		if Verify(tc.root, tc.index, tc.total, tc.entry, tc.path, tc.before, tc.after) {
 71			t.Errorf("%s: accepted, must be rejected", tc.name)
 72		}
 73	}
 74}
 75
 76// The log root is also the Tendermint simple-tree root over the same entries,
 77// so the fixed-leaf-set proof encoding verifies against it too. This is the
 78// claim the realm makes in Render; it is tested, not asserted.
 79func TestVerifyFixedAcceptsTendermintProof(cur realm, t *testing.T) {
 80	seed()
 81	Append(cross(cur), "a fourth commitment")
 82	Append(cross(cur), "a fifth commitment")
 83
 84	leaves := make([][]byte, Size())
 85	for i := range leaves {
 86		leaves[i] = []byte(Entry(i))
 87	}
 88	tree := merkle.New(leaves)
 89	if tree.RootHex() != Root() {
 90		t.Fatalf("tree root %s, log root %s", tree.RootHex(), Root())
 91	}
 92	for i := 0; i < Size(); i++ {
 93		p, err := tree.Proof(i)
 94		if err != nil {
 95			t.Fatalf("i=%d: %v", i, err)
 96		}
 97		if !VerifyFixed(Root(), i, Size(), Entry(i), p.Hex()) {
 98			t.Errorf("i=%d: Tendermint proof rejected by the realm", i)
 99		}
100		if VerifyFixed(Root(), i, Size(), "forged", p.Hex()) {
101			t.Errorf("i=%d: forged leaf accepted", i)
102		}
103	}
104}
105
106func TestCaps(t *testing.T) {
107	seed()
108	tests := []struct {
109		name  string
110		entry string
111	}{
112		{"empty", ""},
113		{"whitespace only", "   "},
114		{"too long", strings.Repeat("x", MaxEntryLen+1)},
115	}
116	for _, tc := range tests {
117		if got := panicked(func() { appendEntry(tc.entry) }); got == "" {
118			t.Errorf("%s: accepted, must panic", tc.name)
119		}
120	}
121	// A note of exactly MaxEntryLen is fine.
122	if got := panicked(func() { appendEntry(strings.Repeat("x", MaxEntryLen)) }); got != "" {
123		t.Errorf("entry of exactly MaxEntryLen panicked: %s", got)
124	}
125}
126
127func TestLogIsBounded(t *testing.T) {
128	seed()
129	for Size() < MaxEntries {
130		appendEntry("filler-" + strconv.Itoa(Size()))
131	}
132	if got := panicked(func() { appendEntry("one too many") }); got == "" {
133		t.Error("appending past MaxEntries was accepted")
134	}
135	seed()
136}
137
138func TestEntryRangeChecked(t *testing.T) {
139	seed()
140	for _, i := range []int{-1, 3, 999} {
141		if got := panicked(func() { Entry(i) }); got == "" {
142			t.Errorf("Entry(%d) did not panic", i)
143		}
144		if got := panicked(func() { ProofOf(i) }); got == "" {
145			t.Errorf("ProofOf(%d) did not panic", i)
146		}
147	}
148}
149
150// panicked runs f and returns the panic message, or "" if it returned
151// normally. Safe here because these calls are in-package and do not cross a
152// realm boundary, where a panic would be an unrecoverable abort instead.
153func panicked(f func()) (msg string) {
154	defer func() {
155		if r := recover(); r != nil {
156			if s, ok := r.(string); ok {
157				msg = s
158			} else {
159				msg = "panic"
160			}
161		}
162	}()
163	f()
164	return ""
165}