1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
//! I/O reactor that drives the protocol state machine.
//!
//! The reactor translates network events into protocol events. This has the
//! added benefit that it's trivial to swap nakamoto's networking code with a
//! different implementation, as the code is fully self-contained.
//!
//! To illustrate the above, lets trace the behavior of the system when a `ping`
//! message is received via a peer connection to the client:
//!
//! 1. The `Reactor` reads from the socket and decodes a `NetworkMessage::Ping`
//!    message.
//! 2. The `Reactor` wraps this message into a protocol input `Input::Received(addr,
//!    NetworkMessage::Ping)`, where `addr` is the remote address of the socket on
//!    which it received this message.
//! 3. The `Reactor` calls `Protocol::step(input, time)`, where `input` is the above
//!    input, and `time` is the current local time.
//! 4. The `Protocol` forwards this message to the `PingManager`, which constructs
//!    a new output `Out::Message(addr, NetworkMessage::Pong)`, and forwards it
//!    upstream, to the reactor.
//! 5. The `Reactor` processes the output, encodes the raw message and writes it to
//!    the socket corresponding to the `addr` address, effectively sending a `pong`
//!    message back to the original sender.
//!
//! Though simplified, the above steps provide a good mental model of how the
//! reactor and protocol interplay to handle network events.
//!
#![allow(clippy::new_without_default)]
#![allow(clippy::inconsistent_struct_constructor)]

#[cfg(unix)]
pub mod reactor;
pub mod socket;
pub mod time;

pub use reactor::{Reactor, Waker};

#[cfg(test)]
mod fallible;

#[cfg(test)]
#[macro_use]
extern crate lazy_static;

/// Makes a function randomly fail with the given error.
#[macro_export]
macro_rules! fallible {
    ($err:expr) => {
        #[cfg(test)]
        {
            let fallible = fallible::FALLIBLE.lock().unwrap();

            if let Some(p) = *fallible {
                let r = fastrand::f64();

                if r <= p {
                    return Err($err.into());
                }
            }
        }
    };
}