Skip to content

Commit d7f8cf9

Browse files
committed
Updates to the code base to unify some stuff.
1 parent 8c21e19 commit d7f8cf9

12 files changed

+338
-7
lines changed

Diff for: build.py

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import logging
2+
from pynt import task
3+
import sys
4+
5+
sys.path.append('.')
6+
from simulation_settings import SimulationSettings, TopologySelection
7+
8+
# Setup logging.
9+
logging.basicConfig(level=logging.DEBUG)
10+
11+
12+
def runSimulation(settings, graph):
13+
"""Execute a single run of the simulation.
14+
15+
Arguments:
16+
settings {SimulationSettings} -- Settings for the simulation.
17+
graph {networkx.Graph} -- Graph of the miners.
18+
"""
19+
20+
for node in graph.nodes:
21+
pass
22+
#graph.nodes[node]['miner'] = settings.protocol.getMinerClass()()
23+
24+
return None
25+
26+
27+
@task()
28+
def run(file='sim.json'):
29+
simulation_settings = SimulationSettings(file)
30+
graph = simulation_settings.topology.generateMinerGraph()
31+
output = runSimulation(simulation_settings, graph)
32+
# TODO: Record this output
33+
34+
35+
@task()
36+
def runMonteCarlo(file='sim.json'):
37+
simulation_settings = SimulationSettings(file)
38+
if simulation_settings.topology_selection == TopologySelection.GENERATE_ONCE:
39+
single_graph = simulation_settings.topology.generateMinerGraph()
40+
41+
for i in range(0, simulation_settings.number_of_executions):
42+
# thread
43+
if simulation_settings.topology_selection == TopologySelection.GENERATE_EACH_TIME:
44+
graph = simulation_settings.topology.generateMinerGraph()
45+
else:
46+
graph = single_graph.copy()
47+
48+
# colate output
49+
output = runSimulation(simulation_settings, graph)
50+
51+
52+
@task()
53+
def analyze(input='./out/data'):
54+
# TODO
55+
pass
56+
57+
58+
# Sets the default task
59+
__DEFAULT__ = run

Diff for: distribution.py

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
from enum import Enum
2+
from numpy import random
3+
from pprint import pformat
4+
5+
6+
class DistributionType(Enum):
7+
"""Types of distributions to sample from.
8+
"""
9+
UNIFORM = 1,
10+
GAUSSIAN = 2,
11+
LAPLACIAN = 3
12+
13+
14+
class Distribution:
15+
def __init__(self, settings):
16+
"""Parses the settings in the value parameter.
17+
18+
Arguments:
19+
settings {dict} -- The setings for the distribution.
20+
"""
21+
self.distribution_type = DistributionType[settings['type']]
22+
if self.distribution_type == DistributionType.UNIFORM:
23+
self.low = settings['low']
24+
self.high = settings['high']
25+
elif self.distribution_type == DistributionType.GAUSSIAN or self.distribution_type == DistributionType.LAPLACIAN:
26+
self.average = settings['average']
27+
self.standard_deviation = settings['standardDeviation']
28+
else:
29+
raise NotImplementedError("Distribution type not yet implemented")
30+
31+
def sample(self, size=None):
32+
"""Samples from the distribution
33+
34+
Keyword Arguments:
35+
size {int or tuple of ints} -- Output shape. If the given shape is, e.g., (m, n, k), then m * n * k samples are drawn.
36+
If size is None (default), a single value is returned if loc and scale are both scalars. Otherwise,
37+
np.broadcast(loc, scale).size samples are drawn. (default: {None})
38+
39+
Returns:
40+
ndarray or scalar -- Drawn samples from the parameterized distribution.
41+
"""
42+
43+
if self.distribution_type == DistributionType.UNIFORM:
44+
return random.uniform(self.low, self.high, size)
45+
elif self.distribution_type == DistributionType.GAUSSIAN:
46+
return random.normal(self.average, self.standard_deviation, size)
47+
elif self.distribution_type == DistributionType.LAPLACIAN:
48+
return random.laplace(self.average, self.standard_deviation, size)
49+
else:
50+
raise NotImplementedError("Distribution type not yet implemented")
51+
52+
def __str__(self):
53+
return pformat(self.__dict__, indent=12)
54+
55+
def __repr__(self):
56+
return pformat(self.__dict__, indent=12)

Diff for: protocol_settings.py

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
from enum import Enum
2+
import json
3+
from pprint import pformat
4+
5+
import protocols
6+
7+
8+
class ProtocolType(Enum):
9+
"""The various protocol types supported by this framework.
10+
"""
11+
BITCOIN = 1
12+
IOTA = 2
13+
14+
15+
class ProtocolSettings:
16+
def __init__(self, value):
17+
"""Parses the settings in the value parameter.
18+
19+
Arguments:
20+
value {str|dict} -- If a string, it is a pointer to a JSON-encoded file containing the settings. If a dict, then it is the settings.
21+
"""
22+
if type(value) is str:
23+
# Treat the value as a file locator.
24+
with open(value, 'r') as settingsFile:
25+
data = json.load(settingsFile)
26+
else:
27+
data = value
28+
29+
self.protocol_type = ProtocolType[data['type']]
30+
if self.protocol_type == ProtocolType.BITCOIN:
31+
self.accept_depth = data['acceptDepth']
32+
self.transaction_generation_probability = data['transactionGenerationProbability']
33+
elif self.protocol_type == ProtocolType.IOTA:
34+
self.transaction_generation_probability = data['transactionGenerationProbability']
35+
else:
36+
raise NotImplementedError("Selected protocol type is not implemented")
37+
38+
def getMinerClass(self):
39+
"""Gets the appropriate class for creating miners based on the protocol type.
40+
"""
41+
if self.protocol_type == ProtocolType.BITCOIN:
42+
return protocols.bitcoin.Bitcoin
43+
elif self.protocol_type == ProtocolType.IOTA:
44+
return protocols.iota.Iota
45+
else:
46+
raise NotImplementedError("Selected protocol type is not implemented")
47+
48+
def __str__(self):
49+
return pformat(self.__dict__, indent=8)
50+
51+
def __repr__(self):
52+
return pformat(self.__dict__, indent=8)

Diff for: protocols/__init__.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import os
2+
for module in os.listdir(os.path.dirname(__file__)):
3+
if module == '__init__.py' or module[-3:] != '.py':
4+
continue
5+
__import__(module[:-3], locals(), globals())
6+
del module

Diff for: bitcoin.py renamed to protocols/bitcoin.py

File renamed without changes.

Diff for: iota.py renamed to protocols/iota.py

File renamed without changes.

Diff for: readme.md

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
## Installation
2+
This project uses virtualenv (.venv). Packages are stored in requirements.txt and can be installed using `pip install -r requirements.txt`

Diff for: requirements.txt

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
astroid==1.6.5
2+
autopep8==1.3.5
3+
backports.functools-lru-cache==1.5
4+
configparser==3.5.0
5+
cycler==0.10.0
6+
decorator==4.3.0
7+
enum34==1.1.6
8+
futures==3.2.0
9+
isort==4.3.4
10+
kiwisolver==1.0.1
11+
lazy-object-proxy==1.3.1
12+
matplotlib==2.2.2
13+
mccabe==0.6.1
14+
networkx==2.1
15+
numpy==1.14.5
16+
pycodestyle==2.4.0
17+
pylint==1.9.2
18+
pynt==0.8.1
19+
pyparsing==2.2.0
20+
python-dateutil==2.7.3
21+
pytz==2018.4
22+
singledispatch==3.4.0.3
23+
six==1.11.0
24+
subprocess32==3.5.2
25+
wrapt==1.10.11

Diff for: sim.json

+21-7
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,22 @@
1-
{
2-
"numMiners" : 200,
3-
"txGenProb" : 0.00019,
4-
"maxTx" : 55,
5-
"bitcoinAcceptDepth" : 6,
6-
"protocol" : "iota",
7-
"outFile" : "out.p"
1+
{
2+
"numberOfExecutions": 20,
3+
"topologySelection": "GENERATE_ONCE",
4+
"terminationCondition": "NUMBER_OF_GENERATED_TRANSACTIONS",
5+
"terminationValue": 50,
6+
7+
"topology": {
8+
"type": "GEOMETRIC_UNIFORM_DELAY",
9+
"radius": 0.125,
10+
"numberOfMiners": 200,
11+
"networkDelay": {
12+
"type": "GAUSSIAN",
13+
"average": 5,
14+
"standardDeviation": 1.3
15+
}
16+
},
17+
"protocol": {
18+
"type": "BITCOIN",
19+
"acceptDepth": 6,
20+
"transactionGenerationProbability": 0.0001667
21+
}
822
}

Diff for: sim.py

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ def runSim(g, o):
4343
o.txGenProb = -1
4444
anyHaveMsg = bool([True for i in g.nodes if g.nodes[i]['miner'].queue])
4545
if o.txGenProb <= 0 and not anyHaveMsg: # run until no miners have messages
46+
4647
break
4748
o.tick += 1
4849

Diff for: simulation_settings.py

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
from enum import Enum
2+
import json
3+
from pprint import pformat
4+
5+
from protocol_settings import ProtocolSettings
6+
from topology_settings import TopologySettings
7+
8+
9+
class TopologySelection(Enum):
10+
"""Enumeration of the possible topology selection.
11+
"""
12+
GENERATE_ONCE = 1
13+
GENERATE_EACH_TIME = 2
14+
15+
16+
class TerminationCondition(Enum):
17+
"""Enumeration tracking when an individual simulation terminates.
18+
"""
19+
NUMBER_OF_GENERATED_TRANSACTIONS = 1,
20+
NUMBER_OF_TIME_TICKS = 2
21+
22+
23+
class SimulationSettings:
24+
"""Handles loading simulation settings information from a file.
25+
"""
26+
27+
def __init__(self, fname):
28+
with open(fname, 'r') as settingsFile:
29+
data = json.load(settingsFile)
30+
31+
# Load settings.
32+
self.number_of_executions = data['numberOfExecutions']
33+
self.topology_selection = TopologySelection[data['topologySelection']]
34+
self.termination_condition = TerminationCondition[data['terminationCondition']]
35+
self.termination_value = data['terminationValue']
36+
37+
# Load the other settings objects
38+
self.topology = TopologySettings(data['topology'])
39+
self.protocol = ProtocolSettings(data['protocol'])
40+
41+
def __str__(self):
42+
return pformat(self.__dict__, indent=4)
43+
44+
def __repr__(self):
45+
return pformat(self.__dict__, indent=4)

Diff for: topology_settings.py

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
from enum import Enum
2+
import json
3+
import networkx
4+
from pprint import pformat
5+
6+
from distribution import Distribution
7+
8+
class TopologyType(Enum):
9+
"""Types of miner topologies.
10+
"""
11+
STATIC = 1, # Load the topology from a file.
12+
GEOMETRIC_UNIFORM_DELAY = 2,
13+
LOBSTER_UNIFORM_DELAY = 3 # TODO: Toy example that should eventually be removed.
14+
# TODO: Add appropraite types
15+
16+
17+
class TopologySettings:
18+
def __init__(self, value):
19+
"""Parses the settings in the value parameter.
20+
21+
Arguments:
22+
value {str|dict} -- If a string, it is a pointer to a JSON-encoded file containing the settings. If a dict, then it is the settings.
23+
"""
24+
if type(value) is str:
25+
# Treat the value as a file locator.
26+
with open(value, 'r') as settingsFile:
27+
data = json.load(settingsFile)
28+
else:
29+
data = value
30+
31+
self.topology_type = TopologyType[data['type']]
32+
if self.topology_type == TopologyType.STATIC:
33+
raise NotImplementedError("Static topologies are not yet implemented")
34+
else:
35+
self.number_of_miners = data['numberOfMiners']
36+
37+
if self.topology_type == TopologyType.GEOMETRIC_UNIFORM_DELAY or self.topology_type == TopologyType.LOBSTER_UNIFORM_DELAY:
38+
# Graphs with uniform delays for message transmission.
39+
self.network_delay = Distribution(data['networkDelay'])
40+
41+
if self.topology_type == TopologyType.GEOMETRIC_UNIFORM_DELAY:
42+
self.radius = data['radius']
43+
elif self.topology_type == TopologyType.LOBSTER_UNIFORM_DELAY:
44+
self.p1 = data['p1']
45+
self.p2 = data['p2']
46+
else:
47+
raise NotImplementedError("Selected topology type is not implemented.")
48+
49+
def generateMinerGraph(self):
50+
"""Generates a miner graph based on the settings in this object.
51+
"""
52+
if self.topology_type == TopologyType.STATIC:
53+
raise NotImplementedError("Static topologies are not yet implemented")
54+
else:
55+
if self.topology_type == TopologyType.GEOMETRIC_UNIFORM_DELAY or self.topology_type == TopologyType.LOBSTER_UNIFORM_DELAY:
56+
# Graphs with uniform delays for message transmission.
57+
if self.topology_type == TopologyType.GEOMETRIC_UNIFORM_DELAY:
58+
graph = networkx.random_geometric_graph(self.number_of_miners, self.radius)
59+
elif self.topology_type == TopologyType.LOBSTER_UNIFORM_DELAY:
60+
graph = networkx.random_lobster(self.number_of_miners, self.p1, self.p2)
61+
62+
for edge in graph.edges:
63+
graph.edges[edge]['network_delay'] = self.network_delay
64+
else:
65+
raise NotImplementedError("Selected topology type is not implemented.")
66+
67+
def __str__(self):
68+
return pformat(self.__dict__, indent=8)
69+
70+
def __repr__(self):
71+
return pformat(self.__dict__, indent=8)

0 commit comments

Comments
 (0)