From 0c726e6b43679434ade7e7e750fe6732da4b7141 Mon Sep 17 00:00:00 2001 From: Fraser Hutchison Date: Tue, 19 May 2020 23:22:00 +0100 Subject: [PATCH] EE-1020: bring code in line with current CL practices --- Cargo.lock | 12 - Cargo.toml | 6 +- README.md | 18 +- images/CasperLabs_Logo_Favicon_RGB_50px.png | Bin 0 -> 6226 bytes images/CasperLabs_Logo_Symbol_RGB.png | Bin 0 -> 149415 bytes src/cli.rs | 32 +- src/components/small_network.rs | 231 ++++++------- src/config.rs | 60 ++-- src/effect.rs | 69 ++-- src/main.rs | 17 +- src/reactor.rs | 70 ++-- src/reactor/queue_kind.rs | 15 +- src/reactor/validator.rs | 39 ++- src/tls.rs | 340 ++++++++++---------- src/{util.rs => utils.rs} | 35 +- src/{util => utils}/round_robin.rs | 44 +-- 16 files changed, 521 insertions(+), 467 deletions(-) create mode 100644 images/CasperLabs_Logo_Favicon_RGB_50px.png create mode 100644 images/CasperLabs_Logo_Symbol_RGB.png rename src/{util.rs => utils.rs} (60%) rename src/{util => utils}/round_robin.rs (81%) diff --git a/Cargo.lock b/Cargo.lock index 80310d8205..3eac623a5b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,17 +24,6 @@ version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f" -[[package]] -name = "async-trait" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c4f3195085c36ea8d24d32b2f828d23296a9370a28aa39d111f6f16bef9f3b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "atty" version = "0.2.14" @@ -75,7 +64,6 @@ name = "casper-node" version = "0.1.0" dependencies = [ "anyhow", - "async-trait", "displaydoc", "either", "enum-iterator", diff --git a/Cargo.toml b/Cargo.toml index 1ea7a03a0e..18c428eb72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,16 +1,14 @@ [package] name = "casper-node" version = "0.1.0" -authors = ["Marc Brinkmann ", - "Fraser Hutchison "] +authors = ["Marc Brinkmann ", "Fraser Hutchison "] edition = "2018" -description = "The CasperLabs blockchain node server" +description = "The CasperLabs blockchain node" publish = false # Prevent accidental `cargo publish` for now. license-file = "LICENSE" [dependencies] anyhow = "1.0.28" -async-trait = "0.1.31" displaydoc = "0.1.6" either = "1.5.3" enum-iterator = "0.6.0" diff --git a/README.md b/README.md index 733de2ba2a..0ead1ebf80 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,21 @@ -# CasperLabs node +# casper-node -The is the core application for the CasperLabs blockchain. - -## Building - -To compile this application, simply run `cargo build` on a recent stable Rust (`>= 1.43.1`) version. +This is the core application for the CasperLabs blockchain. ## Running a validator node -Launching a validator node with the default configuration is done by simply launching the application: +To run a validator node with the default configuration: ``` -casper-node validator +cargo run --release -- validator ``` It is very likely that the configuration requires editing though, so typically one will want to generate a configuration file first, edit it and then launch: ``` -casper-node generate-config > mynode.toml +cargo run --release -- generate-config > mynode.toml # ... edit mynode.toml -casper-node validator -c mynode.toml +cargo run --release -- validator --config=mynode.toml ``` ## Development @@ -30,4 +26,4 @@ A good starting point is to build the documentation and read it in your browser: cargo doc --no-deps --open ``` -When generating a configuration file, it is usually helpful to set the log-level to `DEBUG` during development. \ No newline at end of file +When generating a configuration file, it is usually helpful to set the log-level to `DEBUG` during development. diff --git a/images/CasperLabs_Logo_Favicon_RGB_50px.png b/images/CasperLabs_Logo_Favicon_RGB_50px.png new file mode 100644 index 0000000000000000000000000000000000000000..593254f7bd12748e133d7cd1e5afddccd5f618a6 GIT binary patch literal 6226 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�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⁢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 F699Or2ao^& literal 0 HcmV?d00001 diff --git a/src/cli.rs b/src/cli.rs index b59ae5a348..855fcdd9c4 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,20 +1,28 @@ //! Command-line option parsing. //! -//! Most configuration is done through the configuration, which is the only required command-line -//! argument. However some configuration values can be overwritten for convenience's sake. -use std::{io, io::Write, path}; +//! Most configuration is done via config files (see [`config`](../config/index.html) for details). + +use std::{io, io::Write, path::PathBuf}; + +use anyhow::bail; use structopt::StructOpt; +use tracing::Level; -use crate::{config, reactor, tls}; +use crate::{ + config, + reactor::{self, validator::Reactor}, + tls, +}; // Note: The docstring on `Cli` is the help shown when calling the binary with `--help`. #[derive(Debug, StructOpt)] /// CasperLabs blockchain node. pub enum Cli { - /// Generate a self-signed node certificate + /// Generate a self-signed node certificate. GenerateCert { - /// Output path base the certificate, private key pair in PEM format. The cert will be stored as `output.crt.pem`, while the key will be stored as `output.key.pem`. - output: path::PathBuf, + /// Output path base of the certificate. The certificate will be stored as + /// `output.crt.pem`, while the key will be stored as `output.key.pem`. + output: PathBuf, }, /// Generate a configuration file from defaults and dump it to stdout. GenerateConfig {}, @@ -26,7 +34,7 @@ pub enum Cli { Validator { #[structopt(short, long, env)] /// Path to configuration file. - config: Option, + config: Option, /// Override log-level, forcing debug output. #[structopt(short, long)] @@ -35,12 +43,12 @@ pub enum Cli { } impl Cli { - /// Execute selected CLI command. + /// Executes selected CLI command. pub async fn run(self) -> anyhow::Result<()> { match self { Cli::GenerateCert { output } => { if output.file_name().is_none() { - anyhow::bail!("not a valid output path"); + bail!("not a valid output path"); } let mut cert_path = output.clone(); @@ -69,11 +77,11 @@ impl Cli { .transpose()? .unwrap_or_default(); if debug { - cfg.log.level = tracing::Level::DEBUG; + cfg.log.level = Level::DEBUG; } cfg.log.setup_logging()?; - reactor::launch::(cfg).await + reactor::launch::(cfg).await } } } diff --git a/src/components/small_network.rs b/src/components/small_network.rs index 89ba540376..e967cdc2d1 100644 --- a/src/components/small_network.rs +++ b/src/components/small_network.rs @@ -38,31 +38,53 @@ //! all nodes in the list and simultaneously tell all of its connected nodes about the new node, //! repeating the process. -use crate::effect::{Effect, EffectExt, EffectResultExt}; -use crate::tls::{self, Signed, TlsCert}; -use crate::util::{DisplayIter, Multiple}; -use crate::{config, reactor}; -use anyhow::Context; -use futures::{FutureExt, SinkExt, StreamExt}; +use std::{ + cmp::Ordering, + collections::{HashMap, HashSet}, + fmt::{self, Debug, Display, Formatter}, + hash::Hash, + io, + net::{SocketAddr, TcpListener}, + sync::Arc, + time::{SystemTime, UNIX_EPOCH}, +}; + +use anyhow::{anyhow, bail, Context}; +use futures::{ + stream::{SplitSink, SplitStream}, + FutureExt, SinkExt, StreamExt, +}; use maplit::hashmap; use openssl::{pkey, x509}; -use serde::de::DeserializeOwned; -use serde::{Deserialize, Serialize}; -use std::hash::Hash; -use std::sync::Arc; -use std::{cmp, collections, fmt, io, net, time}; -use tokio::sync::mpsc; +use pkey::{PKey, Private}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use tokio::{ + net::TcpStream, + sync::mpsc::{self, UnboundedReceiver, UnboundedSender}, +}; +use tokio_openssl::SslStream; +use tokio_serde::{formats::SymmetricalMessagePack, SymmetricallyFramed}; +use tokio_util::codec::{Framed, LengthDelimitedCodec}; use tracing::{debug, error, info, warn}; +use x509::X509; -/// A node id. +use crate::{ + config, + effect::{Effect, EffectExt, EffectResultExt}, + reactor::{EventQueueHandle, Queue, Reactor}, + tls::{self, KeyFingerprint, Signed, TlsCert}, + utils::{DisplayIter, Multiple}, +}; + +/// A node ID. /// /// The key fingerprint found on TLS certificates. -pub type NodeId = tls::KeyFingerprint; +pub type NodeId = KeyFingerprint; #[derive(Clone, Debug, Deserialize, Serialize)] pub enum Message

{ /// A pruned set of all endpoint announcements the server has received. - Snapshot(collections::HashSet>), + Snapshot(HashSet>), /// Broadcast a new endpoint known to the sender. BroadcastEndpoint(Signed), /// A payload message. @@ -76,7 +98,7 @@ pub struct Endpoint { /// Will overflow earliest November 2262. timestamp_ns: u64, /// Socket address the node is listening on. - addr: net::SocketAddr, + addr: SocketAddr, /// Certificate. cert: TlsCert, } @@ -88,21 +110,18 @@ pub enum Event

{ /// Connection to the root node failed. RootFailed { error: anyhow::Error }, /// A new TCP connection has been established from an incoming connection. - IncomingNew { - stream: tokio::net::TcpStream, - addr: net::SocketAddr, - }, + IncomingNew { stream: TcpStream, addr: SocketAddr }, /// The TLS handshake completed on the incoming connection. IncomingHandshakeCompleted { result: anyhow::Result<(NodeId, Transport)>, - addr: net::SocketAddr, + addr: SocketAddr, }, /// Received network message. IncomingMessage { node_id: NodeId, msg: Message

}, /// Incoming connection closed. IncomingClosed { result: io::Result<()>, - addr: net::SocketAddr, + addr: SocketAddr, }, /// A new outgoing connection was successfully established. @@ -120,36 +139,36 @@ pub enum Event

{ pub struct SmallNetwork where - R: reactor::Reactor, + R: Reactor, { /// Configuration. cfg: config::SmallNetwork, /// Server certificate. - cert: Arc, + cert: Arc, /// Server private key. - private_key: Arc>, + private_key: Arc>, /// Handle to event queue. - eq: reactor::EventQueueHandle>, - /// A list of known endpoints by node id. - endpoints: collections::HashMap, + eq: EventQueueHandle>, + /// A list of known endpoints by node ID. + endpoints: HashMap, /// Stored signed endpoints that can be sent to other nodes. - signed_endpoints: collections::HashMap>, - /// Outgoing network connections messages. - outgoing: collections::HashMap>>, + signed_endpoints: HashMap>, + /// Outgoing network connections' messages. + outgoing: HashMap>>, } impl SmallNetwork where - R: reactor::Reactor + 'static, - P: Serialize + DeserializeOwned + Clone + fmt::Debug + Send + 'static, + R: Reactor + 'static, + P: Serialize + DeserializeOwned + Clone + Debug + Send + 'static, { #[allow(clippy::type_complexity)] pub fn new( - eq: reactor::EventQueueHandle>, + eq: EventQueueHandle>, cfg: config::SmallNetwork, ) -> anyhow::Result<(SmallNetwork, Multiple>>)> where - R: reactor::Reactor + 'static, + R: Reactor + 'static, { // First, we load or generate the TLS keys. let (cert, private_key) = match (&cfg.cert, &cfg.private_key) { @@ -165,7 +184,7 @@ where (None, None) => tls::generate_node_cert()?, // If we get only one of the two, return an error. - _ => anyhow::bail!("need either both or none of cert, private_key in network config"), + _ => bail!("need either both or none of cert, private_key in network config"), }; // We can now create a listener. @@ -174,9 +193,7 @@ where // Create the model. Initially we know our own endpoint address. let our_endpoint = Endpoint { - timestamp_ns: time::SystemTime::now() - .duration_since(time::UNIX_EPOCH)? - .as_nanos() as u64, + timestamp_ns: SystemTime::now().duration_since(UNIX_EPOCH)?.as_nanos() as u64, addr, cert: tls::validate_cert(cert.clone())?, }; @@ -194,7 +211,7 @@ where cert: Arc::new(cert), private_key: Arc::new(private_key), eq, - outgoing: collections::HashMap::new(), + outgoing: HashMap::new(), }; // Connect to the root node (even if we are the root node, just loopback). @@ -203,7 +220,7 @@ where Ok((model, effects)) } - /// Attempt to connect to the root node. + /// Attempts to connect to the root node. fn connect_to_root(&self) -> Multiple>> { connect_trusted( self.cfg.root_addr, @@ -270,7 +287,7 @@ where Event::IncomingClosed { result, addr } => { match result { Ok(()) => info!(%addr, "connection closed"), - Err(err) => warn!(%addr, %err, "connection dopped"), + Err(err) => warn!(%addr, %err, "connection dropped"), } Multiple::new() } @@ -326,14 +343,14 @@ where } } - /// Queue a message to be sent to all nodes. + /// Queues a message to be sent to all nodes. fn broadcast_message(&self, msg: Message

) { for node_id in self.outgoing.keys() { self.send_message(*node_id, msg.clone()); } } - /// Queue a message to be sent to a specific node. + /// Queues a message to be sent to a specific node. fn send_message(&self, dest: NodeId, msg: Message

) { // Try to send the message. if let Some(sender) = self.outgoing.get(&dest) { @@ -347,9 +364,9 @@ where } } - /// Update the internal endpoint store from a given endpoint. + /// Updates the internal endpoint store from a given endpoint. /// - /// Returns the node id of the endpoint if it was new. + /// Returns the node ID of the endpoint if it was new. #[inline] fn update_endpoint(&mut self, endpoint: &Endpoint) -> Option { let fp = endpoint.cert.public_key_fingerprint(); @@ -365,7 +382,7 @@ where Some(fp) } - /// Update internal endpoint store and if new, output a `BroadcastEndpoint` effect. + /// Updates internal endpoint store and if new, output a `BroadcastEndpoint` effect. #[inline] fn update_and_broadcast_if_new( &mut self, @@ -416,7 +433,7 @@ where } } - /// Setup an established outgoing connection. + /// Sets up an established outgoing connection. fn setup_outgoing( &mut self, node_id: NodeId, @@ -428,8 +445,8 @@ where let (sender, receiver) = mpsc::unbounded_channel(); if self.outgoing.insert(node_id, sender).is_some() { // We assume that for a reconnect to have happened, the outgoing entry must have - // been either non-existant yet or cleaned up by the handler of the connection - // closing event. If this not the case, an assumed invariant has been violated. + // been either non-existent yet or cleaned up by the handler of the connection + // closing event. If this is not the case, an assumed invariant has been violated. error!(%node_id, "did not expect leftover channel in outgoing map"); } @@ -444,7 +461,7 @@ where }) } - /// Handle received message. + /// Handles a received message. // Internal function to keep indentation and nesting sane. fn handle_message(&mut self, node_id: NodeId, msg: Message

) -> Multiple>> { match msg { @@ -467,14 +484,14 @@ where } } -/// Determine bind address for now. +/// Determines bind address for now. /// /// Will attempt to bind on the root address first if the `bind_interface` is the same as the /// interface of `root_addr`. Otherwise uses an unused port on `bind_interface`. -fn create_listener(cfg: &config::SmallNetwork) -> io::Result { +fn create_listener(cfg: &config::SmallNetwork) -> io::Result { if cfg.root_addr.ip() == cfg.bind_interface { // Try to become the root node, if the root nodes interface is available. - match net::TcpListener::bind(cfg.root_addr) { + match TcpListener::bind(cfg.root_addr) { Ok(listener) => { info!("we are the root node!"); return Ok(listener); @@ -489,38 +506,37 @@ fn create_listener(cfg: &config::SmallNetwork) -> io::Result { } // We did not become the root node, bind on random port. - Ok(net::TcpListener::bind((cfg.bind_interface, 0u16))?) + Ok(TcpListener::bind((cfg.bind_interface, 0u16))?) } /// Core accept loop for the networking server. /// /// Never terminates. -async fn server_task( - eq: reactor::EventQueueHandle>, +async fn server_task( + eq: EventQueueHandle>, mut listener: tokio::net::TcpListener, ) { loop { - // We handle accept errors here, since they can be caused by a temporary - // resource shortage or the remote side closing the connection while - // it is waiting in the queue. + // We handle accept errors here, since they can be caused by a temporary resource shortage + // or the remote side closing the connection while it is waiting in the queue. match listener.accept().await { Ok((stream, addr)) => { // Move the incoming connection to the event queue for handling. let ev = Event::IncomingNew { stream, addr }; - eq.schedule(ev, reactor::Queue::NetworkIncoming).await; + eq.schedule(ev, Queue::NetworkIncoming).await; } Err(err) => warn!(%err, "dropping incoming connection during accept"), } } } -/// Server-side TLS handshake +/// Server-side TLS handshake. /// /// This function groups the TLS handshake into a convenient function, enabling the `?` operator. async fn setup_tls( - stream: tokio::net::TcpStream, - cert: Arc, - private_key: Arc>, + stream: TcpStream, + cert: Arc, + private_key: Arc>, ) -> anyhow::Result<(NodeId, Transport)> { let tls_stream = tokio_openssl::accept( &tls::create_tls_acceptor(&cert.as_ref(), &private_key.as_ref())?, @@ -532,7 +548,7 @@ async fn setup_tls( let peer_cert = tls_stream .ssl() .peer_certificate() - .ok_or_else(|| anyhow::anyhow!("no peer certificate presented"))?; + .ok_or_else(|| anyhow!("no peer certificate presented"))?; Ok(( tls::validate_cert(peer_cert)?.public_key_fingerprint(), @@ -542,14 +558,14 @@ async fn setup_tls( /// Network message reader. /// -/// Schedules all received messages until the stream is closed or an error occurred. +/// Schedules all received messages until the stream is closed or an error occurs. async fn message_reader( - eq: reactor::EventQueueHandle>, - mut stream: futures::stream::SplitStream>, + eq: EventQueueHandle>, + mut stream: SplitStream>, node_id: NodeId, ) -> io::Result<()> where - R: reactor::Reactor, + R: Reactor, P: DeserializeOwned + Send, { while let Some(msg_result) = stream.next().await { @@ -558,7 +574,7 @@ where // We've received a message, push it to the reactor. eq.schedule( Event::IncomingMessage { node_id, msg }, - reactor::Queue::NetworkIncoming, + Queue::NetworkIncoming, ) .await; } @@ -571,12 +587,12 @@ where Ok(()) } -/// Network message sender +/// Network message sender. /// -/// Reads from a channel and sends all messages, until the stream is closed or an error occured. +/// Reads from a channel and sends all messages, until the stream is closed or an error occurs. async fn message_sender

( - mut queue: mpsc::UnboundedReceiver>, - mut sink: futures::stream::SplitSink, Message

>, + mut queue: UnboundedReceiver>, + mut sink: SplitSink, Message

>, ) -> io::Result<()> where P: Serialize + Send, @@ -590,47 +606,46 @@ where } /// Transport type alias for base encrypted connections. -type Transport = tokio_openssl::SslStream; +type Transport = SslStream; /// A framed transport for `Message`s. -type FramedTransport

= tokio_serde::SymmetricallyFramed< - tokio_util::codec::Framed, +type FramedTransport

= SymmetricallyFramed< + Framed, Message

, - tokio_serde::formats::SymmetricalMessagePack>, + SymmetricalMessagePack>, >; -/// Construct a new framed transport on a stream. +/// Constructs a new framed transport on a stream. fn framed

(stream: Transport) -> FramedTransport

{ - let length_delimited = - tokio_util::codec::Framed::new(stream, tokio_util::codec::LengthDelimitedCodec::new()); - tokio_serde::SymmetricallyFramed::new( + let length_delimited = Framed::new(stream, LengthDelimitedCodec::new()); + SymmetricallyFramed::new( length_delimited, - tokio_serde::formats::SymmetricalMessagePack::>::default(), + SymmetricalMessagePack::>::default(), ) } -/// Initiate a TLS connection to an endpoint. +/// Initiates a TLS connection to an endpoint. async fn connect_outgoing( endpoint: Endpoint, - cert: Arc, - private_key: Arc>, + cert: Arc, + private_key: Arc>, ) -> anyhow::Result { let (server_cert, transport) = connect_trusted(endpoint.addr, cert, private_key).await?; let remote_id = server_cert.public_key_fingerprint(); if remote_id != endpoint.cert.public_key_fingerprint() { - anyhow::bail!("remote node has wrong ID"); + bail!("remote node has wrong ID"); } Ok(transport) } -/// Initiate a TLS connection to a remote address, regardless of what ID the remote node reports. +/// Initiates a TLS connection to a remote address, regardless of what ID the remote node reports. async fn connect_trusted( - addr: net::SocketAddr, - cert: Arc, - private_key: Arc>, + addr: SocketAddr, + cert: Arc, + private_key: Arc>, ) -> anyhow::Result<(TlsCert, Transport)> { let mut config = tls::create_tls_connector(&cert, &private_key) .context("could not create TLS connector")? @@ -648,14 +663,14 @@ async fn connect_trusted( let server_cert = tls_stream .ssl() .peer_certificate() - .ok_or_else(|| anyhow::anyhow!("no server certificate presented"))?; + .ok_or_else(|| anyhow!("no server certificate presented"))?; Ok((tls::validate_cert(server_cert)?, tls_stream)) } // Impose a total ordering on endpoints. Compare timestamps first, if the same, order by actual // address. If both of these are the same, use the TLS certificate's fingerprint as a tie-breaker. impl Ord for Endpoint { - fn cmp(&self, other: &Self) -> cmp::Ordering { + fn cmp(&self, other: &Self) -> Ordering { Ord::cmp(&self.timestamp_ns, &other.timestamp_ns) .then_with(|| { Ord::cmp( @@ -668,16 +683,13 @@ impl Ord for Endpoint { } impl PartialOrd for Endpoint { #[inline] - fn partial_cmp(&self, other: &Self) -> Option { + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl

fmt::Display for Message

-where - P: fmt::Display, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl Display for Message

{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { Message::Snapshot(snapshot) => { write!(f, "snapshot: {:10}", DisplayIter::new(snapshot.iter())) @@ -688,11 +700,8 @@ where } } -impl

fmt::Display for Event

-where - P: fmt::Display, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl Display for Event

{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { Event::RootConnected { cert, .. } => { write!(f, "root connected @ {}", cert.public_key_fingerprint()) @@ -722,8 +731,8 @@ where } } -impl fmt::Display for Endpoint { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl Display for Endpoint { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!( f, "{}@{} [{}]", @@ -734,12 +743,12 @@ impl fmt::Display for Endpoint { } } -impl fmt::Debug for SmallNetwork +impl Debug for SmallNetwork where - R: reactor::Reactor, - P: fmt::Debug, + R: Reactor, + P: Debug, { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("SmallNetwork") .field("cert", &"") .field("private_key", &"") diff --git a/src/config.rs b/src/config.rs index 57368ecb1b..aabb81c848 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,22 +1,32 @@ -//! Configuration file management +//! Configuration file management. //! -//! Configuration for the node is loaded from TOML files, but all configuration values have -//! sensible defaults. +//! Configuration for the node is loaded from TOML files, but all configuration values have sensible +//! defaults. //! -//! The `cli` offers an option to generate a configuration from defaults for editing. +//! The [`Cli`](../cli/enum.Cli.html#variant.GenerateConfig) offers an option to generate a +//! configuration from defaults for editing. I.e. running the following will dump a default +//! configuration file to stdout: +//! ``` +//! cargo run --release -- generate-config +//! ``` //! //! # Adding a configuration section //! //! When adding a section to the configuration, ensure that //! -//! * it has an entry in the root configuration `Config`, +//! * it has an entry in the root configuration [`Config`](struct.Config.html), //! * `Default` is implemented (derived or manually) with sensible defaults, and //! * it is completely documented. +use std::{ + fs, io, + net::{IpAddr, Ipv4Addr, SocketAddr}, + path::{Path, PathBuf}, +}; + use anyhow::Context; use serde::{Deserialize, Serialize}; -use std::{fs, io, net, path}; -use tracing::debug; +use tracing::{debug, Level}; /// Root configuration. #[derive(Debug, Deserialize, Serialize)] @@ -34,7 +44,7 @@ pub struct Config { pub struct Log { /// Log level. #[serde(with = "log_level")] - pub level: tracing::Level, + pub level: Level, } #[derive(Debug, Deserialize, Serialize)] @@ -42,31 +52,31 @@ pub struct Log { pub struct SmallNetwork { /// Interface to bind to. If it is the same as the in `root_addr`, attempt /// become the root node for this particular small network. - pub bind_interface: net::IpAddr, + pub bind_interface: IpAddr, /// Port to bind to when not the root node. Use 0 for a random port. pub bind_port: u16, /// Address to connect to join the network. - pub root_addr: net::SocketAddr, + pub root_addr: SocketAddr, /// Path to certificate file. - pub cert: Option, + pub cert: Option, /// Path to private key for certificate. - pub private_key: Option, + pub private_key: Option, /// Maximum number of retries when trying to connect to an outgoing node. Unlimited if `None`. pub max_outgoing_retries: Option, } impl SmallNetwork { - /// Create a default instance for `SmallNetwork` with a constant port. + /// Creates a default instance for `SmallNetwork` with a constant port. fn default_on_port(port: u16) -> Self { SmallNetwork { - bind_interface: net::Ipv4Addr::new(127, 0, 0, 1).into(), + bind_interface: Ipv4Addr::new(127, 0, 0, 1).into(), bind_port: 0, - root_addr: (net::Ipv4Addr::new(127, 0, 0, 1), port).into(), + root_addr: (Ipv4Addr::new(127, 0, 0, 1), port).into(), cert: None, private_key: None, max_outgoing_retries: None, @@ -86,14 +96,12 @@ impl Default for Config { impl Default for Log { fn default() -> Self { - Log { - level: tracing::Level::INFO, - } + Log { level: Level::INFO } } } impl Log { - /// Initialize logging system based on settings in configuration. + /// Initializes logging system based on settings in configuration. /// /// Will setup logging as described in this configuration for the whole application. This /// function should only be called once during the lifetime of the application. @@ -112,7 +120,7 @@ impl Log { } /// Loads a TOML-formatted configuration from a given file. -pub fn load_from_file>(config_path: P) -> anyhow::Result { +pub fn load_from_file>(config_path: P) -> anyhow::Result { let path_ref = config_path.as_ref(); Ok(toml::from_str( &fs::read_to_string(path_ref) @@ -121,29 +129,31 @@ pub fn load_from_file>(config_path: P) -> anyhow::Result anyhow::Result { toml::to_string_pretty(cfg).with_context(|| "Failed to serialize default configuration") } /// Serialization/deserialization mod log_level { - use serde::{self, Deserialize}; use std::str::FromStr; + + use serde::{self, de::Error, Deserialize, Deserializer, Serializer}; use tracing::Level; pub fn serialize(value: &Level, serializer: S) -> Result where - S: serde::Serializer, + S: Serializer, { serializer.serialize_str(value.to_string().as_str()) } pub fn deserialize<'de, D>(deserializer: D) -> Result where - D: serde::Deserializer<'de>, + D: Deserializer<'de>, { let s = String::deserialize(deserializer)?; - Level::from_str(s.as_str()).map_err(serde::de::Error::custom) + + Level::from_str(s.as_str()).map_err(Error::custom) } } diff --git a/src/effect.rs b/src/effect.rs index 90d3fa82dc..5098e78b40 100644 --- a/src/effect.rs +++ b/src/effect.rs @@ -1,32 +1,33 @@ //! Effects subsystem. //! -//! Effects describe things that the creator of the effect intends to happen, -//! producing a value upon completion. They are, in fact, futures. +//! Effects describe things that the creator of the effect intends to happen, producing a value upon +//! completion. They are, in fact, futures. //! //! A boxed, pinned future returning an event is called an effect and typed as an `Effect`, //! where `Ev` is the event's type. //! //! ## Using effects //! -//! To create an effect, an events factory is used that implements one or more of the factory -//! traits of this module. For example, given an events factory `eff`, we can create a +//! To create an effect, an events factory is used that implements one or more of the factory traits +//! of this module. For example, given an events factory `events_factory`, we can create a //! `set_timeout` future and turn it into an effect: //! //! ``` -//! # use std::time; +//! use std::time::Duration; //! use crate::effect::EffectExt; //! //! enum Event { -//! ThreeSecondsElapsed(time::Duration) +//! ThreeSecondsElapsed(Duration) //! } //! -//! eff.set_timeout(time::Duration::from_secs(3)) -//! .event(Event::ThreeSecondsElapsed) +//! events_factory +//! .set_timeout(Duration::from_secs(3)) +//! .event(Event::ThreeSecondsElapsed); //! ``` //! //! This example will produce an effect that, after three seconds, creates an //! `Event::ThreeSecondsElapsed`. Note that effects do nothing on their own, they need to be passed -//! to the `Reactor` (see `reactor` module) to be executed. +//! to a [`reactor`](../reactor/index.html) to be executed. //! //! ## Chaining futures and effects //! @@ -37,24 +38,32 @@ //! It is possible to create an effect from multiple effects being run in parallel using `.also`: //! //! ``` -//! # use std::time; +//! use std::time::Duration; //! use crate::effect::{EffectExt, EffectAlso}; //! //! enum Event { -//! ThreeSecondsElapsed(time::Duration), -//! FiveSecondsElapsed(time::Duration), +//! ThreeSecondsElapsed(Duration), +//! FiveSecondsElapsed(Duration), //! } //! //! // This effect produces a single event after five seconds: -//! eff.set_timeout(time::Duration::from_secs(3)) -//! .then(|_| eff.set_timeout(time::Duration::from_secs(2)) -//! .event(Event::FiveSecondsElapsed); +//! events_factory +//! .set_timeout(Duration::from_secs(3)) +//! .then(|_| { +//! events_factory +//! .set_timeout(Duration::from_secs(2)) +//! .event(Event::FiveSecondsElapsed) +//! }); //! //! // Here, two effects are run in parallel, resulting in two events: -//! eff.set_timeout(time::Duration::from_secs(3)) -//! .event(Event::ThreeSecondsElapsed) -//! .also(eff.set_timeout(time::Duration::from_secs(5)) -//! .event(Event::FiveSecondsElapsed)); +//! events_factory +//! .set_timeout(Duration::from_secs(3)) +//! .event(Event::ThreeSecondsElapsed) +//! .also( +//! events_factory +//! .set_timeout(Duration::from_secs(5)) +//! .event(Event::FiveSecondsElapsed), +//! ); //! ``` //! //! ## Arbitrary effects @@ -63,12 +72,12 @@ //! the effects explicitly listed in this module through traits to create them. Post-processing on //! effects to turn them into events should also be kept brief. -use crate::util::Multiple; -use futures::future::BoxFuture; -use futures::FutureExt; +use std::{future::Future, time::Duration}; + +use futures::{future::BoxFuture, FutureExt}; use smallvec::smallvec; -use std::future::Future; -use std::time; + +use crate::utils::Multiple; /// Effect type. /// @@ -79,7 +88,7 @@ pub type Effect = BoxFuture<'static, Multiple>; /// /// Used to convert futures into actual effects. pub trait EffectExt: Future + Send { - /// Finalize a future into an effect that returns an event. + /// Finalizes a future into an effect that returns an event. /// /// The function `f` is used to translate the returned value from an effect into an event. fn event(self, f: F) -> Multiple> @@ -96,7 +105,7 @@ pub trait EffectResultExt { type Value; type Error; - /// Finalize a future returning a `Result` into two different effects. + /// Finalizes a future returning a `Result` into two different effects. /// /// The function `f` is used to translate the returned value from an effect into an event, while /// the function `g` does the same for a potential error. @@ -146,15 +155,15 @@ where /// Core effects. pub trait Core { - /// Do not do anything. + /// Immediately completes without doing anything. /// - /// Immediately completes, can be used to trigger an event. + /// Can be used to trigger an event. fn immediately(self) -> BoxFuture<'static, ()>; - /// Set a timeout. + /// Sets a timeout. /// /// Once the timeout fires, it will return the actual elapsed time since the execution (not /// creation!) of this effect. Event loops typically execute effects right after a called event /// handling function completes. - fn set_timeout(self, timeout: time::Duration) -> BoxFuture<'static, time::Duration>; + fn set_timeout(self, timeout: Duration) -> BoxFuture<'static, Duration>; } diff --git a/src/main.rs b/src/main.rs index 425d838b57..344231339e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,17 +9,30 @@ //! its core event loop is found inside the [reactor](reactor/index.html). To get a tour of the //! sourcecode, be sure to run `cargo doc --open`. +#![doc( + html_favicon_url = "https://raw.githubusercontent.com/CasperLabs/casper-node/master/images/CasperLabs_Logo_Favicon_RGB_50px.png", + html_logo_url = "https://raw.githubusercontent.com/CasperLabs/casper-node/master/images/CasperLabs_Logo_Symbol_RGB.png", + test(attr(forbid(warnings))) +)] +#![warn( + missing_docs, + trivial_casts, + trivial_numeric_casts, + // unreachable_pub, + unused_qualifications +)] + mod cli; mod components; mod config; mod effect; mod reactor; mod tls; -mod util; +mod utils; use structopt::StructOpt; -/// Parse [command-line arguments](cli/index.html) and run application. +/// Parses [command-line arguments](cli/index.html) and run application. #[tokio::main] pub async fn main() -> anyhow::Result<()> { // Parse CLI args and run selected subcommand. diff --git a/src/reactor.rs b/src/reactor.rs index 7b740ffa3c..752de45eb3 100644 --- a/src/reactor.rs +++ b/src/reactor.rs @@ -2,9 +2,9 @@ //! //! Any long running instance of the node application uses an event-dispatch pattern: Events are //! generated and stored on an event queue, then processed one-by-one. This process happens inside -//! the *reactor*, which also exclusively holds the state of the application besides pending events: +//! the reactor*, which also exclusively holds the state of the application besides pending events: //! -//! 1. The reactor pops an event off of the event queue (called a `Scheduler`). +//! 1. The reactor pops an event off the event queue (called a [`Scheduler`](type.Scheduler.html)). //! 2. The event is dispatched by the reactor. Since the reactor holds mutable state, it can grant //! any component that processes an event mutable, exclusive access to its state. //! 3. Once the (synchronous) event processing has completed, the component returns an effect. @@ -13,25 +13,30 @@ //! //! # Reactors //! -//! There no single reactor, but a reactor for each application type, since it defines which -//! components are used and how they are wired up. The reactor defines the state by being a `struct` -//! of components, their initialization through the `Reactor::new` and a function to `dispatch` -//! events to components. +//! There is no single reactor, but rather a reactor for each application type, since it defines +//! which components are used and how they are wired up. The reactor defines the state by being a +//! `struct` of components, their initialization through the +//! [`Reactor::new()`](trait.Reactor.html#tymethod.new) and a method +//! [`Reactor::dispatch_event()`](trait.Reactor.html#tymethod.dispatch_event) to dispatch events to +//! components. //! -//! With all these set up, a reactor can be `launch`ed, causing it to run indefinitely, processing -//! events. +//! With all these set up, a reactor can be [`launch`](fn.launch.html)ed, causing it to run +//! indefinitely, processing events. pub mod non_validator; mod queue_kind; pub mod validator; -use crate::util::Multiple; -use crate::{config, effect, util}; -use async_trait::async_trait; -use futures::FutureExt; use std::{fmt, mem}; + +use futures::FutureExt; use tracing::{debug, info, trace, warn}; +use crate::{ + config, + effect::Effect, + utils::{self, Multiple, WeightedRoundRobin}, +}; pub use queue_kind::Queue; /// Event scheduler @@ -40,7 +45,7 @@ pub use queue_kind::Queue; /// is the central hook for any part of the program that schedules events directly. /// /// Components rarely use this, but use a bound `EventQueueHandle` instead. -pub type Scheduler = util::round_robin::WeightedRoundRobin; +pub type Scheduler = WeightedRoundRobin; /// Bound event queue handle /// @@ -79,7 +84,7 @@ impl EventQueueHandle where R: Reactor, { - /// Create a new event queue handle with an associated wrapper function. + /// Creates a new event queue handle with an associated wrapper function. fn bind(scheduler: &'static Scheduler, wrapper: fn(Ev) -> R::Event) -> Self { EventQueueHandle { scheduler, wrapper } } @@ -93,8 +98,8 @@ where /// Reactor core. /// -/// Any reactor implements should implement this trait and be launched by the `launch` function. -#[async_trait] +/// Any reactor should implement this trait and be launched by the [`launch`](fn.launch.html) +/// function. pub trait Reactor: Sized { // Note: We've gone for the `Sized` bound here, since we return an instance in `new`. As an // alternative, `new` could return a boxed instance instead, removing this requirement. @@ -104,14 +109,14 @@ pub trait Reactor: Sized { /// Defines what kind of event the reactor processes. type Event: Send + fmt::Debug + fmt::Display + 'static; - /// Dispatch an event on the reactor. + /// Dispatches an event on the reactor. /// /// This function is typically only called by the reactor itself to dispatch an event. It is /// safe to call regardless, but will cause the event to skip the queue and things like /// accounting. - fn dispatch_event(&mut self, event: Self::Event) -> Multiple>; + fn dispatch_event(&mut self, event: Self::Event) -> Multiple>; - /// Create a new instance of the reactor. + /// Creates a new instance of the reactor. /// /// This method creates the full state, which consists of all components, and returns a reactor /// instances along with the effects the components generated upon instantiation. @@ -120,12 +125,12 @@ pub trait Reactor: Sized { fn new( cfg: config::Config, scheduler: &'static Scheduler, - ) -> anyhow::Result<(Self, Multiple>)>; + ) -> anyhow::Result<(Self, Multiple>)>; } -/// Run a reactor. +/// Runs a reactor. /// -/// Start the reactor and associated background tasks, then enter main the event processing loop. +/// Starts the reactor and associated background tasks, then enters main the event processing loop. /// /// `launch` will leak memory on start for global structures each time it is called. /// @@ -146,7 +151,7 @@ pub async fn launch(cfg: config::Config) -> anyhow::Result<()> { let scheduler = Scheduler::::new(Queue::weights()); // Create a new event queue for this reactor run. - let scheduler = util::leak(scheduler); + let scheduler = utils::leak(scheduler); let (mut reactor, initial_effects) = R::new(cfg, scheduler)?; @@ -167,14 +172,10 @@ pub async fn launch(cfg: config::Config) -> anyhow::Result<()> { } } -/// Process effects. -/// /// Spawns tasks that will process the given effects. #[inline] -async fn process_effects( - scheduler: &'static Scheduler, - effects: Multiple>, -) where +async fn process_effects(scheduler: &'static Scheduler, effects: Multiple>) +where Ev: Send + 'static, { // TODO: Properly carry around priorities. @@ -189,9 +190,9 @@ async fn process_effects( } } -/// Convert a single effect into another by wrapping it. +/// Converts a single effect into another by wrapping it. #[inline] -pub fn wrap_effect(wrap: F, effect: effect::Effect) -> effect::Effect +pub fn wrap_effect(wrap: F, effect: Effect) -> Effect where F: Fn(Ev) -> REv + Send + 'static, Ev: Send + 'static, @@ -205,12 +206,9 @@ where .boxed() } -/// Convert multiple effects into another by wrapping. +/// Converts multiple effects into another by wrapping. #[inline] -pub fn wrap_effects( - wrap: F, - effects: Multiple>, -) -> Multiple> +pub fn wrap_effects(wrap: F, effects: Multiple>) -> Multiple> where F: Fn(Ev) -> REv + Send + 'static + Clone, Ev: Send + 'static, diff --git a/src/reactor/queue_kind.rs b/src/reactor/queue_kind.rs index c05f5820b9..2ac1149d3e 100644 --- a/src/reactor/queue_kind.rs +++ b/src/reactor/queue_kind.rs @@ -1,11 +1,12 @@ -//! Queue kinds +//! Queue kinds. //! //! The reactor's event queue uses different queues to group events by priority and polls them in a //! round-robin manner. This way, events are only competing for time within one queue, non-congested //! queues can always assume to be speedily processed. +use std::num::NonZeroUsize; + use enum_iterator::IntoEnumIterator; -use std::num; /// Scheduling priority. /// @@ -24,7 +25,7 @@ pub enum Queue { Regular, /// Reporting events on the local node. /// - /// Metric events take precendence over most other events since missing a request for metrics + /// Metric events take precedence over most other events since missing a request for metrics /// might cause the requester to assume that the node is down and forcefully restart it. Metrics, } @@ -36,12 +37,12 @@ impl Default for Queue { } impl Queue { - /// Return the weight of a specific queue. + /// Returns the weight of a specific queue. /// /// The weight determines how many events are at most processed from a specific queue during /// each event processing round. - fn weight(self) -> num::NonZeroUsize { - num::NonZeroUsize::new(match self { + fn weight(self) -> NonZeroUsize { + NonZeroUsize::new(match self { Queue::NetworkIncoming => 4, Queue::Network => 4, Queue::Regular => 8, @@ -51,7 +52,7 @@ impl Queue { } /// Return weights of all possible `Queue`s. - pub(super) fn weights() -> Vec<(Self, num::NonZeroUsize)> { + pub(super) fn weights() -> Vec<(Self, NonZeroUsize)> { Queue::into_enum_iter().map(|q| (q, q.weight())).collect() } } diff --git a/src/reactor/validator.rs b/src/reactor/validator.rs index 0503f940d8..9a99f09cd5 100644 --- a/src/reactor/validator.rs +++ b/src/reactor/validator.rs @@ -1,11 +1,18 @@ //! Reactor for validator nodes. //! -//! Validator nodes join the validator only network upon startup. -use crate::components::small_network; -use crate::util::Multiple; -use crate::{config, effect, reactor}; +//! Validator nodes join the validator-only network upon startup. + +use std::fmt::{self, Display, Formatter}; + use serde::{Deserialize, Serialize}; -use std::fmt; + +use crate::{ + components::small_network::{self, SmallNetwork}, + config::Config, + effect::Effect, + reactor::{self, EventQueueHandle, Scheduler}, + utils::Multiple, +}; /// Top-level event for the reactor. #[derive(Debug)] @@ -19,18 +26,18 @@ pub enum Message {} /// Validator node reactor. pub struct Reactor { - net: small_network::SmallNetwork, + net: SmallNetwork, } impl reactor::Reactor for Reactor { type Event = Event; fn new( - cfg: config::Config, - scheduler: &'static reactor::Scheduler, - ) -> anyhow::Result<(Self, Multiple>)> { - let (net, net_effects) = small_network::SmallNetwork::new( - reactor::EventQueueHandle::bind(scheduler, Event::Network), + cfg: Config, + scheduler: &'static Scheduler, + ) -> anyhow::Result<(Self, Multiple>)> { + let (net, net_effects) = SmallNetwork::new( + EventQueueHandle::bind(scheduler, Event::Network), cfg.validator_net, )?; @@ -40,23 +47,23 @@ impl reactor::Reactor for Reactor { )) } - fn dispatch_event(&mut self, event: Event) -> Multiple> { + fn dispatch_event(&mut self, event: Event) -> Multiple> { match event { Event::Network(ev) => reactor::wrap_effects(Event::Network, self.net.handle_event(ev)), } } } -impl fmt::Display for Event { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl Display for Event { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { Event::Network(ev) => write!(f, "network: {}", ev), } } } -impl fmt::Display for Message { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl Display for Message { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "TODO: MessagePayload") } } diff --git a/src/tls.rs b/src/tls.rs index 610151c957..cde7c6cb1b 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -1,8 +1,8 @@ //! Transport layer security and signing based on OpenSSL. //! -//! This module wraps some of the lower-level TLS constructs to provide a reasonably safe to use API -//! surface for the rest of the application. It also fixates the security parameters of the TLS -//! level in a central place. +//! This module wraps some of the lower-level TLS constructs to provide a reasonably safe-to-use API +//! surface for the rest of the application. It also fixes the security parameters of the TLS level +//! in a central place. //! //! Features include //! @@ -20,44 +20,64 @@ //! ([`Signature`](struct.Signature.html), [`Signed`](struct.Signed.html)), and //! * `serde` support for certificates ([`x509_serde`](x509_serde/index.html)) -use anyhow::Context; +use std::{ + cmp::Ordering, + convert::TryInto, + fmt::{self, Debug, Display, Formatter}, + fs, + hash::Hash, + marker::PhantomData, + path::Path, + str, + time::{SystemTime, UNIX_EPOCH}, +}; + +use anyhow::{anyhow, Context}; use displaydoc::Display; use hex_fmt::HexFmt; -use openssl::{asn1, bn, ec, error, hash, nid, pkey, sha, sign, ssl, x509}; -use serde::de::DeserializeOwned; -use serde::{Deserialize, Serialize}; +use nid::Nid; +use openssl::{ + asn1::{Asn1Integer, Asn1IntegerRef, Asn1Time}, + bn::{BigNum, BigNumContext}, + ec, + error::ErrorStack, + hash::{DigestBytes, MessageDigest}, + nid, + pkey::{PKey, PKeyRef, Private, Public}, + sha, + sign::{Signer, Verifier}, + ssl::{SslAcceptor, SslConnector, SslContextBuilder, SslMethod, SslVerifyMode, SslVersion}, + x509::{X509Builder, X509Name, X509NameBuilder, X509NameRef, X509Ref, X509}, +}; +use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize, Serializer}; use serde_big_array::big_array; -use std::convert::TryInto; -use std::hash::Hash; -use std::marker::PhantomData; -use std::{cmp, fmt, path, str, time}; use thiserror::Error; big_array! { BigArray; } /// The chosen signature algorithm (**ECDSA with SHA512**). -const SIGNATURE_ALGORITHM: nid::Nid = nid::Nid::ECDSA_WITH_SHA512; +const SIGNATURE_ALGORITHM: Nid = Nid::ECDSA_WITH_SHA512; /// The underlying elliptic curve (**P-521**). -const SIGNATURE_CURVE: nid::Nid = nid::Nid::SECP521R1; +const SIGNATURE_CURVE: Nid = Nid::SECP521R1; /// The chosen signature algorithm (**SHA512**). -const SIGNATURE_DIGEST: nid::Nid = nid::Nid::SHA512; +const SIGNATURE_DIGEST: Nid = Nid::SHA512; /// OpenSSL result type alias. /// /// Many functions rely solely on `openssl` functions and return this kind of result. -pub type SslResult = Result; +pub type SslResult = Result; /// SHA512 hash. #[derive(Copy, Clone, Deserialize, Serialize)] pub struct Sha512(#[serde(with = "BigArray")] [u8; Sha512::SIZE]); -/// Certificate fingerprint +/// Certificate fingerprint. #[derive(Copy, Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] pub struct CertFingerprint(Sha512); -/// Public key fingerprint +/// Public key fingerprint. #[derive(Copy, Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] pub struct KeyFingerprint(Sha512); @@ -67,11 +87,11 @@ pub struct Signature(Vec); /// TLS certificate. /// -/// Thin wrapper around `X509` enabling things like serde serialization and fingerprint caching. +/// Thin wrapper around `X509` enabling things like Serde serialization and fingerprint caching. #[derive(Clone)] pub struct TlsCert { /// The wrapped x509 certificate. - x509: openssl::x509::X509, + x509: X509, /// Cached certificate fingerprint. cert_fingerprint: CertFingerprint, @@ -81,19 +101,19 @@ pub struct TlsCert { } // Serialization and deserialization happens only via x509, which is checked upon deserialization. -impl<'de> serde::Deserialize<'de> for TlsCert { +impl<'de> Deserialize<'de> for TlsCert { fn deserialize(deserializer: D) -> Result where - D: serde::Deserializer<'de>, + D: Deserializer<'de>, { validate_cert(x509_serde::deserialize(deserializer)?).map_err(serde::de::Error::custom) } } -impl serde::Serialize for TlsCert { +impl Serialize for TlsCert { fn serialize(&self, serializer: S) -> Result where - S: serde::Serializer, + S: Serializer, { x509_serde::serialize(&self.x509, serializer) } @@ -114,10 +134,10 @@ impl Signed where V: Serialize, { - /// Create new signed value. + /// Creates a new signed value. /// /// Serializes the value to a buffer and signs the buffer. - pub fn new(value: &V, signing_key: &pkey::PKeyRef) -> anyhow::Result { + pub fn new(value: &V, signing_key: &PKeyRef) -> anyhow::Result { let data = rmp_serde::to_vec(value)?; let signature = Signature::create(signing_key, &data)?; @@ -133,23 +153,23 @@ impl Signed where V: DeserializeOwned, { - /// Validate signature and restore value. + /// Validates signature and restore value. #[allow(dead_code)] - pub fn validate(&self, public_key: &pkey::PKeyRef) -> anyhow::Result { + pub fn validate(&self, public_key: &PKeyRef) -> anyhow::Result { if self.signature.verify(public_key, &self.data)? { Ok(rmp_serde::from_read(self.data.as_slice())?) } else { - Err(anyhow::anyhow!("invalid signature")) + Err(anyhow!("invalid signature")) } } - /// Validate a self-signed values. + /// Validates a self-signed value. /// /// Allows for extraction of a public key prior to validating a value. #[inline] pub fn validate_self_signed(&self, extract: F) -> anyhow::Result where - F: FnOnce(&V) -> anyhow::Result>, + F: FnOnce(&V) -> anyhow::Result>, { let unverified = rmp_serde::from_read(self.data.as_slice())?; { @@ -158,7 +178,7 @@ where if self.signature.verify(&public_key, &self.data)? { Ok(unverified) } else { - Err(anyhow::anyhow!("invalid signature")) + Err(anyhow!("invalid signature")) } } } @@ -169,7 +189,7 @@ impl Sha512 { pub const SIZE: usize = 64; /// OpenSSL NID. - const NID: nid::Nid = nid::Nid::SHA512; + const NID: Nid = Nid::SHA512; /// Create a new Sha512 by hashing a slice. pub fn new>(data: B) -> Self { @@ -178,7 +198,7 @@ impl Sha512 { Sha512(openssl_sha.finish()) } - /// Return bytestring of the hash, with length `Self::SIZE`. + /// Returns bytestring of the hash, with length `Self::SIZE`. pub fn bytes(&self) -> &[u8] { let bs = &self.0[..]; @@ -186,8 +206,8 @@ impl Sha512 { bs } - /// Convert an OpenSSL digest into an `Sha512`. - fn from_openssl_digest(digest: &hash::DigestBytes) -> Self { + /// Converts an OpenSSL digest into an `Sha512`. + fn from_openssl_digest(digest: &DigestBytes) -> Self { let digest_bytes = digest.as_ref(); debug_assert_eq!( @@ -202,23 +222,23 @@ impl Sha512 { Sha512(buf) } - /// Return a new OpenSSL `MessageDigest` set to SHA-512. - fn create_message_digest() -> hash::MessageDigest { + /// Returns a new OpenSSL `MessageDigest` set to SHA-512. + fn create_message_digest() -> MessageDigest { // This can only fail if we specify a `Nid` that does not exist, which cannot happen unless // there is something wrong with `Self::NID`. - hash::MessageDigest::from_nid(Self::NID).expect("Sha512::NID is invalid") + MessageDigest::from_nid(Self::NID).expect("Sha512::NID is invalid") } } impl Signature { - /// Sign a binary blob with the blessed ciphers and TLS parameters. - pub fn create(private_key: &pkey::PKeyRef, data: &[u8]) -> SslResult { + /// Signs a binary blob with the blessed ciphers and TLS parameters. + pub fn create(private_key: &PKeyRef, data: &[u8]) -> SslResult { // TODO: This needs verification to ensure we're not doing stupid/textbook RSA-ish. // Sha512 is hardcoded, so check we're creating the correct signature. assert_eq!(Sha512::NID, SIGNATURE_DIGEST); - let mut signer = sign::Signer::new(Sha512::create_message_digest(), private_key)?; + let mut signer = Signer::new(Sha512::create_message_digest(), private_key)?; // The API of OpenSSL is a bit weird here; there is no constant size for the buffer required // to create the signatures. Additionally, we need to truncate it to the returned size. @@ -230,22 +250,18 @@ impl Signature { Ok(Signature(sig_buf)) } - /// Verify that signature matches on a binary blob. - pub fn verify( - self: &Signature, - public_key: &pkey::PKeyRef, - data: &[u8], - ) -> SslResult { + /// Verifies that signature matches on a binary blob. + pub fn verify(self: &Signature, public_key: &PKeyRef, data: &[u8]) -> SslResult { assert_eq!(Sha512::NID, SIGNATURE_DIGEST); - let mut verifier = sign::Verifier::new(Sha512::create_message_digest(), public_key)?; + let mut verifier = Verifier::new(Sha512::create_message_digest(), public_key)?; verifier.verify_oneshot(&self.0, data) } } impl TlsCert { - /// Return the certificates fingerprint. + /// Returns the certificate's fingerprint. /// /// In contrast to the `public_key_fingerprint`, this fingerprint also contains the certificate /// information. @@ -253,28 +269,28 @@ impl TlsCert { self.cert_fingerprint } - /// Extract the public key from the certificate. - pub fn public_key(&self) -> pkey::PKey { + /// Extracts the public key from the certificate. + pub fn public_key(&self) -> PKey { // This can never fail, we validate the certificate on construction and deserialization. self.x509 .public_key() .expect("public key extraction failed, how did we end up with an invalid cert?") } - /// Return the public key fingerprint. + /// Returns the public key fingerprint. pub fn public_key_fingerprint(&self) -> KeyFingerprint { self.key_fingerprint } #[allow(dead_code)] - /// Return OpenSSL X509 certificate. - fn x509(&self) -> &x509::X509 { + /// Returns OpenSSL X509 certificate. + fn x509(&self) -> &X509 { &self.x509 } } -impl fmt::Debug for TlsCert { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl Debug for TlsCert { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "TlsCert({:?})", self.fingerprint()) } } @@ -293,55 +309,55 @@ impl PartialEq for TlsCert { impl Eq for TlsCert {} -/// Generate a self-signed (key, certificate) pair suitable for TLS and signing. +/// Generates a self-signed (key, certificate) pair suitable for TLS and signing. /// -/// The common name of the certificate will be "casperlabs-node". -pub fn generate_node_cert() -> SslResult<(x509::X509, pkey::PKey)> { +/// The common name of the certificate will be "casper-node". +pub fn generate_node_cert() -> SslResult<(X509, PKey)> { let private_key = generate_private_key()?; - let cert = generate_cert(&private_key, "casperlabs-node")?; + let cert = generate_cert(&private_key, "casper-node")?; Ok((cert, private_key)) } -/// Create a TLS acceptor for a server. +/// Creates a TLS acceptor for a server. /// /// The acceptor will restrict TLS parameters to secure one defined in this crate that are /// compatible with connectors built with `create_tls_connector`. /// /// Incoming certificates must still be validated using `validate_cert`. pub fn create_tls_acceptor( - cert: &x509::X509Ref, - private_key: &pkey::PKeyRef, -) -> SslResult { - let mut builder = ssl::SslAcceptor::mozilla_modern_v5(ssl::SslMethod::tls_server())?; + cert: &X509Ref, + private_key: &PKeyRef, +) -> SslResult { + let mut builder = SslAcceptor::mozilla_modern_v5(SslMethod::tls_server())?; set_context_options(&mut builder, cert, private_key)?; Ok(builder.build()) } -/// Create a TLS acceptor for a client. +/// Creates a TLS acceptor for a client. /// /// A connector compatible with the acceptor created using `create_tls_acceptor`. Server /// certificates must always be validated using `validate_cert` after connecting. pub fn create_tls_connector( - cert: &x509::X509Ref, - private_key: &pkey::PKeyRef, -) -> SslResult { - let mut builder = ssl::SslConnector::builder(ssl::SslMethod::tls_client())?; + cert: &X509Ref, + private_key: &PKeyRef, +) -> SslResult { + let mut builder = SslConnector::builder(SslMethod::tls_client())?; set_context_options(&mut builder, cert, private_key)?; Ok(builder.build()) } -/// Set common options of both acceptor and connector on TLS context. +/// Sets common options of both acceptor and connector on TLS context. /// /// Used internally to set various TLS parameters. fn set_context_options( - ctx: &mut ssl::SslContextBuilder, - cert: &x509::X509Ref, - private_key: &pkey::PKeyRef, + ctx: &mut SslContextBuilder, + cert: &X509Ref, + private_key: &PKeyRef, ) -> SslResult<()> { - ctx.set_min_proto_version(Some(ssl::SslVersion::TLS1_3))?; + ctx.set_min_proto_version(Some(SslVersion::TLS1_3))?; ctx.set_certificate(cert)?; ctx.set_private_key(private_key)?; @@ -351,7 +367,7 @@ fn set_context_options( // no certificate and there will be no error from OpenSSL. For this reason, we pass set `PEER` // (causing the request of a cert), but pass all of them through and verify them after the // handshake has completed. - ctx.set_verify_callback(ssl::SslVerifyMode::PEER, |_, _| true); + ctx.set_verify_callback(SslVerifyMode::PEER, |_, _| true); Ok(()) } @@ -360,46 +376,46 @@ fn set_context_options( #[derive(Debug, Display, Error)] pub enum ValidationError { /// error reading public key from certificate: {0:?} - CannotReadPublicKey(#[source] error::ErrorStack), + CannotReadPublicKey(#[source] ErrorStack), /// error reading subject or issuer name: {0:?} - CorruptSubjectOrIssuer(#[source] error::ErrorStack), + CorruptSubjectOrIssuer(#[source] ErrorStack), /// wrong signature scheme WrongSignatureAlgorithm, /// there was an issue reading or converting times: {0:?} - TimeIssue(#[source] error::ErrorStack), + TimeIssue(#[source] ErrorStack), /// the certificate is not yet valid NotYetValid, /// the certificate expired Expired, /// the serial number could not be compared to the reference: {0:?} - InvalidSerialNumber(#[source] error::ErrorStack), + InvalidSerialNumber(#[source] ErrorStack), /// wrong serial number WrongSerialNumber, - /// no valid elliptic curve key could be extracted from cert: {0:?} - CouldNotExtractEcKey(#[source] error::ErrorStack), + /// no valid elliptic curve key could be extracted from certificate: {0:?} + CouldNotExtractEcKey(#[source] ErrorStack), /// the given public key fails basic sanity checks: {0:?} - KeyFailsCheck(#[source] error::ErrorStack), + KeyFailsCheck(#[source] ErrorStack), /// underlying elliptic curve is wrong WrongCurve, /// certificate is not self-signed NotSelfSigned, /// the signature could not be validated - FailedToValidateSignature(#[source] error::ErrorStack), + FailedToValidateSignature(#[source] ErrorStack), /// the signature is invalid InvalidSignature, /// failed to read fingerprint - InvalidFingerprint(#[source] error::ErrorStack), + InvalidFingerprint(#[source] ErrorStack), /// could not create a big num context - BigNumContextNotAvailable(#[source] error::ErrorStack), + BigNumContextNotAvailable(#[source] ErrorStack), /// could not encode public key as bytes - PublicKeyEncodingFailed(#[source] error::ErrorStack), + PublicKeyEncodingFailed(#[source] ErrorStack), } -/// Check that the cryptographic parameters on a certificate are correct and return the fingerprint -/// of the public key. +/// Checks that the cryptographic parameters on a certificate are correct and returns the +/// fingerprint of the public key. /// /// At the very least this ensures that no weaker ciphers have been used to forge a certificate. -pub fn validate_cert(cert: x509::X509) -> Result { +pub fn validate_cert(cert: X509) -> Result { if cert.signature_algorithm().object().nid() != SIGNATURE_ALGORITHM { // The signature algorithm is not of the exact kind we are using to generate our // certificates, an attacker could have used a weaker one to generate colliding keys. @@ -424,11 +440,11 @@ pub fn validate_cert(cert: x509::X509) -> Result { } // Check expiration times against current time. - let asn1_now = asn1::Asn1Time::from_unix(now()).map_err(ValidationError::TimeIssue)?; + let asn1_now = Asn1Time::from_unix(now()).map_err(ValidationError::TimeIssue)?; if asn1_now .compare(cert.not_before()) .map_err(ValidationError::TimeIssue)? - != cmp::Ordering::Greater + != Ordering::Greater { return Err(ValidationError::NotYetValid); } @@ -436,7 +452,7 @@ pub fn validate_cert(cert: x509::X509) -> Result { if asn1_now .compare(cert.not_after()) .map_err(ValidationError::TimeIssue)? - != cmp::Ordering::Less + != Ordering::Less { return Err(ValidationError::Expired); } @@ -473,7 +489,7 @@ pub fn validate_cert(cert: x509::X509) -> Result { // Additionally we can calculate a fingerprint for the public key: let mut big_num_context = - bn::BigNumContext::new().map_err(ValidationError::BigNumContextNotAvailable)?; + BigNumContext::new().map_err(ValidationError::BigNumContextNotAvailable)?; let buf = ec_key .public_key() @@ -495,78 +511,73 @@ pub fn validate_cert(cert: x509::X509) -> Result { }) } -/// Load a certificate from a file. -pub fn load_cert>(src: P) -> anyhow::Result { - let pem = std::fs::read(src.as_ref()) +/// Loads a certificate from a file. +pub fn load_cert>(src: P) -> anyhow::Result { + let pem = fs::read(src.as_ref()) .with_context(|| format!("failed to load certificate {:?}", src.as_ref()))?; - Ok(x509::X509::from_pem(&pem).context("parsing certificate")?) + Ok(X509::from_pem(&pem).context("parsing certificate")?) } -/// Load a private key from a file. -pub fn load_private_key>(src: P) -> anyhow::Result> { - let pem = std::fs::read(src.as_ref()) +/// Loads a private key from a file. +pub fn load_private_key>(src: P) -> anyhow::Result> { + let pem = fs::read(src.as_ref()) .with_context(|| format!("failed to load private key {:?}", src.as_ref()))?; - // TODO: It might be that we need to call `pkey::PKey::private_key_from_pkcs8` instead. - Ok(pkey::PKey::private_key_from_pem(&pem).context("parsing private key")?) + // TODO: It might be that we need to call `PKey::private_key_from_pkcs8` instead. + Ok(PKey::private_key_from_pem(&pem).context("parsing private key")?) } -/// Save a certificate to a file. -pub fn save_cert>(cert: &x509::X509Ref, dest: P) -> anyhow::Result<()> { +/// Saves a certificate to a file. +pub fn save_cert>(cert: &X509Ref, dest: P) -> anyhow::Result<()> { let pem = cert.to_pem().context("converting certificate to PEM")?; - std::fs::write(dest.as_ref(), pem) + fs::write(dest.as_ref(), pem) .with_context(|| format!("failed to write certificate {:?}", dest.as_ref()))?; Ok(()) } -/// Save a private key to a file. -pub fn save_private_key>( - key: &pkey::PKeyRef, - dest: P, -) -> anyhow::Result<()> { +/// Saves a private key to a file. +pub fn save_private_key>(key: &PKeyRef, dest: P) -> anyhow::Result<()> { let pem = key .private_key_to_pem_pkcs8() .context("converting private key to PEM")?; - std::fs::write(dest.as_ref(), pem) + fs::write(dest.as_ref(), pem) .with_context(|| format!("failed to write private key {:?}", dest.as_ref()))?; Ok(()) } -/// Return an OpenSSL compatible timestamp. -/// - +/// Returns an OpenSSL compatible timestamp. fn now() -> i64 { // Note: We could do the timing dance a little better going straight to the UNIX time functions, // but this saves us having to bring in `libc` as a dependency. - let now = time::SystemTime::now(); + let now = SystemTime::now(); let ts: i64 = now - .duration_since(time::UNIX_EPOCH) + .duration_since(UNIX_EPOCH) // This should work unless the clock is set to before 1970. .expect("Great Scott! Your clock is horribly broken, Marty.") .as_secs() - // This will fail past year 2038 on 32 bit systems and a very far into the future, both - // cases we consider out of scope. + // This will fail past year 2038 on 32 bit systems and very far into the future, both cases + // we consider out of scope. .try_into() .expect("32-bit systems and far future are not supported"); ts } -/// Create an ASN1 integer from a `u32`. -fn mknum(n: u32) -> Result { - let bn = openssl::bn::BigNum::from_u32(n)?; +/// Creates an ASN1 integer from a `u32`. +fn mknum(n: u32) -> Result { + let bn = BigNum::from_u32(n)?; bn.to_asn1_integer() } -/// Create an ASN1 name from string components. +/// Creates an ASN1 name from string components. /// /// If `c` or `o` are empty string, they are omitted from the result. -fn mkname(c: &str, o: &str, cn: &str) -> Result { - let mut builder = x509::X509NameBuilder::new()?; +fn mkname(c: &str, o: &str, cn: &str) -> Result { + let mut builder = X509NameBuilder::new()?; if !c.is_empty() { builder.append_entry_by_text("C", c)?; @@ -580,8 +591,8 @@ fn mkname(c: &str, o: &str, cn: &str) -> Result SslResult { +/// Converts an `X509NameRef` to a human readable string. +fn name_to_string(name: &X509NameRef) -> SslResult { let mut output = String::new(); for entry in name.entries() { @@ -594,20 +605,20 @@ fn name_to_string(name: &x509::X509NameRef) -> SslResult { Ok(output) } -/// Check if an `Asn1IntegerRef` is equal to a given u32. -fn num_eq(num: &asn1::Asn1IntegerRef, other: u32) -> SslResult { +/// Checks if an `Asn1IntegerRef` is equal to a given u32. +fn num_eq(num: &Asn1IntegerRef, other: u32) -> SslResult { let l = num.to_bn()?; - let r = bn::BigNum::from_u32(other)?; + let r = BigNum::from_u32(other)?; // The `BigNum` API seems to be really lacking here. - Ok(l.is_negative() == r.is_negative() && l.ucmp(&r.as_ref()) == cmp::Ordering::Equal) + Ok(l.is_negative() == r.is_negative() && l.ucmp(&r.as_ref()) == Ordering::Equal) } -/// Generate a secret key suitable for TLS encryption. -fn generate_private_key() -> SslResult> { +/// Generates a secret key suitable for TLS encryption. +fn generate_private_key() -> SslResult> { // We do not care about browser-compliance, so we're free to use elliptic curves that are more // likely to hold up under pressure than the NIST ones. We want to go with ED25519 because djb - // know's best: pkey::PKey::generate_ed25519() + // knows best: PKey::generate_ed25519() // // However the following bug currently prevents us from doing so: // https://mta.openssl.org/pipermail/openssl-users/2018-July/008362.html (The same error occurs @@ -623,12 +634,12 @@ fn generate_private_key() -> SslResult> { let ec_group = ec::EcGroup::from_curve_name(SIGNATURE_CURVE)?; let ec_key = ec::EcKey::generate(ec_group.as_ref())?; - pkey::PKey::from_ec_key(ec_key) + PKey::from_ec_key(ec_key) } -/// Generate a self-signed certificate based on `private_key` with given CN. -fn generate_cert(private_key: &pkey::PKey, cn: &str) -> SslResult { - let mut builder = x509::X509Builder::new()?; +/// Generates a self-signed certificate based on `private_key` with given CN. +fn generate_cert(private_key: &PKey, cn: &str) -> SslResult { + let mut builder = X509Builder::new()?; // x509 v3 commonly used, the version is 0-indexed, thus 2 == v3. builder.set_version(2)?; @@ -644,10 +655,10 @@ fn generate_cert(private_key: &pkey::PKey, cn: &str) -> SslResult let ts = now(); // We set valid-from to one minute into the past to allow some clock-skew. - builder.set_not_before(asn1::Asn1Time::from_unix(ts - 60)?.as_ref())?; + builder.set_not_before(Asn1Time::from_unix(ts - 60)?.as_ref())?; // Valid-until is a little under 10 years, missing at least 2 leap days. - builder.set_not_after(asn1::Asn1Time::from_unix(ts + 10 * 365 * 24 * 60 * 60)?.as_ref())?; + builder.set_not_after(Asn1Time::from_unix(ts + 10 * 365 * 24 * 60 * 60)?.as_ref())?; // Set the public key and sign. builder.set_pubkey(private_key.as_ref())?; @@ -665,18 +676,21 @@ fn generate_cert(private_key: &pkey::PKey, cn: &str) -> SslResult Ok(cert) } -/// Serde support for `openssl::x509::X509` certificates. +/// Serde support for `openx509::X509` certificates. /// /// Will also check if certificates are valid according to `validate_cert` when deserializing. mod x509_serde { - use super::validate_cert; - use openssl::x509::X509; use std::str; + use openssl::x509::X509; + use serde::{Deserialize, Deserializer, Serializer}; + + use super::validate_cert; + /// Serde-compatible serialization for X509 certificates. pub fn serialize(value: &X509, serializer: S) -> Result where - S: serde::Serializer, + S: Serializer, { let encoded = value.to_pem().map_err(serde::ser::Error::custom)?; @@ -687,10 +701,8 @@ mod x509_serde { /// Serde-compatible deserialization for X509 certificates. pub fn deserialize<'de, D>(deserializer: D) -> Result where - D: serde::Deserializer<'de>, + D: Deserializer<'de>, { - use serde::Deserialize; - // Create an extra copy for simplicity here. If this becomes a bottleneck, feel free to try // to leverage Cow here, or implement a custom visitor that handles both cases. let s: String = Deserialize::deserialize(deserializer)?; @@ -715,53 +727,53 @@ impl Eq for Sha512 {} impl Ord for Sha512 { #[inline] - fn cmp(&self, other: &Self) -> cmp::Ordering { + fn cmp(&self, other: &Self) -> Ordering { Ord::cmp(self.bytes(), other.bytes()) } } impl PartialOrd for Sha512 { #[inline] - fn partial_cmp(&self, other: &Self) -> Option { + fn partial_cmp(&self, other: &Self) -> Option { Some(Ord::cmp(self, other)) } } -impl fmt::Debug for Sha512 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl Debug for Sha512 { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "{}", HexFmt(&self.0[..])) } } -impl fmt::Display for Sha512 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl Display for Sha512 { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "{}", HexFmt(&self.0[0..7])) } } -impl fmt::Display for CertFingerprint { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.0, f) +impl Display for CertFingerprint { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + Display::fmt(&self.0, f) } } -impl fmt::Display for KeyFingerprint { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.0, f) +impl Display for KeyFingerprint { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + Display::fmt(&self.0, f) } } -impl fmt::Display for Signature { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl Display for Signature { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "{}", HexFmt(&self.0[0..7])) } } -impl fmt::Display for Signed +impl Display for Signed where - T: fmt::Display + for<'de> Deserialize<'de>, + T: Display + for<'de> Deserialize<'de>, { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { // Decode the data here, even if it is expensive. match rmp_serde::from_read::<_, T>(self.data.as_slice()) { Ok(item) => write!(f, "signed[{}]<{} bytes>", self.signature, item), diff --git a/src/util.rs b/src/utils.rs similarity index 60% rename from src/util.rs rename to src/utils.rs index f8ba81eb33..7da11e649e 100644 --- a/src/util.rs +++ b/src/utils.rs @@ -1,14 +1,17 @@ -//! Various utilities. -//! -//! The Generic functions that are not limited to a particular module, but are too small to warrant +//! Various functions that are not limited to a particular module, but are too small to warrant //! being factored out into standalone crates. -use std::fmt; -pub mod round_robin; -use std::cell; +mod round_robin; + +use std::{ + cell::RefCell, + fmt::{self, Display, Formatter}, +}; + +use smallvec::SmallVec; + +pub use round_robin::WeightedRoundRobin; -/// Leak a value. -/// /// Moves a value to the heap and then forgets about, leaving only a static reference behind. #[inline] pub fn leak(value: T) -> &'static T { @@ -17,28 +20,28 @@ pub fn leak(value: T) -> &'static T { /// Small amount store. /// -/// Stored in a smallvec to avoid allocations in case there are less than three items grouped. The +/// Stored in a `SmallVec` to avoid allocations in case there are less than three items grouped. The /// size of two items is chosen because one item is the most common use case, and large items are -/// typically boxed. In the latter case two pointers and one enum variant discriminator is almost +/// typically boxed. In the latter case two pointers and one enum variant discriminator is almost /// the same size as an empty vec, which is two pointers. -pub type Multiple = smallvec::SmallVec<[T; 2]>; +pub type Multiple = SmallVec<[T; 2]>; /// A display-helper that shows iterators display joined by ",". #[derive(Debug)] -pub struct DisplayIter(cell::RefCell>); +pub struct DisplayIter(RefCell>); impl DisplayIter { pub fn new(item: T) -> Self { - DisplayIter(cell::RefCell::new(Some(item))) + DisplayIter(RefCell::new(Some(item))) } } -impl fmt::Display for DisplayIter +impl Display for DisplayIter where I: IntoIterator, - T: fmt::Display, + T: Display, { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { if let Some(src) = self.0.borrow_mut().take() { let mut first = true; for item in src.into_iter().take(f.width().unwrap_or(usize::MAX)) { diff --git a/src/util/round_robin.rs b/src/utils/round_robin.rs similarity index 81% rename from src/util/round_robin.rs rename to src/utils/round_robin.rs index c83bec328e..8d4ef6cfd4 100644 --- a/src/util/round_robin.rs +++ b/src/utils/round_robin.rs @@ -1,13 +1,16 @@ -//! Round-robin scheduling. +//! Weighted round-robin scheduling. //! //! This module implements a weighted round-robin scheduler that ensures no deadlocks occur, but -//! still allows prioriting events from one source over another. The module uses `tokio`s +//! still allows prioritizing events from one source over another. The module uses `tokio`'s //! synchronization primitives under the hood. -use std::collections::{HashMap, VecDeque}; -use std::hash::Hash; -use std::num::NonZeroUsize; -use tokio::sync; +use std::{ + collections::{HashMap, VecDeque}, + hash::Hash, + num::NonZeroUsize, +}; + +use tokio::sync::{Mutex, Semaphore}; /// Weighted round-robin scheduler. /// @@ -22,16 +25,16 @@ use tokio::sync; #[derive(Debug)] pub struct WeightedRoundRobin { /// Current iteration state. - state: sync::Mutex>, + state: Mutex>, /// A list of slots that are round-robin'd. slots: Vec>, /// Actual queues. - queues: HashMap>>, + queues: HashMap>>, /// Number of items in all queues combined. - total: sync::Semaphore, + total: Semaphore, } /// The inner state of the queue iteration. @@ -48,8 +51,8 @@ struct IterationState { /// An internal slot in the round-robin scheduler. /// -/// A slot marks the scheduling position, i.e. which queue we are currently -/// polling and how many tickets it has left before the next one is due. +/// A slot marks the scheduling position, i.e. which queue we are currently polling and how many +/// tickets it has left before the next one is due. #[derive(Copy, Clone, Debug)] struct Slot { /// The key, identifying a queue. @@ -63,17 +66,16 @@ impl WeightedRoundRobin where K: Copy + Clone + Eq + Hash, { - /// Create new weighted round-robin scheduler. + /// Creates a new weighted round-robin scheduler. /// - /// Creates a queue for each pair given in `weights`. The second component - /// of each `weight` is the number of times to return items from one - /// queue before moving on to the next one. + /// Creates a queue for each pair given in `weights`. The second component of each `weight` is + /// the number of times to return items from one queue before moving on to the next one. pub fn new(weights: Vec<(K, NonZeroUsize)>) -> Self { assert!(!weights.is_empty(), "must provide at least one slot"); let queues = weights .iter() - .map(|(idx, _)| (*idx, sync::Mutex::new(VecDeque::new()))) + .map(|(idx, _)| (*idx, Mutex::new(VecDeque::new()))) .collect(); let slots: Vec> = weights .into_iter() @@ -85,17 +87,17 @@ where let active_slot = slots[0]; WeightedRoundRobin { - state: sync::Mutex::new(IterationState { + state: Mutex::new(IterationState { active_slot, active_slot_idx: 0, }), slots, queues, - total: sync::Semaphore::new(0), + total: Semaphore::new(0), } } - /// Push an item to a queue identified by key. + /// Pushes an item to a queue identified by key. /// /// ## Panics /// @@ -103,7 +105,7 @@ where pub async fn push(&self, item: I, queue: K) { self.queues .get(&queue) - .expect("tried to push to non-existant queue") + .expect("tried to push to non-existent queue") .lock() .await .push_back(item); @@ -112,7 +114,7 @@ where self.total.add_permits(1); } - /// Return the next item from queue. + /// Returns the next item from queue. /// /// Returns `None` if the queue is empty or an internal error occurred. The /// latter should never happen.