proto.gno
6.59 Kb · 359 lines
1package cometbls
2
3import (
4 "errors"
5
6 "gno.land/p/aib/encoding/proto"
7 "gno.land/p/aib/ics23"
8)
9
10// DecodeProofs decodes a protobuf-encoded MerkleProof (repeated CommitmentProof field 1).
11func DecodeProofs(buf []byte) ([]ics23.CommitmentProof, error) {
12 var out []ics23.CommitmentProof
13
14 pos := 0
15
16 for pos < len(buf) {
17 fieldNum, wireType, newPos, err := decodeTag(buf, pos)
18 if err != nil {
19 return nil, err
20 }
21
22 pos = newPos
23
24 if wireType != proto.LEN {
25 pos, err = skipField(buf, pos, wireType)
26 if err != nil {
27 return nil, err
28 }
29
30 continue
31 }
32
33 s, newPos, err := proto.DecodeString(buf, pos)
34 if err != nil {
35 return nil, err
36 }
37
38 pos = newPos
39 b := []byte(s)
40
41 if fieldNum != 1 {
42 continue
43 }
44
45 cp, err := decodeCommitmentProof(b)
46 if err != nil {
47 return nil, err
48 }
49
50 out = append(out, cp)
51 }
52
53 return out, nil
54}
55
56func decodeCommitmentProof(buf []byte) (ics23.CommitmentProof, error) {
57 pos := 0
58
59 for pos < len(buf) {
60 fieldNum, wireType, newPos, err := decodeTag(buf, pos)
61 if err != nil {
62 return nil, err
63 }
64
65 pos = newPos
66
67 if wireType != proto.LEN {
68 pos, err = skipField(buf, pos, wireType)
69 if err != nil {
70 return nil, err
71 }
72
73 continue
74 }
75
76 s, newPos, err := proto.DecodeString(buf, pos)
77 if err != nil {
78 return nil, err
79 }
80
81 pos = newPos
82 b := []byte(s)
83
84 switch fieldNum {
85 case 1:
86 ep, err := decodeExistenceProof(b)
87 if err != nil {
88 return nil, err
89 }
90
91 return ics23.CommitmentProof_Exist{Exist: ep}, nil
92 case 2:
93 nep, err := decodeNonExistenceProof(b)
94 if err != nil {
95 return nil, err
96 }
97
98 return ics23.CommitmentProof_Nonexist{Nonexist: nep}, nil
99 }
100 }
101
102 return nil, errors.New("empty commitment proof")
103}
104
105func decodeExistenceProof(buf []byte) (*ics23.ExistenceProof, error) {
106 ep := &ics23.ExistenceProof{Path: make([]*ics23.InnerOp, 0, 32)}
107 pos := 0
108
109 for pos < len(buf) {
110 fieldNum, wireType, newPos, err := decodeTag(buf, pos)
111 if err != nil {
112 return nil, err
113 }
114
115 pos = newPos
116
117 switch {
118 case (fieldNum == 1 || fieldNum == 2) && wireType == proto.LEN:
119 s, newPos, err := proto.DecodeString(buf, pos)
120 if err != nil {
121 return nil, err
122 }
123
124 pos = newPos
125 b := []byte(s)
126
127 if fieldNum == 1 {
128 ep.Key = b
129 } else {
130 ep.Value = b
131 }
132
133 case fieldNum == 3 && wireType == proto.LEN:
134 s, newPos, err := proto.DecodeString(buf, pos)
135 if err != nil {
136 return nil, err
137 }
138
139 pos = newPos
140 b := []byte(s)
141
142 ep.Leaf, err = decodeLeafOp(b)
143 if err != nil {
144 return nil, err
145 }
146 case fieldNum == 4 && wireType == proto.LEN:
147 s, newPos, err := proto.DecodeString(buf, pos)
148 if err != nil {
149 return nil, err
150 }
151
152 pos = newPos
153 b := []byte(s)
154
155 inner, err := decodeInnerOp(b)
156 if err != nil {
157 return nil, err
158 }
159
160 ep.Path = append(ep.Path, inner)
161 default:
162 pos, err = skipField(buf, pos, wireType)
163 if err != nil {
164 return nil, err
165 }
166 }
167 }
168
169 return ep, nil
170}
171
172func decodeLeafOp(buf []byte) (*ics23.LeafOp, error) {
173 lop := &ics23.LeafOp{}
174 pos := 0
175
176 for pos < len(buf) {
177 fieldNum, wireType, newPos, err := decodeTag(buf, pos)
178 if err != nil {
179 return nil, err
180 }
181
182 pos = newPos
183
184 switch {
185 case fieldNum >= 1 && fieldNum <= 4 && wireType == proto.VARINT:
186 v, newPos, err := proto.DecodeVarint(buf, pos)
187 if err != nil {
188 return nil, err
189 }
190
191 pos = newPos
192
193 switch fieldNum {
194 case 1:
195 lop.Hash = ics23.HashOp(v)
196 case 2:
197 lop.PrehashKey = ics23.HashOp(v)
198 case 3:
199 lop.PrehashValue = ics23.HashOp(v)
200 case 4:
201 lop.Length = ics23.LengthOp(v)
202 }
203 case fieldNum == 5 && wireType == proto.LEN:
204 s, newPos, err := proto.DecodeString(buf, pos)
205 if err != nil {
206 return nil, err
207 }
208
209 pos = newPos
210 b := []byte(s)
211 lop.Prefix = b
212 default:
213 pos, err = skipField(buf, pos, wireType)
214 if err != nil {
215 return nil, err
216 }
217 }
218 }
219
220 return lop, nil
221}
222
223func decodeInnerOp(buf []byte) (*ics23.InnerOp, error) {
224 iop := &ics23.InnerOp{}
225
226 pos := 0
227 for pos < len(buf) {
228 fieldNum, wireType, newPos, err := decodeTag(buf, pos)
229 if err != nil {
230 return nil, err
231 }
232
233 pos = newPos
234
235 switch {
236 case fieldNum == 1 && wireType == proto.VARINT:
237 v, newPos, err := proto.DecodeVarint(buf, pos)
238 if err != nil {
239 return nil, err
240 }
241
242 pos = newPos
243 iop.Hash = ics23.HashOp(v)
244 case (fieldNum == 2 || fieldNum == 3) && wireType == proto.LEN:
245 s, newPos, err := proto.DecodeString(buf, pos)
246 if err != nil {
247 return nil, err
248 }
249
250 pos = newPos
251
252 b := []byte(s)
253 if fieldNum == 2 {
254 iop.Prefix = b
255 } else {
256 iop.Suffix = b
257 }
258 default:
259 pos, err = skipField(buf, pos, wireType)
260 if err != nil {
261 return nil, err
262 }
263 }
264 }
265
266 return iop, nil
267}
268
269func decodeNonExistenceProof(buf []byte) (*ics23.NonExistenceProof, error) {
270 nep := &ics23.NonExistenceProof{}
271 pos := 0
272
273 for pos < len(buf) {
274 fieldNum, wireType, newPos, err := decodeTag(buf, pos)
275 if err != nil {
276 return nil, err
277 }
278
279 pos = newPos
280 if wireType != proto.LEN {
281 pos, err = skipField(buf, pos, wireType)
282 if err != nil {
283 return nil, err
284 }
285
286 continue
287 }
288
289 s, newPos, err := proto.DecodeString(buf, pos)
290 if err != nil {
291 return nil, err
292 }
293
294 pos = newPos
295 b := []byte(s)
296
297 switch fieldNum {
298 case 1:
299 nep.Key = b
300 case 2:
301 ep, err := decodeExistenceProof(b)
302 if err != nil {
303 return nil, err
304 }
305
306 nep.Left = ep
307 case 3:
308 ep, err := decodeExistenceProof(b)
309 if err != nil {
310 return nil, err
311 }
312
313 nep.Right = ep
314 }
315 }
316
317 return nep, nil
318}
319
320// decodeTag reads a protobuf tag at pos and splits it into the field number and
321// wire type. It is built on top of proto.DecodeVarint.
322func decodeTag(buf []byte, pos int) (fieldNum int, wireType proto.WireType, newPos int, err error) {
323 tag, newPos, err := proto.DecodeVarint(buf, pos)
324 if err != nil {
325 return 0, 0, newPos, err
326 }
327
328 return int(tag >> 3), proto.WireType(tag & 0x7), newPos, nil
329}
330
331// skipField advances pos past a field whose value should be ignored, according
332// to its wire type. Only the wire types used by the ICS23 proof messages are
333// supported.
334func skipField(buf []byte, pos int, wireType proto.WireType) (int, error) {
335 switch wireType {
336 case proto.VARINT:
337 _, newPos, err := proto.DecodeVarint(buf, pos)
338
339 return newPos, err
340 case proto.LEN:
341 _, newPos, err := proto.DecodeString(buf, pos)
342
343 return newPos, err
344 case proto.FIXED64:
345 if pos+8 > len(buf) {
346 return pos, errors.New("buffer underflow while skipping fixed64")
347 }
348
349 return pos + 8, nil
350 case proto.FIXED32:
351 if pos+4 > len(buf) {
352 return pos, errors.New("buffer underflow while skipping fixed32")
353 }
354
355 return pos + 4, nil
356 default:
357 return pos, errors.New("unsupported wire type")
358 }
359}