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

mpt.gno

6.19 Kb · 284 lines
  1package mpt
  2
  3import (
  4	"bytes"
  5	"crypto/keccak256"
  6	"errors"
  7
  8	"gno.land/p/onbloc/encoding/rlp"
  9)
 10
 11var (
 12	ErrEmptyProof       = errors.New("mpt: empty proof")
 13	ErrInvalidRoot      = errors.New("mpt: invalid root length")
 14	ErrUnexpectedNode   = errors.New("mpt: unexpected node shape")
 15	ErrInvalidReference = errors.New("mpt: invalid child reference")
 16	ErrPathMismatch     = errors.New("mpt: path mismatch")
 17	ErrValueMismatch    = errors.New("mpt: value mismatch")
 18	ErrKeyPresent       = errors.New("mpt: key present")
 19	ErrKeyAbsent        = errors.New("mpt: key absent")
 20	ErrUnusedProof      = errors.New("mpt: unused proof nodes")
 21)
 22
 23// EmptyRoot is keccak256(rlp("")), the root of an empty Ethereum MPT.
 24func EmptyRoot() [32]byte {
 25	return keccak256.Sum256(rlp.EncodeBytes(nil))
 26}
 27
 28// Verify verifies an Ethereum Merkle Patricia Trie existence proof.
 29//
 30// root is the 32-byte trie root. key is expanded to nibbles and matched against
 31// the proof path. proof contains raw RLP-encoded nodes, ordered from root to
 32// leaf. value is the RLP string payload stored at the leaf or branch value slot.
 33func Verify(root []byte, key []byte, value []byte, proof [][]byte) error {
 34	result, err := walk(root, keybytesToNibbles(key), proof)
 35	if err != nil {
 36		return err
 37	}
 38
 39	if !result.found {
 40		return ErrKeyAbsent
 41	}
 42
 43	if !bytes.Equal(result.value, value) {
 44		return ErrValueMismatch
 45	}
 46
 47	return nil
 48}
 49
 50// VerifyAbsence verifies that key is absent from the trie under root.
 51func VerifyAbsence(root []byte, key []byte, proof [][]byte) error {
 52	result, err := walk(root, keybytesToNibbles(key), proof)
 53	if err != nil {
 54		return err
 55	}
 56
 57	if result.found {
 58		return ErrKeyPresent
 59	}
 60
 61	return nil
 62}
 63
 64type walkResult struct {
 65	found bool
 66	value []byte
 67}
 68
 69// isBytesNode reports whether an RLP value carries a byte payload (a string or
 70// single byte) rather than a nested list.
 71func isBytesNode(v rlp.Value) bool {
 72	return v.Kind == rlp.KindString || v.Kind == rlp.KindByte
 73}
 74
 75func walk(root []byte, path []byte, proof [][]byte) (walkResult, error) {
 76	if len(root) != keccak256.Size {
 77		return walkResult{}, ErrInvalidRoot
 78	}
 79
 80	if len(proof) == 0 {
 81		emptyRoot := EmptyRoot()
 82		if bytes.Equal(root, emptyRoot[:]) {
 83			return walkResult{found: false}, nil
 84		}
 85
 86		return walkResult{}, ErrEmptyProof
 87	}
 88
 89	if err := matchHash(root, proof[0]); err != nil {
 90		return walkResult{}, err
 91	}
 92
 93	proofIndex := 0
 94	node := proof[0]
 95	pos := 0
 96
 97	for {
 98		v, err := rlp.DecodeValue(node)
 99		if err != nil {
100			return walkResult{}, err
101		}
102
103		if v.Kind != rlp.KindList {
104			return walkResult{}, ErrUnexpectedNode
105		}
106
107		switch len(v.List) {
108		case 17:
109			if pos == len(path) {
110				value := v.List[16]
111				if !isBytesNode(value) {
112					return walkResult{}, ErrUnexpectedNode
113				}
114
115				if len(value.Bytes) == 0 {
116					return finishWalk(walkResult{found: false}, proofIndex, proof)
117				}
118
119				return finishWalk(walkResult{found: true, value: value.Bytes}, proofIndex, proof)
120			}
121
122			child := v.List[path[pos]]
123			if !isBytesNode(child) {
124				return walkResult{}, ErrUnexpectedNode
125			}
126
127			if len(child.Bytes) == 0 {
128				return finishWalk(walkResult{found: false}, proofIndex, proof)
129			}
130
131			next, nextIndex, err := resolveReference(child.Bytes, proof, proofIndex)
132			if err != nil {
133				return walkResult{}, err
134			}
135
136			proofIndex = nextIndex
137			node = next
138			pos++
139
140		case 2:
141			if !isBytesNode(v.List[0]) {
142				return walkResult{}, ErrUnexpectedNode
143			}
144
145			leaf, partial, err := compactDecode(v.List[0].Bytes)
146			if err != nil {
147				return walkResult{}, err
148			}
149
150			if !hasPrefix(path[pos:], partial) {
151				return finishWalk(walkResult{found: false}, proofIndex, proof)
152			}
153
154			pos += len(partial)
155
156			if leaf {
157				if pos != len(path) {
158					return finishWalk(walkResult{found: false}, proofIndex, proof)
159				}
160
161				value := v.List[1]
162				if !isBytesNode(value) {
163					return walkResult{}, ErrUnexpectedNode
164				}
165
166				return finishWalk(walkResult{found: true, value: value.Bytes}, proofIndex, proof)
167			}
168
169			child := v.List[1]
170			if !isBytesNode(child) {
171				return walkResult{}, ErrUnexpectedNode
172			}
173
174			next, nextIndex, err := resolveReference(child.Bytes, proof, proofIndex)
175			if err != nil {
176				return walkResult{}, err
177			}
178
179			proofIndex = nextIndex
180			node = next
181
182		default:
183			return walkResult{}, ErrUnexpectedNode
184		}
185	}
186}
187
188func finishWalk(result walkResult, proofIndex int, proof [][]byte) (walkResult, error) {
189	if proofIndex != len(proof)-1 {
190		return walkResult{}, ErrUnusedProof
191	}
192
193	return result, nil
194}
195
196func resolveReference(ref []byte, proof [][]byte, proofIndex int) ([]byte, int, error) {
197	if len(ref) == 0 {
198		return nil, proofIndex, ErrInvalidReference
199	}
200
201	if len(ref) < keccak256.Size {
202		return ref, proofIndex, nil
203	}
204
205	if len(ref) != keccak256.Size {
206		return nil, proofIndex, ErrInvalidReference
207	}
208
209	nextIndex := proofIndex + 1
210	if nextIndex >= len(proof) {
211		return nil, proofIndex, ErrEmptyProof
212	}
213
214	if err := matchHash(ref, proof[nextIndex]); err != nil {
215		return nil, proofIndex, err
216	}
217
218	return proof[nextIndex], nextIndex, nil
219}
220
221func matchHash(expected []byte, encodedNode []byte) error {
222	h := keccak256.Sum256(encodedNode)
223	if !bytes.Equal(expected, h[:]) {
224		return ErrInvalidReference
225	}
226
227	return nil
228}
229
230func keybytesToNibbles(key []byte) []byte {
231	out := make([]byte, 0, len(key)*2)
232	for _, b := range key {
233		out = append(out, b>>4, b&0x0f)
234	}
235
236	return out
237}
238
239func compactDecode(encoded []byte) (bool, []byte, error) {
240	if len(encoded) == 0 {
241		return false, nil, ErrPathMismatch
242	}
243
244	flag := encoded[0] >> 4
245	if flag > 3 {
246		return false, nil, ErrPathMismatch
247	}
248
249	leaf := (flag & 2) != 0
250	odd := (flag & 1) != 0
251
252	out := make([]byte, 0, len(encoded)*2)
253	if odd {
254		out = append(out, encoded[0]&0x0f)
255	} else if encoded[0]&0x0f != 0 {
256		return false, nil, ErrPathMismatch
257	}
258
259	for i := 1; i < len(encoded); i++ {
260		out = append(out, encoded[i]>>4, encoded[i]&0x0f)
261	}
262	// extensions nodes must contain at least one path nibble.
263	// empty extension paths are non-canonical because they admit
264	// alternative proof encoding.
265	if !leaf && len(out) == 0 {
266		return false, nil, ErrPathMismatch
267	}
268
269	return leaf, out, nil
270}
271
272func hasPrefix(path []byte, prefix []byte) bool {
273	if len(prefix) > len(path) {
274		return false
275	}
276
277	for i := range prefix {
278		if path[i] != prefix[i] {
279			return false
280		}
281	}
282
283	return true
284}