📄 dirvote.c
字号:
return strcmp(a, b);
return 0;
} else {
return 1;
}
}
/** Given a list of networkstatus_t votes, determine and return the number of
* the highest consensus method that is supported by 2/3 of the voters. */
static int
compute_consensus_method(smartlist_t *votes)
{
smartlist_t *all_methods = smartlist_create();
smartlist_t *acceptable_methods = smartlist_create();
smartlist_t *tmp = smartlist_create();
int min = (smartlist_len(votes) * 2) / 3;
int n_ok;
int result;
SMARTLIST_FOREACH(votes, networkstatus_t *, vote,
{
tor_assert(vote->supported_methods);
smartlist_add_all(tmp, vote->supported_methods);
smartlist_sort(tmp, _cmp_int_strings);
smartlist_uniq(tmp, _cmp_int_strings, NULL);
smartlist_add_all(all_methods, tmp);
smartlist_clear(tmp);
});
smartlist_sort(all_methods, _cmp_int_strings);
get_frequent_members(acceptable_methods, all_methods, min);
n_ok = smartlist_len(acceptable_methods);
if (n_ok) {
const char *best = smartlist_get(acceptable_methods, n_ok-1);
result = (int)tor_parse_long(best, 10, 1, INT_MAX, NULL, NULL);
} else {
result = 1;
}
smartlist_free(tmp);
smartlist_free(all_methods);
smartlist_free(acceptable_methods);
return result;
}
/** Return true iff <b>method</b> is a consensus method that we support. */
static int
consensus_method_is_supported(int method)
{
return (method >= 1) && (method <= 2);
}
/** 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
* voting quorum, generate the text of a new v3 consensus vote, and return the
* value in a newly allocated string.
*
* Note: this function DOES NOT check whether the votes are from
* recognized authorities. (dirvote_add_vote does that.) */
char *
networkstatus_compute_consensus(smartlist_t *votes,
int total_authorities,
crypto_pk_env_t *identity_key,
crypto_pk_env_t *signing_key)
{
smartlist_t *chunks;
char *result = NULL;
int consensus_method;
time_t valid_after, fresh_until, valid_until;
int vote_seconds, dist_seconds;
char *client_versions = NULL, *server_versions = NULL;
smartlist_t *flags;
tor_assert(total_authorities >= smartlist_len(votes));
if (!smartlist_len(votes)) {
log_warn(LD_DIR, "Can't compute a consensus from no votes.");
return NULL;
}
flags = smartlist_create();
consensus_method = compute_consensus_method(votes);
if (consensus_method_is_supported(consensus_method)) {
log_info(LD_DIR, "Generating consensus using method %d.",
consensus_method);
} else {
log_warn(LD_DIR, "The other authorities will use consensus method %d, "
"which I don't support. Maybe I should upgrade!",
consensus_method);
consensus_method = 1;
}
/* Compute medians of time-related things, and figure out how many
* routers we might need to talk about. */
{
int n_votes = smartlist_len(votes);
time_t *va_times = tor_malloc(n_votes * sizeof(time_t));
time_t *fu_times = tor_malloc(n_votes * sizeof(time_t));
time_t *vu_times = tor_malloc(n_votes * sizeof(time_t));
int *votesec_list = tor_malloc(n_votes * sizeof(int));
int *distsec_list = tor_malloc(n_votes * sizeof(int));
int n_versioning_clients = 0, n_versioning_servers = 0;
smartlist_t *combined_client_versions = smartlist_create();
smartlist_t *combined_server_versions = smartlist_create();
int j;
SMARTLIST_FOREACH(votes, networkstatus_t *, v,
{
tor_assert(v->is_vote);
va_times[v_sl_idx] = v->valid_after;
fu_times[v_sl_idx] = v->fresh_until;
vu_times[v_sl_idx] = v->valid_until;
votesec_list[v_sl_idx] = v->vote_seconds;
distsec_list[v_sl_idx] = v->dist_seconds;
if (v->client_versions) {
smartlist_t *cv = smartlist_create();
++n_versioning_clients;
smartlist_split_string(cv, v->client_versions, ",",
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
sort_version_list(cv, 1);
smartlist_add_all(combined_client_versions, cv);
smartlist_free(cv); /* elements get freed later. */
}
if (v->server_versions) {
smartlist_t *sv = smartlist_create();
++n_versioning_servers;
smartlist_split_string(sv, v->server_versions, ",",
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
sort_version_list(sv, 1);
smartlist_add_all(combined_server_versions, sv);
smartlist_free(sv); /* elements get freed later. */
}
SMARTLIST_FOREACH(v->known_flags, const char *, cp,
smartlist_add(flags, tor_strdup(cp)));
});
valid_after = median_time(va_times, n_votes);
fresh_until = median_time(fu_times, n_votes);
valid_until = median_time(vu_times, n_votes);
vote_seconds = median_int(votesec_list, n_votes);
dist_seconds = median_int(distsec_list, n_votes);
tor_assert(valid_after+MIN_VOTE_INTERVAL <= fresh_until);
tor_assert(fresh_until+MIN_VOTE_INTERVAL <= valid_until);
tor_assert(vote_seconds >= MIN_VOTE_SECONDS);
tor_assert(dist_seconds >= MIN_DIST_SECONDS);
for (j = 0; j < 2; ++j) {
smartlist_t *lst =
j ? combined_server_versions : combined_client_versions;
int min = (j ? n_versioning_servers : n_versioning_clients) / 2;
smartlist_t *good = smartlist_create();
char *res;
sort_version_list(lst, 0);
get_frequent_members(good, lst, min);
res = smartlist_join_strings(good, ",", 0, NULL);
if (j)
server_versions = res;
else
client_versions = res;
SMARTLIST_FOREACH(lst, char *, cp, tor_free(cp));
smartlist_free(good);
smartlist_free(lst);
}
smartlist_sort_strings(flags);
smartlist_uniq_strings(flags);
tor_free(va_times);
tor_free(fu_times);
tor_free(vu_times);
tor_free(votesec_list);
tor_free(distsec_list);
}
chunks = smartlist_create();
{
char buf[1024];
char va_buf[ISO_TIME_LEN+1], fu_buf[ISO_TIME_LEN+1],
vu_buf[ISO_TIME_LEN+1];
char *flaglist;
format_iso_time(va_buf, valid_after);
format_iso_time(fu_buf, fresh_until);
format_iso_time(vu_buf, valid_until);
flaglist = smartlist_join_strings(flags, " ", 0, NULL);
smartlist_add(chunks, tor_strdup("network-status-version 3\n"
"vote-status consensus\n"));
if (consensus_method >= 2) {
tor_snprintf(buf, sizeof(buf), "consensus-method %d\n",
consensus_method);
smartlist_add(chunks, tor_strdup(buf));
}
tor_snprintf(buf, sizeof(buf),
"valid-after %s\n"
"fresh-until %s\n"
"valid-until %s\n"
"voting-delay %d %d\n"
"client-versions %s\n"
"server-versions %s\n"
"known-flags %s\n",
va_buf, fu_buf, vu_buf,
vote_seconds, dist_seconds,
client_versions, server_versions, flaglist);
smartlist_add(chunks, tor_strdup(buf));
tor_free(flaglist);
}
/* Sort the votes. */
smartlist_sort(votes, _compare_votes_by_authority_id);
/* Add the authority sections. */
SMARTLIST_FOREACH(votes, networkstatus_t *, v,
{
char buf[1024];
struct in_addr in;
char ip[INET_NTOA_BUF_LEN];
char fingerprint[HEX_DIGEST_LEN+1];
char votedigest[HEX_DIGEST_LEN+1];
networkstatus_voter_info_t *voter = get_voter(v);
in.s_addr = htonl(voter->addr);
tor_inet_ntoa(&in, ip, sizeof(ip));
base16_encode(fingerprint, sizeof(fingerprint), voter->identity_digest,
DIGEST_LEN);
base16_encode(votedigest, sizeof(votedigest), voter->vote_digest,
DIGEST_LEN);
tor_snprintf(buf, sizeof(buf),
"dir-source %s %s %s %s %d %d\n"
"contact %s\n"
"vote-digest %s\n",
voter->nickname, fingerprint, voter->address, ip,
voter->dir_port,
voter->or_port,
voter->contact,
votedigest);
smartlist_add(chunks, tor_strdup(buf));
});
/* Add the actual router entries. */
{
int *index; /* index[j] is the current index into votes[j]. */
int *size; /* size[j] is the number of routerstatuses in votes[j]. */
int *flag_counts; /* The number of voters that list flag[j] for the
* currently considered router. */
int i;
smartlist_t *matching_descs = smartlist_create();
smartlist_t *chosen_flags = smartlist_create();
smartlist_t *versions = smartlist_create();
int *n_voter_flags; /* n_voter_flags[j] is the number of flags that
* votes[j] knows about. */
int *n_flag_voters; /* n_flag_voters[f] is the number of votes that care
* about flags[f]. */
int **flag_map; /* flag_map[j][b] is an index f such that flag_map[f]
* is the same flag as votes[j]->known_flags[b]. */
int *named_flag; /* Index of the flag "Named" for votes[j] */
int *unnamed_flag; /* Index of the flag "Unnamed" for votes[j] */
int chosen_named_idx, chosen_unnamed_idx;
strmap_t *name_to_id_map = strmap_new();
char conflict[DIGEST_LEN];
char unknown[DIGEST_LEN];
memset(conflict, 0, sizeof(conflict));
memset(unknown, 0xff, sizeof(conflict));
index = tor_malloc_zero(sizeof(int)*smartlist_len(votes));
size = tor_malloc_zero(sizeof(int)*smartlist_len(votes));
n_voter_flags = tor_malloc_zero(sizeof(int) * smartlist_len(votes));
n_flag_voters = tor_malloc_zero(sizeof(int) * smartlist_len(flags));
flag_map = tor_malloc_zero(sizeof(int*) * smartlist_len(votes));
named_flag = tor_malloc_zero(sizeof(int) * smartlist_len(votes));
unnamed_flag = tor_malloc_zero(sizeof(int) * smartlist_len(votes));
for (i = 0; i < smartlist_len(votes); ++i)
unnamed_flag[i] = named_flag[i] = -1;
chosen_named_idx = smartlist_string_pos(flags, "Named");
chosen_unnamed_idx = smartlist_string_pos(flags, "Unnamed");
/* Build the flag index. */
SMARTLIST_FOREACH(votes, networkstatus_t *, v,
{
flag_map[v_sl_idx] = tor_malloc_zero(
sizeof(int)*smartlist_len(v->known_flags));
SMARTLIST_FOREACH(v->known_flags, const char *, fl,
{
int p = smartlist_string_pos(flags, fl);
tor_assert(p >= 0);
flag_map[v_sl_idx][fl_sl_idx] = p;
++n_flag_voters[p];
if (!strcmp(fl, "Named"))
named_flag[v_sl_idx] = fl_sl_idx;
if (!strcmp(fl, "Unnamed"))
unnamed_flag[v_sl_idx] = fl_sl_idx;
});
n_voter_flags[v_sl_idx] = smartlist_len(v->known_flags);
size[v_sl_idx] = smartlist_len(v->routerstatus_list);
});
/* Named and Unnamed get treated specially */
if (consensus_method >= 2) {
SMARTLIST_FOREACH(votes, networkstatus_t *, v,
{
uint64_t nf;
if (named_flag[v_sl_idx]<0)
continue;
nf = U64_LITERAL(1) << named_flag[v_sl_idx];
SMARTLIST_FOREACH(v->routerstatus_list, vote_routerstatus_t *, rs,
{
if ((rs->flags & nf) != 0) {
const char *d = strmap_get_lc(name_to_id_map, rs->status.nickname);
if (!d) {
/* We have no name officially mapped to this digest. */
strmap_set_lc(name_to_id_map, rs->status.nickname,
rs->status.identity_digest);
} else if (d != conflict &&
memcmp(d, rs->status.identity_digest, DIGEST_LEN)) {
/* Authorities disagree about this nickname. */
strmap_set_lc(name_to_id_map, rs->status.nickname, conflict);
} else {
/* It's already a conflict, or it's already this ID. */
}
}
});
});
SMARTLIST_FOREACH(votes, networkstatus_t *, v,
{
uint64_t uf;
if (unnamed_flag[v_sl_idx]<0)
continue;
uf = U64_LITERAL(1) << unnamed_flag[v_sl_idx];
SMARTLIST_FOREACH(v->routerstatus_list, vote_routerstatus_t *, rs,
{
if ((rs->flags & uf) != 0) {
const char *d = strmap_get_lc(name_to_id_map, rs->status.nickname);
if (d == conflict || d == unknown) {
/* Leave it alone; we know what it is. */
} else if (!d) {
/* We have no name officially mapped to this digest. */
strmap_set_lc(name_to_id_map, rs->status.nickname, unknown);
} else if (!memcmp(d, rs->status.identity_digest, DIGEST_LEN)) {
/* Authorities disagree about this nickname. */
strmap_set_lc(name_to_id_map, rs->status.nickname, conflict);
} else {
/* It's mapped to a different name. */
}
}
});
});
}
/* Now go through all the votes */
flag_counts = tor_malloc(sizeof(int) * smartlist_len(flags));
while (1) {
vote_routerstatus_t *rs;
routerstatus_t rs_out;
const char *lowest_id = NULL;
const char *chosen_version;
const char *chosen_name = NULL;
int is_named = 0, is_unnamed = 0;
int naming_conflict = 0;
int n_listing = 0;
int i;
char buf[256];
/* Of the next-to-be-considered digest in each voter, which is first? */
SMARTLIST_FOREACH(votes, networkstatus_t *, v, {
if (index[v_sl_idx] < size[v_sl_idx]) {
rs = smartlist_get(v->routerstatus_list, index[v_sl_idx]);
if (!lowest_id ||
memcmp(rs->status.identity_digest, lowest_id, DIGEST_LEN) < 0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -