p2p: Implement `encoding` traits for `Alert`
What changed, and why it matters
This commit adds standard encoding and decoding traits for the legacy Bitcoin P2P `Alert` message type in the rust-bitcoin library. It is a routine feature implementation that lets `Alert` be serialized and deserialized like other network messages. There is no indication in the commit or supplied references that this fixes a security bug or introduces a vulnerability.
No security action required. Treat as a normal feature/refactor commit. If reviewing further, verify that `ByteVecDecoder` enforces reasonable maximum read limits for variable-length byte vectors to avoid unbounded allocation when decoding untrusted `Alert` payloads.
Security signals we found
No security-relevant keywords in commit title or message.
No bounds, allocation, or panic fixes visible in the diff.
Decoder uses existing `ByteVecDecoder`, which presumably enforces its own read limits; no new limit logic introduced.
No references to CVEs, advisories, security reports, or researcher attribution in the commit.
Evidence from the diff
The change implements the project’s encoding::Encodable and encoding::Decodable traits for the Alert newtype wrapper around a byte vector. It adds an encoder that writes the payload length as a compact size followed by the raw bytes, plus a decoder that reads a length-prefixed byte vector and wraps it in Alert. The implementation delegates to existing generic encoders/decoders (Encoder2, CompactSizeEncoder, BytesEncoder, ByteVecDecoder) and includes a typed error wrapper. The Alert type itself was already present; this commit only wires it into the new encoding framework.
Changed components
rust-bitcoin p2p modulep2p/src/message_network.rsAlert network message typeInspect captured patch +72 / −0
diff --git a/p2p/src/message_network.rs b/p2p/src/message_network.rs
index 7ff700f1..ec960198 100644
--- a/p2p/src/message_network.rs
+++ b/p2p/src/message_network.rs
@@ -9,11 +9,15 @@ use alloc::borrow::Cow;
use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
+use core::convert::Infallible;
+use core::fmt;
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
use bitcoin::consensus::{encode, Decodable, Encodable, ReadExt, WriteExt};
+use encoding::{ByteVecDecoder, BytesEncoder, CompactSizeEncoder, Encoder2};
use hashes::sha256d;
+use internals::write_err;
use io::{BufRead, Write};
use crate::address::Address;
@@ -330,6 +334,74 @@ impl Alert {
pub fn is_final_alert(&self) -> bool { self.0.eq(&Self::FINAL_ALERT) }
}
+encoding::encoder_newtype! {
+ /// The encoder type for an [`Alert`] message.
+ pub struct AlertEncoder<'e>(Encoder2<CompactSizeEncoder, BytesEncoder<'e>>);
+}
+
+impl encoding::Encodable for Alert {
+ type Encoder<'e> = AlertEncoder<'e>;
+
+ fn encoder(&self) -> Self::Encoder<'_> {
+ AlertEncoder(
+ Encoder2::new(
+ CompactSizeEncoder::new(self.0.len()),
+ BytesEncoder::without_length_prefix(&self.0)
+ )
+ )
+ }
+}
+
+type AlertInnerDecoder = ByteVecDecoder;
+
+/// The decoder for the [`Alert`] message.
+pub struct AlertDecoder(AlertInnerDecoder);
+
+impl encoding::Decoder for AlertDecoder {
+ type Output = Alert;
+ type Error = AlertDecoderError;
+
+ #[inline]
+ fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
+ self.0.push_bytes(bytes).map_err(AlertDecoderError)
+ }
+
+ #[inline]
+ fn end(self) -> Result<Self::Output, Self::Error> {
+ Ok(Alert(self.0.end().map_err(AlertDecoderError)?))
+ }
+
+ #[inline]
+ fn read_limit(&self) -> usize { self.0.read_limit() }
+}
+
+impl encoding::Decodable for Alert {
+ type Decoder = AlertDecoder;
+
+ fn decoder() -> Self::Decoder {
+ AlertDecoder(AlertInnerDecoder::new())
+ }
+}
+
+/// An error decoding a [`Alert`] message.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct AlertDecoderError(<AlertInnerDecoder as encoding::Decoder>::Error);
+
+impl From<Infallible> for AlertDecoderError {
+ fn from(never: Infallible) -> Self { match never {} }
+}
+
+impl fmt::Display for AlertDecoderError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write_err!(f, "alert error"; self.0)
+ }
+}
+
+#[cfg(feature = "std")]
+impl std::error::Error for AlertDecoderError {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.0) }
+}
+
impl_vec_wrapper!(Alert, Vec<u8>);
#[cfg(feature = "arbitrary")]
Why this scored 17/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.