forked from ledgerwatch/erigon
-
Notifications
You must be signed in to change notification settings - Fork 0
/
retain_list.go
388 lines (344 loc) · 12.4 KB
/
retain_list.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
// Copyright 2019 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty off
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package trie
import (
"bytes"
"encoding/binary"
"fmt"
"github.com/nebojsa94/erigon/erigon-lib/common/hexutil"
"math/big"
"sort"
"github.com/holiman/uint256"
libcommon "github.com/nebojsa94/erigon/erigon-lib/common"
"github.com/nebojsa94/erigon/erigon-lib/common/length"
"github.com/nebojsa94/erigon/core/types/accounts"
)
type RetainDecider interface {
Retain([]byte) bool
IsCodeTouched(libcommon.Hash) bool
}
type RetainDeciderWithMarker interface {
RetainDecider
// AddKeyWithMarker adds a key in KEY encoding with marker and returns the
// nibble encoded key.
AddKeyWithMarker(key []byte, marker bool) []byte
RetainWithMarker(prefix []byte) (retain bool, nextMarkedKey []byte)
}
// ProofRetainer is a wrapper around the RetainList passed to the trie builder.
// It is responsible for aggregating proof values from the trie computation and
// will return a valid accounts.AccProofresult after the trie root hash
// calculation has completed.
type ProofRetainer struct {
rl *RetainList
addr libcommon.Address
acc *accounts.Account
accHexKey []byte
storageKeys []libcommon.Hash
storageHexKeys [][]byte
proofs []*proofElement
}
// NewProofRetainer creates a new ProofRetainer instance for a given account and
// set of storage keys. The trie keys corresponding to the account key, and its
// storage keys are added to the given RetainList. The ProofRetainer should be
// set onto the FlatDBTrieLoader via SetProofRetainer before performing its Load
// operation in order to appropriately collect the proof elements.
func NewProofRetainer(addr libcommon.Address, a *accounts.Account, storageKeys []libcommon.Hash, rl *RetainList) (*ProofRetainer, error) {
addrHash, err := libcommon.HashData(addr[:])
if err != nil {
return nil, err
}
accHexKey := rl.AddKey(addrHash[:])
storageHexKeys := make([][]byte, len(storageKeys))
for i, sk := range storageKeys {
storageHash, err := libcommon.HashData(sk[:])
if err != nil {
return nil, err
}
var compactEncoded [72]byte
copy(compactEncoded[:32], addrHash[:])
binary.BigEndian.PutUint64(compactEncoded[32:40], a.Incarnation)
copy(compactEncoded[40:], storageHash[:])
storageHexKeys[i] = rl.AddKey(compactEncoded[:])
}
return &ProofRetainer{
rl: rl,
addr: addr,
acc: a,
accHexKey: accHexKey,
storageKeys: storageKeys,
storageHexKeys: storageHexKeys,
}, nil
}
// ProofElement requests a new proof element for a given prefix. This proof
// element is retained by the ProofRetainer, and will be utilized to compute the
// proof after the trie computation has completed. The prefix is the standard
// nibble encoded prefix used in the rest of the trie computations.
func (pr *ProofRetainer) ProofElement(prefix []byte) *proofElement {
if !pr.rl.Retain(prefix) {
return nil
}
switch {
case bytes.HasPrefix(pr.accHexKey, prefix):
// This prefix is a node between the account and the root
case bytes.HasPrefix(prefix, pr.accHexKey):
// This prefix is the account or one of its storage nodes
default:
// we do not need a proof element for this prefix
return nil
}
pe := &proofElement{
hexKey: append([]byte{}, prefix...),
}
// Since we do a depth-first traversal, reverse the proof elements so that
// they are ordered correctly root -> node -> ... -> leaf as dictated by
// EIP-1186
pr.proofs = append([]*proofElement{pe}, pr.proofs...)
return pe
}
// ProofResult may be invoked only after the Load function of the
// FlatDBTrieLoader has successfully executed. It will populate the Address,
// Balance, Nonce, and CodeHash from the account data supplied in the
// constructor, the StorageHash, storageKey values, and proof elements are
// supplied by the Load operation of the trie construction.
func (pr *ProofRetainer) ProofResult() (*accounts.AccProofResult, error) {
result := &accounts.AccProofResult{
Address: pr.addr,
Balance: (*hexutil.Big)(pr.acc.Balance.ToBig()),
Nonce: hexutil.Uint64(pr.acc.Nonce),
CodeHash: pr.acc.CodeHash,
}
for _, pe := range pr.proofs {
if !bytes.HasPrefix(pr.accHexKey, pe.hexKey) {
continue
}
result.AccountProof = append(result.AccountProof, pe.proof.Bytes())
if bytes.Equal(pr.accHexKey, pe.storageRootKey) {
result.StorageHash = pe.storageRoot
}
}
if pr.acc.Initialised && result.StorageHash == (libcommon.Hash{}) {
return nil, fmt.Errorf("did not find storage root in proof elements")
}
result.StorageProof = make([]accounts.StorProofResult, len(pr.storageKeys))
for i, sk := range pr.storageKeys {
result.StorageProof[i].Key = sk
hexKey := pr.storageHexKeys[i]
if !pr.acc.Initialised || result.StorageHash == EmptyRoot {
// The yellow paper makes it clear that the EmptyRoot is a special case
// when the trie has no nodes, but EIP-1186 states that the proof is
// "starting with the storageHash-Node". Since the trie has no nodes,
// it's unclear whether the correct proof should contain the EmptyRoot
// pre-image of RLP([]byte(nil)), or be empty. This implementation
// chooses 'empty' as it seems more consistent and it is expected that
// provers will treat the EmptyRoot as a special case and ignore the proof
// bytes.
result.StorageProof[i].Value = (*hexutil.Big)(new(big.Int))
continue
}
for _, pe := range pr.proofs {
if len(pe.hexKey) <= 2*32 {
// Ignore the proof elements above the storage tree (64 bytes, as nibble
// encoded)
continue
}
if !bytes.HasPrefix(hexKey, pe.hexKey) {
continue
}
if pe.storageValue != nil && bytes.Equal(pe.storageKey, hexKey[2*(length.Hash+length.Incarnation):]) {
result.StorageProof[i].Value = (*hexutil.Big)(pe.storageValue.ToBig())
}
result.StorageProof[i].Proof = append(result.StorageProof[i].Proof, pe.proof.Bytes())
}
if result.StorageProof[i].Value == nil {
result.StorageProof[i].Value = (*hexutil.Big)(new(big.Int))
}
}
return result, nil
}
// proofElement represent a node or leaf in the trie and its
// corresponding RLP encoding. We store the elements individually when
// aggregating as multiple keys (in particular storage keys) may need to
// reference the same proof elements in their Merkle proof.
type proofElement struct {
// key is the hex encoded key indicating the path of the
// element in the proof.
hexKey []byte
// buf is used to store the proof bytes
proof bytes.Buffer
// storageRoot stores the storage root if this is writing
// an account leaf
storageRoot libcommon.Hash
// storageRootKey stores the actual hexKey from which the storageRoot came.
// This is needed because other proof nodes can be included in the negative
// proof case.
storageRootKey []byte
// storageValue stores the value of the particular storage key if this writer
// is for a storage key
storageValue *uint256.Int
// storageKey stores the actual hexKey from which the storageValue came. This
// is needed because the same proofElement may be used to both prove and
// disprove two different storage elements.
storageKey []byte
}
// RetainList encapsulates the list of keys that are required to be fully available, or loaded
// (by using `BRANCH` opcode instead of `HASHER`) after processing of the sequence of key-value
// pairs
// DESCRIBED: docs/programmers_guide/guide.md#converting-sequence-of-keys-and-value-into-a-multiproof
type RetainList struct {
inited bool // Whether keys are sorted and "LTE" and "GT" indices set
minLength int // Mininum length of prefixes for which `HashOnly` function can return `true`
lteIndex int // Index of the "LTE" key in the keys slice. Next one is "GT"
hexes [][]byte
markers []bool
codeTouches map[libcommon.Hash]struct{}
}
// NewRetainList creates new RetainList
func NewRetainList(minLength int) *RetainList {
return &RetainList{minLength: minLength, codeTouches: make(map[libcommon.Hash]struct{})}
}
func (rl *RetainList) Len() int {
return len(rl.hexes)
}
func (rl *RetainList) Less(i, j int) bool {
return bytes.Compare(rl.hexes[i], rl.hexes[j]) < 0
}
func (rl *RetainList) Swap(i, j int) {
rl.hexes[i], rl.hexes[j] = rl.hexes[j], rl.hexes[i]
rl.markers[i], rl.markers[j] = rl.markers[j], rl.markers[i]
}
// AddKey adds a new key (in KEY encoding) to the list
func (rl *RetainList) AddKey(key []byte) []byte {
return rl.AddKeyWithMarker(key, false)
}
func (rl *RetainList) AddKeyWithMarker(key []byte, marker bool) []byte {
var nibbles = make([]byte, 2*len(key))
for i, b := range key {
nibbles[i*2] = b / 16
nibbles[i*2+1] = b % 16
}
rl.AddHex(nibbles)
rl.markers = append(rl.markers, marker)
return nibbles
}
// AddHex adds a new key (in HEX encoding) to the list
func (rl *RetainList) AddHex(hex []byte) {
rl.hexes = append(rl.hexes, hex)
}
// AddCodeTouch adds a new code touch into the resolve set
func (rl *RetainList) AddCodeTouch(codeHash libcommon.Hash) {
rl.codeTouches[codeHash] = struct{}{}
}
func (rl *RetainList) IsCodeTouched(codeHash libcommon.Hash) bool {
_, ok := rl.codeTouches[codeHash]
return ok
}
func (rl *RetainList) ensureInited() {
if rl.inited {
return
}
if len(rl.markers) == 0 {
rl.markers = make([]bool, len(rl.hexes))
}
if !sort.IsSorted(rl) {
sort.Sort(rl)
}
rl.lteIndex = 0
rl.inited = true
}
// Retain decides whether to emit `HASHER` or `BRANCH` for a given prefix, by
// checking if this is prefix of any of the keys added to the set
// Since keys in the set are sorted, and we expect that the prefixes will
// come in monotonically ascending order, we optimise for this, though
// the function would still work if the order is different
func (rl *RetainList) Retain(prefix []byte) bool {
rl.ensureInited()
if len(prefix) < rl.minLength {
return true
}
// Adjust "GT" if necessary
var gtAdjusted bool
for rl.lteIndex < len(rl.hexes)-1 && bytes.Compare(rl.hexes[rl.lteIndex+1], prefix) <= 0 {
rl.lteIndex++
gtAdjusted = true
}
// Adjust "LTE" if necessary (normally will not be necessary)
for !gtAdjusted && rl.lteIndex > 0 && bytes.Compare(rl.hexes[rl.lteIndex], prefix) > 0 {
rl.lteIndex--
}
if rl.lteIndex < len(rl.hexes) {
if bytes.HasPrefix(rl.hexes[rl.lteIndex], prefix) {
return true
}
}
if rl.lteIndex < len(rl.hexes)-1 {
if bytes.HasPrefix(rl.hexes[rl.lteIndex+1], prefix) {
return true
}
}
return false
}
func (rl *RetainList) RetainWithMarker(prefix []byte) (bool, []byte) {
rl.ensureInited()
if len(prefix) < rl.minLength {
return true, nil
}
// Adjust "GT" if necessary
var gtAdjusted bool
for rl.lteIndex < len(rl.hexes)-1 && bytes.Compare(rl.hexes[rl.lteIndex+1], prefix) <= 0 {
rl.lteIndex++
gtAdjusted = true
}
// Adjust "LTE" if necessary (normally will not be necessary)
for !gtAdjusted && rl.lteIndex > 0 && bytes.Compare(rl.hexes[rl.lteIndex], prefix) > 0 {
rl.lteIndex--
}
if rl.lteIndex < len(rl.hexes) {
if bytes.HasPrefix(rl.hexes[rl.lteIndex], prefix) {
return true, rl.nextMarkedItem(rl.lteIndex)
}
}
if rl.lteIndex < len(rl.hexes)-1 {
if bytes.HasPrefix(rl.hexes[rl.lteIndex+1], prefix) {
return true, rl.nextMarkedItem(rl.lteIndex + 1)
}
}
if rl.lteIndex < len(rl.hexes) {
if bytes.Compare(prefix, rl.hexes[rl.lteIndex]) <= 0 {
return false, rl.nextMarkedItem(rl.lteIndex)
}
}
if rl.lteIndex < len(rl.hexes)-1 {
if bytes.Compare(prefix, rl.hexes[rl.lteIndex+1]) <= 0 {
return false, rl.nextMarkedItem(rl.lteIndex + 1)
}
}
return false, nil
}
func (rl *RetainList) nextMarkedItem(index int) []byte {
for i := index; i < len(rl.markers); i++ {
if rl.markers[i] {
return rl.hexes[i]
}
}
return nil
}
// Rewind lets us reuse this list from the beginning
func (rl *RetainList) Rewind() {
rl.lteIndex = 0
}
func (rl *RetainList) String() string {
return fmt.Sprintf("%x", rl.hexes)
}