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    pub signature: u64,
82    /// The size of the body
83    pub size: u64,
84    /// If the peer has to send data in the order of requests - some
85    /// messages require responses but don't have this set (some notifications)
86    pub have_to_return_data: bool,
87    /// Command
88    pub command: C,
89    /// Return Code - will be 0 for requests and >0 for ok responses otherwise will be
90    /// a negative number corresponding to the error
91    pub return_code: i32,
92    /// The Flags of this header
93    pub flags: Flags,
94    /// The protocol version, for Monero this is currently 1
95    pub protocol_version: u32,
96}
97
98impl<C: LevinCommand> BucketHead<C> {
99    /// Builds the header from bytes, this function does not check any fields should
100    /// match the expected ones.
101    ///
102    /// # Panics
103    /// This function will panic if there aren't enough bytes to fill the header.
104    /// Currently [`HEADER_SIZE`]
105    pub fn from_bytes(buf: &mut BytesMut) -> Self {
106        Self {
107            signature: buf.get_u64_le(),
108            size: buf.get_u64_le(),
109            have_to_return_data: buf.get_u8() != 0,
110            command: buf.get_u32_le().into(),
111            return_code: buf.get_i32_le(),
112            flags: Flags::from(buf.get_u32_le()),
113            protocol_version: buf.get_u32_le(),
114        }
115    }
116
117    /// Serializes the header
118    pub fn write_bytes_into(&self, dst: &mut BytesMut) {
119        dst.reserve(HEADER_SIZE);
120
121        dst.put_u64_le(self.signature);
122        dst.put_u64_le(self.size);
123        dst.put_u8(if self.have_to_return_data { 1 } else { 0 });
124        dst.put_u32_le(self.command.clone().into());
125        dst.put_i32_le(self.return_code);
126        dst.put_u32_le(self.flags.into());
127        dst.put_u32_le(self.protocol_version);
128    }
129}