[or-cvs] [tor/master] Implement proposal 167: Authorities vote on network parameters.

Nick Mathewson nickm at seul.org
Tue Sep 15 03:24:07 UTC 2009


Author: Nick Mathewson <nickm at torproject.org>
Date: Mon, 14 Sep 2009 22:15:57 -0400
Subject: Implement proposal 167: Authorities vote on network parameters.
Commit: 381766ce4b11454607f025aafb6767aa9789d271

This code adds a new field to vote on: "params".  It consists of a list of
sorted key=int pairs.  The output is computed as the median of all the
integers for any key on which anybody voted.

Improved with input from Roger.
---
 ChangeLog              |    5 ++
 doc/spec/dir-spec.txt  |   21 ++++++++++
 src/common/torint.h    |    3 +
 src/or/dirvote.c       |  105 +++++++++++++++++++++++++++++++++++++++++++++++-
 src/or/networkstatus.c |    4 ++
 src/or/or.h            |   10 +++-
 src/or/routerparse.c   |   31 ++++++++++++++
 src/or/test.c          |   62 ++++++++++++++++++++++++++++
 8 files changed, 237 insertions(+), 4 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index bfbbaa9..b1cb770 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,9 @@
 Changes in version 0.2.2.2-alpha - 2009-09-??
+  o Major features:
+    - Authorities can now vote on arbitary integer values as part of the
+      consensus process.  This is designed to help set network parameters.
+      Implements proposal 167.
+
   o Minor bugfixes:
     - Fix an extremely rare infinite recursion bug that could occur if
       we tried to log a message after shutting down the log subsystem.
diff --git a/doc/spec/dir-spec.txt b/doc/spec/dir-spec.txt
index 16f121a..faa3a66 100644
--- a/doc/spec/dir-spec.txt
+++ b/doc/spec/dir-spec.txt
@@ -1098,6 +1098,20 @@
         enough votes were counted for the consensus for an authoritative
         opinion to have been formed about their status.
 
+    "params" SP [Parameters] NL
+
+        [At most once]
+
+        Parameter ::= Keyword '=' Int32
+        Int32 ::= A decimal integer between -2147483648 and 2147483647.
+        Parameters ::= Parameter | Parameters SP Parameter
+
+        The parameters list, if present, contains a space-separated list of
+        key-value pairs, sorted in lexical order by their keyword.  Each
+        parameter has its own meaning.
+
+        (Only included when the vote is generated with consensus-method 7 or
+        later.)
 
    The authority section of a vote contains the following items, followed
    in turn by the authority's current key certificate:
@@ -1406,6 +1420,10 @@
 
      Known-flags is the union of all flags known by any voter.
 
+     Entries are given on the "params" line for every keyword on which any
+     authority voted.  The values given are the low-median of all votes on
+     that keyword.
+
     "client-versions" and "server-versions" are sorted in ascending
      order; A version is recommended in the consensus if it is recommended
      by more than half of the voting authorities that included a
@@ -1473,6 +1491,9 @@
           a router, the authorities produce a consensus containing a 
           Bandwidth= keyword equal to the median of the Measured= votes.
 
+        * If consensus-method 7 or later is in use, the params line is
+          included in the output.
+
      The signatures at the end of a consensus document are sorted in
      ascending order by identity digest.
 
diff --git a/src/common/torint.h b/src/common/torint.h
index 1f74211..be624e0 100644
--- a/src/common/torint.h
+++ b/src/common/torint.h
@@ -117,6 +117,9 @@ typedef unsigned int uint32_t;
 #ifndef INT32_MAX
 #define INT32_MAX 0x7fffffffu
 #endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
 #endif
 
 #if (SIZEOF_LONG == 4)
diff --git a/src/or/dirvote.c b/src/or/dirvote.c
index 58b02da..c2f0a33 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirvote.c
@@ -24,7 +24,9 @@ static int dirvote_publish_consensus(void);
 static char *make_consensus_method_list(int low, int high);
 
 /** The highest consensus method that we currently support. */
-#define MAX_SUPPORTED_CONSENSUS_METHOD 6
+#define MAX_SUPPORTED_CONSENSUS_METHOD 7
+
+#define MIN_METHOD_FOR_PARAMS 7
 
 /* =====
  * Voting
@@ -97,6 +99,7 @@ format_networkstatus_vote(crypto_pk_env_t *private_signing_key,
     char fu[ISO_TIME_LEN+1];
     char vu[ISO_TIME_LEN+1];
     char *flags = smartlist_join_strings(v3_ns->known_flags, " ", 0, NULL);
+    char *params;
     authority_cert_t *cert = v3_ns->cert;
     char *methods =
       make_consensus_method_list(1, MAX_SUPPORTED_CONSENSUS_METHOD);
@@ -105,6 +108,11 @@ format_networkstatus_vote(crypto_pk_env_t *private_signing_key,
     format_iso_time(fu, v3_ns->fresh_until);
     format_iso_time(vu, v3_ns->valid_until);
 
+    if (v3_ns->net_params)
+      params = smartlist_join_strings(v3_ns->net_params, " ", 0, NULL);
+    else
+      params = tor_strdup("");
+
     tor_assert(cert);
     tor_snprintf(status, len,
                  "network-status-version 3\n"
@@ -117,6 +125,7 @@ format_networkstatus_vote(crypto_pk_env_t *private_signing_key,
                  "voting-delay %d %d\n"
                  "%s" /* versions */
                  "known-flags %s\n"
+                 "params %s\n"
                  "dir-source %s %s %s %s %d %d\n"
                  "contact %s\n",
                  v3_ns->type == NS_TYPE_VOTE ? "vote" : "opinion",
@@ -125,9 +134,11 @@ format_networkstatus_vote(crypto_pk_env_t *private_signing_key,
                  v3_ns->vote_seconds, v3_ns->dist_seconds,
                  version_lines,
                  flags,
+                 params,
                  voter->nickname, fingerprint, voter->address,
                    ipaddr, voter->dir_port, voter->or_port, voter->contact);
 
+    tor_free(params);
     tor_free(flags);
     tor_free(methods);
     outp = status + strlen(status);
@@ -507,6 +518,89 @@ compute_consensus_versions_list(smartlist_t *lst, int n_versioning)
   return result;
 }
 
+/** Helper: given a list of valid networkstatus_t, return a new string
+ * containing the contents of the consensus network parameter set.
+ */
+/* private */ char *
+dirvote_compute_params(smartlist_t *votes)
+{
+  int i;
+  int32_t *vals;
+
+  int cur_param_len;
+  const char *cur_param;
+  const char *eq;
+  char *result;
+
+  const int n_votes = smartlist_len(votes);
+  smartlist_t *output;
+  smartlist_t *param_list = smartlist_create();
+
+  /* We require that the parameter lists in the votes are well-formed: that
+     is, that their keywords are unique and sorted, and that their values are
+     between INT32_MIN and INT32_MAX inclusive.  This should be guaranteed by
+     the parsing code. */
+
+  vals = tor_malloc(sizeof(int)*n_votes);
+
+  SMARTLIST_FOREACH_BEGIN(votes, networkstatus_t *, v) {
+    if (!v->net_params)
+      continue;
+    smartlist_add_all(param_list, v->net_params);
+  } SMARTLIST_FOREACH_END(v);
+
+  if (smartlist_len(param_list) == 0) {
+    tor_free(vals);
+    smartlist_free(param_list);
+    return NULL;
+  }
+
+  smartlist_sort_strings(param_list);
+  i = 0;
+  cur_param = smartlist_get(param_list, 0);
+  eq = strchr(cur_param, '=');
+  tor_assert(eq);
+  cur_param_len = eq+1 - cur_param;
+
+  output = smartlist_create();
+
+  SMARTLIST_FOREACH_BEGIN(param_list, const char *, param) {
+    const char *next_param;
+    int ok=0;
+    eq = strchr(param, '=');
+    tor_assert(i<n_votes);
+    vals[i++] = (int32_t)
+      tor_parse_long(eq+1, 10, INT32_MIN, INT32_MAX, &ok, NULL);
+    tor_assert(ok);
+
+    if (param_sl_idx+1 == smartlist_len(param_list))
+      next_param = NULL;
+    else
+      next_param = smartlist_get(param_list, param_sl_idx+1);
+    if (!next_param || strncmp(next_param, param, cur_param_len)) {
+      /* We've reached the end of a series. */
+      int32_t median = median_int32(vals, i);
+      char *out_string = tor_malloc(64+cur_param_len);
+      memcpy(out_string, param, cur_param_len);
+      tor_snprintf(out_string+cur_param_len,64, "%ld", (long)median);
+      smartlist_add(output, out_string);
+
+      i = 0;
+      if (next_param) {
+        eq = strchr(next_param, '=');
+        cur_param_len = eq+1 - next_param;
+      }
+    }
+  } SMARTLIST_FOREACH_END(param);
+
+  result = smartlist_join_strings(output, " ", 0, NULL);
+  SMARTLIST_FOREACH(output, char *, cp, tor_free(cp));
+  smartlist_free(output);
+  smartlist_free(param_list);
+  tor_free(vals);
+  return result;
+}
+
 /** Given a list of vote networkstatus_t in <b>votes</b>, our public
  * authority <b>identity_key</b>, our private authority <b>signing_key</b>,
  * and the number of <b>total_authorities</b> that we believe exist in our
@@ -659,6 +753,15 @@ networkstatus_compute_consensus(smartlist_t *votes,
     tor_free(flaglist);
   }
 
+  if (consensus_method >= MIN_METHOD_FOR_PARAMS) {
+    char *params = dirvote_compute_params(votes);
+    if (params) {
+      smartlist_add(chunks, tor_strdup("params "));
+      smartlist_add(chunks, params);
+      smartlist_add(chunks, tor_strdup("\n"));
+    }
+  }
+
   /* Sort the votes. */
   smartlist_sort(votes, _compare_votes_by_authority_id);
   /* Add the authority sections. */
diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
index b022999..70d43e6 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -286,6 +286,10 @@ networkstatus_vote_free(networkstatus_t *ns)
     SMARTLIST_FOREACH(ns->known_flags, char *, c, tor_free(c));
     smartlist_free(ns->known_flags);
   }
+  if (ns->net_params) {
+    SMARTLIST_FOREACH(ns->net_params, char *, c, tor_free(c));
+    smartlist_free(ns->net_params);
+  }
   if (ns->supported_methods) {
     SMARTLIST_FOREACH(ns->supported_methods, char *, c, tor_free(c));
     smartlist_free(ns->supported_methods);
diff --git a/src/or/or.h b/src/or/or.h
index aaae905..297a8f0 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -1672,6 +1672,10 @@ typedef struct networkstatus_t {
    * not listed here, the voter has no opinion on what its value should be. */
   smartlist_t *known_flags;
 
+  /** List of key=value strings for the parameters in this vote or
+   * consensus, sorted by key. */
+  smartlist_t *net_params;
+
   /** List of networkstatus_voter_info_t.  For a vote, only one element
    * is included.  For a consensus, one element is included for every voter
    * whose vote contributed to the consensus. */
@@ -3661,9 +3665,9 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_env_t *private_key,
                                         authority_cert_t *cert);
 
 #ifdef DIRVOTE_PRIVATE
-char *
-format_networkstatus_vote(crypto_pk_env_t *private_key,
-                          networkstatus_t *v3_ns);
+char *format_networkstatus_vote(crypto_pk_env_t *private_key,
+                                 networkstatus_t *v3_ns);
+char *dirvote_compute_params(smartlist_t *votes);
 #endif
 
 /********************************* dns.c ***************************/
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index 4137dd2..815608a 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -102,6 +102,7 @@ typedef enum {
   K_VOTING_DELAY,
 
   K_KNOWN_FLAGS,
+  K_PARAMS,
   K_VOTE_DIGEST,
   K_CONSENSUS_DIGEST,
   K_CONSENSUS_METHODS,
@@ -433,6 +434,7 @@ static token_rule_t networkstatus_token_table[] = {
   T1("valid-until",            K_VALID_UNTIL,      CONCAT_ARGS, NO_OBJ ),
   T1("voting-delay",           K_VOTING_DELAY,     GE(2),       NO_OBJ ),
   T1("known-flags",            K_KNOWN_FLAGS,      ARGS,        NO_OBJ ),
+  T01("params",                K_PARAMS,           ARGS,        NO_OBJ ),
   T( "fingerprint",            K_FINGERPRINT,      CONCAT_ARGS, NO_OBJ ),
 
   CERTIFICATE_MEMBERS
@@ -470,6 +472,7 @@ static token_rule_t networkstatus_consensus_token_table[] = {
   T01("client-versions",     K_CLIENT_VERSIONS, CONCAT_ARGS, NO_OBJ ),
   T01("server-versions",     K_SERVER_VERSIONS, CONCAT_ARGS, NO_OBJ ),
   T01("consensus-method",    K_CONSENSUS_METHOD,    EQ(1),   NO_OBJ),
+  T01("params",                K_PARAMS,           ARGS,        NO_OBJ ),
 
   END_OF_TABLE
 };
@@ -2408,6 +2411,34 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
     goto err;
   }
 
+  tok = find_opt_by_keyword(tokens, K_PARAMS);
+  if (tok) {
+    inorder = 1;
+    ns->net_params = smartlist_create();
+    for (i = 0; i < tok->n_args; ++i) {
+      int ok=0;
+      char *eq = strchr(tok->args[i], '=');
+      if (!eq) {
+        log_warn(LD_DIR, "Bad element '%s' in params", escaped(tok->args[i]));
+        goto err;
+      }
+      tor_parse_long(eq+1, 10, INT32_MIN, INT32_MAX, &ok, NULL);
+      if (!ok) {
+        log_warn(LD_DIR, "Bad element '%s' in params", escaped(tok->args[i]));
+        goto err;
+      }
+      if (i > 0 && strcmp(tok->args[i-1], tok->args[i]) >= 0) {
+        log_warn(LD_DIR, "%s >= %s", tok->args[i-1], tok->args[i]);
+        inorder = 0;
+      }
+      smartlist_add(ns->net_params, tor_strdup(tok->args[i]));
+    }
+    if (!inorder) {
+      log_warn(LD_DIR, "params not in order");
+      goto err;
+    }
+  }
+
   ns->voters = smartlist_create();
 
   SMARTLIST_FOREACH_BEGIN(tokens, directory_token_t *, _tok) {
diff --git a/src/or/test.c b/src/or/test.c
index d34fc45..338195f 100644
--- a/src/or/test.c
+++ b/src/or/test.c
@@ -3355,6 +3355,54 @@ done:
   return;
 }
 
+static void
+test_dirutil_param_voting(void)
+{
+  networkstatus_t vote1, vote2, vote3, vote4;
+  smartlist_t *votes = smartlist_create();
+  char *res = NULL;
+
+  /* dirvote_compute_params only looks at the net_params field of the votes,
+     so that's all we need to set.
+   */
+  memset(&vote1, 0, sizeof(vote1));
+  memset(&vote2, 0, sizeof(vote2));
+  memset(&vote3, 0, sizeof(vote3));
+  memset(&vote4, 0, sizeof(vote4));
+  vote1.net_params = smartlist_create();
+  vote2.net_params = smartlist_create();
+  vote3.net_params = smartlist_create();
+  vote4.net_params = smartlist_create();
+  smartlist_split_string(vote1.net_params,
+                         "ab=90 abcd=20 cw=50 x-yz=-99", NULL, 0, 0);
+  smartlist_split_string(vote2.net_params,
+                         "ab=27 cw=5 x-yz=88", NULL, 0, 0);
+  smartlist_split_string(vote3.net_params,
+                         "abcd=20 c=60 cw=500 x-yz=-9 zzzzz=101", NULL, 0, 0);
+  smartlist_split_string(vote4.net_params,
+                         "ab=900 abcd=200 c=1 cw=51 x-yz=100", NULL, 0, 0);
+  smartlist_add(votes, &vote1);
+  smartlist_add(votes, &vote2);
+  smartlist_add(votes, &vote3);
+  smartlist_add(votes, &vote4);
+
+  res = dirvote_compute_params(votes);
+  test_streq(res,
+             "ab=90 abcd=20 c=1 cw=50 x-yz=-9 zzzzz=101");
+
+ done:
+  tor_free(res);
+  SMARTLIST_FOREACH(vote1.net_params, char *, cp, tor_free(cp));
+  SMARTLIST_FOREACH(vote2.net_params, char *, cp, tor_free(cp));
+  SMARTLIST_FOREACH(vote3.net_params, char *, cp, tor_free(cp));
+  SMARTLIST_FOREACH(vote4.net_params, char *, cp, tor_free(cp));
+  smartlist_free(vote1.net_params);
+  smartlist_free(vote2.net_params);
+  smartlist_free(vote3.net_params);
+  smartlist_free(vote4.net_params);
+
+}
+
 extern const char AUTHORITY_CERT_1[];
 extern const char AUTHORITY_SIGNKEY_1[];
 extern const char AUTHORITY_CERT_2[];
@@ -3512,6 +3560,9 @@ test_v3_networkstatus(void)
   crypto_pk_get_digest(cert1->identity_key, voter->identity_digest);
   smartlist_add(vote->voters, voter);
   vote->cert = authority_cert_dup(cert1);
+  vote->net_params = smartlist_create();
+  smartlist_split_string(vote->net_params, "circuitwindow=101 foo=990",
+                         NULL, 0, 0);
   vote->routerstatus_list = smartlist_create();
   /* add the first routerstatus. */
   vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
@@ -3653,6 +3704,9 @@ test_v3_networkstatus(void)
   vote->dist_seconds = 300;
   authority_cert_free(vote->cert);
   vote->cert = authority_cert_dup(cert2);
+  vote->net_params = smartlist_create();
+  smartlist_split_string(vote->net_params, "bar=2000000000 circuitwindow=20",
+                         NULL, 0, 0);
   tor_free(vote->client_versions);
   tor_free(vote->server_versions);
   voter = smartlist_get(vote->voters, 0);
@@ -3691,6 +3745,9 @@ test_v3_networkstatus(void)
   vote->dist_seconds = 250;
   authority_cert_free(vote->cert);
   vote->cert = authority_cert_dup(cert3);
+  vote->net_params = smartlist_create();
+  smartlist_split_string(vote->net_params, "circuitwindow=80 foo=660",
+                         NULL, 0, 0);
   smartlist_add(vote->supported_methods, tor_strdup("4"));
   vote->client_versions = tor_strdup("0.1.2.14,0.1.2.17");
   vote->server_versions = tor_strdup("0.1.2.10,0.1.2.15,0.1.2.16");
@@ -3747,6 +3804,10 @@ test_v3_networkstatus(void)
   test_streq(cp, "Authority:Exit:Fast:Guard:MadeOfCheese:MadeOfTin:"
              "Running:Stable:V2Dir:Valid");
   tor_free(cp);
+  cp = smartlist_join_strings(con->net_params, ":", 0, NULL);
+  test_streq(cp, "bar=2000000000:circuitwindow=80:foo=660");
+  tor_free(cp);
+
   test_eq(4, smartlist_len(con->voters)); /*3 voters, 1 legacy key.*/
   /* The voter id digests should be in this order. */
   test_assert(memcmp(cert2->cache_info.identity_digest,
@@ -4866,6 +4927,7 @@ static struct {
   ENT(dir_format),
   ENT(dirutil),
   SUBENT(dirutil, measured_bw),
+  SUBENT(dirutil, param_voting),
   ENT(v3_networkstatus),
   ENT(policies),
   ENT(rend_fns),
-- 
1.5.6.5



More information about the tor-commits mailing list