[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