1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// Rust Levin Library
// Written in 2023 by
//   Cuprate Contributors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//

//! This module provides a struct BucketHead for the header of a levin protocol
//! message.

use bitflags::bitflags;
use bytes::{Buf, BufMut, BytesMut};

use crate::LevinCommand;

/// The size of the header (in bytes)
pub const HEADER_SIZE: usize = 33;

/// Levin header flags
#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
pub struct Flags(u32);

bitflags! {
    impl Flags: u32 {
        /// The request flag.
        ///
        /// Depending on the `have_to_return_data` field in [`BucketHead`], this message is either
        /// a request or notification.
        const REQUEST = 0b0000_0001;
        /// The response flags.
        ///
        /// Messages with this set are responses to requests.
        const RESPONSE = 0b0000_0010;

        /// The start fragment flag.
        ///
        /// Messages with this flag set tell the parser that the next messages until a message
        /// with [`Flags::END_FRAGMENT`] should be combined into a single bucket.
        const START_FRAGMENT = 0b0000_0100;
        /// The end fragment flag.
        ///
        /// Messages with this flag set tell the parser that all fragments of a fragmented message
        /// have been sent.
        const END_FRAGMENT = 0b0000_1000;

        /// A dummy message.
        ///
        /// Messages with this flag will be completely ignored by the parser.
        const DUMMY = Self::START_FRAGMENT.bits() | Self::END_FRAGMENT.bits();

        const _ = !0;
    }
}

impl From<u32> for Flags {
    fn from(value: u32) -> Self {
        Flags(value)
    }
}

impl From<Flags> for u32 {
    fn from(value: Flags) -> Self {
        value.0
    }
}

/// The Header of a Bucket. This contains
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct BucketHead<C> {
    /// The network signature, should be `LEVIN_SIGNATURE` for Monero
    pub signature: u64,
    /// The size of the body
    pub size: u64,
    /// If the peer has to send data in the order of requests - some
    /// messages require responses but don't have this set (some notifications)
    pub have_to_return_data: bool,
    /// Command
    pub command: C,
    /// Return Code - will be 0 for requests and >0 for ok responses otherwise will be
    /// a negative number corresponding to the error
    pub return_code: i32,
    /// The Flags of this header
    pub flags: Flags,
    /// The protocol version, for Monero this is currently 1
    pub protocol_version: u32,
}

impl<C: LevinCommand> BucketHead<C> {
    /// Builds the header from bytes, this function does not check any fields should
    /// match the expected ones.
    ///
    /// # Panics
    /// This function will panic if there aren't enough bytes to fill the header.
    /// Currently [HEADER_SIZE]
    pub fn from_bytes(buf: &mut BytesMut) -> BucketHead<C> {
        BucketHead {
            signature: buf.get_u64_le(),
            size: buf.get_u64_le(),
            have_to_return_data: buf.get_u8() != 0,
            command: buf.get_u32_le().into(),
            return_code: buf.get_i32_le(),
            flags: Flags::from(buf.get_u32_le()),
            protocol_version: buf.get_u32_le(),
        }
    }

    /// Serializes the header
    pub fn write_bytes_into(&self, dst: &mut BytesMut) {
        dst.reserve(HEADER_SIZE);

        dst.put_u64_le(self.signature);
        dst.put_u64_le(self.size);
        dst.put_u8(if self.have_to_return_data { 1 } else { 0 });
        dst.put_u32_le(self.command.clone().into());
        dst.put_i32_le(self.return_code);
        dst.put_u32_le(self.flags.into());
        dst.put_u32_le(self.protocol_version);
    }
}