-
Notifications
You must be signed in to change notification settings - Fork 4
/
hasher.go
185 lines (151 loc) · 5.74 KB
/
hasher.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
package smt
import (
"encoding/binary"
"hash"
)
// TODO_IMPROVE:: Improve how the `hasher` file is consolidated with
// `node_encoders.go` since the two are very similar.
// Ensure the hasher interfaces are satisfied
var (
_ PathHasher = (*pathHasher)(nil)
_ PathHasher = (*nilPathHasher)(nil)
_ ValueHasher = (*valueHasher)(nil)
)
// PathHasher defines how key inputs are hashed to produce trie paths.
type PathHasher interface {
// Path hashes a key (preimage) and returns a trie path (digest).
Path([]byte) []byte
// PathSize returns the length (in bytes) of digests produced by this hasher.
PathSize() int
}
// ValueHasher defines how value data is hashed to produce leaf data.
type ValueHasher interface {
// HashValue hashes value data to produce the digest stored in leaf node.
HashValue([]byte) []byte
// ValueHashSize returns the length (in bytes) of digests produced by this hasher.
ValueHashSize() int
}
// trieHasher is a common hasher for all trie hashers (paths & values).
type trieHasher struct {
hasher hash.Hash
zeroValue []byte
}
// pathHasher is a hasher for trie paths.
type pathHasher struct {
trieHasher
}
// valueHasher is a hasher for leaf values.
type valueHasher struct {
trieHasher
}
// nilPathHasher is a dummy hasher that returns its input - it should not be used outside of the closest proof verification logic
type nilPathHasher struct {
hashSize int
}
// NewTrieHasher returns a new trie hasher with the given hash function.
func NewTrieHasher(hasher hash.Hash) *trieHasher {
th := trieHasher{hasher: hasher}
th.zeroValue = make([]byte, th.hashSize())
return &th
}
// newNilPathHasher returns a new nil path hasher with the given hash size.
// It is not exported as the validation logic for the ClosestProof automatically handles this case.
func newNilPathHasher(hasherSize int) PathHasher {
return &nilPathHasher{hashSize: hasherSize}
}
// Path returns the digest of a key produced by the path hasher
func (ph *pathHasher) Path(key []byte) []byte {
return ph.digestData(key)[:ph.PathSize()]
}
// PathSize returns the length (in bytes) of digests produced by the path hasher
// which is the length of any path in the trie
func (ph *pathHasher) PathSize() int {
return ph.hasher.Size()
}
// HashValue hashes the produces a digest of the data provided by the value hasher
func (vh *valueHasher) HashValue(data []byte) []byte {
return vh.digestData(data)
}
// ValueHashSize returns the length (in bytes) of digests produced by the value hasher
func (vh *valueHasher) ValueHashSize() int {
if vh.hasher == nil {
return 0
}
return vh.hasher.Size()
}
// Path satisfies the PathHasher#Path interface
func (n *nilPathHasher) Path(key []byte) []byte {
return key[:n.hashSize]
}
// PathSize satisfies the PathHasher#PathSize interface
func (n *nilPathHasher) PathSize() int {
return n.hashSize
}
// digestData returns the hash of the data provided using the trie hasher.
func (th *trieHasher) digestData(data []byte) []byte {
th.hasher.Write(data)
digest := th.hasher.Sum(nil)
th.hasher.Reset()
return digest
}
// digestLeafNode returns the encoded leaf data as well as its hash (i.e. digest)
func (th *trieHasher) digestLeafNode(path, data []byte) (digest, value []byte) {
value = encodeLeafNode(path, data)
digest = th.digestData(value)
return
}
// digestInnerNode returns the encoded inner node data as well as its hash (i.e. digest)
func (th *trieHasher) digestInnerNode(leftData, rightData []byte) (digest, value []byte) {
value = encodeInnerNode(leftData, rightData)
digest = th.digestData(value)
return
}
// digestSumNode returns the encoded leaf node data as well as its hash (i.e. digest)
func (th *trieHasher) digestSumLeafNode(path, data []byte) (digest, value []byte) {
value = encodeLeafNode(path, data)
firstSumByteIdx, firstCountByteIdx := getFirstMetaByteIdx(value)
digest = th.digestData(value)
digest = append(digest, value[firstSumByteIdx:firstCountByteIdx]...)
digest = append(digest, value[firstCountByteIdx:]...)
return
}
// digestSumInnerNode returns the encoded inner node data as well as its hash (i.e. digest)
func (th *trieHasher) digestSumInnerNode(leftData, rightData []byte) (digest, value []byte) {
value = encodeSumInnerNode(leftData, rightData)
firstSumByteIdx, firstCountByteIdx := getFirstMetaByteIdx(value)
digest = th.digestData(value)
digest = append(digest, value[firstSumByteIdx:firstCountByteIdx]...)
digest = append(digest, value[firstCountByteIdx:]...)
return
}
// parseInnerNode returns the encoded left and right nodes
func (th *trieHasher) parseInnerNode(data []byte) (leftData, rightData []byte) {
leftData = data[len(innerNodePrefix) : th.hashSize()+len(innerNodePrefix)]
rightData = data[len(innerNodePrefix)+th.hashSize():]
return
}
// parseSumInnerNode returns the encoded left & right nodes, as well as the sum
// and non-empty leaf count in the sub-trie of the current node.
func (th *trieHasher) parseSumInnerNode(data []byte) (leftData, rightData []byte, sum, count uint64) {
firstSumByteIdx, firstCountByteIdx := getFirstMetaByteIdx(data)
// Extract the sum from the encoded node data
var sumBz [sumSizeBytes]byte
copy(sumBz[:], data[firstSumByteIdx:firstCountByteIdx])
binary.BigEndian.PutUint64(sumBz[:], sum)
// Extract the count from the encoded node data
var countBz [countSizeBytes]byte
copy(countBz[:], data[firstCountByteIdx:])
binary.BigEndian.PutUint64(countBz[:], count)
// Extract the left and right children
leftIdxLastByte := len(innerNodePrefix) + th.hashSize() + sumSizeBytes + countSizeBytes
dataValue := data[:firstSumByteIdx]
leftData = dataValue[len(innerNodePrefix):leftIdxLastByte]
rightData = dataValue[leftIdxLastByte:]
return
}
func (th *trieHasher) hashSize() int {
return th.hasher.Size()
}
func (th *trieHasher) placeholder() []byte {
return th.zeroValue
}