-
Notifications
You must be signed in to change notification settings - Fork 5
/
scenarios.py
647 lines (519 loc) · 25.5 KB
/
scenarios.py
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
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
# _*_ coding: utf-8 _*_
"""
The included algorithms work optimally when they have pre-trusted peers to
defer to.
If your test case does something like render all peers unlikeable you may want
to set your good peers up with some pre-trusted peers.
Scenario two is the most useful for seeing the kind of conditions that cause
peers to acheive consensus about the maliciousness of their peers.
This can be produced with the following:
./eigentrust.py -v -n20 -p2 -t10000 -s two > `date "+%d-%m-%Y-%H%M%S%N"`.log
Real-world networks are unlikely to begin with 20 users so it's advised to test
new algorithms with low node counts and high iteration counts.
"""
import utils
import random
def scenario_one(options):
"""
Half of the population are good peers.
Pre-trusted peers are selected from within the set of good peers though
this can be made to overextend by setting |P| > (|nodes| / 2).
Makes for an uncomplicated calculate_trust() computation.
"""
routers = utils.generate_routers(options, minimum=4)
good_routers = routers[:len(routers) / 2]
bad_routers = routers[len(routers) / 2:]
[setattr(_, "probably_malicious", True) for _ in bad_routers]
utils.introduce(good_routers)
[_.tbucket.append(_.peers[:options.pre_trusted]) for _ in good_routers]
utils.introduce(bad_routers)
utils.introduce(good_routers, bad_routers)
# Note that this is based on a definite transaction count but that it's
# through a random transaction count that the distributed trust algorithm
# can be used to detect malicious peers via the set of pre-trusted peers.
utils.log("Emulating %s iterations of transactions with all peers." % \
"{:,}".format(options.transactions))
for _ in range(options.transactions):
for router in routers:
for peer in router:
c = random.randint(0, 1)
if options.verbose:
utils.log("%s is making %i transactions with %s." % (router, c, peer))
[router.transact_with(peer) for i in range(c)]
# Calculate trust every 5 rounds here. The periodicity in reality is a
# function of network size.
if _ > 1 and not (_+1) % 5:
for i, router in enumerate(routers):
utils.log("%i %s %s is sensing." % (i+1, router, router.node))
router.tbucket.calculate_trust()
# The return value of a scenario is used to populate "locals" in the event
# that you choose to use the --repl flag to spawn an interactive interpreter.
return {"routers": routers}
def scenario_two(options):
"""
Half of the population are good peers.
Pre-trusted peers are selected from within the set of good peers though
this can be made to overextend by setting |P| > (|nodes| / 2).
A mix of new peers are introduced every 1/5th of the iteration count.
Good peers have a 1 in 250 chance of receiving negative feedback from other
good peers.
This scenario has the highest likelihood of exhibiting consensus events.
"""
routers = utils.generate_routers(options, minimum=2)
good_routers = routers[:len(routers) / 2]
bad_routers = routers[len(routers) / 2:]
[setattr(_, "probably_malicious", True) for _ in bad_routers]
utils.introduce(good_routers)
[_.tbucket.append(_.peers[:options.pre_trusted]) for _ in good_routers]
utils.introduce(bad_routers)
utils.introduce(good_routers, bad_routers)
# Note that this is based on a definite transaction count but it's through a
# random transaction count with the possibility of some peers not transacting
# with some of their peers at all that the distributed trust algorithm can be
# used to detect malicious peers via the set of pre-trusted peers alone.
utils.log("Emulating %s iterations of transactions with all peers." % \
"{:,}".format(options.transactions))
for _ in range(options.transactions):
for router in routers:
for peer in router:
if not random.randint(0, 1): continue
if not router.probably_malicious and not peer.router.probably_malicious:
if peer.trust and random.randint(0, 250) == 1:
utils.log("Good peer %s is having a bad transaction with good peer %s." % \
(router.node, peer))
router.transact_with(peer, transaction_type=False)
continue
router.transact_with(peer)
# Calculate trust every 5 rounds here. Normally the periodicity would be
# a function of network size.
if _ > 1 and not (_+1) % 5:
for i, router in enumerate(routers):
utils.log("%i %s %s is sensing." % (i+1, router, router.node))
router.tbucket.calculate_trust()
# Introduce a mix of new peers every 1/5th of the iteration count
if _ > 5 and not _ % (options.transactions / 5):
new_good_routers = utils.generate_routers(options, maximum=random.randint(1, 3))
new_bad_routers = utils.generate_routers(options,
maximum=random.randint(1, 3),
attrs={'probably_malicious': True})
routers.extend(new_good_routers)
routers.extend(new_bad_routers)
[setattr(r, "routers", routers) for r in routers]
utils.introduce(new_good_routers, random.sample(routers,
random.choice(range(2, len(routers)))))
utils.introduce(new_bad_routers, random.sample(routers,
random.choice(range(2, len(routers)))))
for r in new_good_routers:
utils.log("Introduced %s %s into the system." % (r, r.node))
for r in new_bad_routers:
utils.log("Introduced %s %s into the system." % (r, r.node))
return {"routers": routers}
def scenario_three(options):
"""
Most of the population are good peers.
Pre trusted-peers are maximally deflationary.
A mix of new peers are introduced every 1/5th of the iteration count.
Good peers have a 1 in 250 chance of receiving negative feedback from other
good peers.
"""
class EvilRouter(utils.Router):
def __init__(self):
utils.Router.__init__(self)
self.probably_malicious = False
def render_peers(self):
response = []
for peer in self.peers:
data = peer.jsonify()
low = 0.5 - (data['transactions'] * self.node.epsilon)
data['trust'] = random.choice([low, 0])
response.append(data)
return response
routers = []
good_routers = utils.generate_routers(options, minimum=4)
bad_routers = utils.generate_routers(options, minimum=1,
maximum=options.pre_trusted,
router_class=EvilRouter)
routers.extend(good_routers)
routers.extend(bad_routers)
[setattr(r, "routers", routers) for r in routers]
utils.introduce(routers)
[r.tbucket.append(_) for _ in r.peers if _.router.__class__.__name__ == \
"EvilRouter" for r in good_routers]
# Note that this is based on a definite transaction count but it's through a
# random transaction count with the possibility of some peers not transacting
# with some of their peers at all that the distributed trust algorithm can be
# used to detect malicious peers via the set of pre-trusted peers alone.
utils.log("Emulating %s iterations of transactions with all peers." % \
"{:,}".format(options.transactions))
for _ in range(options.transactions):
for router in routers:
for peer in router:
if not random.randint(0, 1): continue
if not router.probably_malicious and not peer.router.probably_malicious:
if random.randint(0, 250) == 1:
utils.log("Good peer %s is having a bad transaction with good peer %s." % \
(router.node, peer))
router.transact_with(peer, transaction_type=False)
continue
router.transact_with(peer)
# Calculate trust every 5 rounds here. Normally the periodicity would be
# a function of network size.
if _ > 1 and not (_+1) % 5:
for i, router in enumerate(routers):
utils.log("%i %s %s is sensing." % (i+1, router, router.node))
router.tbucket.calculate_trust()
# Introduce a mix of new peers every 1/5th of the iteration count
if _ > 5 and not _ % (options.transactions / 5):
new_good_routers = utils.generate_routers(options,
maximum=random.randint(1, 3))
new_bad_routers = utils.generate_routers(options,
maximum=random.randint(1, 3),
attrs={'probably_malicious': True})
routers.extend(new_good_routers)
routers.extend(new_bad_routers)
[setattr(r, "routers", routers) for r in routers]
utils.introduce(new_good_routers, random.sample(good_routers,
random.choice(range(2, 6))))
utils.introduce(new_bad_routers, random.sample(good_routers,
random.choice(range(2, 6))))
for r in new_good_routers:
utils.log("Introduced %s %s into the system." % (r, r.node))
for r in new_bad_routers:
utils.log("Introduced %s %s into the system." % (r, r.node))
return {"routers": routers}
def scenario_four(options):
"""
There are no malicious peers.
A mix of new peers are introduced every 1/5th of the iteration count.
Peers have a 1 in 250 chance of receiving negative feedback from eachother.
This is to mimic a real-life system with growth from a small number of
initial users.
"""
routers = utils.generate_routers(options, minimum=2)
utils.introduce(routers)
[_.tbucket.append(_.peers[:options.pre_trusted]) for _ in routers]
# Note that this is based on a definite transaction count but it's through a
# random transaction count with the possibility of some peers not transacting
# with some of their peers at all that the distributed trust algorithm can be
# used to detect malicious peers via the set of pre-trusted peers alone.
utils.log("Emulating %s iterations of transactions with all peers." % \
"{:,}".format(options.transactions))
for _ in range(options.transactions):
for router in routers:
for peer in router:
if not random.randint(0, 1): continue
if not router.probably_malicious and not peer.router.probably_malicious:
if peer.trust and random.randint(0, 250) == 1:
utils.log("Peer %s is having a bad transaction with %s." % \
(router.node, peer))
router.transact_with(peer, transaction_type=False)
continue
router.transact_with(peer)
# Calculate trust every 5 rounds here. Normally the periodicity would be
# a function of network size.
if _ > 1 and not (_+1) % 5:
for i, router in enumerate(routers):
utils.log("%i %s %s is sensing." % (i+1, router, router.node))
router.tbucket.calculate_trust()
# Introduce a mix of new peers every 1/5th of the iteration count
if _ > 5 and not _ % (options.transactions / 5):
new_routers = utils.generate_routers(options, maximum=random.randint(1, 3))
routers.extend(new_routers)
[setattr(r, "routers", routers) for r in routers]
utils.introduce(new_routers, random.sample(routers,
random.choice(range(2, len(routers)))))
for r in new_routers:
utils.log("Introduced %s %s into the system." % (r, r.node))
return {"routers": routers}
def threat_model_a(options):
"""
Independently malicious peers who're not initially aware of eachother.
"""
routers = utils.generate_routers(options, minimum=10)
[setattr(r, "probably_malicious", True) for r in routers]
good_peer = utils.Router()
[r.routers.append(good_peer) for r in routers]
good_peer.routers = routers
utils.introduce(good_peer, routers)
routers.insert(0, good_peer)
utils.log("Emulating %s iterations of transactions with all peers." % \
"{:,}".format(options.transactions))
for _ in range(options.transactions):
for router in routers:
for peer in router.peers:
if not random.randint(0, 1): continue
router.transact_with(peer)
# Calculate trust every 5 rounds here. Normally the periodicity would be
# a function of network size.
if _ > 1 and not (_+1) % 5:
for i, router in enumerate(routers):
utils.log("%i %s %s is sensing." % (i+1, router, router.node))
router.tbucket.calculate_trust()
return {"routers": routers}
def threat_model_b(options):
"""
Chain of Malicious Collectives who know eachother upfront and
deterministically give a high trust value to another malicious peer.
Resembles a malicious chain of mutual high local trust values.
"""
class EvilRouter(utils.Router):
def __init__(self):
utils.Router.__init__(self)
self.probably_malicious = True
def render_peers(self):
response = []
for peer in self.peers:
data = peer.jsonify()
if any(filter(lambda r: r.node == peer, self.collective)):
data['trust'] = peer.transactions * self.node.epsilon
response.append(data)
return response
routers = utils.generate_routers(options, minimum=7, router_class=EvilRouter)
good_peers = utils.generate_routers(options, minimum=3)
[setattr(r, "collective", routers) for r in routers]
all_routers = []
all_routers.extend(good_peers)
all_routers.extend(routers)
[setattr(r, "routers", all_routers) for r in routers]
[setattr(r, "routers", all_routers) for r in good_peers]
utils.introduce(routers)
utils.introduce(good_peers)
# Set good peers up with some pre-trusted friends
[_.tbucket.append(_.peers[:options.pre_trusted]) for _ in good_peers]
divisor = 1 if options.nodes == 1 else 2
utils.introduce(good_peers, random.sample(routers, len(routers) / divisor))
utils.log("Emulating %s iterations of transactions with all peers." % \
"{:,}".format(options.transactions))
for _ in range(options.transactions):
for router in all_routers:
for peer in router.peers:
if not random.randint(0, 1): continue
router.transact_with(peer)
# Calculate trust every 5 rounds here. Normally the periodicity would be
# a function of network size.
if _ > 1 and not (_+1) % 5:
for i, router in enumerate(routers):
utils.log("%i %s %s is sensing." % (i+1, router, router.node))
router.tbucket.calculate_trust()
return {"routers": all_routers}
def threat_model_c(options):
"""
Malicious Collectives with camouflage.
Malicious peers try to earn high local trust from good peers by providing
authentic services in f% of all cases.
"""
class EvilRouter(utils.Router):
def __init__(self):
utils.Router.__init__(self)
self.probably_malicious = True
self.counter = 0
self.f = 0.2 # out of 1.0.
self.responses = [0, 0] # [negative, positive]
@property
def malicious(self):
self.counter += 1
if self.counter >= 100: self.counter = 0
if self.counter <= max(int(100 * self.f), 1):
self.responses[0] += 1
return True
self.responses[1] += 1
return False
bad_peers = utils.generate_routers(options, minimum=10, router_class=EvilRouter)
good_peers = utils.generate_routers(options, minimum=5)
routers = []
routers.extend(bad_peers)
routers.extend(good_peers)
[setattr(r, "routers", routers) for r in bad_peers]
[setattr(r, "routers", routers) for r in good_peers]
utils.introduce(good_peers)
utils.introduce(bad_peers)
# Configure pre-trusted peers
[_.tbucket.append(_.peers[:options.pre_trusted]) for _ in good_peers]
utils.introduce(good_peers, random.sample(bad_peers, options.nodes))
transactions = max(options.transactions, 100)
utils.log("Emulating %s transactions with each peer." % \
"{:,}".format(transactions))
for _ in range(transactions):
for router in routers:
for peer in router.peers:
if not random.randint(0, 1): continue
router.transact_with(peer)
# Calculate trust every 5 rounds here. Normally the periodicity would be
# a function of network size.
if _ > 1 and not (_+1) % 5:
for i, router in enumerate(routers):
utils.log("%i %s %s is sensing." % (i+1, router, router.node))
router.tbucket.calculate_trust()
for router in bad_peers:
utils.log("%s %i negative transactions, %i positive." % \
(router, router.responses[0], router.responses[1]))
return {"routers": routers}
def threat_model_d(options):
"""
Malicious peers who are strategically organised into two groups.
One group of peers act as normal peers and try to increase their global
reputation by only providing good services and use the reputation they
gain to boost the trust values of another group of malicious peers.
"""
class AccompliceRouter(utils.Router):
def render_peers(self):
response = []
for peer in self.peers:
data = peer.jsonify()
if any(filter(lambda r: r.node == peer, self.collective)):
data['trust'] = 0.5 + (peer.transactions * \
self.node.epsilon)
response.append(data)
return response
class EvilRouter(utils.Router):
def __init__(self):
utils.Router.__init__(self)
self.probably_malicious = True
def render_peers(self):
response = []
for peer in self.peers:
data = peer.jsonify()
if any(filter(lambda r: r.node == peer, self.collective)):
data['trust'] = max(0.5 + (peer.transactions * \
self.node.epsilon), 0.5)
response.append(data)
return response
bad_peers = utils.generate_routers(options, minimum=10,
router_class=EvilRouter)
accomplice_peers = utils.generate_routers(options, minimum=10,
router_class=AccompliceRouter)
good_peers = utils.generate_routers(options, minimum=20)
routers = []
routers.extend(bad_peers)
routers.extend(accomplice_peers)
routers.extend(good_peers)
[setattr(r, "collective", bad_peers) for r in bad_peers]
[setattr(r, "collective", bad_peers) for r in accomplice_peers]
[setattr(r, "routers", routers) for r in routers]
utils.introduce(routers)
# Set good peers up with some pre-trusted friends
[_.tbucket.append(random.sample(_.peers, options.pre_trusted)) for _ in good_peers]
utils.log("Emulating %s iterations of transactions with all peers." % \
"{:,}".format(options.transactions))
for _ in range(options.transactions):
for router in good_peers:
for peer in router.peers:
if not random.randint(0, 1): continue
router.transact_with(peer)
# Accomplice routers work by doubling the trust trust rating of
# peers in the collective, which necessitates some good transactions
for router in routers:
for peer in router.peers:
if not random.randint(0, 1): continue
router.transact_with(peer)
# Calculate trust every 5 rounds here. Normally the periodicity would be
# a function of network size.
if _ > 1 and not (_+1) % 5:
for i, router in enumerate(routers):
utils.log("%i %s %s is sensing." % (i+1, router, router.node))
router.tbucket.calculate_trust()
return {"routers": routers}
def threat_model_e(options):
"""
Sybil attack. A hundred malicious peers who only provide bad services,
who're then replaced with a new similarly malicious identity once contacted
by good peers.
"""
bad_peers = utils.generate_routers(options, minimum=100)
good_peers = utils.generate_routers(options, minimum=100)
[setattr(r, "probably_malicious", True) for r in bad_peers]
routers = []
routers.extend(bad_peers)
routers.extend(good_peers)
[setattr(r, "routers", routers) for r in bad_peers]
[setattr(r, "routers", routers) for r in good_peers]
utils.introduce(bad_peers)
utils.introduce(good_peers)
# Set good peers up with some pre-trusted friends
[_.tbucket.append(_.peers[:options.pre_trusted]) for _ in good_peers]
divisor = 1 if options.nodes == 1 else 2
utils.introduce(good_peers, random.sample(bad_peers, len(routers) / divisor))
utils.log("Emulating %s iterations of transactions with all peers." % \
"{:,}".format(options.transactions))
for _ in range(options.transactions):
for router in good_peers:
for peer in router.peers:
if not random.randint(0, 1): continue
positive_transaction = router.transact_with(peer)
if positive_transaction == False:
router.dereference(peer, and_router=True)
new_router = utils.Router()
new_router.probably_malicious = True
utils.introduce(router, new_router)
# Accomplice routers work by doubling the trust trust rating of
# peers in the collective, which necessitates some good transactions
for router in routers:
for peer in router.peers:
if not random.randint(0, 1): continue
router.transact_with(peer)
# Calculate trust every 5 rounds here. Normally the periodicity would be
# a function of network size.
if _ > 1 and not (_+1) % 5:
for i, router in enumerate(routers):
utils.log("%i %s %s is sensing." % (i+1, router, router.node))
router.tbucket.calculate_trust()
return {"routers": routers}
def threat_model_f(options):
"""
Virus disseminating peers who send one inauthentic virus infected file every
100th request.
"""
class EvilRouter(utils.Router):
def __init__(self):
utils.Router.__init__(self)
self.probably_malicious = True
self.counter = 0
@property
def malicious(self):
self.counter += 1
return not self.counter % 100
bad_peers = utils.generate_routers(options, minimum=10,
router_class=EvilRouter)
good_peers = utils.generate_routers(options, minimum=5)
routers = []
routers.extend(bad_peers)
routers.extend(good_peers)
[setattr(r, "routers", routers) for r in bad_peers]
[setattr(r, "routers", routers) for r in good_peers]
utils.introduce(good_peers)
utils.introduce(bad_peers)
# It's at this point that you want to set up your pre-trusted peers
[_.tbucket.append(_.peers[:options.pre_trusted]) for _ in good_peers]
# and then some not so trustworthy peers
utils.introduce(good_peers, random.sample(bad_peers, options.nodes))
# Since our EvilRouter only does its thing once every hundred transactions
# we're going to define a minimum transaction count of 1,000 in this case.
transactions = max(options.transactions, 1000)
utils.log("Emulating %s transactions with each peer." % \
"{:,}".format(transactions))
for _ in range(transactions):
# Accomplice routers work by doubling the trust trust rating of
# peers in the collective, which requires some good transactions
for router in routers:
for peer in router.peers:
if not random.randint(0, 1): continue
router.transact_with(peer)
# Calculate trust every 5 rounds here. Normally the periodicity would be
# a function of network size.
if _ > 1 and not (_+1) % 5:
for i, router in enumerate(routers):
utils.log("%i %s %s is sensing." % (i+1, router, router.node))
router.tbucket.calculate_trust()
return {"routers": routers}
map = {
"one": scenario_one,
"two": scenario_two,
"three": scenario_three,
"four": scenario_four,
"A": threat_model_a,
"B": threat_model_b,
"C": threat_model_c,
"D": threat_model_d,
"E": threat_model_e,
"F": threat_model_f
}