client.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  1. #!/usr/bin/env python3
  2. import random # For simulation, not cryptography!
  3. import math
  4. import sys
  5. import logging
  6. import network
  7. import dirauth
  8. import relay
  9. import nacl.hash
  10. class VanillaCreatedExtendedHandler:
  11. """A handler for VanillaCreatedCircuitCell and
  12. VanillaExtendedCircuitCell cells."""
  13. def __init__(self, channelmgr, ntor, expecteddesc):
  14. self.channelmgr = channelmgr
  15. self.ntor = ntor
  16. self.expecteddesc = expecteddesc
  17. self.onionkey = expecteddesc.descdict['onionkey']
  18. self.idkey = expecteddesc.descdict['idkey']
  19. def received_cell(self, circhandler, cell):
  20. secret = self.ntor.verify(cell.ntor_reply, self.onionkey, self.idkey)
  21. enckey = nacl.hash.sha256(secret + b'upstream')
  22. deckey = nacl.hash.sha256(secret + b'downstream')
  23. circhandler.add_crypt_layer(enckey, deckey)
  24. if len(circhandler.circuit_descs) == 0:
  25. # This was a VanillaCreatedCircuitCell
  26. circhandler.replace_celltype_handler(
  27. relay.VanillaCreatedCircuitCell, None)
  28. else:
  29. # This was a VanillaExtendedCircuitCell
  30. circhandler.replace_celltype_handler(
  31. relay.VanillaExtendedCircuitCell, None)
  32. circhandler.circuit_descs.append(self.expecteddesc)
  33. # Are we done building the circuit?
  34. if len(circhandler.circuit_descs) == 3:
  35. # Yes!
  36. return
  37. nexthop = None
  38. while nexthop is None:
  39. nexthop = self.channelmgr.relaypicker.pick_weighted_relay()
  40. if nexthop.descdict['addr'] in \
  41. [ desc.descdict['addr'] \
  42. for desc in circhandler.circuit_descs ]:
  43. nexthop = None
  44. # Construct the VanillaExtendCircuitCell
  45. ntor = relay.NTor(self.channelmgr.perfstats)
  46. ntor_request = ntor.request()
  47. circextendmsg = relay.VanillaExtendCircuitCell(
  48. nexthop.descdict['addr'], ntor_request)
  49. # Set up the reply handler
  50. circhandler.replace_celltype_handler(
  51. relay.VanillaExtendedCircuitCell,
  52. VanillaCreatedExtendedHandler(self.channelmgr, ntor, nexthop))
  53. # Send the cell
  54. circhandler.send_cell(circextendmsg)
  55. class TelescopingCreatedHandler:
  56. """A handler for TelescopingCreatedCircuitCell cells; this will only always
  57. communicate with the client's guard."""
  58. def __init__(self, channelmgr, ntor):
  59. self.channelmgr = channelmgr
  60. self.ntor = ntor
  61. self.onionkey = self.channelmgr.guard.snipdict['onionkey']
  62. self.idkey = self.channelmgr.guard.snipdict['idkey']
  63. def received_cell(self, circhandler, cell):
  64. logging.debug("Received cell in TelescopingCreatedHandler")
  65. secret = self.ntor.verify(cell.ntor_reply, self.onionkey, self.idkey)
  66. enckey = nacl.hash.sha256(secret + b'upstream')
  67. deckey = nacl.hash.sha256(secret + b'downstream')
  68. circhandler.add_crypt_layer(enckey, deckey)
  69. circhandler.replace_celltype_handler(relay.TelescopingCreatedCircuitCell, None)
  70. nexthopidx = None
  71. while nexthopidx is None:
  72. nexthopidx = self.channelmgr.relaypicker.pick_weighted_relay_index()
  73. #print("WARNING: Unimplemented! Need to check if this idx is in the list of circhandlers idxs")
  74. # TODO verify we don't need to do the above
  75. # Construct the VanillaExtendCircuitCell
  76. ntor = relay.NTor(self.channelmgr.perfstats)
  77. ntor_request = ntor.request()
  78. circextendmsg = relay.TelescopingExtendCircuitCell(
  79. nexthopidx, ntor_request)
  80. # Set up the reply handler
  81. circhandler.replace_celltype_handler(
  82. relay.TelescopingExtendedCircuitCell,
  83. TelescopingExtendedHandler(self.channelmgr, ntor))
  84. # Send the cell
  85. circhandler.send_cell(circextendmsg)
  86. class TelescopingExtendedHandler:
  87. """A handler for TelescopingExtendedCircuitCell cells."""
  88. def __init__(self, channelmgr, ntor):
  89. self.channelmgr = channelmgr
  90. self.ntor = ntor
  91. def received_cell(self, circhandler, cell):
  92. logging.debug("Received cell in TelescopingExtendedHandler")
  93. # Validate the SNIP
  94. dirauth.SNIP.verify(cell.snip, self.channelmgr.consensus,
  95. network.thenetwork.dirauthkeys()[0],
  96. self.channelmgr.perfstats)
  97. onionkey = cell.snip.snipdict['onionkey']
  98. idkey = cell.snip.snipdict['idkey']
  99. secret = self.ntor.verify(cell.ntor_reply, onionkey, idkey)
  100. enckey = nacl.hash.sha256(secret + b'upstream')
  101. deckey = nacl.hash.sha256(secret + b'downstream')
  102. circhandler.add_crypt_layer(enckey, deckey)
  103. circhandler.replace_celltype_handler(
  104. relay.TelescopingExtendedCircuitCell, None)
  105. circhandler.circuit_descs.append(cell.snip)
  106. # Are we done building the circuit?
  107. logging.warning("we may need another circhandler structure for snips")
  108. if len(circhandler.circuit_descs) == 3:
  109. logging.debug("Circuit is long enough; exiting.")
  110. # Yes!
  111. return
  112. nexthopidx = None
  113. while nexthopidx is None:
  114. nexthopidx = self.channelmgr.relaypicker.pick_weighted_relay_index()
  115. logging.warning("Unimplemented! Need to check if this idx is in the list of circhandlers idxs")
  116. # Construct the VanillaExtendCircuitCell
  117. ntor = relay.NTor(self.channelmgr.perfstats)
  118. ntor_request = ntor.request()
  119. circextendmsg = relay.TelescopingExtendCircuitCell(
  120. nexthopidx, ntor_request)
  121. # Set up the reply handler
  122. circhandler.replace_celltype_handler(
  123. relay.TelescopingExtendedCircuitCell,
  124. TelescopingExtendedHandler(self.channelmgr, ntor))
  125. # Send the cell
  126. circhandler.send_cell(circextendmsg)
  127. class ClientChannelManager(relay.ChannelManager):
  128. """The subclass of ChannelManager for clients."""
  129. def __init__(self, myaddr, dirauthaddrs, perfstats):
  130. super().__init__(myaddr, dirauthaddrs, perfstats)
  131. self.guardaddr = None
  132. self.guard = None
  133. def get_consensus_from_fallbackrelay(self):
  134. """Download a fresh consensus from a random fallbackrelay."""
  135. fb = random.choice(network.thenetwork.getfallbackrelays())
  136. if network.thenetwork.womode == network.WOMode.VANILLA:
  137. if self.consensus is not None and \
  138. len(self.consensus.consdict['relays']) > 0:
  139. self.send_msg(relay.RelayGetConsensusDiffMsg(), fb.netaddr)
  140. else:
  141. self.send_msg(relay.RelayGetConsensusMsg(), fb.netaddr)
  142. else:
  143. self.send_msg(relay.RelayGetConsensusMsg(), fb.netaddr)
  144. def ensure_guard_vanilla(self):
  145. """Ensure that we have a channel to a guard (Vanilla Onion
  146. Routing version)."""
  147. while True:
  148. if self.guardaddr is None:
  149. # Pick a guard from the consensus
  150. self.guard = self.relaypicker.pick_weighted_relay()
  151. self.guardaddr = self.guard.descdict['addr']
  152. self.test_guard_connection()
  153. if self.guardaddr is not None:
  154. break
  155. logging.debug('chose guard=%s', self.guardaddr)
  156. def test_guard_connection(self):
  157. # Connect to the guard
  158. try:
  159. self.get_channel_to(self.guardaddr)
  160. except network.NetNoServer:
  161. # Our guard is gone
  162. self.guardaddr = None
  163. self.guard = None
  164. def ensure_guard_walking_onions(self):
  165. """Ensure we have a channel to a guard (Walking Onions version).
  166. For the first implementation, we assume an out-of-band mechanism
  167. that just simply hands us a guard; we don't count the number of
  168. operations or bandwidth as this operation in practice occurs
  169. infrequently."""
  170. while True:
  171. if self.guardaddr is None:
  172. #randomly-sample a guard
  173. logging.warning("Unimplemented! guard should be selected from any relays.")
  174. self.guard = self.relaypicker.pick_weighted_relay()
  175. # here, we have a SNIP instead of a relay descriptor
  176. self.guardaddr = self.guard.snipdict['addr']
  177. self.test_guard_connection()
  178. if self.guardaddr is not None:
  179. break
  180. logging.debug('chose guard=%s', self.guardaddr)
  181. def ensure_guard(self):
  182. """Ensure that we have a channel to a guard."""
  183. if network.thenetwork.womode == network.WOMode.VANILLA:
  184. self.ensure_guard_vanilla()
  185. return
  186. # At this point, we are either in Telescoping or Single-Pass mode
  187. self.ensure_guard_walking_onions()
  188. def new_circuit_vanilla(self):
  189. """Create a new circuit from this client. (Vanilla Onion Routing
  190. version)"""
  191. # Get our channel to the guard
  192. guardchannel = self.get_channel_to(self.guardaddr)
  193. # Allocate a new circuit id on it
  194. circid, circhandler = guardchannel.new_circuit()
  195. # Construct the VanillaCreateCircuitMsg
  196. ntor = relay.NTor(self.perfstats)
  197. ntor_request = ntor.request()
  198. circcreatemsg = relay.VanillaCreateCircuitMsg(circid, ntor_request)
  199. # Set up the reply handler
  200. circhandler.replace_celltype_handler(
  201. relay.VanillaCreatedCircuitCell,
  202. VanillaCreatedExtendedHandler(self, ntor, self.guard))
  203. # Send the message
  204. guardchannel.send_msg(circcreatemsg)
  205. return circhandler
  206. def new_circuit_telescoping(self):
  207. """Create a new circuit from this client. (Telescoping Walking Onions
  208. version). If an error occurs and the circuit is deleted from the guard
  209. channel, return None, otherwise, return the circuit handler."""
  210. # Get our channel to the guard
  211. guardchannel = self.get_channel_to(self.guardaddr)
  212. # Allocate a new circuit id on it
  213. circid, circhandler = guardchannel.new_circuit()
  214. # Construct the TelescopingCreateCircuitMsg
  215. ntor = relay.NTor(self.perfstats)
  216. ntor_request = ntor.request()
  217. circcreatemsg = relay.TelescopingCreateCircuitMsg(circid, ntor_request)
  218. # Set up the reply handler
  219. circhandler.replace_celltype_handler(
  220. relay.TelescopingCreatedCircuitCell,
  221. TelescopingCreatedHandler(self, ntor))
  222. # Send the message
  223. guardchannel.send_msg(circcreatemsg)
  224. # Check to make sure the circuit is open before sending it- if there
  225. # was an error when establishing it, the circuit could already be
  226. # closed.
  227. if not guardchannel.is_circuit_open(circid):
  228. logging.debug("Circuit was already closed, not sending bytes. circid: " + str(circid))
  229. return None
  230. return circhandler
  231. def new_circuit_singlepass(self):
  232. """Create a new circuit from this client. (Single-Pass Walking Onions
  233. version). If an error occurs and the circuit is deleted from the guard
  234. channel, return None, otherwise, return the circuit handler."""
  235. # Get our channel to the guard
  236. guardchannel = self.get_channel_to(self.guardaddr)
  237. # Allocate a new circuit id on it
  238. circid, circhandler = guardchannel.new_circuit()
  239. # first, create the path-selection key used for Sphinx
  240. client_key = nacl.public.PrivateKey.generate()
  241. # Construct the SinglePassCreateCircuitMsg
  242. ntor = relay.NTor(self.perfstats)
  243. ntor_request = ntor.request()
  244. circcreatemsg = relay.SinglePassCreateCircuitMsg(circid, ntor_request,
  245. client_key.public_key)
  246. # Send the message
  247. guardchannel.send_msg(circcreatemsg)
  248. # Check to make sure the circuit is open before sending it- if there
  249. # was an error when establishing it, the circuit could already be
  250. # closed.
  251. if not guardchannel.is_circuit_open(circid):
  252. logging.debug("Circuit was already closed, not sending bytes. circid: " + str(circid))
  253. return None
  254. #TODO validate the Sphinx keys on reply
  255. logging.warning("Unimplemented! The client should validate the NTOR handshake using Sphinx operations here.")
  256. return circhandler
  257. def new_circuit(self):
  258. """Create a new circuit from this client."""
  259. circhandler = None
  260. # If an error occured, circhandler will still be None, so we should
  261. # try again.
  262. while circhandler is None:
  263. if network.thenetwork.womode == network.WOMode.VANILLA:
  264. circhandler = self.new_circuit_vanilla()
  265. elif network.thenetwork.womode == network.WOMode.TELESCOPING:
  266. circhandler = self.new_circuit_telescoping()
  267. elif network.thenetwork.womode == network.WOMode.SINGLEPASS:
  268. circhandler = self.new_circuit_singlepass()
  269. return circhandler
  270. def received_msg(self, msg, peeraddr, channel):
  271. """Callback when a NetMsg not specific to a circuit is
  272. received."""
  273. logging.debug("Client %s received msg %s from %s" % (self.myaddr, msg, peeraddr))
  274. if isinstance(msg, relay.RelayConsensusMsg) or \
  275. isinstance(msg, relay.RelayConsensusDiffMsg):
  276. self.relaypicker = dirauth.Consensus.verify(msg.consensus,
  277. network.thenetwork.dirauthkeys(), self.perfstats)
  278. self.consensus = msg.consensus
  279. else:
  280. return super().received_msg(msg, peeraddr, channel)
  281. def received_cell(self, circid, cell, peeraddr, channel):
  282. """Callback with a circuit-specific cell is received."""
  283. logging.debug("Client %s received cell on circ %d: %s from %s" % (self.myaddr, circid, cell, peeraddr))
  284. if isinstance(msg, relay.CloseCell):
  285. logging.debug("Log: Client received close cell; closing circuit")
  286. # TODO close cell
  287. return super().received_cell(circid, cell, peeraddr, channel)
  288. class Client:
  289. """A class representing a Tor client."""
  290. def __init__(self, dirauthaddrs):
  291. # Get a network address for client-side use only (do not bind it
  292. # to the network)
  293. self.netaddr = network.NetAddr()
  294. self.perfstats = network.PerfStats(network.EntType.CLIENT)
  295. self.perfstats.name = "Client at %s" % self.netaddr
  296. self.perfstats.is_bootstrapping = True
  297. self.channelmgr = ClientChannelManager(self.netaddr, dirauthaddrs,
  298. self.perfstats)
  299. # Register for epoch tick notifications
  300. network.thenetwork.wantepochticks(self, True)
  301. def terminate(self):
  302. """Quit this client."""
  303. # Stop listening for epoch ticks
  304. network.thenetwork.wantepochticks(self, False)
  305. # Close relay connections
  306. self.channelmgr.terminate()
  307. def get_consensus(self):
  308. """Fetch a new consensus."""
  309. # We're going to want a new consensus from our guard. In order
  310. # to get that, we'll need a channel to our guard. In order to
  311. # get that, we'll need a guard address. In order to get that,
  312. # we'll need a consensus (uh, oh; in that case, fetch the
  313. # consensus from a fallback relay).
  314. guardaddr = self.channelmgr.guardaddr
  315. guardchannel = None
  316. if guardaddr is not None:
  317. try:
  318. guardchannel = self.channelmgr.get_channel_to(guardaddr)
  319. except network.NetNoServer:
  320. guardaddr = None
  321. if guardchannel is None:
  322. logging.debug("In bootstrapping mode")
  323. self.channelmgr.get_consensus_from_fallbackrelay()
  324. logging.debug('client consensus=%s', self.channelmgr.consensus)
  325. return
  326. if network.thenetwork.womode == network.WOMode.VANILLA:
  327. if self.channelmgr.consensus is not None and len(self.channelmgr.consensus.consdict['relays']) > 0:
  328. guardchannel.send_msg(relay.RelayGetConsensusDiffMsg())
  329. logging.debug('got consensus diff, client consensus=%s', self.channelmgr.consensus)
  330. return
  331. # At this point, we are in one of the following scenarios:
  332. # 1. This is a walking onions protocol, and the client fetches the
  333. # complete consensus each epoch
  334. # 2. This is Vanilla Onion Routing and the client doesn't have a
  335. # consensus and needs to bootstrap it.
  336. guardchannel.send_msg(relay.RelayGetConsensusMsg())
  337. logging.debug('client consensus=%s', self.channelmgr.consensus)
  338. def newepoch(self, epoch):
  339. """Callback that fires at the start of each epoch"""
  340. # We'll need a new consensus
  341. self.get_consensus()
  342. # If we don't have a guard, pick one and make a channel to it
  343. self.channelmgr.ensure_guard()
  344. if __name__ == '__main__':
  345. perfstats = network.PerfStats(network.EntType.NONE)
  346. totsent = 0
  347. totrecv = 0
  348. dirasent = 0
  349. dirarecv = 0
  350. relaysent = 0
  351. relayrecv = 0
  352. clisent = 0
  353. clirecv = 0
  354. if len(sys.argv) < 3:
  355. print("Must pass in network mode and snip auth mode!")
  356. print("Network options are vanilla, telescoping, or single-pass.")
  357. print("SNIP auth options are merkle or threshold.")
  358. sys.exit(0)
  359. logging.basicConfig(level=logging.DEBUG)
  360. network_mode = network.WOMode.string_to_type(sys.argv[1])
  361. if network_mode == -1:
  362. print("Not a valid network mode: " + network_mode)
  363. sys.exit(0)
  364. snipauth_mode = network.SNIPAuthMode.string_to_type(sys.argv[2])
  365. if network_mode == -1:
  366. print("Not a valid SNIP authentication mode: " + snipauth_mode)
  367. sys.exit(0)
  368. # Initialize the (non-cryptographic) random seed
  369. random.seed(1)
  370. if network_mode == network.WOMode.VANILLA:
  371. network.thenetwork.set_wo_style(network.WOMode.VANILLA,
  372. network.SNIPAuthMode.NONE)
  373. elif network_mode == network.WOMode.TELESCOPING:
  374. if snipauth_mode == network.SNIPAuthMode.MERKLE:
  375. network.thenetwork.set_wo_style(network.WOMode.TELESCOPING,
  376. network.SNIPAuthMode.MERKLE)
  377. else:
  378. network.thenetwork.set_wo_style(network.WOMode.TELESCOPING,
  379. network.SNIPAuthMode.THRESHSIG)
  380. elif network_mode == network.WOMode.SINGLEPASS:
  381. if snipauth_mode == network.SNIPAuthMode.MERKLE:
  382. network.thenetwork.set_wo_style(network.WOMode.SINGLEPASS,
  383. network.SNIPAuthMode.MERKLE)
  384. else:
  385. network.thenetwork.set_wo_style(network.WOMode.SINGLEPASS,
  386. network.SNIPAuthMode.THRESHSIG)
  387. else:
  388. sys.exit("Received unsupported network mode, exiting.")
  389. # Start some dirauths
  390. numdirauths = 9
  391. dirauthaddrs = []
  392. dirauths = []
  393. for i in range(numdirauths):
  394. dira = dirauth.DirAuth(i, numdirauths)
  395. dirauths.append(dira)
  396. dirauthaddrs.append(dira.netaddr)
  397. # Start some relays
  398. numrelays = 10
  399. relays = []
  400. for i in range(numrelays):
  401. # Relay bandwidths (at least the ones fast enough to get used)
  402. # in the live Tor network (as of Dec 2019) are well approximated
  403. # by (200000-(200000-25000)/3*log10(x)) where x is a
  404. # uniform integer in [1,2500]
  405. x = random.randint(1,2500)
  406. bw = int(200000-(200000-25000)/3*math.log10(x))
  407. relays.append(relay.Relay(dirauthaddrs, bw, 0))
  408. # The fallback relays are a hardcoded list of about 5% of the
  409. # relays, used by clients for bootstrapping
  410. numfallbackrelays = int(numrelays * 0.05) + 1
  411. fallbackrelays = random.sample(relays, numfallbackrelays)
  412. for r in fallbackrelays:
  413. r.set_is_fallbackrelay()
  414. network.thenetwork.setfallbackrelays(fallbackrelays)
  415. # Tick the epoch
  416. network.thenetwork.nextepoch()
  417. dirauth.Consensus.verify(dirauth.DirAuth.consensus, network.thenetwork.dirauthkeys(), perfstats)
  418. print('ticked; epoch=', network.thenetwork.getepoch())
  419. relays[3].channelmgr.send_msg(relay.RelayRandomHopMsg(30), relays[5].netaddr)
  420. # See what channels exist and do a consistency check
  421. for r in relays:
  422. print("%s: %s" % (r.netaddr, [ str(k) for k in r.channelmgr.channels.keys()]))
  423. raddr = r.netaddr
  424. for ad, ch in r.channelmgr.channels.items():
  425. if ch.peer.channelmgr.myaddr != ad:
  426. print('address mismatch:', raddr, ad, ch.peer.channelmgr.myaddr)
  427. if ch.peer.channelmgr.channels[raddr].peer is not ch:
  428. print('asymmetry:', raddr, ad, ch, ch.peer.channelmgr.channels[raddr].peer)
  429. # Start some clients
  430. numclients = 1
  431. clients = []
  432. for i in range(numclients):
  433. clients.append(Client(dirauthaddrs))
  434. # Tick the epoch
  435. network.thenetwork.nextepoch()
  436. # See what channels exist and do a consistency check
  437. for c in clients:
  438. print("%s: %s" % (c.netaddr, [ str(k) for k in c.channelmgr.channels.keys()]))
  439. caddr = c.netaddr
  440. for ad, ch in c.channelmgr.channels.items():
  441. if ch.peer.channelmgr.myaddr != ad:
  442. print('address mismatch:', caddr, ad, ch.peer.channelmgr.myaddr)
  443. if ch.peer.channelmgr.channels[caddr].peer is not ch:
  444. print('asymmetry:', caddr, ad, ch, ch.peer.channelmgr.channels[caddr].peer)
  445. # Pick a bunch of bw-weighted random relays and look at the
  446. # distribution
  447. for i in range(100):
  448. r = relays[0].channelmgr.relaypicker.pick_weighted_relay()
  449. if network.thenetwork.womode == network.WOMode.VANILLA:
  450. print("relay",r.descdict["addr"])
  451. else:
  452. print("relay",r.snipdict["addr"])
  453. relays[3].terminate()
  454. relaysent += relays[3].perfstats.bytes_sent
  455. relayrecv += relays[3].perfstats.bytes_received
  456. del relays[3]
  457. # Tick the epoch
  458. network.thenetwork.nextepoch()
  459. circs = []
  460. for i in range(20):
  461. circ = clients[0].channelmgr.new_circuit()
  462. if circ is None:
  463. sys.exit("ERR: Client unable to create circuits")
  464. circs.append(circ)
  465. circ.send_cell(relay.StringCell("hello world circuit %d" % i))
  466. # Tick the epoch
  467. network.thenetwork.nextepoch()
  468. # See what channels exist and do a consistency check
  469. for r in relays:
  470. print("%s: %s" % (r.netaddr, [ str(k) + str([ck for ck in r.channelmgr.channels[k].circuithandlers.keys()]) for k in r.channelmgr.channels.keys()]))
  471. raddr = r.netaddr
  472. for ad, ch in r.channelmgr.channels.items():
  473. if ch.peer.channelmgr.myaddr != ad:
  474. print('address mismatch:', raddr, ad, ch.peer.channelmgr.myaddr)
  475. if ch.peer.channelmgr.channels[raddr].peer is not ch:
  476. print('asymmetry:', raddr, ad, ch, ch.peer.channelmgr.channels[raddr].peer)
  477. # See what channels exist and do a consistency check
  478. for c in clients:
  479. print("%s: %s" % (c.netaddr, [ str(k) + str([ck for ck in c.channelmgr.channels[k].circuithandlers.keys()]) for k in c.channelmgr.channels.keys()]))
  480. caddr = c.netaddr
  481. for ad, ch in c.channelmgr.channels.items():
  482. if ch.peer.channelmgr.myaddr != ad:
  483. print('address mismatch:', caddr, ad, ch.peer.channelmgr.myaddr)
  484. if ch.peer.channelmgr.channels[caddr].peer is not ch:
  485. print('asymmetry:', caddr, ad, ch, ch.peer.channelmgr.channels[caddr].peer)
  486. if ch.circuithandlers.keys() != \
  487. ch.peer.channelmgr.channels[caddr].circuithandlers.keys():
  488. print('circuit asymmetry:', caddr, ad, ch.peer.channelmgr.myaddr)
  489. for c in circs:
  490. c.close()
  491. for d in dirauths:
  492. print(d.perfstats)
  493. dirasent += d.perfstats.bytes_sent
  494. dirarecv += d.perfstats.bytes_received
  495. print("DirAuths sent=%s recv=%s" % (dirasent, dirarecv))
  496. totsent += dirasent
  497. totrecv += dirarecv
  498. for r in relays:
  499. print(r.perfstats)
  500. relaysent += r.perfstats.bytes_sent
  501. relayrecv += r.perfstats.bytes_received
  502. print("Relays sent=%s recv=%s" % (relaysent, relayrecv))
  503. totsent += relaysent
  504. totrecv += relayrecv
  505. for c in clients:
  506. print(c.perfstats)
  507. clisent += c.perfstats.bytes_sent
  508. clirecv += c.perfstats.bytes_received
  509. print("Client sent=%s recv=%s" % (clisent, clirecv))
  510. totsent += clisent
  511. totrecv += clirecv
  512. print("Total sent=%s recv=%s" % (totsent, totrecv))