What changed, and why it matters
This commit adds support for decoding a new Bitcoin peer-to-peer network message called 'sendtxrcncl', which is part of an upcoming feature called Erlay. It does not change any existing behavior; it simply lets the library recognize and parse this message type. There is no indication this introduces a security problem.
No immediate security action required. If desired, consider whether the decoder should reject unknown `version` values explicitly, per BIP-330 expectations, and add tests for malformed/oversized inputs.
Security signals we found
New network message decoder added with no input validation on the version field
Private version field prevents construction of non-version-one values from public API, but decoded values are not restricted
No changes to authentication, consensus, cryptography, or resource limits
Evidence from the diff
The patch introduces a new module p2p/src/message_erlay.rs implementing serialization/deserialization for the BIP-330 sendtxrcncl message (version u32 LE, salt u64 LE). It exposes SendTxRcnCl, encoders/decoders, and an error type, and registers the module in p2p/src/lib.rs. The decoder accepts any 12-byte input and does not validate the version field; the version is kept private and only constructible as version 1 via the public constructor. No existing code paths are modified.
Changed components
rust-bitcoin p2p message parsing libraryp2p/src/message_erlay.rsp2p/src/lib.rsInspect captured patch +118 / −0
diff --git a/p2p/src/lib.rs b/p2p/src/lib.rs
index 90d7e193..70ff5f5f 100644
--- a/p2p/src/lib.rs
+++ b/p2p/src/lib.rs
@@ -20,6 +20,7 @@ pub mod message;
pub mod message_blockdata;
pub mod message_bloom;
pub mod message_compact_blocks;
+pub mod message_erlay;
pub mod message_filter;
#[cfg(feature = "std")]
pub mod message_network;
diff --git a/p2p/src/message_erlay.rs b/p2p/src/message_erlay.rs
new file mode 100644
index 00000000..b3972282
--- /dev/null
+++ b/p2p/src/message_erlay.rs
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: CC0-1.0
+
+//! Messages related to [Erlay transaction announcements](https://github.com/bitcoin/bips/blob/master/bip-0330.mediawiki#user-content-sendtxrcncl).
+
+use encoding::{ArrayDecoder, ArrayEncoder, Decoder2, Encoder2};
+
+use crate::message_erlay::error::SendTxRcnClDecoderError;
+
+/// Announce support for the transaction reconciliation protocol.
+///
+/// Note that this message should be [sent before
+/// verack](https://github.com/bitcoin/bips/blob/master/bip-0330.mediawiki#sendtxrcncl).
+#[derive(PartialEq, Eq, Clone, Debug, Copy, Hash, PartialOrd, Ord)]
+pub struct SendTxRcnCl {
+ // Transaction reconciliation protocol version.
+ version: u32,
+ /// Salt used in short ID computation.
+ pub salt: u64,
+}
+
+impl SendTxRcnCl {
+ /// Version one of the protocol.
+ pub const VERSION_ONE: u32 = 1;
+
+ /// Build a new announcement for erlay with salt.
+ pub fn from_salt(salt: u64) -> Self {
+ Self { version: Self::VERSION_ONE, salt }
+ }
+
+ /// Get the transaction reconciliation protocol version.
+ pub fn version(&self) -> u32 {
+ self.version
+ }
+}
+
+encoding::encoder_newtype_exact! {
+ /// The encoder for a [`SendTxRcnCl`] message.
+ #[derive(Debug, Clone)]
+ pub struct SendTxRcnClEncoder<'e>(Encoder2<ArrayEncoder<4>, ArrayEncoder<8>>);
+}
+
+impl encoding::Encode for SendTxRcnCl {
+ type Encoder<'e>
+ = SendTxRcnClEncoder<'e>
+ where
+ Self: 'e;
+
+ fn encoder(&self) -> Self::Encoder<'_> {
+ SendTxRcnClEncoder::new(Encoder2::new(
+ ArrayEncoder::without_length_prefix(self.version.to_le_bytes()),
+ ArrayEncoder::without_length_prefix(self.salt.to_le_bytes()),
+ ))
+ }
+}
+
+type SendTxRcnClInnerDecoder = Decoder2<ArrayDecoder<4>, ArrayDecoder<8>>;
+
+/// The decoder for a [`SendTxRcnCl`] message.
+#[derive(Debug, Default, Clone)]
+pub struct SendTxRcnClDecoder(SendTxRcnClInnerDecoder);
+
+impl encoding::Decoder for SendTxRcnClDecoder {
+ type Output = SendTxRcnCl;
+ type Error = SendTxRcnClDecoderError;
+
+ #[inline]
+ fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
+ self.0.push_bytes(bytes).map_err(SendTxRcnClDecoderError)
+ }
+
+ #[inline]
+ fn end(self) -> Result<Self::Output, Self::Error> {
+ let (version, salt) = self.0.end().map_err(SendTxRcnClDecoderError)?;
+ Ok(SendTxRcnCl { version: u32::from_le_bytes(version), salt: u64::from_le_bytes(salt) })
+ }
+
+ #[inline]
+ fn read_limit(&self) -> usize {
+ self.0.read_limit()
+ }
+}
+
+impl encoding::Decode for SendTxRcnCl {
+ type Decoder = SendTxRcnClDecoder;
+}
+
+/// Error types for erlay messages.
+pub mod error {
+ use core::convert::Infallible;
+ use core::fmt;
+ use internals::write_err;
+
+ /// An error occurring when decoding a [`SendTxRcnCl`](super) message.
+ #[derive(Debug, Clone, PartialEq, Eq)]
+ pub struct SendTxRcnClDecoderError(
+ pub(super) <super::SendTxRcnClInnerDecoder as encoding::Decoder>::Error,
+ );
+
+ impl From<Infallible> for SendTxRcnClDecoderError {
+ fn from(never: Infallible) -> Self {
+ match never {}
+ }
+ }
+
+ impl fmt::Display for SendTxRcnClDecoderError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write_err!(f, "sendtxrcncl error"; self.0)
+ }
+ }
+
+ #[cfg(feature = "std")]
+ impl std::error::Error for SendTxRcnClDecoderError {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ Some(&self.0)
+ }
+ }
+}
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.