[tor-commits] [stem/master] Expand and test make_router_status_entry()

atagar at torproject.org atagar at torproject.org
Sun Jul 2 21:54:56 UTC 2017


commit 3eb7836cbd96e49704586d90afaef814202d2360
Author: Damian Johnson <atagar at torproject.org>
Date:   Sun Jul 2 11:58:12 2017 -0700

    Expand and test make_router_status_entry()
    
    To start with make_router_status_entry() just covered what I needed for
    BridgeDB. Expanding it to cover all router status entry fields that are part of
    server descriptors, and adding test coverage.
---
 stem/descriptor/__init__.py               | 12 +++++-
 stem/descriptor/router_status_entry.py    |  5 +--
 stem/descriptor/server_descriptor.py      | 14 +++++-
 test/unit/descriptor/server_descriptor.py | 71 +++++++++++++++++++++++++++++++
 4 files changed, 97 insertions(+), 5 deletions(-)

diff --git a/stem/descriptor/__init__.py b/stem/descriptor/__init__.py
index e883c55..bf039f8 100644
--- a/stem/descriptor/__init__.py
+++ b/stem/descriptor/__init__.py
@@ -405,6 +405,9 @@ def _descriptor_content(attr = None, exclude = (), header_template = (), footer_
 
       if value is None:
         continue
+      elif isinstance(value, (tuple, list)):
+        for v in value:
+          content.append('%s %s' % (keyword, v))
       elif value == '':
         content.append(keyword)
       elif value.startswith('\n'):
@@ -413,7 +416,14 @@ def _descriptor_content(attr = None, exclude = (), header_template = (), footer_
       else:
         content.append('%s %s' % (keyword, value))
 
-  remainder = [('%s %s' % (k, v) if v else k) for k, v in attr.items()]
+  remainder = []
+
+  for k, v in attr.items():
+    if isinstance(v, (tuple, list)):
+      remainder += ['%s %s' % (k, entry) for entry in v]
+    else:
+      remainder.append('%s %s' % (k, v))
+
   return stem.util.str_tools._to_bytes('\n'.join(header_content + remainder + footer_content))
 
 
diff --git a/stem/descriptor/router_status_entry.py b/stem/descriptor/router_status_entry.py
index 81bc63d..398448c 100644
--- a/stem/descriptor/router_status_entry.py
+++ b/stem/descriptor/router_status_entry.py
@@ -292,9 +292,8 @@ def _parse_id_line(descriptor, entries):
   value = _value('id', entries)
 
   if value:
-    if not (descriptor.document and descriptor.document.is_vote):
-      vote_status = 'vote' if descriptor.document else '<undefined document>'
-      raise ValueError("%s 'id' line should only appear in votes (appeared in a %s): id %s" % (descriptor._name(), vote_status, value))
+    if descriptor.document and not descriptor.document.is_vote:
+      raise ValueError("%s 'id' line should only appear in votes: id %s" % (descriptor._name(), value))
 
     value_comp = value.split()
 
diff --git a/stem/descriptor/server_descriptor.py b/stem/descriptor/server_descriptor.py
index 8c11b6c..180fe53 100644
--- a/stem/descriptor/server_descriptor.py
+++ b/stem/descriptor/server_descriptor.py
@@ -838,6 +838,7 @@ class RelayDescriptor(ServerDescriptor):
       return _append_router_signature(content, signing_key.private)
     else:
       return _descriptor_content(attr, exclude, base_header, (
+        ('router-sig-ed25519', None),
         ('router-signature', _random_crypto_blob('SIGNATURE')),
       ))
 
@@ -867,6 +868,9 @@ class RelayDescriptor(ServerDescriptor):
       that would be in the consensus
     """
 
+    if not self.fingerprint:
+      raise ValueError('Server descriptor lacks a fingerprint. This is an optional field, but required to make a router status entry.')
+
     attr = {
       'r': ' '.join([
         self.nickname,
@@ -877,10 +881,18 @@ class RelayDescriptor(ServerDescriptor):
         str(self.or_port),
         str(self.dir_port) if self.dir_port else '0',
       ]),
+      'w': 'Bandwidth=%i' % self.average_bandwidth,
+      'p': self.exit_policy.summary().replace(', ', ','),
     }
 
     if self.tor_version:
-      attr['v'] = self.tor_version
+      attr['v'] = 'Tor %s' % self.tor_version
+
+    if self.or_addresses:
+      attr['a'] = ['%s:%s' % (addr, port) for addr, port, _ in self.or_addresses]
+
+    if self.certificate:
+      attr['id'] = 'ed25519 %s' % base64.b64encode(self.certificate.key).rstrip('=')
 
     return RouterStatusEntryV3.create(attr)
 
diff --git a/test/unit/descriptor/server_descriptor.py b/test/unit/descriptor/server_descriptor.py
index 8578726..4d36bd8 100644
--- a/test/unit/descriptor/server_descriptor.py
+++ b/test/unit/descriptor/server_descriptor.py
@@ -11,6 +11,7 @@ import time
 import unittest
 
 import stem.descriptor
+import stem.descriptor.router_status_entry
 import stem.descriptor.server_descriptor
 import stem.exit_policy
 import stem.prereq
@@ -260,6 +261,76 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
     RelayDescriptor.create(sign = True)
     self.assertRaisesRegexp(NotImplementedError, 'Signing of BridgeDescriptor not implemented', BridgeDescriptor.create, sign = True)
 
+  def test_router_status_entry(self):
+    """
+    Tests creation of router status entries.
+    """
+
+    desc_without_fingerprint = RelayDescriptor.create()
+    exc_msg = 'Server descriptor lacks a fingerprint. This is an optional field, but required to make a router status entry.'
+    self.assertRaisesRegexp(ValueError, exc_msg, desc_without_fingerprint.make_router_status_entry)
+
+    desc = RelayDescriptor.create({
+      'router': 'caerSidi 71.35.133.197 9001 0 0',
+      'published': '2012-02-29 04:03:19',
+      'fingerprint': '4F0C 867D F0EF 6816 0568 C826 838F 482C EA7C FE44',
+      'or-address': ['71.35.133.197:9001', '[12ab:2e19:3bcf::02:9970]:9001'],
+      'onion-key': '\n-----BEGIN RSA PUBLIC KEY-----%s-----END RSA PUBLIC KEY-----' % stem.descriptor.CRYPTO_BLOB,
+      'signing-key': '\n-----BEGIN RSA PUBLIC KEY-----%s-----END RSA PUBLIC KEY-----' % stem.descriptor.CRYPTO_BLOB,
+    }).make_router_status_entry()
+
+    self.assertEqual(stem.descriptor.router_status_entry.RouterStatusEntryV3, type(desc))
+    self.assertEqual('caerSidi', desc.nickname)
+    self.assertEqual('4F0C867DF0EF68160568C826838F482CEA7CFE44', desc.fingerprint)
+    self.assertEqual(datetime.datetime(2012, 2, 29, 4, 3, 19), desc.published)
+    self.assertEqual('71.35.133.197', desc.address)
+    self.assertEqual(9001, desc.or_port)
+    self.assertEqual(None, desc.dir_port)
+    self.assertEqual(['Fast', 'Named', 'Running', 'Stable', 'Valid'], desc.flags)
+    self.assertEqual(None, desc.version)
+    self.assertEqual(None, desc.version_line)
+
+    self.assertEqual([(u'71.35.133.197', 9001, False), (u'12ab:2e19:3bcf::02:9970', 9001, True)], desc.or_addresses)
+    self.assertEqual(None, desc.identifier_type)
+    self.assertEqual(None, desc.identifier)
+    self.assertEqual('4F0069BF91C04581B7C3CA9272E2D3228D4EA571', desc.digest)
+    self.assertEqual(153600, desc.bandwidth)
+    self.assertEqual(None, desc.measured)
+    self.assertEqual(False, desc.is_unmeasured)
+    self.assertEqual([], desc.unrecognized_bandwidth_entries)
+    self.assertEqual(stem.exit_policy.MicroExitPolicy('reject 1-65535'), desc.exit_policy)
+    self.assertEqual([], desc.microdescriptor_hashes)
+
+  def test_make_router_status_entry_with_live_descriptor(self):
+    """
+    Tests creation of router status entries with a live server descriptor.
+    """
+
+    with open(get_resource('server_descriptor_with_ed25519'), 'rb') as descriptor_file:
+      desc = next(stem.descriptor.parse_file(descriptor_file, validate = True)).make_router_status_entry()
+
+    self.assertEqual(stem.descriptor.router_status_entry.RouterStatusEntryV3, type(desc))
+    self.assertEqual('destiny', desc.nickname)
+    self.assertEqual('F65E0196C94DFFF48AFBF2F5F9E3E19AAE583FD0', desc.fingerprint)
+    self.assertEqual(datetime.datetime(2015, 8, 22, 15, 21, 45), desc.published)
+    self.assertEqual('94.242.246.23', desc.address)
+    self.assertEqual(9001, desc.or_port)
+    self.assertEqual(443, desc.dir_port)
+    self.assertEqual(['Fast', 'Named', 'Running', 'Stable', 'Valid'], desc.flags)
+    self.assertEqual(stem.version.Version('0.2.7.2-alpha-dev'), desc.version)
+    self.assertEqual('Tor 0.2.7.2-alpha-dev', desc.version_line)
+
+    self.assertEqual([('2a01:608:ffff:ff07::1:23', 9003, True)], desc.or_addresses)
+    self.assertEqual('ed25519', desc.identifier_type)
+    self.assertEqual('pbYagEQPUiNjcDp/oY2oESXkDzd8PZlr26kaR7nUkao', desc.identifier)
+    self.assertEqual('B5E441051D139CCD84BC765D130B01E44DAC29AD', desc.digest)
+    self.assertEqual(149715200, desc.bandwidth)
+    self.assertEqual(None, desc.measured)
+    self.assertEqual(False, desc.is_unmeasured)
+    self.assertEqual([], desc.unrecognized_bandwidth_entries)
+    self.assertEqual(stem.exit_policy.MicroExitPolicy('reject 25,465,587,10000,14464'), desc.exit_policy)
+    self.assertEqual([], desc.microdescriptor_hashes)
+
   @patch('time.time', Mock(return_value = time.mktime(datetime.date(2010, 1, 1).timetuple())))
   def test_with_ed25519(self):
     """





More information about the tor-commits mailing list