ant_protocol/storage/
header.rs

1// Copyright 2024 MaidSafe.net limited.
2//
3// This SAFE Network Software is licensed to you under The General Public License (GPL), version 3.
4// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed
5// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
6// KIND, either express or implied. Please review the Licences for the specific language governing
7// permissions and limitations relating to use of the SAFE Network Software.
8
9use crate::error::Error;
10use crate::PrettyPrintRecordKey;
11use bytes::{BufMut, Bytes, BytesMut};
12use libp2p::kad::Record;
13use prometheus_client::encoding::EncodeLabelValue;
14use rmp_serde::Serializer;
15use serde::{Deserialize, Serialize};
16use std::fmt::Display;
17use xor_name::XorName;
18
19/// Data types that natively suppported by autonomi network.
20#[derive(EncodeLabelValue, Debug, Serialize, Deserialize, Clone, Copy, Eq, PartialEq, Hash)]
21pub enum DataTypes {
22    Chunk,
23    GraphEntry,
24    Pointer,
25    Scratchpad,
26}
27
28impl DataTypes {
29    pub fn get_index(&self) -> u32 {
30        match self {
31            Self::Chunk => 0,
32            Self::GraphEntry => 1,
33            Self::Pointer => 2,
34            Self::Scratchpad => 3,
35        }
36    }
37
38    pub fn from_index(index: u32) -> Option<Self> {
39        match index {
40            0 => Some(Self::Chunk),
41            1 => Some(Self::GraphEntry),
42            2 => Some(Self::Pointer),
43            3 => Some(Self::Scratchpad),
44            _ => None,
45        }
46    }
47}
48
49/// Indicates the type of the record content.
50/// This is to be only used within the node instance to reflect different content version.
51/// Hence, only need to have two entries: Chunk and NonChunk.
52#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
53pub enum ValidationType {
54    Chunk,
55    NonChunk(XorName),
56}
57
58#[derive(Debug, Serialize, Deserialize)]
59pub struct RecordHeader {
60    pub kind: RecordKind,
61}
62
63/// To be used between client and nodes, hence need to indicate whehter payment info involved.
64#[derive(Debug, Eq, PartialEq, Clone, Copy)]
65pub enum RecordKind {
66    DataOnly(DataTypes),
67    DataWithPayment(DataTypes),
68}
69
70/// Allowing 10 data types to be defined, leaving margin for future.
71pub const RECORD_KIND_PAYMENT_STARTING_INDEX: u32 = 10;
72
73impl Serialize for RecordKind {
74    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
75    where
76        S: serde::Serializer,
77    {
78        let index = match self {
79            Self::DataOnly(ref data_types) => data_types.get_index(),
80            Self::DataWithPayment(ref data_types) => {
81                RECORD_KIND_PAYMENT_STARTING_INDEX + data_types.get_index()
82            }
83        };
84        serializer.serialize_u32(index)
85    }
86}
87
88impl<'de> Deserialize<'de> for RecordKind {
89    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
90    where
91        D: serde::Deserializer<'de>,
92    {
93        let num = u32::deserialize(deserializer)?;
94        let data_type_index = if num < RECORD_KIND_PAYMENT_STARTING_INDEX {
95            num
96        } else {
97            num - RECORD_KIND_PAYMENT_STARTING_INDEX
98        };
99
100        if let Some(data_type) = DataTypes::from_index(data_type_index) {
101            if num < RECORD_KIND_PAYMENT_STARTING_INDEX {
102                Ok(Self::DataOnly(data_type))
103            } else {
104                Ok(Self::DataWithPayment(data_type))
105            }
106        } else {
107            Err(serde::de::Error::custom(format!(
108                "Unexpected index {num} for RecordKind variant",
109            )))
110        }
111    }
112}
113impl Display for RecordKind {
114    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115        write!(f, "RecordKind({self:?})")
116    }
117}
118
119impl RecordHeader {
120    pub const SIZE: usize = 2;
121
122    pub fn try_serialize(self) -> Result<BytesMut, Error> {
123        let bytes = BytesMut::new();
124        let mut buf = bytes.writer();
125
126        self.serialize(&mut Serializer::new(&mut buf))
127            .map_err(|err| {
128                error!("Failed to serialized RecordHeader {self:?} with error: {err:?}");
129                Error::RecordHeaderParsingFailed
130            })?;
131
132        let b = buf.into_inner();
133
134        Ok(b)
135    }
136
137    pub fn try_deserialize(bytes: &[u8]) -> Result<Self, Error> {
138        rmp_serde::from_slice(bytes).map_err(|err| {
139            error!("Failed to deserialize RecordHeader with error: {err:?}");
140            Error::RecordHeaderParsingFailed
141        })
142    }
143
144    pub fn from_record(record: &Record) -> Result<Self, Error> {
145        if record.value.len() < RecordHeader::SIZE + 1 {
146            return Err(Error::RecordHeaderParsingFailed);
147        }
148        Self::try_deserialize(&record.value[..RecordHeader::SIZE + 1])
149    }
150
151    pub fn is_record_of_type_chunk(record: &Record) -> Result<bool, Error> {
152        let kind = Self::from_record(record)?.kind;
153        Ok(kind == RecordKind::DataOnly(DataTypes::Chunk))
154    }
155
156    pub fn get_data_type(record: &Record) -> Result<DataTypes, Error> {
157        let kind = Self::from_record(record)?.kind;
158        match kind {
159            RecordKind::DataOnly(data_type) | RecordKind::DataWithPayment(data_type) => {
160                Ok(data_type)
161            }
162        }
163    }
164}
165
166/// Utility to deserialize a `KAD::Record` into any type.
167/// Use `RecordHeader::from_record` if you want the `RecordHeader` instead.
168pub fn try_deserialize_record<T: serde::de::DeserializeOwned>(record: &Record) -> Result<T, Error> {
169    let bytes = if record.value.len() > RecordHeader::SIZE {
170        &record.value[RecordHeader::SIZE..]
171    } else {
172        return Err(Error::RecordParsingFailed);
173    };
174    rmp_serde::from_slice(bytes).map_err(|err| {
175        error!(
176            "Failed to deserialized record {} with error: {err:?}",
177            PrettyPrintRecordKey::from(&record.key)
178        );
179        Error::RecordParsingFailed
180    })
181}
182
183/// Utility to serialize the provided data along with the RecordKind to be stored as Record::value
184/// Returns Bytes to avoid accidental clone allocations
185pub fn try_serialize_record<T: serde::Serialize>(
186    data: &T,
187    record_kind: RecordKind,
188) -> Result<Bytes, Error> {
189    let mut buf = RecordHeader { kind: record_kind }.try_serialize()?.writer();
190    data.serialize(&mut Serializer::new(&mut buf))
191        .map_err(|err| {
192            error!("Failed to serialized Records with error: {err:?}");
193            Error::RecordParsingFailed
194        })?;
195    let bytes = buf.into_inner();
196    Ok(bytes.freeze())
197}
198
199#[cfg(test)]
200mod tests {
201    use super::*;
202    use crate::error::Result;
203
204    #[test]
205    fn verify_record_header_encoded_size() -> Result<()> {
206        let chunk_with_payment = RecordHeader {
207            kind: RecordKind::DataWithPayment(DataTypes::Chunk),
208        }
209        .try_serialize()?;
210        assert_eq!(chunk_with_payment.len(), RecordHeader::SIZE);
211
212        let chunk = RecordHeader {
213            kind: RecordKind::DataOnly(DataTypes::Chunk),
214        }
215        .try_serialize()?;
216        assert_eq!(chunk.len(), RecordHeader::SIZE);
217
218        let graphentry = RecordHeader {
219            kind: RecordKind::DataOnly(DataTypes::GraphEntry),
220        }
221        .try_serialize()?;
222        assert_eq!(graphentry.len(), RecordHeader::SIZE);
223
224        let scratchpad = RecordHeader {
225            kind: RecordKind::DataOnly(DataTypes::Scratchpad),
226        }
227        .try_serialize()?;
228        assert_eq!(scratchpad.len(), RecordHeader::SIZE);
229
230        let scratchpad_with_payment = RecordHeader {
231            kind: RecordKind::DataWithPayment(DataTypes::Scratchpad),
232        }
233        .try_serialize()?;
234        assert_eq!(scratchpad_with_payment.len(), RecordHeader::SIZE);
235
236        let pointer = RecordHeader {
237            kind: RecordKind::DataOnly(DataTypes::Pointer),
238        }
239        .try_serialize()?;
240        assert_eq!(pointer.len(), RecordHeader::SIZE);
241
242        let pointer_with_payment = RecordHeader {
243            kind: RecordKind::DataWithPayment(DataTypes::Pointer),
244        }
245        .try_serialize()?;
246        assert_eq!(pointer_with_payment.len(), RecordHeader::SIZE);
247
248        Ok(())
249    }
250
251    #[test]
252    fn test_record_kind_serialization() -> Result<()> {
253        let kinds = vec![
254            RecordKind::DataOnly(DataTypes::Chunk),
255            RecordKind::DataWithPayment(DataTypes::Chunk),
256            RecordKind::DataOnly(DataTypes::GraphEntry),
257            RecordKind::DataWithPayment(DataTypes::GraphEntry),
258            RecordKind::DataOnly(DataTypes::Scratchpad),
259            RecordKind::DataWithPayment(DataTypes::Scratchpad),
260            RecordKind::DataOnly(DataTypes::Pointer),
261            RecordKind::DataWithPayment(DataTypes::Pointer),
262        ];
263
264        for kind in kinds {
265            let header = RecordHeader { kind };
266            let header2 = RecordHeader { kind };
267
268            let serialized = header.try_serialize()?;
269            let deserialized = RecordHeader::try_deserialize(&serialized)?;
270            assert_eq!(header2.kind, deserialized.kind);
271        }
272
273        Ok(())
274    }
275}