codec.gno
2.61 Kb · 141 lines
1package storage
2
3import (
4 "encoding/binary"
5 "errors"
6)
7
8var (
9 ErrInvalidProofBytes = errors.New("storage: invalid proof bytes")
10 ErrTrailingProofBytes = errors.New("storage: trailing proof bytes")
11)
12
13// DecodeProof decodes Union's ethereum_light_client_types::StorageProof
14// bincode wire form into the normalized Proof shape used by this package.
15//
16// Wire layout with fixed-int/little-endian bincode:
17// - key: U256 as 32 little-endian bytes
18// - value: U256 as 32 little-endian bytes
19// - proof: Vec<Bytes> as u64 length followed by repeated byte vectors
20func DecodeProof(buf []byte) (Proof, error) {
21 d := proofDecoder{buf: buf}
22
23 key, err := d.readU256()
24 if err != nil {
25 return Proof{}, err
26 }
27
28 value, err := d.readU256()
29 if err != nil {
30 return Proof{}, err
31 }
32
33 count, err := d.readUint64()
34 if err != nil {
35 return Proof{}, err
36 }
37
38 if count > uint64(len(buf)) {
39 return Proof{}, ErrInvalidProofBytes
40 }
41
42 proof := make([][]byte, int(count))
43 for i := 0; i < int(count); i++ {
44 proof[i], err = d.readBytes()
45 if err != nil {
46 return Proof{}, err
47 }
48 }
49
50 if d.pos != len(buf) {
51 return Proof{}, ErrTrailingProofBytes
52 }
53
54 return Proof{Key: key, Value: value, Proof: proof}, nil
55}
56
57func EncodeProof(proof Proof) []byte {
58 var out []byte
59
60 out = append(out, reverse32(proof.Key)...)
61 out = append(out, reverse32(proof.Value)...)
62 out = appendUint64LE(out, uint64(len(proof.Proof)))
63
64 for _, node := range proof.Proof {
65 out = appendUint64LE(out, uint64(len(node)))
66 out = append(out, node...)
67 }
68
69 return out
70}
71
72type proofDecoder struct {
73 buf []byte
74 pos int
75}
76
77func (d *proofDecoder) readU256() ([]byte, error) {
78 if d.pos+32 > len(d.buf) {
79 return nil, ErrInvalidProofBytes
80 }
81
82 le := d.buf[d.pos : d.pos+32]
83 d.pos += 32
84
85 out := make([]byte, 32)
86 for i := 0; i < 32; i++ {
87 out[i] = le[31-i]
88 }
89
90 return out, nil
91}
92
93func (d *proofDecoder) readBytes() ([]byte, error) {
94 n, err := d.readUint64()
95 if err != nil {
96 return nil, err
97 }
98
99 if n > uint64(len(d.buf)-d.pos) {
100 return nil, ErrInvalidProofBytes
101 }
102
103 out := make([]byte, int(n))
104 copy(out, d.buf[d.pos:d.pos+int(n)])
105 d.pos += int(n)
106
107 return out, nil
108}
109
110func (d *proofDecoder) readUint64() (uint64, error) {
111 if d.pos+8 > len(d.buf) {
112 return 0, ErrInvalidProofBytes
113 }
114
115 v := binary.LittleEndian.Uint64(d.buf[d.pos : d.pos+8])
116 d.pos += 8
117
118 return v, nil
119}
120
121func appendUint64LE(out []byte, v uint64) []byte {
122 var word [8]byte
123 binary.LittleEndian.PutUint64(word[:], v)
124
125 return append(out, word[:]...)
126}
127
128func reverse32(b []byte) []byte {
129 out := make([]byte, 32)
130
131 n := len(b)
132 if n > 32 {
133 n = 32
134 }
135
136 for i := 0; i < n; i++ {
137 out[i] = b[len(b)-1-i]
138 }
139
140 return out
141}