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