-
Notifications
You must be signed in to change notification settings - Fork 29
/
Copy pathsimulation.go
379 lines (347 loc) · 11.3 KB
/
simulation.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
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
package onet
import (
"io/ioutil"
"net"
"os"
"strconv"
"strings"
"time"
"github.com/BurntSushi/toml"
"go.dedis.ch/kyber/v3"
"go.dedis.ch/kyber/v3/suites"
"go.dedis.ch/kyber/v3/util/key"
"go.dedis.ch/onet/v3/log"
"go.dedis.ch/onet/v3/network"
"golang.org/x/xerrors"
)
type simulationCreate func(string) (Simulation, error)
var simulationRegistered map[string]simulationCreate
// SimulationFileName is the name of the (binary encoded) file containing the
// simulation config.
const SimulationFileName = "simulation.bin"
// Simulation is an interface needed by every protocol that wants to be available
// to be used in a simulation.
type Simulation interface {
// This has to initialise all necessary files and copy them to the
// 'dir'-directory. This directory will be accessible to all simulated
// hosts.
// Setup also gets a slice of all available hosts. In turn it has
// to return a tree using one or more of these hosts. It can create
// the Roster as desired, putting more than one ServerIdentity/Host on the same host.
// The 'config'-argument holds all arguments read from the runfile in
// toml-format.
Setup(dir string, hosts []string) (*SimulationConfig, error)
// Node will be run for every node and might be used to setup load-
// creation. It is started once the Host is set up and running, but before
// 'Run'
Node(config *SimulationConfig) error
// Run will begin with the simulation or return an error. It is sure
// to be run on the host where 'tree.Root' is. It should only return
// when all rounds are done.
Run(config *SimulationConfig) error
}
// SimulationConfig has to be returned from 'Setup' and will be passed to
// 'Run'.
type SimulationConfig struct {
// Represents the tree that has to be used
Tree *Tree
// The Roster used by the tree
Roster *Roster
// All private keys generated by 'Setup', indexed by the complete addresses
PrivateKeys map[network.Address]*SimulationPrivateKey
// If non-nil, points to our overlay
Overlay *Overlay
// If non-nil, points to our host
Server *Server
// Tells if the simulation should use TLS addresses; if not, use PlainTCP
TLS bool
// Additional configuration used to run
Config string
}
// SimulationPrivateKey contains the default private key and the service
// private keys for the ones registered with a suite.
type SimulationPrivateKey struct {
Private kyber.Scalar
Services []kyber.Scalar
}
// newSimulationPrivateKey instantiates and makes the map
func newSimulationPrivateKey(priv kyber.Scalar) *SimulationPrivateKey {
return &SimulationPrivateKey{
Private: priv,
Services: make([]kyber.Scalar, 0),
}
}
// SimulationConfigFile stores the state of the simulation's config.
// Only used internally.
type SimulationConfigFile struct {
TreeMarshal *TreeMarshal
Roster *Roster
PrivateKeys map[network.Address]*SimulationPrivateKey
TLS bool
Config string
}
// LoadSimulationConfig gets all configuration from dir + SimulationFileName and instantiates the
// corresponding host 'ca'.
func LoadSimulationConfig(s, dir, ca string) ([]*SimulationConfig, error) {
// Have all servers created by NewServerTCP below put their
// db's into this simulation directory.
os.Setenv("CONODE_SERVICE_PATH", dir)
// TODO: Figure this out from the incoming simulation file somehow
suite := suites.MustFind(s)
network.RegisterMessage(SimulationConfigFile{})
bin, err := ioutil.ReadFile(dir + "/" + SimulationFileName)
if err != nil {
return nil, xerrors.Errorf("reading file: %v", err)
}
_, msg, err := network.Unmarshal(bin, suite)
if err != nil {
return nil, xerrors.Errorf("unmarshaling: %v", err)
}
scf := msg.(*SimulationConfigFile)
sc := &SimulationConfig{
Roster: scf.Roster,
PrivateKeys: scf.PrivateKeys,
TLS: scf.TLS,
Config: scf.Config,
}
sc.Tree, err = scf.TreeMarshal.MakeTree(sc.Roster)
if err != nil {
return nil, xerrors.Errorf("making tree: %v", err)
}
var ret []*SimulationConfig
if ca != "" {
if !strings.Contains(ca, ":") {
// to correctly match hosts a column is needed, else
// 10.255.0.1 would also match 10.255.0.10 and others
ca += ":"
}
for _, e := range sc.Roster.List {
if strings.Contains(e.Address.String(), ca) {
e.SetPrivate(scf.PrivateKeys[e.Address].Private)
// Populate the private key in the same array order
for i, privkey := range scf.PrivateKeys[e.Address].Services {
sid := e.ServiceIdentities[i]
suite, err := suites.Find(sid.Suite)
if err != nil {
return nil, xerrors.Errorf("Unknown suite with name %s", sid.Suite)
}
e.ServiceIdentities[i] = network.NewServiceIdentity(sid.Name, suite, sid.Public, privkey)
}
server := NewServerTCP(e, suite)
server.UnauthOk = true
server.Quiet = true
scNew := *sc
scNew.Server = server
scNew.Overlay = server.overlay
ret = append(ret, &scNew)
}
}
if len(ret) == 0 {
return nil, xerrors.New("Address not used in simulation: " + ca)
}
} else {
ret = append(ret, sc)
}
addr := string(sc.Roster.List[0].Address)
if strings.Contains(addr, "127.0.0.") {
// Now strip all superfluous numbers of localhost
for i := range sc.Roster.List {
_, port, _ := net.SplitHostPort(sc.Roster.List[i].Address.NetworkAddress())
// put 127.0.0.1 because 127.0.0.X is not reachable on Mac OS X
if sc.TLS {
sc.Roster.List[i].Address = network.NewTLSAddress("127.0.0.1:" + port)
} else {
sc.Roster.List[i].Address = network.NewTCPAddress("127.0.0.1:" + port)
}
}
}
return ret, nil
}
// Save takes everything in the SimulationConfig structure and saves it to
// dir + SimulationFileName
func (sc *SimulationConfig) Save(dir string) error {
network.RegisterMessage(&SimulationConfigFile{})
scf := &SimulationConfigFile{
TreeMarshal: sc.Tree.MakeTreeMarshal(),
Roster: sc.Roster,
PrivateKeys: sc.PrivateKeys,
TLS: sc.TLS,
Config: sc.Config,
}
buf, err := network.Marshal(scf)
if err != nil {
log.Fatal(err)
}
err = ioutil.WriteFile(dir+"/"+SimulationFileName, buf, 0660)
if err != nil {
log.Fatal(err)
}
return nil
}
// GetService returns the service with the given name.
func (sc *SimulationConfig) GetService(name string) Service {
return sc.Server.serviceManager.service(name)
}
// SimulationRegister is must to be called to register a simulation.
// Protocol or simulation developers must not forget to call this function
// with the protocol's name.
func SimulationRegister(name string, sim simulationCreate) {
if simulationRegistered == nil {
simulationRegistered = make(map[string]simulationCreate)
}
simulationRegistered[name] = sim
}
// NewSimulation returns a simulation and decodes the 'conf' into the
// simulation-structure
func NewSimulation(name string, conf string) (Simulation, error) {
sim, ok := simulationRegistered[name]
if !ok {
return nil, xerrors.New("Didn't find simulation " + name)
}
simInst, err := sim(conf)
if err != nil {
return nil, xerrors.Errorf("creating simulation: %v", err)
}
_, err = toml.Decode(conf, simInst)
if err != nil {
return nil, xerrors.Errorf("decoding toml: %v", err)
}
return simInst, nil
}
// SimulationBFTree is the main struct storing the data for all the simulations
// which use a tree with a certain branching factor or depth.
type SimulationBFTree struct {
Rounds int
BF int
Hosts int
SingleHost bool
Depth int
Suite string
PreScript string // executable script to run before the simulation on each machine
TLS bool // tells if using TLS or PlainTCP addresses
}
// CreateRoster creates an Roster with the host-names in 'addresses'.
// It creates 's.Hosts' entries, starting from 'port' for each round through
// 'addresses'. The network.Address(es) created are of type TLS or PlainTCP,
// depending on the value 'TLS' in 'sc'.
func (s *SimulationBFTree) CreateRoster(sc *SimulationConfig, addresses []string, port int) {
start := time.Now()
sc.TLS = s.TLS
suite, err := suites.Find(s.Suite)
if err != nil {
log.Fatalf("Could not look up suite \"%v\": %+v", s.Suite, err)
}
nbrAddr := len(addresses)
if sc.PrivateKeys == nil {
sc.PrivateKeys = make(map[network.Address]*SimulationPrivateKey)
}
hosts := s.Hosts
if s.SingleHost {
// If we want to work with a single host, we only make one
// host per server
log.Fatal("Not supported yet")
hosts = nbrAddr
if hosts > s.Hosts {
hosts = s.Hosts
}
}
localhosts := false
listeners := make([]net.Listener, hosts)
services := make([]net.Listener, hosts)
if strings.Contains(addresses[0], "127.0.0.") {
localhosts = true
}
entities := make([]*network.ServerIdentity, hosts)
log.Lvl3("Doing", hosts, "hosts")
key := key.NewKeyPair(suite)
for c := 0; c < hosts; c++ {
key.Private.Add(key.Private, suite.Scalar().One())
key.Public.Add(key.Public, suite.Point().Base())
address := addresses[c%nbrAddr] + ":"
var add network.Address
if localhosts {
// If we have localhosts, we have to search for an empty port
port := 0
for port == 0 {
var err error
listeners[c], err = net.Listen("tcp", ":0")
if err != nil {
log.Fatal("Couldn't search for empty port:", err)
}
_, p, _ := net.SplitHostPort(listeners[c].Addr().String())
port, _ = strconv.Atoi(p)
services[c], err = net.Listen("tcp", ":"+strconv.Itoa(port+1))
if err != nil {
port = 0
}
}
address += strconv.Itoa(port)
if sc.TLS {
add = network.NewTLSAddress(address)
} else {
add = network.NewTCPAddress(address)
}
log.Lvl4("Found free port", address)
} else {
address += strconv.Itoa(port + (c/nbrAddr)*2)
if sc.TLS {
add = network.NewTLSAddress(address)
} else {
add = network.NewTCPAddress(address)
}
}
entities[c] = network.NewServerIdentity(key.Public.Clone(), add)
entities[c].SetPrivate(key.Private)
ServiceFactory.generateKeyPairs(entities[c])
privConf := newSimulationPrivateKey(key.Private.Clone())
sc.PrivateKeys[entities[c].Address] = privConf
for _, sid := range entities[c].ServiceIdentities {
privConf.Services = append(privConf.Services, sid.GetPrivate().Clone())
}
}
// And close all our listeners
if localhosts {
for _, l := range listeners {
err := l.Close()
if err != nil {
log.Fatal("Couldn't close port:", l, err)
}
}
for _, l := range services {
err := l.Close()
if err != nil {
log.Fatal("Couldn't close port:", l, err)
}
}
}
sc.Roster = NewRoster(entities)
log.Lvl3("Creating entity List took: " + time.Now().Sub(start).String())
}
// CreateTree the tree as defined in SimulationBFTree and stores the result
// in 'sc'
func (s *SimulationBFTree) CreateTree(sc *SimulationConfig) error {
log.Lvl3("CreateTree strarted")
start := time.Now()
if sc.Roster == nil {
return xerrors.New("Empty Roster")
}
sc.Tree = sc.Roster.GenerateBigNaryTree(s.BF, s.Hosts)
log.Lvl3("Creating tree took: " + time.Now().Sub(start).String())
return nil
}
// Node - standard registers the entityList and the Tree with that Overlay,
// so we don't have to pass that around for the experiments.
func (s *SimulationBFTree) Node(sc *SimulationConfig) error {
sc.Overlay.RegisterTree(sc.Tree)
return nil
}
// GetSingleHost returns the 'SingleHost'-flag
func (sc SimulationConfig) GetSingleHost() bool {
var sh struct{ SingleHost bool }
_, err := toml.Decode(sc.Config, &sh)
if err != nil {
log.Error("Couldn't decode string", sc.Config, "into toml.")
return false
}
return sh.SingleHost
}