Search Apps Documentation Source Content File Folder Download Copy Actions Download

proof_test.gno

8.06 Kb · 222 lines
  1package ics23_test
  2
  3import (
  4	"encoding/hex"
  5	"testing"
  6
  7	"gno.land/p/aib/ics23"
  8	"gno.land/p/nt/uassert/v0"
  9)
 10
 11func TestProofSpecEqual(t *testing.T) {
 12	newLeaf := func() *ics23.LeafOp {
 13		return &ics23.LeafOp{
 14			Prefix:       []byte{0},
 15			PrehashKey:   ics23.HashOp_NO_HASH,
 16			Hash:         ics23.HashOp_SHA256,
 17			PrehashValue: ics23.HashOp_SHA256,
 18			Length:       ics23.LengthOp_VAR_PROTO,
 19		}
 20	}
 21	newInner := func() *ics23.InnerSpec {
 22		return &ics23.InnerSpec{
 23			ChildOrder:      []int32{0, 1},
 24			MinPrefixLength: 4,
 25			MaxPrefixLength: 12,
 26			ChildSize:       33,
 27			EmptyChild:      nil,
 28			Hash:            ics23.HashOp_SHA256,
 29		}
 30	}
 31	newSpec := func() *ics23.ProofSpec {
 32		return &ics23.ProofSpec{
 33			LeafSpec:                   newLeaf(),
 34			InnerSpec:                  newInner(),
 35			MaxDepth:                   0,
 36			MinDepth:                   0,
 37			PrehashKeyBeforeComparison: false,
 38		}
 39	}
 40
 41	testCases := []struct {
 42		name     string
 43		malleate func(a, b *ics23.ProofSpec)
 44		expEqual bool
 45	}{
 46		{"equal — same fresh spec", func(a, b *ics23.ProofSpec) {}, true},
 47		{"equal — same as well-known IavlSpec", func(a, b *ics23.ProofSpec) {
 48			*a = *ics23.IavlSpec()
 49			*b = *ics23.IavlSpec()
 50		}, true},
 51		{"differ — MaxDepth", func(a, b *ics23.ProofSpec) { b.MaxDepth = 10 }, false},
 52		{"differ — MinDepth", func(a, b *ics23.ProofSpec) { b.MinDepth = 1 }, false},
 53		{"differ — PrehashKeyBeforeComparison", func(a, b *ics23.ProofSpec) { b.PrehashKeyBeforeComparison = true }, false},
 54		{"differ — LeafSpec.Hash", func(a, b *ics23.ProofSpec) { b.LeafSpec.Hash = ics23.HashOp_NO_HASH }, false},
 55		{"differ — LeafSpec.PrehashKey", func(a, b *ics23.ProofSpec) { b.LeafSpec.PrehashKey = ics23.HashOp_SHA256 }, false},
 56		{"differ — LeafSpec.PrehashValue", func(a, b *ics23.ProofSpec) { b.LeafSpec.PrehashValue = ics23.HashOp_NO_HASH }, false},
 57		{"differ — LeafSpec.Length", func(a, b *ics23.ProofSpec) { b.LeafSpec.Length = ics23.LengthOp_NO_PREFIX }, false},
 58		{"differ — LeafSpec.Prefix", func(a, b *ics23.ProofSpec) { b.LeafSpec.Prefix = []byte{1} }, false},
 59		{"differ — LeafSpec nil vs non-nil", func(a, b *ics23.ProofSpec) { b.LeafSpec = nil }, false},
 60		{"differ — InnerSpec.ChildSize", func(a, b *ics23.ProofSpec) { b.InnerSpec.ChildSize = 32 }, false},
 61		{"differ — InnerSpec.MinPrefixLength", func(a, b *ics23.ProofSpec) { b.InnerSpec.MinPrefixLength = 1 }, false},
 62		{"differ — InnerSpec.MaxPrefixLength", func(a, b *ics23.ProofSpec) { b.InnerSpec.MaxPrefixLength = 99 }, false},
 63		{"differ — InnerSpec.Hash", func(a, b *ics23.ProofSpec) { b.InnerSpec.Hash = ics23.HashOp_NO_HASH }, false},
 64		{"differ — InnerSpec.ChildOrder length", func(a, b *ics23.ProofSpec) { b.InnerSpec.ChildOrder = []int32{0, 1, 2} }, false},
 65		{"differ — InnerSpec.ChildOrder content", func(a, b *ics23.ProofSpec) { b.InnerSpec.ChildOrder = []int32{1, 0} }, false},
 66		{"differ — InnerSpec.EmptyChild", func(a, b *ics23.ProofSpec) { b.InnerSpec.EmptyChild = []byte{0, 0, 0} }, false},
 67		{"differ — InnerSpec nil vs non-nil", func(a, b *ics23.ProofSpec) { b.InnerSpec = nil }, false},
 68	}
 69	for _, tc := range testCases {
 70		t.Run(tc.name, func(t *testing.T) {
 71			a, b := newSpec(), newSpec()
 72			tc.malleate(a, b)
 73			uassert.Equal(t, tc.expEqual, a.Equal(b), "a.Equal(b)")
 74			// Equal must be symmetric.
 75			uassert.Equal(t, tc.expEqual, b.Equal(a), "b.Equal(a)")
 76		})
 77	}
 78}
 79
 80func TestProofSpecEqualNil(t *testing.T) {
 81	spec := ics23.IavlSpec()
 82
 83	var nilSpec *ics23.ProofSpec
 84	uassert.True(t, nilSpec.Equal(nil), "nil.Equal(nil) must be true")
 85	uassert.False(t, spec.Equal(nil), "non-nil.Equal(nil) must be false")
 86	uassert.False(t, nilSpec.Equal(spec), "nil.Equal(non-nil) must be false")
 87}
 88
 89func TestLeafOpEqual(t *testing.T) {
 90	var nilOp *ics23.LeafOp
 91	uassert.True(t, nilOp.Equal(nil))
 92
 93	a := &ics23.LeafOp{Hash: ics23.HashOp_SHA256, Prefix: []byte{0x01}}
 94	b := &ics23.LeafOp{Hash: ics23.HashOp_SHA256, Prefix: []byte{0x01}}
 95	uassert.True(t, a.Equal(b))
 96	uassert.False(t, a.Equal(nil))
 97
 98	b.Prefix = []byte{0x02}
 99	uassert.False(t, a.Equal(b))
100}
101
102func TestInnerSpecEqual(t *testing.T) {
103	var nilSpec *ics23.InnerSpec
104	uassert.True(t, nilSpec.Equal(nil))
105
106	a := &ics23.InnerSpec{ChildOrder: []int32{0, 1}, ChildSize: 33, Hash: ics23.HashOp_SHA256}
107	b := &ics23.InnerSpec{ChildOrder: []int32{0, 1}, ChildSize: 33, Hash: ics23.HashOp_SHA256}
108	uassert.True(t, a.Equal(b))
109	uassert.False(t, a.Equal(nil))
110
111	b.ChildSize = 32
112	uassert.False(t, a.Equal(b))
113}
114
115func TestLeafOpProtoMarshal(t *testing.T) {
116	t.Run("nil receiver returns nil", func(t *testing.T) {
117		var nilLeaf *ics23.LeafOp
118		uassert.Equal(t, 0, len(nilLeaf.ProtoMarshal()))
119	})
120
121	t.Run("matches expected wire format for IavlSpec.LeafSpec", func(t *testing.T) {
122		bz := ics23.IavlSpec().LeafSpec.ProtoMarshal()
123		// Field 1 (Hash=SHA256=1):     08 01
124		// Field 2 (PrehashKey=0):      omitted
125		// Field 3 (PrehashValue=1):    18 01
126		// Field 4 (Length=VAR_PROTO=1):20 01
127		// Field 5 (Prefix=[0x00]):     2a 01 00
128		uassert.Equal(t, "0801180120012a0100", hex.EncodeToString(bz))
129	})
130}
131
132func TestInnerSpecProtoMarshal(t *testing.T) {
133	t.Run("nil receiver returns nil", func(t *testing.T) {
134		var nilInner *ics23.InnerSpec
135		uassert.Equal(t, 0, len(nilInner.ProtoMarshal()))
136	})
137
138	t.Run("matches expected wire format for IavlSpec.InnerSpec", func(t *testing.T) {
139		bz := ics23.IavlSpec().InnerSpec.ProtoMarshal()
140		// Field 1 (ChildOrder=[0,1] packed): 0a 02 00 01
141		// Field 2 (ChildSize=33):            10 21
142		// Field 3 (MinPrefixLength=4):       18 04
143		// Field 4 (MaxPrefixLength=12):      20 0c
144		// Field 5 (EmptyChild=nil):          omitted
145		// Field 6 (Hash=SHA256=1):           30 01
146		uassert.Equal(t, "0a02000110211804200c3001", hex.EncodeToString(bz))
147	})
148}
149
150func TestProofSpecProtoMarshal(t *testing.T) {
151	t.Run("nil receiver returns nil", func(t *testing.T) {
152		var nilSpec *ics23.ProofSpec
153		uassert.Equal(t, 0, len(nilSpec.ProtoMarshal()))
154	})
155
156	t.Run("matches expected wire format for IavlSpec", func(t *testing.T) {
157		bz := ics23.IavlSpec().ProtoMarshal()
158		// Field 1 (LeafSpec, len=9):  0a 09 <leaf bytes>
159		// Field 2 (InnerSpec, len=12):12 0c <inner bytes>
160		// Fields 3,4,5: omitted (zero defaults)
161		want := "0a090801180120012a0100" + "120c0a02000110211804200c3001"
162		uassert.Equal(t, want, hex.EncodeToString(bz))
163	})
164
165	t.Run("optional fields are emitted when set", func(t *testing.T) {
166		spec := &ics23.ProofSpec{
167			MaxDepth:                   8,
168			MinDepth:                   1,
169			PrehashKeyBeforeComparison: true,
170		}
171		bz := spec.ProtoMarshal()
172		// Field 3 (MaxDepth=8):                         18 08
173		// Field 4 (MinDepth=1):                         20 01
174		// Field 5 (PrehashKeyBeforeComparison=true):    28 01
175		uassert.Equal(t, "180820012801", hex.EncodeToString(bz))
176	})
177}
178
179func TestIsLeftNeighbor_NoPanicOnEdgeCases(cur realm, t *testing.T) {
180	spec := ics23.IavlSpec().InnerSpec
181	childSize := int(spec.ChildSize)
182
183	validOp := func() *ics23.InnerOp {
184		return &ics23.InnerOp{Prefix: []byte("aaaa"), Suffix: make([]byte, childSize)}
185	}
186	convergingOp := func() *ics23.InnerOp {
187		return &ics23.InnerOp{Prefix: []byte("aaaa"), Suffix: make([]byte, childSize)}
188	}
189	malformedOp := func() *ics23.InnerOp {
190		return &ics23.InnerOp{Prefix: make([]byte, 100), Suffix: nil}
191	}
192
193	cases := []struct {
194		name      string
195		left      []*ics23.InnerOp
196		right     []*ics23.InnerOp
197		expectErr bool
198	}{
199		{"empty left path", nil, []*ics23.InnerOp{validOp()}, false},
200		{"empty right path", []*ics23.InnerOp{validOp()}, nil, false},
201		{"common suffix exhausted", []*ics23.InnerOp{convergingOp()}, []*ics23.InnerOp{convergingOp()}, false},
202		{"malformed left prefix", []*ics23.InnerOp{malformedOp()}, []*ics23.InnerOp{validOp()}, true},
203		{"malformed right prefix", []*ics23.InnerOp{validOp()}, []*ics23.InnerOp{malformedOp()}, true},
204	}
205	for _, tc := range cases {
206		t.Run(tc.name, func(t *testing.T) {
207			var (
208				ok  bool
209				err error
210			)
211			uassert.NotPanics(t, cur, func() {
212				ok, err = ics23.IsLeftNeighbor(spec, tc.left, tc.right)
213			})
214			uassert.False(t, ok, "expected isNeighbor=false")
215			if tc.expectErr {
216				uassert.Error(t, err, "expected error for malformed input")
217			} else {
218				uassert.NoError(t, err)
219			}
220		})
221	}
222}