forked from lightningnetwork/lnd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
store.go
184 lines (151 loc) · 5.2 KB
/
store.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
package shachain
import (
"encoding/binary"
"io"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/go-errors/errors"
)
// Store is an interface which serves as an abstraction over data structure
// responsible for efficiently storing and restoring of hash secrets by given
// indexes.
//
// Description: The Lightning Network wants a chain of (say 1 million)
// unguessable 256 bit values; we generate them and send them one at a time to
// a remote node. We don't want the remote node to have to store all the
// values, so it's better if they can derive them once they see them.
type Store interface {
// LookUp function is used to restore/lookup/fetch the previous secret
// by its index.
LookUp(uint64) (*chainhash.Hash, error)
// AddNextEntry attempts to store the given hash within its internal
// storage in an efficient manner.
//
// NOTE: The hashes derived from the shachain MUST be inserted in the
// order they're produced by a shachain.Producer.
AddNextEntry(*chainhash.Hash) error
// Encode writes a binary serialization of the shachain elements
// currently saved by implementation of shachain.Store to the passed
// io.Writer.
Encode(io.Writer) error
}
// RevocationStore is a concrete implementation of the Store interface. The
// revocation store is able to efficiently store N derived shachain elements in
// a space efficient manner with a space complexity of O(log N). The original
// description of the storage methodology can be found here:
// https://github.com/lightningnetwork/lightning-rfc/blob/master/03-transactions.md#efficient-per-commitment-secret-storage
type RevocationStore struct {
// lenBuckets stores the number of currently active buckets.
lenBuckets uint8
// buckets is an array of elements from which we may derive all
// previous elements, each bucket corresponds to the element with the
// particular number of trailing zeros.
buckets [maxHeight]element
// index is an available index which will be assigned to the new
// element.
index index
}
// A compile time check to ensure RevocationStore implements the Store
// interface.
var _ Store = (*RevocationStore)(nil)
// NewRevocationStore creates the new shachain store.
func NewRevocationStore() *RevocationStore {
return &RevocationStore{
lenBuckets: 0,
index: startIndex,
}
}
// NewRevocationStoreFromBytes recreates the initial store state from the given
// binary shachain store representation.
func NewRevocationStoreFromBytes(r io.Reader) (*RevocationStore, error) {
store := &RevocationStore{}
if err := binary.Read(r, binary.BigEndian, &store.lenBuckets); err != nil {
return nil, err
}
for i := uint8(0); i < store.lenBuckets; i++ {
var hashIndex index
err := binary.Read(r, binary.BigEndian, &hashIndex)
if err != nil {
return nil, err
}
var nextHash chainhash.Hash
if _, err := io.ReadFull(r, nextHash[:]); err != nil {
return nil, err
}
store.buckets[i] = element{
index: hashIndex,
hash: nextHash,
}
}
if err := binary.Read(r, binary.BigEndian, &store.index); err != nil {
return nil, err
}
return store, nil
}
// LookUp function is used to restore/lookup/fetch the previous secret by its
// index. If secret which corresponds to given index was not previously placed
// in store we will not able to derive it and function will fail.
//
// NOTE: This function is part of the Store interface.
func (store *RevocationStore) LookUp(v uint64) (*chainhash.Hash, error) {
ind := newIndex(v)
// Trying to derive the index from one of the existing buckets elements.
for i := uint8(0); i < store.lenBuckets; i++ {
element, err := store.buckets[i].derive(ind)
if err != nil {
continue
}
return &element.hash, nil
}
return nil, errors.Errorf("unable to derive hash #%v", ind)
}
// AddNextEntry attempts to store the given hash within its internal storage in
// an efficient manner.
//
// NOTE: The hashes derived from the shachain MUST be inserted in the order
// they're produced by a shachain.Producer.
//
// NOTE: This function is part of the Store interface.
func (store *RevocationStore) AddNextEntry(hash *chainhash.Hash) error {
newElement := &element{
index: store.index,
hash: *hash,
}
bucket := countTrailingZeros(newElement.index)
for i := uint8(0); i < bucket; i++ {
e, err := newElement.derive(store.buckets[i].index)
if err != nil {
return err
}
if !e.isEqual(&store.buckets[i]) {
return errors.New("hash isn't derivable from " +
"previous ones")
}
}
store.buckets[bucket] = *newElement
if bucket+1 > store.lenBuckets {
store.lenBuckets = bucket + 1
}
store.index--
return nil
}
// Encode writes a binary serialization of the shachain elements currently
// saved by implementation of shachain.Store to the passed io.Writer.
//
// NOTE: This function is part of the Store interface.
func (store *RevocationStore) Encode(w io.Writer) error {
err := binary.Write(w, binary.BigEndian, store.lenBuckets)
if err != nil {
return err
}
for i := uint8(0); i < store.lenBuckets; i++ {
element := store.buckets[i]
err := binary.Write(w, binary.BigEndian, element.index)
if err != nil {
return err
}
if _, err = w.Write(element.hash[:]); err != nil {
return err
}
}
return binary.Write(w, binary.BigEndian, store.index)
}