Commit 381766ce authored by Nick Mathewson's avatar Nick Mathewson 🥔
Browse files

Implement proposal 167: Authorities vote on network parameters.

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.
parent 0edc3930
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
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.
+21 −0
Original line number Diff line number Diff line
@@ -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.

+3 −0
Original line number Diff line number Diff line
@@ -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)
+104 −1
Original line number Diff line number Diff line
@@ -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. */
+4 −0
Original line number Diff line number Diff line
@@ -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);
Loading