-
Notifications
You must be signed in to change notification settings - Fork 212
/
layer.go
242 lines (222 loc) · 6.15 KB
/
layer.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
package sim
import (
"github.com/spacemeshos/go-spacemesh/common/types"
"github.com/spacemeshos/go-spacemesh/log"
"github.com/spacemeshos/go-spacemesh/proposals/util"
"github.com/spacemeshos/go-spacemesh/signing"
"github.com/spacemeshos/go-spacemesh/sql/beacons"
)
// DefaultNumBlocks is a number of blocks in a layer by default.
const DefaultNumBlocks = 5
// NextOpt is for configuring layer generator.
type NextOpt func(*nextConf)
func nextConfDefaults() nextConf {
return nextConf{
VoteGen: PerfectVoting,
Coinflip: true,
LayerSize: -1,
NumBlocks: DefaultNumBlocks,
BlockTickHeights: make([]uint64, DefaultNumBlocks),
}
}
type nextConf struct {
Reorder uint32
FailHare bool
EmptyHare bool
HareOutputIndex int
Coinflip bool
LayerSize int
NumBlocks int
BlockTickHeights []uint64
VoteGen VotesGenerator
}
// WithNextReorder configures when reordered layer should be returned.
// Examples:
// Next() Next(WithNextReorder(1)) Next()
// 1 3 2
// Next() Next(WithNextReorder(2)) Next() Next()
// 1 3 4 2
//
// So the Next layer with WithNextReorder will be delayed exactly by `delay` value.
func WithNextReorder(delay uint32) NextOpt {
return func(c *nextConf) {
c.Reorder = delay
}
}
// WithoutHareOutput will prevent from saving hare output.
func WithoutHareOutput() NextOpt {
return func(c *nextConf) {
c.FailHare = true
}
}
// WithEmptyHareOutput will save an empty vector for hare output.
func WithEmptyHareOutput() NextOpt {
return func(c *nextConf) {
c.EmptyHare = true
}
}
// WithLayerSizeOverwrite overwrite expected layer size.
func WithLayerSizeOverwrite(size int) NextOpt {
return func(c *nextConf) {
c.LayerSize = size
}
}
// WithVoteGenerator declares vote generator for a layer.
func WithVoteGenerator(gen VotesGenerator) NextOpt {
return func(c *nextConf) {
c.VoteGen = gen
}
}
// WithCoin is to setup weak coin for voting. By default coin will support blocks.
func WithCoin(coin bool) NextOpt {
return func(c *nextConf) {
c.Coinflip = coin
}
}
// WithBlockTickHeights updates height of the blocks.
func WithBlockTickHeights(heights ...uint64) NextOpt {
return func(c *nextConf) {
c.BlockTickHeights = heights
}
}
// WithNumBlocks sets number of the generated blocks.
func WithNumBlocks(num int) NextOpt {
return func(c *nextConf) {
c.NumBlocks = num
}
}
// WithHareOutputIndex sets the index of the block that will be stored as a hare output.
func WithHareOutputIndex(i int) NextOpt {
return func(c *nextConf) {
c.HareOutputIndex = i
}
}
// Next generates the next layer.
func (g *Generator) Next(opts ...NextOpt) types.LayerID {
cfg := nextConfDefaults()
for _, opt := range opts {
opt(&cfg)
}
// TODO(dshulyak) we are not reordering already reordered layer
if lid, exist := g.reordered[g.nextLayer]; exist {
delete(g.reordered, g.nextLayer)
return lid
}
lid := g.genLayer(cfg)
if cfg.Reorder != 0 {
// Add(1) to account for generated layer at the end
g.reordered[lid.Add(cfg.Reorder).Add(1)] = lid
return g.genLayer(cfg)
}
return lid
}
func (g *Generator) genBeacon() {
eid := g.nextLayer.Sub(1).GetEpoch()
beacon := types.Beacon{}
g.rng.Read(beacon[:])
for _, state := range g.states {
state.OnBeacon(eid, beacon)
}
}
func (g *Generator) genTXIDs(n int) []types.TransactionID {
txIDs := make([]types.TransactionID, 0, n)
for i := 0; i < n; i++ {
txid := types.TransactionID{}
g.rng.Read(txid[:])
txIDs = append(txIDs, txid)
}
return txIDs
}
func (g *Generator) genLayer(cfg nextConf) types.LayerID {
if g.nextLayer.Sub(1).GetEpoch() < g.nextLayer.GetEpoch() {
g.generateAtxs()
g.genBeacon()
}
layer := types.NewLayer(g.nextLayer)
size := int(g.conf.LayerSize)
if cfg.LayerSize >= 0 {
size = cfg.LayerSize
}
var total uint64
for _, atx := range g.activations {
total += atx.GetWeight()
}
miners := make([]uint32, len(g.activations))
for i := 0; i < size; i++ {
miner := i % len(g.activations)
miners[miner]++
}
for miner, maxj := range miners {
if maxj == 0 {
continue
}
voting := cfg.VoteGen(g.rng, g.layers, miner)
atx := g.activations[miner]
signer := g.keys[miner]
proofs := []types.VotingEligibility{}
for j := uint32(0); j < maxj; j++ {
proofs = append(proofs, types.VotingEligibility{J: j})
}
beacon, err := beacons.Get(g.states[0].DB, g.nextLayer.GetEpoch())
if err != nil {
g.logger.With().Panic("failed to get a beacon", log.Err(err))
}
n, err := util.GetNumEligibleSlots(atx.GetWeight(), 0, total, g.conf.LayerSize, g.conf.LayersPerEpoch)
if err != nil {
g.logger.With().Panic("eligible slots", log.Err(err))
}
ballot := &types.Ballot{
InnerBallot: types.InnerBallot{
Layer: g.nextLayer,
AtxID: atx.ID(),
EpochData: &types.EpochData{
EligibilityCount: n,
ActiveSetHash: types.Hash32{1, 2, 3},
Beacon: beacon,
},
},
Votes: voting,
EligibilityProofs: proofs,
}
ballot.Signature = signer.Sign(signing.BALLOT, ballot.SignedBytes())
ballot.SmesherID = signer.NodeID()
if err := ballot.Initialize(); err != nil {
g.logger.With().Panic("failed to init ballot", log.Err(err))
}
for _, state := range g.states {
state.OnBallot(ballot)
}
layer.AddBallot(ballot)
}
if len(cfg.BlockTickHeights) < cfg.NumBlocks {
g.logger.With().Panic("BlockTickHeights should be at least NumBlocks",
log.Int("num blocks", cfg.NumBlocks),
)
}
for i := 0; i < cfg.NumBlocks; i++ {
block := &types.Block{}
block.LayerIndex = g.nextLayer
block.TxIDs = g.genTXIDs(3)
block.TickHeight = cfg.BlockTickHeights[i]
block.Initialize()
for _, state := range g.states {
state.OnBlock(block)
}
layer.AddBlock(block)
}
if !cfg.FailHare {
hareOutput := types.EmptyBlockID
if !cfg.EmptyHare && len(layer.BlocksIDs()) > 0 {
hareOutput = layer.BlocksIDs()[cfg.HareOutputIndex]
}
for _, state := range g.states {
state.OnHareOutput(layer.Index(), hareOutput)
}
}
for _, state := range g.states {
state.OnCoinflip(layer.Index(), cfg.Coinflip)
}
g.layers = append(g.layers, layer)
g.nextLayer = g.nextLayer.Add(1)
return layer.Index()
}