This repository has been archived by the owner on Mar 26, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathsparse_binary.nim
182 lines (146 loc) · 6.28 KB
/
sparse_binary.nim
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
import
ranges/[ptr_arith, typedranges, bitranges], rlp/types,
defs, utils, db, sparse_proofs
export
types, utils, bitranges,
sparse_proofs.verifyProof
type
DB = TrieDatabaseRef
SparseBinaryTrie* = object
db: DB
rootHash: ByteRange
proc `==`(a: ByteRange, b: KeccakHash): bool =
if a.len != b.data.len: return false
equalMem(a.baseAddr, b.data[0].unsafeAddr, a.len)
type
# 256 * 2 div 8
DoubleHash = array[64, byte]
proc initDoubleHash(a, b: openArray[byte]): DoubleHash =
assert(a.len == 32, $a.len)
assert(b.len == 32, $b.len)
copyMem(result[ 0].addr, a[0].unsafeAddr, 32)
copyMem(result[32].addr, b[0].unsafeAddr, 32)
proc initDoubleHash(x: ByteRange): DoubleHash =
initDoubleHash(x.toOpenArray, x.toOpenArray)
proc init*(x: typedesc[SparseBinaryTrie], db: DB): SparseBinaryTrie =
result.db = db
# Initialize an empty tree with one branch
var value = initDoubleHash(emptyNodeHashes[0])
result.rootHash = keccakHash(value)
result.db.put(result.rootHash.toOpenArray, value)
for i in 0..<treeHeight - 1:
value = initDoubleHash(emptyNodeHashes[i+1])
result.db.put(emptyNodeHashes[i].toOpenArray, value)
result.db.put(emptyLeafNodeHash.data, zeroBytesRange.toOpenArray)
proc initSparseBinaryTrie*(db: DB): SparseBinaryTrie =
init(SparseBinaryTrie, db)
proc init*(x: typedesc[SparseBinaryTrie], db: DB,
rootHash: BytesContainer | KeccakHash): SparseBinaryTrie =
checkValidHashZ(rootHash)
result.db = db
result.rootHash = rootHash
proc initSparseBinaryTrie*(db: DB, rootHash: BytesContainer | KeccakHash): SparseBinaryTrie =
init(SparseBinaryTrie, db, rootHash)
proc getDB*(t: SparseBinaryTrie): auto = t.db
proc getRootHash*(self: SparseBinaryTrie): ByteRange {.inline.} =
self.rootHash
proc getAux(self: SparseBinaryTrie, path: BitRange, rootHash: ByteRange): ByteRange =
var nodeHash = rootHash
for targetBit in path:
let value = self.db.get(nodeHash.toOpenArray).toRange
if value.len == 0: return zeroBytesRange
if targetBit: nodeHash = value[32..^1]
else: nodeHash = value[0..31]
if nodeHash.toOpenArray == emptyLeafNodeHash.data:
result = zeroBytesRange
else:
result = self.db.get(nodeHash.toOpenArray).toRange
proc get*(self: SparseBinaryTrie, key: BytesContainer): ByteRange =
## gets a key from the tree.
assert(key.len == pathByteLen)
let path = MutByteRange(key.toRange).bits
self.getAux(path, self.rootHash)
proc get*(self: SparseBinaryTrie, key, rootHash: distinct BytesContainer): ByteRange =
## gets a key from the tree at a specific root.
assert(key.len == pathByteLen)
let path = MutByteRange(key.toRange).bits
self.getAux(path, rootHash.toRange)
proc hashAndSave*(self: SparseBinaryTrie, node: ByteRange): ByteRange =
result = keccakHash(node)
self.db.put(result.toOpenArray, node.toOpenArray)
proc hashAndSave*(self: SparseBinaryTrie, a, b: ByteRange): ByteRange =
let value = initDoubleHash(a.toOpenArray, b.toOpenArray)
result = keccakHash(value)
self.db.put(result.toOpenArray, value)
proc setAux(self: var SparseBinaryTrie, value: ByteRange,
path: BitRange, depth: int, nodeHash: ByteRange): ByteRange =
if depth == treeHeight:
result = self.hashAndSave(value)
else:
let
node = self.db.get(nodeHash.toOpenArray).toRange
leftNode = node[0..31]
rightNode = node[32..^1]
if path[depth]:
result = self.hashAndSave(leftNode, self.setAux(value, path, depth+1, rightNode))
else:
result = self.hashAndSave(self.setAux(value, path, depth+1, leftNode), rightNode)
proc set*(self: var SparseBinaryTrie, key, value: distinct BytesContainer) =
## sets a new value for a key in the tree, returns the new root,
## and sets the new current root of the tree.
assert(key.len == pathByteLen)
let path = MutByteRange(key.toRange).bits
self.rootHash = self.setAux(value.toRange, path, 0, self.rootHash)
proc set*(self: var SparseBinaryTrie, key, value, rootHash: distinct BytesContainer): ByteRange =
## sets a new value for a key in the tree at a specific root,
## and returns the new root.
assert(key.len == pathByteLen)
let path = MutByteRange(key.toRange).bits
self.setAux(value.toRange, path, 0, rootHash.toRange)
template exists*(self: SparseBinaryTrie, key: BytesContainer): bool =
self.get(toRange(key)) != zeroBytesRange
proc del*(self: var SparseBinaryTrie, key: BytesContainer) =
## Equals to setting the value to zeroBytesRange
assert(key.len == pathByteLen)
self.set(key, zeroBytesRange)
# Dictionary API
template `[]`*(self: SparseBinaryTrie, key: BytesContainer): ByteRange =
self.get(key)
template `[]=`*(self: var SparseBinaryTrie, key, value: distinct BytesContainer) =
self.set(key, value)
template contains*(self: SparseBinaryTrie, key: BytesContainer): bool =
self.exists(key)
proc proveAux(self: SparseBinaryTrie, key, rootHash: ByteRange, output: var seq[ByteRange]): bool =
assert(key.len == pathByteLen)
var currVal = self.db.get(rootHash.toOpenArray).toRange
if currVal.len == 0: return false
let path = MutByteRange(key).bits
for i, bit in path:
if bit:
# right side
output[i] = currVal[0..31]
currVal = self.db.get(currVal[32..^1].toOpenArray).toRange
if currVal.len == 0: return false
else:
output[i] = currVal[32..^1]
currVal = self.db.get(currVal[0..31].toOpenArray).toRange
if currVal.len == 0: return false
result = true
# prove generates a Merkle proof for a key.
proc prove*(self: SparseBinaryTrie, key: BytesContainer): seq[ByteRange] =
result = newSeq[ByteRange](treeHeight)
if not self.proveAux(key.toRange, self.rootHash, result):
result = @[]
# prove generates a Merkle proof for a key, at a specific root.
proc prove*(self: SparseBinaryTrie, key, rootHash: distinct BytesContainer): seq[ByteRange] =
result = newSeq[ByteRange](treeHeight)
if not self.proveAux(key.toRange, rootHash.toRange, result):
result = @[]
# proveCompact generates a compacted Merkle proof for a key.
proc proveCompact*(self: SparseBinaryTrie, key: BytesContainer): seq[ByteRange] =
var temp = self.prove(key)
temp.compactProof
# proveCompact generates a compacted Merkle proof for a key, at a specific root.
proc proveCompact*(self: SparseBinaryTrie, key, rootHash: distinct BytesContainer): seq[ByteRange] =
var temp = self.prove(key, rootHash)
temp.compactProof