Stem Docs

Saving and Loading a Tor Consensus

Saving and Loading a Tor ConsensusΒΆ

../../_images/back.png

Reading and writing a Tor consensus to disk is similar to other descriptor types with one small difference.

Most descriptors are just about a single relay. Server descriptors and microdescriptors, for instance, can be concatenated together and dumped to a file because they're each independent of each other.

The Tor consensus, however, is a larger document containing information about the Tor network in addition to a little data on each of the relays.

In Stem the overall document is a NetworkStatusDocumentV3, and the information on individual relays are RouterStatusEntryV3 instances.

Why does this matter? By default when you read a consensus Stem provides you just the RouterStatusEntryV3. This is for performance reasons, and because usually that's what developers want. But for writing the conssensus to disk we'll want the whole document instead.

So how do we get it? Just tell Stem that's what you want. The DocumentHandler tells Stem how to read the consensus. For example, to write the consensus simply do the following...

from stem.descriptor import DocumentHandler
from stem.descriptor.remote import DescriptorDownloader

downloader = DescriptorDownloader()
consensus = downloader.get_consensus(document_handler = DocumentHandler.DOCUMENT).run()[0]

with open('/tmp/descriptor_dump', 'w') as descriptor_file:
  descriptor_file.write(str(consensus))

Our consensus here is the current NetworkStatusDocumentV3. The descriptor_dump file now looks like...

network-status-version 3
vote-status consensus
consensus-method 18
valid-after 2014-11-17 23:00:00
fresh-until 2014-11-18 00:00:00
valid-until 2014-11-18 02:00:00
voting-delay 300 300
... etc...

You can then read it back with parse_file()...

from stem.descriptor import DocumentHandler, parse_file

consensus = next(parse_file(
  '/tmp/descriptor_dump',
  descriptor_type = 'network-status-consensus-3 1.0',
  document_handler = DocumentHandler.DOCUMENT,
))

for fingerprint, relay in consensus.routers.items():
  print("%s: %s" % (fingerprint, relay.nickname))