diff --git a/protocol/Cargo.toml b/protocol/Cargo.toml index 1d21473..a8e4343 100644 --- a/protocol/Cargo.toml +++ b/protocol/Cargo.toml @@ -11,14 +11,11 @@ rust-version = "1.63.0" [features] default = ["std"] -# High-level wrappers using futures traits. -futures = ["std", "dep:futures"] # High-level wrappers using tokio traits - may affect MSRV requirements. tokio = ["std", "dep:tokio"] std = ["bitcoin/std", "bitcoin_hashes/std", "chacha20-poly1305/std", "rand/std", "rand/std_rng"] [dependencies] -futures = { version = "0.3.30", default-features = false, optional = true, features = ["std"] } # The tokio feature may increase the MSRV beyond 1.63.0 # depending on which version of tokio is selected by the caller. tokio = { version = "1", default-features = false, optional = true, features = ["io-util"] } diff --git a/protocol/README.md b/protocol/README.md index 26c0246..d4db084 100644 --- a/protocol/README.md +++ b/protocol/README.md @@ -4,15 +4,14 @@ A BIP324 library to establish and communicate over an encrypted channel. The library is designed with a bare `no_std` and "Sans I/O" interface to keep it as agnostic as possible to application runtimes, but higher level interfaces are exposed for ease of use. -The `futures` feature includes the high-level `AsyncProcotol` type which helps create and manage an encrypted channel. +The `tokio` feature includes the high-level `AsyncProcotol` type which helps create and manage an encrypted channel. The lower-level `CipherSession` and `Handshake` types can be directly used by applications which require more control. The handshake performs the one-and-a-half round trip dance between the peers in order to generate secret materials and verify a channel. A successful handshake results in a cipher session which performs the encrypt and decrypt operations for the lifetime of the channel. ## Feature Flags * `std` -- Standard library dependencies for I/O, memory allocation, and random number generators. -* `futures` -- High level wrappers for asynchronous read and write runtimes using agnostic futures-rs traits. -* `tokio` -- Same wrappers as `futures`, but using the popular tokio runtime's specific traits instead of futures-rs. +* `tokio` -- High level I/O wrappers for asynchronous tokio runtime. ## Minimum Supported Rust Version (MSRV) diff --git a/protocol/src/io.rs b/protocol/src/io.rs index 40d34ab..726bd5d 100644 --- a/protocol/src/io.rs +++ b/protocol/src/io.rs @@ -4,35 +4,31 @@ //! connections over Read/Write transports. use core::fmt; - -#[cfg(feature = "std")] +#[cfg(feature = "tokio")] use std::vec; -#[cfg(feature = "std")] use std::vec::Vec; -use bitcoin::Network; +use crate::{Error, PacketType}; -// Default to the futures-rs traits, but can overwrite with more specific -// tokio implementations for easier caller integration. -#[cfg(all(feature = "futures", not(feature = "tokio")))] -use futures::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; #[cfg(feature = "tokio")] -use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; +use bitcoin::Network; +#[cfg(feature = "tokio")] use crate::{ handshake::{self, GarbageResult, VersionResult}, - Error, Handshake, InboundCipher, OutboundCipher, PacketType, Role, NUM_ELLIGATOR_SWIFT_BYTES, + Handshake, InboundCipher, OutboundCipher, Role, NUM_ELLIGATOR_SWIFT_BYTES, NUM_GARBAGE_TERMINTOR_BYTES, }; +#[cfg(feature = "tokio")] +use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; + /// A decrypted BIP324 payload with its packet type. -#[cfg(feature = "std")] pub struct Payload { contents: Vec, packet_type: PacketType, } -#[cfg(feature = "std")] impl Payload { /// Create a new payload. pub fn new(contents: Vec, packet_type: PacketType) -> Self { @@ -54,7 +50,6 @@ impl Payload { } /// High level error type for the protocol interface. -#[cfg(feature = "std")] #[derive(Debug)] pub enum ProtocolError { /// Wrap all IO errors with suggestion for next step on failure. @@ -64,7 +59,6 @@ pub enum ProtocolError { } /// Suggest to caller next step on protocol failure. -#[cfg(feature = "std")] #[derive(Debug)] pub enum ProtocolFailureSuggestion { /// Caller could attempt to retry the connection with protocol V1 if desired. @@ -73,7 +67,6 @@ pub enum ProtocolFailureSuggestion { Abort, } -#[cfg(feature = "std")] impl From for ProtocolError { fn from(error: std::io::Error) -> Self { // Detect IO errors which possibly mean the remote doesn't understand @@ -94,19 +87,18 @@ impl From for ProtocolError { } } -#[cfg(feature = "std")] impl From for ProtocolError { fn from(error: Error) -> Self { ProtocolError::Internal(error) } } -#[cfg(feature = "std")] impl ProtocolError { /// Create an EOF error that suggests retrying with V1 protocol. /// /// This is used when the remote peer closes the connection during handshake, /// which often indicates they don't support the V2 protocol. + #[cfg(feature = "tokio")] fn eof() -> Self { ProtocolError::Io( std::io::Error::new( @@ -118,7 +110,6 @@ impl ProtocolError { } } -#[cfg(feature = "std")] impl std::error::Error for ProtocolError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { @@ -128,7 +119,6 @@ impl std::error::Error for ProtocolError { } } -#[cfg(feature = "std")] impl fmt::Display for ProtocolError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -149,13 +139,13 @@ impl fmt::Display for ProtocolError { } /// A protocol session with handshake and send/receive packet management. -#[cfg(any(feature = "futures", feature = "tokio"))] +#[cfg(feature = "tokio")] pub struct AsyncProtocol { reader: AsyncProtocolReader, writer: AsyncProtocolWriter, } -#[cfg(any(feature = "futures", feature = "tokio"))] +#[cfg(feature = "tokio")] impl AsyncProtocol { /// New protocol session which completes the initial handshake and returns a handler. /// @@ -297,7 +287,9 @@ impl AsyncProtocol { } /// State machine of an asynchronous packet read. -#[cfg(any(feature = "futures", feature = "tokio"))] +/// +/// This maintains state between await points to ensure cancellation safety. +#[cfg(feature = "tokio")] #[derive(Debug)] enum DecryptState { ReadingLength { @@ -310,7 +302,7 @@ enum DecryptState { }, } -#[cfg(any(feature = "futures", feature = "tokio"))] +#[cfg(feature = "tokio")] impl DecryptState { /// Transition state to reading the length bytes. fn init_reading_length() -> Self { @@ -330,13 +322,13 @@ impl DecryptState { } /// Manages an async buffer to automatically decrypt contents of received packets. -#[cfg(any(feature = "futures", feature = "tokio"))] +#[cfg(feature = "tokio")] pub struct AsyncProtocolReader { inbound_cipher: InboundCipher, state: DecryptState, } -#[cfg(any(feature = "futures", feature = "tokio"))] +#[cfg(feature = "tokio")] impl AsyncProtocolReader { /// Decrypt contents of received packet from buffer. /// @@ -397,12 +389,12 @@ impl AsyncProtocolReader { } /// Manages an async buffer to automatically encrypt and send contents in packets. -#[cfg(any(feature = "futures", feature = "tokio"))] +#[cfg(feature = "tokio")] pub struct AsyncProtocolWriter { outbound_cipher: OutboundCipher, } -#[cfg(any(feature = "futures", feature = "tokio"))] +#[cfg(feature = "tokio")] impl AsyncProtocolWriter { /// Encrypt contents and write packet buffer. /// diff --git a/protocol/src/lib.rs b/protocol/src/lib.rs index 99224c7..6996762 100644 --- a/protocol/src/lib.rs +++ b/protocol/src/lib.rs @@ -23,7 +23,7 @@ extern crate std; mod fschacha20poly1305; mod handshake; -#[cfg(any(feature = "futures", feature = "tokio"))] +#[cfg(feature = "std")] pub mod io; #[cfg(feature = "std")] pub mod serde; @@ -43,14 +43,7 @@ pub use handshake::{ GarbageResult, Handshake, Initialized, ReceivedGarbage, ReceivedKey, SentKey, SentVersion, VersionResult, }; -// Re-exports from io module (async I/O types for backwards compatibility) -#[cfg(any(feature = "futures", feature = "tokio"))] -pub use io::{ - AsyncProtocol, AsyncProtocolReader, AsyncProtocolWriter, Payload, ProtocolError, - ProtocolFailureSuggestion, -}; -// Internal imports use fschacha20poly1305::{FSChaCha20, FSChaCha20Poly1305}; /// Value for header byte with the decoy flag flipped to true. diff --git a/proxy/src/bin/proxy.rs b/proxy/src/bin/proxy.rs index 1320726..2e58aaa 100644 --- a/proxy/src/bin/proxy.rs +++ b/proxy/src/bin/proxy.rs @@ -6,8 +6,9 @@ use std::str::FromStr; use bip324::{ + io::{AsyncProtocol, ProtocolFailureSuggestion}, serde::{deserialize, serialize}, - AsyncProtocol, PacketType, ProtocolFailureSuggestion, Role, + PacketType, Role, }; use bip324_proxy::{V1ProtocolReader, V1ProtocolWriter}; use bitcoin::Network; @@ -87,7 +88,9 @@ async fn v2_proxy( .await { Ok(p) => p, - Err(bip324::ProtocolError::Io(_, ProtocolFailureSuggestion::RetryV1)) if v1_fallback => { + Err(bip324::io::ProtocolError::Io(_, ProtocolFailureSuggestion::RetryV1)) + if v1_fallback => + { info!("V2 protocol failed, falling back to V1..."); return v1_proxy(client, network).await; } diff --git a/proxy/src/lib.rs b/proxy/src/lib.rs index 6f65ec4..cd8fa6f 100644 --- a/proxy/src/lib.rs +++ b/proxy/src/lib.rs @@ -34,7 +34,7 @@ pub enum Error { WrongCommand, Serde, Io(std::io::Error), - Protocol(bip324::ProtocolError), + Protocol(bip324::io::ProtocolError), } impl fmt::Display for Error { @@ -61,8 +61,8 @@ impl std::error::Error for Error { } } -impl From for Error { - fn from(e: bip324::ProtocolError) -> Self { +impl From for Error { + fn from(e: bip324::io::ProtocolError) -> Self { Error::Protocol(e) } }