-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathbuild.py
167 lines (132 loc) · 5.59 KB
/
build.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
import concurrent.futures
import copy
import logging
from pynt import task
import sys
import time
sys.path.append('.')
import analysis
import plot
from simulation_settings import SimulationSettings, TopologySelection
from simulation import Simulation
import transaction
# Setup logging.
logging.basicConfig(level=logging.DEBUG)
def runOnce(settings, graph, thread_id=0):
"""Execute a single run of the simulation.
Arguments:
settings {SimulationSettings} -- Settings for the simulation.
graph {networkx.Graph} -- Graph of the miners.
Keyword Arguments:
thread_id {int} -- The thread number of this run of the simulation. (default: {0})
Returns:
Simulation -- Completed simulation object.
"""
simulation = Simulation(settings, graph, thread_id)
simulation.runSimulation()
return simulation
def runThreaded(settings, graph, thread_id, out_dir):
"""Runs the simulation once, directing output to a thread-unique file. Intended to be used as the thread's target function.
Arguments:
settings {SimulationSettings} -- Stores all settings for the run.
graph {networkx.Graph} -- Graph object to run the simulation on; should have edge delays.
thread_id {int} -- The thread number of this run of the simulation.
out_dir {string} -- The directory where output should be written.
"""
assert out_dir[-1] == '/'
logging.debug('Started thread %d' % thread_id)
out_file = "%sdata%d.json" % (out_dir, thread_id)
simulation = runOnce(settings, graph, thread_id)
simulation.writeData(out_file)
logging.debug('Finished thread %d' % thread_id)
@task()
def runMonteCarlo(file='sim.json', out_dir='./out/'):
"""Runs a number of Monte Carlo simulations according to settings loaded from file.
Keyword Arguments:
file {str} -- File name to load settings from. (default: {'sim.json'})
out_dir {str} -- Directory name to write output to. (default: {'./out/'})
"""
settings = SimulationSettings(file)
if settings.topology_selection == TopologySelection.GENERATE_ONCE:
single_graph = settings.topology.generateMinerGraph()
start = time.time()
with concurrent.futures.ThreadPoolExecutor(max_workers=settings.thread_workers) as executor:
for thread_id in range(0, settings.number_of_executions):
if settings.topology_selection == TopologySelection.GENERATE_EACH_TIME:
graph = settings.topology.generateMinerGraph()
else:
graph = copy.deepcopy(single_graph) # graph.copy() wasn't deepcopying miner objects attached to nodes.
thread_settings = copy.deepcopy(settings)
executor.submit(runThreaded, thread_settings, graph, thread_id, out_dir)
logging.info("Time: %f" % (time.time() - start))
@task()
def run(file='sim.json', out='./out/data.json'):
"""Executes one simulation.
Keyword Arguments:
file {str} -- File name to load settings from. (default: {'sim.json'})
out {str} -- File name to store output to. (default: {'./out/data.json'})
"""
settings = SimulationSettings(file)
graph = settings.topology.generateMinerGraph()
logging.info("Starting simulation")
start = time.time()
simulation = runOnce(settings, graph)
simulation.writeData(out)
logging.info("Simulation time: %f" % (time.time() - start))
# analyze()
@task()
def runWithDebug(file='sim.json', out='./out/data.json'):
"""Executes one simulation and analyzes resulting data, including some debug functions
Keyword Arguments:
file {str} -- File name to load settings from. (default: {'sim.json'})
out {str} -- File name to store output to. (default: {'./out/data.json'})
"""
settings = SimulationSettings(file)
graph = settings.topology.generateMinerGraph()
logging.info("Starting simulation")
start = time.time()
simulation = runOnce(settings, graph)
# DEBUG
g = simulation.graph
allMinerIds = set()
allMiners = []
for n in g.nodes:
m = g.nodes[n]['miner']
allMinerIds.add(m.id)
allMiners.append(m)
unconsensed_tx = [] # Consensed by 1 or more but not all miners.
miners_to_compare = set([0]) # Set of miners to display if some tx are unconsensed (always includes 0 for reference).
for t in simulation.all_tx:
states = {}
for e in t.history:
states[e.miner_id] = e.state
s = set([i for i in states if states[i] == transaction.State.CONSENSUS]) # Have to do it like this to capture FINAL state, not just "was this ever in consensus".
if s and allMinerIds - s:
miners_to_compare |= set(list(s)[:1])
unconsensed_tx.append(t)
if unconsensed_tx:
print "Consensus has still not been reached for some tx:", [t.id for t in unconsensed_tx]
print miners_to_compare
plot.plotAllDags([g.nodes[i]['miner'] for i in miners_to_compare])
else:
print "All tx consensed!"
plot.plotDag(simulation.graph.nodes[0]['miner'])
# END DEBUG
simulation.writeData(out)
logging.info("Simulation time: %f" % (time.time() - start))
analyze()
@task()
def analyze(data_dir='./out/'):
"""Analyzes the data stored in the given directory.
Keyword Arguments:
data_dir {str} -- Path to the directory containing data files. (default: {'./out/'})
"""
data = analysis.loadData(data_dir)
# for run in data:
# analysis.showMultiCDF(run[1])
analysis.showTotalCDF(data)
analysis.reportDisconsensed(data)
# Sets the default task.
__DEFAULT__ = run
if __name__ == "__main__":
__DEFAULT__()