waffles_solana_sdk/
packet.rs

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