Skip to main content

cuprate_levin/
header.rs

1// Rust Levin Library
2// Written in 2023 by
3//   Cuprate Contributors
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files (the "Software"), to deal
7// in the Software without restriction, including without limitation the rights
8// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9// copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11//
12// The above copyright notice and this permission notice shall be included in all
13// copies or substantial portions of the Software.
14//
15
16//! This module provides a struct `BucketHead` for the header of a levin protocol
17//! message.
18
19use bitflags::bitflags;
20use bytes::{Buf, BufMut, BytesMut};
21
22use crate::LevinCommand;
23
24/// The size of the header (in bytes)
25pub const HEADER_SIZE: usize = 33;
26
27/// Levin header flags
28#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
29#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
30pub struct Flags(u32);
31
32bitflags! {
33    impl Flags: u32 {
34        /// The request flag.
35        ///
36        /// Depending on the `have_to_return_data` field in [`BucketHead`], this message is either
37        /// a request or notification.
38        const REQUEST = 0b0000_0001;
39        /// The response flags.
40        ///
41        /// Messages with this set are responses to requests.
42        const RESPONSE = 0b0000_0010;
43
44        /// The start fragment flag.
45        ///
46        /// Messages with this flag set tell the parser that the next messages until a message
47        /// with [`Flags::END_FRAGMENT`] should be combined into a single bucket.
48        const START_FRAGMENT = 0b0000_0100;
49        /// The end fragment flag.
50        ///
51        /// Messages with this flag set tell the parser that all fragments of a fragmented message
52        /// have been sent.
53        const END_FRAGMENT = 0b0000_1000;
54
55        /// A dummy message.
56        ///
57        /// Messages with this flag will be completely ignored by the parser.
58        const DUMMY = Self::START_FRAGMENT.bits() | Self::END_FRAGMENT.bits();
59
60        const _ = !0;
61    }
62}
63
64impl From<u32> for Flags {
65    fn from(value: u32) -> Self {
66        Self(value)
67    }
68}
69
70impl From<Flags> for u32 {
71    fn from(value: Flags) -> Self {
72        value.0
73    }
74}
75
76/// The Header of a Bucket. This contains
77#[derive(Debug, PartialEq, Eq, Clone, Copy)]
78#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
79pub struct BucketHead<C> {
80    /// The network signature, should be `LEVIN_SIGNATURE` for Monero
81    #[cfg_attr(feature = "arbitrary", arbitrary(value = crate::MONERO_LEVIN_SIGNATURE))]
82    pub signature: u64,
83    /// The size of the body
84    pub size: u64,
85    /// If the peer has to send data in the order of requests - some
86    /// messages require responses but don't have this set (some notifications)
87    pub have_to_return_data: bool,
88    /// Command
89    pub command: C,
90    /// Return Code - will be 0 for requests and >0 for ok responses otherwise will be
91    /// a negative number corresponding to the error
92    pub return_code: i32,
93    /// The Flags of this header
94    pub flags: Flags,
95    /// The protocol version, for Monero this is currently 1
96    pub protocol_version: u32,
97}
98
99impl<C: LevinCommand> BucketHead<C> {
100    /// Builds the header from bytes, this function does not check any fields should
101    /// match the expected ones.
102    ///
103    /// # Panics
104    /// This function will panic if there aren't enough bytes to fill the header.
105    /// Currently [`HEADER_SIZE`]
106    pub fn from_bytes(buf: &mut BytesMut) -> Self {
107        Self {
108            signature: buf.get_u64_le(),
109            size: buf.get_u64_le(),
110            have_to_return_data: buf.get_u8() != 0,
111            command: buf.get_u32_le().into(),
112            return_code: buf.get_i32_le(),
113            flags: Flags::from(buf.get_u32_le()),
114            protocol_version: buf.get_u32_le(),
115        }
116    }
117
118    /// Serializes the header
119    pub fn write_bytes_into(&self, dst: &mut BytesMut) {
120        dst.reserve(HEADER_SIZE);
121
122        dst.put_u64_le(self.signature);
123        dst.put_u64_le(self.size);
124        dst.put_u8(if self.have_to_return_data { 1 } else { 0 });
125        dst.put_u32_le(self.command.clone().into());
126        dst.put_i32_le(self.return_code);
127        dst.put_u32_le(self.flags.into());
128        dst.put_u32_le(self.protocol_version);
129    }
130}