p2p: Implement `encoding` traits for `GetCFilters`
What changed, and why it matters
This commit adds new Rust code to encode and decode a Bitcoin peer-to-peer message called GetCFilters. It is a routine feature implementation with no visible security bug. The change does not fix a vulnerability, alter existing security behavior, or introduce suspicious logic such as unchecked lengths or unsafe code.
No security action required. Review as normal code quality for the new encoding trait implementation.
Security signals we found
No security signals detected in the diff.
Change is a feature addition, not a security fix.
No unsafe code, no manual buffer arithmetic, no new dependencies, no altered trust boundaries.
Evidence from the diff
The patch implements the project’s new encoding::Encodable/Decodable traits for GetCFilters in p2p/src/message_filter.rs. It adds a generated encoder, a decoder wrapping Decoder3<ArrayDecoder<1>, BlockHeightDecoder, BlockHashDecoder>, a typed error wrapper, and keeps the existing impl_consensus_encoding! macro implementation. No unsafe blocks, no manual length parsing, no resource exhaustion vectors, and no behavioral changes to message handling are present in the diff.
Changed components
p2p/src/message_filter.rsGetCFilters message encoding/decodingInspect captured patch +87 / −2
diff --git a/p2p/src/message_filter.rs b/p2p/src/message_filter.rs
index bed5ea33..e25ffaea 100644
--- a/p2p/src/message_filter.rs
+++ b/p2p/src/message_filter.rs
@@ -5,12 +5,16 @@
//! This module describes BIP-0157 Client Side Block Filtering network messages.
use alloc::vec::Vec;
+use core::convert::Infallible;
+use core::fmt;
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
+use encoding::{ArrayDecoder, ArrayEncoder, Decoder3, Encoder3};
use hashes::{sha256d, HashEngine};
-use primitives::BlockHash;
-use units::BlockHeight;
+use internals::write_err;
+use primitives::{block::{BlockHashDecoder, BlockHashEncoder}, BlockHash};
+use units::{block::{BlockHeightDecoder, BlockHeightEncoder}, BlockHeight};
use crate::consensus::impl_consensus_encoding;
@@ -77,6 +81,87 @@ pub struct GetCFilters {
/// The hash of the last block in the requested range
pub stop_hash: BlockHash,
}
+
+encoding::encoder_newtype! {
+ /// Encoder type for the [`GetCFilters`] message.
+ pub struct GetCFiltersEncoder(Encoder3<ArrayEncoder<1>, BlockHeightEncoder, BlockHashEncoder>);
+}
+
+impl encoding::Encodable for GetCFilters {
+ type Encoder<'e> = GetCFiltersEncoder;
+
+ fn encoder(&self) -> Self::Encoder<'_> {
+ GetCFiltersEncoder(
+ Encoder3::new(
+ ArrayEncoder::without_length_prefix(self.filter_type.to_le_bytes()),
+ self.start_height.encoder(),
+ self.stop_hash.encoder()
+ )
+ )
+ }
+}
+
+type GetCFiltersInnerDecoder = Decoder3<ArrayDecoder<1>, BlockHeightDecoder, BlockHashDecoder>;
+
+/// Decoder type for the [`GetCFilters`] message.
+pub struct GetCFiltersDecoder(GetCFiltersInnerDecoder);
+
+impl encoding::Decoder for GetCFiltersDecoder {
+ type Output = GetCFilters;
+ type Error = GetCFiltersDecoderError;
+
+ #[inline]
+ fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
+ self.0.push_bytes(bytes).map_err(GetCFiltersDecoderError)
+ }
+
+ #[inline]
+ fn end(self) -> Result<Self::Output, Self::Error> {
+ let (ty, start_height, stop_hash) = self.0.end().map_err(GetCFiltersDecoderError)?;
+ Ok(GetCFilters {
+ filter_type: u8::from_le_bytes(ty),
+ start_height,
+ stop_hash
+ })
+ }
+
+ #[inline]
+ fn read_limit(&self) -> usize { self.0.read_limit() }
+}
+
+impl encoding::Decodable for GetCFilters {
+ type Decoder = GetCFiltersDecoder;
+
+ fn decoder() -> Self::Decoder {
+ GetCFiltersDecoder(
+ Decoder3::new(
+ ArrayDecoder::new(),
+ BlockHeightDecoder::new(),
+ BlockHashDecoder::new()
+ )
+ )
+ }
+}
+
+/// Errors occuring when decoding a [`GetCFilters`] message.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct GetCFiltersDecoderError(<GetCFiltersInnerDecoder as encoding::Decoder>::Error);
+
+impl From<Infallible> for GetCFiltersDecoderError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
+impl fmt::Display for GetCFiltersDecoderError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write_err!(f, "getcfilters error"; self.0)
+ }
+}
+
+#[cfg(feature = "std")]
+impl std::error::Error for GetCFiltersDecoderError {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.0) }
+}
+
impl_consensus_encoding!(GetCFilters, filter_type, start_height, stop_hash);
/// cfilter message
Why this scored 12/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.