📄 dirvote.c
字号:
out->cache_info.signed_descriptor_body =
tor_strndup(cert->cache_info.signed_descriptor_body,
cert->cache_info.signed_descriptor_len);
out->cache_info.saved_location = SAVED_NOWHERE;
out->identity_key = crypto_pk_dup_key(cert->identity_key);
out->signing_key = crypto_pk_dup_key(cert->signing_key);
return out;
}
/* =====
* Vote scheduling
* ===== */
/** Set *<b>timing_out</b> to the intervals at which we would like to vote.
* Note that these aren't the intervals we'll use to vote; they're the ones
* that we'll vote to use. */
void
dirvote_get_preferred_voting_intervals(vote_timing_t *timing_out)
{
or_options_t *options = get_options();
tor_assert(timing_out);
timing_out->vote_interval = options->V3AuthVotingInterval;
timing_out->n_intervals_valid = options->V3AuthNIntervalsValid;
timing_out->vote_delay = options->V3AuthVoteDelay;
timing_out->dist_delay = options->V3AuthDistDelay;
}
/** Return the start of the next interval of size <b>interval</b> (in seconds)
* after <b>now</b>. Midnight always starts a fresh interval, and if the last
* interval of a day would be truncated to less than half its size, it is
* rolled into the previous interval. */
time_t
dirvote_get_start_of_next_interval(time_t now, int interval)
{
struct tm tm;
time_t midnight_today;
time_t midnight_tomorrow;
time_t next;
tor_gmtime_r(&now, &tm);
tm.tm_hour = 0;
tm.tm_min = 0;
tm.tm_sec = 0;
midnight_today = tor_timegm(&tm);
midnight_tomorrow = midnight_today + (24*60*60);
next = midnight_today + ((now-midnight_today)/interval + 1)*interval;
/* Intervals never cross midnight. */
if (next > midnight_tomorrow)
next = midnight_tomorrow;
/* If the interval would only last half as long as it's supposed to, then
* skip over to the next day. */
if (next + interval/2 > midnight_tomorrow)
next = midnight_tomorrow;
return next;
}
/** Scheduling information for a voting interval. */
static struct {
/** When do we generate and distribute our vote for this interval? */
time_t voting_starts;
/** When do we send an HTTP request for any votes that we haven't
* been posted yet?*/
time_t fetch_missing_votes;
/** When do we give up on getting more votes and generate a consensus? */
time_t voting_ends;
/** When do we send an HTTP request for any signatures we're expecting to
* see on the consensus? */
time_t fetch_missing_signatures;
/** When do we publish the consensus? */
time_t interval_starts;
/* True iff we have generated and distributed our vote. */
int have_voted;
/* True iff we've requested missing votes. */
int have_fetched_missing_votes;
/* True iff we have built a consensus and sent the signatures around. */
int have_built_consensus;
/* True iff we've fetched missing signatures. */
int have_fetched_missing_signatures;
/* True iff we have published our consensus. */
int have_published_consensus;
} voting_schedule = {0,0,0,0,0,0,0,0,0,0};
/** Set voting_schedule to hold the timing for the next vote we should be
* doing. */
void
dirvote_recalculate_timing(or_options_t *options, time_t now)
{
int interval, vote_delay, dist_delay;
time_t start;
time_t end;
networkstatus_t *consensus;
if (!authdir_mode_v3(options))
return;
consensus = networkstatus_get_live_consensus(now);
memset(&voting_schedule, 0, sizeof(voting_schedule));
if (consensus) {
interval = (int)( consensus->fresh_until - consensus->valid_after );
vote_delay = consensus->vote_seconds;
dist_delay = consensus->dist_seconds;
} else {
interval = DEFAULT_VOTING_INTERVAL_WHEN_NO_CONSENSUS;
vote_delay = dist_delay = 300;
}
tor_assert(interval > 0);
if (vote_delay + dist_delay > interval/2)
vote_delay = dist_delay = interval / 4;
start = voting_schedule.interval_starts =
dirvote_get_start_of_next_interval(now,interval);
end = dirvote_get_start_of_next_interval(start+1, interval);
tor_assert(end > start);
voting_schedule.fetch_missing_signatures = start - (dist_delay/2);
voting_schedule.voting_ends = start - dist_delay;
voting_schedule.fetch_missing_votes = start - dist_delay - (vote_delay/2);
voting_schedule.voting_starts = start - dist_delay - vote_delay;
{
char tbuf[ISO_TIME_LEN+1];
format_iso_time(tbuf, voting_schedule.interval_starts);
log_notice(LD_DIR,"Choosing expected valid-after time as %s: "
"consensus_set=%d, interval=%d",
tbuf, consensus?1:0, interval);
}
}
/** Entry point: Take whatever voting actions are pending as of <b>now</b>. */
void
dirvote_act(or_options_t *options, time_t now)
{
if (!authdir_mode_v3(options))
return;
if (!voting_schedule.voting_starts) {
char *keys = list_v3_auth_ids();
authority_cert_t *c = get_my_v3_authority_cert();
log_notice(LD_DIR, "Scheduling voting. Known authority IDs are %s. "
"Mine is %s.",
keys, hex_str(c->cache_info.identity_digest, DIGEST_LEN));
tor_free(keys);
dirvote_recalculate_timing(options, now);
}
if (voting_schedule.voting_starts < now && !voting_schedule.have_voted) {
log_notice(LD_DIR, "Time to vote.");
dirvote_perform_vote();
voting_schedule.have_voted = 1;
}
if (voting_schedule.fetch_missing_votes < now &&
!voting_schedule.have_fetched_missing_votes) {
log_notice(LD_DIR, "Time to fetch any votes that we're missing.");
dirvote_fetch_missing_votes();
voting_schedule.have_fetched_missing_votes = 1;
}
if (voting_schedule.voting_ends < now &&
!voting_schedule.have_built_consensus) {
log_notice(LD_DIR, "Time to compute a consensus.");
dirvote_compute_consensus();
/* XXXX We will want to try again later if we haven't got enough
* votes yet. Implement this if it turns out to ever happen. */
voting_schedule.have_built_consensus = 1;
}
if (voting_schedule.fetch_missing_signatures < now &&
!voting_schedule.have_fetched_missing_signatures) {
log_notice(LD_DIR, "Time to fetch any signatures that we're missing.");
dirvote_fetch_missing_signatures();
voting_schedule.have_fetched_missing_signatures = 1;
}
if (voting_schedule.interval_starts < now &&
!voting_schedule.have_published_consensus) {
log_notice(LD_DIR, "Time to publish the consensus and discard old votes");
dirvote_publish_consensus();
dirvote_clear_votes(0);
voting_schedule.have_published_consensus = 1;
/* XXXX We will want to try again later if we haven't got enough
* signatures yet. Implement this if it turns out to ever happen. */
dirvote_recalculate_timing(options, now);
}
}
/** A vote networkstatus_t and its unparsed body: held around so we can
* use it to generate a consensus (at voting_ends) and so we can serve it to
* other authorities that might want it. */
typedef struct pending_vote_t {
cached_dir_t *vote_body;
networkstatus_t *vote;
} pending_vote_t;
/** List of pending_vote_t for the current vote. Before we've used them to
* build a consensus, the votes go here. */
static smartlist_t *pending_vote_list = NULL;
/** List of pending_vote_t for the previous vote. After we've used them to
* build a consensus, the votes go here for the next period. */
static smartlist_t *previous_vote_list = NULL;
/** The body of the consensus that we're currently building. Once we
* have it built, it goes into dirserv.c */
static char *pending_consensus_body = NULL;
/** The detached signatures for the consensus that we're currently
* building. */
static char *pending_consensus_signatures = NULL;
/** The parsed in-progress consensus document. */
static networkstatus_t *pending_consensus = NULL;
/** List of ns_detached_signatures_t: hold signatures that get posted to us
* before we have generated the consensus on our own. */
static smartlist_t *pending_consensus_signature_list = NULL;
/** Generate a networkstatus vote and post it to all the v3 authorities.
* (V3 Authority only) */
static int
dirvote_perform_vote(void)
{
crypto_pk_env_t *key = get_my_v3_authority_signing_key();
authority_cert_t *cert = get_my_v3_authority_cert();
networkstatus_t *ns;
char *contents;
pending_vote_t *pending_vote;
int status;
const char *msg = "";
if (!cert || !key) {
log_warn(LD_NET, "Didn't find key/certificate to generate v3 vote");
return -1;
}
if (!(ns = dirserv_generate_networkstatus_vote_obj(key, cert)))
return -1;
contents = format_networkstatus_vote(key, ns);
networkstatus_vote_free(ns);
if (!contents)
return -1;
pending_vote = dirvote_add_vote(contents, &msg, &status);
tor_free(contents);
if (!pending_vote) {
log_warn(LD_DIR, "Couldn't store my own vote! (I told myself, '%s'.)",
msg);
return -1;
}
directory_post_to_dirservers(DIR_PURPOSE_UPLOAD_VOTE,
ROUTER_PURPOSE_GENERAL,
V3_AUTHORITY,
pending_vote->vote_body->dir,
pending_vote->vote_body->dir_len, 0);
log_notice(LD_DIR, "Vote posted.");
return 0;
}
/** Send an HTTP request to every other v3 authority, for the votes of every
* authority for which we haven't received a vote yet in this period. (V3
* authority only) */
static void
dirvote_fetch_missing_votes(void)
{
smartlist_t *missing_fps = smartlist_create();
char *resource;
SMARTLIST_FOREACH(router_get_trusted_dir_servers(),
trusted_dir_server_t *, ds,
{
if (!(ds->type & V3_AUTHORITY))
continue;
if (!dirvote_get_vote(ds->v3_identity_digest,
DGV_BY_ID|DGV_INCLUDE_PENDING)) {
char *cp = tor_malloc(HEX_DIGEST_LEN+1);
base16_encode(cp, HEX_DIGEST_LEN+1, ds->v3_identity_digest,
DIGEST_LEN);
smartlist_add(missing_fps, cp);
}
});
if (!smartlist_len(missing_fps)) {
smartlist_free(missing_fps);
return;
}
log_notice(LOG_NOTICE, "We're missing votes from %d authorities. Asking "
"every other authority for a copy.", smartlist_len(missing_fps));
resource = smartlist_join_strings(missing_fps, "+", 0, NULL);
directory_get_from_all_authorities(DIR_PURPOSE_FETCH_STATUS_VOTE,
0, resource);
tor_free(resource);
SMARTLIST_FOREACH(missing_fps, char *, cp, tor_free(cp));
smartlist_free(missing_fps);
}
/** Send a request to every other authority for its detached signatures,
* unless we have signatures from all other v3 authorities already. */
static void
dirvote_fetch_missing_signatures(void)
{
if (!pending_consensus)
return;
if (networkstatus_check_consensus_signature(pending_consensus, -1) == 1)
return; /* we have a signature from everybody. */
directory_get_from_all_authorities(DIR_PURPOSE_FETCH_DETACHED_SIGNATURES,
0, NULL);
}
/** Drop all currently pending votes, consensus, and detached signatures. */
static void
dirvote_clear_votes(int all_votes)
{
if (!previous_vote_list)
previous_vote_list = smartlist_create();
if (!pending_vote_list)
pending_vote_list = smartlist_create();
/* All "previous" votes are now junk. */
SMARTLIST_FOREACH(previous_vote_list, pending_vote_t *, v, {
cached_dir_decref(v->vote_body);
v->vote_body = NULL;
networkstatus_vote_free(v->vote);
tor_free(v);
});
smartlist_clear(previous_vote_list);
if (all_votes) {
/* If we're dumping all the votes, we delete the pending ones. */
SMARTLIST_FOREACH(pending_vote_list, pending_vote_t *, v, {
cached_dir_decref(v->vote_body);
v->vote_body = NULL;
networkstatus_vote_free(v->vote);
tor_free(v);
});
} else {
/* Otherwise, we move them into "previous". */
smartlist_add_all(previous_vote_list, pending_vote_list);
}
smartlist_clear(pending_vote_list);
if (pending_consensus_signature_list) {
SMARTLIST_FOREACH(pending_consensus_signature_list, char *, cp,
tor_free(cp));
smartlist_clear(pending_consensus_signature_list);
}
tor_free(pending_consensus_body);
tor_free(pending_consensus_signatures);
if (pending_consensus) {
networkstatus_vote_free(pending_consensus);
pending_consensus = NULL;
}
}
/** Return a newly allocated string containing the hex-encoded v3 authority
identity digest of every recognized v3 authority. */
static char *
list_v3_auth_ids(void)
{
smartlist_t *known_v3_keys = smartlist_create();
char *keys;
SMARTLIST_FOREACH(router_get_trusted_dir_servers(),
trusted_dir_server_t *, ds,
if ((ds->type & V3_AUTHORITY) &&
!tor_digest_is_zero(ds->v3_identity_digest))
smartlist_add(known_v3_keys,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -