atlas_packet/
lib.rs

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