[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[or-cvs] [tor/master 04/15] Initial conversion to use node_t throughout our codebase.



Author: Nick Mathewson <nickm@xxxxxxxxxxxxxx>
Date: Wed, 29 Sep 2010 15:00:41 -0400
Subject: Initial conversion to use node_t throughout our codebase.
Commit: 26e897420e07611e0b2c10b28202c388eea4bd6b

A node_t is an abstraction over routerstatus_t, routerinfo_t, and
microdesc_t.  It should try to present a consistent interface to all
of them.  There should be a node_t for a server whenever there is
  * A routerinfo_t for it in the routerlist
  * A routerstatus_t in the current_consensus.
(note that a microdesc_t alone isn't enough to make a node_t exist,
since microdescriptors aren't usable on their own.)

There are three ways to get a node_t right now: looking it up by ID,
looking it up by nickname, and iterating over the whole list of
microdescriptors.

All (or nearly all) functions that are supposed to return "a router"
-- especially those used in building connections and circuits --
should return a node_t, not a routerinfo_t or a routerstatus_t.

A node_t should hold all the *mutable* flags about a node.  This
patch moves the is_foo flags from routerinfo_t into node_t.  The
flags in routerstatus_t remain, but they get set from the consensus
and should not change.

Some other highlights of this patch are:

  * Looking up routerinfo and routerstatus by nickname is now
    unified and based on the "look up a node by nickname" function.
    This tries to look only at the values from current consensus,
    and not get confused by the routerinfo_t->is_named flag, which
    could get set for other weird reasons.  This changes the
    behavior of how authorities (when acting as clients) deal with
    nodes that have been listed by nickname.

  * I tried not to artificially increase the size of the diff here
    by moving functions around.  As a result, some functions that
    now operate on nodes are now in the wrong file -- they should
    get moved to nodelist.c once this refactoring settles down.
    This moving should happen as part of a patch that moves
    functions AND NOTHING ELSE.

  * Some old code is now left around inside #if 0/1 blocks, and
    should get removed once I've verified that I don't want it
    sitting around to see how we used to do things.

There are still some unimplemented functions: these are flagged
with "UNIMPLEMENTED_NODELIST()."  I'll work on filling in the
implementation here, piece by piece.

I wish this patch could have been smaller, but there did not seem to
be any piece of it that was independent from the rest.  Moving flags
forces many functions that once returned routerinfo_t * to return
node_t *, which forces their friends to change, and so on.
---
 changes/nodelist         |   10 +
 src/or/circuitbuild.c    |  437 ++++++++++++++++---------------
 src/or/circuitbuild.h    |    5 +-
 src/or/circuitlist.c     |    9 +-
 src/or/circuituse.c      |   66 ++---
 src/or/circuituse.h      |    3 +-
 src/or/command.c         |    7 +-
 src/or/connection_edge.c |   36 ++--
 src/or/connection_edge.h |    2 +-
 src/or/connection_or.c   |   15 +-
 src/or/control.c         |   46 ++--
 src/or/directory.c       |   33 ++-
 src/or/dirserv.c         |  199 ++++++++------
 src/or/dirserv.h         |   10 +-
 src/or/dirvote.h         |    1 +
 src/or/microdesc.c       |    1 +
 src/or/networkstatus.c   |  126 +--------
 src/or/networkstatus.h   |    5 +-
 src/or/nodelist.c        |  418 ++++++++++++++++++++++++++++--
 src/or/nodelist.h        |   39 +++-
 src/or/or.h              |  100 +++++--
 src/or/policies.c        |   52 ++++
 src/or/policies.h        |    9 +-
 src/or/relay.c           |   11 +-
 src/or/rendclient.c      |    8 +-
 src/or/rendservice.c     |   41 ++--
 src/or/rephist.c         |   33 ++--
 src/or/router.c          |   39 ++-
 src/or/routerlist.c      |  651 +++++++++++++++++++++++++---------------------
 src/or/routerlist.h      |   53 ++--
 src/or/routerparse.c     |    3 -
 31 files changed, 1512 insertions(+), 956 deletions(-)
 create mode 100644 changes/nodelist

diff --git a/changes/nodelist b/changes/nodelist
new file mode 100644
index 0000000..033a6c1
--- /dev/null
+++ b/changes/nodelist
@@ -0,0 +1,10 @@
+  o Code refactorings
+    - Unified our node-listing and selecting logic.  We had at least
+      two major ways to look at the question of "which Tor servers do
+      we know about": our list of router descriptors, and the current
+      consensus.  We're adding a third in microdescriptors.  Having
+      so many systems without an abstraction layer over them was
+      hurting the codebase.  Now, we have a new "node_t" abstraction
+      that presents a consistent interface to a client's view of
+      a Tor node, and holds (nearly) all of the mutable state
+      formerly in routerinfo_t and routerstatus_t.
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index 02f361c..58d4e60 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -23,6 +23,7 @@
 #include "directory.h"
 #include "main.h"
 #include "networkstatus.h"
+#include "nodelist.h"
 #include "onion.h"
 #include "policies.h"
 #include "relay.h"
@@ -54,8 +55,8 @@ extern circuit_t *global_circuitlist;
 
 /** An entry_guard_t represents our information about a chosen long-term
  * first hop, known as a "helper" node in the literature. We can't just
- * use a routerinfo_t, since we want to remember these even when we
- * don't have a directory. */
+ * use a node_t, since we want to remember these even when we
+ * don't have any directory info. */
 typedef struct {
   char nickname[MAX_NICKNAME_LEN+1];
   char identity[DIGEST_LEN];
@@ -94,7 +95,7 @@ static int circuit_deliver_create_cell(circuit_t *circ,
 static int onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit);
 static crypt_path_t *onion_next_hop_in_cpath(crypt_path_t *cpath);
 static int onion_extend_cpath(origin_circuit_t *circ);
-static int count_acceptable_routers(smartlist_t *routers);
+static int count_acceptable_nodes(smartlist_t *routers);
 static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
 
 static void entry_guards_changed(void);
@@ -1401,10 +1402,9 @@ circuit_list_path_impl(origin_circuit_t *circ, int verbose, int verbose_names)
 
   hop = circ->cpath;
   do {
-    const routerinfo_t *ri;
-    const routerstatus_t *rs;
     char *elt;
     const char *id;
+    const node_t *node;
     if (!hop)
       break;
     if (!verbose && hop->state != CPATH_STATE_OPEN)
@@ -1414,10 +1414,8 @@ circuit_list_path_impl(origin_circuit_t *circ, int verbose, int verbose_names)
     id = hop->extend_info->identity_digest;
     if (verbose_names) {
       elt = tor_malloc(MAX_VERBOSE_NICKNAME_LEN+1);
-      if ((ri = router_get_by_digest(id))) {
-        router_get_verbose_nickname(elt, ri);
-      } else if ((rs = router_get_consensus_status_by_id(id))) {
-        routerstatus_get_verbose_nickname(elt, rs);
+      if ((node = node_get_by_id(id))) {
+        node_get_verbose_nickname(node, elt);
       } else if (is_legal_nickname(hop->extend_info->nickname)) {
         elt[0] = '$';
         base16_encode(elt+1, HEX_DIGEST_LEN+1, id, DIGEST_LEN);
@@ -1429,9 +1427,9 @@ circuit_list_path_impl(origin_circuit_t *circ, int verbose, int verbose_names)
         base16_encode(elt+1, HEX_DIGEST_LEN+1, id, DIGEST_LEN);
       }
     } else { /* ! verbose_names */
-      if ((ri = router_get_by_digest(id)) &&
-          ri->is_named) {
-        elt = tor_strdup(hop->extend_info->nickname);
+      node = node_get_by_id(id);
+      if (node && node_is_named(node)) {
+        elt = tor_strdup(node_get_nickname(node));
       } else {
         elt = tor_malloc(HEX_DIGEST_LEN+2);
         elt[0] = '$';
@@ -1501,7 +1499,6 @@ circuit_rep_hist_note_result(origin_circuit_t *circ)
 {
   crypt_path_t *hop;
   const char *prev_digest = NULL;
-  const routerinfo_t *router;
   hop = circ->cpath;
   if (!hop) /* circuit hasn't started building yet. */
     return;
@@ -1512,19 +1509,17 @@ circuit_rep_hist_note_result(origin_circuit_t *circ)
     prev_digest = me->cache_info.identity_digest;
   }
   do {
-    router = router_get_by_digest(hop->extend_info->identity_digest);
-    if (router) {
+    const node_t *node = node_get_by_id(hop->extend_info->identity_digest);
+    if (node) { /* Why do we check this?  We know the identity. -NM XXXX */
       if (prev_digest) {
         if (hop->state == CPATH_STATE_OPEN)
-          rep_hist_note_extend_succeeded(prev_digest,
-                                         router->cache_info.identity_digest);
+          rep_hist_note_extend_succeeded(prev_digest, node->identity);
         else {
-          rep_hist_note_extend_failed(prev_digest,
-                                      router->cache_info.identity_digest);
+          rep_hist_note_extend_failed(prev_digest, node->identity);
           break;
         }
       }
-      prev_digest = router->cache_info.identity_digest;
+      prev_digest = node->identity;
     } else {
       prev_digest = NULL;
     }
@@ -1866,7 +1861,7 @@ int
 circuit_send_next_onion_skin(origin_circuit_t *circ)
 {
   crypt_path_t *hop;
-  const routerinfo_t *router;
+  const node_t *node;
   char payload[2+4+DIGEST_LEN+ONIONSKIN_CHALLENGE_LEN];
   char *onionskin;
   size_t payload_len;
@@ -1882,7 +1877,7 @@ circuit_send_next_onion_skin(origin_circuit_t *circ)
     else
       control_event_bootstrap(BOOTSTRAP_STATUS_CIRCUIT_CREATE, 0);
 
-    router = router_get_by_digest(circ->_base.n_conn->identity_digest);
+    node = node_get_by_id(circ->_base.n_conn->identity_digest);
     fast = should_use_create_fast_for_circuit(circ);
     if (!fast) {
       /* We are an OR and we know the right onion key: we should
@@ -1916,7 +1911,7 @@ circuit_send_next_onion_skin(origin_circuit_t *circ)
     circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_BUILDING);
     log_info(LD_CIRC,"First hop: finished sending %s cell to '%s'",
              fast ? "CREATE_FAST" : "CREATE",
-             router ? router->nickname : "<unnamed>");
+             node ? node_get_nickname(node) : "<unnamed>");
   } else {
     tor_assert(circ->cpath->state == CPATH_STATE_OPEN);
     tor_assert(circ->_base.state == CIRCUIT_STATE_BUILDING);
@@ -2390,12 +2385,12 @@ onionskin_answer(or_circuit_t *circ, uint8_t cell_type, const char *payload,
  */
 static int
 new_route_len(uint8_t purpose, extend_info_t *exit,
-              smartlist_t *routers)
+              smartlist_t *nodes)
 {
   int num_acceptable_routers;
   int routelen;
 
-  tor_assert(routers);
+  tor_assert(nodes);
 
   routelen = DEFAULT_ROUTE_LEN;
   if (exit &&
@@ -2403,10 +2398,10 @@ new_route_len(uint8_t purpose, extend_info_t *exit,
       purpose != CIRCUIT_PURPOSE_S_ESTABLISH_INTRO)
     routelen++;
 
-  num_acceptable_routers = count_acceptable_routers(routers);
+  num_acceptable_routers = count_acceptable_nodes(nodes);
 
   log_debug(LD_CIRC,"Chosen route length %d (%d/%d routers suitable).",
-            routelen, num_acceptable_routers, smartlist_len(routers));
+            routelen, num_acceptable_routers, smartlist_len(nodes));
 
   if (num_acceptable_routers < 2) {
     log_info(LD_CIRC,
@@ -2475,12 +2470,12 @@ circuit_all_predicted_ports_handled(time_t now, int *need_uptime,
   return enough;
 }
 
-/** Return 1 if <b>router</b> can handle one or more of the ports in
+/** Return 1 if <b>node</b> can handle one or more of the ports in
  * <b>needed_ports</b>, else return 0.
  */
 static int
-router_handles_some_port(const routerinfo_t *router, smartlist_t *needed_ports)
-{
+node_handles_some_port(const node_t *node, smartlist_t *needed_ports)
+{ /* XXXX MOVE */
   int i;
   uint16_t port;
 
@@ -2490,7 +2485,10 @@ router_handles_some_port(const routerinfo_t *router, smartlist_t *needed_ports)
        needed_ports is explicitly a smartlist of uint16_t's */
     port = *(uint16_t *)smartlist_get(needed_ports, i);
     tor_assert(port);
-    r = compare_addr_to_addr_policy(0, port, router->exit_policy);
+    if (node)
+      r = compare_addr_to_node_policy(0, port, node);
+    else
+      continue;
     if (r != ADDR_POLICY_REJECTED && r != ADDR_POLICY_PROBABLY_REJECTED)
       return 1;
   }
@@ -2523,18 +2521,17 @@ ap_stream_wants_exit_attention(connection_t *conn)
  *
  * Return NULL if we can't find any suitable routers.
  */
-static const routerinfo_t *
-choose_good_exit_server_general(routerlist_t *dir, int need_uptime,
-                                int need_capacity)
+static const node_t *
+choose_good_exit_server_general(int need_uptime, int need_capacity)
 {
   int *n_supported;
-  int i;
   int n_pending_connections = 0;
   smartlist_t *connections;
   int best_support = -1;
   int n_best_support=0;
-  const routerinfo_t *router;
   or_options_t *options = get_options();
+  const smartlist_t *the_nodes;
+  const node_t *node=NULL;
 
   connections = get_connection_array();
 
@@ -2555,10 +2552,11 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime,
    *
    * -1 means "Don't use this router at all."
    */
-  n_supported = tor_malloc(sizeof(int)*smartlist_len(dir->routers));
-  for (i = 0; i < smartlist_len(dir->routers); ++i) {/* iterate over routers */
-    router = smartlist_get(dir->routers, i);
-    if (router_is_me(router)) {
+  the_nodes = nodelist_get_list();
+  n_supported = tor_malloc(sizeof(int)*smartlist_len(the_nodes));
+  SMARTLIST_FOREACH_BEGIN(the_nodes, const node_t *, node) {
+    const int i = node_sl_idx;
+    if (router_digest_is_me(node->identity)) {
       n_supported[i] = -1;
 //      log_fn(LOG_DEBUG,"Skipping node %s -- it's me.", router->nickname);
       /* XXX there's probably a reverse predecessor attack here, but
@@ -2566,13 +2564,15 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime,
        */
       continue;
     }
-    if (!router->is_running || router->is_bad_exit) {
+    if (!node->ri && !(node->rs && node->md))
+      continue;
+    if (!node->is_running || node->is_bad_exit) {
       n_supported[i] = -1;
       continue; /* skip routers that are known to be down or bad exits */
     }
-    if (router_is_unreliable(router, need_uptime, need_capacity, 0) &&
+    if (node_is_unreliable(node, need_uptime, need_capacity, 0) &&
         (!options->ExitNodes ||
-         !routerset_contains_router(options->ExitNodes, router))) {
+         !routerset_contains_node(options->ExitNodes, node))) {
       /* FFFF Someday, differentiate between a routerset that names
        * routers, and a routerset that names countries, and only do this
        * check if they've asked for specific exit relays. Or if the country
@@ -2581,18 +2581,19 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime,
       continue; /* skip routers that are not suitable, unless we have
                  * ExitNodes set, in which case we asked for it */
     }
-    if (!(router->is_valid || options->_AllowInvalid & ALLOW_INVALID_EXIT)) {
+    if (!(node->is_valid || options->_AllowInvalid & ALLOW_INVALID_EXIT)) {
       /* if it's invalid and we don't want it */
       n_supported[i] = -1;
 //      log_fn(LOG_DEBUG,"Skipping node %s (index %d) -- invalid router.",
 //             router->nickname, i);
       continue; /* skip invalid routers */
     }
-    if (options->ExcludeSingleHopRelays && router->allow_single_hop_exits) {
+    if (options->ExcludeSingleHopRelays &&
+        node_allows_single_hop_exits(node)) {
       n_supported[i] = -1;
       continue;
     }
-    if (router_exit_policy_rejects_all(router)) {
+    if (node_exit_policy_rejects_all(node)) {
       n_supported[i] = -1;
 //      log_fn(LOG_DEBUG,"Skipping node %s (index %d) -- it rejects all.",
 //             router->nickname, i);
@@ -2603,7 +2604,7 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime,
     SMARTLIST_FOREACH_BEGIN(connections, connection_t *, conn) {
       if (!ap_stream_wants_exit_attention(conn))
         continue; /* Skip everything but APs in CIRCUIT_WAIT */
-      if (connection_ap_can_use_exit(TO_EDGE_CONN(conn), router, 1)) {
+      if (connection_ap_can_use_exit(TO_EDGE_CONN(conn), node, 1)) {
         ++n_supported[i];
 //        log_fn(LOG_DEBUG,"%s is supported. n_supported[%d] now %d.",
 //               router->nickname, i, n_supported[i]);
@@ -2628,7 +2629,7 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime,
        * count of equally good routers.*/
       ++n_best_support;
     }
-  }
+  } SMARTLIST_FOREACH_END(node);
   log_info(LD_CIRC,
            "Found %d servers that might support %d/%d pending connections.",
            n_best_support, best_support >= 0 ? best_support : 0,
@@ -2639,18 +2640,19 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime,
   if (best_support > 0) {
     smartlist_t *supporting = smartlist_create(), *use = smartlist_create();
 
-    for (i = 0; i < smartlist_len(dir->routers); i++)
-      if (n_supported[i] == best_support)
-        smartlist_add(supporting, smartlist_get(dir->routers, i));
+    SMARTLIST_FOREACH(the_nodes, const node_t *, node, {
+      if (n_supported[node_sl_idx] == best_support)
+        smartlist_add(supporting, (void*)node);
+    });
 
-    routersets_get_disjunction(use, supporting, options->ExitNodes,
+    routersets_get_node_disjunction(use, supporting, options->ExitNodes,
                                options->_ExcludeExitNodesUnion, 1);
     if (smartlist_len(use) == 0 && options->ExitNodes &&
         !options->StrictNodes) { /* give up on exitnodes and try again */
-      routersets_get_disjunction(use, supporting, NULL,
+      routersets_get_node_disjunction(use, supporting, NULL,
                                  options->_ExcludeExitNodesUnion, 1);
     }
-    router = routerlist_sl_choose_by_bandwidth(use, WEIGHT_FOR_EXIT);
+    node = node_sl_choose_by_bandwidth(use, WEIGHT_FOR_EXIT);
     smartlist_free(use);
     smartlist_free(supporting);
   } else {
@@ -2669,7 +2671,7 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime,
                  need_capacity?", fast":"",
                  need_uptime?", stable":"");
         tor_free(n_supported);
-        return choose_good_exit_server_general(dir, 0, 0);
+        return choose_good_exit_server_general(0, 0);
       }
       log_notice(LD_CIRC, "All routers are down or won't exit%s -- "
                  "choosing a doomed exit at random.",
@@ -2681,28 +2683,27 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime,
     for (attempt = 0; attempt < 2; attempt++) {
       /* try once to pick only from routers that satisfy a needed port,
        * then if there are none, pick from any that support exiting. */
-      for (i = 0; i < smartlist_len(dir->routers); i++) {
-        router = smartlist_get(dir->routers, i);
-        if (n_supported[i] != -1 &&
-            (attempt || router_handles_some_port(router, needed_ports))) {
+      SMARTLIST_FOREACH_BEGIN(the_nodes, const node_t *, node) {
+        if (n_supported[node_sl_idx] != -1 &&
+            (attempt || node_handles_some_port(node, needed_ports))) {
 //          log_fn(LOG_DEBUG,"Try %d: '%s' is a possibility.",
 //                 try, router->nickname);
-          smartlist_add(supporting, (void*)router);
+          smartlist_add(supporting, (void*)node);
         }
-      }
+      } SMARTLIST_FOREACH_END(node);
 
-      routersets_get_disjunction(use, supporting, options->ExitNodes,
+      routersets_get_node_disjunction(use, supporting, options->ExitNodes,
                                  options->_ExcludeExitNodesUnion, 1);
       if (smartlist_len(use) == 0 && options->ExitNodes &&
           !options->StrictNodes) { /* give up on exitnodes and try again */
-        routersets_get_disjunction(use, supporting, NULL,
+        routersets_get_node_disjunction(use, supporting, NULL,
                                    options->_ExcludeExitNodesUnion, 1);
       }
       /* FFF sometimes the above results in null, when the requested
        * exit node is considered down by the consensus. we should pick
        * it anyway, since the user asked for it. */
-      router = routerlist_sl_choose_by_bandwidth(use, WEIGHT_FOR_EXIT);
-      if (router)
+      node = node_sl_choose_by_bandwidth(use, WEIGHT_FOR_EXIT);
+      if (node)
         break;
       smartlist_clear(supporting);
       smartlist_clear(use);
@@ -2714,9 +2715,9 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime,
   }
 
   tor_free(n_supported);
-  if (router) {
-    log_info(LD_CIRC, "Chose exit server '%s'", router->nickname);
-    return router;
+  if (node) {
+    log_info(LD_CIRC, "Chose exit server '%s'", node_get_nickname(node));
+    return node;
   }
   if (options->ExitNodes && options->StrictNodes) {
     log_warn(LD_CIRC,
@@ -2736,12 +2737,12 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime,
  * For client-side rendezvous circuits, choose a random node, weighted
  * toward the preferences in 'options'.
  */
-static const routerinfo_t *
-choose_good_exit_server(uint8_t purpose, routerlist_t *dir,
+static const node_t *
+choose_good_exit_server(uint8_t purpose,
                         int need_uptime, int need_capacity, int is_internal)
 {
   or_options_t *options = get_options();
-  router_crn_flags_t flags = 0;
+  router_crn_flags_t flags = CRN_NEED_DESC;
   if (need_uptime)
     flags |= CRN_NEED_UPTIME;
   if (need_capacity)
@@ -2754,7 +2755,7 @@ choose_good_exit_server(uint8_t purpose, routerlist_t *dir,
       if (is_internal) /* pick it like a middle hop */
         return router_choose_random_node(NULL, options->ExcludeNodes, flags);
       else
-        return choose_good_exit_server_general(dir,need_uptime,need_capacity);
+        return choose_good_exit_server_general(need_uptime,need_capacity);
     case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
       if (options->_AllowInvalid & ALLOW_INVALID_RENDEZVOUS)
         flags |= CRN_ALLOW_INVALID;
@@ -2835,13 +2836,12 @@ static int
 onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit)
 {
   cpath_build_state_t *state = circ->build_state;
-  routerlist_t *rl = router_get_routerlist();
 
   if (state->onehop_tunnel) {
     log_debug(LD_CIRC, "Launching a one-hop circuit for dir tunnel.");
     state->desired_path_len = 1;
   } else {
-    int r = new_route_len(circ->_base.purpose, exit, rl->routers);
+    int r = new_route_len(circ->_base.purpose, exit, nodelist_get_list());
     if (r < 1) /* must be at least 1 */
       return -1;
     state->desired_path_len = r;
@@ -2852,14 +2852,14 @@ onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit)
     log_info(LD_CIRC,"Using requested exit node '%s'", exit->nickname);
     exit = extend_info_dup(exit);
   } else { /* we have to decide one */
-    const routerinfo_t *router =
-      choose_good_exit_server(circ->_base.purpose, rl, state->need_uptime,
+    const node_t *node =
+      choose_good_exit_server(circ->_base.purpose, state->need_uptime,
                               state->need_capacity, state->is_internal);
-    if (!router) {
+    if (!node) {
       log_warn(LD_CIRC,"failed to choose an exit server");
       return -1;
     }
-    exit = extend_info_from_router(router);
+    exit = extend_info_from_node(node);
   }
   state->chosen_exit = exit;
   return 0;
@@ -2910,35 +2910,30 @@ circuit_extend_to_new_exit(origin_circuit_t *circ, extend_info_t *exit)
  * and available for building circuits through.
  */
 static int
-count_acceptable_routers(smartlist_t *routers)
+count_acceptable_nodes(smartlist_t *nodes)
 {
-  int i, n;
   int num=0;
-  routerinfo_t *r;
 
-  n = smartlist_len(routers);
-  for (i=0;i<n;i++) {
-    r = smartlist_get(routers, i);
-//    log_debug(LD_CIRC,
+  SMARTLIST_FOREACH_BEGIN(nodes, const node_t *, node) {
+    //    log_debug(LD_CIRC,
 //              "Contemplating whether router %d (%s) is a new option.",
 //              i, r->nickname);
-    if (r->is_running == 0) {
+    if (! node->is_running)
 //      log_debug(LD_CIRC,"Nope, the directory says %d is not running.",i);
-      goto next_i_loop;
-    }
-    if (r->is_valid == 0) {
+      continue;
+    if (! node->is_valid)
 //      log_debug(LD_CIRC,"Nope, the directory says %d is not valid.",i);
-      goto next_i_loop;
+      continue;
+    if (! node_has_descriptor(node))
+      continue;
       /* XXX This clause makes us count incorrectly: if AllowInvalidRouters
        * allows this node in some places, then we're getting an inaccurate
        * count. For now, be conservative and don't count it. But later we
        * should try to be smarter. */
-    }
-    num++;
+    ++num;
+  } SMARTLIST_FOREACH_END(node);
+
 //    log_debug(LD_CIRC,"I like %d. num_acceptable_routers now %d.",i, num);
-    next_i_loop:
-      ; /* C requires an explicit statement after the label */
-  }
 
   return num;
 }
@@ -2966,31 +2961,31 @@ onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop)
  * circuit. In particular, make sure we don't pick the exit node or its
  * family, and make sure we don't duplicate any previous nodes or their
  * families. */
-static const routerinfo_t *
+static const node_t *
 choose_good_middle_server(uint8_t purpose,
                           cpath_build_state_t *state,
                           crypt_path_t *head,
                           int cur_len)
 {
   int i;
-  const routerinfo_t *r, *choice;
+  const node_t *r, *choice;
   crypt_path_t *cpath;
   smartlist_t *excluded;
   or_options_t *options = get_options();
-  router_crn_flags_t flags = 0;
+  router_crn_flags_t flags = CRN_NEED_DESC;
   tor_assert(_CIRCUIT_PURPOSE_MIN <= purpose &&
              purpose <= _CIRCUIT_PURPOSE_MAX);
 
   log_debug(LD_CIRC, "Contemplating intermediate hop: random choice.");
   excluded = smartlist_create();
-  if ((r = build_state_get_exit_router(state))) {
+  if ((r = build_state_get_exit_node(state))) {
     smartlist_add(excluded, (void*) r);
-    routerlist_add_family(excluded, r);
+    nodelist_add_node_family(excluded, r);
   }
   for (i = 0, cpath = head; i < cur_len; ++i, cpath=cpath->next) {
-    if ((r = router_get_by_digest(cpath->extend_info->identity_digest))) {
+    if ((r = node_get_by_id(cpath->extend_info->identity_digest))) {
       smartlist_add(excluded, (void*)r);
-      routerlist_add_family(excluded, r);
+      nodelist_add_node_family(excluded, r);
     }
   }
 
@@ -3013,13 +3008,14 @@ choose_good_middle_server(uint8_t purpose,
  * If <b>state</b> is NULL, we're choosing a router to serve as an entry
  * guard, not for any particular circuit.
  */
-static const routerinfo_t *
+static const node_t *
 choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state)
 {
-  const routerinfo_t *r, *choice;
+  const node_t *choice;
   smartlist_t *excluded;
   or_options_t *options = get_options();
-  router_crn_flags_t flags = CRN_NEED_GUARD;
+  router_crn_flags_t flags = CRN_NEED_GUARD|CRN_NEED_DESC;
+  const node_t *node;
 
   if (state && options->UseEntryGuards &&
       (purpose != CIRCUIT_PURPOSE_TESTING || options->BridgeRelay)) {
@@ -3028,29 +3024,26 @@ choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state)
 
   excluded = smartlist_create();
 
-  if (state && (r = build_state_get_exit_router(state))) {
-    smartlist_add(excluded, (void*)r);
-    routerlist_add_family(excluded, r);
+  if (state && (node = build_state_get_exit_node(state))) {
+    smartlist_add(excluded, (void*)node);
+    nodelist_add_node_family(excluded, node);
   }
   if (firewall_is_fascist_or()) {
     /*XXXX This could slow things down a lot; use a smarter implementation */
     /* exclude all ORs that listen on the wrong port, if anybody notices. */
-    routerlist_t *rl = router_get_routerlist();
-    int i;
-
-    for (i=0; i < smartlist_len(rl->routers); i++) {
-      r = smartlist_get(rl->routers, i);
-      if (!fascist_firewall_allows_or(r))
-        smartlist_add(excluded, (void*)r);
-    }
+    smartlist_t *nodes = nodelist_get_list();
+    SMARTLIST_FOREACH(nodes, const node_t *,node, {
+      if (!fascist_firewall_allows_node(node))
+        smartlist_add(excluded, (void*)node);
+    });
   }
   /* and exclude current entry guards, if applicable */
   if (options->UseEntryGuards && entry_guards) {
     SMARTLIST_FOREACH(entry_guards, entry_guard_t *, entry,
       {
-        if ((r = router_get_by_digest(entry->identity))) {
-          smartlist_add(excluded, (void*)r);
-          routerlist_add_family(excluded, r);
+        if ((node = node_get_by_id(entry->identity))) {
+          smartlist_add(excluded, (void*)node);
+          nodelist_add_node_family(excluded, node);
         }
       });
   }
@@ -3106,14 +3099,14 @@ onion_extend_cpath(origin_circuit_t *circ)
   if (cur_len == state->desired_path_len - 1) { /* Picking last node */
     info = extend_info_dup(state->chosen_exit);
   } else if (cur_len == 0) { /* picking first node */
-    const routerinfo_t *r = choose_good_entry_server(purpose, state);
+    const node_t *r = choose_good_entry_server(purpose, state);
     if (r)
-      info = extend_info_from_router(r);
+      info = extend_info_from_node(r);
   } else {
-    const routerinfo_t *r =
+    const node_t *r =
       choose_good_middle_server(purpose, state, circ->cpath, cur_len);
     if (r)
-      info = extend_info_from_router(r);
+      info = extend_info_from_node(r);
   }
 
   if (!info) {
@@ -3181,6 +3174,29 @@ extend_info_from_router(const routerinfo_t *r)
                            r->onion_pkey, &addr, r->or_port);
 }
 
+/** Allocate and return a new extend_info that can be used to build a ircuit
+ * to or through the node <b>node</b>.  May return NULL if there is not
+ * enough info about <b>node</b> to extend to it--for example, if there
+ * is no routerinfo_t or microdesc_t.
+ **/
+extend_info_t *
+extend_info_from_node(const node_t *node)
+{
+  if (node->ri) {
+    return extend_info_from_router(node->ri);
+  } else if (node->rs && node->md) {
+    tor_addr_t addr;
+    tor_addr_from_ipv4h(&addr, node->rs->addr);
+    return extend_info_alloc(node->rs->nickname,
+                             node->identity,
+                             node->md->onion_pkey,
+                             &addr,
+                             node->rs->or_port);
+  } else {
+    return NULL;
+  }
+}
+
 /** Release storage held by an extend_info_t struct. */
 void
 extend_info_free(extend_info_t *info)
@@ -3211,12 +3227,12 @@ extend_info_dup(extend_info_t *info)
  * If there is no chosen exit, or if we don't know the routerinfo_t for
  * the chosen exit, return NULL.
  */
-const routerinfo_t *
-build_state_get_exit_router(cpath_build_state_t *state)
+const node_t *
+build_state_get_exit_node(cpath_build_state_t *state)
 {
   if (!state || !state->chosen_exit)
     return NULL;
-  return router_get_by_digest(state->chosen_exit->identity_digest);
+  return node_get_by_id(state->chosen_exit->identity_digest);
 }
 
 /** Return the nickname for the chosen exit router in <b>state</b>. If
@@ -3238,9 +3254,8 @@ build_state_get_exit_nickname(cpath_build_state_t *state)
  *
  * If it's not usable, set *<b>reason</b> to a static string explaining why.
  */
-/*XXXX take a routerstatus, not a routerinfo. */
 static int
-entry_guard_set_status(entry_guard_t *e, const routerinfo_t *ri,
+entry_guard_set_status(entry_guard_t *e, const node_t *node,
                        time_t now, or_options_t *options, const char **reason)
 {
   char buf[HEX_DIGEST_LEN+1];
@@ -3249,16 +3264,17 @@ entry_guard_set_status(entry_guard_t *e, const routerinfo_t *ri,
   *reason = NULL;
 
   /* Do we want to mark this guard as bad? */
-  if (!ri)
+  if (!node)
     *reason = "unlisted";
-  else if (!ri->is_running)
+  else if (!node->is_running)
     *reason = "down";
-  else if (options->UseBridges && ri->purpose != ROUTER_PURPOSE_BRIDGE)
+  else if (options->UseBridges && (!node->ri ||
+                                   node->ri->purpose != ROUTER_PURPOSE_BRIDGE))
     *reason = "not a bridge";
-  else if (!options->UseBridges && !ri->is_possible_guard &&
-           !routerset_contains_router(options->EntryNodes,ri))
+  else if (!options->UseBridges && !node->is_possible_guard &&
+           !routerset_contains_node(options->EntryNodes,node))
     *reason = "not recommended as a guard";
-  else if (routerset_contains_router(options->ExcludeNodes, ri))
+  else if (routerset_contains_node(options->ExcludeNodes, node))
     *reason = "excluded";
 
   if (*reason && ! e->bad_since) {
@@ -3302,7 +3318,7 @@ entry_is_time_to_retry(entry_guard_t *e, time_t now)
     return now > (e->last_attempted + 36*60*60);
 }
 
-/** Return the router corresponding to <b>e</b>, if <b>e</b> is
+/** Return the node corresponding to <b>e</b>, if <b>e</b> is
  * working well enough that we are willing to use it as an entry
  * right now. (Else return NULL.) In particular, it must be
  * - Listed as either up or never yet contacted;
@@ -3316,11 +3332,11 @@ entry_is_time_to_retry(entry_guard_t *e, time_t now)
  *
  * If the answer is no, set *<b>msg</b> to an explanation of why.
  */
-static INLINE const routerinfo_t *
+static INLINE const node_t *
 entry_is_live(entry_guard_t *e, int need_uptime, int need_capacity,
               int assume_reachable, const char **msg)
 {
-  const routerinfo_t *r;
+  const node_t *node;
   or_options_t *options = get_options();
   tor_assert(msg);
 
@@ -3334,33 +3350,36 @@ entry_is_live(entry_guard_t *e, int need_uptime, int need_capacity,
     *msg = "unreachable";
     return NULL;
   }
-  r = router_get_by_digest(e->identity);
-  if (!r) {
+  node = node_get_by_id(e->identity);
+  if (!node && !node_has_descriptor(node)) {
     *msg = "no descriptor";
     return NULL;
   }
-  if (get_options()->UseBridges && r->purpose != ROUTER_PURPOSE_BRIDGE) {
-    *msg = "not a bridge";
-    return NULL;
-  }
-  if (!get_options()->UseBridges && r->purpose != ROUTER_PURPOSE_GENERAL) {
-    *msg = "not general-purpose";
-    return NULL;
+  if (get_options()->UseBridges) {
+    if (node_get_purpose(node) != ROUTER_PURPOSE_BRIDGE) {
+      *msg = "not a bridge";
+      return NULL;
+    }
+  } else { /* !get_options()->UseBridges */
+    if (node_get_purpose(node) != ROUTER_PURPOSE_GENERAL) {
+      *msg = "not general-purpose";
+      return NULL;
+    }
   }
   if (options->EntryNodes &&
-      routerset_contains_router(options->EntryNodes, r)) {
+      routerset_contains_node(options->EntryNodes, node)) {
     /* they asked for it, they get it */
     need_uptime = need_capacity = 0;
   }
-  if (router_is_unreliable(r, need_uptime, need_capacity, 0)) {
+  if (node_is_unreliable(node, need_uptime, need_capacity, 0)) {
     *msg = "not fast/stable";
     return NULL;
   }
-  if (!fascist_firewall_allows_or(r)) {
+  if (!fascist_firewall_allows_node(node)) {
     *msg = "unreachable by config";
     return NULL;
   }
-  return r;
+  return node;
 }
 
 /** Return the number of entry guards that we think are usable. */
@@ -3458,15 +3477,15 @@ control_event_guard_deferred(void)
  * If <b>chosen</b> is defined, use that one, and if it's not
  * already in our entry_guards list, put it at the *beginning*.
  * Else, put the one we pick at the end of the list. */
-static const routerinfo_t *
-add_an_entry_guard(routerinfo_t *chosen, int reset_status)
+static const node_t *
+add_an_entry_guard(const node_t *chosen, int reset_status)
 {
-  const routerinfo_t *router;
+  const node_t *node;
   entry_guard_t *entry;
 
   if (chosen) {
-    router = chosen;
-    entry = is_an_entry_guard(router->cache_info.identity_digest);
+    node = chosen;
+    entry = is_an_entry_guard(node->identity);
     if (entry) {
       if (reset_status) {
         entry->bad_since = 0;
@@ -3475,14 +3494,15 @@ add_an_entry_guard(routerinfo_t *chosen, int reset_status)
       return NULL;
     }
   } else {
-    router = choose_good_entry_server(CIRCUIT_PURPOSE_C_GENERAL, NULL);
-    if (!router)
+    node = choose_good_entry_server(CIRCUIT_PURPOSE_C_GENERAL, NULL);
+    if (!node)
       return NULL;
   }
   entry = tor_malloc_zero(sizeof(entry_guard_t));
-  log_info(LD_CIRC, "Chose '%s' as new entry guard.", router->nickname);
-  strlcpy(entry->nickname, router->nickname, sizeof(entry->nickname));
-  memcpy(entry->identity, router->cache_info.identity_digest, DIGEST_LEN);
+  log_info(LD_CIRC, "Chose '%s' as new entry guard.",
+           node_get_nickname(node));
+  strlcpy(entry->nickname, node_get_nickname(node), sizeof(entry->nickname));
+  memcpy(entry->identity, node->identity, DIGEST_LEN);
   /* Choose expiry time smudged over the past month. The goal here
    * is to a) spread out when Tor clients rotate their guards, so they
    * don't all select them on the same day, and b) avoid leaving a
@@ -3497,7 +3517,7 @@ add_an_entry_guard(routerinfo_t *chosen, int reset_status)
   control_event_guard(entry->nickname, entry->identity, "NEW");
   control_event_guard_deferred();
   log_entry_guards(LOG_INFO);
-  return router;
+  return node;
 }
 
 /** If the use of entry guards is configured, choose more entry guards
@@ -3651,7 +3671,7 @@ entry_guards_compute_status(or_options_t *options, time_t now)
   reasons = digestmap_new();
   SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, entry)
     {
-      const routerinfo_t *r = router_get_by_digest(entry->identity);
+      const node_t *r = node_get_by_id(entry->identity);
       const char *reason = NULL;
       if (entry_guard_set_status(entry, r, now, options, &reason))
         changed = 1;
@@ -3672,7 +3692,7 @@ entry_guards_compute_status(or_options_t *options, time_t now)
     SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, entry) {
       const char *reason = digestmap_get(reasons, entry->identity);
       const char *live_msg = "";
-      const routerinfo_t *r = entry_is_live(entry, 0, 1, 0, &live_msg);
+      const node_t *r = entry_is_live(entry, 0, 1, 0, &live_msg);
       log_info(LD_CIRC, "Summary: Entry '%s' is %s, %s%s%s, and %s%s.",
                entry->nickname,
                entry->unreachable_since ? "unreachable" : "reachable",
@@ -3789,7 +3809,7 @@ entry_guard_register_connect_status(const char *digest, int succeeded,
           break;
         if (e->made_contact) {
           const char *msg;
-          const routerinfo_t *r = entry_is_live(e, 0, 1, 1, &msg);
+          const node_t *r = entry_is_live(e, 0, 1, 1, &msg);
           if (r && e->unreachable_since) {
             refuse_conn = 1;
             e->can_retry = 1;
@@ -3830,7 +3850,7 @@ entry_nodes_should_be_added(void)
 static void
 entry_guards_prepend_from_config(or_options_t *options)
 {
-  smartlist_t *entry_routers, *entry_fps;
+  smartlist_t *entry_nodes, *entry_fps;
   smartlist_t *old_entry_guards_on_list, *old_entry_guards_not_on_list;
   tor_assert(entry_guards);
 
@@ -3850,7 +3870,7 @@ entry_guards_prepend_from_config(or_options_t *options)
     tor_free(string);
   }
 
-  entry_routers = smartlist_create();
+  entry_nodes = smartlist_create();
   entry_fps = smartlist_create();
   old_entry_guards_on_list = smartlist_create();
   old_entry_guards_not_on_list = smartlist_create();
@@ -3863,9 +3883,9 @@ entry_guards_prepend_from_config(or_options_t *options)
    *  Perhaps we should do this calculation once whenever the list of routers
    *  changes or the entrynodes setting changes.
    */
-  routerset_get_all_routers(entry_routers, options->EntryNodes, 0);
-  SMARTLIST_FOREACH(entry_routers, routerinfo_t *, ri,
-                    smartlist_add(entry_fps,ri->cache_info.identity_digest));
+  routerset_get_all_nodes(entry_nodes, options->EntryNodes, 0);
+  SMARTLIST_FOREACH(entry_nodes, const node_t *,node,
+                    smartlist_add(entry_fps, (void*)node->identity));
   SMARTLIST_FOREACH(entry_guards, entry_guard_t *, e, {
     if (smartlist_digest_isin(entry_fps, e->identity))
       smartlist_add(old_entry_guards_on_list, e);
@@ -3874,9 +3894,9 @@ entry_guards_prepend_from_config(or_options_t *options)
   });
 
   /* Remove all currently configured entry guards from entry_routers. */
-  SMARTLIST_FOREACH(entry_routers, routerinfo_t *, ri, {
-    if (is_an_entry_guard(ri->cache_info.identity_digest)) {
-      SMARTLIST_DEL_CURRENT(entry_routers, ri);
+  SMARTLIST_FOREACH(entry_nodes, const node_t *, node, {
+    if (is_an_entry_guard(node->identity)) {
+      SMARTLIST_DEL_CURRENT(entry_nodes, node);
     }
   });
 
@@ -3885,8 +3905,8 @@ entry_guards_prepend_from_config(or_options_t *options)
   /* First, the previously configured guards that are in EntryNodes. */
   smartlist_add_all(entry_guards, old_entry_guards_on_list);
   /* Next, the rest of EntryNodes */
-  SMARTLIST_FOREACH(entry_routers, routerinfo_t *, ri, {
-    add_an_entry_guard(ri, 0);
+  SMARTLIST_FOREACH(entry_nodes, const node_t *, node, {
+    add_an_entry_guard(node, 0);
   });
   /* Finally, the remaining previously configured guards that are not in
    * EntryNodes, unless we're strict in which case we drop them */
@@ -3897,7 +3917,7 @@ entry_guards_prepend_from_config(or_options_t *options)
     smartlist_add_all(entry_guards, old_entry_guards_not_on_list);
   }
 
-  smartlist_free(entry_routers);
+  smartlist_free(entry_nodes);
   smartlist_free(entry_fps);
   smartlist_free(old_entry_guards_on_list);
   smartlist_free(old_entry_guards_not_on_list);
@@ -3935,22 +3955,22 @@ entry_list_is_totally_static(or_options_t *options)
  * make sure not to pick this circuit's exit or any node in the
  * exit's family. If <b>state</b> is NULL, we're looking for a random
  * guard (likely a bridge). */
-const routerinfo_t *
+const node_t *
 choose_random_entry(cpath_build_state_t *state)
 {
   or_options_t *options = get_options();
   smartlist_t *live_entry_guards = smartlist_create();
   smartlist_t *exit_family = smartlist_create();
-  const routerinfo_t *chosen_exit =
-    state?build_state_get_exit_router(state) : NULL;
-  const routerinfo_t *r = NULL;
+  const node_t *chosen_exit =
+    state?build_state_get_exit_node(state) : NULL;
+  const node_t *node = NULL;
   int need_uptime = state ? state->need_uptime : 0;
   int need_capacity = state ? state->need_capacity : 0;
   int preferred_min, consider_exit_family = 0;
 
   if (chosen_exit) {
     smartlist_add(exit_family, (void*) chosen_exit);
-    routerlist_add_family(exit_family, chosen_exit);
+    nodelist_add_node_family(exit_family, chosen_exit);
     consider_exit_family = 1;
   }
 
@@ -3968,13 +3988,13 @@ choose_random_entry(cpath_build_state_t *state)
   smartlist_clear(live_entry_guards);
   SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, entry) {
       const char *msg;
-      r = entry_is_live(entry, need_uptime, need_capacity, 0, &msg);
-      if (!r)
+      node = entry_is_live(entry, need_uptime, need_capacity, 0, &msg);
+      if (!node)
         continue; /* down, no point */
-      if (consider_exit_family && smartlist_isin(exit_family, r))
+      if (consider_exit_family && smartlist_isin(exit_family, node))
         continue; /* avoid relays that are family members of our exit */
       if (options->EntryNodes &&
-          !routerset_contains_router(options->EntryNodes, r)) {
+          !routerset_contains_node(options->EntryNodes, node)) {
         /* We've come to the end of our preferred entry nodes. */
         if (smartlist_len(live_entry_guards))
           goto choose_and_finish; /* only choose from the ones we like */
@@ -3987,7 +4007,7 @@ choose_random_entry(cpath_build_state_t *state)
                    "No relays from EntryNodes available. Using others.");
         }
       }
-      smartlist_add(live_entry_guards, (void*)r);
+      smartlist_add(live_entry_guards, (void*)node);
       if (!entry->made_contact) {
         /* Always start with the first not-yet-contacted entry
          * guard. Otherwise we might add several new ones, pick
@@ -4017,8 +4037,8 @@ choose_random_entry(cpath_build_state_t *state)
       /* XXX if guard doesn't imply fast and stable, then we need
        * to tell add_an_entry_guard below what we want, or it might
        * be a long time til we get it. -RD */
-      r = add_an_entry_guard(NULL, 0);
-      if (r) {
+      node = add_an_entry_guard(NULL, 0);
+      if (node) {
         entry_guards_changed();
         /* XXX we start over here in case the new node we added shares
          * a family with our exit node. There's a chance that we'll just
@@ -4028,16 +4048,16 @@ choose_random_entry(cpath_build_state_t *state)
         goto retry;
       }
     }
-    if (!r && need_uptime) {
+    if (!node && need_uptime) {
       need_uptime = 0; /* try without that requirement */
       goto retry;
     }
-    if (!r && need_capacity) {
+    if (!node && need_capacity) {
       /* still no? last attempt, try without requiring capacity */
       need_capacity = 0;
       goto retry;
     }
-    if (!r && entry_list_is_constrained(options) && consider_exit_family) {
+    if (!node && entry_list_is_constrained(options) && consider_exit_family) {
       /* still no? if we're using bridges or have strictentrynodes
        * set, and our chosen exit is in the same family as all our
        * bridges/entry guards, then be flexible about families. */
@@ -4051,16 +4071,16 @@ choose_random_entry(cpath_build_state_t *state)
   if (entry_list_is_constrained(options)) {
     /* We need to weight by bandwidth, because our bridges or entryguards
      * were not already selected proportional to their bandwidth. */
-    r = routerlist_sl_choose_by_bandwidth(live_entry_guards, WEIGHT_FOR_GUARD);
+    node = node_sl_choose_by_bandwidth(live_entry_guards, WEIGHT_FOR_GUARD);
   } else {
     /* We choose uniformly at random here, because choose_good_entry_server()
      * already weights its choices by bandwidth, so we don't want to
      * *double*-weight our guard selection. */
-    r = smartlist_choose(live_entry_guards);
+    node = smartlist_choose(live_entry_guards);
   }
   smartlist_free(live_entry_guards);
   smartlist_free(exit_family);
-  return r;
+  return node;
 }
 
 /** Parse <b>state</b> and learn about the entry guards it describes.
@@ -4307,7 +4327,7 @@ getinfo_helper_entry_guards(control_connection_t *conn,
         char *c = tor_malloc(len);
         const char *status = NULL;
         time_t when = 0;
-        const routerinfo_t *ri;
+        const node_t *node;
 
         if (!e->made_contact) {
           status = "never-connected";
@@ -4318,9 +4338,9 @@ getinfo_helper_entry_guards(control_connection_t *conn,
           status = "up";
         }
 
-        ri = router_get_by_digest(e->identity);
-        if (ri) {
-          router_get_verbose_nickname(nbuf, ri);
+        node = node_get_by_id(e->identity);
+        if (node) {
+          node_get_verbose_nickname(node, nbuf);
         } else {
           nbuf[0] = '$';
           base16_encode(nbuf+1, sizeof(nbuf)-1, e->identity, DIGEST_LEN);
@@ -4566,14 +4586,17 @@ learned_bridge_descriptor(routerinfo_t *ri, int from_cache)
     int first = !any_bridge_descriptors_known();
     bridge_info_t *bridge = get_configured_bridge_by_routerinfo(ri);
     time_t now = time(NULL);
-    ri->is_running = 1;
+    router_set_status(ri->cache_info.identity_digest, 1);
 
     if (bridge) { /* if we actually want to use this one */
+      const node_t *node;
       /* it's here; schedule its re-fetch for a long time from now. */
       if (!from_cache)
         download_status_reset(&bridge->fetch_status);
 
-      add_an_entry_guard(ri, 1);
+      node = node_get_by_id(ri->cache_info.identity_digest);
+      tor_assert(node);
+      add_an_entry_guard(node, 1);
       log_notice(LD_DIR, "new bridge descriptor '%s' (%s)", ri->nickname,
                  from_cache ? "cached" : "fresh");
       /* set entry->made_contact so if it goes down we don't drop it from
@@ -4627,18 +4650,18 @@ any_pending_bridge_descriptor_fetches(void)
 static int
 entries_retry_helper(or_options_t *options, int act)
 {
-  routerinfo_t *ri;
+  const node_t *node;
   int any_known = 0;
   int any_running = 0;
-  int purpose = options->UseBridges ?
-                  ROUTER_PURPOSE_BRIDGE : ROUTER_PURPOSE_GENERAL;
+  int need_bridges = options->UseBridges != 0;
   if (!entry_guards)
     entry_guards = smartlist_create();
   SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, e) {
-      ri = router_get_mutable_by_digest(e->identity);
-      if (ri && ri->purpose == purpose) {
+      node = node_get_by_id(e->identity);
+      if (node && node_has_descriptor(node) &&
+          node_is_bridge(node) == need_bridges) {
         any_known = 1;
-        if (ri->is_running)
+        if (node->is_running)
           any_running = 1; /* some entry is both known and running */
         else if (act) {
           /* Mark all current connections to this OR as unhealthy, since
@@ -4647,10 +4670,10 @@ entries_retry_helper(or_options_t *options, int act)
            * the node down and undermine the retry attempt. We mark even
            * the established conns, since if the network just came back
            * we'll want to attach circuits to fresh conns. */
-          connection_or_set_bad_connections(ri->cache_info.identity_digest, 1);
+          connection_or_set_bad_connections(node->identity, 1);
 
           /* mark this entry node for retry */
-          router_set_status(ri->cache_info.identity_digest, 1);
+          router_set_status(node->identity, 1);
           e->can_retry = 1;
           e->bad_since = 0;
         }
diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h
index 59e818d..7c125e9 100644
--- a/src/or/circuitbuild.h
+++ b/src/or/circuitbuild.h
@@ -45,9 +45,10 @@ extend_info_t *extend_info_alloc(const char *nickname, const char *digest,
                                  crypto_pk_env_t *onion_key,
                                  const tor_addr_t *addr, uint16_t port);
 extend_info_t *extend_info_from_router(const routerinfo_t *r);
+extend_info_t *extend_info_from_node(const node_t *node);
 extend_info_t *extend_info_dup(extend_info_t *info);
 void extend_info_free(extend_info_t *info);
-const routerinfo_t *build_state_get_exit_router(cpath_build_state_t *state);
+const node_t *build_state_get_exit_node(cpath_build_state_t *state);
 const char *build_state_get_exit_nickname(cpath_build_state_t *state);
 
 void entry_guards_compute_status(or_options_t *options, time_t now);
@@ -55,7 +56,7 @@ int entry_guard_register_connect_status(const char *digest, int succeeded,
                                         int mark_relay_status, time_t now);
 void entry_nodes_should_be_added(void);
 int entry_list_is_constrained(or_options_t *options);
-const routerinfo_t *choose_random_entry(cpath_build_state_t *state);
+const node_t *choose_random_entry(cpath_build_state_t *state);
 int entry_guards_parse_state(or_state_t *state, int set, char **msg);
 void entry_guards_update_state(or_state_t *state);
 int getinfo_helper_entry_guards(control_connection_t *conn,
diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c
index 96a06c0..da6d329 100644
--- a/src/or/circuitlist.c
+++ b/src/or/circuitlist.c
@@ -19,6 +19,7 @@
 #include "connection_or.h"
 #include "control.h"
 #include "networkstatus.h"
+#include "nodelist.h"
 #include "onion.h"
 #include "relay.h"
 #include "rendclient.h"
@@ -946,15 +947,15 @@ circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info,
         if (info) {
           /* need to make sure we don't duplicate hops */
           crypt_path_t *hop = circ->cpath;
-          const routerinfo_t *ri1 = router_get_by_digest(info->identity_digest);
+          const node_t *ri1 = node_get_by_id(info->identity_digest);
           do {
-            const routerinfo_t *ri2;
+            const node_t *ri2;
             if (!memcmp(hop->extend_info->identity_digest,
                         info->identity_digest, DIGEST_LEN))
               goto next;
             if (ri1 &&
-                (ri2 = router_get_by_digest(hop->extend_info->identity_digest))
-                && routers_in_same_family(ri1, ri2))
+                (ri2 = node_get_by_id(hop->extend_info->identity_digest))
+                && nodes_in_same_family(ri1, ri2))
               goto next;
             hop=hop->next;
           } while (hop!=circ->cpath);
diff --git a/src/or/circuituse.c b/src/or/circuituse.c
index a2a09be..ca156d4 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -17,6 +17,7 @@
 #include "connection.h"
 #include "connection_edge.h"
 #include "control.h"
+#include "nodelist.h"
 #include "policies.h"
 #include "rendclient.h"
 #include "rendcommon.h"
@@ -43,7 +44,7 @@ circuit_is_acceptable(circuit_t *circ, edge_connection_t *conn,
                       int need_uptime, int need_internal,
                       time_t now)
 {
-  const routerinfo_t *exitrouter;
+  const node_t *exitnode;
   cpath_build_state_t *build_state;
   tor_assert(circ);
   tor_assert(conn);
@@ -85,7 +86,7 @@ circuit_is_acceptable(circuit_t *circ, edge_connection_t *conn,
    * of the one we meant to finish at.
    */
   build_state = TO_ORIGIN_CIRCUIT(circ)->build_state;
-  exitrouter = build_state_get_exit_router(build_state);
+  exitnode = build_state_get_exit_node(build_state);
 
   if (need_uptime && !build_state->need_uptime)
     return 0;
@@ -93,7 +94,7 @@ circuit_is_acceptable(circuit_t *circ, edge_connection_t *conn,
     return 0;
 
   if (purpose == CIRCUIT_PURPOSE_C_GENERAL) {
-    if (!exitrouter && !build_state->onehop_tunnel) {
+    if (!exitnode && !build_state->onehop_tunnel) {
       log_debug(LD_CIRC,"Not considering circuit with unknown router.");
       return 0; /* this circuit is screwed and doesn't know it yet,
                  * or is a rendezvous circuit. */
@@ -127,7 +128,7 @@ circuit_is_acceptable(circuit_t *circ, edge_connection_t *conn,
         return 0;
       }
     }
-    if (exitrouter && !connection_ap_can_use_exit(conn, exitrouter, 0)) {
+    if (exitnode && !connection_ap_can_use_exit(conn, exitnode, 0)) {
       /* can't exit from this router */
       return 0;
     }
@@ -473,7 +474,7 @@ circuit_stream_is_being_handled(edge_connection_t *conn,
                                 uint16_t port, int min)
 {
   circuit_t *circ;
-  const routerinfo_t *exitrouter;
+  const node_t *exitnode;
   int num=0;
   time_t now = time(NULL);
   int need_uptime = smartlist_string_num_isin(get_options()->LongLivedPorts,
@@ -489,14 +490,14 @@ circuit_stream_is_being_handled(edge_connection_t *conn,
       if (build_state->is_internal || build_state->onehop_tunnel)
         continue;
 
-      exitrouter = build_state_get_exit_router(build_state);
-      if (exitrouter && (!need_uptime || build_state->need_uptime)) {
+      exitnode = build_state_get_exit_node(build_state);
+      if (exitnode && (!need_uptime || build_state->need_uptime)) {
         int ok;
         if (conn) {
-          ok = connection_ap_can_use_exit(conn, exitrouter, 0);
+          ok = connection_ap_can_use_exit(conn, exitnode, 0);
         } else {
-          addr_policy_result_t r = compare_addr_to_addr_policy(
-              0, port, exitrouter->exit_policy);
+          addr_policy_result_t r;
+          r = compare_addr_to_node_policy(0, port, exitnode);
           ok = r != ADDR_POLICY_REJECTED && r != ADDR_POLICY_PROBABLY_REJECTED;
         }
         if (ok) {
@@ -563,7 +564,7 @@ circuit_predict_and_launch_new(void)
     log_info(LD_CIRC,
              "Have %d clean circs (%d internal), need another exit circ.",
              num, num_internal);
-    circuit_launch_by_router(CIRCUIT_PURPOSE_C_GENERAL, NULL, flags);
+    circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, flags);
     return;
   }
 
@@ -575,7 +576,7 @@ circuit_predict_and_launch_new(void)
              "Have %d clean circs (%d internal), need another internal "
              "circ for my hidden service.",
              num, num_internal);
-    circuit_launch_by_router(CIRCUIT_PURPOSE_C_GENERAL, NULL, flags);
+    circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, flags);
     return;
   }
 
@@ -593,7 +594,7 @@ circuit_predict_and_launch_new(void)
              "Have %d clean circs (%d uptime-internal, %d internal), need"
              " another hidden service circ.",
              num, num_uptime_internal, num_internal);
-    circuit_launch_by_router(CIRCUIT_PURPOSE_C_GENERAL, NULL, flags);
+    circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, flags);
     return;
   }
 
@@ -606,7 +607,7 @@ circuit_predict_and_launch_new(void)
     flags = CIRCLAUNCH_NEED_CAPACITY;
     log_info(LD_CIRC,
              "Have %d clean circs need another buildtime test circ.", num);
-    circuit_launch_by_router(CIRCUIT_PURPOSE_C_GENERAL, NULL, flags);
+    circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, flags);
     return;
   }
 }
@@ -644,7 +645,7 @@ circuit_build_needed_circs(time_t now)
         circ &&
         circ->timestamp_created + TESTING_CIRCUIT_INTERVAL < now) {
       log_fn(LOG_INFO,"Creating a new testing circuit.");
-      circuit_launch_by_router(CIRCUIT_PURPOSE_C_GENERAL, NULL, 0);
+      circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, 0);
     }
 #endif
   }
@@ -1076,17 +1077,9 @@ static int did_circs_fail_last_period = 0;
 /** Launch a new circuit; see circuit_launch_by_extend_info() for
  * details on arguments. */
 origin_circuit_t *
-circuit_launch_by_router(uint8_t purpose,
-                         const routerinfo_t *exit, int flags)
+circuit_launch(uint8_t purpose, int flags)
 {
-  origin_circuit_t *circ;
-  extend_info_t *info = NULL;
-  if (exit)
-    info = extend_info_from_router(exit);
-  circ = circuit_launch_by_extend_info(purpose, info, flags);
-
-  extend_info_free(info);
-  return circ;
+  return circuit_launch_by_extend_info(purpose, NULL, flags);
 }
 
 /** Launch a new circuit with purpose <b>purpose</b> and exit node
@@ -1256,9 +1249,9 @@ circuit_get_open_circ_or_launch(edge_connection_t *conn,
       uint32_t addr = 0;
       if (tor_inet_aton(conn->socks_request->address, &in))
         addr = ntohl(in.s_addr);
-      if (router_exit_policy_all_routers_reject(addr,
-                                                conn->socks_request->port,
-                                                need_uptime)) {
+      if (router_exit_policy_all_nodes_reject(addr,
+                                              conn->socks_request->port,
+                                              need_uptime)) {
         log_notice(LD_APP,
                    "No Tor server allows exit to %s:%d. Rejecting.",
                    safe_str_client(conn->socks_request->address),
@@ -1267,10 +1260,9 @@ circuit_get_open_circ_or_launch(edge_connection_t *conn,
       }
     } else {
       /* XXXX022 Duplicates checks in connection_ap_handshake_attach_circuit */
-      const routerinfo_t *router =
-        router_get_by_nickname(conn->chosen_exit_name, 1);
+      const node_t *node = node_get_by_nickname(conn->chosen_exit_name, 1);
       int opt = conn->chosen_exit_optional;
-      if (router && !connection_ap_can_use_exit(conn, router, 0)) {
+      if (node && !connection_ap_can_use_exit(conn, node, 0)) {
         log_fn(opt ? LOG_INFO : LOG_WARN, LD_APP,
                "Requested exit point '%s' would refuse request. %s.",
                conn->chosen_exit_name, opt ? "Trying others" : "Closing");
@@ -1318,11 +1310,11 @@ circuit_get_open_circ_or_launch(edge_connection_t *conn,
      */
     if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_GENERAL) {
       if (conn->chosen_exit_name) {
-        const routerinfo_t *r;
+        const node_t *r;
         int opt = conn->chosen_exit_optional;
-        r = router_get_by_nickname(conn->chosen_exit_name, 1);
+        r = node_get_by_nickname(conn->chosen_exit_name, 1);
         if (r) {
-          extend_info = extend_info_from_router(r);
+          extend_info = extend_info_from_node(r);
         } else {
           log_debug(LD_DIR, "considering %d, %s",
                     want_onehop, conn->chosen_exit_name);
@@ -1572,9 +1564,9 @@ connection_ap_handshake_attach_circuit(edge_connection_t *conn)
     origin_circuit_t *circ=NULL;
 
     if (conn->chosen_exit_name) {
-      const routerinfo_t *router = router_get_by_nickname(conn->chosen_exit_name, 1);
+      const node_t *node = node_get_by_nickname(conn->chosen_exit_name, 1);
       int opt = conn->chosen_exit_optional;
-      if (!router && !want_onehop) {
+      if (!node && !want_onehop) {
         /* We ran into this warning when trying to extend a circuit to a
          * hidden service directory for which we didn't have a router
          * descriptor. See flyspray task 767 for more details. We should
@@ -1590,7 +1582,7 @@ connection_ap_handshake_attach_circuit(edge_connection_t *conn)
         }
         return -1;
       }
-      if (router && !connection_ap_can_use_exit(conn, router, 0)) {
+      if (node && !connection_ap_can_use_exit(conn, node, 0)) {
         log_fn(opt ? LOG_INFO : LOG_WARN, LD_APP,
                "Requested exit point '%s' would refuse request. %s.",
                conn->chosen_exit_name, opt ? "Trying others" : "Closing");
diff --git a/src/or/circuituse.h b/src/or/circuituse.h
index 617685e..4e4cd1a 100644
--- a/src/or/circuituse.h
+++ b/src/or/circuituse.h
@@ -41,8 +41,7 @@ void circuit_build_failed(origin_circuit_t *circ);
 origin_circuit_t *circuit_launch_by_extend_info(uint8_t purpose,
                                                 extend_info_t *info,
                                                 int flags);
-origin_circuit_t *circuit_launch_by_router(uint8_t purpose,
-                                           const routerinfo_t *exit, int flags);
+origin_circuit_t *circuit_launch(uint8_t purpose, int flags);
 void circuit_reset_failure_count(int timeout);
 int connection_ap_handshake_attach_chosen_circuit(edge_connection_t *conn,
                                                   origin_circuit_t *circ,
diff --git a/src/or/command.c b/src/or/command.c
index 6296c47..46713a5 100644
--- a/src/or/command.c
+++ b/src/or/command.c
@@ -25,6 +25,7 @@
 #include "control.h"
 #include "cpuworker.h"
 #include "hibernate.h"
+#include "nodelist.h"
 #include "onion.h"
 #include "relay.h"
 #include "router.h"
@@ -267,15 +268,15 @@ command_process_create_cell(cell_t *cell, or_connection_t *conn)
   }
 
   if (circuit_id_in_use_on_orconn(cell->circ_id, conn)) {
-    const routerinfo_t *router = router_get_by_digest(conn->identity_digest);
+    const node_t *node = node_get_by_id(conn->identity_digest);
     log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
            "Received CREATE cell (circID %d) for known circ. "
            "Dropping (age %d).",
            cell->circ_id, (int)(time(NULL) - conn->_base.timestamp_created));
-    if (router)
+    if (node)
       log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
              "Details: nickname \"%s\", platform %s.",
-             router->nickname, escaped(router->platform));
+             node_get_nickname(node), escaped(node_get_platform(node)));
     return;
   }
 
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index 2b9ccc5..4ab636a 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -23,6 +23,7 @@
 #include "dirserv.h"
 #include "hibernate.h"
 #include "main.h"
+#include "nodelist.h"
 #include "policies.h"
 #include "reasons.h"
 #include "relay.h"
@@ -587,7 +588,7 @@ void
 circuit_discard_optional_exit_enclaves(extend_info_t *info)
 {
   edge_connection_t *edge_conn;
-  const routerinfo_t *r1, *r2;
+  const node_t *r1, *r2;
 
   smartlist_t *conns = get_connection_array();
   SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
@@ -599,8 +600,8 @@ circuit_discard_optional_exit_enclaves(extend_info_t *info)
     if (!edge_conn->chosen_exit_optional &&
         !edge_conn->chosen_exit_retries)
       continue;
-    r1 = router_get_by_nickname(edge_conn->chosen_exit_name, 0);
-    r2 = router_get_by_nickname(info->nickname, 0);
+    r1 = node_get_by_nickname(edge_conn->chosen_exit_name, 0);
+    r2 = node_get_by_nickname(info->nickname, 0);
     if (!r1 || !r2 || r1 != r2)
       continue;
     tor_assert(edge_conn->socks_request);
@@ -1575,12 +1576,12 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
         return -1;
       }
     } else {
-      const routerinfo_t *r;
+      const node_t *r;
       conn->chosen_exit_name = tor_strdup(socks->address);
-      r = router_get_by_nickname(conn->chosen_exit_name, 1);
+      r = node_get_by_nickname(conn->chosen_exit_name, 1);
       *socks->address = 0;
       if (r) {
-        strlcpy(socks->address, r->address, sizeof(socks->address));
+        node_get_address_string(r, socks->address, sizeof(socks->address));
       } else {
         log_warn(LD_APP,
                  "Unrecognized server in exit address '%s.exit'. Refusing.",
@@ -1631,16 +1632,16 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
 
       if (!conn->use_begindir && !conn->chosen_exit_name && !circ) {
         /* see if we can find a suitable enclave exit */
-        const routerinfo_t *r =
+        const node_t *r =
           router_find_exact_exit_enclave(socks->address, socks->port);
         if (r) {
           log_info(LD_APP,
                    "Redirecting address %s to exit at enclave router %s",
-                   safe_str_client(socks->address), r->nickname);
+                   safe_str_client(socks->address), node_get_nickname(r));
           /* use the hex digest, not nickname, in case there are two
              routers with this nickname */
           conn->chosen_exit_name =
-            tor_strdup(hex_str(r->cache_info.identity_digest, DIGEST_LEN));
+            tor_strdup(hex_str(r->identity, DIGEST_LEN));
           conn->chosen_exit_optional = 1;
         }
       }
@@ -2896,7 +2897,7 @@ connection_edge_is_rendezvous_stream(edge_connection_t *conn)
  * this relay, return 0.
  */
 int
-connection_ap_can_use_exit(edge_connection_t *conn, const routerinfo_t *exit,
+connection_ap_can_use_exit(edge_connection_t *conn, const node_t *exit,
                            int excluded_means_no)
 {
   or_options_t *options = get_options();
@@ -2910,10 +2911,10 @@ connection_ap_can_use_exit(edge_connection_t *conn, const routerinfo_t *exit,
    * make sure the exit node of the existing circuit matches exactly.
    */
   if (conn->chosen_exit_name) {
-    const routerinfo_t *chosen_exit =
-      router_get_by_nickname(conn->chosen_exit_name, 1);
-    if (!chosen_exit || memcmp(chosen_exit->cache_info.identity_digest,
-                               exit->cache_info.identity_digest, DIGEST_LEN)) {
+    const node_t *chosen_exit =
+      node_get_by_nickname(conn->chosen_exit_name, 1);
+    if (!chosen_exit || memcmp(chosen_exit->identity,
+                               exit->identity, DIGEST_LEN)) {
       /* doesn't match */
 //      log_debug(LD_APP,"Requested node '%s', considering node '%s'. No.",
 //                conn->chosen_exit_name, exit->nickname);
@@ -2928,8 +2929,7 @@ connection_ap_can_use_exit(edge_connection_t *conn, const routerinfo_t *exit,
     addr_policy_result_t r;
     if (tor_inet_aton(conn->socks_request->address, &in))
       addr = ntohl(in.s_addr);
-    r = compare_addr_to_addr_policy(addr, conn->socks_request->port,
-                                    exit->exit_policy);
+    r = compare_addr_to_node_policy(addr, conn->socks_request->port, exit);
     if (r == ADDR_POLICY_REJECTED)
       return 0; /* We know the address, and the exit policy rejects it. */
     if (r == ADDR_POLICY_PROBABLY_REJECTED && !conn->chosen_exit_name)
@@ -2938,12 +2938,12 @@ connection_ap_can_use_exit(edge_connection_t *conn, const routerinfo_t *exit,
                  * this node, err on the side of caution. */
   } else if (SOCKS_COMMAND_IS_RESOLVE(conn->socks_request->command)) {
     /* Don't send DNS requests to non-exit servers by default. */
-    if (!conn->chosen_exit_name && policy_is_reject_star(exit->exit_policy))
+    if (!conn->chosen_exit_name && node_exit_policy_rejects_all(exit))
       return 0;
   }
   if (options->_ExcludeExitNodesUnion &&
       (options->StrictNodes || excluded_means_no) &&
-      routerset_contains_router(options->_ExcludeExitNodesUnion, exit)) {
+      routerset_contains_node(options->_ExcludeExitNodesUnion, exit)) {
     /* If we are trying to avoid this node as exit, and we have StrictNodes
      * set, then this is not a suitable exit. Refuse it.
      *
diff --git a/src/or/connection_edge.h b/src/or/connection_edge.h
index 24ea327..f54d7a4 100644
--- a/src/or/connection_edge.h
+++ b/src/or/connection_edge.h
@@ -48,7 +48,7 @@ int connection_exit_begin_resolve(cell_t *cell, or_circuit_t *circ);
 void connection_exit_connect(edge_connection_t *conn);
 int connection_edge_is_rendezvous_stream(edge_connection_t *conn);
 int connection_ap_can_use_exit(edge_connection_t *conn,
-                               const routerinfo_t *exit,
+                               const node_t *exit,
                                int excluded_means_no);
 void connection_ap_expire_beginning(void);
 void connection_ap_attach_pending(void);
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index ee17282..ce285cd 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -22,6 +22,7 @@
 #include "geoip.h"
 #include "main.h"
 #include "networkstatus.h"
+#include "nodelist.h"
 #include "reasons.h"
 #include "relay.h"
 #include "rephist.h"
@@ -438,7 +439,7 @@ connection_or_init_conn_from_address(or_connection_t *conn,
                                      const char *id_digest,
                                      int started_here)
 {
-  const routerinfo_t *r = router_get_by_digest(id_digest);
+  const node_t *r = node_get_by_id(id_digest);
   connection_or_set_identity_digest(conn, id_digest);
   connection_or_update_token_buckets_helper(conn, 1, get_options());
 
@@ -446,8 +447,10 @@ connection_or_init_conn_from_address(or_connection_t *conn,
   tor_addr_copy(&conn->_base.addr, addr);
   tor_addr_copy(&conn->real_addr, addr);
   if (r) {
+    tor_addr_t node_addr;
+    node_get_addr(r, &node_addr);
     /* XXXX proposal 118 will make this more complex. */
-    if (tor_addr_eq_ipv4h(&conn->_base.addr, r->addr))
+    if (tor_addr_eq(&conn->_base.addr, &node_addr))
       conn->is_canonical = 1;
     if (!started_here) {
       /* Override the addr/port, so our log messages will make sense.
@@ -460,12 +463,12 @@ connection_or_init_conn_from_address(or_connection_t *conn,
        * right IP address and port 56244, that wouldn't be as helpful. now we
        * log the "right" port too, so we know if it's moria1 or moria2.
        */
-      tor_addr_from_ipv4h(&conn->_base.addr, r->addr);
-      conn->_base.port = r->or_port;
+      tor_addr_copy(&conn->_base.addr, &node_addr);
+      conn->_base.port = node_get_orport(r);
     }
-    conn->nickname = tor_strdup(r->nickname);
+    conn->nickname = tor_strdup(node_get_nickname(r));
     tor_free(conn->_base.address);
-    conn->_base.address = tor_strdup(r->address);
+    conn->_base.address = tor_dup_addr(&node_addr);
   } else {
     const char *n;
     /* If we're an authoritative directory server, we may know a
diff --git a/src/or/control.c b/src/or/control.c
index 7161fea..a46ce2c 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -26,6 +26,7 @@
 #include "hibernate.h"
 #include "main.h"
 #include "networkstatus.h"
+#include "nodelist.h"
 #include "policies.h"
 #include "reasons.h"
 #include "router.h"
@@ -2107,7 +2108,7 @@ static int
 handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
                              const char *body)
 {
-  smartlist_t *router_nicknames=NULL, *routers=NULL;
+  smartlist_t *router_nicknames=NULL, *nodes=NULL;
   origin_circuit_t *circ = NULL;
   int zero_circ;
   uint8_t intended_purpose = CIRCUIT_PURPOSE_C_GENERAL;
@@ -2138,8 +2139,7 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
     if ((smartlist_len(args) == 1) ||
         (smartlist_len(args) >= 2 && is_keyval_pair(smartlist_get(args, 1)))) {
       // "EXTENDCIRCUIT 0" || EXTENDCIRCUIT 0 foo=bar"
-      circ = circuit_launch_by_router(intended_purpose, NULL,
-                                      CIRCLAUNCH_NEED_CAPACITY);
+      circ = circuit_launch(intended_purpose, CIRCLAUNCH_NEED_CAPACITY);
       if (!circ) {
         connection_write_str_to_buf("551 Couldn't start circuit\r\n", conn);
       } else {
@@ -2167,17 +2167,21 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
   SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
   smartlist_free(args);
 
-  routers = smartlist_create();
+  nodes = smartlist_create();
   SMARTLIST_FOREACH(router_nicknames, const char *, n,
   {
-    const routerinfo_t *r = router_get_by_nickname(n, 1);
-    if (!r) {
+    const node_t *node = node_get_by_nickname(n, 1);
+    if (!node) {
       connection_printf_to_buf(conn, "552 No such router \"%s\"\r\n", n);
       goto done;
     }
-    smartlist_add(routers, (void*) r);
+    if (!node_has_descriptor(node)) {
+      connection_printf_to_buf(conn, "552 descriptor for \"%s\"\r\n", n);
+      goto done;
+    }
+    smartlist_add(nodes, (void*)node);
   });
-  if (!smartlist_len(routers)) {
+  if (!smartlist_len(nodes)) {
     connection_write_str_to_buf("512 No router names provided\r\n", conn);
     goto done;
   }
@@ -2188,9 +2192,10 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
   }
 
   /* now circ refers to something that is ready to be extended */
-  SMARTLIST_FOREACH(routers, const routerinfo_t *, r,
+  SMARTLIST_FOREACH(nodes, const node_t *, node,
   {
-    extend_info_t *info = extend_info_from_router(r);
+    extend_info_t *info = extend_info_from_node(node);
+    tor_assert(info); /* True, since node_has_descriptor(node) == true */
     circuit_append_new_exit(circ, info);
     extend_info_free(info);
   });
@@ -2224,7 +2229,7 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
  done:
   SMARTLIST_FOREACH(router_nicknames, char *, n, tor_free(n));
   smartlist_free(router_nicknames);
-  smartlist_free(routers);
+  smartlist_free(nodes);
   return 0;
 }
 
@@ -2340,16 +2345,17 @@ handle_control_attachstream(control_connection_t *conn, uint32_t len,
   }
   /* Is this a single hop circuit? */
   if (circ && (circuit_get_cpath_len(circ)<2 || hop==1)) {
-    const routerinfo_t *r = NULL;
+    const node_t *node = NULL;
     char *exit_digest;
     if (circ->build_state &&
         circ->build_state->chosen_exit &&
         !tor_digest_is_zero(circ->build_state->chosen_exit->identity_digest)) {
       exit_digest = circ->build_state->chosen_exit->identity_digest;
-      r = router_get_by_digest(exit_digest);
+      node = node_get_by_id(exit_digest);
     }
     /* Do both the client and relay allow one-hop exit circuits? */
-    if (!r || !r->allow_single_hop_exits ||
+    if (!node ||
+        !node_allows_single_hop_exits(node) ||
         !get_options()->AllowSingleHopCircuits) {
       connection_write_str_to_buf(
       "551 Can't attach stream to this one-hop circuit.\r\n", conn);
@@ -3177,10 +3183,10 @@ control_event_stream_status(edge_connection_t *conn, stream_status_event_t tp,
 static void
 orconn_target_get_name(char *name, size_t len, or_connection_t *conn)
 {
-  const routerinfo_t *ri = router_get_by_digest(conn->identity_digest);
-  if (ri) {
+  const node_t *node = node_get_by_id(conn->identity_digest);
+  if (node) {
     tor_assert(len > MAX_VERBOSE_NICKNAME_LEN);
-    router_get_verbose_nickname(name, ri);
+    node_get_verbose_nickname(node, name);
   } else if (! tor_digest_is_zero(conn->identity_digest)) {
     name[0] = '$';
     base16_encode(name+1, len-1, conn->identity_digest,
@@ -3722,9 +3728,9 @@ control_event_guard(const char *nickname, const char *digest,
 
   {
     char buf[MAX_VERBOSE_NICKNAME_LEN+1];
-    const routerinfo_t *ri = router_get_by_digest(digest);
-    if (ri) {
-      router_get_verbose_nickname(buf, ri);
+    const node_t *node = node_get_by_id(digest);
+    if (node) {
+      node_get_verbose_nickname(node, buf);
     } else {
       tor_snprintf(buf, sizeof(buf), "$%s~%s", hbuf, nickname);
     }
diff --git a/src/or/directory.c b/src/or/directory.c
index 2e0eb00..27de0ae 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -17,6 +17,7 @@
 #include "main.h"
 #include "microdesc.h"
 #include "networkstatus.h"
+#include "nodelist.h"
 #include "policies.h"
 #include "rendclient.h"
 #include "rendcommon.h"
@@ -219,13 +220,14 @@ dir_conn_purpose_to_string(int purpose)
 int
 router_supports_extrainfo(const char *identity_digest, int is_authority)
 {
-  const routerinfo_t *ri = router_get_by_digest(identity_digest);
+  const node_t *node = node_get_by_id(identity_digest);
 
-  if (ri) {
-    if (ri->caches_extra_info)
+  if (node->ri) {
+    if (node->ri->caches_extra_info)
       return 1;
-    if (is_authority && ri->platform &&
-        tor_version_as_new_as(ri->platform, "Tor 0.2.0.0-alpha-dev (r10070)"))
+    if (is_authority && node->ri->platform &&
+        tor_version_as_new_as(node->ri->platform,
+                              "Tor 0.2.0.0-alpha-dev (r10070)"))
       return 1;
   }
   if (is_authority) {
@@ -401,10 +403,12 @@ directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose,
        * possible directory question. This won't be true forever. -RD */
       /* It certainly is not true with conditional consensus downloading,
        * so, for now, never assume the server supports that. */
-      const routerinfo_t *ri = choose_random_entry(NULL);
-      if (ri) {
+      const node_t *node = choose_random_entry(NULL);
+      if (node && node->ri) {
+        /* every bridge has a routerinfo. */
         tor_addr_t addr;
-        tor_addr_from_ipv4h(&addr, ri->addr);
+        routerinfo_t *ri = node->ri;
+        node_get_addr(node, &addr);
         directory_initiate_command(ri->address, &addr,
                                    ri->or_port, 0,
                                    0, /* don't use conditional consensus url */
@@ -523,18 +527,19 @@ directory_initiate_command_routerstatus_rend(const routerstatus_t *status,
                                              time_t if_modified_since,
                                              const rend_data_t *rend_query)
 {
-  const routerinfo_t *router;
+  const node_t *node;
   char address_buf[INET_NTOA_BUF_LEN+1];
   struct in_addr in;
   const char *address;
   tor_addr_t addr;
-  router = router_get_by_digest(status->identity_digest);
-  if (!router && anonymized_connection) {
+  node = node_get_by_id(status->identity_digest);
+  if (!node && anonymized_connection) {
     log_info(LD_DIR, "Not sending anonymized request to directory '%s'; we "
                      "don't have its router descriptor.", status->nickname);
     return;
-  } else if (router) {
-    address = router->address;
+  } else if (node) {
+    node_get_address_string(node, address_buf, sizeof(address_buf));
+    address = address_buf;
   } else {
     in.s_addr = htonl(status->addr);
     tor_inet_ntoa(&in, address_buf, sizeof(address_buf));
@@ -3694,7 +3699,7 @@ dir_microdesc_download_failed(smartlist_t *failed,
   if (! consensus)
     return;
   SMARTLIST_FOREACH_BEGIN(failed, const char *, d) {
-    rs = router_get_mutable_consensus_status_by_descriptor_digest(consensus, d);
+    rs = router_get_mutable_consensus_status_by_descriptor_digest(consensus,d);
     if (!rs)
       continue;
     dls = &rs->dl_status;
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index 0da12e4..dec1577 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -16,6 +16,7 @@
 #include "hibernate.h"
 #include "microdesc.h"
 #include "networkstatus.h"
+#include "nodelist.h"
 #include "policies.h"
 #include "rephist.h"
 #include "router.h"
@@ -65,8 +66,6 @@ static char *format_versions_list(config_line_t *ln);
 struct authdir_config_t;
 static int add_fingerprint_to_dir(const char *nickname, const char *fp,
                                   struct authdir_config_t *list);
-static uint32_t dirserv_router_get_status(const routerinfo_t *router,
-                                          const char **msg);
 static uint32_t
 dirserv_get_status_impl(const char *fp, const char *nickname,
                         const char *address,
@@ -304,7 +303,7 @@ dirserv_load_fingerprint_file(void)
  *
  * If the status is 'FP_REJECT' and <b>msg</b> is provided, set
  * *<b>msg</b> to an explanation of why. */
-static uint32_t
+uint32_t
 dirserv_router_get_status(const routerinfo_t *router, const char **msg)
 {
   char d[DIGEST_LEN];
@@ -361,7 +360,7 @@ dirserv_get_name_status(const char *id_digest, const char *nickname)
   return 0;
 }
 
-/** Helper: As dirserv_get_router_status, but takes the router fingerprint
+/** Helper: As dirserv_router_get_status, but takes the router fingerprint
  * (hex, no spaces), nickname, address (used for logging only), IP address, OR
  * port, platform (logging only) and contact info (logging only) as arguments.
  *
@@ -376,7 +375,7 @@ dirserv_get_status_impl(const char *id_digest, const char *nickname,
                         const char **msg, int should_log)
 {
   int reject_unlisted = get_options()->AuthDirRejectUnlisted;
-  uint32_t result = 0;
+  uint32_t result;
   router_status_t *status_by_digest;
 
   if (!fingerprint_list)
@@ -534,7 +533,7 @@ dirserv_router_has_valid_address(routerinfo_t *ri)
  */
 int
 authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg,
-                               int complain)
+                               int complain, int *valid_out)
 {
   /* Okay.  Now check whether the fingerprint is recognized. */
   uint32_t status = dirserv_router_get_status(ri, msg);
@@ -575,15 +574,24 @@ authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg,
     *msg = "Rejected: Address is not an IP, or IP is a private address.";
     return -1;
   }
-  /* Okay, looks like we're willing to accept this one. */
-  ri->is_named = (status & FP_NAMED) ? 1 : 0;
-  ri->is_valid = (status & FP_INVALID) ? 0 : 1;
-  ri->is_bad_directory = (status & FP_BADDIR) ? 1 : 0;
-  ri->is_bad_exit = (status & FP_BADEXIT) ? 1 : 0;
+
+  *valid_out = ! (status & FP_INVALID);
 
   return 0;
 }
 
+/** Update the relevant flags of <b>node</b> based on our opinion as a
+ * directory authority in <b>authstatus</b>, as returned by
+ * dirserv_router_get_status or equivalent.  */
+void
+dirserv_set_node_flags_from_authoritative_status(node_t *node,
+                                                 uint32_t authstatus)
+{
+  node->is_valid = (authstatus & FP_INVALID) ? 0 : 1;
+  node->is_bad_directory = (authstatus & FP_BADDIR) ? 1 : 0;
+  node->is_bad_exit = (authstatus & FP_BADEXIT) ? 1 : 0;
+}
+
 /** True iff <b>a</b> is more severe than <b>b</b>. */
 static int
 WRA_MORE_SEVERE(was_router_added_t a, was_router_added_t b)
@@ -752,8 +760,7 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source)
     routerlist_descriptors_added(changed, 0);
     smartlist_free(changed);
     if (!*msg) {
-      *msg =  ri->is_valid ? "Descriptor for valid server accepted" :
-        "Descriptor for invalid server accepted";
+      *msg =  "Descriptor accepted";
     }
     log_info(LD_DIRSERV,
              "Added descriptor from '%s' (source: %s): %s.",
@@ -808,50 +815,60 @@ dirserv_add_extrainfo(extrainfo_t *ei, const char **msg)
 static void
 directory_remove_invalid(void)
 {
-  int i;
   int changed = 0;
   routerlist_t *rl = router_get_routerlist();
+  smartlist_t *nodes = smartlist_create();
+  smartlist_add_all(nodes, nodelist_get_list());
 
-  routerlist_assert_ok(rl);
-
-  for (i = 0; i < smartlist_len(rl->routers); ++i) {
+  SMARTLIST_FOREACH_BEGIN(nodes, node_t *, node) {
     const char *msg;
-    routerinfo_t *ent = smartlist_get(rl->routers, i);
-    uint32_t r = dirserv_router_get_status(ent, &msg);
+    routerinfo_t *ent = node->ri;
+    uint32_t r;
+    if (!ent)
+      continue;
+    r = dirserv_router_get_status(ent, &msg);
     if (r & FP_REJECT) {
       log_info(LD_DIRSERV, "Router '%s' is now rejected: %s",
                ent->nickname, msg?msg:"");
       routerlist_remove(rl, ent, 0, time(NULL));
-      i--;
       changed = 1;
       continue;
     }
-    if (bool_neq((r & FP_NAMED), ent->is_named)) {
+#if 0
+    if (bool_neq((r & FP_NAMED), ent->auth_says_is_named)) {
       log_info(LD_DIRSERV,
                "Router '%s' is now %snamed.", ent->nickname,
                (r&FP_NAMED)?"":"un");
       ent->is_named = (r&FP_NAMED)?1:0;
       changed = 1;
     }
-    if (bool_neq((r & FP_INVALID), !ent->is_valid)) {
+    if (bool_neq((r & FP_UNNAMED), ent->auth_says_is_unnamed)) {
+      log_info(LD_DIRSERV,
+               "Router '%s' is now %snamed. (FP_UNNAMED)", ent->nickname,
+               (r&FP_NAMED)?"":"un");
+      ent->is_named = (r&FP_NUNAMED)?0:1;
+      changed = 1;
+    }
+#endif
+    if (bool_neq((r & FP_INVALID), !node->is_valid)) {
       log_info(LD_DIRSERV, "Router '%s' is now %svalid.", ent->nickname,
                (r&FP_INVALID) ? "in" : "");
-      ent->is_valid = (r&FP_INVALID)?0:1;
+      node->is_valid = (r&FP_INVALID)?0:1;
       changed = 1;
     }
-    if (bool_neq((r & FP_BADDIR), ent->is_bad_directory)) {
+    if (bool_neq((r & FP_BADDIR), node->is_bad_directory)) {
       log_info(LD_DIRSERV, "Router '%s' is now a %s directory", ent->nickname,
                (r & FP_BADDIR) ? "bad" : "good");
-      ent->is_bad_directory = (r&FP_BADDIR) ? 1: 0;
+      node->is_bad_directory = (r&FP_BADDIR) ? 1: 0;
       changed = 1;
     }
-    if (bool_neq((r & FP_BADEXIT), ent->is_bad_exit)) {
+    if (bool_neq((r & FP_BADEXIT), node->is_bad_exit)) {
       log_info(LD_DIRSERV, "Router '%s' is now a %s exit", ent->nickname,
                (r & FP_BADEXIT) ? "bad" : "good");
-      ent->is_bad_exit = (r&FP_BADEXIT) ? 1: 0;
+      node->is_bad_exit = (r&FP_BADEXIT) ? 1: 0;
       changed = 1;
     }
-  }
+  } SMARTLIST_FOREACH_END(node);
   if (changed)
     directory_set_dirty();
 
@@ -894,10 +911,11 @@ directory_set_dirty(void)
  * as running iff <b>is_live</b> is true.
  */
 static char *
-list_single_server_status(routerinfo_t *desc, int is_live)
+list_single_server_status(const routerinfo_t *desc, int is_live)
 {
   char buf[MAX_NICKNAME_LEN+HEX_DIGEST_LEN+4]; /* !nickname=$hexdigest\0 */
   char *cp;
+  const node_t *node;
 
   tor_assert(desc);
 
@@ -905,7 +923,8 @@ list_single_server_status(routerinfo_t *desc, int is_live)
   if (!is_live) {
     *cp++ = '!';
   }
-  if (desc->is_valid) {
+  node = node_get_by_id(desc->cache_info.identity_digest);
+  if (node && node->is_valid) {
     strlcpy(cp, desc->nickname, sizeof(buf)-(cp-buf));
     cp += strlen(cp);
     *cp++ = '=';
@@ -944,6 +963,8 @@ dirserv_set_router_is_running(routerinfo_t *router, time_t now)
     unreachable.
    */
   int answer;
+  node_t *node = node_get_mutable_by_id(router->cache_info.identity_digest);
+  tor_assert(node);
 
   if (router_is_me(router)) {
     /* We always know if we are down ourselves. */
@@ -968,7 +989,7 @@ dirserv_set_router_is_running(routerinfo_t *router, time_t now)
     rep_hist_note_router_unreachable(router->cache_info.identity_digest, now);
   }
 
-  router->is_running = answer;
+  node->is_running = answer;
 }
 
 /** Based on the routerinfo_ts in <b>routers</b>, allocate the
@@ -996,6 +1017,8 @@ list_server_status_v1(smartlist_t *routers, char **router_status_out,
   rs_entries = smartlist_create();
 
   SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) {
+    const node_t *node = node_get_by_id(ri->cache_info.identity_digest);
+    tor_assert(node);
     if (authdir) {
       /* Update router status in routerinfo_t. */
       dirserv_set_router_is_running(ri, now);
@@ -1003,12 +1026,13 @@ list_server_status_v1(smartlist_t *routers, char **router_status_out,
     if (for_controller) {
       char name_buf[MAX_VERBOSE_NICKNAME_LEN+2];
       char *cp = name_buf;
-      if (!ri->is_running)
+      if (!node->is_running)
         *cp++ = '!';
       router_get_verbose_nickname(cp, ri);
       smartlist_add(rs_entries, tor_strdup(name_buf));
     } else if (ri->cache_info.published_on >= cutoff) {
-      smartlist_add(rs_entries, list_single_server_status(ri, ri->is_running));
+      smartlist_add(rs_entries, list_single_server_status(ri,
+                                                          node->is_running));
     }
   } SMARTLIST_FOREACH_END(ri);
 
@@ -1046,12 +1070,12 @@ format_versions_list(config_line_t *ln)
  * not hibernating, and not too old. Else return 0.
  */
 static int
-router_is_active(routerinfo_t *ri, time_t now)
+router_is_active(const routerinfo_t *ri, const node_t *node, time_t now)
 {
   time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH;
   if (ri->cache_info.published_on < cutoff)
     return 0;
-  if (!ri->is_running || !ri->is_valid || ri->is_hibernating)
+  if (!node->is_running || !node->is_valid || ri->is_hibernating)
     return 0;
   return 1;
 }
@@ -1717,7 +1741,7 @@ static uint64_t total_exit_bandwidth = 0;
 /** Helper: estimate the uptime of a router given its stated uptime and the
  * amount of time since it last stated its stated uptime. */
 static INLINE long
-real_uptime(routerinfo_t *router, time_t now)
+real_uptime(const routerinfo_t *router, time_t now)
 {
   if (now < router->cache_info.published_on)
     return router->uptime;
@@ -1768,7 +1792,8 @@ dirserv_thinks_router_is_unreliable(time_t now,
  * been set.
  */
 static int
-dirserv_thinks_router_is_hs_dir(routerinfo_t *router, time_t now)
+dirserv_thinks_router_is_hs_dir(const routerinfo_t *router,
+                                const node_t *node, time_t now)
 {
   long uptime = real_uptime(router, now);
 
@@ -1778,7 +1803,7 @@ dirserv_thinks_router_is_hs_dir(routerinfo_t *router, time_t now)
    * version is too old. */
   return (router->wants_to_be_hs_dir && router->dir_port &&
           uptime > get_options()->MinUptimeHidServDirectoryV2 &&
-          router->is_running);
+          node->is_running);
 }
 
 /** Look through the routerlist, the Mean Time Between Failure history, and
@@ -1826,19 +1851,22 @@ dirserv_compute_performance_thresholds(routerlist_t *rl)
   /* Weighted fractional uptime for each active router. */
   wfus = tor_malloc(sizeof(double)*smartlist_len(rl->routers));
 
+  nodelist_assert_ok();
+
   /* Now, fill in the arrays. */
-  SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri, {
-    if (router_is_active(ri, now)) {
+  SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), node_t *, node) {
+    routerinfo_t *ri = node->ri;
+    if (ri && router_is_active(ri, node, now)) {
       const char *id = ri->cache_info.identity_digest;
       uint32_t bw;
-      ri->is_exit = (!router_exit_policy_rejects_all(ri) &&
-                    exit_policy_is_general_exit(ri->exit_policy));
+      node->is_exit = (!router_exit_policy_rejects_all(ri) &&
+                       exit_policy_is_general_exit(ri->exit_policy));
       uptimes[n_active] = (uint32_t)real_uptime(ri, now);
       mtbfs[n_active] = rep_hist_get_stability(id, now);
       tks  [n_active] = rep_hist_get_weighted_time_known(id, now);
       bandwidths[n_active] = bw = router_get_advertised_bandwidth(ri);
       total_bandwidth += bw;
-      if (ri->is_exit && !ri->is_bad_exit) {
+      if (node->is_exit && !node->is_bad_exit) {
         total_exit_bandwidth += bw;
       } else {
         bandwidths_excluding_exits[n_active_nonexit] = bw;
@@ -1846,7 +1874,7 @@ dirserv_compute_performance_thresholds(routerlist_t *rl)
       }
       ++n_active;
     }
-  });
+  } SMARTLIST_FOREACH_END(node);
 
   /* Now, compute thresholds. */
   if (n_active) {
@@ -1872,15 +1900,17 @@ dirserv_compute_performance_thresholds(routerlist_t *rl)
   /* Now that we have a time-known that 7/8 routers are known longer than,
    * fill wfus with the wfu of every such "familiar" router. */
   n_familiar = 0;
-  SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri, {
-      if (router_is_active(ri, now)) {
+
+  SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), node_t *, node) {
+      routerinfo_t *ri = node->ri;
+      if (ri && router_is_active(ri, node, now)) {
         const char *id = ri->cache_info.identity_digest;
         long tk = rep_hist_get_weighted_time_known(id, now);
         if (tk < guard_tk)
           continue;
         wfus[n_familiar++] = rep_hist_get_weighted_fractional_uptime(id, now);
       }
-    });
+  } SMARTLIST_FOREACH_END(node);
   if (n_familiar)
     guard_wfu = median_double(wfus, n_familiar);
   if (guard_wfu > WFU_TO_GUARANTEE_GUARD)
@@ -2123,6 +2153,8 @@ _compare_routerinfo_by_ip_and_bw(const void **a, const void **b)
   routerinfo_t *first = *(routerinfo_t **)a, *second = *(routerinfo_t **)b;
   int first_is_auth, second_is_auth;
   uint32_t bw_first, bw_second;
+  const node_t *node_first, *node_second;
+  int first_is_running, second_is_running;
 
   /* we return -1 if first should appear before second... that is,
    * if first is a better router. */
@@ -2145,9 +2177,14 @@ _compare_routerinfo_by_ip_and_bw(const void **a, const void **b)
   else if (!first_is_auth && second_is_auth)
     return 1;
 
-  else if (first->is_running && !second->is_running)
+  node_first = node_get_by_id(first->cache_info.identity_digest);
+  node_second = node_get_by_id(second->cache_info.identity_digest);
+  first_is_running = node_first && node_first->is_running;
+  second_is_running = node_second && node_second->is_running;
+
+  if (first_is_running && !second_is_running)
     return -1;
-  else if (!first->is_running && second->is_running)
+  else if (!first_is_running && second_is_running)
     return 1;
 
   bw_first = router_get_advertised_bandwidth(first);
@@ -2216,7 +2253,9 @@ get_possible_sybil_list(const smartlist_t *routers)
  */
 void
 set_routerstatus_from_routerinfo(routerstatus_t *rs,
-                                 routerinfo_t *ri, time_t now,
+                                 node_t *node,
+                                 routerinfo_t *ri,
+                                 time_t now,
                                  int naming, int listbadexits,
                                  int listbaddirs)
 {
@@ -2228,48 +2267,46 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs,
     router_digest_is_trusted_dir(ri->cache_info.identity_digest);
 
   /* Already set by compute_performance_thresholds. */
-  rs->is_exit = ri->is_exit;
-  rs->is_stable = ri->is_stable =
-    router_is_active(ri, now) &&
+  rs->is_exit = node->is_exit;
+  rs->is_stable = node->is_stable =
+    router_is_active(ri, node, now) &&
     !dirserv_thinks_router_is_unreliable(now, ri, 1, 0) &&
     !unstable_version;
-  rs->is_fast = ri->is_fast =
-    router_is_active(ri, now) &&
+  rs->is_fast = node->is_fast =
+    router_is_active(ri, node, now) &&
     !dirserv_thinks_router_is_unreliable(now, ri, 0, 1);
-  rs->is_running = ri->is_running; /* computed above */
+  rs->is_running = node->is_running; /* computed above */
 
   if (naming) {
     uint32_t name_status = dirserv_get_name_status(
-                         ri->cache_info.identity_digest, ri->nickname);
+                                              node->identity, ri->nickname);
     rs->is_named = (naming && (name_status & FP_NAMED)) ? 1 : 0;
     rs->is_unnamed = (naming && (name_status & FP_UNNAMED)) ? 1 : 0;
   }
-  rs->is_valid = ri->is_valid;
+  rs->is_valid = node->is_valid;
 
-  if (rs->is_fast &&
+  if (node->is_fast &&
       (router_get_advertised_bandwidth(ri) >= BANDWIDTH_TO_GUARANTEE_GUARD ||
        router_get_advertised_bandwidth(ri) >=
                               MIN(guard_bandwidth_including_exits,
                                   guard_bandwidth_excluding_exits))) {
-    long tk = rep_hist_get_weighted_time_known(
-                                      ri->cache_info.identity_digest, now);
-    double wfu = rep_hist_get_weighted_fractional_uptime(
-                                      ri->cache_info.identity_digest, now);
+    long tk = rep_hist_get_weighted_time_known(node->identity, now);
+    double wfu = rep_hist_get_weighted_fractional_uptime(node->identity, now);
     rs->is_possible_guard = (wfu >= guard_wfu && tk >= guard_tk) ? 1 : 0;
   } else {
     rs->is_possible_guard = 0;
   }
-  rs->is_bad_directory = listbaddirs && ri->is_bad_directory;
-  rs->is_bad_exit = listbadexits && ri->is_bad_exit;
-  ri->is_hs_dir = dirserv_thinks_router_is_hs_dir(ri, now);
-  rs->is_hs_dir = ri->is_hs_dir;
+  rs->is_bad_directory = listbaddirs && node->is_bad_directory;
+  rs->is_bad_exit = listbadexits && node->is_bad_exit;
+  node->is_hs_dir = dirserv_thinks_router_is_hs_dir(ri, node, now);
+  rs->is_hs_dir = node->is_hs_dir;
   rs->is_v2_dir = ri->dir_port != 0;
 
   if (!strcasecmp(ri->nickname, UNNAMED_ROUTER_NICKNAME))
     rs->is_named = rs->is_unnamed = 0;
 
   rs->published_on = ri->cache_info.published_on;
-  memcpy(rs->identity_digest, ri->cache_info.identity_digest, DIGEST_LEN);
+  memcpy(rs->identity_digest, node->identity, DIGEST_LEN);
   memcpy(rs->descriptor_digest, ri->cache_info.signed_descriptor_digest,
          DIGEST_LEN);
   rs->addr = ri->addr;
@@ -2294,18 +2331,6 @@ clear_status_flags_on_sybil(routerstatus_t *rs)
    * forget to add it to this clause. */
 }
 
-/** Clear all the status flags in routerinfo <b>router</b>. We put this
- * function here because it's eerily similar to
- * clear_status_flags_on_sybil() above. One day we should merge them. */
-void
-router_clear_status_flags(routerinfo_t *router)
-{
-  router->is_valid = router->is_running = router->is_hs_dir =
-    router->is_fast = router->is_stable =
-    router->is_possible_guard = router->is_exit =
-    router->is_bad_exit = router->is_bad_directory = 0;
-}
-
 /**
  * Helper function to parse out a line in the measured bandwidth file
  * into a measured_bw_line_t output structure. Returns -1 on failure
@@ -2553,10 +2578,13 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_env_t *private_key,
       routerstatus_t *rs;
       vote_routerstatus_t *vrs;
       microdesc_t *md;
+      node_t *node = node_get_mutable_by_id(ri->cache_info.identity_digest);
+      if (!node)
+        continue;
 
       vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
       rs = &vrs->status;
-      set_routerstatus_from_routerinfo(rs, ri, now,
+      set_routerstatus_from_routerinfo(rs, node, ri, now,
                                        naming, listbadexits, listbaddirs);
 
       if (digestmap_get(omit_as_sybil, ri->cache_info.identity_digest))
@@ -2796,8 +2824,12 @@ generate_v2_networkstatus_opinion(void)
     if (ri->cache_info.published_on >= cutoff) {
       routerstatus_t rs;
       char *version = version_from_platform(ri->platform);
-
-      set_routerstatus_from_routerinfo(&rs, ri, now,
+      node_t *node = node_get_mutable_by_id(ri->cache_info.identity_digest);
+      if (!node) {
+        tor_free(version);
+        continue;
+      }
+      set_routerstatus_from_routerinfo(&rs, node, ri, now,
                                        naming, listbadexits, listbaddirs);
 
       if (digestmap_get(omit_as_sybil, ri->cache_info.identity_digest))
@@ -3122,6 +3154,7 @@ dirserv_orconn_tls_done(const char *address,
       }
     }
   });
+
   /* FFFF Maybe we should reinstate the code that dumps routers with the same
    * addr/port but with nonmatching keys, but instead of dumping, we should
    * skip testing. */
diff --git a/src/or/dirserv.h b/src/or/dirserv.h
index f14aee6..a779632 100644
--- a/src/or/dirserv.h
+++ b/src/or/dirserv.h
@@ -40,8 +40,6 @@
    MAX_V_LINE_LEN                                                       \
    )
 
-#define UNNAMED_ROUTER_NICKNAME "Unnamed"
-
 int connection_dirserv_flushed_some(dir_connection_t *conn);
 
 int dirserv_add_own_fingerprint(const char *nickname, crypto_pk_env_t *pk);
@@ -104,7 +102,13 @@ int dirserv_should_launch_reachability_test(const routerinfo_t *ri,
 void dirserv_single_reachability_test(time_t now, routerinfo_t *router);
 void dirserv_test_reachability(time_t now);
 int authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg,
-                                   int complain);
+                                   int complain,
+                                   int *valid_out);
+uint32_t dirserv_router_get_status(const routerinfo_t *router,
+                                   const char **msg);
+void dirserv_set_node_flags_from_authoritative_status(node_t *node,
+                                                      uint32_t authstatus);
+
 int dirserv_would_reject_router(const routerstatus_t *rs);
 int dirserv_remove_old_statuses(smartlist_t *fps, time_t cutoff);
 int dirserv_have_any_serverdesc(smartlist_t *fps, int spool_src);
diff --git a/src/or/dirvote.h b/src/or/dirvote.h
index e384dc5..33213a8 100644
--- a/src/or/dirvote.h
+++ b/src/or/dirvote.h
@@ -63,6 +63,7 @@ const char *dirvote_get_pending_detached_signatures(void);
 #define DGV_INCLUDE_PREVIOUS 4
 const cached_dir_t *dirvote_get_vote(const char *fp, int flags);
 void set_routerstatus_from_routerinfo(routerstatus_t *rs,
+                                      node_t *node,
                                       routerinfo_t *ri, time_t now,
                                       int naming, int listbadexits,
                                       int listbaddirs);
diff --git a/src/or/microdesc.c b/src/or/microdesc.c
index 19970aa..c3511cf 100644
--- a/src/or/microdesc.c
+++ b/src/or/microdesc.c
@@ -587,3 +587,4 @@ update_microdescs_from_networkstatus(time_t now)
       md->last_listed = ns->valid_after;
   } SMARTLIST_FOREACH_END(rs);
 }
+
diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
index e5dd4a9..7374ced 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -107,9 +107,8 @@ void
 networkstatus_reset_warnings(void)
 {
   if (current_consensus) {
-    SMARTLIST_FOREACH(current_consensus->routerstatus_list,
-                      routerstatus_t *, rs,
-                      rs->name_lookup_warned = 0);
+    SMARTLIST_FOREACH(nodelist_get_list(), node_t *, node,
+                      node->name_lookup_warned = 0);
   }
 
   have_warned_about_old_version = 0;
@@ -1070,92 +1069,11 @@ const routerstatus_t *
 router_get_consensus_status_by_nickname(const char *nickname,
                                         int warn_if_unnamed)
 {
-  char digest[DIGEST_LEN];
-  routerstatus_t *best=NULL;
-  smartlist_t *matches=NULL;
-  const char *named_id=NULL;
-
-  if (!current_consensus || !nickname)
-    return NULL;
-
-  /* Is this name really a hexadecimal identity digest? */
-  if (nickname[0] == '$') {
-    if (base16_decode(digest, DIGEST_LEN, nickname+1, strlen(nickname+1))<0)
-      return NULL;
-    return networkstatus_vote_find_entry(current_consensus, digest);
-  } else if (strlen(nickname) == HEX_DIGEST_LEN &&
-       (base16_decode(digest, DIGEST_LEN, nickname, strlen(nickname))==0)) {
-    return networkstatus_vote_find_entry(current_consensus, digest);
-  }
-
-  /* Is there a server that is Named with this name? */
-  if (named_server_map)
-    named_id = strmap_get_lc(named_server_map, nickname);
-  if (named_id)
-    return networkstatus_vote_find_entry(current_consensus, named_id);
-
-  /* Okay; is this name listed as Unnamed? */
-  if (unnamed_server_map &&
-      strmap_get_lc(unnamed_server_map, nickname)) {
-    log_info(LD_GENERAL, "The name %s is listed as Unnamed; it is not the "
-             "canonical name of any server we know.", escaped(nickname));
+  const node_t *node = node_get_by_nickname(nickname, warn_if_unnamed);
+  if (node)
+    return node->rs;
+  else
     return NULL;
-  }
-
-  /* This name is not canonical for any server; go through the list and
-   * see who it matches. */
-  /*XXXX This is inefficient; optimize it if it matters. */
-  matches = smartlist_create();
-  SMARTLIST_FOREACH(current_consensus->routerstatus_list,
-                    routerstatus_t *, lrs,
-    {
-      if (!strcasecmp(lrs->nickname, nickname)) {
-        if (lrs->is_named) {
-          tor_fragile_assert() /* This should never happen. */
-          smartlist_free(matches);
-          return lrs;
-        } else {
-          if (lrs->is_unnamed) {
-            tor_fragile_assert(); /* nor should this. */
-            smartlist_clear(matches);
-            best=NULL;
-            break;
-          }
-          smartlist_add(matches, lrs);
-          best = lrs;
-        }
-      }
-    });
-
-  if (smartlist_len(matches)>1 && warn_if_unnamed) {
-    int any_unwarned=0;
-    SMARTLIST_FOREACH(matches, routerstatus_t *, lrs,
-      {
-        if (! lrs->name_lookup_warned) {
-          lrs->name_lookup_warned=1;
-          any_unwarned=1;
-        }
-      });
-    if (any_unwarned) {
-      log_warn(LD_CONFIG,"There are multiple matches for the nickname \"%s\","
-               " but none is listed as named by the directory authorities. "
-               "Choosing one arbitrarily.", nickname);
-    }
-  } else if (warn_if_unnamed && best && !best->name_lookup_warned) {
-    char fp[HEX_DIGEST_LEN+1];
-    base16_encode(fp, sizeof(fp),
-                  best->identity_digest, DIGEST_LEN);
-    log_warn(LD_CONFIG,
-         "When looking up a status, you specified a server \"%s\" by name, "
-         "but the directory authorities do not have any key registered for "
-         "this nickname -- so it could be used by any server, "
-         "not just the one you meant. "
-         "To make sure you get the same server in the future, refer to "
-         "it by key, as \"$%s\".", nickname, fp);
-    best->name_lookup_warned = 1;
-  }
-  smartlist_free(matches);
-  return best;
 }
 
 /** Return the identity digest that's mapped to officially by
@@ -1629,7 +1547,6 @@ networkstatus_copy_old_consensus_info(networkstatus_t *new_c,
                                 rs_new->identity_digest, DIGEST_LEN),
                          STMT_NIL) {
     /* Okay, so we're looking at the same identity. */
-    rs_new->name_lookup_warned = rs_old->name_lookup_warned;
     rs_new->last_dir_503_at = rs_old->last_dir_503_at;
 
     if (!memcmp(rs_old->descriptor_digest, rs_new->descriptor_digest,
@@ -2062,7 +1979,6 @@ routers_update_status_from_consensus_networkstatus(smartlist_t *routers,
   trusted_dir_server_t *ds;
   or_options_t *options = get_options();
   int authdir = authdir_mode_v2(options) || authdir_mode_v3(options);
-  int namingdir = authdir && options->NamingAuthoritativeDir;
   networkstatus_t *ns = current_consensus;
   if (!ns || !smartlist_len(ns->routerstatus_list))
     return;
@@ -2076,25 +1992,19 @@ routers_update_status_from_consensus_networkstatus(smartlist_t *routers,
                          memcmp(rs->identity_digest,
                                router->cache_info.identity_digest, DIGEST_LEN),
   {
+#if 0
     /* We have no routerstatus for this router. Clear flags and skip it. */
-    if (!namingdir)
-      router->is_named = 0;
     if (!authdir) {
       if (router->purpose == ROUTER_PURPOSE_GENERAL)
         router_clear_status_flags(router);
     }
+#endif
   }) {
     /* We have a routerstatus for this router. */
     const char *digest = router->cache_info.identity_digest;
 
     ds = router_get_trusteddirserver_by_digest(digest);
 
-    if (!namingdir) {
-      if (rs->is_named && !strcasecmp(router->nickname, rs->nickname))
-        router->is_named = 1;
-      else
-        router->is_named = 0;
-    }
     /* Is it the same descriptor, or only the same identity? */
     if (!memcmp(router->cache_info.signed_descriptor_digest,
                 rs->descriptor_digest, DIGEST_LEN)) {
@@ -2102,18 +2012,7 @@ routers_update_status_from_consensus_networkstatus(smartlist_t *routers,
         router->cache_info.last_listed_as_valid_until = ns->valid_until;
     }
 
-    if (!authdir) {
-      /* If we're not an authdir, believe others. */
-      router->is_valid = rs->is_valid;
-      router->is_running = rs->is_running;
-      router->is_fast = rs->is_fast;
-      router->is_stable = rs->is_stable;
-      router->is_possible_guard = rs->is_possible_guard;
-      router->is_exit = rs->is_exit;
-      router->is_bad_directory = rs->is_bad_directory;
-      router->is_bad_exit = rs->is_bad_exit;
-      router->is_hs_dir = rs->is_hs_dir;
-    } else {
+    if (authdir) {
       /* If we _are_ an authority, we should check whether this router
        * is one that will cause us to need a reachability test. */
       routerinfo_t *old_router =
@@ -2123,7 +2022,7 @@ routers_update_status_from_consensus_networkstatus(smartlist_t *routers,
           dirserv_should_launch_reachability_test(router, old_router);
       }
     }
-    if (router->is_running && ds) {
+    if (rs->is_running && ds) {
       download_status_reset(&ds->v2_ns_dl_status);
     }
     if (reset_failures) {
@@ -2214,6 +2113,9 @@ networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now)
 
   statuses = smartlist_create();
   SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri, {
+    node_t *node = node_get_mutable_by_id(ri->cache_info.identity_digest);
+    if (!node)
+      continue;
     if (ri->cache_info.published_on < cutoff)
       continue;
     if (ri->purpose != purpose)
@@ -2221,7 +2123,7 @@ networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now)
     if (bridge_auth && ri->purpose == ROUTER_PURPOSE_BRIDGE)
       dirserv_set_router_is_running(ri, now);
     /* then generate and write out status lines for each of them */
-    set_routerstatus_from_routerinfo(&rs, ri, now, 0, 0, 0);
+    set_routerstatus_from_routerinfo(&rs, node, ri, now, 0, 0, 0);
     smartlist_add(statuses, networkstatus_getinfo_helper_single(&rs));
   });
 
diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h
index 61c475d..aa5496e 100644
--- a/src/or/networkstatus.h
+++ b/src/or/networkstatus.h
@@ -59,8 +59,9 @@ const routerstatus_t *router_get_consensus_status_by_descriptor_digest(
 routerstatus_t *router_get_mutable_consensus_status_by_descriptor_digest(
                                    networkstatus_t *consensus,
                                    const char *digest);
-const routerstatus_t *router_get_consensus_status_by_nickname(const char *nickname,
-                                                       int warn_if_unnamed);
+const routerstatus_t *router_get_consensus_status_by_nickname(
+                                   const char *nickname,
+                                   int warn_if_unnamed);
 const char *networkstatus_get_router_digest_by_nickname(const char *nickname);
 int networkstatus_nickname_is_unnamed(const char *nickname);
 void networkstatus_consensus_download_failed(int status_code,
diff --git a/src/or/nodelist.c b/src/or/nodelist.c
index b620b40..f2923a4 100644
--- a/src/or/nodelist.c
+++ b/src/or/nodelist.c
@@ -5,14 +5,17 @@
 /* See LICENSE for licensing information */
 
 #include "or.h"
-#include "nodelist.h"
+#include "config.h"
+#include "dirserv.h"
 #include "microdesc.h"
 #include "networkstatus.h"
+#include "nodelist.h"
+#include "router.h"
 #include "routerlist.h"
 
 #include <string.h>
 
-static void nodelist_drop_node(node_t *node);
+static void nodelist_drop_node(node_t *node, int remove_from_ht);
 static void node_free(node_t *node);
 
 /** A nodelist_t holds a node_t object for every router we're "willing to use
@@ -24,6 +27,7 @@ typedef struct nodelist_t {
   smartlist_t *nodes;
   /* Hash table to map from node ID digest to node. */
   HT_HEAD(nodelist_map, node_t) nodes_by_id;
+
 } nodelist_t;
 
 static INLINE unsigned int
@@ -63,10 +67,9 @@ init_nodelist(void)
   }
 }
 
-/** Return the node_t whose identity is <b>identity_digest</b>, or NULL
- * if no such node exists. */
+/** As node_get_by_id, but returns a non-const pointer */
 node_t *
-node_get_by_id(const char *identity_digest)
+node_get_mutable_by_id(const char *identity_digest)
 {
   node_t search, *node;
   if (PREDICT_UNLIKELY(the_nodelist == NULL))
@@ -77,6 +80,14 @@ node_get_by_id(const char *identity_digest)
   return node;
 }
 
+/** Return the node_t whose identity is <b>identity_digest</b>, or NULL
+ * if no such node exists. */
+const node_t *
+node_get_by_id(const char *identity_digest)
+{
+  return node_get_mutable_by_id(identity_digest);
+}
+
 /** Internal: return the node_t whose identity_digest is
  * <b>identity_digest</b>.  If none exists, create a new one, add it to the
  * nodelist, and return it.
@@ -88,7 +99,7 @@ node_get_or_create(const char *identity_digest)
 {
   node_t *node;
 
-  if ((node = node_get_by_id(identity_digest)))
+  if ((node = node_get_mutable_by_id(identity_digest)))
     return node;
 
   node = tor_malloc_zero(sizeof(node_t));
@@ -98,6 +109,8 @@ node_get_or_create(const char *identity_digest)
   smartlist_add(the_nodelist->nodes, node);
   node->nodelist_idx = smartlist_len(the_nodelist->nodes) - 1;
 
+  node->country = -1;
+
   return node;
 }
 
@@ -109,6 +122,16 @@ nodelist_add_routerinfo(routerinfo_t *ri)
   init_nodelist();
   node = node_get_or_create(ri->cache_info.identity_digest);
   node->ri = ri;
+
+  if (node->country == -1)
+    node_set_country(node);
+
+  if (authdir_mode(get_options())) {
+    const char *discard=NULL;
+    uint32_t status = dirserv_router_get_status(ri, &discard);
+    dirserv_set_node_flags_from_authoritative_status(node, status);
+  }
+
   return node;
 }
 
@@ -133,7 +156,7 @@ nodelist_add_microdesc(microdesc_t *md)
   rs = router_get_consensus_status_by_descriptor_digest(ns, md->digest);
   if (rs == NULL)
     return NULL;
-  node = node_get_by_id(rs->identity_digest);
+  node = node_get_mutable_by_id(rs->identity_digest);
   if (node)
     node->md = md;
   return node;
@@ -147,6 +170,8 @@ nodelist_add_microdesc(microdesc_t *md)
 void
 nodelist_set_consensus(networkstatus_t *ns)
 {
+  or_options_t *options = get_options();
+  int authdir = authdir_mode_v2(options) || authdir_mode_v3(options);
   init_nodelist();
 
   SMARTLIST_FOREACH(the_nodelist->nodes, node_t *, node,
@@ -162,8 +187,43 @@ nodelist_set_consensus(networkstatus_t *ns)
                                                        rs->descriptor_digest);
       }
     }
+
+    node_set_country(node);
+
+    /* If we're not an authdir, believe others. */
+    if (!authdir) {
+      node->is_valid = rs->is_valid;
+      node->is_running = rs->is_running;
+      node->is_fast = rs->is_fast;
+      node->is_stable = rs->is_stable;
+      node->is_possible_guard = rs->is_possible_guard;
+      node->is_exit = rs->is_exit;
+      node->is_bad_directory = rs->is_bad_directory;
+      node->is_bad_exit = rs->is_bad_exit;
+      node->is_hs_dir = rs->is_hs_dir;
+    }
+
   } SMARTLIST_FOREACH_END(rs);
+
   nodelist_purge();
+
+  if (! authdir) {
+    SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) {
+      /* We have no routerstatus for this router. Clear flags so we can skip
+       * it, maybe.*/
+      if (!node->rs) {
+        tor_assert(node->ri); /* if it had only an md, or nothing, purge
+                               * would have removed it. */
+        if (node->ri->purpose == ROUTER_PURPOSE_GENERAL) {
+          /* Clear all flags. */
+          node->is_valid = node->is_running = node->is_hs_dir =
+            node->is_fast = node->is_stable =
+            node->is_possible_guard = node->is_exit =
+            node->is_bad_exit = node->is_bad_directory = 0;
+        }
+      }
+    } SMARTLIST_FOREACH_END(node);
+  }
 }
 
 /** Helper: return true iff a node has a usable amount of information*/
@@ -178,7 +238,7 @@ node_is_usable(const node_t *node)
 void
 nodelist_remove_microdesc(const char *identity_digest, microdesc_t *md)
 {
-  node_t *node = node_get_by_id(identity_digest);
+  node_t *node = node_get_mutable_by_id(identity_digest);
   if (node && node->md == md)
     node->md = NULL;
 }
@@ -187,11 +247,11 @@ nodelist_remove_microdesc(const char *identity_digest, microdesc_t *md)
 void
 nodelist_remove_routerinfo(routerinfo_t *ri)
 {
-  node_t *node = node_get_by_id(ri->cache_info.identity_digest);
+  node_t *node = node_get_mutable_by_id(ri->cache_info.identity_digest);
   if (node && node->ri == ri) {
     node->ri = NULL;
     if (! node_is_usable(node)) {
-      nodelist_drop_node(node);
+      nodelist_drop_node(node, 1);
       node_free(node);
     }
   }
@@ -200,12 +260,14 @@ nodelist_remove_routerinfo(routerinfo_t *ri)
 /** Remove <b>node</b> from the nodelist.  (Asserts that it was there to begin
  * with.) */
 static void
-nodelist_drop_node(node_t *node)
+nodelist_drop_node(node_t *node, int remove_from_ht)
 {
   node_t *tmp;
   int idx;
-  tmp = HT_REMOVE(nodelist_map, &the_nodelist->nodes_by_id, node);
-  tor_assert(tmp == node);
+  if (remove_from_ht) {
+    tmp = HT_REMOVE(nodelist_map, &the_nodelist->nodes_by_id, node);
+    tor_assert(tmp == node);
+  }
 
   idx = node->nodelist_idx;
   tor_assert(idx >= 0);
@@ -238,6 +300,7 @@ nodelist_purge(void)
   if (PREDICT_UNLIKELY(the_nodelist == NULL))
     return;
 
+  /* Remove the non-usable nodes. */
   for (iter = HT_START(nodelist_map, &the_nodelist->nodes_by_id); iter; ) {
     node_t *node = *iter;
 
@@ -245,11 +308,11 @@ nodelist_purge(void)
       iter = HT_NEXT(nodelist_map, &the_nodelist->nodes_by_id, iter);
     } else {
       iter = HT_NEXT_RMV(nodelist_map, &the_nodelist->nodes_by_id, iter);
-      nodelist_drop_node(node);
+      nodelist_drop_node(node, 0);
       node_free(node);
     }
   }
-
+  nodelist_assert_ok();
 }
 
 /** Release all storage held by the nodelist. */
@@ -286,25 +349,25 @@ nodelist_assert_ok(void)
   /* every routerinfo in rl->routers should be in the nodelist. */
   if (rl) {
     SMARTLIST_FOREACH_BEGIN(rl->routers, routerinfo_t *, ri) {
-      node_t *node = node_get_by_id(ri->cache_info.identity_digest);
+      const node_t *node = node_get_by_id(ri->cache_info.identity_digest);
       tor_assert(node && node->ri == ri);
       tor_assert(0 == memcmp(ri->cache_info.identity_digest,
                              node->identity, DIGEST_LEN));
       tor_assert(! digestmap_get(dm, node->identity));
-      digestmap_set(dm, node->identity, node);
+      digestmap_set(dm, node->identity, (void*)node);
     } SMARTLIST_FOREACH_END(ri);
   }
 
   /* every routerstatus in ns should be in the nodelist */
   if (ns) {
     SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, routerstatus_t *, rs) {
-      node_t *node = node_get_by_id(rs->identity_digest);
+      const node_t *node = node_get_by_id(rs->identity_digest);
       tor_assert(node && node->rs == rs);
       tor_assert(0 == memcmp(rs->identity_digest, node->identity, DIGEST_LEN));
-      digestmap_set(dm, node->identity, node);
+      digestmap_set(dm, node->identity, (void*)node);
       if (ns->flavor == FLAV_MICRODESC) {
-        /* If it's a microdesc consensus, every entry that has a microdescriptor
-         * should be in the nodelist.
+        /* If it's a microdesc consensus, every entry that has a
+         * microdescriptor should be in the nodelist.
          */
         microdesc_t *md =
           microdesc_cache_lookup_by_digest256(NULL, rs->descriptor_digest);
@@ -326,3 +389,316 @@ nodelist_assert_ok(void)
   digestmap_free(dm, NULL);
 }
 
+/** Return a list of a node_t * for every node we know about.  The caller
+ * MUST NOT modify the list. (You can set and clear flags in the nodes if
+ * you must, but you must not add or remove nodes.) */
+smartlist_t *
+nodelist_get_list(void)
+{
+  init_nodelist();
+  return the_nodelist->nodes;
+}
+
+/** Given a nickname (possibly verbose, possibly a hexadecimal digest), return
+ * the corresponding node_t, or NULL if none exists.  Warn the user if
+ * <b>warn_if_unnamed</b> is set, and they have specified a router by
+ * nickname, but the Named flag isn't set for that router. */
+const node_t *
+node_get_by_nickname(const char *nickname, int warn_if_unnamed)
+{
+  char digest_buf[DIGEST_LEN];
+  char nn_buf[MAX_NICKNAME_LEN+1];
+  char nn_char='\0';
+
+  if (!the_nodelist)
+    return NULL;
+
+  /* ???? NM Naming authorities had an additional weird behavior here where
+     they would treat their own namings as slightly authoritative in a
+     strange and inconsistent way.  I think that this way is better, but we
+     could get the old behavior back if we wanted to by adding a function
+     to look in the fp_by_name table in fingerprint_list, and using this
+     function to override the name-to-digest lookup below if we are a
+     naming server.  -NM
+   */
+
+  /* Handle these cases: DIGEST, $DIGEST, $DIGEST=name, $DIGEST~name. */
+  if (hex_digest_nickname_decode(nickname, digest_buf, &nn_char, nn_buf)==0) {
+    const node_t *node = node_get_by_id(digest_buf);
+    if (!node)
+      return NULL;
+    if (nn_char) {
+      const char *real_name = node_get_nickname(node);
+      if (!real_name || strcasecmp(real_name, nn_buf))
+        return NULL;
+      if (nn_char == '=') {
+        const char *named_id =
+          networkstatus_get_router_digest_by_nickname(nn_buf);
+        if (!named_id || memcmp(named_id, digest_buf, DIGEST_LEN))
+          return NULL;
+      }
+    }
+    return node;
+  }
+
+  if (!strcasecmp(nickname, UNNAMED_ROUTER_NICKNAME))
+    return NULL;
+
+  /* Okay, so if we get here, the nickname is just a nickname.  Is there
+   * a binding for it in the consensus? */
+  {
+    const char *named_id =
+      networkstatus_get_router_digest_by_nickname(nickname);
+    if (named_id)
+      return node_get_by_id(named_id);
+  }
+
+  /* Is it marked as owned-by-someone-else? */
+  if (networkstatus_nickname_is_unnamed(nickname)) {
+    log_info(LD_GENERAL, "The name %s is listed as Unnamed: there is some "
+             "router that holds it, but not one listed in the current "
+             "consensus.", escaped(nickname));
+    return NULL;
+  }
+
+  /* Okay, so the name is not canonical for anybody. */
+  {
+    smartlist_t *matches = smartlist_create();
+    const node_t *choice = NULL;
+
+    SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) {
+      if (!strcasecmp(node_get_nickname(node), nickname))
+        smartlist_add(matches, node);
+    } SMARTLIST_FOREACH_END(node);
+
+    if (smartlist_len(matches)>1 && warn_if_unnamed) {
+      int any_unwarned = 0;
+      SMARTLIST_FOREACH_BEGIN(matches, node_t *, node) {
+        if (!node->name_lookup_warned) {
+          node->name_lookup_warned = 1;
+          any_unwarned = 1;
+        }
+      } SMARTLIST_FOREACH_END(node);
+
+      if (any_unwarned) {
+        log_warn(LD_CONFIG, "There are multiple matches for the name %s, "
+                 "but none is listed as Named in the directory consensus. "
+                 "Choosing one arbitrarily.", nickname);
+      }
+    } else if (smartlist_len(matches)>1 && warn_if_unnamed) {
+      char fp[HEX_DIGEST_LEN+1];
+      node_t *node = smartlist_get(matches, 0);
+      if (node->name_lookup_warned) {
+        base16_encode(fp, sizeof(fp), node->identity, DIGEST_LEN);
+        log_warn(LD_CONFIG,
+                 "You specified a server \"%s\" by name, but the directory "
+                 "authorities do not have any key registered for this "
+                 "nickname -- so it could be used by any server, not just "
+                 "the one you meant. "
+                 "To make sure you get the same server in the future, refer "
+                 "to it by key, as \"$%s\".", nickname, fp);
+        node->name_lookup_warned = 1;
+      }
+    }
+
+    if (smartlist_len(matches))
+      choice = smartlist_get(matches, 0);
+
+    smartlist_free(matches);
+    return choice;
+  }
+}
+
+/** Return the nickname of <b>node</b>, or NULL if we can't find one. */
+const char *
+node_get_nickname(const node_t *node)
+{
+  if (node->rs)
+    return node->rs->nickname;
+  else if (node->ri)
+    return node->ri->nickname;
+  else
+    return NULL;
+}
+
+/** Return true iff the nickname of <b>node</b> is canonical, based on the
+ * latest consensus. */
+int
+node_is_named(const node_t *node)
+{
+  const char *named_id;
+  const char *nickname = node_get_nickname(node);
+  if (!nickname)
+    return 0;
+  named_id = networkstatus_get_router_digest_by_nickname(nickname);
+  if (!named_id)
+    return 0;
+  return !memcmp(named_id, node->identity, DIGEST_LEN);
+}
+
+/** Return true iff <b>node</b> appears to be a directory authority or
+ * directory cache */
+int
+node_is_dir(const node_t *node)
+{
+  if (node->rs)
+    return node->rs->dir_port != 0;
+  else if (node->ri)
+    return node->ri->dir_port != 0;
+  else
+    return 0;
+}
+
+
+/** Return true iff <b>node</b> has either kind of usable descriptor -- that
+ * is, a routerdecriptor or a microdescriptor. */
+int
+node_has_descriptor(const node_t *node)
+{
+  return (node->ri ||
+          (node->rs && node->md));
+}
+
+/** Return the router_purpose of <b>node</b>. */
+int
+node_get_purpose(const node_t *node)
+{
+  if (node->ri)
+    return node->ri->purpose;
+  else
+    return ROUTER_PURPOSE_GENERAL;
+}
+
+/** Compute the verbose ("extended") nickname of <b>node</b> and store it
+ * into the MAX_VERBOSE_NICKNAME_LEN+1 character buffer at
+ * <b>verbose_nickname_out</b> */
+void
+node_get_verbose_nickname(const node_t *node,
+                          char *verbose_name_out)
+{
+  const char *nickname = node_get_nickname(node);
+  int is_named = node_is_named(node);
+  verbose_name_out[0] = '$';
+  base16_encode(verbose_name_out+1, HEX_DIGEST_LEN+1, node->identity,
+                DIGEST_LEN);
+  if (!nickname)
+    return;
+  verbose_name_out[1+HEX_DIGEST_LEN] = is_named ? '=' : '~';
+  strlcpy(verbose_name_out+1+HEX_DIGEST_LEN+1, nickname, MAX_NICKNAME_LEN+1);
+}
+
+/** Return true iff it seems that <b>node</b> allows circuits to exit
+ * through it directlry from the client. */
+int
+node_allows_single_hop_exits(const node_t *node)
+{
+  (void)node;
+  UNIMPLEMENTED_NODELIST();
+  return 0;
+}
+
+/** Return true iff it seems that <b>node</b> has an exit policy that
+ * doesn't actually permit anything to exit. */
+int
+node_exit_policy_rejects_all(const node_t *node)
+{
+  (void)node;
+  UNIMPLEMENTED_NODELIST();
+  return 0;
+}
+
+/** Copy the address for <b>node</b> into *<b>addr_out</b>. */
+int
+node_get_addr(const node_t *node, tor_addr_t *addr_out)
+{
+  if (node->ri) {
+    tor_addr_from_ipv4h(addr_out, node->ri->addr);
+    return 0;
+  } else if (node->rs) {
+    tor_addr_from_ipv4h(addr_out, node->rs->addr);
+    return 0;
+  }
+  return -1;
+}
+
+/** Return the host-order IPv4 address for <b>node</b>, or 0 if it doesn't
+ * seem to have one.  */
+uint32_t
+node_get_addr_ipv4h(const node_t *node)
+{
+  if (node->ri) {
+    return node->ri->addr;
+  } else if (node->rs) {
+    return node->rs->addr;
+  }
+  return 0;
+}
+
+/** Copy a string representation of the IP address for <b>node</b> into the
+ * <b>len</b>-byte buffer at <b>buf</b>.
+ */
+void
+node_get_address_string(const node_t *node, char *buf, size_t len)
+{
+  if (node->ri) {
+    strlcpy(buf, node->ri->address, len);
+  } else if (node->rs) {
+    tor_addr_t addr;
+    tor_addr_from_ipv4h(&addr, node->rs->addr);
+    tor_addr_to_str(buf, &addr, len, 0);
+  } else {
+    buf[0] = '\0';
+  }
+}
+
+/** Return <b>node</b>'s declared uptime, or -1 if it doesn't seem to have
+ * one. */
+long
+node_get_declared_uptime(const node_t *node)
+{
+  (void)node;
+  UNIMPLEMENTED_NODELIST();
+  return 0;
+}
+
+/** Return <b>node</b>'s declared or_port */
+uint16_t
+node_get_orport(const node_t *node)
+{
+  if (node->ri)
+    return node->ri->or_port;
+  else if (node->rs)
+    return node->rs->or_port;
+  else
+    return 0;
+}
+
+/** Return <b>node</b>'s platform string */
+const char *
+node_get_platform(const node_t *node)
+{
+  (void)node;
+  UNIMPLEMENTED_NODELIST();
+  return NULL;
+}
+
+/** Return <b>node</b>'s time of publication. */
+time_t
+node_get_published_on(const node_t *node)
+{
+  (void)node;
+  UNIMPLEMENTED_NODELIST();
+  return 0;
+}
+
+/** Return true iff <b>node</b> is one representing this router. */
+int
+node_is_me(const node_t *node)
+{
+  return router_digest_is_me(node->identity);
+}
+
+/* KILLTHIS XXXX NM -- it's a dummy to keep UNIMPLEMENTED_NODELIST()
+ * working */
+int unimplemented_nodelist_truth = 1;
+
diff --git a/src/or/nodelist.h b/src/or/nodelist.h
index 6a77ba6..3dceb75 100644
--- a/src/or/nodelist.h
+++ b/src/or/nodelist.h
@@ -12,7 +12,8 @@
 #ifndef _TOR_NODELIST_H
 #define _TOR_NODELIST_H
 
-node_t *node_get_by_id(const char *identity_digest);
+node_t *node_get_mutable_by_id(const char *identity_digest);
+const node_t *node_get_by_id(const char *identity_digest);
 node_t *nodelist_add_routerinfo(routerinfo_t *ri);
 node_t *nodelist_add_microdesc(microdesc_t *md);
 void nodelist_set_consensus(networkstatus_t *ns);
@@ -24,5 +25,41 @@ void nodelist_purge(void);
 void nodelist_free_all(void);
 void nodelist_assert_ok(void);
 
+const node_t *node_get_by_nickname(const char *nickname, int warn_if_unnamed);
+void node_get_verbose_nickname(const node_t *node,
+                               char *verbose_name_out);
+int node_is_named(const node_t *node);
+int node_is_dir(const node_t *node);
+int node_has_descriptor(const node_t *node);
+int node_get_purpose(const node_t *node);
+#define node_is_bridge(node) \
+  (node_get_purpose((node)) == ROUTER_PURPOSE_BRIDGE)
+int node_is_me(const node_t *node);
+int node_exit_policy_rejects_all(const node_t *node);
+int node_get_addr(const node_t *node, tor_addr_t *addr_out);
+uint32_t node_get_addr_ipv4h(const node_t *node);
+int node_allows_single_hop_exits(const node_t *node);
+uint16_t node_get_orport(const node_t *node);
+const char *node_get_nickname(const node_t *node);
+const char *node_get_platform(const node_t *node);
+void node_get_address_string(const node_t *node, char *cp, size_t len);
+long node_get_declared_uptime(const node_t *node);
+time_t node_get_published_on(const node_t *node);
+
+smartlist_t *nodelist_get_list(void);
+
+/* XXXX These need to move out of routerlist.c */
+void nodelist_refresh_countries(void);
+void node_set_country(node_t *node);
+void nodelist_add_node_family(smartlist_t *nodes, const node_t *node);
+int nodes_in_same_family(const node_t *node1, const node_t *node2);
+
+/* This means: implement this code or function or thing, nick! */
+#define UNIMPLEMENTED_NODELIST()                        \
+  while (unimplemented_nodelist_truth) {                \
+    tor_assert(0);                                      \
+  }
+extern int unimplemented_nodelist_truth;
+
 #endif
 
diff --git a/src/or/or.h b/src/or/or.h
index e46237c..d9be013 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -809,6 +809,9 @@ typedef enum {
  * Tor 0.1.2.x is obsolete, we can remove this. */
 #define DEFAULT_CLIENT_NICKNAME "client"
 
+/** Name chosen by routers that don't configure nicknames */
+#define UNNAMED_ROUTER_NICKNAME "Unnamed"
+
 /** Number of bytes in a SOCKS4 header. */
 #define SOCKS4_NETWORK_LEN 8
 
@@ -1510,52 +1513,44 @@ typedef struct {
   unsigned int allow_single_hop_exits:1;  /**< Whether the router says
                                            * it allows single hop exits. */
 
-  /* local info */
-  unsigned int is_running:1; /**< As far as we know, is this OR currently
-                              * running? */
-  unsigned int is_valid:1; /**< Has a trusted dirserver validated this OR?
-                               *  (For Authdir: Have we validated this OR?)
-                               */
-  unsigned int is_named:1; /**< Do we believe the nickname that this OR gives
-                            * us? */
-  unsigned int is_fast:1; /** Do we think this is a fast OR? */
-  unsigned int is_stable:1; /** Do we think this is a stable OR? */
-  unsigned int is_possible_guard:1; /**< Do we think this is an OK guard? */
-  unsigned int is_exit:1; /**< Do we think this is an OK exit? */
-  unsigned int is_bad_exit:1; /**< Do we think this exit is censored, borked,
-                               * or otherwise nasty? */
-  unsigned int is_bad_directory:1; /**< Do we think this directory is junky,
-                                    * underpowered, or otherwise useless? */
   unsigned int wants_to_be_hs_dir:1; /**< True iff this router claims to be
                                       * a hidden service directory. */
-  unsigned int is_hs_dir:1; /**< True iff this router is a hidden service
-                             * directory according to the authorities. */
   unsigned int policy_is_reject_star:1; /**< True iff the exit policy for this
                                          * router rejects everything. */
   /** True if, after we have added this router, we should re-launch
    * tests for it. */
   unsigned int needs_retest_if_added:1;
 
-/** Tor can use this router for general positions in circuits. */
+/** Tor can use this router for general positions in circuits; we got it
+ * from a directory server as usual, or we're an authority and a server
+ * uploaded it. */
 #define ROUTER_PURPOSE_GENERAL 0
-/** Tor should avoid using this router for circuit-building. */
+/** Tor should avoid using this router for circuit-building: we got it
+ * from a crontroller.  If the controller wants to use it, it'll have to
+ * ask for it by identity. */
 #define ROUTER_PURPOSE_CONTROLLER 1
-/** Tor should use this router only for bridge positions in circuits. */
+/** Tor should use this router only for bridge positions in circuits: we got
+ * it via a directory request from the bridge itself, or a bridge
+ * authority. x*/
 #define ROUTER_PURPOSE_BRIDGE 2
 /** Tor should not use this router; it was marked in cached-descriptors with
  * a purpose we didn't recognize. */
 #define ROUTER_PURPOSE_UNKNOWN 255
 
-  uint8_t purpose; /** What positions in a circuit is this router good for? */
+  /* In what way did we find out about this router?  One of ROUTER_PURPOSE_*.
+   * Routers of different purposes are kept segregated and used for different
+   * things; see notes on ROUTER_PURPOSE_* macros above.
+   */
+  uint8_t purpose;
 
   /* The below items are used only by authdirservers for
    * reachability testing. */
+
   /** When was the last time we could reach this OR? */
   time_t last_reachable;
   /** When did we start testing reachability for this OR? */
   time_t testing_since;
-  /** According to the geoip db what country is this router in? */
-  country_t country;
+
 } routerinfo_t;
 
 /** Information needed to keep and cache a signed extra-info document. */
@@ -1643,9 +1638,6 @@ typedef struct routerstatus_t {
    * from this authority.)  Applies in v2 networkstatus document only.
    */
   unsigned int need_to_mirror:1;
-  unsigned int name_lookup_warned:1; /**< Have we warned the user for referring
-                                      * to this (unnamed) router by nickname?
-                                      */
   time_t last_dir_503_at; /**< When did this router last tell us that it
                            * was too busy to serve directory info? */
   download_status_t dl_status;
@@ -1698,17 +1690,66 @@ typedef struct microdesc_t {
                       * XXX this probably should not stay a string. */
 } microdesc_t;
 
-/** DOCDOC */
+/** A node_t represents a Tor router.
+ *
+ * Specifically, a node_t is a Tor router as we are using it: a router that
+ * we are considering for circuits, connections, and so on.  A node_t is a
+ * thin wrapper around the routerstatus, routerinfo, and microdesc for a
+ * single wrapper, and provides a consistent interface for all of them.
+ *
+ * Also, a node_t has mutable state.  While a routerinfo, a routerstatus,
+ * and a microdesc have[*] only the information read from a router
+ * descriptor, a consensus entry, and a microdescriptor (respectively)...
+ * a node_t has flags based on *our own current opinion* of the node.
+ *
+ * [*] Actually, there is some leftover information in each that is mutable.
+ *  We should try to excise that.
+ */
 typedef struct node_t {
+  /* Indexing information */
+
   /** Used to look up the node_t by its identity digest. */
   HT_ENTRY(node_t) ht_ent;
   /** Position of the node within the list of nodes */
   int nodelist_idx;
 
+  /** The identity digest of this node_t.  No more than one node_t per
+   * identity may exist at a time. */
   char identity[DIGEST_LEN];
+
   microdesc_t *md;
   routerinfo_t *ri;
   routerstatus_t *rs;
+
+  /* local info: copied from routerstatus, then possibly frobbed based
+   * on experience.  Authorities set this stuff directly. */
+
+  unsigned int is_running:1; /**< As far as we know, is this OR currently
+                              * running? */
+  unsigned int is_valid:1; /**< Has a trusted dirserver validated this OR?
+                               *  (For Authdir: Have we validated this OR?)
+                               */
+  unsigned int is_fast:1; /** Do we think this is a fast OR? */
+  unsigned int is_stable:1; /** Do we think this is a stable OR? */
+  unsigned int is_possible_guard:1; /**< Do we think this is an OK guard? */
+  unsigned int is_exit:1; /**< Do we think this is an OK exit? */
+  unsigned int is_bad_exit:1; /**< Do we think this exit is censored, borked,
+                               * or otherwise nasty? */
+  unsigned int is_bad_directory:1; /**< Do we think this directory is junky,
+                                    * underpowered, or otherwise useless? */
+  unsigned int is_hs_dir:1; /**< True iff this router is a hidden service
+                             * directory according to the authorities. */
+
+  /* Local info: warning state. */
+
+  unsigned int name_lookup_warned:1; /**< Have we warned the user for referring
+                                      * to this (unnamed) router by nickname?
+                                      */
+
+  /* Local info: derived. */
+
+  /** According to the geoip db what country is this router in? */
+  country_t country;
 } node_t;
 
 /** How many times will we try to download a router's descriptor before giving
@@ -3548,7 +3589,8 @@ typedef enum {
   CRN_NEED_GUARD = 1<<2,
   CRN_ALLOW_INVALID = 1<<3,
   /* XXXX not used, apparently. */
-  CRN_WEIGHT_AS_EXIT = 1<<5
+  CRN_WEIGHT_AS_EXIT = 1<<5,
+  CRN_NEED_DESC = 1<<6
 } router_crn_flags_t;
 
 /** Return value for router_add_to_routerlist() and dirserv_add_descriptor() */
diff --git a/src/or/policies.c b/src/or/policies.c
index b4d3595..1404e20 100644
--- a/src/or/policies.c
+++ b/src/or/policies.c
@@ -11,6 +11,7 @@
 #include "or.h"
 #include "config.h"
 #include "dirserv.h"
+#include "nodelist.h"
 #include "policies.h"
 #include "routerparse.h"
 #include "ht.h"
@@ -269,6 +270,22 @@ fascist_firewall_allows_or(const routerinfo_t *ri)
   return fascist_firewall_allows_address_or(&addr, ri->or_port);
 }
 
+/** Return true iff we think our firewall will let us make an OR connection to
+ * <b>node</b>. */
+int
+fascist_firewall_allows_node(const node_t *node)
+{
+  if (node->ri) {
+    return fascist_firewall_allows_or(node->ri);
+  } else if (node->rs) {
+    tor_addr_t addr;
+    tor_addr_from_ipv4h(&addr, node->rs->addr);
+    return fascist_firewall_allows_address_or(&addr, node->rs->or_port);
+  } else {
+    return 1;
+  }
+}
+
 /** Return true iff we think our firewall will let us make a directory
  * connection to addr:port. */
 int
@@ -858,6 +875,7 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
   return 0;
 }
 
+#if 0
 /** Replace the exit policy of <b>r</b> with reject *:*. */
 void
 policies_set_router_exitpolicy_to_reject_all(routerinfo_t *r)
@@ -868,6 +886,15 @@ policies_set_router_exitpolicy_to_reject_all(routerinfo_t *r)
   item = router_parse_addr_policy_item_from_string("reject *:*", -1);
   smartlist_add(r->exit_policy, item);
 }
+#endif
+
+/** Replace the exit policy of <b>node</b> with reject *:* */
+void
+policies_set_node_exitpolicy_to_reject_all(node_t *node)
+{
+  (void)node;
+  UNIMPLEMENTED_NODELIST();
+}
 
 /** Return 1 if there is at least one /8 subnet in <b>policy</b> that
  * allows exiting to <b>port</b>.  Otherwise, return 0. */
@@ -1288,6 +1315,31 @@ policy_summarize(smartlist_t *policy)
   return result;
 }
 
+/** Decides whether addr:port is probably or definitely accepted or rejcted by
+ * <b>node</b>.  See compare_tor_addr_to_addr_policy for details on addr/port
+ * interpretation. */
+addr_policy_result_t
+compare_addr_to_node_policy(uint32_t addr, uint16_t port, const node_t *node)
+{
+  tor_addr_t a;
+  tor_addr_from_ipv4h(&a, addr);
+  return compare_tor_addr_to_node_policy(&a, port, node);
+}
+
+/** Decides whether addr:port is probably or definitely accepted or rejcted by
+ * <b>node</b>.  See compare_tor_addr_to_addr_policy for details on addr/port
+ * interpretation. */
+addr_policy_result_t
+compare_tor_addr_to_node_policy(const tor_addr_t *addr, uint16_t port,
+                                const node_t *node)
+{
+  (void)addr;
+  (void)port;
+  (void)node;
+  UNIMPLEMENTED_NODELIST();
+  return 0;
+}
+
 /** Implementation for GETINFO control command: knows the answer for questions
  * about "exit-policy/..." */
 int
diff --git a/src/or/policies.h b/src/or/policies.h
index 4a34745..acc254d 100644
--- a/src/or/policies.h
+++ b/src/or/policies.h
@@ -20,6 +20,7 @@
 int firewall_is_fascist_or(void);
 int fascist_firewall_allows_address_or(const tor_addr_t *addr, uint16_t port);
 int fascist_firewall_allows_or(const routerinfo_t *ri);
+int fascist_firewall_allows_node(const node_t *node);
 int fascist_firewall_allows_address_dir(const tor_addr_t *addr, uint16_t port);
 int dir_policy_permits_address(const tor_addr_t *addr);
 int socks_policy_permits_address(const tor_addr_t *addr);
@@ -38,10 +39,16 @@ addr_policy_result_t compare_tor_addr_to_addr_policy(const tor_addr_t *addr,
                               uint16_t port, const smartlist_t *policy);
 addr_policy_result_t compare_addr_to_addr_policy(uint32_t addr,
                               uint16_t port, const smartlist_t *policy);
+
+addr_policy_result_t compare_addr_to_node_policy(uint32_t addr,
+                              uint16_t port, const node_t *node);
+addr_policy_result_t compare_tor_addr_to_node_policy(const tor_addr_t *addr,
+                              uint16_t port, const node_t *node);
+
 int policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
                                int rejectprivate, const char *local_address,
                                int add_default_policy);
-void policies_set_router_exitpolicy_to_reject_all(routerinfo_t *exitrouter);
+void policies_set_node_exitpolicy_to_reject_all(node_t *exitrouter);
 int exit_policy_is_general_exit(smartlist_t *policy);
 int policy_is_reject_star(const smartlist_t *policy);
 int getinfo_helper_policies(control_connection_t *conn,
diff --git a/src/or/relay.c b/src/or/relay.c
index 48948f0..c645b72 100644
--- a/src/or/relay.c
+++ b/src/or/relay.c
@@ -24,6 +24,7 @@
 #include "main.h"
 #include "mempool.h"
 #include "networkstatus.h"
+#include "nodelist.h"
 #include "policies.h"
 #include "reasons.h"
 #include "relay.h"
@@ -712,7 +713,7 @@ connection_ap_process_end_not_open(
     edge_connection_t *conn, crypt_path_t *layer_hint)
 {
   struct in_addr in;
-  routerinfo_t *exitrouter;
+  node_t *exitrouter;
   int reason = *(cell->payload+RELAY_HEADER_SIZE);
   int control_reason = reason | END_STREAM_REASON_FLAG_REMOTE;
   (void) layer_hint; /* unused */
@@ -725,7 +726,7 @@ connection_ap_process_end_not_open(
     log_info(LD_APP,"Address '%s' refused due to '%s'. Considering retrying.",
              safe_str(conn->socks_request->address),
              stream_end_reason_to_string(reason));
-    exitrouter = router_get_mutable_by_digest(chosen_exit_digest);
+    exitrouter = node_get_mutable_by_id(chosen_exit_digest);
     switch (reason) {
       case END_STREAM_REASON_EXITPOLICY:
         if (rh->length >= 5) {
@@ -760,8 +761,8 @@ connection_ap_process_end_not_open(
           log_info(LD_APP,
                  "Exitrouter '%s' seems to be more restrictive than its exit "
                  "policy. Not using this router as exit for now.",
-                 exitrouter->nickname);
-          policies_set_router_exitpolicy_to_reject_all(exitrouter);
+                 node_get_nickname(exitrouter));
+          policies_set_node_exitpolicy_to_reject_all(exitrouter);
         }
         /* rewrite it to an IP if we learned one. */
         if (addressmap_rewrite(conn->socks_request->address,
@@ -824,7 +825,7 @@ connection_ap_process_end_not_open(
       case END_STREAM_REASON_HIBERNATING:
       case END_STREAM_REASON_RESOURCELIMIT:
         if (exitrouter) {
-          policies_set_router_exitpolicy_to_reject_all(exitrouter);
+          policies_set_node_exitpolicy_to_reject_all(exitrouter);
         }
         if (conn->chosen_exit_optional) {
           /* stop wanting a specific exit */
diff --git a/src/or/rendclient.c b/src/or/rendclient.c
index 67bd55a..5c5e1bf 100644
--- a/src/or/rendclient.c
+++ b/src/or/rendclient.c
@@ -16,6 +16,7 @@
 #include "connection_edge.h"
 #include "directory.h"
 #include "main.h"
+#include "nodelist.h"
 #include "relay.h"
 #include "rendclient.h"
 #include "rendcommon.h"
@@ -738,7 +739,6 @@ rend_client_get_random_intro(const rend_data_t *rend_query)
   int i;
   rend_cache_entry_t *entry;
   rend_intro_point_t *intro;
-  const routerinfo_t *router;
 
   if (rend_cache_lookup_entry(rend_query->onion_address, -1, &entry) < 1) {
     log_warn(LD_REND,
@@ -755,8 +755,8 @@ rend_client_get_random_intro(const rend_data_t *rend_query)
   intro = smartlist_get(entry->parsed->intro_nodes, i);
   /* Do we need to look up the router or is the extend info complete? */
   if (!intro->extend_info->onion_key) {
-    router = router_get_by_nickname(intro->extend_info->nickname, 0);
-    if (!router) {
+    const node_t *node = node_get_by_nickname(intro->extend_info->nickname, 0);
+    if (!node) {
       log_info(LD_REND, "Unknown router with nickname '%s'; trying another.",
                intro->extend_info->nickname);
       rend_intro_point_free(intro);
@@ -764,7 +764,7 @@ rend_client_get_random_intro(const rend_data_t *rend_query)
       goto again;
     }
     extend_info_free(intro->extend_info);
-    intro->extend_info = extend_info_from_router(router);
+    intro->extend_info = extend_info_from_node(node);
   }
   return extend_info_dup(intro->extend_info);
 }
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index f6769d5..f67e9ab 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -14,6 +14,7 @@
 #include "config.h"
 #include "directory.h"
 #include "networkstatus.h"
+#include "nodelist.h"
 #include "rendclient.h"
 #include "rendcommon.h"
 #include "rendservice.h"
@@ -1001,7 +1002,7 @@ rend_service_introduce(origin_circuit_t *circuit, const char *request,
   } else {
     char *rp_nickname;
     size_t nickname_field_len;
-    const routerinfo_t *router;
+    const node_t *node;
     int version;
     if (*buf == 1) {
       rp_nickname = buf+1;
@@ -1028,8 +1029,8 @@ rend_service_introduce(origin_circuit_t *circuit, const char *request,
     len -= nickname_field_len;
     len -= rp_nickname - buf; /* also remove header space used by version, if
                                * any */
-    router = router_get_by_nickname(rp_nickname, 0);
-    if (!router) {
+    node = node_get_by_nickname(rp_nickname, 0);
+    if (!node) {
       log_info(LD_REND, "Couldn't find router %s named in introduce2 cell.",
                escaped_safe_str_client(rp_nickname));
       /* XXXX Add a no-such-router reason? */
@@ -1037,7 +1038,7 @@ rend_service_introduce(origin_circuit_t *circuit, const char *request,
       goto err;
     }
 
-    extend_info = extend_info_from_router(router);
+    extend_info = extend_info_from_node(node);
   }
 
   if (len != REND_COOKIE_LEN+DH_KEY_LEN) {
@@ -1754,19 +1755,19 @@ void
 rend_services_introduce(void)
 {
   int i,j,r;
-  const routerinfo_t *router;
+  const node_t *node;
   rend_service_t *service;
   rend_intro_point_t *intro;
   int changed, prev_intro_nodes;
-  smartlist_t *intro_routers;
+  smartlist_t *intro_nodes;
   time_t now;
   or_options_t *options = get_options();
 
-  intro_routers = smartlist_create();
+  intro_nodes = smartlist_create();
   now = time(NULL);
 
   for (i=0; i < smartlist_len(rend_service_list); ++i) {
-    smartlist_clear(intro_routers);
+    smartlist_clear(intro_nodes);
     service = smartlist_get(rend_service_list, i);
 
     tor_assert(service);
@@ -1786,8 +1787,8 @@ rend_services_introduce(void)
        service. */
     for (j=0; j < smartlist_len(service->intro_nodes); ++j) {
       intro = smartlist_get(service->intro_nodes, j);
-      router = router_get_by_digest(intro->extend_info->identity_digest);
-      if (!router || !find_intro_circuit(intro, service->pk_digest)) {
+      node = node_get_by_id(intro->extend_info->identity_digest);
+      if (!node || !find_intro_circuit(intro, service->pk_digest)) {
         log_info(LD_REND,"Giving up on %s as intro point for %s.",
                  intro->extend_info->nickname, service->service_id);
         if (service->desc) {
@@ -1806,8 +1807,8 @@ rend_services_introduce(void)
         smartlist_del(service->intro_nodes,j--);
         changed = 1;
       }
-      if (router)
-        smartlist_add(intro_routers, (void*)router);
+      if (node)
+        smartlist_add(intro_nodes, (void*)node);
     }
 
     /* We have enough intro points, and the intro points we thought we had were
@@ -1836,26 +1837,26 @@ rend_services_introduce(void)
 #define NUM_INTRO_POINTS_INIT (NUM_INTRO_POINTS + 2)
     for (j=prev_intro_nodes; j < (prev_intro_nodes == 0 ?
              NUM_INTRO_POINTS_INIT : NUM_INTRO_POINTS); ++j) {
-      router_crn_flags_t flags = CRN_NEED_UPTIME;
+      router_crn_flags_t flags = CRN_NEED_UPTIME|CRN_NEED_DESC;
       if (get_options()->_AllowInvalid & ALLOW_INVALID_INTRODUCTION)
         flags |= CRN_ALLOW_INVALID;
-      router = router_choose_random_node(intro_routers,
-                                         options->ExcludeNodes, flags);
-      if (!router) {
+      node = router_choose_random_node(intro_nodes,
+                                       options->ExcludeNodes, flags);
+      if (!node) {
         log_warn(LD_REND,
                  "Could only establish %d introduction points for %s.",
                  smartlist_len(service->intro_nodes), service->service_id);
         break;
       }
       changed = 1;
-      smartlist_add(intro_routers, (void*)router);
+      smartlist_add(intro_nodes, (void*)node);
       intro = tor_malloc_zero(sizeof(rend_intro_point_t));
-      intro->extend_info = extend_info_from_router(router);
+      intro->extend_info = extend_info_from_node(node);
       intro->intro_key = crypto_new_pk_env();
       tor_assert(!crypto_pk_generate_key(intro->intro_key));
       smartlist_add(service->intro_nodes, intro);
       log_info(LD_REND, "Picked router %s as an intro point for %s.",
-               router->nickname, service->service_id);
+               node_get_nickname(node), service->service_id);
     }
 
     /* If there's no need to launch new circuits, stop here. */
@@ -1872,7 +1873,7 @@ rend_services_introduce(void)
       }
     }
   }
-  smartlist_free(intro_routers);
+  smartlist_free(intro_nodes);
 }
 
 /** Regenerate and upload rendezvous service descriptors for all
diff --git a/src/or/rephist.c b/src/or/rephist.c
index d998e87..97a3c54 100644
--- a/src/or/rephist.c
+++ b/src/or/rephist.c
@@ -14,6 +14,7 @@
 #include "circuitlist.h"
 #include "circuituse.h"
 #include "config.h"
+#include "nodelist.h"
 #include "rephist.h"
 #include "router.h"
 #include "routerlist.h"
@@ -579,7 +580,7 @@ rep_hist_dump_stats(time_t now, int severity)
   size_t len;
   int ret;
   unsigned long upt, downt;
-  const routerinfo_t *r;
+  const node_t *node;
 
   rep_history_clean(now - get_options()->RephistTrackTime);
 
@@ -593,8 +594,8 @@ rep_hist_dump_stats(time_t now, int severity)
     digestmap_iter_get(orhist_it, &digest1, &or_history_p);
     or_history = (or_history_t*) or_history_p;
 
-    if ((r = router_get_by_digest(digest1)))
-      name1 = r->nickname;
+    if ((node = node_get_by_id(digest1)) && node_get_nickname(node))
+      name1 = node_get_nickname(node);
     else
       name1 = "(unknown)";
     base16_encode(hexdigest1, sizeof(hexdigest1), digest1, DIGEST_LEN);
@@ -624,8 +625,8 @@ rep_hist_dump_stats(time_t now, int severity)
            lhist_it = digestmap_iter_next(or_history->link_history_map,
                                           lhist_it)) {
         digestmap_iter_get(lhist_it, &digest2, &link_history_p);
-        if ((r = router_get_by_digest(digest2)))
-          name2 = r->nickname;
+        if ((node = node_get_by_id(digest2)) && node_get_nickname(node))
+          name2 = node_get_nickname(node);
         else
           name2 = "(unknown)";
 
@@ -871,28 +872,28 @@ rep_hist_get_router_stability_doc(time_t now)
   }
 
   DIGESTMAP_FOREACH(history_map, id, or_history_t *, hist) {
-    const routerinfo_t *ri;
+    const node_t *node;
     char dbuf[BASE64_DIGEST_LEN+1];
     char header_buf[512];
     char *info;
     digest_to_base64(dbuf, id);
-    ri = router_get_by_digest(id);
-    if (ri) {
-      char *ip = tor_dup_ip(ri->addr);
+    node = node_get_by_id(id);
+    if (node) {
+      char ip[INET_NTOA_BUF_LEN+1];
       char tbuf[ISO_TIME_LEN+1];
-      format_iso_time(tbuf, ri->cache_info.published_on);
+      node_get_address_string(node,ip,sizeof(ip));
+      format_iso_time(tbuf, node_get_published_on(node));
       tor_snprintf(header_buf, sizeof(header_buf),
                    "router %s %s %s\n"
                    "published %s\n"
                    "relevant-flags %s%s%s\n"
                    "declared-uptime %ld\n",
-                   dbuf, ri->nickname, ip,
+                   dbuf, node_get_nickname(node), ip,
                    tbuf,
-                   ri->is_running ? "Running " : "",
-                   ri->is_valid ? "Valid " : "",
-                   ri->is_hibernating ? "Hibernating " : "",
-                   ri->uptime);
-      tor_free(ip);
+                   node->is_running ? "Running " : "",
+                   node->is_valid ? "Valid " : "",
+                   node->ri && node->ri->is_hibernating ? "Hibernating " : "",
+                   node_get_declared_uptime(node));
     } else {
       tor_snprintf(header_buf, sizeof(header_buf),
                    "router %s {no descriptor}\n", dbuf);
diff --git a/src/or/router.c b/src/or/router.c
index dfb2f06..507785e 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -7,6 +7,7 @@
 #define ROUTER_PRIVATE
 
 #include "or.h"
+#include "circuitbuild.h"
 #include "circuitlist.h"
 #include "circuituse.h"
 #include "config.h"
@@ -19,6 +20,7 @@
 #include "hibernate.h"
 #include "main.h"
 #include "networkstatus.h"
+#include "nodelist.h"
 #include "policies.h"
 #include "relay.h"
 #include "rephist.h"
@@ -780,11 +782,14 @@ consider_testing_reachability(int test_or, int test_dir)
     return;
 
   if (test_or && (!orport_reachable || !circuit_enough_testing_circs())) {
+    extend_info_t *ei;
     log_info(LD_CIRC, "Testing %s of my ORPort: %s:%d.",
              !orport_reachable ? "reachability" : "bandwidth",
              me->address, me->or_port);
-    circuit_launch_by_router(CIRCUIT_PURPOSE_TESTING, me,
-                             CIRCLAUNCH_NEED_CAPACITY|CIRCLAUNCH_IS_INTERNAL);
+    ei = extend_info_from_router(me);
+    circuit_launch_by_extend_info(CIRCUIT_PURPOSE_TESTING, ei,
+                            CIRCLAUNCH_NEED_CAPACITY|CIRCLAUNCH_IS_INTERNAL);
+    extend_info_free(ei);
   }
 
   tor_addr_from_ipv4h(&addr, me->addr);
@@ -1333,13 +1338,12 @@ router_rebuild_descriptor(int force)
   ri->policy_is_reject_star =
     policy_is_reject_star(ri->exit_policy);
 
-  if (desc_routerinfo) { /* inherit values */
-    ri->is_valid = desc_routerinfo->is_valid;
-    ri->is_running = desc_routerinfo->is_running;
-    ri->is_named = desc_routerinfo->is_named;
-  }
+#if 0
+  /* XXXX NM NM I belive this is safe to remove */
   if (authdir_mode(options))
     ri->is_valid = ri->is_named = 1; /* believe in yourself */
+#endif
+
   if (options->MyFamily) {
     smartlist_t *family;
     if (!warned_nonexistent_family)
@@ -1349,11 +1353,11 @@ router_rebuild_descriptor(int force)
     smartlist_split_string(family, options->MyFamily, ",",
       SPLIT_SKIP_SPACE|SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
     SMARTLIST_FOREACH_BEGIN(family, char *, name) {
-        const routerinfo_t *member;
+       const node_t *member;
        if (!strcasecmp(name, options->Nickname))
-         member = ri;
+         goto skip; /* Don't list ourself, that's redundant */
        else
-         member = router_get_by_nickname(name, 1);
+         member = node_get_by_nickname(name, 1);
        if (!member) {
          int is_legal = is_legal_nickname_or_hexdigest(name);
          if (!smartlist_string_isin(warned_nonexistent_family, name) &&
@@ -1373,17 +1377,19 @@ router_rebuild_descriptor(int force)
            smartlist_add(ri->declared_family, name);
            name = NULL;
          }
-       } else if (router_is_me(member)) {
+       } else if (router_digest_is_me(member->identity)) {
          /* Don't list ourself in our own family; that's redundant */
+         /* XXX shouldn't be possible */
        } else {
          char *fp = tor_malloc(HEX_DIGEST_LEN+2);
          fp[0] = '$';
          base16_encode(fp+1,HEX_DIGEST_LEN+1,
-                       member->cache_info.identity_digest, DIGEST_LEN);
+                       member->identity, DIGEST_LEN);
          smartlist_add(ri->declared_family, fp);
          if (smartlist_string_isin(warned_nonexistent_family, name))
            smartlist_string_remove(warned_nonexistent_family, name);
        }
+    skip:
        tor_free(name);
     } SMARTLIST_FOREACH_END(name);
 
@@ -1443,8 +1449,6 @@ router_rebuild_descriptor(int force)
                          strlen(ri->cache_info.signed_descriptor_body),
                          ri->cache_info.signed_descriptor_digest);
 
-  routerinfo_set_country(ri);
-
   tor_assert(! routerinfo_incompatible_with_extrainfo(ri, ei, NULL, NULL));
 
   routerinfo_free(desc_routerinfo);
@@ -2098,10 +2102,15 @@ is_legal_hexdigest(const char *s)
 void
 router_get_verbose_nickname(char *buf, const routerinfo_t *router)
 {
+  const char *good_digest = networkstatus_get_router_digest_by_nickname(
+                                                         router->nickname);
+  int is_named = good_digest && !memcmp(good_digest,
+                                        router->cache_info.identity_digest,
+                                        DIGEST_LEN);
   buf[0] = '$';
   base16_encode(buf+1, HEX_DIGEST_LEN+1, router->cache_info.identity_digest,
                 DIGEST_LEN);
-  buf[1+HEX_DIGEST_LEN] = router->is_named ? '=' : '~';
+  buf[1+HEX_DIGEST_LEN] = is_named ? '=' : '~';
   strlcpy(buf+1+HEX_DIGEST_LEN+1, router->nickname, MAX_NICKNAME_LEN+1);
 }
 
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index 0f1bfc0..e456f43 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -1072,7 +1072,7 @@ router_pick_trusteddirserver(authority_type_t type, int flags)
 static const routerstatus_t *
 router_pick_directory_server_impl(authority_type_t type, int flags)
 {
-  const routerstatus_t *result;
+  const node_t *result;
   smartlist_t *direct, *tunnel;
   smartlist_t *trusted_direct, *trusted_tunnel;
   smartlist_t *overloaded_direct, *overloaded_tunnel;
@@ -1093,49 +1093,54 @@ router_pick_directory_server_impl(authority_type_t type, int flags)
   overloaded_tunnel = smartlist_create();
 
   /* Find all the running dirservers we know about. */
-  SMARTLIST_FOREACH_BEGIN(consensus->routerstatus_list, routerstatus_t *,
-                          status) {
+  SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), const node_t *, node) {
     int is_trusted;
-    int is_overloaded = status->last_dir_503_at + DIR_503_TIMEOUT > now;
+    int is_overloaded;
     tor_addr_t addr;
-    if (!status->is_running || !status->dir_port || !status->is_valid)
+    const routerstatus_t *status = node->rs;
+    if (!status)
+      continue;
+
+    if (!node->is_running || !status->dir_port || !node->is_valid)
       continue;
-    if (status->is_bad_directory)
+    if (node->is_bad_directory)
       continue;
-    if (requireother && router_digest_is_me(status->identity_digest))
+    if (requireother && router_digest_is_me(node->identity))
       continue;
     if (type & V3_AUTHORITY) {
       if (!(status->version_supports_v3_dir ||
-            router_digest_is_trusted_dir_type(status->identity_digest,
+            router_digest_is_trusted_dir_type(node->identity,
                                               V3_AUTHORITY)))
         continue;
     }
-    is_trusted = router_digest_is_trusted_dir(status->identity_digest);
-    if ((type & V2_AUTHORITY) && !(status->is_v2_dir || is_trusted))
+    is_trusted = router_digest_is_trusted_dir(node->identity);
+    if ((type & V2_AUTHORITY) && !(node->rs->is_v2_dir || is_trusted))
       continue;
     if ((type & EXTRAINFO_CACHE) &&
-        !router_supports_extrainfo(status->identity_digest, 0))
+        !router_supports_extrainfo(node->identity, 0))
       continue;
 
     /* XXXX IP6 proposal 118 */
-    tor_addr_from_ipv4h(&addr, status->addr);
+    tor_addr_from_ipv4h(&addr, node->rs->addr);
+
+    is_overloaded = status->last_dir_503_at + DIR_503_TIMEOUT > now;
 
     if (prefer_tunnel &&
         status->version_supports_begindir &&
         (!fascistfirewall ||
          fascist_firewall_allows_address_or(&addr, status->or_port)))
       smartlist_add(is_trusted ? trusted_tunnel :
-                      is_overloaded ? overloaded_tunnel : tunnel, status);
+                    is_overloaded ? overloaded_tunnel : tunnel, (void*)node);
     else if (!fascistfirewall ||
              fascist_firewall_allows_address_dir(&addr, status->dir_port))
       smartlist_add(is_trusted ? trusted_direct :
-                      is_overloaded ? overloaded_direct : direct, status);
-  } SMARTLIST_FOREACH_END(status);
+                    is_overloaded ? overloaded_direct : direct, (void*)node);
+  } SMARTLIST_FOREACH_END(node);
 
   if (smartlist_len(tunnel)) {
-    result = routerstatus_sl_choose_by_bandwidth(tunnel, WEIGHT_FOR_DIR);
+    result = node_sl_choose_by_bandwidth(tunnel, WEIGHT_FOR_DIR);
   } else if (smartlist_len(overloaded_tunnel)) {
-    result = routerstatus_sl_choose_by_bandwidth(overloaded_tunnel,
+    result = node_sl_choose_by_bandwidth(overloaded_tunnel,
                                                  WEIGHT_FOR_DIR);
   } else if (smartlist_len(trusted_tunnel)) {
     /* FFFF We don't distinguish between trusteds and overloaded trusteds
@@ -1144,10 +1149,10 @@ router_pick_directory_server_impl(authority_type_t type, int flags)
      * is a feature, but it could easily be a bug. -RD */
     result = smartlist_choose(trusted_tunnel);
   } else if (smartlist_len(direct)) {
-    result = routerstatus_sl_choose_by_bandwidth(direct, WEIGHT_FOR_DIR);
+    result = node_sl_choose_by_bandwidth(direct, WEIGHT_FOR_DIR);
   } else if (smartlist_len(overloaded_direct)) {
-    result = routerstatus_sl_choose_by_bandwidth(overloaded_direct,
-                                                 WEIGHT_FOR_DIR);
+    result = node_sl_choose_by_bandwidth(overloaded_direct,
+                                         WEIGHT_FOR_DIR);
   } else {
     result = smartlist_choose(trusted_direct);
   }
@@ -1157,7 +1162,7 @@ router_pick_directory_server_impl(authority_type_t type, int flags)
   smartlist_free(trusted_tunnel);
   smartlist_free(overloaded_direct);
   smartlist_free(overloaded_tunnel);
-  return result;
+  return result->rs;
 }
 
 /** Choose randomly from among the trusted dirservers that are up.  Flags
@@ -1260,13 +1265,10 @@ router_pick_trusteddirserver_impl(authority_type_t type, int flags,
 static void
 mark_all_trusteddirservers_up(void)
 {
-  if (routerlist) {
-    SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, router,
-       if (router_digest_is_trusted_dir(router->cache_info.identity_digest) &&
-         router->dir_port > 0) {
-         router->is_running = 1;
-       });
-  }
+  SMARTLIST_FOREACH(nodelist_get_list(), node_t *, node, {
+       if (router_digest_is_trusted_dir(node->identity))
+         node->is_running = 1;
+    });
   if (trusted_dir_servers) {
     SMARTLIST_FOREACH(trusted_dir_servers, trusted_dir_server_t *, dir,
     {
@@ -1306,6 +1308,7 @@ routers_in_same_network_family(const routerinfo_t *r1, const routerinfo_t *r2)
   return (r1->addr & 0xffff0000) == (r2->addr & 0xffff0000);
 }
 
+#if 0
 /** Look through the routerlist and identify routers that
  * advertise the same /16 network address as <b>router</b>.
  * Add each of them to <b>sl</b>.
@@ -1319,14 +1322,17 @@ routerlist_add_network_family(smartlist_t *sl, const routerinfo_t *router)
       smartlist_add(sl, r);
   });
 }
+#endif
 
 /** Add all the family of <b>router</b> to the smartlist <b>sl</b>.
  * This is used to make sure we don't pick siblings in a single path,
  * or pick more than one relay from a family for our entry guard list.
  */
 void
-routerlist_add_family(smartlist_t *sl, const routerinfo_t *router)
+nodelist_add_node_family(smartlist_t *sl, const node_t *router)
 {
+  /* XXXX MOVE */
+#if 0
   const routerinfo_t *r;
   config_line_t *cl;
   or_options_t *options = get_options();
@@ -1357,6 +1363,23 @@ routerlist_add_family(smartlist_t *sl, const routerinfo_t *router)
       add_nickname_list_to_smartlist(sl, cl->value, 0);
     }
   }
+#endif
+  (void)sl;
+  (void)router;
+  UNIMPLEMENTED_NODELIST();
+}
+
+/** Given a <b>router</b>, add every node_t in its family to <b>sl</b>.
+ *
+ * Note the type mismatch: This function takes a routerinfo, but adds nodes
+ * to the smartlist!
+ */
+static void
+routerlist_add_nodes_in_family(smartlist_t *sl, const routerinfo_t *router)
+{
+  (void)router;
+  (void)sl;
+  UNIMPLEMENTED_NODELIST();
 }
 
 /** Return true iff r is named by some nickname in <b>lst</b>. */
@@ -1373,12 +1396,14 @@ router_in_nickname_smartlist(smartlist_t *lst, const routerinfo_t *r)
 /** Return true iff r1 and r2 are in the same family, but not the same
  * router. */
 int
-routers_in_same_family(const routerinfo_t *r1, const routerinfo_t *r2)
+nodes_in_same_family(const node_t *node1, const node_t *node2)
 {
+#if 0
   or_options_t *options = get_options();
   config_line_t *cl;
 
-  if (options->EnforceDistinctSubnets && routers_in_same_network_family(r1,r2))
+  if (options->EnforceDistinctSubnets &&
+      nodes_in_same_network_family(node1,node2))
     return 1;
 
   if (router_in_nickname_smartlist(r1->declared_family, r2) &&
@@ -1390,19 +1415,26 @@ routers_in_same_family(const routerinfo_t *r1, const routerinfo_t *r2)
         router_nickname_is_in_list(r2, cl->value))
       return 1;
   }
+#endif
+  (void)node1;
+  (void)node2;
+  UNIMPLEMENTED_NODELIST();
   return 0;
 }
 
+#if 0
 /** Given a (possibly NULL) comma-and-whitespace separated list of nicknames,
- * see which nicknames in <b>list</b> name routers in our routerlist, and add
- * the routerinfos for those routers to <b>sl</b>.  If <b>must_be_running</b>,
+ * see which nicknames in <b>list</b> name nodes we know about, and add
+ * the nodes for those routers to <b>sl</b>.  If <b>must_be_running</b>,
  * only include routers that we think are running.
  * Warn if any non-Named routers are specified by nickname.
  */
-void
+static void
 add_nickname_list_to_smartlist(smartlist_t *sl, const char *list,
                                int must_be_running)
-{
+{ /*XXXX MOVE */
+  /*XXXX this is only used in one place. Can we kill it?*/
+  const node_t *node;
   const routerinfo_t *router;
   smartlist_t *nickname_list;
   int have_dir_info = router_have_minimum_dir_info();
@@ -1424,11 +1456,12 @@ add_nickname_list_to_smartlist(smartlist_t *sl, const char *list,
       log_warn(LD_CONFIG, "Nickname '%s' is misformed; skipping", nick);
       continue;
     }
-    router = router_get_by_nickname(nick, 1);
+    node = node_get_by_nickname(nick, 1);
+    router = node->ri;
     warned = smartlist_string_isin(warned_nicknames, nick);
-    if (router) {
-      if (!must_be_running || router->is_running) {
-        smartlist_add(sl,(void*)router);
+    if (node) {
+      if (!must_be_running || node->is_running) {
+        smartlist_add(sl,(void*)node);
       }
     } else if (!router_get_consensus_status_by_nickname(nick,1)) {
       if (!warned) {
@@ -1441,6 +1474,7 @@ add_nickname_list_to_smartlist(smartlist_t *sl, const char *list,
   SMARTLIST_FOREACH(nickname_list, char *, nick, tor_free(nick));
   smartlist_free(nickname_list);
 }
+#endif
 
 /** Return 1 iff any member of the (possibly NULL) comma-separated list
  * <b>list</b> is an acceptable nickname or hexdigest for <b>router</b>.  Else
@@ -1466,29 +1500,27 @@ router_nickname_is_in_list(const routerinfo_t *router, const char *list)
   return v;
 }
 
-/** Add every suitable router from our routerlist to <b>sl</b>, so that
+/** Add every suitable node from our nodelist to <b>sl</b>, so that
  * we can pick a node for a circuit.
  */
 static void
-router_add_running_routers_to_smartlist(smartlist_t *sl, int allow_invalid,
-                                        int need_uptime, int need_capacity,
-                                        int need_guard)
-{
-  if (!routerlist)
-    return;
+router_add_running_nodes_to_smartlist(smartlist_t *sl, int allow_invalid,
+                                      int need_uptime, int need_capacity,
+                                      int need_guard, int need_desc)
+{ /* XXXX MOVE */
+  SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), const node_t *, node) {
+    if (!node->is_running ||
+        (!node->is_valid && !allow_invalid))
+      continue;
+    if (need_desc && !(node->ri || (node->rs && node->md)))
+      continue;
+    if (node->ri && node->ri->purpose != ROUTER_PURPOSE_GENERAL)
+      continue;
+    if (node_is_unreliable(node, need_uptime, need_capacity, need_guard))
+      continue;
 
-  SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, router,
-  {
-    if (router->is_running &&
-        router->purpose == ROUTER_PURPOSE_GENERAL &&
-        (router->is_valid || allow_invalid) &&
-        !router_is_unreliable(router, need_uptime,
-                              need_capacity, need_guard)) {
-      /* If it's running, and it's suitable according to the
-       * other flags we had in mind */
-      smartlist_add(sl, router);
-    }
-  });
+    smartlist_add(sl, (void *)node);
+  } SMARTLIST_FOREACH_END(node);
 }
 
 /** Look through the routerlist until we find a router that has my key.
@@ -1511,9 +1543,9 @@ routerlist_find_my_routerinfo(void)
  * that allows exit to this address:port, or return NULL if there
  * isn't a good one.
  */
-const routerinfo_t *
+const node_t *
 router_find_exact_exit_enclave(const char *address, uint16_t port)
-{
+{/*XXXX MOVE*/
   uint32_t addr;
   struct in_addr in;
   tor_addr_t a;
@@ -1524,13 +1556,12 @@ router_find_exact_exit_enclave(const char *address, uint16_t port)
 
   tor_addr_from_ipv4h(&a, addr);
 
-  SMARTLIST_FOREACH(routerlist->routers, const routerinfo_t *, router,
-  {
-    if (router->addr == addr &&
-        router->is_running &&
-        compare_tor_addr_to_addr_policy(&a, port, router->exit_policy) ==
+  SMARTLIST_FOREACH(nodelist_get_list(), const node_t *, node, {
+    if (node_get_addr_ipv4h(node) == addr &&
+        node->is_running &&
+        compare_tor_addr_to_node_policy(&a, port, node) ==
           ADDR_POLICY_ACCEPTED)
-      return router;
+      return node;
   });
   return NULL;
 }
@@ -1542,14 +1573,14 @@ router_find_exact_exit_enclave(const char *address, uint16_t port)
  * If <b>need_guard</b>, we require that the router is a possible entry guard.
  */
 int
-router_is_unreliable(const routerinfo_t *router, int need_uptime,
-                     int need_capacity, int need_guard)
+node_is_unreliable(const node_t *node, int need_uptime,
+                   int need_capacity, int need_guard)
 {
-  if (need_uptime && !router->is_stable)
+  if (need_uptime && !node->is_stable)
     return 1;
-  if (need_capacity && !router->is_fast)
+  if (need_capacity && !node->is_fast)
     return 1;
-  if (need_guard && !router->is_possible_guard)
+  if (need_guard && !node->is_possible_guard)
     return 1;
   return 0;
 }
@@ -1613,13 +1644,10 @@ kb_to_bytes(uint32_t bw)
 }
 
 /** Helper function:
- * choose a random element of smartlist <b>sl</b>, weighted by
+ * choose a random element of smartlist <b>sl</b> of nodes, weighted by
  * the advertised bandwidth of each element using the consensus
  * bandwidth weights.
  *
- * If <b>statuses</b> is zero, then <b>sl</b> is a list of
- * routerinfo_t's. Otherwise it's a list of routerstatus_t's.
- *
  * If <b>rule</b>==WEIGHT_FOR_EXIT. we're picking an exit node: consider all
  * nodes' bandwidth equally regardless of their Exit status, since there may
  * be some in the list because they exit to obscure ports. If
@@ -1629,10 +1657,9 @@ kb_to_bytes(uint32_t bw)
  * guard node: consider all guard's bandwidth equally. Otherwise, weight
  * guards proportionally less.
  */
-static void *
-smartlist_choose_by_bandwidth_weights(smartlist_t *sl,
-                                      bandwidth_weight_rule_t rule,
-                                      int statuses)
+static const node_t *
+smartlist_choose_node_by_bandwidth_weights(smartlist_t *sl,
+                                           bandwidth_weight_rule_t rule)
 {
   int64_t weight_scale;
   int64_t rand_bw;
@@ -1727,15 +1754,14 @@ smartlist_choose_by_bandwidth_weights(smartlist_t *sl,
   bandwidths = tor_malloc_zero(sizeof(double)*smartlist_len(sl));
 
   // Cycle through smartlist and total the bandwidth.
-  for (i = 0; i < (unsigned)smartlist_len(sl); ++i) {
+  SMARTLIST_FOREACH_BEGIN(sl, const node_t *, node) {
     int is_exit = 0, is_guard = 0, is_dir = 0, this_bw = 0, is_me = 0;
     double weight = 1;
-    if (statuses) {
-      routerstatus_t *status = smartlist_get(sl, i);
-      is_exit = status->is_exit;
-      is_guard = status->is_possible_guard;
-      is_dir = (status->dir_port != 0);
-      if (!status->has_bandwidth) {
+    is_exit = node->is_exit;
+    is_guard = node->is_possible_guard;
+    is_dir = node_is_dir(node);
+    if (node->rs) {
+      if (!node->rs->has_bandwidth) {
         tor_free(bandwidths);
         /* This should never happen, unless all the authorites downgrade
          * to 0.2.0 or rogue routerstatuses get inserted into our consensus. */
@@ -1744,26 +1770,17 @@ smartlist_choose_by_bandwidth_weights(smartlist_t *sl,
                  "old router selection algorithm.");
         return NULL;
       }
-      this_bw = kb_to_bytes(status->bandwidth);
-      if (router_digest_is_me(status->identity_digest))
-        is_me = 1;
+      this_bw = kb_to_bytes(node->rs->bandwidth);
+    } else if (node->ri) {
+      /* bridge or other descriptor not in our consensus */
+      this_bw = bridge_get_advertised_bandwidth_bounded(node->ri);
+      have_unknown = 1;
     } else {
-      const routerstatus_t *rs;
-      routerinfo_t *router = smartlist_get(sl, i);
-      rs = router_get_consensus_status_by_id(
-             router->cache_info.identity_digest);
-      is_exit = router->is_exit;
-      is_guard = router->is_possible_guard;
-      is_dir = (router->dir_port != 0);
-      if (rs && rs->has_bandwidth) {
-        this_bw = kb_to_bytes(rs->bandwidth);
-      } else { /* bridge or other descriptor not in our consensus */
-        this_bw = bridge_get_advertised_bandwidth_bounded(router);
-        have_unknown = 1;
-      }
-      if (router_digest_is_me(router->cache_info.identity_digest))
-        is_me = 1;
+      /* We can't use this one. */
+      continue;
     }
+    is_me = router_digest_is_me(node->identity);
+
     if (is_guard && is_exit) {
       weight = (is_dir ? Wdb*Wd : Wd);
     } else if (is_guard) {
@@ -1774,11 +1791,11 @@ smartlist_choose_by_bandwidth_weights(smartlist_t *sl,
       weight = (is_dir ? Wmb*Wm : Wm);
     }
 
-    bandwidths[i] = weight*this_bw;
+    bandwidths[node_sl_idx] = weight*this_bw;
     weighted_bw += weight*this_bw;
     if (is_me)
       sl_last_weighted_bw_of_me = weight*this_bw;
-  }
+  } SMARTLIST_FOREACH_END(node);
 
   /* XXXX022 this is a kludge to expose these values. */
   sl_last_total_weighted_bw = weighted_bw;
@@ -1826,12 +1843,9 @@ smartlist_choose_by_bandwidth_weights(smartlist_t *sl,
 }
 
 /** Helper function:
- * choose a random element of smartlist <b>sl</b>, weighted by
+ * choose a random node_t element of smartlist <b>sl</b>, weighted by
  * the advertised bandwidth of each element.
  *
- * If <b>statuses</b> is zero, then <b>sl</b> is a list of
- * routerinfo_t's. Otherwise it's a list of routerstatus_t's.
- *
  * If <b>rule</b>==WEIGHT_FOR_EXIT. we're picking an exit node: consider all
  * nodes' bandwidth equally regardless of their Exit status, since there may
  * be some in the list because they exit to obscure ports. If
@@ -1841,12 +1855,11 @@ smartlist_choose_by_bandwidth_weights(smartlist_t *sl,
  * guard node: consider all guard's bandwidth equally. Otherwise, weight
  * guards proportionally less.
  */
-static void *
-smartlist_choose_by_bandwidth(smartlist_t *sl, bandwidth_weight_rule_t rule,
-                              int statuses)
+static const node_t *
+smartlist_choose_node_by_bandwidth(smartlist_t *sl,
+                                   bandwidth_weight_rule_t rule)
 {
   unsigned int i;
-  const routerinfo_t *router;
   const routerstatus_t *status=NULL;
   int32_t *bandwidths;
   int is_exit;
@@ -1888,19 +1901,19 @@ smartlist_choose_by_bandwidth(smartlist_t *sl, bandwidth_weight_rule_t rule,
   guard_bits = bitarray_init_zero(smartlist_len(sl));
 
   /* Iterate over all the routerinfo_t or routerstatus_t, and */
-  for (i = 0; i < (unsigned)smartlist_len(sl); ++i) {
+  SMARTLIST_FOREACH_BEGIN(sl, const node_t *, node) {
     /* first, learn what bandwidth we think i has */
     int is_known = 1;
     int32_t flags = 0;
     uint32_t this_bw = 0;
-    if (statuses) {
-      status = smartlist_get(sl, i);
-      if (router_digest_is_me(status->identity_digest))
-        me_idx = i;
-      router = router_get_by_digest(status->identity_digest);
-      is_exit = status->is_exit;
-      is_guard = status->is_possible_guard;
-      if (status->has_bandwidth) {
+
+    if (router_digest_is_me(node->identity))
+      me_idx = node_sl_idx;
+
+    is_exit = node->is_exit;
+    is_guard = node->is_possible_guard;
+    if (node->rs) {
+      if (node->rs->has_bandwidth) {
         this_bw = kb_to_bytes(status->bandwidth);
       } else { /* guess */
         /* XXX022 once consensuses always list bandwidths, we can take
@@ -1910,27 +1923,11 @@ smartlist_choose_by_bandwidth(smartlist_t *sl, bandwidth_weight_rule_t rule,
         flags |= is_exit ? 2 : 0;
         flags |= is_guard ? 4 : 0;
       }
-    } else {
-      const routerstatus_t *rs;
-      router = smartlist_get(sl, i);
-      rs = router_get_consensus_status_by_id(
-             router->cache_info.identity_digest);
-      if (router_digest_is_me(router->cache_info.identity_digest))
-        me_idx = i;
-      is_exit = router->is_exit;
-      is_guard = router->is_possible_guard;
-      if (rs && rs->has_bandwidth) {
-        this_bw = kb_to_bytes(rs->bandwidth);
-      } else if (rs) { /* guess; don't trust the descriptor */
-        /* XXX022 once consensuses always list bandwidths, we can take
-         * this guessing business out. -RD */
-        is_known = 0;
-        flags = router->is_fast ? 1 : 0;
-        flags |= is_exit ? 2 : 0;
-        flags |= is_guard ? 4 : 0;
-      } else /* bridge or other descriptor not in our consensus */
-        this_bw = bridge_get_advertised_bandwidth_bounded(router);
+    } else if (node->ri) {
+      /* Must be a bridge if we're willing to use it */
+      this_bw = bridge_get_advertised_bandwidth_bounded(node->ri);
     }
+
     if (is_exit)
       bitarray_set(exit_bits, i);
     if (is_guard)
@@ -1950,9 +1947,9 @@ smartlist_choose_by_bandwidth(smartlist_t *sl, bandwidth_weight_rule_t rule,
         total_nonexit_bw += this_bw;
     } else {
       ++n_unknown;
-      bandwidths[i] = -flags;
+      bandwidths[node_sl_idx] = -flags;
     }
-  }
+  } SMARTLIST_FOREACH_END(node);
 
   /* Now, fill in the unknown values. */
   if (n_unknown) {
@@ -2094,40 +2091,23 @@ smartlist_choose_by_bandwidth(smartlist_t *sl, bandwidth_weight_rule_t rule,
   return smartlist_get(sl, i);
 }
 
-/** Choose a random element of router list <b>sl</b>, weighted by
- * the advertised bandwidth of each router.
- */
-const routerinfo_t *
-routerlist_sl_choose_by_bandwidth(smartlist_t *sl,
-                                  bandwidth_weight_rule_t rule)
-{
-  routerinfo_t *ret;
-  if ((ret = smartlist_choose_by_bandwidth_weights(sl, rule, 0))) {
-    return ret;
-  } else {
-    return smartlist_choose_by_bandwidth(sl, rule, 0);
-  }
-}
-
 /** Choose a random element of status list <b>sl</b>, weighted by
- * the advertised bandwidth of each status.
- */
-const routerstatus_t *
-routerstatus_sl_choose_by_bandwidth(smartlist_t *sl,
-                                    bandwidth_weight_rule_t rule)
-{
-  /* We are choosing neither exit nor guard here. Weight accordingly. */
-  routerstatus_t *ret;
-  if ((ret = smartlist_choose_by_bandwidth_weights(sl, rule, 1))) {
+ * the advertised bandwidth of each node */
+const node_t *
+node_sl_choose_by_bandwidth(smartlist_t *sl,
+                            bandwidth_weight_rule_t rule)
+{ /*XXXX MOVE */
+  const node_t *ret;
+  if ((ret = smartlist_choose_node_by_bandwidth_weights(sl, rule))) {
     return ret;
   } else {
-    return smartlist_choose_by_bandwidth(sl, rule, 1);
+    return smartlist_choose_node_by_bandwidth(sl, rule);
   }
 }
 
-/** Return a random running router from the routerlist. Never
- * pick a node whose routerinfo is in
- * <b>excludedsmartlist</b>, or whose routerinfo matches <b>excludedset</b>,
+/** Return a random running node from the nodelist. Never
+ * pick a node that is in
+ * <b>excludedsmartlist</b>, or which matches <b>excludedset</b>,
  * even if they are the only nodes available.
  * If <b>CRN_NEED_UPTIME</b> is set in flags and any router has more than
  * a minimum uptime, return one of those.
@@ -2139,21 +2119,26 @@ routerstatus_sl_choose_by_bandwidth(smartlist_t *sl,
  * If <b>CRN_WEIGHT_AS_EXIT</b> is set in flags, we weight bandwidths as if
  * picking an exit node, otherwise we weight bandwidths for picking a relay
  * node (that is, possibly discounting exit nodes).
+ * If <b>CRN_NEED_DESC</b> is set in flags, we only consider nodes that
+ * have a routerinfo or microdescriptor -- that is, enough info to be
+ * used to build a circuit.
  */
-const routerinfo_t *
+const node_t *
 router_choose_random_node(smartlist_t *excludedsmartlist,
                           routerset_t *excludedset,
                           router_crn_flags_t flags)
-{
+{ /* XXXX MOVE */
   const int need_uptime = (flags & CRN_NEED_UPTIME) != 0;
   const int need_capacity = (flags & CRN_NEED_CAPACITY) != 0;
   const int need_guard = (flags & CRN_NEED_GUARD) != 0;
   const int allow_invalid = (flags & CRN_ALLOW_INVALID) != 0;
   const int weight_for_exit = (flags & CRN_WEIGHT_AS_EXIT) != 0;
+  const int need_desc = (flags & CRN_NEED_DESC) != 0;
 
   smartlist_t *sl=smartlist_create(),
-              *excludednodes=smartlist_create();
-  const routerinfo_t *choice = NULL, *r;
+    *excludednodes=smartlist_create();
+  const node_t *choice = NULL;
+  const routerinfo_t *r;
   bandwidth_weight_rule_t rule;
 
   tor_assert(!(weight_for_exit && need_guard));
@@ -2163,29 +2148,30 @@ router_choose_random_node(smartlist_t *excludedsmartlist,
   /* Exclude relays that allow single hop exit circuits, if the user
    * wants to (such relays might be risky) */
   if (get_options()->ExcludeSingleHopRelays) {
-    routerlist_t *rl = router_get_routerlist();
-    SMARTLIST_FOREACH(rl->routers, routerinfo_t *, r,
-      if (r->allow_single_hop_exits) {
-        smartlist_add(excludednodes, r);
+    SMARTLIST_FOREACH(nodelist_get_list(), node_t *, node,
+      if (node_allows_single_hop_exits(node)) {
+        smartlist_add(excludednodes, node);
       });
   }
 
   if ((r = routerlist_find_my_routerinfo())) {
-    smartlist_add(excludednodes, (void *)r);
-    routerlist_add_family(excludednodes, r);
+    const node_t *me = node_get_by_id(r->cache_info.identity_digest);
+    if (me)
+      smartlist_add(excludednodes, (void *)me);
+    routerlist_add_nodes_in_family(excludednodes, r);
   }
 
-  router_add_running_routers_to_smartlist(sl, allow_invalid,
-                                          need_uptime, need_capacity,
-                                          need_guard);
+  router_add_running_nodes_to_smartlist(sl, allow_invalid,
+                                        need_uptime, need_capacity,
+                                        need_guard, need_desc);
   smartlist_subtract(sl,excludednodes);
   if (excludedsmartlist)
     smartlist_subtract(sl,excludedsmartlist);
   if (excludedset)
-    routerset_subtract_routers(sl,excludedset);
+    routerset_subtract_nodes(sl,excludedset);
 
   // Always weight by bandwidth
-  choice = routerlist_sl_choose_by_bandwidth(sl, rule);
+  choice = node_sl_choose_by_bandwidth(sl, rule);
 
   smartlist_free(sl);
   if (!choice && (need_uptime || need_capacity || need_guard)) {
@@ -2208,35 +2194,87 @@ router_choose_random_node(smartlist_t *excludedsmartlist,
   return choice;
 }
 
-/** Helper: Return true iff the <b>identity_digest</b> and <b>nickname</b>
- * combination of a router, encoded in hexadecimal, matches <b>hexdigest</b>
- * (which is optionally prefixed with a single dollar sign).  Return false if
- * <b>hexdigest</b> is malformed, or it doesn't match.  */
-static INLINE int
-hex_digest_matches(const char *hexdigest, const char *identity_digest,
-                   const char *nickname, int is_named)
+/** Helper: given an extended nickname in <b>hexdigest</b> try to decode it.
+ * Return 0 on success, -1 on failure.  Store the result into the
+ * DIGEST_LEN-byte buffer at <b>digest_out</b>, the single character at
+ * <b>nickname_qualifier_char_out</b>, and the MAXNICKNAME_LEN+1-byte buffer
+ * at <b>nickname_out</b>.
+ *
+ * The recognized format is:
+ *   HexName = Dollar? HexDigest NamePart?
+ *   Dollar = '?'
+ *   HexDigest = HexChar*20
+ *   HexChar = 'a'..'f' | 'A'..'F' | '0'..'9'
+ *   NamePart = QualChar Name
+ *   QualChar = '=' | '~'
+ *   Name = NameChar*(1..MAX_NICKNAME_LEN)
+ *   NameChar = Any ASCII alphanumeric character
+ */
+int
+hex_digest_nickname_decode(const char *hexdigest,
+                           char *digest_out,
+                           char *nickname_qualifier_char_out,
+                           char *nickname_out)
 {
-  char digest[DIGEST_LEN];
   size_t len;
+
   tor_assert(hexdigest);
   if (hexdigest[0] == '$')
     ++hexdigest;
 
   len = strlen(hexdigest);
-  if (len < HEX_DIGEST_LEN)
+  if (len < HEX_DIGEST_LEN) {
+    return -1;
+  } else if (len > HEX_DIGEST_LEN && (hexdigest[HEX_DIGEST_LEN] == '=' ||
+                                    hexdigest[HEX_DIGEST_LEN] == '~') &&
+           len <= HEX_DIGEST_LEN+1+MAX_NICKNAME_LEN) {
+    *nickname_qualifier_char_out = hexdigest[HEX_DIGEST_LEN];
+    strlcpy(nickname_out, hexdigest+HEX_DIGEST_LEN+1 , MAX_NICKNAME_LEN+1);
+  } else if (len == HEX_DIGEST_LEN) {
+    ;
+  } else {
+    return -1;
+  }
+
+  if (base16_decode(digest_out, DIGEST_LEN, hexdigest, HEX_DIGEST_LEN)<0)
+    return -1;
+  return 0;
+}
+
+/** Helper: Return true iff the <b>identity_digest</b> and <b>nickname</b>
+ * combination of a router, encoded in hexadecimal, matches <b>hexdigest</b>
+ * (which is optionally prefixed with a single dollar sign).  Return false if
+ * <b>hexdigest</b> is malformed, or it doesn't match.  */
+static int
+hex_digest_nickname_matches(const char *hexdigest, const char *identity_digest,
+                            const char *nickname, int is_named)
+{
+  char digest[DIGEST_LEN];
+  char nn_char='\0';
+  char nn_buf[MAX_NICKNAME_LEN+1];
+
+  if (hex_digest_nickname_decode(hexdigest, digest, &nn_char, nn_buf) == -1)
     return 0;
-  else if (len > HEX_DIGEST_LEN &&
-           (hexdigest[HEX_DIGEST_LEN] == '=' ||
-            hexdigest[HEX_DIGEST_LEN] == '~')) {
-    if (strcasecmp(hexdigest+HEX_DIGEST_LEN+1, nickname))
+
+  if (nn_char == '=' || nn_char == '~') {
+    if (strcasecmp(nn_buf, nickname))
       return 0;
-    if (hexdigest[HEX_DIGEST_LEN] == '=' && !is_named)
+    if (nn_char == '=' && !is_named)
       return 0;
   }
 
-  if (base16_decode(digest, DIGEST_LEN, hexdigest, HEX_DIGEST_LEN)<0)
-    return 0;
-  return (!memcmp(digest, identity_digest, DIGEST_LEN));
+  return !memcmp(digest, identity_digest, DIGEST_LEN);
+}
+
+/* Return true iff <b>router</b> is listed as named in the current consensus. */
+static int
+router_is_named(const routerinfo_t *router)
+{
+  const char *digest =
+    networkstatus_get_router_digest_by_nickname(router->nickname);
+
+  return (digest &&
+          !memcmp(digest, router->cache_info.identity_digest, DIGEST_LEN));
 }
 
 /** Return true iff the digest of <b>router</b>'s identity key,
@@ -2246,8 +2284,10 @@ hex_digest_matches(const char *hexdigest, const char *identity_digest,
 static INLINE int
 router_hex_digest_matches(const routerinfo_t *router, const char *hexdigest)
 {
-  return hex_digest_matches(hexdigest, router->cache_info.identity_digest,
-                            router->nickname, router->is_named);
+  return hex_digest_nickname_matches(hexdigest,
+                                     router->cache_info.identity_digest,
+                                     router->nickname,
+                                     router_is_named(router));
 }
 
 /** Return true if <b>router</b>'s nickname matches <b>nickname</b>
@@ -2269,6 +2309,13 @@ router_nickname_matches(const routerinfo_t *router, const char *nickname)
 const routerinfo_t *
 router_get_by_nickname(const char *nickname, int warn_if_unnamed)
 {
+#if 1
+  const node_t *node = node_get_by_nickname(nickname, warn_if_unnamed);
+  if (node)
+    return node->ri;
+  else
+    return NULL;
+#else
   int maybedigest;
   char digest[DIGEST_LEN];
   routerinfo_t *best_match=NULL;
@@ -2366,8 +2413,8 @@ router_get_by_nickname(const char *nickname, int warn_if_unnamed)
     }
     return best_match;
   }
-
   return NULL;
+#endif
 }
 
 /** Try to find a routerinfo for <b>digest</b>. If we don't have one,
@@ -2433,35 +2480,11 @@ hexdigest_to_digest(const char *hexdigest, char *digest)
 const routerinfo_t *
 router_get_by_hexdigest(const char *hexdigest)
 {
-  char digest[DIGEST_LEN];
-  size_t len;
-  const routerinfo_t *ri;
-
-  tor_assert(hexdigest);
-  if (!routerlist)
+  if (is_legal_nickname(hexdigest))
     return NULL;
-  if (hexdigest[0]=='$')
-    ++hexdigest;
-  len = strlen(hexdigest);
-  if (hexdigest_to_digest(hexdigest, digest) < 0)
-    return NULL;
-
-  ri = router_get_by_digest(digest);
 
-  if (ri && len > HEX_DIGEST_LEN) {
-    if (hexdigest[HEX_DIGEST_LEN] == '=') {
-      if (strcasecmp(ri->nickname, hexdigest+HEX_DIGEST_LEN+1) ||
-          !ri->is_named)
-        return NULL;
-    } else if (hexdigest[HEX_DIGEST_LEN] == '~') {
-      if (strcasecmp(ri->nickname, hexdigest+HEX_DIGEST_LEN+1))
-        return NULL;
-    } else {
-      return NULL;
-    }
-  }
-
-  return ri;
+  /* It's not a legal nickname, so it must be a hexdigest or nothing. */
+  return router_get_by_nickname(hexdigest, 1);
 }
 
 /** As router_get_by_digest,but return a pointer that you're allowed to
@@ -3162,7 +3185,7 @@ routerlist_reset_warnings(void)
 void
 router_set_status(const char *digest, int up)
 {
-  routerinfo_t *router;
+  node_t *node;
   routerstatus_t *status;
   tor_assert(digest);
 
@@ -3170,15 +3193,21 @@ router_set_status(const char *digest, int up)
                     if (!memcmp(d->digest, digest, DIGEST_LEN))
                       d->is_running = up);
 
-  router = router_get_mutable_by_digest(digest);
-  if (router) {
-    log_debug(LD_DIR,"Marking router '%s/%s' as %s.",
-              router->nickname, router->address, up ? "up" : "down");
-    if (!up && router_is_me(router) && !we_are_hibernating())
+  node = node_get_mutable_by_id(digest);
+  if (node) {
+#if 0
+    char buf[MAX_VERBOSE_NICKNAME_LEN+1];
+    node_get_verbose_nickname(node,buf);
+    log_debug(LD_DIR,"Marking router %s as %s.",
+              buf, up ? "up" : "down");
+#endif
+    if (!up && node_is_me(node) && !we_are_hibernating())
       log_warn(LD_NET, "We just marked ourself as down. Are your external "
                "addresses reachable?");
-    router->is_running = up;
+    node->is_running = up;
   }
+
+  /*XXXX NM don't change routerstatus's is_running. */
   status = router_get_mutable_consensus_status_by_id(digest);
   if (status && status->is_running != up) {
     status->is_running = up;
@@ -3249,12 +3278,12 @@ router_add_to_routerlist(routerinfo_t *router, const char **msg,
 
   if (authdir) {
     if (authdir_wants_to_reject_router(router, msg,
-                                       !from_cache && !from_fetch)) {
+                                       !from_cache && !from_fetch,
+                                       &authdir_believes_valid)) {
       tor_assert(*msg);
       routerinfo_free(router);
       return ROUTER_AUTHDIR_REJECTS;
     }
-    authdir_believes_valid = router->is_valid;
   } else if (from_fetch) {
     /* Only check the descriptor digest against the network statuses when
      * we are receiving in response to a fetch. */
@@ -3925,24 +3954,24 @@ routerlist_retry_directory_downloads(time_t now)
   update_microdesc_downloads(now);
 }
 
-/** Return 1 if all running sufficiently-stable routers will reject
+/** Return 1 if all running sufficiently-stable routers we can use will reject
  * addr:port, return 0 if any might accept it. */
 int
-router_exit_policy_all_routers_reject(uint32_t addr, uint16_t port,
-                                          int need_uptime)
-{
+router_exit_policy_all_nodes_reject(uint32_t addr, uint16_t port,
+                                    int need_uptime)
+{ /* XXXX MOVE */
   addr_policy_result_t r;
-  if (!routerlist) return 1;
 
-  SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, router,
-  {
-    if (router->is_running &&
-        !router_is_unreliable(router, need_uptime, 0, 0)) {
-      r = compare_addr_to_addr_policy(addr, port, router->exit_policy);
+  SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), const node_t *, node) {
+    if (node->is_running &&
+        !node_is_unreliable(node, need_uptime, 0, 0)) {
+
+      r = compare_addr_to_node_policy(addr, port, node);
+
       if (r != ADDR_POLICY_REJECTED && r != ADDR_POLICY_PROBABLY_REJECTED)
         return 0; /* this one could be ok. good enough. */
     }
-  });
+  } SMARTLIST_FOREACH_END(node);
   return 1; /* all will reject. */
 }
 
@@ -4796,7 +4825,7 @@ count_usable_descriptors(int *num_present, int *num_usable,
 
   SMARTLIST_FOREACH(consensus->routerstatus_list, routerstatus_t *, rs,
      {
-       if (in_set && ! routerset_contains_routerstatus(in_set, rs))
+       if (in_set && ! routerset_contains_routerstatus(in_set, rs, -1))
          continue;
        if (client_would_use_router(rs, now, options)) {
          ++*num_usable; /* the consensus says we want it. */
@@ -5437,7 +5466,7 @@ refresh_all_country_info(void)
   if (options->_ExcludeExitNodesUnion)
     routerset_refresh_countries(options->_ExcludeExitNodesUnion);
 
-  routerlist_refresh_countries();
+  nodelist_refresh_countries();
 }
 
 /** Add all members of the set <b>source</b> to <b>target</b>. */
@@ -5487,11 +5516,10 @@ routerset_is_empty(const routerset_t *set)
 static int
 routerset_contains(const routerset_t *set, const tor_addr_t *addr,
                    uint16_t orport,
-                   const char *nickname, const char *id_digest, int is_named,
+                   const char *nickname, const char *id_digest,
                    country_t country)
 {
   if (!set || !set->list) return 0;
-  (void) is_named; /* not supported */
   if (nickname && strmap_get_lc(set->names, nickname))
     return 4;
   if (id_digest && digestmap_get(set->digests, id_digest))
@@ -5519,13 +5547,14 @@ routerset_contains_extendinfo(const routerset_t *set, const extend_info_t *ei)
                             ei->port,
                             ei->nickname,
                             ei->identity_digest,
-                            -1, /*is_named*/
                             -1 /*country*/);
 }
 
-/** Return true iff <b>ri</b> is in <b>set</b>. */
+/** Return true iff <b>ri</b> is in <b>set</b>.  If country is <b>-1</b>, we
+ * look up the country. */
 int
-routerset_contains_router(const routerset_t *set, const routerinfo_t *ri)
+routerset_contains_router(const routerset_t *set, const routerinfo_t *ri,
+                          country_t country)
 {
   tor_addr_t addr;
   tor_addr_from_ipv4h(&addr, ri->addr);
@@ -5534,13 +5563,15 @@ routerset_contains_router(const routerset_t *set, const routerinfo_t *ri)
                             ri->or_port,
                             ri->nickname,
                             ri->cache_info.identity_digest,
-                            ri->is_named,
-                            ri->country);
+                            country);
 }
 
-/** Return true iff <b>rs</b> is in <b>set</b>. */
+/** Return true iff <b>rs</b> is in <b>set</b>.  If country is <b>-1</b>, we
+ * look up the country. */
 int
-routerset_contains_routerstatus(const routerset_t *set, const routerstatus_t *rs)
+routerset_contains_routerstatus(const routerset_t *set,
+                                const routerstatus_t *rs,
+                                country_t country)
 {
   tor_addr_t addr;
   tor_addr_from_ipv4h(&addr, rs->addr);
@@ -5549,46 +5580,55 @@ routerset_contains_routerstatus(const routerset_t *set, const routerstatus_t *rs
                             rs->or_port,
                             rs->nickname,
                             rs->identity_digest,
-                            rs->is_named,
-                            -1);
+                            country);
+}
+
+/** Return true iff <b>node</b> is in <b>set</b>. */
+int
+routerset_contains_node(const routerset_t *set, const node_t *node)
+{
+  if (node->rs)
+    return routerset_contains_routerstatus(set, node->rs, node->country);
+  else if (node->ri)
+    return routerset_contains_router(set, node->ri, node->country);
+  else
+    return 0;
 }
 
-/** Add every known routerinfo_t that is a member of <b>routerset</b> to
+/** Add every known node_t that is a member of <b>routerset</b> to
  * <b>out</b>.  If <b>running_only</b>, only add the running ones. */
 void
-routerset_get_all_routers(smartlist_t *out, const routerset_t *routerset,
-                          int running_only)
-{
+routerset_get_all_nodes(smartlist_t *out, const routerset_t *routerset,
+                        int running_only)
+{ /* XXXX MOVE */
   tor_assert(out);
   if (!routerset || !routerset->list)
     return;
-  if (!warned_nicknames)
-    warned_nicknames = smartlist_create();
-  if (routerset_is_list(routerset)) {
 
+  if (routerset_is_list(routerset)) {
     /* No routers are specified by type; all are given by name or digest.
      * we can do a lookup in O(len(list)). */
     SMARTLIST_FOREACH(routerset->list, const char *, name, {
-        const routerinfo_t *router = router_get_by_nickname(name, 1);
-        if (router) {
-          if (!running_only || router->is_running)
-            smartlist_add(out, (void*)router);
+        const node_t *node = node_get_by_nickname(name, 1);
+        if (node) {
+          if (!running_only || node->is_running)
+            smartlist_add(out, (void*)node);
         }
     });
   } else {
     /* We need to iterate over the routerlist to get all the ones of the
      * right kind. */
-    routerlist_t *rl = router_get_routerlist();
-    SMARTLIST_FOREACH(rl->routers, routerinfo_t *, router, {
-        if (running_only && !router->is_running)
+    smartlist_t *nodes = nodelist_get_list();
+    SMARTLIST_FOREACH(nodes, const node_t *, node, {
+        if (running_only && !node->is_running)
           continue;
-        if (routerset_contains_router(routerset, router))
-          smartlist_add(out, (void*)router);
+        if (routerset_contains_node(routerset, node))
+          smartlist_add(out, (void*)node);
     });
   }
 }
 
-/** Add to <b>target</b> every routerinfo_t from <b>source</b> except:
+/** Add to <b>target</b> every node_t from <b>source</b> except:
  *
  * 1) Don't add it if <b>include</b> is non-empty and the relay isn't in
  * <b>include</b>; and
@@ -5597,39 +5637,39 @@ routerset_get_all_routers(smartlist_t *out, const routerset_t *routerset,
  * 3) If <b>running_only</b>, don't add non-running routers.
  */
 void
-routersets_get_disjunction(smartlist_t *target,
+routersets_get_node_disjunction(smartlist_t *target,
                            const smartlist_t *source,
                            const routerset_t *include,
                            const routerset_t *exclude, int running_only)
 {
-  SMARTLIST_FOREACH(source, routerinfo_t *, router, {
+  SMARTLIST_FOREACH(source, const node_t *, node, {
     int include_result;
-    if (running_only && !router->is_running)
+    if (running_only && !node->is_running)
       continue;
     if (!routerset_is_empty(include))
-      include_result = routerset_contains_router(include, router);
+      include_result = routerset_contains_node(include, node);
     else
       include_result = 1;
 
     if (include_result) {
-      int exclude_result = routerset_contains_router(exclude, router);
+      int exclude_result = routerset_contains_node(exclude, node);
       if (include_result >= exclude_result)
-        smartlist_add(target, router);
+        smartlist_add(target, (void*)node);
     }
   });
 }
 
-/** Remove every routerinfo_t from <b>lst</b> that is in <b>routerset</b>. */
+/** Remove every node_t from <b>lst</b> that is in <b>routerset</b>. */
 void
-routerset_subtract_routers(smartlist_t *lst, const routerset_t *routerset)
-{
+routerset_subtract_nodes(smartlist_t *lst, const routerset_t *routerset)
+{ /*XXXX MOVE ? */
   tor_assert(lst);
   if (!routerset)
     return;
-  SMARTLIST_FOREACH(lst, routerinfo_t *, r, {
-      if (routerset_contains_router(routerset, r)) {
+  SMARTLIST_FOREACH(lst, const node_t *, node, {
+      if (routerset_contains_node(routerset, node)) {
         //log_debug(LD_DIR, "Subtracting %s",r->nickname);
-        SMARTLIST_DEL_CURRENT(lst, r);
+        SMARTLIST_DEL_CURRENT(lst, node);
       }
     });
 }
@@ -5690,18 +5730,23 @@ routerset_free(routerset_t *routerset)
 /** Refresh the country code of <b>ri</b>.  This function MUST be called on
  * each router when the GeoIP database is reloaded, and on all new routers. */
 void
-routerinfo_set_country(routerinfo_t *ri)
+node_set_country(node_t *node)
 {
-  ri->country = geoip_get_country_by_ip(ri->addr);
+  if (node->rs)
+    node->country = geoip_get_country_by_ip(node->rs->addr);
+  else if (node->ri)
+    node->country = geoip_get_country_by_ip(node->ri->addr);
+  else
+    node->country = -1;
 }
 
 /** Set the country code of all routers in the routerlist. */
 void
-routerlist_refresh_countries(void)
+nodelist_refresh_countries(void) /* MOVE */
 {
-  routerlist_t *rl = router_get_routerlist();
-  SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri,
-                    routerinfo_set_country(ri));
+  smartlist_t *nodes = nodelist_get_list();
+  SMARTLIST_FOREACH(nodes, node_t *, node,
+                    node_set_country(node));
 }
 
 /** Determine the routers that are responsible for <b>id</b> (binary) and
diff --git a/src/or/routerlist.h b/src/or/routerlist.h
index 90975e7..cda2d2e 100644
--- a/src/or/routerlist.h
+++ b/src/or/routerlist.h
@@ -27,33 +27,30 @@ int router_reload_router_list(void);
 int authority_cert_dl_looks_uncertain(const char *id_digest);
 smartlist_t *router_get_trusted_dir_servers(void);
 
-const routerstatus_t *router_pick_directory_server(authority_type_t type, int flags);
+const routerstatus_t *router_pick_directory_server(authority_type_t type,
+                                                   int flags);
 trusted_dir_server_t *router_get_trusteddirserver_by_digest(const char *d);
 trusted_dir_server_t *trusteddirserver_get_by_v3_auth_digest(const char *d);
-const routerstatus_t *router_pick_trusteddirserver(authority_type_t type, int flags);
+const routerstatus_t *router_pick_trusteddirserver(authority_type_t type,
+                                                   int flags);
 int router_get_my_share_of_directory_requests(double *v2_share_out,
                                               double *v3_share_out);
 void router_reset_status_download_failures(void);
 void routerlist_add_family(smartlist_t *sl, const routerinfo_t *router);
-int routers_in_same_family(const routerinfo_t *r1, const routerinfo_t *r2);
 int routers_have_same_or_addr(const routerinfo_t *r1, const routerinfo_t *r2);
-void add_nickname_list_to_smartlist(smartlist_t *sl, const char *list,
-                                    int must_be_running);
 int router_nickname_is_in_list(const routerinfo_t *router, const char *list);
 const routerinfo_t *routerlist_find_my_routerinfo(void);
-const routerinfo_t *router_find_exact_exit_enclave(const char *address,
+const node_t *router_find_exact_exit_enclave(const char *address,
                                              uint16_t port);
-int router_is_unreliable(const routerinfo_t *router, int need_uptime,
+int node_is_unreliable(const node_t *router, int need_uptime,
                          int need_capacity, int need_guard);
 uint32_t router_get_advertised_bandwidth(const routerinfo_t *router);
 uint32_t router_get_advertised_bandwidth_capped(const routerinfo_t *router);
 
-const routerinfo_t *routerlist_sl_choose_by_bandwidth(smartlist_t *sl,
-                                                bandwidth_weight_rule_t rule);
-const routerstatus_t *routerstatus_sl_choose_by_bandwidth(smartlist_t *sl,
-                                                bandwidth_weight_rule_t rule);
+const node_t *node_sl_choose_by_bandwidth(smartlist_t *sl,
+                                          bandwidth_weight_rule_t rule);
 
-const routerinfo_t *router_choose_random_node(smartlist_t *excludedsmartlist,
+const node_t *router_choose_random_node(smartlist_t *excludedsmartlist,
                                         struct routerset_t *excludedset,
                                         router_crn_flags_t flags);
 
@@ -133,8 +130,9 @@ void router_load_extrainfo_from_string(const char *s, const char *eos,
                                        int descriptor_digests);
 
 void routerlist_retry_directory_downloads(time_t now);
-int router_exit_policy_all_routers_reject(uint32_t addr, uint16_t port,
-                                          int need_uptime);
+int router_exit_policy_all_nodes_reject(uint32_t addr, uint16_t port,
+                                        int need_uptime);
+
 int router_exit_policy_rejects_all(const routerinfo_t *router);
 trusted_dir_server_t *add_trusted_dir_server(const char *nickname,
                            const char *address,
@@ -165,29 +163,31 @@ const char *esc_router_info(const routerinfo_t *router);
 void routers_sort_by_identity(smartlist_t *routers);
 
 routerset_t *routerset_new(void);
+void routerset_refresh_countries(routerset_t *rs);
 int routerset_parse(routerset_t *target, const char *s,
                     const char *description);
 void routerset_union(routerset_t *target, const routerset_t *source);
 int routerset_is_list(const routerset_t *set);
 int routerset_needs_geoip(const routerset_t *set);
-int routerset_contains_router(const routerset_t *set, const routerinfo_t *ri);
+int routerset_contains_router(const routerset_t *set, const routerinfo_t *ri,
+                              country_t country);
 int routerset_contains_routerstatus(const routerset_t *set,
-                                    const routerstatus_t *rs);
+                                    const routerstatus_t *rs,
+                                    country_t country);
 int routerset_contains_extendinfo(const routerset_t *set,
                                   const extend_info_t *ei);
-void routerset_get_all_routers(smartlist_t *out, const routerset_t *routerset,
-                               int running_only);
-void routersets_get_disjunction(smartlist_t *target, const smartlist_t *source,
+int routerset_contains_node(const routerset_t *set, const node_t *node);
+void routerset_get_all_nodes(smartlist_t *out, const routerset_t *routerset,
+                             int running_only);
+void routersets_get_node_disjunction(smartlist_t *target,
+                                const smartlist_t *source,
                                 const routerset_t *include,
                                 const routerset_t *exclude, int running_only);
-void routerset_subtract_routers(smartlist_t *out,
-                                const routerset_t *routerset);
+void routerset_subtract_nodes(smartlist_t *out,
+                              const routerset_t *routerset);
 char *routerset_to_string(const routerset_t *routerset);
-void routerset_refresh_countries(routerset_t *target);
 int routerset_equal(const routerset_t *old, const routerset_t *new);
 void routerset_free(routerset_t *routerset);
-void routerinfo_set_country(routerinfo_t *ri);
-void routerlist_refresh_countries(void);
 void refresh_all_country_info(void);
 
 int hid_serv_get_responsible_directories(smartlist_t *responsible_dirs,
@@ -201,5 +201,10 @@ void launch_descriptor_downloads(int purpose,
                                  const routerstatus_t *source,
                                  time_t now);
 
+int hex_digest_nickname_decode(const char *hexdigest,
+                               char *digest_out,
+                               char *nickname_qualifier_out,
+                               char *nickname_out);
+
 #endif
 
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index f7e645e..b987d6f 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -1344,7 +1344,6 @@ router_parse_entry_from_string(const char *s, const char *end,
   tor_assert(tok->n_args >= 5);
 
   router = tor_malloc_zero(sizeof(routerinfo_t));
-  router->country = -1;
   router->cache_info.routerlist_index = -1;
   router->cache_info.annotations_len = s-start_of_annotations + prepend_len;
   router->cache_info.signed_descriptor_len = end-s;
@@ -1543,8 +1542,6 @@ router_parse_entry_from_string(const char *s, const char *end,
                             "router descriptor") < 0)
     goto err;
 
-  routerinfo_set_country(router);
-
   if (!router->or_port) {
     log_warn(LD_DIR,"or_port unreadable or 0. Failing.");
     goto err;
-- 
1.7.1