solana_packet/
lib.rs

1//! The definition of a Solana network packet.
2#![cfg_attr(feature = "frozen-abi", feature(min_specialization))]
3#![cfg_attr(docsrs, feature(doc_auto_cfg))]
4
5#[cfg(feature = "bincode")]
6use bincode::{Options, Result};
7#[cfg(feature = "frozen-abi")]
8use solana_frozen_abi_macro::AbiExample;
9use {
10    bitflags::bitflags,
11    std::{
12        fmt,
13        net::{IpAddr, Ipv4Addr, SocketAddr},
14        slice::SliceIndex,
15    },
16};
17#[cfg(feature = "serde")]
18use {
19    serde_derive::{Deserialize, Serialize},
20    serde_with::{serde_as, Bytes},
21};
22
23#[cfg(test)]
24static_assertions::const_assert_eq!(PACKET_DATA_SIZE, 1232);
25/// Maximum over-the-wire size of a Transaction
26///   1280 is IPv6 minimum MTU
27///   40 bytes is the size of the IPv6 header
28///   8 bytes is the size of the fragment header
29pub const PACKET_DATA_SIZE: usize = 1280 - 40 - 8;
30
31bitflags! {
32    #[repr(C)]
33    #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
34    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
35    pub struct PacketFlags: u8 {
36        const DISCARD        = 0b0000_0001;
37        const FORWARDED      = 0b0000_0010;
38        const REPAIR         = 0b0000_0100;
39        const SIMPLE_VOTE_TX = 0b0000_1000;
40        const TRACER_PACKET  = 0b0001_0000;
41        // Previously used - this can now be re-used for something else.
42        const UNUSED = 0b0010_0000;
43        /// For tracking performance
44        const PERF_TRACK_PACKET  = 0b0100_0000;
45        /// For marking packets from staked nodes
46        const FROM_STAKED_NODE = 0b1000_0000;
47    }
48}
49
50#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
51#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
52#[derive(Clone, Debug, PartialEq, Eq)]
53#[repr(C)]
54pub struct Meta {
55    pub size: usize,
56    pub addr: IpAddr,
57    pub port: u16,
58    pub flags: PacketFlags,
59}
60
61#[cfg(feature = "frozen-abi")]
62impl ::solana_frozen_abi::abi_example::AbiExample for PacketFlags {
63    fn example() -> Self {
64        Self::empty()
65    }
66}
67
68#[cfg(feature = "frozen-abi")]
69impl ::solana_frozen_abi::abi_example::TransparentAsHelper for PacketFlags {}
70
71#[cfg(feature = "frozen-abi")]
72impl ::solana_frozen_abi::abi_example::EvenAsOpaque for PacketFlags {
73    const TYPE_NAME_MATCHER: &'static str = "::_::InternalBitFlags";
74}
75
76// serde_as is used as a work around because array isn't supported by serde
77// (and serde_bytes).
78//
79// the root cause is of a historical special handling for [T; 0] in rust's
80// `Default` and supposedly mirrored serde's `Serialize` (macro) impls,
81// pre-dating stabilized const generics, meaning it'll take long time...:
82//   https://github.com/rust-lang/rust/issues/61415
83//   https://github.com/rust-lang/rust/issues/88744#issuecomment-1138678928
84//
85// Due to the nature of the root cause, the current situation is complicated.
86// All in all, the serde_as solution is chosen for good perf and low maintenance
87// need at the cost of another crate dependency..
88//
89// For details, please refer to the below various links...
90//
91// relevant merged/published pr for this serde_as functionality used here:
92//   https://github.com/jonasbb/serde_with/pull/277
93// open pr at serde_bytes:
94//   https://github.com/serde-rs/bytes/pull/28
95// open issue at serde:
96//   https://github.com/serde-rs/serde/issues/1937
97// closed pr at serde (due to the above mentioned [N; 0] issue):
98//   https://github.com/serde-rs/serde/pull/1860
99// ryoqun's dirty experiments:
100//   https://github.com/ryoqun/serde-array-comparisons
101//
102// We use the cfg_eval crate as advised by the serde_with guide:
103// https://docs.rs/serde_with/latest/serde_with/guide/serde_as/index.html#gating-serde_as-on-features
104#[cfg_attr(feature = "serde", cfg_eval::cfg_eval, serde_as)]
105#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
106#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
107#[derive(Clone, Eq)]
108#[repr(C)]
109pub struct Packet {
110    // Bytes past Packet.meta.size are not valid to read from.
111    // Use Packet.data(index) to read from the buffer.
112    #[cfg_attr(feature = "serde", serde_as(as = "Bytes"))]
113    buffer: [u8; PACKET_DATA_SIZE],
114    meta: Meta,
115}
116
117impl Packet {
118    pub fn new(buffer: [u8; PACKET_DATA_SIZE], meta: Meta) -> Self {
119        Self { buffer, meta }
120    }
121
122    /// Returns an immutable reference to the underlying buffer up to
123    /// packet.meta.size. The rest of the buffer is not valid to read from.
124    /// packet.data(..) returns packet.buffer.get(..packet.meta.size).
125    /// Returns None if the index is invalid or if the packet is already marked
126    /// as discard.
127    #[inline]
128    pub fn data<I>(&self, index: I) -> Option<&<I as SliceIndex<[u8]>>::Output>
129    where
130        I: SliceIndex<[u8]>,
131    {
132        // If the packet is marked as discard, it is either invalid or
133        // otherwise should be ignored, and so the payload should not be read
134        // from.
135        if self.meta.discard() {
136            None
137        } else {
138            self.buffer.get(..self.meta.size)?.get(index)
139        }
140    }
141
142    /// Returns a mutable reference to the entirety of the underlying buffer to
143    /// write into. The caller is responsible for updating Packet.meta.size
144    /// after writing to the buffer.
145    #[inline]
146    pub fn buffer_mut(&mut self) -> &mut [u8] {
147        debug_assert!(!self.meta.discard());
148        &mut self.buffer[..]
149    }
150
151    #[inline]
152    pub fn meta(&self) -> &Meta {
153        &self.meta
154    }
155
156    #[inline]
157    pub fn meta_mut(&mut self) -> &mut Meta {
158        &mut self.meta
159    }
160
161    #[cfg(feature = "bincode")]
162    pub fn from_data<T: serde::Serialize>(dest: Option<&SocketAddr>, data: T) -> Result<Self> {
163        let mut packet = Self::default();
164        Self::populate_packet(&mut packet, dest, &data)?;
165        Ok(packet)
166    }
167
168    #[cfg(feature = "bincode")]
169    pub fn populate_packet<T: serde::Serialize>(
170        &mut self,
171        dest: Option<&SocketAddr>,
172        data: &T,
173    ) -> Result<()> {
174        debug_assert!(!self.meta.discard());
175        let mut wr = std::io::Cursor::new(self.buffer_mut());
176        bincode::serialize_into(&mut wr, data)?;
177        self.meta.size = wr.position() as usize;
178        if let Some(dest) = dest {
179            self.meta.set_socket_addr(dest);
180        }
181        Ok(())
182    }
183
184    #[cfg(feature = "bincode")]
185    pub fn deserialize_slice<T, I>(&self, index: I) -> Result<T>
186    where
187        T: serde::de::DeserializeOwned,
188        I: SliceIndex<[u8], Output = [u8]>,
189    {
190        let bytes = self.data(index).ok_or(bincode::ErrorKind::SizeLimit)?;
191        bincode::options()
192            .with_limit(PACKET_DATA_SIZE as u64)
193            .with_fixint_encoding()
194            .reject_trailing_bytes()
195            .deserialize(bytes)
196    }
197}
198
199impl fmt::Debug for Packet {
200    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201        write!(
202            f,
203            "Packet {{ size: {:?}, addr: {:?} }}",
204            self.meta.size,
205            self.meta.socket_addr()
206        )
207    }
208}
209
210#[allow(clippy::uninit_assumed_init)]
211impl Default for Packet {
212    fn default() -> Self {
213        let buffer = std::mem::MaybeUninit::<[u8; PACKET_DATA_SIZE]>::uninit();
214        Self {
215            buffer: unsafe { buffer.assume_init() },
216            meta: Meta::default(),
217        }
218    }
219}
220
221impl PartialEq for Packet {
222    fn eq(&self, other: &Self) -> bool {
223        self.meta() == other.meta() && self.data(..) == other.data(..)
224    }
225}
226
227impl Meta {
228    pub fn socket_addr(&self) -> SocketAddr {
229        SocketAddr::new(self.addr, self.port)
230    }
231
232    pub fn set_socket_addr(&mut self, socket_addr: &SocketAddr) {
233        self.addr = socket_addr.ip();
234        self.port = socket_addr.port();
235    }
236
237    pub fn set_from_staked_node(&mut self, from_staked_node: bool) {
238        self.flags
239            .set(PacketFlags::FROM_STAKED_NODE, from_staked_node);
240    }
241
242    #[inline]
243    pub fn discard(&self) -> bool {
244        self.flags.contains(PacketFlags::DISCARD)
245    }
246
247    #[inline]
248    pub fn set_discard(&mut self, discard: bool) {
249        self.flags.set(PacketFlags::DISCARD, discard);
250    }
251
252    #[inline]
253    pub fn set_tracer(&mut self, is_tracer: bool) {
254        self.flags.set(PacketFlags::TRACER_PACKET, is_tracer);
255    }
256
257    #[inline]
258    pub fn set_track_performance(&mut self, is_performance_track: bool) {
259        self.flags
260            .set(PacketFlags::PERF_TRACK_PACKET, is_performance_track);
261    }
262
263    #[inline]
264    pub fn set_simple_vote(&mut self, is_simple_vote: bool) {
265        self.flags.set(PacketFlags::SIMPLE_VOTE_TX, is_simple_vote);
266    }
267
268    #[inline]
269    pub fn forwarded(&self) -> bool {
270        self.flags.contains(PacketFlags::FORWARDED)
271    }
272
273    #[inline]
274    pub fn repair(&self) -> bool {
275        self.flags.contains(PacketFlags::REPAIR)
276    }
277
278    #[inline]
279    pub fn is_simple_vote_tx(&self) -> bool {
280        self.flags.contains(PacketFlags::SIMPLE_VOTE_TX)
281    }
282
283    #[inline]
284    pub fn is_tracer_packet(&self) -> bool {
285        self.flags.contains(PacketFlags::TRACER_PACKET)
286    }
287
288    #[inline]
289    pub fn is_perf_track_packet(&self) -> bool {
290        self.flags.contains(PacketFlags::PERF_TRACK_PACKET)
291    }
292
293    #[inline]
294    pub fn is_from_staked_node(&self) -> bool {
295        self.flags.contains(PacketFlags::FROM_STAKED_NODE)
296    }
297}
298
299impl Default for Meta {
300    fn default() -> Self {
301        Self {
302            size: 0,
303            addr: IpAddr::V4(Ipv4Addr::UNSPECIFIED),
304            port: 0,
305            flags: PacketFlags::empty(),
306        }
307    }
308}
309
310#[cfg(test)]
311mod tests {
312    use super::*;
313
314    #[test]
315    fn test_deserialize_slice() {
316        let p = Packet::from_data(None, u32::MAX).unwrap();
317        assert_eq!(p.deserialize_slice(..).ok(), Some(u32::MAX));
318        assert_eq!(p.deserialize_slice(0..4).ok(), Some(u32::MAX));
319        assert_eq!(
320            p.deserialize_slice::<u16, _>(0..4)
321                .map_err(|e| e.to_string()),
322            Err("Slice had bytes remaining after deserialization".to_string()),
323        );
324        assert_eq!(
325            p.deserialize_slice::<u32, _>(0..0)
326                .map_err(|e| e.to_string()),
327            Err("io error: unexpected end of file".to_string()),
328        );
329        assert_eq!(
330            p.deserialize_slice::<u32, _>(0..1)
331                .map_err(|e| e.to_string()),
332            Err("io error: unexpected end of file".to_string()),
333        );
334        assert_eq!(
335            p.deserialize_slice::<u32, _>(0..5)
336                .map_err(|e| e.to_string()),
337            Err("the size limit has been reached".to_string()),
338        );
339        #[allow(clippy::reversed_empty_ranges)]
340        let reversed_empty_range = 4..0;
341        assert_eq!(
342            p.deserialize_slice::<u32, _>(reversed_empty_range)
343                .map_err(|e| e.to_string()),
344            Err("the size limit has been reached".to_string()),
345        );
346        assert_eq!(
347            p.deserialize_slice::<u32, _>(4..5)
348                .map_err(|e| e.to_string()),
349            Err("the size limit has been reached".to_string()),
350        );
351    }
352}