|
@@ -7,6 +7,46 @@ import nacl.encoding
|
|
import nacl.signing
|
|
import nacl.signing
|
|
import network
|
|
import network
|
|
|
|
|
|
|
|
+from enum import Enum
|
|
|
|
+
|
|
|
|
+class EntType(Enum):
|
|
|
|
+ """The different types of entities in the system."""
|
|
|
|
+ NONE = 0
|
|
|
|
+ DIRAUTH = 1
|
|
|
|
+ RELAY = 2
|
|
|
|
+ CLIENT = 3
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+class PerfStats:
|
|
|
|
+ """A class to store performance statistics for a relay or client.
|
|
|
|
+ We keep track of bytes sent, bytes received, and counts of
|
|
|
|
+ public-key operations of various types. We will reset these every
|
|
|
|
+ epoch."""
|
|
|
|
+
|
|
|
|
+ def __init__(self, ent_type):
|
|
|
|
+ # Which type of entity is this for (DIRAUTH, RELAY, CLIENT)
|
|
|
|
+ self.ent_type = ent_type
|
|
|
|
+ # A printable name for the entity
|
|
|
|
+ self.name = None
|
|
|
|
+ # True if bootstrapping this epoch
|
|
|
|
+ self.is_bootstrapping = False
|
|
|
|
+ # Bytes sent and received
|
|
|
|
+ self.bytes_sent = 0
|
|
|
|
+ self.bytes_received = 0
|
|
|
|
+ # Public-key operations: key generation, signing, verification,
|
|
|
|
+ # Diffie-Hellman
|
|
|
|
+ self.keygens = 0
|
|
|
|
+ self.sigs = 0
|
|
|
|
+ self.verifs = 0
|
|
|
|
+ self.dhs = 0
|
|
|
|
+
|
|
|
|
+ def __str__(self):
|
|
|
|
+ return "%s: type=%s boot=%s sent=%d recv=%d keygen=%d sig=%d verif=%d dh=%d" % \
|
|
|
|
+ (self.name, self.ent_type.name, self.is_bootstrapping, \
|
|
|
|
+ self.bytes_sent, self.bytes_received, self.keygens, \
|
|
|
|
+ self.sigs, self.verifs, self.dhs)
|
|
|
|
+
|
|
|
|
+
|
|
# A relay descriptor is a dict containing:
|
|
# A relay descriptor is a dict containing:
|
|
# epoch: epoch id
|
|
# epoch: epoch id
|
|
# idkey: a public identity key
|
|
# idkey: a public identity key
|
|
@@ -35,15 +75,17 @@ class RelayDescriptor:
|
|
res += "]\n"
|
|
res += "]\n"
|
|
return res
|
|
return res
|
|
|
|
|
|
- def sign(self, signingkey):
|
|
|
|
|
|
+ def sign(self, signingkey, perfstats):
|
|
serialized = self.__str__(False)
|
|
serialized = self.__str__(False)
|
|
signed = signingkey.sign(serialized.encode("ascii"))
|
|
signed = signingkey.sign(serialized.encode("ascii"))
|
|
|
|
+ perfstats.sigs += 1
|
|
self.descdict["sig"] = signed.signature
|
|
self.descdict["sig"] = signed.signature
|
|
|
|
|
|
@staticmethod
|
|
@staticmethod
|
|
- def verify(desc):
|
|
|
|
|
|
+ def verify(desc, perfstats):
|
|
assert(type(desc) is RelayDescriptor)
|
|
assert(type(desc) is RelayDescriptor)
|
|
serialized = desc.__str__(False)
|
|
serialized = desc.__str__(False)
|
|
|
|
+ perfstats.verifs += 1
|
|
desc.descdict["idkey"].verify(serialized.encode("ascii"), desc.descdict["sig"])
|
|
desc.descdict["idkey"].verify(serialized.encode("ascii"), desc.descdict["sig"])
|
|
|
|
|
|
|
|
|
|
@@ -75,11 +117,12 @@ class Consensus:
|
|
res += "]\n"
|
|
res += "]\n"
|
|
return res
|
|
return res
|
|
|
|
|
|
- def sign(self, signingkey, index):
|
|
|
|
|
|
+ def sign(self, signingkey, index, perfstats):
|
|
"""Use the given signing key to sign the consensus, storing the
|
|
"""Use the given signing key to sign the consensus, storing the
|
|
result in the sigs list at the given index."""
|
|
result in the sigs list at the given index."""
|
|
serialized = self.__str__(False)
|
|
serialized = self.__str__(False)
|
|
signed = signingkey.sign(serialized.encode("ascii"))
|
|
signed = signingkey.sign(serialized.encode("ascii"))
|
|
|
|
+ perfstats.sigs += 1
|
|
if 'sigs' not in self.consdict:
|
|
if 'sigs' not in self.consdict:
|
|
self.consdict['sigs'] = []
|
|
self.consdict['sigs'] = []
|
|
if index >= len(self.consdict['sigs']):
|
|
if index >= len(self.consdict['sigs']):
|
|
@@ -110,12 +153,13 @@ class Consensus:
|
|
return self.consdict['relays'][idx-1]
|
|
return self.consdict['relays'][idx-1]
|
|
|
|
|
|
@staticmethod
|
|
@staticmethod
|
|
- def verify(consensus, verifkeylist):
|
|
|
|
|
|
+ def verify(consensus, verifkeylist, perfstats):
|
|
"""Use the given list of verification keys to check the
|
|
"""Use the given list of verification keys to check the
|
|
signatures on the consensus."""
|
|
signatures on the consensus."""
|
|
assert(type(consensus) is Consensus)
|
|
assert(type(consensus) is Consensus)
|
|
serialized = consensus.__str__(False)
|
|
serialized = consensus.__str__(False)
|
|
for i, vk in enumerate(verifkeylist):
|
|
for i, vk in enumerate(verifkeylist):
|
|
|
|
+ perfstats.verifs += 1
|
|
vk.verify(serialized.encode("ascii"), consensus.consdict['sigs'][i])
|
|
vk.verify(serialized.encode("ascii"), consensus.consdict['sigs'][i])
|
|
|
|
|
|
|
|
|
|
@@ -206,9 +250,13 @@ class DirAuth(network.Server):
|
|
self.me = me
|
|
self.me = me
|
|
self.tot = tot
|
|
self.tot = tot
|
|
self.name = "Dirauth %d of %d" % (me+1, tot)
|
|
self.name = "Dirauth %d of %d" % (me+1, tot)
|
|
|
|
+ self.perfstats = PerfStats(EntType.DIRAUTH)
|
|
|
|
+ self.perfstats.name = self.name
|
|
|
|
+ self.perfstats.is_bootstrapping = True
|
|
|
|
|
|
# Create the dirauth signature keypair
|
|
# Create the dirauth signature keypair
|
|
self.sigkey = nacl.signing.SigningKey.generate()
|
|
self.sigkey = nacl.signing.SigningKey.generate()
|
|
|
|
+ self.perfstats.keygens += 1
|
|
|
|
|
|
network.thenetwork.setdirauthkey(me, self.sigkey.verify_key)
|
|
network.thenetwork.setdirauthkey(me, self.sigkey.verify_key)
|
|
network.thenetwork.wantepochticks(self, True, True)
|
|
network.thenetwork.wantepochticks(self, True, True)
|
|
@@ -234,17 +282,18 @@ class DirAuth(network.Server):
|
|
consensusdescs.append(desc)
|
|
consensusdescs.append(desc)
|
|
DirAuth.consensus = Consensus(epoch, consensusdescs)
|
|
DirAuth.consensus = Consensus(epoch, consensusdescs)
|
|
|
|
|
|
-
|
|
|
|
def epoch_ending(self, epoch):
|
|
def epoch_ending(self, epoch):
|
|
# Only dirauth 0 actually needs to generate the consensus
|
|
# Only dirauth 0 actually needs to generate the consensus
|
|
# because of the shared class-static state, but everyone has to
|
|
# because of the shared class-static state, but everyone has to
|
|
# sign it. Note that this code relies on dirauth 0's
|
|
# sign it. Note that this code relies on dirauth 0's
|
|
# epoch_ending callback being called before any of the other
|
|
# epoch_ending callback being called before any of the other
|
|
# dirauths'.
|
|
# dirauths'.
|
|
|
|
+ if (epoch+1) not in DirAuth.uploadeddescs:
|
|
|
|
+ DirAuth.uploadeddescs[epoch+1] = dict()
|
|
if self.me == 0:
|
|
if self.me == 0:
|
|
self.generate_consensus(epoch+1)
|
|
self.generate_consensus(epoch+1)
|
|
del DirAuth.uploadeddescs[epoch+1]
|
|
del DirAuth.uploadeddescs[epoch+1]
|
|
- DirAuth.consensus.sign(self.sigkey, self.me)
|
|
|
|
|
|
+ DirAuth.consensus.sign(self.sigkey, self.me, self.perfstats)
|
|
|
|
|
|
def received(self, client, msg):
|
|
def received(self, client, msg):
|
|
if isinstance(msg, DirAuthUploadDescMsg):
|
|
if isinstance(msg, DirAuthUploadDescMsg):
|