forked from jacohend/lnd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathelement.go
167 lines (143 loc) · 4.7 KB
/
element.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
package shachain
import (
"crypto/sha256"
"errors"
"github.com/btcsuite/btcd/chaincfg/chainhash"
)
// element represents the entity which contains the hash and index
// corresponding to it. An element is the output of the shachain PRF. By
// comparing two indexes we're able to mutate the hash in such way to derive
// another element.
type element struct {
index index
hash chainhash.Hash
}
// newElementFromStr creates new element from the given hash string.
func newElementFromStr(s string, index index) (*element, error) {
hash, err := hashFromString(s)
if err != nil {
return nil, err
}
return &element{
index: index,
hash: *hash,
}, nil
}
// derive computes one shachain element from another by applying a series of
// bit flips and hashing operations based on the starting and ending index.
func (e *element) derive(toIndex index) (*element, error) {
fromIndex := e.index
positions, err := fromIndex.deriveBitTransformations(toIndex)
if err != nil {
return nil, err
}
buf := e.hash.CloneBytes()
for _, position := range positions {
// Flip the bit and then hash the current state.
byteNumber := position / 8
bitNumber := position % 8
buf[byteNumber] ^= (1 << bitNumber)
h := sha256.Sum256(buf)
buf = h[:]
}
hash, err := chainhash.NewHash(buf)
if err != nil {
return nil, err
}
return &element{
index: toIndex,
hash: *hash,
}, nil
}
// isEqual returns true if two elements are identical and false otherwise.
func (e *element) isEqual(e2 *element) bool {
return (e.index == e2.index) &&
(&e.hash).IsEqual(&e2.hash)
}
const (
// maxHeight is used to determine the maximum allowable index and the
// length of the array required to order to derive all previous hashes
// by index. The entries of this array as also known as buckets.
maxHeight uint8 = 48
// rootIndex is an index which corresponds to the root hash.
rootIndex index = 0
)
// startIndex is the index of first element in the shachain PRF.
var startIndex index = (1 << maxHeight) - 1
// index is a number which identifies the hash number and serves as a way to
// determine the hashing operation required to derive one hash from another.
// index is initialized with the startIndex and decreases down to zero with
// successive derivations.
type index uint64
// newIndex is used to create index instance. The inner operations with index
// implies that index decreasing from some max number to zero, but for
// simplicity and backward compatibility with previous logic it was transformed
// to work in opposite way.
func newIndex(v uint64) index {
return startIndex - index(v)
}
// deriveBitTransformations function checks that the 'to' index is derivable
// from the 'from' index by checking the indexes are prefixes of another. The
// bit positions where the zeroes should be changed to ones in order for the
// indexes to become the same are returned. This set of bits is needed in order
// to derive one hash from another.
//
// NOTE: The index 'to' is derivable from index 'from' iff index 'from' lies
// left and above index 'to' on graph below, for example:
// 1. 7(0b111) -> 7
// 2. 6(0b110) -> 6,7
// 3. 5(0b101) -> 5
// 4. 4(0b100) -> 4,5,6,7
// 5. 3(0b011) -> 3
// 6. 2(0b010) -> 2, 3
// 7. 1(0b001) -> 1
//
// ^ bucket number
// |
// 3 | x
// | |
// 2 | | x
// | | |
// 1 | | x | x
// | | | | |
// 0 | | x | x | x | x
// | | | | | | | | |
// +---|---|---|---|---|---|---|---|---> index
// 0 1 2 3 4 5 6 7
//
func (from index) deriveBitTransformations(to index) ([]uint8, error) {
var positions []uint8
if from == to {
return positions, nil
}
// + --------------- +
// | № | from | to |
// + -- + ---- + --- +
// | 48 | 1 | 1 |
// | 47 | 0 | 0 | [48-5] - same part of 'from' and 'to'
// | 46 | 0 | 0 | indexes which also is called prefix.
// ....
// | 5 | 1 | 1 |
// | 4 | 0 | 1 | <--- position after which indexes becomes
// | 3 | 0 | 0 | different, after this position
// | 2 | 0 | 1 | bits in 'from' index all should be
// | 1 | 0 | 0 | zeros or such indexes considered to be
// | 0 | 0 | 1 | not derivable.
// + -- + ---- + --- +
zeros := countTrailingZeros(from)
if uint64(from) != getPrefix(to, zeros) {
return nil, errors.New("prefixes are different - indexes " +
"aren't derivable")
}
// The remaining part of 'to' index represents the positions which we
// will use then in order to derive one element from another.
for position := zeros - 1; ; position-- {
if getBit(to, position) == 1 {
positions = append(positions, position)
}
if position == 0 {
break
}
}
return positions, nil
}