aucoin/tools/network_test.py
Casper V. Kristensen b7053ad014
Publish
2018-07-15 23:30:55 +02:00

183 lines
6.7 KiB
Python

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()