p2p: Implement `encoding` traits for `ServiceFlags`
What changed, and why it matters
This commit adds standard encoding and decoding support for ServiceFlags, a data type used in Bitcoin peer-to-peer messages. It is a routine feature addition with no visible security bug. The change simply lets the library serialize and deserialize ServiceFlags using a new internal encoding framework.
No security action required. Review as normal code-quality/feature work if desired.
Security signals we found
No security-relevant signals detected in the diff
New serialization trait implementation only
Error type uses Infallible conversion, consistent with infallible inner decoder
Evidence from the diff
The patch implements the new encoding::Encodable and encoding::Decodable traits for ServiceFlags in p2p/src/lib.rs. It introduces ServiceFlagsEncoder, ServiceFlagsDecoder, and ServiceFlagsDecoderError, delegating to an 8-byte array encoder/decoder and treating the payload as little-endian u64. The implementation mirrors the existing bitcoin::consensus::encode behavior. No unsafe code, no input validation changes, and no resource limits are altered beyond what the underlying ArrayDecoder<8> enforces.
Changed components
p2p/src/lib.rsServiceFlags encoding/decoding traitsInspect captured patch +72 / −1
diff --git a/p2p/src/lib.rs b/p2p/src/lib.rs
index 2d51fb45..81291294 100644
--- a/p2p/src/lib.rs
+++ b/p2p/src/lib.rs
@@ -30,6 +30,7 @@ extern crate std;
use alloc::borrow::ToOwned;
use alloc::string::String;
use core::borrow::{Borrow, BorrowMut};
+use core::convert::Infallible;
use core::str::FromStr;
use core::{fmt, ops};
@@ -38,7 +39,7 @@ use arbitrary::{Arbitrary, Unstructured};
use bitcoin::consensus::encode::{self, Decodable, Encodable};
use bitcoin::network::{Network, Params, TestnetVersion};
use hex::FromHex;
-use internals::impl_to_hex_from_lower_hex;
+use internals::{impl_to_hex_from_lower_hex, write_err};
use io::{BufRead, Write};
#[rustfmt::skip]
@@ -266,6 +267,76 @@ impl Decodable for ServiceFlags {
Ok(Self(Decodable::consensus_decode(r)?))
}
}
+
+encoding::encoder_newtype! {
+ /// The encoder for the [`ServiceFlags`] type.
+ pub struct ServiceFlagsEncoder(encoding::ArrayEncoder<8>);
+}
+
+impl encoding::Encodable for ServiceFlags {
+ type Encoder<'e> = ServiceFlagsEncoder;
+ fn encoder(&self) -> Self::Encoder<'_> {
+ ServiceFlagsEncoder(encoding::ArrayEncoder::without_length_prefix(
+ self.0.to_le_bytes(),
+ ))
+ }
+}
+
+/// The decoder for the [`ServiceFlags`] type.
+pub struct ServiceFlagsDecoder(encoding::ArrayDecoder<8>);
+
+impl ServiceFlagsDecoder {
+ /// Constructs a new [`ServiceFlags`] decoder.
+ pub const fn new() -> Self { Self(encoding::ArrayDecoder::new()) }
+}
+
+impl Default for ServiceFlagsDecoder {
+ fn default() -> Self { Self::new() }
+}
+
+impl encoding::Decoder for ServiceFlagsDecoder {
+ type Output = ServiceFlags;
+ type Error = ServiceFlagsDecoderError;
+
+ #[inline]
+ fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
+ Ok(self.0.push_bytes(bytes).map_err(ServiceFlagsDecoderError)?)
+ }
+
+ #[inline]
+ fn end(self) -> Result<Self::Output, Self::Error> {
+ let n = u64::from_le_bytes(self.0.end().map_err(ServiceFlagsDecoderError)?);
+ Ok(ServiceFlags(n))
+ }
+
+ #[inline]
+ fn read_limit(&self) -> usize { self.0.read_limit() }
+}
+
+impl encoding::Decodable for ServiceFlags {
+ type Decoder = ServiceFlagsDecoder;
+ fn decoder() -> Self::Decoder { ServiceFlagsDecoder(encoding::ArrayDecoder::<8>::new()) }
+}
+
+/// An error consensus decoding an `ServiceFlags`.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct ServiceFlagsDecoderError(<encoding::ArrayDecoder<8> as encoding::Decoder>::Error);
+
+impl From<Infallible> for ServiceFlagsDecoderError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
+impl fmt::Display for ServiceFlagsDecoderError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write_err!(f, "serviceflags error"; self.0)
+ }
+}
+
+#[cfg(feature = "std")]
+impl std::error::Error for ServiceFlagsDecoderError {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.0) }
+}
+
/// Network magic bytes to identify the cryptocurrency network the message was intended for.
#[derive(Copy, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
pub struct Magic([u8; 4]);
Why this scored 18/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.