-
Notifications
You must be signed in to change notification settings - Fork 109
/
drand.go
315 lines (283 loc) 路 8.3 KB
/
drand.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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
package core
import (
"errors"
"fmt"
"sync"
"github.com/dedis/drand/beacon"
"github.com/dedis/drand/dkg"
"github.com/dedis/drand/fs"
"github.com/dedis/drand/key"
"github.com/dedis/drand/net"
dkg_proto "github.com/dedis/drand/protobuf/dkg"
"github.com/nikkolasg/slog"
)
// Drand is the main logic of the program. It reads the keys / group file, it
// can start the DKG, read/write shars to files and can initiate/respond to TBlS
// signature requests.
type Drand struct {
opts *Config
priv *key.Pair
// current group this drand node is using
group *key.Group
// index in the current group
idx int
store key.Store
gateway net.Gateway
dkg *dkg.Handler
beacon *beacon.Handler
beaconStore beacon.Store
// dkg private share. can be nil if dkg not finished yet.
share *key.Share
// dkg public key. Can be nil if dkg not finished yet.
pub *key.DistPublic
dkgDone bool
// proposed next group hash for a resharing operation
nextGroupHash string
nextGroup *key.Group
nextConf *dkg.Config
// global state lock
state sync.Mutex
}
// NewDrand returns an drand struct. It assumes the private key pair
// has been generated and saved already.
func NewDrand(s key.Store, c *Config) (*Drand, error) {
d, err := initDrand(s, c)
if err != nil {
return nil, err
}
return d, nil
}
// initDrand inits the drand struct by loading the private key, and by creating the
// gateway with the correct options.
func initDrand(s key.Store, c *Config) (*Drand, error) {
if c.insecure == false && (c.certPath == "" || c.keyPath == "") {
return nil, errors.New("config: need to set WithInsecure if no certificate and private key path given")
}
priv, err := s.LoadKeyPair()
if err != nil {
return nil, err
}
// trick to always set the listening address by default based on the
// identity. If there is an option to set the address, it will override the
// default set here..
d := &Drand{
store: s,
priv: priv,
opts: c,
}
a := c.ListenAddress(priv.Public.Address())
p := c.ControlPort()
if c.insecure {
d.gateway = net.NewGrpcGatewayInsecure(a, p, d, d, d.opts.grpcOpts...)
} else {
d.gateway = net.NewGrpcGatewayFromCertManager(a, p, c.certPath, c.keyPath, c.certmanager, d, d, d.opts.grpcOpts...)
}
d.gateway.StartAll()
return d, nil
}
// LoadDrand restores a drand instance that is ready to serve randomness, with a
// pre-existing distributed share.
func LoadDrand(s key.Store, c *Config) (*Drand, error) {
d, err := initDrand(s, c)
if err != nil {
return nil, err
}
d.group, err = s.LoadGroup()
if err != nil {
return nil, err
}
d.share, err = s.LoadShare()
if err != nil {
return nil, err
}
d.pub, err = s.LoadDistPublic()
if err != nil {
return nil, err
}
if err := d.initBeacon(); err != nil {
return nil, err
}
slog.Debugf("drand: loaded and serving at %s", d.priv.Public.Address())
return d, nil
}
// StartDKG starts the DKG protocol by sending the first packet of the DKG
// protocol to every other node in the group. It returns nil if the DKG protocol
// finished successfully or an error otherwise.
func (d *Drand) StartDKG() error {
if err := d.createDKG(); err != nil {
return err
}
d.dkg.Start()
return nil
}
// WaitDKG waits on the running dkg protocol. In case of an error, it returns
// it. In case of a finished DKG protocol, it saves the dist. public key and
// private share. These should be loadable by the store.
func (d *Drand) WaitDKG() error {
if err := d.createDKG(); err != nil {
return err
}
d.state.Lock()
waitCh := d.dkg.WaitShare()
errCh := d.dkg.WaitError()
d.state.Unlock()
var err error
select {
case share := <-waitCh:
s := key.Share(share)
d.share = &s
case err = <-errCh:
}
if err != nil {
return err
}
d.state.Lock()
defer d.state.Unlock()
d.store.SaveShare(d.share)
d.store.SaveDistPublic(d.share.Public())
// XXX change to qualified group when handling failure during DKG time
d.group = d.nextConf.NewNodes
d.group.PublicKey = d.share.Public()
d.store.SaveGroup(d.group)
d.dkgDone = true
d.dkg = nil
d.nextConf = nil
return nil
}
// createDKG create the new dkg handler according to the nextConf field. If the
// dkg is not nil, it does not do anything.
func (d *Drand) createDKG() error {
d.state.Lock()
defer d.state.Unlock()
if d.dkg != nil {
return nil
}
if d.nextConf == nil {
return errors.New("drand: invalid state -> nil nextConf")
}
var err error
c := d.nextConf
if d.dkg, err = dkg.NewHandler(d.dkgNetwork(c), c); err != nil {
return err
}
return nil
}
// DefaultSeed is the message signed during the first beacon generation,
// alongside with the round number 0.
var DefaultSeed = []byte("Truth is like the sun. You can shut it out for a time, but it ain't goin' away.")
// StartBeacon initializes the beacon if needed and launch a go routine that
// runs the generation loop.
func (d *Drand) StartBeacon() error {
if err := d.initBeacon(); err != nil {
return err
}
go d.BeaconLoop()
return nil
}
// StopBeacon stops the beacon generation process and resets it.
func (d *Drand) StopBeacon() {
d.state.Lock()
defer d.state.Unlock()
if d.beacon == nil {
return
}
d.beacon.Stop()
d.beacon = nil
}
// BeaconLoop starts periodically the TBLS protocol. The seed is the first
// message signed alongside with the current timestamp. All subsequent
// signatures are chained:
// s_i+1 = SIG(s_i || timestamp)
// For the moment, each resulting signature is stored in a file named
// beacons/<timestamp>.sig.
// The period is determined according the group.toml this node belongs to.
func (d *Drand) BeaconLoop() error {
d.state.Lock()
// heuristic: we catchup when we can retrieve a beacon from the db
// if there is an error we quit, if there is no beacon saved yet, we
// run the loop as usual.
var catchup = true
b, err := d.beaconStore.Last()
if err != nil {
if err == beacon.ErrNoBeaconSaved {
// we are starting the beacon generation
catchup = false
} else {
// there's a serious error
d.state.Unlock()
return fmt.Errorf("drand: could not determine beacon state: %s", err)
}
}
if catchup {
slog.Infof("drand: starting beacon loop in catch-up mode from round %v", b.Round)
} else {
slog.Infof("drand: starting beacon loop")
}
period := getPeriod(d.group)
d.state.Unlock()
d.beacon.Loop(DefaultSeed, period, catchup)
return nil
}
// Stop simply stops all drand operations.
func (d *Drand) Stop() {
d.StopBeacon()
d.state.Lock()
d.gateway.StopAll()
d.state.Unlock()
}
// isDKGDone returns true if the DKG protocol has already been executed. That
// means that the only packet that this node should receive are TBLS packet.
func (d *Drand) isDKGDone() bool {
d.state.Lock()
defer d.state.Unlock()
return d.dkgDone
}
func (d *Drand) initBeacon() error {
d.state.Lock()
defer d.state.Unlock()
if d.beacon != nil {
return nil
}
d.dkgDone = true
fs.CreateSecureFolder(d.opts.DBFolder())
store, err := beacon.NewBoltStore(d.opts.dbFolder, d.opts.boltOpts)
if err != nil {
return err
}
d.beaconStore = beacon.NewCallbackStore(store, d.beaconCallback)
d.beacon = beacon.NewHandler(d.gateway.InternalClient, d.priv, d.share, d.group, d.beaconStore)
return nil
}
func (d *Drand) beaconCallback(b *beacon.Beacon) {
d.opts.callbacks(b)
}
// little trick to be able to capture when drand is using the DKG methods,
// instead of offloading that to an external struct without any vision of drand
// internals, or implementing a big "Send" method directly on drand.
func (d *Drand) sendDkgPacket(p net.Peer, pack *dkg_proto.DKGPacket) error {
_, err := d.gateway.InternalClient.Setup(p, pack)
return err
}
func (d *Drand) sendResharePacket(p net.Peer, pack *dkg_proto.DKGPacket) error {
// no concurrency to get nextHash since this is only used within a locked drand
reshare := &dkg_proto.ResharePacket{
Packet: pack,
GroupHash: d.nextGroupHash,
}
_, err := d.gateway.InternalClient.Reshare(p, reshare)
return err
}
func (d *Drand) dkgNetwork(conf *dkg.Config) *dkgNetwork {
// simple test to check if we are in a resharing mode or in a fresh dkg mode
// that will lead to two different outer protobuf structures
if conf.OldNodes == nil {
return &dkgNetwork{d.sendDkgPacket}
}
return &dkgNetwork{d.sendResharePacket}
}
type dkgNetwork struct {
send func(net.Peer, *dkg_proto.DKGPacket) error
}
func (d *dkgNetwork) Send(p net.Peer, pack *dkg_proto.DKGPacket) error {
return d.send(p, pack)
}