1
//! Directory Mirror Operation.
2
//!
3
//! # Specifications
4
//!
5
//! * [Directory cache operation](https://spec.torproject.org/dir-spec/directory-cache-operation.html).
6
//!
7
//! # Rationale
8
//!
9
//! This module implements the "core operation" of a directory mirror.
10
//! "Core operation" primarily refers to the logic involved in downloading
11
//! network documents from an upstream authority and inserting them into the
12
//! database.  This module notably **DOES NOT** provide any public (in the HTTP
13
//! sense) endpoints for querying documents.  This is purposely behind a different
14
//! module, so that the directory authority implementation can also make use of it.
15
//! You can think of this module as the one implementing the things unique
16
//! to directory mirrors.
17

            
18
use std::{
19
    collections::{HashMap, HashSet, VecDeque},
20
    fmt::Debug,
21
    hash::Hash,
22
    marker::PhantomData,
23
    mem,
24
    net::SocketAddr,
25
    time::Duration,
26
};
27

            
28
use educe::Educe;
29
use r2d2::Pool;
30
use r2d2_sqlite::SqliteConnectionManager;
31
use rand::Rng;
32
use rusqlite::Transaction;
33
use tokio::net::TcpStream;
34
use tokio_util::compat::TokioAsyncReadCompatExt;
35
use tor_checkable::TimeBound;
36
use tor_dirclient::{
37
    Error as DirClientError, RequestError, RequestFailedError,
38
    request::{AuthCertRequest, ConsensusRequest, Requestable, RouterDescRequest},
39
};
40
use tor_dircommon::{authority::AuthorityContacts, config::DirTolerance};
41
use tor_error::internal;
42
use tor_netdoc::{
43
    doc::{
44
        authcert::{AuthCert, AuthCertKeyIds, AuthCertUnverified},
45
        netstatus::{ConsensusFlavor, ConsensusVerifiabilityError},
46
        routerdesc::RouterDescUnverified,
47
    },
48
    parse2::{
49
        self, NetdocParseable, NetdocParseableUnverified, ParseError, ParseInput, VerifyFailed,
50
    },
51
};
52
use tor_rtcompat::PreferredRuntime;
53
use tracing::{debug, warn};
54

            
55
use crate::{
56
    database::{
57
        self as db, AuthCertMeta, ConsensusMeta, ContentEncoding, DescriptorMeta, Sha1, Sha256,
58
        Timestamp,
59
    },
60
    err::{AuthorityRequestError, DatabaseError, OperationError},
61
    types::{FlavoredConsensusUnverified, FlavoredDescriptor, FlavoredDescriptorPlain},
62
};
63

            
64
mod poc;
65

            
66
/// The duration to try verifying an unverified consensus.
67
///
68
/// This exists primarily to properly handle the
69
/// [`ConsensusVerifiabilityError::InsufficientTrustedSigners`] error.  Besides,
70
/// it also serves as an upper-limit for the time we can spend in the phase for
71
/// fetching authority certificates, which could otherwise run for a very long
72
/// time, as long as every request makes progress and finishes before a
73
/// connection timeout is reached.
74
///
75
/// Obviously, we cannot use the values inside the consensus, as they are
76
/// untrusted.
77
const UNVERIFIED_TTL: Duration = Duration::from_mins(10);
78

            
79
/// Query a maximum.
80
const DESCRIPTOR_LIMIT: Option<u64> = Some(92);
81

            
82
/// The endpoint(s) of a download authority identifying it.
83
type DownloadAuthority = [SocketAddr];
84

            
85
/// The various states for the [`StaticEngine`].
86
#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::Display)]
87
enum State {
88
    /// Loads the most recent valid (and verified) consensus from the database
89
    /// into memory.
90
    ///
91
    /// Transitions from:
92
    /// * Start, if a recent valid consensus exists in the database.
93
    /// * [`State::StoreConsensus`], if successfully finished.
94
    ///
95
    /// Transitions into:
96
    /// * [`State::Descriptors`]
97
    LoadConsensus,
98

            
99
    /// Downloads the most recent consensus from a directory authority.
100
    ///
101
    /// Transitions from:
102
    /// * Start, if no recent valid consensus exists in the database.
103
    /// * [`State::Descriptors`], if ttl is over.
104
    /// * [`State::Hibernate`], if ttl is over.
105
    ///
106
    /// Transitions into:
107
    /// * [`State::AuthCerts`], if we miss authority certificates.
108
    /// * [`State::StoreConsensus`], if all authority certificates exist in the
109
    ///   database.
110
    /// * [`State::Hibernate`], if we have insufficient trusted authorities.
111
    // TODO DIRMIRROR: What to do in the case of getting an invalid consensus
112
    // such as junk data?  The normal retry logic sounds reasonable here.
113
    FetchConsensus,
114

            
115
    /// Downloads, validates, and stores the missing authority certificates from
116
    /// the downloaded unvalidated consensus into the database.
117
    ///
118
    /// Transitions from:
119
    /// * [`State::FetchConsensus`], if we miss authority certificates.
120
    /// * [`State::AuthCerts`], if we still miss authority certificates.
121
    ///
122
    /// Transitions into:
123
    /// * [`State::AuthCerts`], if we still miss authority certificates.
124
    /// * [`State::StoreConsensus`], if we got all authority certificates.
125
    AuthCerts,
126

            
127
    /// Validates and stores the downloaded unvalidated consensus into the
128
    /// database.
129
    ///
130
    /// Transitions from:
131
    /// * [`State::FetchConsensus`], if we have all authority certificates.
132
    /// * [`State::AuthCerts`], if we have all authority certificates.
133
    ///
134
    /// Transitions into:
135
    /// * [`State::LoadConsensus`]
136
    StoreConsensus,
137

            
138
    /// Downloads missing network documents (descriptors) from a directory
139
    /// authority.
140
    ///
141
    /// Transitions from:
142
    /// * [`State::LoadConsensus`], if we initialize.
143
    /// * [`State::Descriptors`], if we still have missing descriptors left.
144
    ///
145
    /// Transitions into:
146
    /// * [`State::FetchConsensus`], if ttl is over.
147
    /// * [`State::Descriptors`], if we still have missing descriptors left.
148
    /// * [`State::Hibernate`], if nothing is left.
149
    Descriptors,
150

            
151
    /// Hibernate because nothing is left.
152
    ///
153
    /// Transitions from:
154
    /// * [`State::FetchConsensus`], if we have insufficient trusted
155
    ///   authorities.
156
    /// * [`State::Descriptors`], if we have downloaded all descriptors.
157
    ///
158
    /// Transitions into:
159
    /// * [`State::FetchConsensus`], if the ttl is over.
160
    Hibernate,
161
}
162

            
163
/// The execution engine for the finite state machine.
164
///
165
/// The states themselves are explained in [`State`].
166
///
167
/// This data structure itself is static and contains no state, but merely
168
/// configuration primitives that stay constant throughout the runtime of the
169
/// program, such as the [`AuthorityContacts`], and the
170
/// [`DirTolerance`].  It can be kept throughout the entire runtime and only
171
/// consists for convenience in order to not give each state machine related
172
/// (then static) method a super long signature containing these fields.
173
///
174
/// Besides these dynamic fields, there is also a generic parameter specifying
175
/// the consensus flavor that is being used.
176
///
177
/// The state itself is computed fully deterministically from the data found
178
/// within the database and [`ConsensusBoundData`].
179
///
180
/// This is the reason on why this structure is not called `StateMachine`,
181
/// because this implies that the type in itself carries state, which is not
182
/// true, because the state is stored entirely external, with this engine
183
/// only processing and modifying it.
184
///
185
/// See [`StaticEngine::determine_state()`] for more details.
186
#[derive(Debug)]
187
struct StaticEngine<T> {
188
    /// The authorities we are acknowledging.
189
    authorities: AuthorityContacts,
190

            
191
    /// The document tolerance we are accepting.
192
    tolerance: DirTolerance,
193

            
194
    /// The preferred runtime for compatibility with other arti crates.
195
    ///
196
    /// Generally obtained through [`PreferredRuntime::current()`].
197
    rt: PreferredRuntime,
198

            
199
    /// The content encodings we apply to documents.
200
    encodings: HashSet<ContentEncoding>,
201

            
202
    /// Utilizes the generic type parameter.
203
    _phantom: PhantomData<T>,
204
}
205

            
206
/// Additional state machine data concerning a single consensus.
207
///
208
/// This data type exists to store data that cannot be stored in the database,
209
/// including the consensus that is currently verified, the consensus that is
210
/// currently served, the timestamp until we will serve it, etc.
211
///
212
/// # Field Requirements
213
///
214
/// In order for a field to be contained here, at least one of the following
215
/// criteria should be met:
216
/// * The item needs to persist between multiple invocation and cannot be
217
///   obtained from the database.
218
/// * The item itself serves as an identifier to the database and querying it
219
///   it again can not be done in a pure fashion, e.g. a [`ConsensusMeta`]
220
///   identifier.
221
/// * The item exists purely for optimization purposes and can be thrown away
222
///   at any time without data loss, such as content-addressable caches.
223
///   These caches must be pure in the sense that a cache miss must always
224
///   return the same result as a cache hit and vice versa.
225
//
226
// TODO DIRMIRROR: This is probably better of as a struct instead of an enum.
227
#[derive(Debug, Clone)]
228
enum ConsensusBoundData<T: FlavoredConsensusUnverified> {
229
    /// No state is loaded in memory at the moment.
230
    None,
231

            
232
    /// We have downloaded a consensus but it is not yet verified.
233
    Unverified {
234
        /// The unverified parsed consensus we have.
235
        consensus: T,
236

            
237
        /// The unparsed raw consensus we have.
238
        raw: String,
239

            
240
        /// When to stop dealing with this consensus and download again.
241
        ///
242
        /// Useful to have in order to not stuck forever in a verification
243
        /// loop, i.e. due to unavailable authentication certificates, etc.
244
        ttl: Timestamp,
245
    },
246

            
247
    /// We have downloaded and verified a consensus.
248
    Verified {
249
        /// The verified consensus we have.
250
        ///
251
        /// Contained in this structure because it is the only way we can
252
        /// uniquely identify a consensus, which is crucial for operation, as
253
        /// things like the `ttl` are derived from it.
254
        ///
255
        /// While methods like [`ConsensusMeta::query`] would probably also
256
        /// work correctly, relying on them feels rather wrong, as they were
257
        /// not made to be pure.
258
        consensus: ConsensusMeta<T>,
259

            
260
        /// When to stop dealing with this consensus and fetching a new one.
261
        ttl: Timestamp,
262

            
263
        /// Descriptors we refuse to further download.
264
        ///
265
        /// Right now, this state gets cleaned, everytime we restart or obtain
266
        /// a new consensus.  This is probably okay, as we most likely do not
267
        /// want to store it on disk.
268
        futile: Box<FutileDescriptors>,
269
    },
270
}
271

            
272
impl<T: FlavoredConsensusUnverified> StaticEngine<T> {
273
    /// Determines the [`State`] only from the database and [`ConsensusBoundData`].
274
    ///
275
    /// This method is fully idempotent, meaning it only depends upon the data
276
    /// found within the database and the [`ConsensusBoundData`]; there is no
277
    /// internal `state` variable or something contained within [`StaticEngine`].
278
7
    fn determine_state(
279
7
        &self,
280
7
        tx: &Transaction<'_>,
281
7
        data: &ConsensusBoundData<T>,
282
7
        now: Timestamp,
283
7
    ) -> Result<State, DatabaseError> {
284
        // Determine the state primarily upon ConsensusBoundData combined with
285
        // a few database queries, as well as the current time of course.
286
7
        let state = match data {
287
            // ConsensusBoundData::None means that we currently have no
288
            // consensus in memory.  This may be the case because we just
289
            // started up or because we just downloaded, validated, and inserted
290
            // a consensus into the database and reset ConsensusBoundData to
291
            // None afterwards.
292
            ConsensusBoundData::None => {
293
                // Check whether there is a valid consensus in the database at all.
294
2
                match self.recent_consensus(tx, now)? {
295
                    // Some consensus means we can load it.
296
1
                    Some(_) => State::LoadConsensus,
297

            
298
                    // None means we must download it.
299
1
                    None => State::FetchConsensus,
300
                }
301
            }
302

            
303
            // ConsensusBoundData::Unverified means that we recently downloaded
304
            // a consensus through State::FetchConsensus.  It is not fully
305
            // validated yet and we may not even be able due to missing
306
            // authority certificates.
307
4
            ConsensusBoundData::Unverified { consensus, ttl, .. } if now < *ttl => {
308
4
                let certs_already = self.certs_already(tx, now)?;
309
4
                match consensus.can_verify(self.authorities.v3idents(), &certs_already) {
310
                    // We can verify and insert the consensus.
311
2
                    Ok(()) => State::StoreConsensus,
312

            
313
                    // We will never be able to verify the consensus, better
314
                    // times may come ...
315
                    Err(ConsensusVerifiabilityError::InsufficientTrustedSigners) => {
316
1
                        State::Hibernate
317
                    }
318

            
319
                    // We need to fetch more authority certificates from the
320
                    // network.
321
1
                    Err(ConsensusVerifiabilityError::MissingAuthCerts { .. }) => State::AuthCerts,
322
                }
323
            }
324

            
325
            ConsensusBoundData::Unverified { .. } => State::FetchConsensus,
326

            
327
            // ConsensusBoundData::Verified means that we have successfully
328
            // loaded a recent valid consensus from the database using
329
            // State::LoadConsensus.  Depending on this, we download the missing
330
            // network documents (descriptors) from a directory authority, if
331
            // any.
332
            ConsensusBoundData::Verified {
333
1
                consensus,
334
1
                ttl,
335
1
                futile,
336
            } => {
337
                // See if there are any missing descriptors that are not Refused
338
                // yet.  It is important to not exclude the NotFound descriptors
339
                // here because they may be available at a different authority.
340
                //
341
                // TODO DIRMIRROR: What to do in the case there is no authority
342
                // left in our list of endpoints?  Right now, this module of the
343
                // code is not aware of the endpoints to try and it is the
344
                // responsibility of the caller to check this.  Perhaps we
345
                // should provide a possibility to force hibernate, as in passing
346
                // an Option<DownloadAuthority> or an empty list.  An alternative
347
                // worth exploring might also be to perform the RetryDelay
348
                // wait logic, as in to not let the caller perform any timeouts
349
                // themselves.  Either way, we want to wait to hibernate and
350
                // wait for the next consensus in the case that no authority
351
                // is left.
352
                //
353
                // TODO DIRMIRROR: This appears to be prone to copy and paste
354
                // errors, we should do something about it, as in macros and/or
355
                // traits.
356
1
                let (missing_routers, missing_micros, missing_extras) = (
357
1
                    consensus.missing_routers(tx, futile.routers.refused(), Some(1))?,
358
1
                    consensus.missing_micros(tx, futile.micros.refused(), Some(1))?,
359
1
                    consensus.missing_extras(tx, futile.extras.refused(), Some(1))?,
360
                );
361

            
362
1
                if *ttl <= now {
363
                    // The ttl has been surpassed, download a new
364
                    // consensus.  It is very important TO NOT transition to
365
                    // State::LoadConsensus here, because the current consensus
366
                    // may still be valid but not fresh anymore, in which case
367
                    // State::LoadConsensus will continue to obtain it from the
368
                    // database until valid-after has been surpassed, which is
369
                    // most definitely not what we want.
370
                    State::FetchConsensus
371
1
                } else if missing_routers.is_empty()
372
                    && missing_micros.is_empty()
373
                    && missing_extras.is_empty()
374
                {
375
                    // All queues are empty, meaning we are done, until ttl
376
                    // ends.
377
                    State::Hibernate
378
                } else {
379
                    // The ttl has not been surpassed and we have stuff
380
                    // to download, so we need to obtain the descriptors.
381
1
                    State::Descriptors
382
                }
383
            }
384
        };
385
7
        Ok(state)
386
7
    }
387

            
388
    /// Executes a single state iteration in the finite state machine.
389
    ///
390
    /// The return value is of type [`Result<(), OperationError>`].
391
    /// The success type is not of much interest for calling applications.
392
    /// However, the error case itself should be passed towards
393
    /// [`crate::err::IsFatal::is_fatal()`] in order to either abort the
394
    /// application or retry with an appropriate timeout.
395
    ///
396
    // TODO: Use tracing instrumentation here.
397
    // TODO DIRMIRROR: Document the state transition check which we have to do
398
    // because of database invariances no longer holding true.
399
    async fn execute<R: Rng>(
400
        &self,
401
        pool: &Pool<SqliteConnectionManager>,
402
        data: &mut ConsensusBoundData<T>,
403
        endpoint: &DownloadAuthority,
404
        now: Timestamp,
405
        rng: &mut R,
406
    ) -> Result<(), OperationError> {
407
        // TODO DIRMIRROR: Right now, we pass around the pool object which is
408
        // not very nice.  Instead, we should match based on the following
409
        // criteria: Needs write transaction, needs read transaction, needs
410
        // pool, needs no database access.
411

            
412
        // TODO: Should we return DatabaseError or something like
413
        // StateDeterminationError?  Either way, both cases should be seriously
414
        // fatal.
415
        let state = db::read_tx(pool, |tx| self.determine_state(tx, data, now))??;
416
        debug!("state is {state}");
417

            
418
        match state {
419
            State::LoadConsensus => self.load_consensus(pool, data, now, rng),
420
            State::FetchConsensus => Ok(self.fetch_consensus(data, endpoint, now).await?),
421
            State::AuthCerts => self.auth_certs(pool, data, endpoint, now).await,
422
            State::StoreConsensus => self.store_consensus(pool, data, now),
423
            State::Descriptors => self.descriptors(pool, data, endpoint, now).await,
424
            State::Hibernate => self.hibernate(data, now).await,
425
        }
426
    }
427

            
428
    /// Executes [`State::LoadConsensus`].
429
    ///
430
    /// This method does the following:
431
    /// * Load the most recent valid consensus from the database.
432
    /// * Compute the ttl for it.
433
    /// * Compute the missing descriptors for it.
434
3
    fn load_consensus<R: Rng>(
435
3
        &self,
436
3
        pool: &Pool<SqliteConnectionManager>,
437
3
        data: &mut ConsensusBoundData<T>,
438
3
        now: Timestamp,
439
3
        rng: &mut R,
440
3
    ) -> Result<(), OperationError> {
441
        // Load the most recent valid consensus from the database.
442
        //
443
        // If there is no consensus, we should have not entered the state, which
444
        // means that the database must have been externally verified.
445
        // In this case, it is probably better to return a bug, as external
446
        // applications arbitrarily modifying the database while we are running
447
        // leaves too much room for wrong/weird behavior.
448
3
        let consensus = db::read_tx(pool, |tx| self.recent_consensus(tx, now))??
449
3
            .ok_or_else(|| internal!("database externally modified?"))?;
450
3
        let ttl = consensus.ttl(rng);
451
3
        let futile = Box::new(FutileDescriptors::default());
452

            
453
3
        *data = ConsensusBoundData::Verified {
454
3
            consensus,
455
3
            ttl,
456
3
            futile,
457
3
        };
458
3
        Ok(())
459
3
    }
460

            
461
    /// Fetches a consensus from an upstream authority.
462
    // TODO DIRMIRROR: Add logging.
463
    #[allow(clippy::string_slice)] // TODO
464
1
    async fn fetch_consensus(
465
1
        &self,
466
1
        data: &mut ConsensusBoundData<T>,
467
1
        endpoint: &DownloadAuthority,
468
1
        now: Timestamp,
469
1
    ) -> Result<(), AuthorityRequestError> {
470
        // Obtain the consensus.
471
1
        let (raw, consensus) = self
472
1
            .send_request(endpoint, ConsensusRequest::new(T::flavor()))
473
1
            .await?;
474
1
        let mut consensus = consensus.into_iter().collect::<VecDeque<_>>();
475

            
476
        // Check for the correct number of results.
477
1
        if consensus.len() != 1 {
478
            return Err(AuthorityRequestError::Response(
479
                "invalid number of consensus?",
480
            ));
481
1
        }
482
1
        let (consensus, start, end) = match consensus.pop_front().expect("pop failed?") {
483
1
            (Ok(consensus), start, end) => (consensus, start, end),
484
            // We do not mark unparseable consensuses as futile because they
485
            // cannot be referenced in a semi-unique fashion but rather depend
486
            // on the time only.
487
            //
488
            // TODO DIRMIRROR: Return a more descriptive error; ideally in
489
            // all places where only one document was returned and that document
490
            // failed to parse.
491
            (Err(_), _, _) => {
492
                return Err(AuthorityRequestError::Response(
493
                    "invalid consensus received",
494
                ));
495
            }
496
        };
497
1
        let raw = raw[start..end].to_string();
498

            
499
1
        let ttl = now + UNVERIFIED_TTL;
500

            
501
        // And store it.
502
1
        *data = ConsensusBoundData::Unverified {
503
1
            consensus,
504
1
            raw,
505
1
            ttl,
506
1
        };
507

            
508
1
        Ok(())
509
1
    }
510

            
511
    /// Fetches, validates, and stores authority certificates.
512
    #[allow(clippy::string_slice)] // TODO
513
1
    async fn auth_certs(
514
1
        &self,
515
1
        pool: &Pool<SqliteConnectionManager>,
516
1
        data: &mut ConsensusBoundData<T>,
517
1
        endpoint: &DownloadAuthority,
518
1
        now: Timestamp,
519
1
    ) -> Result<(), OperationError> {
520
1
        let certs_already = db::read_tx(pool, |tx| self.certs_already(tx, now))??;
521

            
522
        // Obtain the missing authority certificate identifiers.
523
1
        let missing = match data {
524
1
            ConsensusBoundData::Unverified { consensus, .. } => {
525
1
                match consensus.can_verify(self.authorities.v3idents(), &certs_already) {
526
1
                    Err(ConsensusVerifiabilityError::MissingAuthCerts { missing, .. }) => missing,
527
                    _ => return Err(OperationError::Bug(internal!("we have all auth certs"))),
528
                }
529
            }
530
            _ => return Err(OperationError::Bug(internal!("data is not unverified"))),
531
        };
532

            
533
        // Compose the request.
534
1
        let mut requ = AuthCertRequest::new();
535
4
        for kp in missing.iter().copied() {
536
4
            requ.push(kp);
537
4
        }
538

            
539
        // Fire it off.
540
1
        let (resp, certs) = self
541
1
            .send_request::<_, AuthCertUnverified>(endpoint, requ)
542
1
            .await?;
543

            
544
        // Verify each certificate.  Invalid certificates and other problems get
545
        // logged and filtered out, with the result being then inserted into
546
        // the database.
547
1
        let certs = certs
548
1
            .into_iter()
549
4
            .filter_map(|(unverified, start, end)| {
550
4
                let unverified = match unverified {
551
4
                    Ok(v) => v,
552
                    Err(e) => {
553
                        // We do not mark unparseable authority certificates as
554
                        // futile because they do not have an authoritative
555
                        // digest we could ad-hoc compare them against.
556
                        debug!("ignoring invalid authority certificate: {e}");
557
                        return None;
558
                    }
559
                };
560
4
                let kp = unverified.inspect_unverified().0.key_ids();
561

            
562
                // Skip certificates we did not asked for.
563
                //
564
                // Not much of an issue because certificate verification will
565
                // usually fail anyways, except for this weird edge-case where we
566
                // actually have that id fingerprint in the v3idents.
567
4
                if !missing.contains(&kp) {
568
                    debug!("authority returned certificate we did not asked for: {kp:?}");
569
                    return None;
570
4
                }
571

            
572
4
                let verified = match unverified.verify(self.authorities.v3idents()) {
573
4
                    Ok(c) => c,
574
                    Err(e) => {
575
                        debug!("received invalid auth cert ({kp:?}): {e}");
576
                        return None;
577
                    }
578
                };
579

            
580
4
                let timely = match self
581
4
                    .tolerance
582
4
                    .extend_tolerance(verified)
583
4
                    .if_valid_at(&now.into())
584
                {
585
4
                    Ok(c) => c,
586
                    Err(e) => {
587
                        debug!("received non-timely auth cert ({kp:?}): {e}");
588
                        return None;
589
                    }
590
                };
591

            
592
4
                Some((timely, &resp[start..end]))
593
4
            })
594
1
            .collect::<Vec<_>>();
595

            
596
        // When we have reached this, it means that this call made no progress,
597
        // i.e. the authority only returned certificates we were not interested
598
        // in.
599
1
        if certs.is_empty() {
600
            Err(AuthorityRequestError::NoRequestedDocumentsReturned)?;
601
1
        }
602

            
603
        // Finally, insert them all into the database.
604
1
        db::rw_tx(pool, |tx| {
605
4
            for (cert, data) in certs {
606
4
                AuthCertMeta::insert(tx, self.encodings.iter().copied(), &cert, data)?;
607
            }
608
1
            Ok::<_, DatabaseError>(())
609
1
        })??;
610

            
611
1
        Ok(())
612
1
    }
613

            
614
    /// Verifies a consensus and inserts it into the database.
615
1
    fn store_consensus(
616
1
        &self,
617
1
        pool: &Pool<SqliteConnectionManager>,
618
1
        data: &mut ConsensusBoundData<T>,
619
1
        now: Timestamp,
620
1
    ) -> Result<(), OperationError> {
621
        // It is fine to replace data with ConsensusBoundData::None because in
622
        // both cases, the following state of this will always be LoadConsensus
623
        // or FetchConsensus, both of them no longer requiring the previous data.
624
        //
625
        // Yes, we want to explicitly discard this in the case of a recoverable
626
        // error in order to retry again by fetching a new consensus from a
627
        // different authority.
628
1
        let (unverified, raw) = match mem::replace(data, ConsensusBoundData::None) {
629
1
            ConsensusBoundData::Unverified { consensus, raw, .. } => (consensus, raw),
630
            _ => return Err(OperationError::Bug(internal!("data is not unverified"))),
631
        };
632

            
633
        // TODO DIRMIRROR: Somewhere in here, we should generate the consensus
634
        // diffs.  However, this is a CPU intensive task and needs further
635
        // coordination with the orport developers.  See arti#2706
636

            
637
1
        db::rw_tx(pool, |tx| {
638
1
            let certs_already = self.certs_already(tx, now)?;
639
1
            let (verified, sigs) = self.verify_consensus(unverified, &certs_already, now)?;
640
1
            ConsensusMeta::<T>::insert(
641
1
                tx,
642
1
                self.encodings.iter().copied(),
643
1
                (&verified, &sigs),
644
1
                &raw,
645
            )?;
646
1
            Ok::<_, OperationError>(())
647
1
        })?
648
1
    }
649

            
650
    /// Downloads, verifies, and inserts the descriptors associated with the
651
    /// currently served consensus.
652
    async fn descriptors(
653
        &self,
654
        pool: &Pool<SqliteConnectionManager>,
655
        data: &mut ConsensusBoundData<T>,
656
        endpoint: &DownloadAuthority,
657
        now: Timestamp,
658
    ) -> Result<(), OperationError> {
659
        match T::flavor() {
660
            ConsensusFlavor::Plain => {
661
                // TODO DIRMIRROR: Add extras.
662
                self.descriptors_rd(pool, data, endpoint, now).await
663
            }
664
            ConsensusFlavor::Microdesc => {
665
                // TODO DIRMIRROR: Add micros.
666
                todo!()
667
            }
668
        }
669
    }
670

            
671
    /// Router descriptor part of [`StaticEngine::descriptors()`].
672
2
    async fn descriptors_rd(
673
2
        &self,
674
2
        pool: &Pool<SqliteConnectionManager>,
675
2
        data: &mut ConsensusBoundData<T>,
676
2
        endpoint: &DownloadAuthority,
677
2
        now: Timestamp,
678
2
    ) -> Result<(), OperationError> {
679
2
        debug_assert_eq!(T::flavor(), ConsensusFlavor::Plain);
680
2
        let (meta, futile) = match data {
681
            ConsensusBoundData::Verified {
682
2
                consensus, futile, ..
683
2
            } => (consensus, futile),
684
            _ => return Err(OperationError::Bug(internal!("data not verified"))),
685
        };
686

            
687
        // Obtain the missing descriptors.
688
2
        let routers = db::read_tx(pool, |tx| {
689
2
            meta.missing_routers(tx, futile.routers.excluded(endpoint), DESCRIPTOR_LIMIT)
690
2
        })??;
691
2
        if routers.is_empty() {
692
            // This is equivalent to sending off an empty request and failing
693
            // afterwards because no progress was made, but it saves us
694
            // bandwidth by not doing a redundant HTTP request.  We may enter
695
            // this branch if there are no descriptors left that are not marked
696
            // as not found or refused, in which case we must try the next
697
            // authority as it may have the not found descriptors.
698
            debug!("not downloading any router descriptors");
699
            Err(OperationError::AuthorityRequest(
700
                AuthorityRequestError::NoRequestedDocumentsReturned,
701
            ))?;
702
2
        }
703

            
704
2
        debug!("downloading the following router descriptors: {routers:?}");
705
2
        let requ = RouterDescRequest::from_iter(routers.iter().map(|sha1| sha1.0));
706
2
        let (raw, docs) = self
707
2
            .send_request::<_, RouterDescUnverified>(endpoint, requ)
708
2
            .await?;
709

            
710
        // Verify each router descriptor.
711
        //
712
        // We do this in a closure so we do not accidentially return early,
713
        // because we must mark the not found and refused descriptors as such
714
        // prior to a return, even in an error case.
715
2
        let process_downloads = || -> DescriptorDownloads<Sha1, FlavoredDescriptorPlain> {
716
2
            let mut downloads = DescriptorDownloads::from_missing(routers.into_iter());
717
21
            for (unverified, start, end) in docs {
718
21
                let Ok(unverified) = unverified else {
719
                    // TODO DIRMIRROR: Handle parser failures as potentially futile.
720
                    //
721
                    // This is going to be a big can of worms, because in the case
722
                    // of a parser failure, we must check whether the part between
723
                    // "router" and "router-signature" matches the digest of a
724
                    // requested descriptor, while checking that the body and
725
                    // signature parts of the document alone parse just fine.
726
                    //
727
                    // body_ok && signature_ok => the parser has not failed, unreachable
728
                    // body_ok && !signature_ok => not futile
729
                    // !body_ok && signature_ok => futile
730
                    // !body_ok && !signature_ok => futile
731
                    //
732
                    // (with ok meaning that the parser suceeded, no statement
733
                    // of cryptographic validity is being made here)
734
                    //
735
                    // The `RoutereDescUnverifiedParsedBody as NetdocParseable`
736
                    // pattern may be useful for this check.
737
                    continue;
738
                };
739

            
740
                // Defensive internal checks.
741
21
                let Some(raw) = raw.get(start..end) else {
742
                    warn!("parse2 returned invalid string slice: {start}..{end}");
743
                    continue;
744
                };
745
21
                let raw = raw.to_string();
746
21
                let Some(sha1) = unverified.sigs.hashes.sha1 else {
747
                    warn!("accumulator contains no SHA-1?");
748
                    continue;
749
                };
750
21
                let sha1 = Sha1(sha1);
751

            
752
                // Checks whether the response is weird, as in containing
753
                // unrequested or duplicate descriptors.
754
21
                match downloads.get(&sha1) {
755
7
                    Some(DescriptorDownloadResult::NotFound) => {}
756
                    Some(
757
                        DescriptorDownloadResult::Found(_, _) | DescriptorDownloadResult::Refused,
758
                    ) => {
759
                        debug!("{sha1:?} contained multiple times in response");
760
                        continue;
761
                    }
762
                    None => {
763
14
                        debug!("ignoring unrequested router descriptor: {sha1:?}");
764
14
                        continue;
765
                    }
766
                };
767

            
768
                // Perform the relevant cryptographic checks.
769
7
                let sigs = unverified.sigs.clone();
770
7
                let verified = match unverified.verify() {
771
7
                    Ok(verified) => verified,
772
                    Err(e) => {
773
                        // Continuing here implicitly marks the descriptor as not
774
                        // found after the loop ends.
775
                        debug!("ignoring invalid router descriptor: {sha1:?}: {e}");
776
                        continue;
777
                    }
778
                };
779
7
                let timely = match self
780
7
                    .tolerance
781
7
                    .extend_tolerance(verified)
782
7
                    .if_valid_at(&now.into())
783
                {
784
7
                    Ok(timely) => timely,
785
                    Err(e) => {
786
                        // Untimely is considered as refused for the lifespan of the
787
                        // current consensus.
788
                        debug!("ignoring untimely router descriptor: {sha1:?}: {e}");
789
                        let prev = downloads.insert(sha1, DescriptorDownloadResult::Refused);
790
                        debug_assert!(matches!(prev, Some(DescriptorDownloadResult::NotFound)));
791
                        continue;
792
                    }
793
                };
794

            
795
7
                let prev =
796
7
                    downloads.insert(sha1, DescriptorDownloadResult::Found((timely, sigs), raw));
797
7
                debug_assert!(matches!(prev, Some(DescriptorDownloadResult::NotFound)));
798
            }
799
2
            downloads
800
2
        };
801

            
802
        // Process all downloads isolated, so that we do not return early
803
        // on accident.  While we still have one minor exception below with
804
        // regard to DatabaseError, that one is well documented.
805
2
        let downloads = process_downloads();
806

            
807
        // Determine if we made progress by checking whether we have any
808
        // descriptors that are either found or refused.  If this is the case,
809
        // we will return later, once the descriptors are marked as such.
810
2
        let progress = downloads
811
2
            .values()
812
8
            .find(|x| !matches!(x, DescriptorDownloadResult::NotFound))
813
2
            .map(|_| ()); // Do not borrow.
814

            
815
        // Insert the obtained descriptors in one large bulk transaction or
816
        // mark them as futile.
817
        //
818
        // It is okay if we fail before setting the descriptors as futile here,
819
        // because database failures are pretty much irrecoverable anyways.
820
        // Doing this together in the same loop saves us from having to
821
        // filter and split the large HashMap into multiple ones.  It should be
822
        // secure because the inner closure has the return error type of
823
        // DatabaseError, meaning only these fatal errors can return early.
824
2
        db::rw_tx(pool, |tx| {
825
14
            for (digest, res) in downloads.into_iter() {
826
14
                match res {
827
7
                    DescriptorDownloadResult::NotFound => {
828
7
                        futile.routers.set_not_found(digest, endpoint);
829
7
                    }
830
                    DescriptorDownloadResult::Refused => futile.routers.set_refused(digest),
831
7
                    DescriptorDownloadResult::Found(descriptor, raw) => {
832
7
                        DescriptorMeta::insert(
833
7
                            tx,
834
7
                            self.encodings.iter().copied(),
835
7
                            &descriptor,
836
7
                            &raw,
837
                        )?;
838
                    }
839
                }
840
            }
841
2
            Ok::<_, DatabaseError>(())
842
2
        })??;
843

            
844
        // If no progress, return with an error.  We can do so here because
845
        // we have finally marked descriptors as futile.
846
2
        if progress.is_none() {
847
1
            Err(OperationError::AuthorityRequest(
848
1
                AuthorityRequestError::NoRequestedDocumentsReturned,
849
1
            ))?;
850
1
        }
851

            
852
1
        Ok(())
853
2
    }
854

            
855
    /// Hibernates for the remaining ttl of the consensus.
856
    async fn hibernate(
857
        &self,
858
        data: &mut ConsensusBoundData<T>,
859
        now: Timestamp,
860
    ) -> Result<(), OperationError> {
861
        match data {
862
            ConsensusBoundData::None => {
863
                return Err(internal!("hibernating without a consensus?").into());
864
            }
865
            // TODO DIRMIRROR: Should the Unverified case be an error or a
866
            // success? In other words: Should we exclude the current authority
867
            // from the set of the next authorities to try?
868
            ConsensusBoundData::Verified { ttl, .. }
869
            | ConsensusBoundData::Unverified { ttl, .. } => {
870
                let timeout = *ttl - now;
871
                debug!("hibernating for {}s", timeout.as_secs());
872
                tokio::time::sleep(timeout).await;
873
            }
874
        }
875

            
876
        Ok(())
877
    }
878

            
879
    /// Convenience wrapper around [`tor_dirclient::send_request()`].
880
    ///
881
    /// It opens a TCP connection, performs the request, and parses the result.
882
    ///
883
    /// Returns the raw response alongside the output of
884
    /// [`parse2::parse_netdoc_multiple_sophisticated()`].
885
    ///
886
    /// An important distinction towards [`tor_dirclient::send_request()`] is,
887
    /// that we do not consider an empty response or a 404 response as an error.
888
    /// Instead, we just return [`Ok`] with an empty [`String`] and an empty
889
    /// [`Vec`] instead.  The reason for this is, that the futile document
890
    /// strategy (i.e. determing which documents of the request are missing)
891
    /// should be the same for responses containing zero (requested) documents
892
    /// and responses containing at least one but less than the number of
893
    /// the originally requested documents.
894
    ///
895
    /// The output is required because we need the raw document alongside the
896
    /// offsets to have the actual data we will insert into the database later
897
    /// on.
898
4
    async fn send_request<R: Requestable, D: NetdocParseable>(
899
4
        &self,
900
4
        endpoint: &DownloadAuthority,
901
4
        requ: R,
902
4
    ) -> Result<(String, Vec<(Result<D, ParseError>, usize, usize)>), AuthorityRequestError> {
903
        // The check is required to not let Tokio panic.
904
4
        if endpoint.is_empty() {
905
            return Err(AuthorityRequestError::Bug(internal!("empty endpoint?")));
906
4
        }
907

            
908
        // Open the TCP connection.
909
4
        let mut stream = TcpStream::connect(endpoint)
910
4
            .await
911
4
            .map_err(AuthorityRequestError::TcpConnect)?
912
4
            .compat();
913

            
914
        // Perform the request and map the result nicely.
915
4
        let resp = tor_dirclient::send_request(&self.rt, &requ, &mut stream, None)
916
4
            .await
917
4
            .map(|resp| {
918
4
                resp.output_string()
919
4
                    .map(|resp| resp.to_owned())
920
                    // Allows us to flatten the result.
921
4
                    .map_err(tor_dirclient::Error::from)
922
4
            });
923

            
924
        // We can immediately drop the connection now, no need to occupy even
925
        // more resources from the authority.  Doing so is fine, it is HTTP/1.0
926
        // and there is no connection reuse anyways.
927
4
        drop(stream);
928

            
929
        // Non-trivial error handling strategy explained in the rustdoc comment
930
        // above.
931
4
        let resp = match resp.flatten() {
932
4
            Ok(resp) => Ok(resp),
933

            
934
            // This error might actually be Ok in certain cases.
935
            Err(DirClientError::RequestFailed(
936
                ref outer @ RequestFailedError {
937
                    error: ref inner, ..
938
                },
939
            )) => match inner {
940
                // If one of these errors occurs, do not fail but rather return
941
                // no documents.
942
                RequestError::EmptyRequest => {
943
                    warn!("we should not send an empty request");
944
                    return Ok(Default::default());
945
                }
946
                RequestError::EmptyResponse | RequestError::HttpStatus(404, _) => {
947
                    return Ok(Default::default());
948
                }
949

            
950
                // All other errors imply an underlying error with the
951
                // respective endpoint, suggesting a switch.
952
                _ => Err(outer.clone().into()),
953
            },
954

            
955
            // Should not happen because the other DirClientErrors are either
956
            // Bug or related to CircMgr, which we do not use.
957
            Err(e) => Err(AuthorityRequestError::Bug(internal!(
958
                "unhandled dirclient error: {e}"
959
            ))),
960
        }?;
961

            
962
        // Parse the response.
963
4
        let parsed = parse2::parse_netdoc_multiple_sophisticated(&ParseInput::new(&resp, ""))?;
964

            
965
4
        Ok((resp, parsed))
966
4
    }
967

            
968
    /// Queries and validates the most recent consensus from the database.
969
    ///
970
    /// Effectively a wrapper around [`StaticEngine::verify_consensus()`] with
971
    /// the data obtained from the database.
972
5
    fn recent_consensus(
973
5
        &self,
974
5
        tx: &Transaction<'_>,
975
5
        now: Timestamp,
976
5
    ) -> Result<Option<ConsensusMeta<T>>, DatabaseError> {
977
5
        let meta = match ConsensusMeta::<T>::query(tx)?.as_slice() {
978
5
            &[front] | &[front, ..] => front,
979
            &[] => return Ok(None),
980
        };
981
5
        let raw = meta.data(tx)?;
982

            
983
5
        let unverified = match parse2::parse_netdoc::<T>(&ParseInput::new(&raw, "<database>")) {
984
5
            Ok(unverified) => unverified,
985
            Err(e) => {
986
                // TODO DIRMIRROR: Perhaps we should remove it?
987
                debug!("unparseable consensus in database: {meta:?} {e}");
988
                return Ok(None);
989
            }
990
        };
991

            
992
5
        let certs_already = self.certs_already(tx, now)?;
993

            
994
5
        match self.verify_consensus(unverified, &certs_already, now) {
995
4
            Ok(_) => Ok(Some(meta)),
996
1
            Err(e) => {
997
                // TODO DIRMIRROR: Perhaps we should remove it?
998
1
                debug!("ignoring invalid consensus in database: {meta:?} {e}");
999
1
                Ok(None)
            }
        }
5
    }
    /// Parses and verifies a raw consensus in a database agnostic fashion.
6
    fn verify_consensus(
6
        &self,
6
        unverified: T,
6
        certs_already: &[AuthCert],
6
        now: Timestamp,
6
    ) -> Result<(T::Body, T::Signatures), VerifyFailed> {
6
        let sigs = unverified.sigs().clone();
6
        let verified = unverified.verify(self.authorities.v3idents(), certs_already)?;
5
        self.tolerance
5
            .extend_tolerance(verified)
5
            .if_valid_at(&now.into())
5
            .map(|body| (body, sigs))
5
            .map_err(|e| e.into())
6
    }
    /// Loads all authority certificates from the database into a [`Vec<AuthCert>`].
    ///
    /// Performance wise, it is quite inefficient as it parses and verifies all
    /// certificates over again.  However, this should be fine as the N for
    /// directory authorities is usually very small.
14
    fn certs_already(
14
        &self,
14
        tx: &Transaction<'_>,
14
        now: Timestamp,
14
    ) -> Result<Vec<AuthCert>, DatabaseError> {
14
        let raw_certs = AuthCertMeta::query(tx)?
14
            .into_iter()
48
            .map(|meta| Ok::<_, DatabaseError>((meta, meta.data(tx)?)))
14
            .collect::<Result<Vec<_>, _>>()?;
14
        let mut certs_already = Vec::new();
48
        for (meta, raw) in raw_certs {
            // Only used for debugging.
            // TODO DIRMIRROR: Put this into span with tracing.
48
            let key_ids = AuthCertKeyIds {
48
                id_fingerprint: meta.kp_auth_id_rsa_sha1.0.into(),
48
                sk_fingerprint: meta.kp_auth_sign_rsa_sha1.0.into(),
48
            };
48
            let unverified = match parse2::parse_netdoc::<AuthCertUnverified>(&ParseInput::new(
48
                &raw,
48
                "<database>",
48
            )) {
48
                Ok(c) => c,
                Err(e) => {
                    // TODO DIRMIRROR: Perhaps we should remove it?
                    debug!("unparseable auth cert ({key_ids:?}) in database: {e}");
                    continue;
                }
            };
48
            let verified = match unverified.verify(self.authorities.v3idents()) {
44
                Ok(c) => c,
4
                Err(e) => {
4
                    debug!("ignoring invalid auth cert ({key_ids:?}): {e}");
4
                    continue;
                }
            };
44
            let timely = match self
44
                .tolerance
44
                .extend_tolerance(verified)
44
                .if_valid_at(&now.into())
            {
36
                Ok(c) => c,
8
                Err(e) => {
8
                    debug!("ignoring non-timely auth cert ({key_ids:?}): {e}");
8
                    continue;
                }
            };
36
            certs_already.push(timely);
        }
14
        Ok(certs_already)
14
    }
}
/// Records which allow us to avoid futile download attempts
///
/// One set of records of "futile" documents, for each kind of document.
#[derive(Debug, Clone, Default)]
struct FutileDescriptors {
    /// The futile router descriptors.
    routers: FutileDescriptorSet<Sha1>,
    /// The futile extra-infos.
    extras: FutileDescriptorSet<Sha1>,
    /// The futile micro descriptors.
    micros: FutileDescriptorSet<Sha256>,
}
/// A generic struct storing which document are futile for which reason.
///
/// A document is "futile" if there is no point retrying its download.
/// More precisely,
///
///   * We note which authorities didn't have a document when we tried to download it.
///     This avoids us trying that same document again, uselessly.
// TODO DIRMIRROR but what if the authority acquires the document in the meantime?
//
///   * We note the hashes of documents we couldn't parse.
///     If the hash matched, but the parse failed, there is no point trying to get
///     this document at all because the same hash means the same document
///     and therefore the same parse error.
// TODO: Right now, we allow a value to be present in both: not_found and refused.
// Do we want to allow this or should we potentially change the representation
// similar to the way we are doing it in DescriptorDownloadResult?  Not blocking,
// as a hazard here is not the end of the world, but solving it may be more
// elegant.
#[derive(Educe)]
#[educe(Debug, Clone, Default)]
struct FutileDescriptorSet<T: Debug + Clone + Copy + Eq + Hash> {
    /// The descriptors that are not found on a given endpoint.
    not_found: HashSet<(T, Box<DownloadAuthority>)>,
    /// The descriptor that are outright refused.
    refused: HashSet<T>,
}
impl<T: Debug + Clone + Copy + Eq + Hash> FutileDescriptorSet<T> {
    /// Returns an iterator over the digest that are not found.
2
    fn not_found(&self, endpoint: &DownloadAuthority) -> impl Iterator<Item = T> {
2
        self.not_found
2
            .iter()
2
            .filter(|(_, this_endpoint)| **this_endpoint == *endpoint)
2
            .map(|(d, _)| *d)
2
    }
    /// Returns an iterator over the digest that are refused.
5
    fn refused(&self) -> impl Iterator<Item = T> {
5
        self.refused.iter().copied()
5
    }
    /// Chaining of `.not_found()` and `.refused()`.
2
    fn excluded(&self, endpoint: &DownloadAuthority) -> impl Iterator<Item = T> {
2
        self.not_found(endpoint).chain(self.refused())
2
    }
    /// Marks a digest in combination with an authority as not found.
7
    fn set_not_found(&mut self, digest: T, endpoint: &DownloadAuthority) {
7
        self.not_found.insert((digest, endpoint.into()));
7
    }
    /// Marks a digest as refused.
    fn set_refused(&mut self, digest: T) {
        self.refused.insert(digest);
    }
}
/// Intermediate data structure for storing the result of a downloaded
/// descriptor within a batch of downloads.
///
/// Populated before the insertion into [`FutileDescriptors`] takes place,
/// so that the actual population of [`FutileDescriptors`] remains atomic.
#[derive(Debug, Clone)]
enum DescriptorDownloadResult<D: FlavoredDescriptor> {
    /// The descriptor was found and verified.
    Found(D, String),
    /// The descriptor was not found; implicit default.
    NotFound,
    /// We do not accept this descriptor.
    Refused,
}
/// Collection of [`DescriptorDownloadResult`] values indexed by their digest.
///
/// Used during downloading a batch in order to atomically update
/// [`FutileDescriptors`].
#[derive(Debug, Clone, derive_more::Deref, derive_more::DerefMut, derive_more::IntoIterator)]
struct DescriptorDownloads<K, D: FlavoredDescriptor>(HashMap<K, DescriptorDownloadResult<D>>);
impl<K: Eq + Hash, D: FlavoredDescriptor> DescriptorDownloads<K, D> {
    /// Creates a [`DescriptorDownloads`] from a set of all missing descriptors.
    ///
    /// The idea is to call this right after the database call and before
    /// any downloads are actually being made, as it initializes the data
    /// structure with all the keys to [`DescriptorDownloadResult::NotFound`].
2
    fn from_missing(digests: impl Iterator<Item = K>) -> Self {
        Self(
2
            digests
14
                .map(|digest| (digest, DescriptorDownloadResult::NotFound))
2
                .collect(),
        )
2
    }
}
#[cfg(test)]
mod test {
    // @@ begin test lint list maintained by maint/add_warning @@
    #![allow(clippy::bool_assert_comparison)]
    #![allow(clippy::clone_on_copy)]
    #![allow(clippy::dbg_macro)]
    #![allow(clippy::mixed_attributes_style)]
    #![allow(clippy::print_stderr)]
    #![allow(clippy::print_stdout)]
    #![allow(clippy::single_char_pattern)]
    #![allow(clippy::unwrap_used)]
    #![allow(clippy::unchecked_time_subtraction)]
    #![allow(clippy::useless_vec)]
    #![allow(clippy::needless_pass_by_value)]
    #![allow(clippy::string_slice)] // See arti#2571
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
    use std::{collections::HashSet, iter};
    use rusqlite::params;
    use strum::IntoEnumIterator;
    use tokio::{
        io::{AsyncReadExt, AsyncWriteExt},
        net::TcpListener,
    };
    use tor_basic_utils::test_rng::testing_rng;
    use crate::{database::sql, err::IsFatal, testdata2};
    use super::*;
    type Plain = tor_netdoc::doc::netstatus::plain::NetworkStatusUnverified;
    type Md = tor_netdoc::doc::netstatus::md::NetworkStatusUnverified;
    /// Returns a [`StaticEngine`] for testing.
    fn static_engine<T>() -> StaticEngine<T> {
        StaticEngine {
            authorities: testdata2::current_auth_cert_contacts(),
            tolerance: DirTolerance::default(),
            rt: PreferredRuntime::current().unwrap(),
            encodings: ContentEncoding::iter().collect(),
            _phantom: Default::default(),
        }
    }
    /// Tests whether the load consensus state computes missing descriptors
    /// properly.
    ///
    /// For this, the test removes a present router descriptor from the storage
    /// to verify that it is detected as missing and added to the download
    /// queue.
    #[tokio::test]
    async fn state_load_consensus() {
        let pool = testdata2::test_db();
        let mut data = ConsensusBoundData::<Plain>::None;
        let engine = static_engine();
        let time: Timestamp = testdata2::valid_system_time().into();
        let fresh_until: Timestamp = testdata2::current_consensus_ns()
            .0
            .preamble
            .lifetime
            .fresh_until
            .0
            .into();
        let valid_until: Timestamp = testdata2::current_consensus_ns()
            .0
            .preamble
            .lifetime
            .valid_until
            .0
            .into();
        // This is the middle of valid_until and fresh_until.
        let fresh_until_half = fresh_until + ((valid_until - fresh_until) / 2);
        // Remove a single router descriptor from our storage to see whether it
        // appears in the download queue as expected.
        let relay_to_remove = &testdata2::current_consensus_ns().0.routers[0];
        let relay_to_remove = db::Sha1::from(*relay_to_remove.doc_digest());
        pool.get()
            .unwrap()
            .execute(
                sql!("DELETE FROM descriptor WHERE sha1 = ?1"),
                params![relay_to_remove],
            )
            .unwrap();
        engine
            .load_consensus(&pool, &mut data, time, &mut testing_rng())
            .unwrap();
        // El-cheapo assert_eq due to lack of PartialEq for tor-netdoc poc.
        match data {
            ConsensusBoundData::Verified { consensus, ttl, .. } => {
                // If everything worked properly, then the queue should only
                // contain the relay we removed, because that is missing now.
                db::read_tx(&pool, |tx| {
                    assert_eq!(
                        consensus.missing_routers(tx, iter::empty(), None).unwrap(),
                        HashSet::from([relay_to_remove])
                    );
                    assert!(
                        consensus
                            .missing_extras(tx, iter::empty(), None)
                            .unwrap()
                            .is_empty()
                    );
                    assert!(
                        consensus
                            .missing_micros(tx, iter::empty(), None)
                            .unwrap()
                            .is_empty()
                    );
                })
                .unwrap();
                assert!(ttl >= fresh_until);
                assert!(ttl <= fresh_until_half);
            }
            _ => panic!("data is not verified"),
        }
    }
    /// Tests whether the fetch consensus state properly fetches a consensus
    /// and keeps it in memory as unverified.
    ///
    /// For this, we spawn a tokio task simulating a web server which responds
    /// with a consensus.
    #[tokio::test]
    async fn state_fetch_consensus() {
        let pool = testdata2::test_db();
        let mut data = ConsensusBoundData::<Plain>::None;
        let engine = static_engine();
        let now = Timestamp::from(testdata2::invalid_system_time());
        let state = db::read_tx(&pool, |tx| engine.determine_state(tx, &data, now))
            .unwrap()
            .unwrap();
        assert_eq!(state, State::FetchConsensus);
        let server = TcpListener::bind("[::1]:0").await.unwrap();
        let saddr = server.local_addr().unwrap();
        tokio::spawn(async move {
            let (mut stream, _) = server.accept().await.unwrap();
            let mut buf = vec![0; 1024];
            let _ = stream.read(&mut buf).await.unwrap();
            let consensus = testdata2::current_consensus_ns().2;
            let resp = format!(
                "HTTP/1.0 200 OK\r\nContent-Encoding: identity\r\nContent-Length: {}\r\n\r\n{consensus}",
                consensus.len()
            );
            stream.write_all(resp.as_bytes()).await.unwrap();
        });
        engine
            .fetch_consensus(&mut data, &[saddr], now)
            .await
            .unwrap();
        match data {
            ConsensusBoundData::Unverified { raw, ttl, .. } => {
                assert_eq!(raw, testdata2::current_consensus_ns().2);
                assert_eq!(ttl, now + UNVERIFIED_TTL);
            }
            _ => panic!("data is not unverified"),
        }
    }
    /// Tests the download, verification, and insertion of authority certificates.
    ///
    /// For this, it starts by removing an existing one from the test database
    /// to see it getting re-downloaded, re-verified, and re-inserted again.
    #[tokio::test]
    async fn state_auth_certs() {
        let pool = testdata2::test_db();
        let mut data = ConsensusBoundData::<Plain>::Unverified {
            consensus: parse2::parse_netdoc(&ParseInput::new(
                testdata2::current_consensus_ns().2,
                "",
            ))
            .unwrap(),
            raw: testdata2::current_consensus_ns().2.to_owned(),
            ttl: Timestamp::from(testdata2::valid_system_time()) + UNVERIFIED_TTL,
        };
        let mut engine = static_engine();
        // We want to download authority certificates; for this, remove
        // one of them from the database.
        pool.get()
            .unwrap()
            .execute(
                sql!(
                    "
                    DELETE FROM authority_key_certificate
                    WHERE :kp_auth_id_rsa_sha1 = ?1
                    "
                ),
                params![db::Sha1::from(
                    testdata2::current_auth_cert_ids()[0].to_bytes()
                )],
            )
            .unwrap();
        assert_eq!(
            db::read_tx(&pool, |tx| engine.determine_state(
                tx,
                &data,
                testdata2::valid_system_time().into()
            ))
            .unwrap()
            .unwrap(),
            State::AuthCerts
        );
        let server = TcpListener::bind("[::1]:0").await.unwrap();
        let saddr = server.local_addr().unwrap();
        tokio::spawn(async move {
            let mut buf = [0; 1024];
            let (mut stream, _) = server.accept().await.unwrap();
            let _ = stream.read(&mut buf).await.unwrap();
            let authcerts = testdata2::current_auth_certs()
                .into_iter()
                .map(|x| x.1)
                .collect::<String>();
            stream.write_all(format!(
                "HTTP/1.0 200 OK\r\nContent-Encoding: identity\r\nContent-Length: {}\r\n\r\n{authcerts}",
                authcerts.len()
            ).as_bytes()).await.unwrap();
        });
        // Fetch all authcerts.
        engine
            .auth_certs(
                &pool,
                &mut data,
                &[saddr],
                testdata2::valid_system_time().into(),
            )
            .await
            .unwrap();
        // Check whether we are done with all authcerts.
        assert_eq!(
            db::read_tx(&pool, |tx| engine.determine_state(
                tx,
                &data,
                testdata2::valid_system_time().into(),
            ))
            .unwrap()
            .unwrap(),
            State::StoreConsensus
        );
        // Check whether we now have all auth certs in the database.
        let certs = db::read_tx(&pool, |tx| {
            engine.certs_already(tx, testdata2::valid_system_time().into())
        })
        .unwrap()
        .unwrap();
        let got = certs
            .into_iter()
            .map(|c| c.dir_identity_key.to_rsa_identity())
            .collect::<HashSet<_>>();
        let expect = engine.authorities.v3idents().iter().copied().collect();
        assert_eq!(got, expect);
        // Check an edge case of not having enough trusted signers.
        engine.authorities = AuthorityContacts::builder().build().unwrap();
        let state = db::read_tx(&pool, |tx| {
            engine.determine_state(tx, &data, testdata2::valid_system_time().into())
        })
        .unwrap()
        .unwrap();
        assert_eq!(state, State::Hibernate);
    }
    /// Checks whether we can probably load and parse all certificates from
    /// the database.
    #[tokio::test]
    async fn certs_already() {
        let pool = testdata2::test_db();
        let engine = static_engine::<Plain>();
        db::read_tx(&pool, |tx| {
            // With a correct system time, this should align with all authority
            // fingerprints we have.
            let certs_already = engine
                .certs_already(tx, testdata2::valid_system_time().into())
                .unwrap();
            assert_eq!(certs_already.len(), engine.authorities.v3idents().len());
            assert!(certs_already.iter().all(|c| {
                engine
                    .authorities
                    .v3idents()
                    .contains(&c.dir_identity_key.to_rsa_identity())
            }));
            // With an invalid system time, it should be empty.
            let certs_already = engine
                .certs_already(tx, testdata2::invalid_system_time().into())
                .unwrap();
            assert!(certs_already.is_empty());
        })
        .unwrap();
    }
    /// Ensures that an unverified consensus gets properly inserted into the
    /// database.
    #[tokio::test]
    async fn state_store_consensus() {
        let pool = testdata2::test_db();
        let unverified: Plain =
            parse2::parse_netdoc(&ParseInput::new(testdata2::current_consensus_ns().2, ""))
                .unwrap();
        let engine = static_engine();
        let now = Timestamp::from(testdata2::valid_system_time());
        let mut data = ConsensusBoundData::<Plain>::Unverified {
            consensus: unverified.clone(),
            raw: testdata2::current_consensus_ns().2.to_string(),
            ttl: now + UNVERIFIED_TTL,
        };
        // Clean all consensus related tables.
        pool.get()
            .unwrap()
            .execute_batch(sql!(
                "
                DELETE FROM consensus_descriptor_member;
                DELETE FROM consensus_authority_voter;
                DELETE FROM consensus;
                "
            ))
            .unwrap();
        // Execute the StoreConsensus state.
        let state = db::read_tx(&pool, |tx| engine.determine_state(tx, &data, now))
            .unwrap()
            .unwrap();
        assert_eq!(state, State::StoreConsensus);
        engine.store_consensus(&pool, &mut data, now).unwrap();
        assert!(matches!(&data, ConsensusBoundData::None));
        // Verify that it got inserted.
        let state = db::read_tx(&pool, |tx| engine.determine_state(tx, &data, now))
            .unwrap()
            .unwrap();
        assert_eq!(state, State::LoadConsensus);
        engine
            .load_consensus(&pool, &mut data, now, &mut testing_rng())
            .unwrap();
        assert!(matches!(data, ConsensusBoundData::Verified { .. }));
    }
    /// Tests the descriptor bootstrapping for router descriptors.
    #[tokio::test]
    async fn state_descriptors_plain() {
        let pool = testdata2::test_db();
        let engine = static_engine::<Plain>();
        let now = Timestamp::from(testdata2::valid_system_time());
        let mut data = ConsensusBoundData::None;
        let rng = &mut testing_rng();
        // Server task that can at most handle two requests.
        //
        // The first response contains all available router descriptors
        // statically, whereas the second one only contains a single router
        // descriptor.
        let listener = TcpListener::bind("[::1]:0").await.unwrap();
        let endpoint = listener.local_addr().unwrap();
        tokio::spawn(async move {
            let mut buf = [0; 1024];
            let full = testdata2::current_router_descs()
                .into_iter()
                .map(|(_, _, raw)| raw)
                .collect::<String>();
            let single = testdata2::current_router_descs()
                .into_iter()
                .take(1)
                .map(|(_, _, raw)| raw)
                .collect::<String>();
            let (mut stream, _) = listener.accept().await.unwrap();
            let _ = stream.read(&mut buf).await.unwrap();
            stream
                .write_all(
                    format!(
                        "HTTP/1.0 200 OK\r\nContent-Length: {}\r\n\r\n{}",
                        full.len(),
                        full
                    )
                    .as_bytes(),
                )
                .await
                .unwrap();
            drop(stream);
            let (mut stream, _) = listener.accept().await.unwrap();
            let _ = stream.read(&mut buf).await.unwrap();
            stream
                .write_all(
                    format!(
                        "HTTP/1.0 200 OK\r\nContent-Length: {}\r\n\r\n{}",
                        single.len(),
                        single
                    )
                    .as_bytes(),
                )
                .await
                .unwrap();
        });
        // Delete all router descriptors from the database and then load
        // a consensus into ConsensusBoundData.
        db::rw_tx(&pool, |tx| tx.execute(sql!("DELETE FROM descriptor"), ()))
            .unwrap()
            .unwrap();
        engine.load_consensus(&pool, &mut data, now, rng).unwrap();
        let state = db::read_tx(&pool, |tx| engine.determine_state(tx, &data, now))
            .unwrap()
            .unwrap();
        assert_eq!(state, State::Descriptors);
        // Successful bootstrap of all descriptors.
        engine
            .descriptors_rd(&pool, &mut data, &[endpoint], now)
            .await
            .unwrap();
        // Afterwards, the queue for missing router descriptors should be empty
        // as well as futile.
        let (meta, futile) = match &data {
            ConsensusBoundData::Verified {
                consensus: meta,
                futile,
                ..
            } => (*meta, futile.clone()),
            _ => panic!(),
        };
        let missing_routers =
            db::read_tx(&pool, |tx| meta.missing_routers(tx, iter::empty(), None))
                .unwrap()
                .unwrap();
        assert!(missing_routers.is_empty());
        assert!(futile.routers.not_found.is_empty());
        assert!(futile.routers.refused.is_empty());
        // Now, if we do this again, futile should be populated.
        db::rw_tx(&pool, |tx| tx.execute(sql!("DELETE FROM descriptor"), ()))
            .unwrap()
            .unwrap();
        // This will yield a non-fatal error because it makes no progress, but
        // that is okay; we primarily want to test whether the futile
        // descriptor handling works.
        let err = engine
            .descriptors_rd(&pool, &mut data, &[endpoint], now)
            .await
            .unwrap_err();
        assert!(!err.is_fatal());
        // Now, the missing descriptors should equal the ones that were not
        // found on the authority, as in being in the futile set.
        let missing_routers =
            db::read_tx(&pool, |tx| meta.missing_routers(tx, iter::empty(), None))
                .unwrap()
                .unwrap();
        let futile = match &data {
            ConsensusBoundData::Verified { futile, .. } => futile,
            _ => panic!(),
        };
        assert!(!missing_routers.is_empty());
        assert_eq!(
            missing_routers,
            futile
                .routers
                .not_found
                .iter()
                .filter_map(|(digest, this_endpoint)| if **this_endpoint == [endpoint] {
                    Some(*digest)
                } else {
                    None
                })
                .collect::<HashSet<_>>()
        );
        assert!(futile.routers.refused.is_empty());
    }
}