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}