-
Notifications
You must be signed in to change notification settings - Fork 212
/
generator.go
261 lines (226 loc) · 5.83 KB
/
generator.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
package sim
import (
"math/rand"
"time"
"github.com/spacemeshos/go-spacemesh/activation"
"github.com/spacemeshos/go-spacemesh/common/types"
"github.com/spacemeshos/go-spacemesh/log"
"github.com/spacemeshos/go-spacemesh/signing"
)
// GenOpt for configuring Generator.
type GenOpt func(*Generator)
// WithSeed configures seed for Generator. By default 0 is used.
func WithSeed(seed int64) GenOpt {
return func(g *Generator) {
g.rng = rand.New(rand.NewSource(seed))
}
}
// WithLayerSize configures average layer size.
func WithLayerSize(size uint32) GenOpt {
return func(g *Generator) {
g.conf.LayerSize = size
}
}
// WithLogger configures logger.
func WithLogger(logger log.Log) GenOpt {
return func(g *Generator) {
g.logger = logger
}
}
// WithPath configures path for persistent databases.
func WithPath(path string) GenOpt {
return func(g *Generator) {
g.conf.Path = path
}
}
// WithStates creates n states.
func WithStates(n int) GenOpt {
if n == 0 {
panic("generator without attached state is not supported")
}
return func(g *Generator) {
g.conf.StateInstances = n
}
}
func withRng(rng *rand.Rand) GenOpt {
return func(g *Generator) {
g.rng = rng
}
}
func withConf(conf config) GenOpt {
return func(g *Generator) {
g.conf = conf
}
}
type config struct {
Path string
LayerSize uint32
LayersPerEpoch uint32
StateInstances int
}
func defaults() config {
return config{
LayerSize: 30,
LayersPerEpoch: types.GetLayersPerEpoch(),
StateInstances: 1,
}
}
// New creates Generator instance.
func New(opts ...GenOpt) *Generator {
g := &Generator{
rng: rand.New(rand.NewSource(0)),
conf: defaults(),
logger: log.NewNop(),
reordered: map[types.LayerID]types.LayerID{},
}
for _, opt := range opts {
opt(g)
}
// TODO support multiple persist states.
for i := 0; i < g.conf.StateInstances; i++ {
g.states = append(g.states, newState(g.logger, g.conf))
}
return g
}
// Generator for layers of blocks.
type Generator struct {
logger log.Log
rng *rand.Rand
conf config
states []State
nextLayer types.LayerID
// key is when to return => value is the layer to return
reordered map[types.LayerID]types.LayerID
layers []*types.Layer
units [2]int
activations []*types.VerifiedActivationTx
ticksRange [2]int
ticks []uint64
prevHeight []uint64
keys []*signing.EdSigner
}
// SetupOpt configures setup.
type SetupOpt func(g *setupConf)
// WithSetupMinerRange number of miners will be selected between low and high values.
func WithSetupMinerRange(low, high int) SetupOpt {
return func(conf *setupConf) {
conf.Miners = [2]int{low, high}
}
}
// WithSetupUnitsRange adjusts units of the ATXs, which will directly affect block weight.
func WithSetupUnitsRange(low, high int) SetupOpt {
return func(conf *setupConf) {
conf.UnitsRange = [2]int{low, high}
}
}
// WithSetupTicksRange configures range of atxs, that will be randomly chosen by atxs.
func WithSetupTicksRange(low, high int) SetupOpt {
return func(conf *setupConf) {
conf.TicksRange = [2]int{low, high}
}
}
// WithSetupTicks configures ticks for every atx.
func WithSetupTicks(ticks ...uint64) SetupOpt {
return func(conf *setupConf) {
if len(ticks) > 0 {
conf.Ticks = ticks
}
}
}
type setupConf struct {
Miners [2]int
UnitsRange [2]int
TicksRange [2]int
Ticks []uint64
}
func defaultSetupConf() setupConf {
return setupConf{
Miners: [2]int{30, 30},
UnitsRange: [2]int{10, 10},
TicksRange: [2]int{10, 10},
}
}
// GetState at index.
func (g *Generator) GetState(i int) State {
return g.states[i]
}
func (g *Generator) addState(state State) {
g.states = append(g.states, state)
}
func (g *Generator) popState(i int) State {
state := g.states[i]
copy(g.states[i:], g.states[i+1:])
g.states[len(g.states)-1] = State{}
g.states = g.states[:len(g.states)-1]
return state
}
// Setup should be called before running Next.
func (g *Generator) Setup(opts ...SetupOpt) {
conf := defaultSetupConf()
for _, opt := range opts {
opt(&conf)
}
if conf.Ticks != nil && conf.Miners[0] != conf.Miners[1] && len(conf.Ticks) != conf.Miners[0] {
g.logger.Panic("if conf.Ticks is provided it should be equal to the constant number of conf.Miners")
}
g.units = conf.UnitsRange
g.ticks = conf.Ticks
g.ticksRange = conf.TicksRange
if len(g.layers) == 0 {
genesis := types.NewLayer(types.GetEffectiveGenesis())
ballot := &types.Ballot{}
ballot.Layer = genesis.Index()
genesis.AddBallot(ballot)
g.layers = append(g.layers, genesis)
}
last := g.layers[len(g.layers)-1]
g.nextLayer = last.Index().Add(1)
miners := intInRange(g.rng, conf.Miners)
g.activations = make([]*types.VerifiedActivationTx, miners)
g.prevHeight = make([]uint64, miners)
for i := uint32(0); i < miners; i++ {
sig, err := signing.NewEdSigner(signing.WithKeyFromRand(g.rng))
if err != nil {
panic(err)
}
g.keys = append(g.keys, sig)
}
}
func (g *Generator) generateAtxs() {
for i := range g.activations {
units := intInRange(g.rng, g.units)
sig, err := signing.NewEdSigner()
if err != nil {
panic(err)
}
address := types.GenerateAddress(sig.PublicKey().Bytes())
nipost := types.NIPostChallenge{
PublishEpoch: g.nextLayer.Sub(1).GetEpoch(),
}
atx := types.NewActivationTx(nipost, address, nil, units, nil)
var ticks uint64
if g.ticks != nil {
ticks = g.ticks[i]
} else {
ticks = uint64(intInRange(g.rng, g.ticksRange))
}
if err := activation.SignAndFinalizeAtx(sig, atx); err != nil {
panic(err)
}
atx.SetEffectiveNumUnits(atx.NumUnits)
atx.SetReceived(time.Now())
vatx, err := atx.Verify(g.prevHeight[i], ticks)
if err != nil {
panic(err)
}
g.prevHeight[i] += ticks
g.activations[i] = vatx
for _, state := range g.states {
state.OnActivationTx(vatx)
}
}
}
// Layer returns generated layer.
func (g *Generator) Layer(i int) *types.Layer {
return g.layers[i+1]
}