Skip to content

Commit f282c17

Browse files
authored
Merge pull request #4 from vulcanize/dev
Concurrent traversal utilities
2 parents 10f5d0e + c6a1534 commit f282c17

File tree

3 files changed

+241
-0
lines changed

3 files changed

+241
-0
lines changed

go.mod

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module github.com/vulcanize/go-eth-state-node-iterator
2+
3+
go 1.13
4+
5+
require github.com/ethereum/go-ethereum v1.9.11
6+
7+
replace github.com/ethereum/go-ethereum v1.9.11 => github.com/vulcanize/go-ethereum v1.9.11-statediff-0.0.2

iterator.go

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
//
2+
// Copyright © 2020 Vulcanize, Inc
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package iterator
18+
19+
import (
20+
"bytes"
21+
"math/bits"
22+
23+
"github.com/ethereum/go-ethereum/common"
24+
"github.com/ethereum/go-ethereum/core/state"
25+
"github.com/ethereum/go-ethereum/trie"
26+
)
27+
28+
type PrefixBoundIterator struct {
29+
current trie.NodeIterator
30+
endKey []byte
31+
}
32+
33+
func (it *PrefixBoundIterator) Next(descend bool) bool {
34+
if it.endKey == nil {
35+
return it.current.Next(descend)
36+
}
37+
if !it.current.Next(descend) {
38+
return false
39+
}
40+
// stop if underlying iterator went past endKey
41+
cmp := bytes.Compare(it.current.Path(), it.endKey)
42+
return cmp <= 0
43+
}
44+
45+
func (it *PrefixBoundIterator) Error() error {
46+
return it.current.Error()
47+
}
48+
func (it *PrefixBoundIterator) Hash() common.Hash {
49+
return it.current.Hash()
50+
}
51+
func (it *PrefixBoundIterator) Path() []byte {
52+
return it.current.Path()
53+
}
54+
func (it *PrefixBoundIterator) Leaf() bool {
55+
return it.current.Leaf()
56+
}
57+
func (it *PrefixBoundIterator) LeafKey() []byte {
58+
return it.current.LeafKey()
59+
}
60+
func (it *PrefixBoundIterator) LeafBlob() []byte {
61+
return it.current.LeafBlob()
62+
}
63+
func (it *PrefixBoundIterator) LeafProof() [][]byte {
64+
return it.current.LeafProof()
65+
}
66+
func (it *PrefixBoundIterator) Parent() common.Hash {
67+
return it.current.Parent()
68+
}
69+
70+
// Iterator with an upper bound value (hex path prefix)
71+
func NewPrefixBoundIterator(it trie.NodeIterator, to []byte) *PrefixBoundIterator {
72+
return &PrefixBoundIterator{current: it, endKey: to}
73+
}
74+
75+
type prefixGenerator struct {
76+
current []byte
77+
step byte
78+
stepIndex uint
79+
}
80+
81+
func newPrefixGenerator(nbins uint) prefixGenerator {
82+
if bits.OnesCount(nbins) != 1 {
83+
panic("nbins must be a power of 2")
84+
}
85+
// determine step dist. and path index at which to step
86+
var step byte
87+
var stepIndex uint
88+
for ; nbins != 0; stepIndex++ {
89+
divisor := byte(nbins & 0xf)
90+
if divisor != 0 {
91+
step = 0x10 / divisor
92+
}
93+
nbins = nbins >> 4
94+
}
95+
return prefixGenerator{
96+
current: make([]byte, stepIndex),
97+
step: step,
98+
stepIndex: stepIndex - 1,
99+
}
100+
}
101+
102+
func (gen *prefixGenerator) Value() []byte {
103+
return gen.current
104+
}
105+
106+
func (gen *prefixGenerator) HasNext() bool {
107+
return gen.current[0] <= 0xf
108+
}
109+
110+
func (gen *prefixGenerator) Next() {
111+
gen.current[gen.stepIndex] += gen.step
112+
overflow := false
113+
for ix := 0; ix < len(gen.current); ix++ {
114+
rix := len(gen.current) - 1 - ix // reverse
115+
if overflow {
116+
gen.current[rix]++
117+
overflow = false
118+
}
119+
if rix != 0 && gen.current[rix] > 0xf {
120+
gen.current[rix] = 0
121+
overflow = true
122+
}
123+
}
124+
}
125+
126+
// Generates paths that cut trie domain into `nbins` uniform conterminous bins (w/ opt. prefix)
127+
// eg. MakePaths([], 2) => [[0] [8]]
128+
// MakePaths([4], 32) => [[4 0 0] [4 0 8] [4 1 0]... [4 f 8]]
129+
func MakePaths(prefix []byte, nbins uint) [][]byte {
130+
var res [][]byte
131+
for it := newPrefixGenerator(nbins); it.HasNext(); it.Next() {
132+
next := make([]byte, len(prefix))
133+
copy(next, prefix)
134+
next = append(next, it.Value()...)
135+
res = append(res, next)
136+
}
137+
return res
138+
}
139+
140+
func eachPrefixRange(prefix []byte, nbins uint, callback func([]byte, []byte)) {
141+
prefixes := MakePaths(prefix, nbins)
142+
prefixes = append(prefixes, nil) // include tail
143+
prefixes[0] = nil // set bin 0 left bound to nil to include root
144+
for i := 0; i < len(prefixes)-1; i++ {
145+
key := prefixes[i]
146+
if len(key)%2 != 0 { // zero-pad for odd-length keys
147+
key = append(key, 0)
148+
}
149+
callback(key, prefixes[i+1])
150+
}
151+
}
152+
153+
// Cut a trie by path prefix, returning `nbins` iterators covering its subtries
154+
func SubtrieIterators(tree state.Trie, nbins uint) []trie.NodeIterator {
155+
var iters []trie.NodeIterator
156+
eachPrefixRange(nil, nbins, func(key []byte, endKey []byte) {
157+
it := tree.NodeIterator(HexToKeyBytes(key))
158+
iters = append(iters, NewPrefixBoundIterator(it, endKey))
159+
})
160+
return iters
161+
}
162+
163+
// Factory for per-bin subtrie iterators
164+
type SubtrieIteratorFactory struct {
165+
tree state.Trie
166+
startKeys, endKeys [][]byte
167+
}
168+
169+
func (fac *SubtrieIteratorFactory) Length() int { return len(fac.startKeys) }
170+
171+
func (fac *SubtrieIteratorFactory) IteratorAt(bin uint) *PrefixBoundIterator {
172+
it := fac.tree.NodeIterator(HexToKeyBytes(fac.startKeys[bin]))
173+
return NewPrefixBoundIterator(it, fac.endKeys[bin])
174+
}
175+
176+
// Cut a trie by path prefix, returning `nbins` iterators covering its subtries
177+
func NewSubtrieIteratorFactory(tree state.Trie, nbins uint) SubtrieIteratorFactory {
178+
starts := make([][]byte, 0, nbins)
179+
ends := make([][]byte, 0, nbins)
180+
eachPrefixRange(nil, nbins, func(key []byte, endKey []byte) {
181+
starts = append(starts, key)
182+
ends = append(ends, endKey)
183+
})
184+
return SubtrieIteratorFactory{tree: tree, startKeys: starts, endKeys: ends}
185+
}

util.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package iterator
2+
3+
import (
4+
"bytes"
5+
"github.com/ethereum/go-ethereum/trie"
6+
)
7+
8+
func CompareNodes(a, b trie.NodeIterator) int {
9+
if cmp := bytes.Compare(a.Path(), b.Path()); cmp != 0 {
10+
return cmp
11+
}
12+
if a.Leaf() && !b.Leaf() {
13+
return -1
14+
} else if b.Leaf() && !a.Leaf() {
15+
return 1
16+
}
17+
if cmp := bytes.Compare(a.Hash().Bytes(), b.Hash().Bytes()); cmp != 0 {
18+
return cmp
19+
}
20+
if a.Leaf() && b.Leaf() {
21+
return bytes.Compare(a.LeafBlob(), b.LeafBlob())
22+
}
23+
return 0
24+
}
25+
26+
// hexToKeyBytes turns hex nibbles into key bytes.
27+
// This can only be used for keys of even length.
28+
func HexToKeyBytes(hex []byte) []byte {
29+
if hasTerm(hex) {
30+
hex = hex[:len(hex)-1]
31+
}
32+
if len(hex)&1 != 0 {
33+
panic("can't convert hex key of odd length")
34+
}
35+
key := make([]byte, len(hex)/2)
36+
decodeNibbles(hex, key)
37+
return key
38+
}
39+
40+
func decodeNibbles(nibbles []byte, bytes []byte) {
41+
for bi, ni := 0, 0; ni < len(nibbles); bi, ni = bi+1, ni+2 {
42+
bytes[bi] = nibbles[ni]<<4 | nibbles[ni+1]
43+
}
44+
}
45+
46+
// hasTerm returns whether a hex key has the terminator flag.
47+
func hasTerm(s []byte) bool {
48+
return len(s) > 0 && s[len(s)-1] == 16
49+
}

0 commit comments

Comments
 (0)