Remove failure from zebra-chain, zebra-network.
Failure uses a distinct Fail trait rather than the standard library's Error trait, which causes a lot of interoperability problems with tower and other Error-using crates. Since failure was created, the standard library's Error trait was improved, and its conveniences are now available without the custom Fail trait using `thiserror` (for easy error derives) and `anyhow` (for a better boxed Error).
This commit is contained in:
parent
199038e6b8
commit
f6e62b0f5e
|
@ -8,8 +8,8 @@ edition = "2018"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
thiserror = "1"
|
||||||
byteorder = "1.3"
|
byteorder = "1.3"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
failure = "0.1"
|
|
||||||
#hex = "0.4" This conflicts with tracing-subscriber?
|
#hex = "0.4" This conflicts with tracing-subscriber?
|
||||||
sha2 = "0.8"
|
sha2 = "0.8"
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
//! Blockchain-related datastructures for Zebra. 🦓
|
//! Blockchain-related datastructures for Zebra. 🦓
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate failure;
|
|
||||||
|
|
||||||
mod merkle_tree;
|
mod merkle_tree;
|
||||||
mod sha256d_writer;
|
mod sha256d_writer;
|
||||||
|
|
||||||
|
|
|
@ -10,25 +10,19 @@ use std::io;
|
||||||
use std::net::{IpAddr, SocketAddr};
|
use std::net::{IpAddr, SocketAddr};
|
||||||
|
|
||||||
use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
/// A serialization error.
|
/// A serialization error.
|
||||||
// XXX refine error types -- better to use boxed errors?
|
// XXX refine error types -- better to use boxed errors?
|
||||||
#[derive(Fail, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum SerializationError {
|
pub enum SerializationError {
|
||||||
/// An underlying IO error.
|
/// An underlying IO error.
|
||||||
#[fail(display = "io error {}", _0)]
|
#[error("io error")]
|
||||||
IoError(io::Error),
|
Io(#[from] io::Error),
|
||||||
/// The data to be deserialized was malformed.
|
/// The data to be deserialized was malformed.
|
||||||
// XXX refine errors
|
// XXX refine errors
|
||||||
#[fail(display = "parse error: {}", _0)]
|
#[error("parse error: {0}")]
|
||||||
ParseError(&'static str),
|
Parse(&'static str),
|
||||||
}
|
|
||||||
|
|
||||||
// Allow upcasting io::Error to SerializationError
|
|
||||||
impl From<io::Error> for SerializationError {
|
|
||||||
fn from(e: io::Error) -> Self {
|
|
||||||
Self::IoError(e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consensus-critical serialization for Zcash.
|
/// Consensus-critical serialization for Zcash.
|
||||||
|
|
|
@ -13,7 +13,7 @@ bytes = "0.4"
|
||||||
rand = "0.7"
|
rand = "0.7"
|
||||||
byteorder = "1.3"
|
byteorder = "1.3"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
failure = "0.1"
|
thiserror = "1"
|
||||||
serde = { version = "1", features = ["serde_derive"] }
|
serde = { version = "1", features = ["serde_derive"] }
|
||||||
pin-project = "0.4"
|
pin-project = "0.4"
|
||||||
# indexmap has rayon support for parallel iteration,
|
# indexmap has rayon support for parallel iteration,
|
||||||
|
|
|
@ -7,8 +7,6 @@ extern crate pin_project;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate failure;
|
|
||||||
#[macro_use]
|
|
||||||
extern crate tracing;
|
extern crate tracing;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate bitflags;
|
extern crate bitflags;
|
||||||
|
|
|
@ -4,30 +4,12 @@
|
||||||
mod client;
|
mod client;
|
||||||
/// Asynchronously connects to peers.
|
/// Asynchronously connects to peers.
|
||||||
mod connector;
|
mod connector;
|
||||||
|
/// Peer-related errors.
|
||||||
|
mod error;
|
||||||
/// Handles inbound requests from the network to our node.
|
/// Handles inbound requests from the network to our node.
|
||||||
mod server;
|
mod server;
|
||||||
|
|
||||||
pub use client::PeerClient;
|
pub use client::PeerClient;
|
||||||
pub use connector::PeerConnector;
|
pub use connector::PeerConnector;
|
||||||
|
pub use error::{PeerError, SharedPeerError};
|
||||||
pub use server::PeerServer;
|
pub use server::PeerServer;
|
||||||
|
|
||||||
/// An error related to a peer connection.
|
|
||||||
#[derive(Fail, Debug, Clone)]
|
|
||||||
pub enum PeerError {
|
|
||||||
/// Wrapper around `failure::Error` that can be `Clone`.
|
|
||||||
#[fail(display = "{}", _0)]
|
|
||||||
Inner(std::sync::Arc<failure::Error>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<failure::Error> for PeerError {
|
|
||||||
fn from(e: failure::Error) -> PeerError {
|
|
||||||
PeerError::Inner(std::sync::Arc::new(e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX hack
|
|
||||||
impl Into<crate::BoxedStdError> for PeerError {
|
|
||||||
fn into(self) -> crate::BoxedStdError {
|
|
||||||
Box::new(format_err!("dropped error info").compat())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ use tower::Service;
|
||||||
|
|
||||||
use crate::protocol::internal::{Request, Response};
|
use crate::protocol::internal::{Request, Response};
|
||||||
|
|
||||||
use super::{server::ErrorSlot, PeerError};
|
use super::{error::ErrorSlot, SharedPeerError};
|
||||||
|
|
||||||
/// The "client" duplex half of a peer connection.
|
/// The "client" duplex half of a peer connection.
|
||||||
pub struct PeerClient {
|
pub struct PeerClient {
|
||||||
|
@ -29,12 +29,12 @@ pub struct PeerClient {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(super) struct ClientRequest(
|
pub(super) struct ClientRequest(
|
||||||
pub(super) Request,
|
pub(super) Request,
|
||||||
pub(super) oneshot::Sender<Result<Response, PeerError>>,
|
pub(super) oneshot::Sender<Result<Response, SharedPeerError>>,
|
||||||
);
|
);
|
||||||
|
|
||||||
impl Service<Request> for PeerClient {
|
impl Service<Request> for PeerClient {
|
||||||
type Response = Response;
|
type Response = Response;
|
||||||
type Error = PeerError;
|
type Error = SharedPeerError;
|
||||||
type Future =
|
type Future =
|
||||||
Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
|
Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
|
||||||
|
|
||||||
|
@ -52,6 +52,7 @@ impl Service<Request> for PeerClient {
|
||||||
fn call(&mut self, req: Request) -> Self::Future {
|
fn call(&mut self, req: Request) -> Self::Future {
|
||||||
use futures::future::FutureExt;
|
use futures::future::FutureExt;
|
||||||
use tracing_futures::Instrument;
|
use tracing_futures::Instrument;
|
||||||
|
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
match self.server_tx.try_send(ClientRequest(req, tx)) {
|
match self.server_tx.try_send(ClientRequest(req, tx)) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -68,16 +69,15 @@ impl Service<Request> for PeerClient {
|
||||||
panic!("called call without poll_ready");
|
panic!("called call without poll_ready");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// need a bit of yoga to get result types to align,
|
Ok(()) => {
|
||||||
// because the oneshot future can error
|
// The reciever end of the oneshot is itself a future.
|
||||||
Ok(()) => rx
|
rx.map(|oneshot_recv_result| {
|
||||||
.map(|val| match val {
|
oneshot_recv_result
|
||||||
Ok(Ok(rsp)) => Ok(rsp),
|
.expect("ClientRequest oneshot sender must not be dropped before send")
|
||||||
Ok(Err(e)) => Err(e),
|
|
||||||
Err(_) => Err(format_err!("oneshot died").into()),
|
|
||||||
})
|
})
|
||||||
.instrument(self.span.clone())
|
.instrument(self.span.clone())
|
||||||
.boxed(),
|
.boxed()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use failure::Error;
|
|
||||||
use futures::channel::mpsc;
|
use futures::channel::mpsc;
|
||||||
use tokio::{codec::Framed, net::TcpStream, prelude::*};
|
use tokio::{codec::Framed, net::TcpStream, prelude::*};
|
||||||
use tower::Service;
|
use tower::Service;
|
||||||
|
@ -21,10 +20,7 @@ use crate::{
|
||||||
BoxedStdError, Config, Network,
|
BoxedStdError, Config, Network,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{error::ErrorSlot, server::ServerState, PeerClient, PeerError, PeerServer};
|
||||||
client::PeerClient,
|
|
||||||
server::{ErrorSlot, PeerServer, ServerState},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A [`Service`] that connects to a remote peer and constructs a client/server pair.
|
/// A [`Service`] that connects to a remote peer and constructs a client/server pair.
|
||||||
pub struct PeerConnector<S> {
|
pub struct PeerConnector<S> {
|
||||||
|
@ -64,12 +60,12 @@ where
|
||||||
|
|
||||||
impl<S> Service<SocketAddr> for PeerConnector<S>
|
impl<S> Service<SocketAddr> for PeerConnector<S>
|
||||||
where
|
where
|
||||||
S: Service<Request, Response = Response> + Clone + Send + 'static,
|
S: Service<Request, Response = Response, Error = BoxedStdError> + Clone + Send + 'static,
|
||||||
S::Future: Send,
|
S::Future: Send,
|
||||||
S::Error: Send + Into<BoxedStdError>,
|
S::Error: Send + Into<BoxedStdError>,
|
||||||
{
|
{
|
||||||
type Response = PeerClient;
|
type Response = PeerClient;
|
||||||
type Error = Error;
|
type Error = PeerError;
|
||||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
|
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
|
||||||
|
|
||||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
|
@ -93,7 +89,7 @@ where
|
||||||
debug!("opening tcp stream");
|
debug!("opening tcp stream");
|
||||||
|
|
||||||
let mut stream = Framed::new(
|
let mut stream = Framed::new(
|
||||||
TcpStream::connect(addr).await?,
|
TcpStream::connect(addr).await.expect("PeerError does not contain an io::Error variant, but this code will be removed in the next PR, so there's no need to handle this error"),
|
||||||
Codec::builder().for_network(network).finish(),
|
Codec::builder().for_network(network).finish(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -121,7 +117,7 @@ where
|
||||||
let remote_version = stream
|
let remote_version = stream
|
||||||
.next()
|
.next()
|
||||||
.await
|
.await
|
||||||
.ok_or_else(|| format_err!("stream closed during handshake"))??;
|
.ok_or_else(|| PeerError::ConnectionClosed)??;
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
?remote_version,
|
?remote_version,
|
||||||
|
@ -132,7 +128,7 @@ where
|
||||||
let remote_verack = stream
|
let remote_verack = stream
|
||||||
.next()
|
.next()
|
||||||
.await
|
.await
|
||||||
.ok_or_else(|| format_err!("stream closed during handshake"))??;
|
.ok_or_else(|| PeerError::ConnectionClosed)??;
|
||||||
|
|
||||||
debug!(?remote_verack, "got remote peer's verack");
|
debug!(?remote_verack, "got remote peer's verack");
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use zebra_chain::serialization::SerializationError;
|
||||||
|
|
||||||
|
/// A wrapper around `Arc<PeerError>` that implements `Error`.
|
||||||
|
#[derive(Error, Debug, Clone)]
|
||||||
|
#[error("{0}")]
|
||||||
|
pub struct SharedPeerError(#[from] Arc<PeerError>);
|
||||||
|
|
||||||
|
/// An error related to peer connection handling.
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum PeerError {
|
||||||
|
/// The remote peer closed the connection.
|
||||||
|
#[error("Peer closed connection")]
|
||||||
|
ConnectionClosed,
|
||||||
|
/// The [`PeerClient`] half of the [`PeerClient`]/[`PeerServer`] pair died before
|
||||||
|
/// the [`PeerServer`] half did.
|
||||||
|
#[error("PeerClient instance died")]
|
||||||
|
DeadPeerClient,
|
||||||
|
/// The [`PeerServer`] half of the [`PeerServer`]/[`PeerClient`] pair died before
|
||||||
|
/// the [`PeerClient`] half did.
|
||||||
|
#[error("PeerServer instance died")]
|
||||||
|
DeadPeerServer,
|
||||||
|
/// The remote peer did not respond to a [`PeerClient`] request in time.
|
||||||
|
#[error("Client request timed out")]
|
||||||
|
ClientRequestTimeout,
|
||||||
|
/// A serialization error occurred while reading or writing a message.
|
||||||
|
#[error("Serialization error")]
|
||||||
|
Serialization(#[from] SerializationError),
|
||||||
|
/// A badly-behaved remote peer sent a handshake message after the handshake was
|
||||||
|
/// already complete.
|
||||||
|
#[error("Remote peer sent handshake messages after handshake")]
|
||||||
|
DuplicateHandshake,
|
||||||
|
/// This node's internal services were overloaded, so the connection was dropped
|
||||||
|
/// to shed load.
|
||||||
|
#[error("Internal services over capacity")]
|
||||||
|
Overloaded,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone)]
|
||||||
|
pub(super) struct ErrorSlot(pub(super) Arc<Mutex<Option<SharedPeerError>>>);
|
||||||
|
|
||||||
|
impl ErrorSlot {
|
||||||
|
pub fn try_get_error(&self) -> Option<SharedPeerError> {
|
||||||
|
self.0
|
||||||
|
.lock()
|
||||||
|
.expect("error mutex should be unpoisoned")
|
||||||
|
.as_ref()
|
||||||
|
.map(|e| e.clone())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use failure::Error;
|
|
||||||
use futures::{
|
use futures::{
|
||||||
channel::{mpsc, oneshot},
|
channel::{mpsc, oneshot},
|
||||||
future::{self, Either},
|
future::{self, Either},
|
||||||
|
@ -11,6 +10,7 @@ use tokio::{
|
||||||
timer::{delay_for, Delay},
|
timer::{delay_for, Delay},
|
||||||
};
|
};
|
||||||
use tower::Service;
|
use tower::Service;
|
||||||
|
use zebra_chain::serialization::SerializationError;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
constants,
|
constants,
|
||||||
|
@ -21,26 +21,13 @@ use crate::{
|
||||||
BoxedStdError,
|
BoxedStdError,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{client::ClientRequest, PeerError};
|
use super::{client::ClientRequest, error::ErrorSlot, PeerError, SharedPeerError};
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
|
||||||
pub(super) struct ErrorSlot(Arc<Mutex<Option<PeerError>>>);
|
|
||||||
|
|
||||||
impl ErrorSlot {
|
|
||||||
pub fn try_get_error(&self) -> Option<PeerError> {
|
|
||||||
self.0
|
|
||||||
.lock()
|
|
||||||
.expect("error mutex should be unpoisoned")
|
|
||||||
.as_ref()
|
|
||||||
.map(|e| e.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) enum ServerState {
|
pub(super) enum ServerState {
|
||||||
/// Awaiting a client request or a peer message.
|
/// Awaiting a client request or a peer message.
|
||||||
AwaitingRequest,
|
AwaitingRequest,
|
||||||
/// Awaiting a peer message we can interpret as a client request.
|
/// Awaiting a peer message we can interpret as a client request.
|
||||||
AwaitingResponse(Request, oneshot::Sender<Result<Response, PeerError>>),
|
AwaitingResponse(Request, oneshot::Sender<Result<Response, SharedPeerError>>),
|
||||||
/// A failure has occurred and we are shutting down the server.
|
/// A failure has occurred and we are shutting down the server.
|
||||||
Failed,
|
Failed,
|
||||||
}
|
}
|
||||||
|
@ -62,15 +49,14 @@ pub struct PeerServer<S, Tx> {
|
||||||
|
|
||||||
impl<S, Tx> PeerServer<S, Tx>
|
impl<S, Tx> PeerServer<S, Tx>
|
||||||
where
|
where
|
||||||
S: Service<Request, Response = Response>,
|
S: Service<Request, Response = Response, Error = BoxedStdError>,
|
||||||
S::Error: Into<BoxedStdError>,
|
S::Error: Into<BoxedStdError>,
|
||||||
Tx: Sink<Message> + Unpin,
|
Tx: Sink<Message, Error = SerializationError> + Unpin,
|
||||||
Tx::Error: Into<Error>,
|
|
||||||
{
|
{
|
||||||
/// Run this peer server to completion.
|
/// Run this peer server to completion.
|
||||||
pub async fn run<Rx>(mut self, mut peer_rx: Rx)
|
pub async fn run<Rx>(mut self, mut peer_rx: Rx)
|
||||||
where
|
where
|
||||||
Rx: Stream<Item = Result<Message, Error>> + Unpin,
|
Rx: Stream<Item = Result<Message, SerializationError>> + Unpin,
|
||||||
{
|
{
|
||||||
// At a high level, the event loop we want is as follows: we check for any
|
// At a high level, the event loop we want is as follows: we check for any
|
||||||
// incoming messages from the remote peer, check if they should be interpreted
|
// incoming messages from the remote peer, check if they should be interpreted
|
||||||
|
@ -122,9 +108,7 @@ where
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.expect("timeout must be set while awaiting response");
|
.expect("timeout must be set while awaiting response");
|
||||||
match future::select(peer_rx.next(), timer_ref).await {
|
match future::select(peer_rx.next(), timer_ref).await {
|
||||||
Either::Left((None, _)) => {
|
Either::Left((None, _)) => self.fail_with(PeerError::ConnectionClosed),
|
||||||
self.fail_with(format_err!("peer closed connection").into())
|
|
||||||
}
|
|
||||||
// XXX switch back to hard failure when we parse all message types
|
// XXX switch back to hard failure when we parse all message types
|
||||||
//Either::Left((Some(Err(e)), _)) => self.fail_with(e.into()),
|
//Either::Left((Some(Err(e)), _)) => self.fail_with(e.into()),
|
||||||
Either::Left((Some(Err(peer_err)), _timer)) => error!(%peer_err),
|
Either::Left((Some(Err(peer_err)), _timer)) => error!(%peer_err),
|
||||||
|
@ -139,7 +123,8 @@ where
|
||||||
// Re-matching lets us take ownership of tx
|
// Re-matching lets us take ownership of tx
|
||||||
self.state = match self.state {
|
self.state = match self.state {
|
||||||
ServerState::AwaitingResponse(_, tx) => {
|
ServerState::AwaitingResponse(_, tx) => {
|
||||||
let _ = tx.send(Err(format_err!("request timed out").into()));
|
let e = PeerError::ClientRequestTimeout;
|
||||||
|
let _ = tx.send(Err(Arc::new(e).into()));
|
||||||
ServerState::AwaitingRequest
|
ServerState::AwaitingRequest
|
||||||
}
|
}
|
||||||
_ => panic!("unreachable"),
|
_ => panic!("unreachable"),
|
||||||
|
@ -179,7 +164,7 @@ where
|
||||||
if guard.is_some() {
|
if guard.is_some() {
|
||||||
panic!("called fail_with on already-failed server state");
|
panic!("called fail_with on already-failed server state");
|
||||||
} else {
|
} else {
|
||||||
*guard = Some(e);
|
*guard = Some(Arc::new(e).into());
|
||||||
}
|
}
|
||||||
// Drop the guard immediately to release the mutex.
|
// Drop the guard immediately to release the mutex.
|
||||||
std::mem::drop(guard);
|
std::mem::drop(guard);
|
||||||
|
@ -215,13 +200,13 @@ where
|
||||||
.peer_tx
|
.peer_tx
|
||||||
.send(Message::GetAddr)
|
.send(Message::GetAddr)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.into().into())
|
.map_err(|e| e.into())
|
||||||
.map(|()| AwaitingResponse(GetPeers, tx)),
|
.map(|()| AwaitingResponse(GetPeers, tx)),
|
||||||
(AwaitingRequest, PushPeers(addrs)) => self
|
(AwaitingRequest, PushPeers(addrs)) => self
|
||||||
.peer_tx
|
.peer_tx
|
||||||
.send(Message::Addr(addrs))
|
.send(Message::Addr(addrs))
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.into().into())
|
.map_err(|e| e.into())
|
||||||
.map(|()| {
|
.map(|()| {
|
||||||
// PushPeers does not have a response, so we return OK as
|
// PushPeers does not have a response, so we return OK as
|
||||||
// soon as we send the request. Sending through a oneshot
|
// soon as we send the request. Sending through a oneshot
|
||||||
|
@ -280,17 +265,17 @@ where
|
||||||
// These messages are transport-related, handle them separately:
|
// These messages are transport-related, handle them separately:
|
||||||
match msg {
|
match msg {
|
||||||
Message::Version { .. } => {
|
Message::Version { .. } => {
|
||||||
self.fail_with(format_err!("got version message after handshake").into());
|
self.fail_with(PeerError::DuplicateHandshake);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Message::Verack { .. } => {
|
Message::Verack { .. } => {
|
||||||
self.fail_with(format_err!("got verack message after handshake").into());
|
self.fail_with(PeerError::DuplicateHandshake);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Message::Ping(nonce) => {
|
Message::Ping(nonce) => {
|
||||||
match self.peer_tx.send(Message::Pong(nonce)).await {
|
match self.peer_tx.send(Message::Pong(nonce)).await {
|
||||||
Ok(()) => {}
|
Ok(()) => {}
|
||||||
Err(e) => self.fail_with(e.into().into()),
|
Err(e) => self.fail_with(e.into()),
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -317,26 +302,31 @@ where
|
||||||
/// of connected peers.
|
/// of connected peers.
|
||||||
async fn drive_peer_request(&mut self, req: Request) {
|
async fn drive_peer_request(&mut self, req: Request) {
|
||||||
trace!(?req);
|
trace!(?req);
|
||||||
use tower::ServiceExt;
|
use tower::{load_shed::error::Overloaded, ServiceExt};
|
||||||
if let Err(e) = self
|
|
||||||
.svc
|
if let Err(_) = self.svc.ready().await {
|
||||||
.ready()
|
// Treat all service readiness errors as Overloaded
|
||||||
.await
|
self.fail_with(PeerError::Overloaded);
|
||||||
.map_err(|e| Error::from_boxed_compat(e.into()))
|
|
||||||
{
|
|
||||||
self.fail_with(e.into());
|
|
||||||
}
|
}
|
||||||
match self
|
|
||||||
.svc
|
let rsp = match self.svc.call(req).await {
|
||||||
.call(req)
|
Err(e) => {
|
||||||
.await
|
if e.is::<Overloaded>() {
|
||||||
.map_err(|e| Error::from_boxed_compat(e.into()))
|
self.fail_with(PeerError::Overloaded);
|
||||||
{
|
} else {
|
||||||
Err(e) => self.fail_with(e.into()),
|
// We could send a reject to the remote peer.
|
||||||
Ok(Response::Ok) => { /* generic success, do nothing */ }
|
error!(%e);
|
||||||
Ok(Response::Peers(addrs)) => {
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Ok(rsp) => rsp,
|
||||||
|
};
|
||||||
|
|
||||||
|
match rsp {
|
||||||
|
Response::Ok => { /* generic success, do nothing */ }
|
||||||
|
Response::Peers(addrs) => {
|
||||||
if let Err(e) = self.peer_tx.send(Message::Addr(addrs)).await {
|
if let Err(e) = self.peer_tx.send(Message::Addr(addrs)).await {
|
||||||
self.fail_with(e.into().into());
|
self.fail_with(e.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,10 @@ use std::{
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
|
|
||||||
use failure::Error;
|
|
||||||
use tokio::prelude::*;
|
use tokio::prelude::*;
|
||||||
use tower::discover::{Change, Discover};
|
use tower::discover::{Change, Discover};
|
||||||
|
|
||||||
use crate::peer::PeerClient;
|
use crate::peer::{PeerClient, PeerError};
|
||||||
|
|
||||||
/// A [`tower::discover::Discover`] implementation to report new `PeerClient`s.
|
/// A [`tower::discover::Discover`] implementation to report new `PeerClient`s.
|
||||||
///
|
///
|
||||||
|
@ -22,7 +21,7 @@ pub struct PeerDiscover {
|
||||||
impl Discover for PeerDiscover {
|
impl Discover for PeerDiscover {
|
||||||
type Key = SocketAddr;
|
type Key = SocketAddr;
|
||||||
type Service = PeerClient;
|
type Service = PeerClient;
|
||||||
type Error = Error;
|
type Error = PeerError;
|
||||||
|
|
||||||
fn poll_discover(
|
fn poll_discover(
|
||||||
self: Pin<&mut Self>,
|
self: Pin<&mut Self>,
|
||||||
|
|
|
@ -5,12 +5,13 @@ use std::io::{Cursor, Read, Write};
|
||||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
use chrono::{TimeZone, Utc};
|
use chrono::{TimeZone, Utc};
|
||||||
use failure::Error;
|
|
||||||
use tokio::codec::{Decoder, Encoder};
|
use tokio::codec::{Decoder, Encoder};
|
||||||
|
|
||||||
use zebra_chain::{
|
use zebra_chain::{
|
||||||
block::{Block, BlockHeader, BlockHeaderHash},
|
block::{Block, BlockHeader, BlockHeaderHash},
|
||||||
serialization::{ReadZcashExt, WriteZcashExt, ZcashDeserialize, ZcashSerialize},
|
serialization::{
|
||||||
|
ReadZcashExt, SerializationError as Error, WriteZcashExt, ZcashDeserialize, ZcashSerialize,
|
||||||
|
},
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
types::{BlockHeight, Sha256dChecksum},
|
types::{BlockHeight, Sha256dChecksum},
|
||||||
};
|
};
|
||||||
|
@ -273,7 +274,7 @@ impl Codec {
|
||||||
// FilterAdd => {}
|
// FilterAdd => {}
|
||||||
// FilterClear => {}
|
// FilterClear => {}
|
||||||
// MerkleBlock => {}
|
// MerkleBlock => {}
|
||||||
_ => bail!("unimplemented message type"),
|
_ => return Err(Error::Parse("unimplemented message type")),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -296,6 +297,7 @@ impl Decoder for Codec {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||||
|
use Error::Parse;
|
||||||
match self.state {
|
match self.state {
|
||||||
DecodeState::Head => {
|
DecodeState::Head => {
|
||||||
// First check that the src buffer contains an entire header.
|
// First check that the src buffer contains an entire header.
|
||||||
|
@ -316,14 +318,12 @@ impl Decoder for Codec {
|
||||||
let checksum = Sha256dChecksum(header_reader.read_4_bytes()?);
|
let checksum = Sha256dChecksum(header_reader.read_4_bytes()?);
|
||||||
trace!(?self.state, ?magic, ?command, body_len, ?checksum, "read header from src buffer");
|
trace!(?self.state, ?magic, ?command, body_len, ?checksum, "read header from src buffer");
|
||||||
|
|
||||||
ensure!(
|
if magic != self.builder.network.magic() {
|
||||||
magic == self.builder.network.magic(),
|
return Err(Parse("supplied magic did not meet expectations"));
|
||||||
"supplied magic did not meet expectations"
|
}
|
||||||
);
|
if body_len >= self.builder.max_len {
|
||||||
ensure!(
|
return Err(Parse("body length exceeded maximum size"));
|
||||||
body_len < self.builder.max_len,
|
}
|
||||||
"body length exceeded maximum size",
|
|
||||||
);
|
|
||||||
|
|
||||||
// Reserve buffer space for the expected body and the following header.
|
// Reserve buffer space for the expected body and the following header.
|
||||||
src.reserve(body_len + HEADER_LEN);
|
src.reserve(body_len + HEADER_LEN);
|
||||||
|
@ -354,10 +354,11 @@ impl Decoder for Codec {
|
||||||
let body = src.split_to(body_len);
|
let body = src.split_to(body_len);
|
||||||
self.state = DecodeState::Head;
|
self.state = DecodeState::Head;
|
||||||
|
|
||||||
ensure!(
|
if checksum != Sha256dChecksum::from(&body[..]) {
|
||||||
checksum == Sha256dChecksum::from(&body[..]),
|
return Err(Parse(
|
||||||
"supplied message checksum does not match computed checksum"
|
"supplied message checksum does not match computed checksum",
|
||||||
);
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let body_reader = Cursor::new(&body);
|
let body_reader = Cursor::new(&body);
|
||||||
match &command {
|
match &command {
|
||||||
|
@ -381,7 +382,7 @@ impl Decoder for Codec {
|
||||||
b"filteradd\0\0\0" => self.read_filteradd(body_reader),
|
b"filteradd\0\0\0" => self.read_filteradd(body_reader),
|
||||||
b"filterclear\0" => self.read_filterclear(body_reader),
|
b"filterclear\0" => self.read_filterclear(body_reader),
|
||||||
b"merkleblock\0" => self.read_merkleblock(body_reader),
|
b"merkleblock\0" => self.read_merkleblock(body_reader),
|
||||||
_ => bail!("unknown command"),
|
_ => return Err(Parse("unknown command")),
|
||||||
}
|
}
|
||||||
// We need Ok(Some(msg)) to signal that we're done decoding.
|
// We need Ok(Some(msg)) to signal that we're done decoding.
|
||||||
// This is also convenient for tracing the parse result.
|
// This is also convenient for tracing the parse result.
|
||||||
|
@ -415,7 +416,7 @@ impl Codec {
|
||||||
relay: match reader.read_u8()? {
|
relay: match reader.read_u8()? {
|
||||||
0 => false,
|
0 => false,
|
||||||
1 => true,
|
1 => true,
|
||||||
_ => bail!("non-bool value supplied in relay field"),
|
_ => return Err(Error::Parse("non-bool value supplied in relay field")),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -433,8 +434,7 @@ impl Codec {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_reject<R: Read>(&self, mut _reader: R) -> Result<Message, Error> {
|
fn read_reject<R: Read>(&self, mut _reader: R) -> Result<Message, Error> {
|
||||||
trace!("reject");
|
return Err(Error::Parse("reject messages are not implemented"));
|
||||||
bail!("unimplemented message type")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_addr<R: Read>(&self, mut reader: R) -> Result<Message, Error> {
|
fn read_addr<R: Read>(&self, mut reader: R) -> Result<Message, Error> {
|
||||||
|
@ -544,28 +544,23 @@ impl Codec {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_mempool<R: Read>(&self, mut _reader: R) -> Result<Message, Error> {
|
fn read_mempool<R: Read>(&self, mut _reader: R) -> Result<Message, Error> {
|
||||||
trace!("mempool");
|
return Err(Error::Parse("mempool messages are not implemented"));
|
||||||
bail!("unimplemented message type")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_filterload<R: Read>(&self, mut _reader: R) -> Result<Message, Error> {
|
fn read_filterload<R: Read>(&self, mut _reader: R) -> Result<Message, Error> {
|
||||||
trace!("filterload");
|
return Err(Error::Parse("filterload messages are not implemented"));
|
||||||
bail!("unimplemented message type")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_filteradd<R: Read>(&self, mut _reader: R) -> Result<Message, Error> {
|
fn read_filteradd<R: Read>(&self, mut _reader: R) -> Result<Message, Error> {
|
||||||
trace!("filteradd");
|
return Err(Error::Parse("filteradd messages are not implemented"));
|
||||||
bail!("unimplemented message type")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_filterclear<R: Read>(&self, mut _reader: R) -> Result<Message, Error> {
|
fn read_filterclear<R: Read>(&self, mut _reader: R) -> Result<Message, Error> {
|
||||||
trace!("filterclear");
|
return Err(Error::Parse("filterclear messages are not implemented"));
|
||||||
bail!("unimplemented message type")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_merkleblock<R: Read>(&self, mut _reader: R) -> Result<Message, Error> {
|
fn read_merkleblock<R: Read>(&self, mut _reader: R) -> Result<Message, Error> {
|
||||||
trace!("merkleblock");
|
return Err(Error::Parse("merkleblock messages are not implemented"));
|
||||||
bail!("unimplemented message type")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,7 @@ impl ZcashDeserialize for InventoryHash {
|
||||||
1 => Ok(InventoryHash::Tx(TransactionHash(bytes))),
|
1 => Ok(InventoryHash::Tx(TransactionHash(bytes))),
|
||||||
2 => Ok(InventoryHash::Block(BlockHeaderHash(bytes))),
|
2 => Ok(InventoryHash::Block(BlockHeaderHash(bytes))),
|
||||||
3 => Ok(InventoryHash::FilteredBlock(BlockHeaderHash(bytes))),
|
3 => Ok(InventoryHash::FilteredBlock(BlockHeaderHash(bytes))),
|
||||||
_ => Err(SerializationError::ParseError("invalid inventory code")),
|
_ => Err(SerializationError::Parse("invalid inventory code")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue