|
@@ -0,0 +1,98 @@
|
|
|
+#!/usr/bin/env python3
|
|
|
+
|
|
|
+import random # For simulation, not cryptography!
|
|
|
+import math
|
|
|
+import sys
|
|
|
+import logging
|
|
|
+
|
|
|
+import network
|
|
|
+import dirauth
|
|
|
+import relay
|
|
|
+import client
|
|
|
+
|
|
|
+class Simulator:
|
|
|
+ def __init__(self, relaytarget, clienttarget):
|
|
|
+ self.relaytarget = relaytarget
|
|
|
+ self.clienttarget = clienttarget
|
|
|
+
|
|
|
+ # Some (for now) hard-coded parameters
|
|
|
+
|
|
|
+ # The number of directory authorities
|
|
|
+ numdirauths = 9
|
|
|
+
|
|
|
+ # The fraction of relays that are fallback relays
|
|
|
+ fracfallbackrelays = 0.05
|
|
|
+
|
|
|
+ # Start some dirauths
|
|
|
+ self.dirauthaddrs = []
|
|
|
+ self.dirauths = []
|
|
|
+ for i in range(numdirauths):
|
|
|
+ dira = dirauth.DirAuth(i, numdirauths)
|
|
|
+ self.dirauths.append(dira)
|
|
|
+ self.dirauthaddrs.append(dira.netaddr)
|
|
|
+
|
|
|
+ # Start some relays
|
|
|
+ self.relays = []
|
|
|
+ for i in range(self.relaytarget):
|
|
|
+ # Relay bandwidths (at least the ones fast enough to get used)
|
|
|
+ # in the live Tor network (as of Dec 2019) are well approximated
|
|
|
+ # by (200000-(200000-25000)/3*log10(x)) where x is a
|
|
|
+ # uniform integer in [1,2500]
|
|
|
+ x = random.randint(1,2500)
|
|
|
+ bw = int(200000-(200000-25000)/3*math.log10(x))
|
|
|
+ self.relays.append(relay.Relay(self.dirauthaddrs, bw, 0))
|
|
|
+
|
|
|
+ # The fallback relays are a hardcoded list of a small fraction
|
|
|
+ # of the relays, used by clients for bootstrapping
|
|
|
+ numfallbackrelays = int(self.relaytarget * fracfallbackrelays) + 1
|
|
|
+ fallbackrelays = random.sample(self.relays, numfallbackrelays)
|
|
|
+ for r in fallbackrelays:
|
|
|
+ r.set_is_fallbackrelay()
|
|
|
+ network.thenetwork.setfallbackrelays(fallbackrelays)
|
|
|
+
|
|
|
+ # Tick the epoch to build the first consensus
|
|
|
+ network.thenetwork.nextepoch()
|
|
|
+
|
|
|
+ # Start some clients
|
|
|
+ self.clients = []
|
|
|
+ for i in range(clienttarget):
|
|
|
+ self.clients.append(client.Client(self.dirauthaddrs))
|
|
|
+
|
|
|
+ # Tick the epoch to bootstrap the clients
|
|
|
+ network.thenetwork.nextepoch()
|
|
|
+
|
|
|
+ # Throw away all the performance statistics to this point
|
|
|
+ for d in self.dirauths: d.perfstats.reset()
|
|
|
+ for r in self.relays: r.perfstats.reset()
|
|
|
+ for c in self.clients: c.perfstats.reset()
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == '__main__':
|
|
|
+ # Args: womode snipauthmode networkscale numepochs randseed
|
|
|
+ if len(sys.argv) != 6:
|
|
|
+ sys.stderr.write("Usage: womode snipauthmode networkscale numepochs randseed\n")
|
|
|
+ sys.exit(1)
|
|
|
+
|
|
|
+ womode = network.WOMode[sys.argv[1].upper()]
|
|
|
+ snipauthmode = network.SNIPAuthMode[sys.argv[2].upper()]
|
|
|
+ networkscale = float(sys.argv[3])
|
|
|
+ numepochs = int(sys.argv[4])
|
|
|
+ randseed = int(sys.argv[5])
|
|
|
+
|
|
|
+ # Seed the PRNG. On Ubuntu 18.04, this in fact makes future calls
|
|
|
+ # to (non-cryptographic) random numbers deterministic. On Ubuntu
|
|
|
+ # 16.04, it does not.
|
|
|
+ random.seed(randseed)
|
|
|
+
|
|
|
+ # Uncomment to see all the debug messages
|
|
|
+ # logging.basicConfig(level=logging.DEBUG)
|
|
|
+
|
|
|
+ # Set the Walking Onions style to use
|
|
|
+ network.thenetwork.set_wo_style(womode, snipauthmode)
|
|
|
+
|
|
|
+ # The steady-state numbers of relays and clients
|
|
|
+ relaytarget = math.ceil(6500 * networkscale)
|
|
|
+ clienttarget = math.ceil(2500000 * networkscale)
|
|
|
+
|
|
|
+ # Create the simulation
|
|
|
+ simulator = Simulator(relaytarget, clienttarget)
|