Skip to content

Commit

Permalink
Clarify crate doc logic
Browse files Browse the repository at this point in the history
  • Loading branch information
rrybarczyk committed Oct 8, 2024
1 parent d8e9138 commit e63c1a7
Show file tree
Hide file tree
Showing 4 changed files with 378 additions and 160 deletions.
234 changes: 167 additions & 67 deletions protocols/v2/codec-sv2/src/decoder.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,27 @@
//! # Decoder
//!
//! Provides functionality for decoding Stratum V2 messages, including support for
//! the Noise protocol for secure communication.
//! Provides utilities for decoding messages held by Sv2 frames, with or without Noise protocol
//! support.
//!
//! It includes primitives to both decode encoded standard Sv2 frames and to decrypt
//! and decode Noise-encrypted encoded Sv2 frames, ensuring secure communication when required.
//!
//! ## Usage
//! All messages passed between Sv2 roles are encoded as Sv2 frames. These frames are decoded using
//! primitives in this module. There are two types of decoders for reading these frames: one for
//! regular Sv2 frames (`StandardDecoder`), and another for Noise-encrypted frames
//! (`StandardNoiseDecoder`). Both decoders manage the deserialization of incoming data and, when
//! applicable, the decryption of the data upon receiving the transmitted message.
//!
//! ### Buffer Management
//!
//! The decoders rely on buffers to hold intermediate data during the decoding process.
//!
//! This module is designed to be used to decode incoming Sv2 messages, with optional Noise
//! protocol encryption support for secure communication.
//! - When the `with_buffer_pool` feature is enabled, the internal `Buffer` type is backed by a
//! pool-allocated buffer (`binary_sv2::BufferPool`), providing more efficient memory usage,
//! particularly in high-throughput scenarios.
//! - If this feature is not enabled, a system memory buffer (`binary_sv2::BufferFromSystemMemory`)
//! is used for simpler applications where memory efficiency is less critical.

#[cfg(feature = "noise_sv2")]
use binary_sv2::Deserialize;
Expand All @@ -28,6 +43,14 @@ use framing_sv2::{
#[cfg(feature = "noise_sv2")]
use noise_sv2::NoiseCodec;

#[cfg(feature = "noise_sv2")]
use crate::error::Error;
use crate::error::Result;

use crate::Error::MissingBytes;
#[cfg(feature = "noise_sv2")]
use crate::State;

#[cfg(not(feature = "with_buffer_pool"))]
use buffer_sv2::{Buffer as IsBuffer, BufferFromSystemMemory as Buffer};

Expand All @@ -44,58 +67,77 @@ use buffer_sv2::{Buffer as IsBuffer, BufferFromSystemMemory, BufferPool};
#[cfg(feature = "with_buffer_pool")]
type Buffer = BufferPool<BufferFromSystemMemory>;

#[cfg(feature = "noise_sv2")]
use crate::error::Error;
use crate::error::Result;

use crate::Error::MissingBytes;
#[cfg(feature = "noise_sv2")]
use crate::State;

/// An encoded or decoded frame that could either be a regular or Noise-protected frame.
/// An encoded or decoded Sv2 frame containing either a regular or Noise-protected message.
///
/// A wrapper around the `binary_sv2::Frame` enum that represents either a regular or
/// Noise-protected Sv2 frame containing the generic message type (`T`).
pub type StandardEitherFrame<T> = Frame<T, <Buffer as IsBuffer>::Slice>;

/// An encoded or decoded Sv2 frame.
///
/// A wrapper around the `binary_sv2::Sv2Frame` that represents a regular Sv2 frame containing the
/// generic message type (`T`).
pub type StandardSv2Frame<T> = Sv2Frame<T, <Buffer as IsBuffer>::Slice>;

/// Standard decoder with Noise protocol support.
/// Standard Sv2 decoder with Noise protocol support.
///
/// Used for decoding and decrypting generic message types (`T`) encoded in Sv2 frames and
/// encrypted via the Noise protocol.
#[cfg(feature = "noise_sv2")]
pub type StandardNoiseDecoder<T> = WithNoise<Buffer, T>;

/// Standard Sv2 decoder without Noise protocol support.
///
/// Used for decoding generic message types (`T`) encoded in Sv2 frames.
pub type StandardDecoder<T> = WithoutNoise<Buffer, T>;

/// Decoder for Sv2 frames with Noise protocol support.
///
/// Accumulates the encrypted data into a dedicated buffer until the entire encrypted frame is
/// received. The Noise protocol is then used to decrypt the accumulated data into another
/// dedicated buffer, converting it back into its original serialized form. This decrypted data is
/// then deserialized into the original Sv2 frame and message format.
#[cfg(feature = "noise_sv2")]
#[derive(Debug)]
pub struct WithNoise<B: IsBuffer, T: Serialize + binary_sv2::GetSize> {
// Used to maintain type information for the generic parameter `T`, which represents the type
// of frames being decoded.
// Marker for the type of frame being decoded.
//
// `T` refers to a type that implements the necessary traits for serialization
// Used to maintain the generic type (`T`) information of the message payload held by the
// frame. `T` refers to a type that implements the necessary traits for serialization
// (`binary_sv2::Serialize`) and size calculation (`binary_sv2::GetSize`).
frame: PhantomData<T>,

// Number of missing bytes needed to complete the Noise header or payload.
// Tracks the number of bytes remaining until the full frame is received.
//
// Keeps track of how many more bytes are required to fully decode the current Noise frame.
// Ensures that the full encrypted Noise frame has been received by keeping track of the
// remaining bytes. Once the complete frame is received, decoding can proceed.
missing_noise_b: usize,

// Buffer for holding encrypted Noise data.
// Buffer for holding incoming encrypted Noise data to be decrypted.
//
// Stores the incoming encrypted data until it is ready to be decrypted and processed.
// Stores the incoming encrypted data, allowing the decoder to accumulate the necessary bytes
// for full decryption. Once the entire encrypted frame is received, the decoder processes the
// buffer to extract the underlying frame.
noise_buffer: B,

// Buffer for holding decrypted Sv2 data.
// Buffer for holding decrypted data to be decoded.
//
// Stores the decrypted data from the Noise protocol until it is ready to be processed and
// concerted into a Sv2 frame.
// Stores the decrypted data until it is ready to be processed and converted into a Sv2 frame.
sv2_buffer: B,
}

#[cfg(feature = "noise_sv2")]
impl<'a, T: Serialize + GetSize + Deserialize<'a>, B: IsBuffer + AeadBuffer> WithNoise<B, T> {
/// Attempts to decode the next frame, returning either a frame or an error indicating how many
/// bytes are missing.
/// Attempts to decode the next Noise encrypted frame.
///
/// On success, the decoded and decrypted frame is returned. Otherwise, an error indicating the
/// number of missing bytes required to complete the encoded frame, an error on a badly
/// formatted message header, or an error on decryption failure is returned.
///
/// In this case of the `Error::MissingBytes`, the user should resize the decoder buffer using
/// `writable`, read another chunk from the incoming message stream, and then call `next_frame`
/// again. This process should be repeated until `next_frame` returns `Ok`, indicating that the
/// full message has been received, and the decoding and decryption of the frame can proceed.
#[inline]
pub fn next_frame(&mut self, state: &mut State) -> Result<Frame<T, B::Slice>> {
match state {
Expand Down Expand Up @@ -142,11 +184,59 @@ impl<'a, T: Serialize + GetSize + Deserialize<'a>, B: IsBuffer + AeadBuffer> Wit
}
}

// Decodes a Noise-encrypted frame.
/// Provides a writable buffer for receiving incoming Noise-encrypted Sv2 data.
///
/// This buffer is used to store incoming data, and its size is adjusted based on the number
/// of missing bytes. As new data is read, it is written into this buffer until enough data has
/// been received to fully decode a frame. The buffer must have the correct number of bytes
/// available to progress to the decoding process.
#[inline]
pub fn writable(&mut self) -> &mut [u8] {
self.noise_buffer.get_writable(self.missing_noise_b)
}

/// Determines whether the decoder's internal buffers can be safely dropped.
///
/// For more information, refer to the [`buffer_sv2`
/// crate](https://docs.rs/buffer_sv2/1.1.0/buffer_sv2/).
pub fn droppable(&self) -> bool {
self.noise_buffer.is_droppable() && self.sv2_buffer.is_droppable()
}

// Processes and decodes a Sv2 frame during the Noise protocol handshake phase.
//
// Handles the decoding of a handshake frame from the `noise_buffer`. It converts the received
// data into a `HandShakeFrame` and encapsulates it into a `Frame` for further processing by
// the codec.
//
// Handles the decryption of Noise-encrypted frames, including both the header and the payload.
// It processes the frame in chunks if necessary, ensuring that all encrypted data is properly
// decrypted and converted into a usable frame.
// This is used exclusively during the initial handshake phase of the Noise protocol, before
// transitioning to regular frame encryption and decryption.
fn while_handshaking(&mut self) -> Frame<T, B::Slice> {
let src = self.noise_buffer.get_data_owned().as_mut().to_vec();

// Since the frame length is already validated during the handshake process, this
// operation is infallible
let frame = HandShakeFrame::from_bytes_unchecked(src.into());

frame.into()
}

// Decodes a Noise-encrypted Sv2 frame, handling both the message header and payload
// decryption.
//
// On success, the decoded frame is returned. Otherwise, an error indicating the number of
// missing bytes required to complete the encoded frame, an error on a badly formatted message
// header, or a decryption failure error is returned.
//
// Processes Noise-encrypted Sv2 frames by first decrypting the header, followed by the
// payload. If the frame's data is received in chunks, it ensures that decryption occurs
// incrementally as more encrypted data becomes available. The decrypted data is then stored in
// the `sv2_buffer`, from which the resulting Sv2 frame is extracted and returned.
//
// If there are still bytes missing to complete the frame, the function will return an
// `Error::MissingBytes` with the number of additional bytes required to fully decrypt the
// frame. Once all bytes are available, the decryption process completes and the frame can be
// successfully decoded.
#[inline]
fn decode_noise_frame(&mut self, noise_codec: &mut NoiseCodec) -> Result<Frame<T, B::Slice>> {
match (
Expand Down Expand Up @@ -195,34 +285,6 @@ impl<'a, T: Serialize + GetSize + Deserialize<'a>, B: IsBuffer + AeadBuffer> Wit
}
}
}

// Processes and decodes frames during the handshake phase of the Noise protocol.
//
// Used while the codec is in the handshake phase of the Noise protocol. It decodes a received
// handshake frame from the `noise_buffer`, converting it into a `HandShakeFrame`, and then
// encapsulates it a `Frame`, marking it as ready for further processing by the codec.
fn while_handshaking(&mut self) -> Frame<T, B::Slice> {
let src = self.noise_buffer.get_data_owned().as_mut().to_vec();

// below is inffalible as noise frame length has been already checked
let frame = HandShakeFrame::from_bytes_unchecked(src.into());

frame.into()
}

/// Provides a writable buffer for incoming data.
///
/// The buffer must have the correct number of bytes required by the codec to progress to the
/// next step.
#[inline]
pub fn writable(&mut self) -> &mut [u8] {
self.noise_buffer.get_writable(self.missing_noise_b)
}

/// Determines whether the decoder's internal buffers can be safely dropped.
pub fn droppable(&self) -> bool {
self.noise_buffer.is_droppable() && self.sv2_buffer.is_droppable()
}
}

#[cfg(feature = "noise_sv2")]
Expand All @@ -248,30 +310,46 @@ impl<T: Serialize + binary_sv2::GetSize> Default for WithNoise<Buffer, T> {
}
}

/// Decoder for Sv2 frames without Noise protocol support.
/// Decoder for standard Sv2 frames.
///
/// Accumulates the data into a dedicated buffer until the entire Sv2 frame is received. This data
/// is then deserialized into the original Sv2 frame and message format.
#[derive(Debug)]
pub struct WithoutNoise<B: IsBuffer, T: Serialize + binary_sv2::GetSize> {
// Marker for the type of frame being decoded.
//
// Used to maintain type information for the generic parameter `T` which represents the type of
// frames being decoded. `T` refers to a type that implements the necessary traits for
// serialization (`binary_sv2::Serialize`) and size calculation (`binary_sv2::GetSize`).
// Used to maintain the generic type (`T`) information of the message payload held by the
// frame. `T` refers to a type that implements the necessary traits for serialization
// (`binary_sv2::Serialize`) and size calculation (`binary_sv2::GetSize`).
frame: PhantomData<T>,

// Number of missing bytes needed to complete the frame.
// Tracks the number of bytes remaining until the full frame is received.
//
// Keeps track of how many more bytes are required to fully decode the current frame.
// Ensures that the full Sv2 frame has been received by keeping track of the remaining bytes.
// Once the complete frame is received, decoding can proceed.
missing_b: usize,

// Buffer for holding data to be decoded.
// Buffer for holding incoming data to be decoded into a Sv2 frame.
//
// Stores incoming data until it is ready to be processed and converted into a Sv2 frame.
// This buffer stores incoming data as it is received, allowing the decoder to accumulate the
// necessary bytes until a full frame is available. Once the full encoded frame has been
// received, the buffer's contents are processed and decoded into an Sv2 frame.
buffer: B,
}

impl<T: Serialize + binary_sv2::GetSize, B: IsBuffer> WithoutNoise<B, T> {
/// Attempts to decode the next frame, returning either a frame or an error indicating how many
/// bytes are missing.
///
/// Attempts to decode the next Sv2 frame.
///
/// On success, the decoded frame is returned. Otherwise, an error indicating the number of
/// missing bytes required to complete the frame is returned.
///
/// In the case of `Error::MissingBytes`, the user should resize the decoder buffer using
/// `writable`, read another chunk from the incoming message stream, and then call `next_frame`
/// again. This process should be repeated until `next_frame` returns `Ok`, indicating that the
/// full message has been received, and the frame can be fully decoded.
#[inline]
pub fn next_frame(&mut self) -> Result<Sv2Frame<T, B::Slice>> {
let len = self.buffer.len();
Expand All @@ -292,7 +370,12 @@ impl<T: Serialize + binary_sv2::GetSize, B: IsBuffer> WithoutNoise<B, T> {
}
}

/// Provides a writable buffer for incoming data.
/// Provides a writable buffer for receiving incoming Sv2 data.
///
/// This buffer is used to store incoming data, and its size is adjusted based on the number of
/// missing bytes. As new data is read, it is written into this buffer until enough data has
/// been received to fully decode a frame. The buffer must have the correct number of bytes
/// available to progress to the decoding process.
pub fn writable(&mut self) -> &mut [u8] {
self.buffer.get_writable(self.missing_b)
}
Expand All @@ -317,3 +400,20 @@ impl<T: Serialize + binary_sv2::GetSize> Default for WithoutNoise<Buffer, T> {
Self::new()
}
}

#[cfg(test)]
mod tests {
use super::*;
use binary_sv2::{binary_codec_sv2, Serialize};

#[derive(Serialize)]
pub struct TestMessage {}

#[test]
fn unencrypted_writable_with_missing_b_initialized_as_header_size() {
let mut decoder = StandardDecoder::<TestMessage>::new();
let actual = decoder.writable();
let expect = [0u8; Header::SIZE];
assert_eq!(actual, expect);
}
}
Loading

0 comments on commit e63c1a7

Please sign in to comment.