p2p: Implement `encoding` traits for `FilterHash/Header`
What changed, and why it matters
This commit adds standard encoding and decoding support for two existing hash-like types (FilterHash and FilterHeader) used in Bitcoin peer-to-peer messages. It is a routine feature addition that wires up serialization traits; there is no indication it fixes a vulnerability or introduces unsafe behavior.
No security action required; review as normal code-quality/feature addition.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change implements the project’s Encodable/Decodable traits for FilterHash and FilterHeader in p2p/src/message_filter.rs. It adds thin newtype encoders/decoders wrapping a fixed 32-byte array encoder/decoder, plus trivial error types. The implementation delegates directly to the existing ArrayEncoder<32>/ArrayDecoder<32> and uses to_byte_array/from_byte_array conversions. No bounds, length-prefix handling, or parsing logic changes are visible beyond the new trait implementations.
Changed components
p2p/src/message_filter.rsFilterHashFilterHeaderInspect captured patch +126 / −0
diff --git a/p2p/src/message_filter.rs b/p2p/src/message_filter.rs
index 06f1d582..1e38c33a 100644
--- a/p2p/src/message_filter.rs
+++ b/p2p/src/message_filter.rs
@@ -57,6 +57,132 @@ macro_rules! impl_hashencode {
impl_hashencode!(FilterHash);
impl_hashencode!(FilterHeader);
+encoding::encoder_newtype! {
+ /// Encoder type for [`FilterHash`].
+ pub struct FilterHashEncoder(ArrayEncoder<32>);
+}
+
+impl encoding::Encodable for FilterHash {
+ type Encoder<'e> = FilterHashEncoder;
+
+ fn encoder(&self) -> Self::Encoder<'_> {
+ FilterHashEncoder(ArrayEncoder::without_length_prefix(self.to_byte_array()))
+ }
+}
+
+encoding::encoder_newtype! {
+ /// Encoder type for [`FilterHeader`].
+ pub struct FilterHeaderEncoder(ArrayEncoder<32>);
+}
+
+impl encoding::Encodable for FilterHeader {
+ type Encoder<'e> = FilterHeaderEncoder;
+
+ fn encoder(&self) -> Self::Encoder<'_> {
+ FilterHeaderEncoder(ArrayEncoder::without_length_prefix(self.to_byte_array()))
+ }
+}
+
+type HashInnerDecoder = ArrayDecoder<32>;
+
+/// Decoder for the [`FilterHash`] type.
+pub struct FilterHashDecoder(HashInnerDecoder);
+
+impl encoding::Decoder for FilterHashDecoder {
+ type Output = FilterHash;
+ type Error = FilterHashDecoderError;
+
+ #[inline]
+ fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
+ self.0.push_bytes(bytes).map_err(FilterHashDecoderError)
+ }
+
+ #[inline]
+ fn end(self) -> Result<Self::Output, Self::Error> {
+ let arr = self.0.end().map_err(FilterHashDecoderError)?;
+ Ok(FilterHash::from_byte_array(arr))
+ }
+
+ #[inline]
+ fn read_limit(&self) -> usize { self.0.read_limit() }
+}
+
+impl encoding::Decodable for FilterHash {
+ type Decoder = FilterHashDecoder;
+
+ fn decoder() -> Self::Decoder {
+ FilterHashDecoder(ArrayDecoder::new())
+ }
+}
+
+/// Errors occuring when decoding a [`FilterHash`] message.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct FilterHashDecoderError(<HashInnerDecoder as encoding::Decoder>::Error);
+
+impl From<Infallible> for FilterHashDecoderError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
+impl fmt::Display for FilterHashDecoderError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write_err!(f, "filterhash error"; self.0)
+ }
+}
+
+#[cfg(feature = "std")]
+impl std::error::Error for FilterHashDecoderError {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.0) }
+}
+
+/// Decoder for the [`FilterHeader`] type.
+pub struct FilterHeaderDecoder(HashInnerDecoder);
+
+impl encoding::Decoder for FilterHeaderDecoder {
+ type Output = FilterHeader;
+ type Error = FilterHeaderDecoderError;
+
+ #[inline]
+ fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
+ self.0.push_bytes(bytes).map_err(FilterHeaderDecoderError)
+ }
+
+ #[inline]
+ fn end(self) -> Result<Self::Output, Self::Error> {
+ let arr = self.0.end().map_err(FilterHeaderDecoderError)?;
+ Ok(FilterHeader::from_byte_array(arr))
+ }
+
+ #[inline]
+ fn read_limit(&self) -> usize { self.0.read_limit() }
+}
+
+impl encoding::Decodable for FilterHeader {
+ type Decoder = FilterHeaderDecoder;
+
+ fn decoder() -> Self::Decoder {
+ FilterHeaderDecoder(ArrayDecoder::new())
+ }
+}
+
+/// Errors occuring when decoding a [`FilterHash`] message.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct FilterHeaderDecoderError(<HashInnerDecoder as encoding::Decoder>::Error);
+
+impl From<Infallible> for FilterHeaderDecoderError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
+impl fmt::Display for FilterHeaderDecoderError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write_err!(f, "filterheader error"; self.0)
+ }
+}
+
+#[cfg(feature = "std")]
+impl std::error::Error for FilterHeaderDecoderError {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.0) }
+}
+
#[cfg(feature = "arbitrary")]
impl<'a> Arbitrary<'a> for FilterHash {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
Why this scored 15/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.