aucoin/tools/network_test.py

183 lines
6.7 KiB
Python
Raw Normal View History

2018-07-15 23:30:55 +02:00
import json
import os
import statistics
import subprocess
import time
from collections import defaultdict
from http.server import BaseHTTPRequestHandler, HTTPServer
from threading import Thread, Lock
def make_handler(timestamps, connectivity, num_nodes, callback):
class TimestampHandler(BaseHTTPRequestHandler, object):
def __init__(self, request, client_address, server):
self.timestamps = timestamps
super().__init__(request, client_address, server)
def _send_response(self):
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
def do_GET(self):
print("Get request", str(self.path))
self._send_response()
def do_POST(self):
content_length = int(self.headers["Content-Length"])
transaction_hash = self.rfile.read(content_length)
t = time.time()
self.timestamps.append(t)
print(f"POST {transaction_hash.hex()} at {t}")
print(f"Number of notifications: {len(self.timestamps)}/{num_nodes}")
if len(self.timestamps) == num_nodes:
print("Number of notifications is equal to the number of nodes; closing server and calling callback")
self.server.server_close()
callback(self.timestamps)
self._send_response()
def do_PUT(self):
content_length = int(self.headers["Content-Length"])
peer_info_bytes = self.rfile.read(content_length)
peer_info = json.loads(peer_info_bytes.decode(encoding="ascii"))
connectivity[peer_info["node"]] = peer_info["peerlist"]
self._send_response()
return TimestampHandler
def print_stats(timestamps, connectivity):
print("-----------------------------------------")
print(f"Received notification from {len(timestamps)} nodes")
first = timestamps[0]
last = timestamps[-1]
print("First timestamp at", first)
print("Last timestamp at", last)
print("Difference:", (last - first) * 1000, "ms")
print()
print("Median time it took:", statistics.median([ts - first for ts in timestamps[1:]]) * 1000, "ms")
print("Average time it took:", statistics.mean([ts - first for ts in timestamps[1:]]) * 1000, "ms")
print("Connectivity:")
print(json.dumps(connectivity))
def graph(timestamps):
pass
def start_node(miners, max_peers, interface, seed, delay):
return subprocess.Popen(["python3.6", "-m", "aucoin",
"--miners", str(miners),
"--max-peers", str(max_peers),
"--interface", interface,
"--seed", seed, # 192.0.2.0 doesn't exist according to RFC
"--notify-url", "http://localhost:8080",
"--delay", str(delay),
"--no-catch-up",
"-v",
"--clean"],
env=dict(os.environ, HOME=f"~/.aucoin-test/{interface}"))
def run_experiment(num_nodes):
results = defaultdict(dict) # results[delay][max_peers] = timestamps
continue_lock = Lock()
for delay in [1]:
for max_peers in (4,):
continue_lock.acquire()
print("==================================================================")
print(f"Running experiment with {num_nodes} nodes at {delay}ms delay and max_peers={max_peers}")
print("Please start seed node manually using:")
print("VVV")
print(f"python3.6 -m aucoin --miners=0 --max-peers={max_peers} --interface=127.0.0.1 --seed=192.0.2.0 --notify-url http://localhost:8080 --delay={delay} --no-catch-up -v")
print("^^^")
# input("Press any key when done to continue")
#print("Waiting 1s for the seed node to fail connecting to non-existent seed..")
#time.sleep(1)
print("Continuing..")
print("Starting other nodes..")
nodes = []
for n in range(2, num_nodes+2): # start at 127.0.0.2
node = start_node(miners=0, max_peers=max_peers, interface=f"127.0.0.{n}", seed="127.0.0.1", delay=delay)
nodes.append(node)
print("Sleeping 10s to allow establishing connections..")
time.sleep(5)
print("Continuing..")
def all_nodes_received_transaction_callback(timestamps):
print("Killing all nodes..")
for node in nodes:
node.kill()
time.sleep(1)
print("VVV")
print("Please Ctrl+C the seed node")
print("^^^")
input("Press any key when done..")
print("Saving and printing stats..")
results[delay][max_peers] = timestamps
print_stats(timestamps, connectivity)
print("=== RESULTS ===")
print(json.dumps(results))
print("===============")
continue_lock.release()
print("Starting web server in other thread")
timestamps = []
connectivity = {}
http_server = HTTPServer(("0.0.0.0", 8080), make_handler(timestamps, connectivity, num_nodes + 1, all_nodes_received_transaction_callback)) # num_nodes + 1 accounts for seed node
http_thread = Thread(target=http_server.serve_forever)
http_thread.start()
print("Server started on port 8080")
print("Waiting 2s for the webserver to start up..")
time.sleep(2)
print("Continuing..")
print("Please send a transaction from the seed node, e.g.:")
print("VVV")
print("send aabb 10")
print("^^^")
print("Waiting for all peers to receive transaction..")
for node in nodes:
node.wait()
# The flow continues in all_nodes_received_transaction_callback()
def plot():
times = [962, 843, 1329, 1142, 1078, 1223, 1406, 1492]
worst = [1879, 1979, 2039, 2149, 2185, 2655, 2304, 3190]
nodes = [10, 20, 30, 50, 75, 100, 125, 150]
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
plt.plot(nodes, times, 'r-x', nodes, worst, 'b-x')
plt.xlabel('number of peers')
plt.ylabel('time in milliseconds from transaction was sent')
red = mpatches.Patch(color='red', label='median')
green = mpatches.Patch(color='blue', label='last record')
plt.legend(handles=[green, red])
plt.show()
if __name__ == '__main__':
run_experiment(num_nodes=70)
# plot()