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::PrettyPrintRecordKey;
10use crate::error::Error;
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    /// Data with Merkle batch payment proof
71    /// Used when data was paid for via Merkle tree batch payment
72    DataWithMerklePayment(DataTypes),
73}
74
75/// Allowing 10 data types to be defined, leaving margin for future.
76pub const RECORD_KIND_PAYMENT_STARTING_INDEX: u32 = 10;
77
78/// Starting index for Merkle payment records
79pub const RECORD_KIND_MERKLE_PAYMENT_STARTING_INDEX: u32 = 20;
80
81impl Serialize for RecordKind {
82    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
83    where
84        S: serde::Serializer,
85    {
86        let index = match self {
87            Self::DataOnly(data_types) => data_types.get_index(),
88            Self::DataWithPayment(data_types) => {
89                RECORD_KIND_PAYMENT_STARTING_INDEX + data_types.get_index()
90            }
91            Self::DataWithMerklePayment(data_types) => {
92                RECORD_KIND_MERKLE_PAYMENT_STARTING_INDEX + data_types.get_index()
93            }
94        };
95        serializer.serialize_u32(index)
96    }
97}
98
99impl<'de> Deserialize<'de> for RecordKind {
100    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
101    where
102        D: serde::Deserializer<'de>,
103    {
104        let num = u32::deserialize(deserializer)?;
105
106        let (kind_type, data_type_index) = if num < RECORD_KIND_PAYMENT_STARTING_INDEX {
107            ("DataOnly", num)
108        } else if num < RECORD_KIND_MERKLE_PAYMENT_STARTING_INDEX {
109            ("DataWithPayment", num - RECORD_KIND_PAYMENT_STARTING_INDEX)
110        } else {
111            (
112                "DataWithMerklePayment",
113                num - RECORD_KIND_MERKLE_PAYMENT_STARTING_INDEX,
114            )
115        };
116
117        let data_type = DataTypes::from_index(data_type_index).ok_or_else(|| {
118            serde::de::Error::custom(format!("Unexpected index {num} for RecordKind variant"))
119        })?;
120
121        Ok(match kind_type {
122            "DataOnly" => Self::DataOnly(data_type),
123            "DataWithPayment" => Self::DataWithPayment(data_type),
124            "DataWithMerklePayment" => Self::DataWithMerklePayment(data_type),
125            _ => unreachable!(),
126        })
127    }
128}
129impl Display for RecordKind {
130    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131        write!(f, "RecordKind({self:?})")
132    }
133}
134
135impl RecordHeader {
136    pub const SIZE: usize = 2;
137
138    pub fn try_serialize(self) -> Result<BytesMut, Error> {
139        let bytes = BytesMut::new();
140        let mut buf = bytes.writer();
141
142        self.serialize(&mut Serializer::new(&mut buf))
143            .map_err(|err| {
144                error!("Failed to serialized RecordHeader {self:?} with error: {err:?}");
145                Error::RecordHeaderParsingFailed
146            })?;
147
148        let b = buf.into_inner();
149
150        Ok(b)
151    }
152
153    pub fn try_deserialize(bytes: &[u8]) -> Result<Self, Error> {
154        rmp_serde::from_slice(bytes).map_err(|err| {
155            error!("Failed to deserialize RecordHeader with error: {err:?}");
156            Error::RecordHeaderParsingFailed
157        })
158    }
159
160    pub fn from_record(record: &Record) -> Result<Self, Error> {
161        if record.value.len() < RecordHeader::SIZE + 1 {
162            return Err(Error::RecordHeaderParsingFailed);
163        }
164        Self::try_deserialize(&record.value[..RecordHeader::SIZE + 1])
165    }
166
167    pub fn is_record_of_type_chunk(record: &Record) -> Result<bool, Error> {
168        let kind = Self::from_record(record)?.kind;
169        Ok(kind == RecordKind::DataOnly(DataTypes::Chunk))
170    }
171
172    pub fn get_data_type(record: &Record) -> Result<DataTypes, Error> {
173        let kind = Self::from_record(record)?.kind;
174        match kind {
175            RecordKind::DataOnly(data_type)
176            | RecordKind::DataWithPayment(data_type)
177            | RecordKind::DataWithMerklePayment(data_type) => Ok(data_type),
178        }
179    }
180}
181
182/// Utility to deserialize a `KAD::Record` into any type.
183/// Use `RecordHeader::from_record` if you want the `RecordHeader` instead.
184pub fn try_deserialize_record<T: serde::de::DeserializeOwned>(record: &Record) -> Result<T, Error> {
185    let bytes = if record.value.len() > RecordHeader::SIZE {
186        &record.value[RecordHeader::SIZE..]
187    } else {
188        return Err(Error::RecordParsingFailed);
189    };
190    rmp_serde::from_slice(bytes).map_err(|err| {
191        error!(
192            "Failed to deserialized record {} with error: {err:?}",
193            PrettyPrintRecordKey::from(&record.key)
194        );
195        Error::RecordParsingFailed
196    })
197}
198
199/// Utility to serialize the provided data along with the RecordKind to be stored as Record::value
200/// Returns Bytes to avoid accidental clone allocations
201pub fn try_serialize_record<T: serde::Serialize>(
202    data: &T,
203    record_kind: RecordKind,
204) -> Result<Bytes, Error> {
205    let mut buf = RecordHeader { kind: record_kind }.try_serialize()?.writer();
206    data.serialize(&mut Serializer::new(&mut buf))
207        .map_err(|err| {
208            error!("Failed to serialized Records with error: {err:?}");
209            Error::RecordParsingFailed
210        })?;
211    let bytes = buf.into_inner();
212    Ok(bytes.freeze())
213}
214
215#[cfg(test)]
216mod tests {
217    use super::*;
218    use crate::error::Result;
219
220    #[test]
221    fn verify_record_header_encoded_size() -> Result<()> {
222        let chunk_with_payment = RecordHeader {
223            kind: RecordKind::DataWithPayment(DataTypes::Chunk),
224        }
225        .try_serialize()?;
226        assert_eq!(chunk_with_payment.len(), RecordHeader::SIZE);
227
228        let chunk = RecordHeader {
229            kind: RecordKind::DataOnly(DataTypes::Chunk),
230        }
231        .try_serialize()?;
232        assert_eq!(chunk.len(), RecordHeader::SIZE);
233
234        let graphentry = RecordHeader {
235            kind: RecordKind::DataOnly(DataTypes::GraphEntry),
236        }
237        .try_serialize()?;
238        assert_eq!(graphentry.len(), RecordHeader::SIZE);
239
240        let scratchpad = RecordHeader {
241            kind: RecordKind::DataOnly(DataTypes::Scratchpad),
242        }
243        .try_serialize()?;
244        assert_eq!(scratchpad.len(), RecordHeader::SIZE);
245
246        let scratchpad_with_payment = RecordHeader {
247            kind: RecordKind::DataWithPayment(DataTypes::Scratchpad),
248        }
249        .try_serialize()?;
250        assert_eq!(scratchpad_with_payment.len(), RecordHeader::SIZE);
251
252        let pointer = RecordHeader {
253            kind: RecordKind::DataOnly(DataTypes::Pointer),
254        }
255        .try_serialize()?;
256        assert_eq!(pointer.len(), RecordHeader::SIZE);
257
258        let pointer_with_payment = RecordHeader {
259            kind: RecordKind::DataWithPayment(DataTypes::Pointer),
260        }
261        .try_serialize()?;
262        assert_eq!(pointer_with_payment.len(), RecordHeader::SIZE);
263
264        // Test Merkle payment variants
265        let chunk_with_merkle_payment = RecordHeader {
266            kind: RecordKind::DataWithMerklePayment(DataTypes::Chunk),
267        }
268        .try_serialize()?;
269        assert_eq!(chunk_with_merkle_payment.len(), RecordHeader::SIZE);
270
271        let graphentry_with_merkle_payment = RecordHeader {
272            kind: RecordKind::DataWithMerklePayment(DataTypes::GraphEntry),
273        }
274        .try_serialize()?;
275        assert_eq!(graphentry_with_merkle_payment.len(), RecordHeader::SIZE);
276
277        let pointer_with_merkle_payment = RecordHeader {
278            kind: RecordKind::DataWithMerklePayment(DataTypes::Pointer),
279        }
280        .try_serialize()?;
281        assert_eq!(pointer_with_merkle_payment.len(), RecordHeader::SIZE);
282
283        let scratchpad_with_merkle_payment = RecordHeader {
284            kind: RecordKind::DataWithMerklePayment(DataTypes::Scratchpad),
285        }
286        .try_serialize()?;
287        assert_eq!(scratchpad_with_merkle_payment.len(), RecordHeader::SIZE);
288
289        Ok(())
290    }
291
292    #[test]
293    fn test_record_kind_serialization() -> Result<()> {
294        let kinds = vec![
295            RecordKind::DataOnly(DataTypes::Chunk),
296            RecordKind::DataWithPayment(DataTypes::Chunk),
297            RecordKind::DataWithMerklePayment(DataTypes::Chunk),
298            RecordKind::DataOnly(DataTypes::GraphEntry),
299            RecordKind::DataWithPayment(DataTypes::GraphEntry),
300            RecordKind::DataWithMerklePayment(DataTypes::GraphEntry),
301            RecordKind::DataOnly(DataTypes::Scratchpad),
302            RecordKind::DataWithPayment(DataTypes::Scratchpad),
303            RecordKind::DataWithMerklePayment(DataTypes::Scratchpad),
304            RecordKind::DataOnly(DataTypes::Pointer),
305            RecordKind::DataWithPayment(DataTypes::Pointer),
306            RecordKind::DataWithMerklePayment(DataTypes::Pointer),
307        ];
308
309        for kind in kinds {
310            let header = RecordHeader { kind };
311            let header2 = RecordHeader { kind };
312
313            let serialized = header.try_serialize()?;
314            let deserialized = RecordHeader::try_deserialize(&serialized)?;
315            assert_eq!(header2.kind, deserialized.kind);
316        }
317
318        Ok(())
319    }
320
321    #[test]
322    fn test_merkle_payment_record_kind_indices() -> Result<()> {
323        // Test that Merkle payment variants serialize to correct indices (20-23)
324        let test_cases = vec![
325            (RecordKind::DataWithMerklePayment(DataTypes::Chunk), 20u32),
326            (
327                RecordKind::DataWithMerklePayment(DataTypes::GraphEntry),
328                21u32,
329            ),
330            (RecordKind::DataWithMerklePayment(DataTypes::Pointer), 22u32),
331            (
332                RecordKind::DataWithMerklePayment(DataTypes::Scratchpad),
333                23u32,
334            ),
335        ];
336
337        for (kind, expected_index) in test_cases {
338            let header = RecordHeader { kind };
339            let serialized = header.try_serialize()?;
340
341            // Parse the messagepack format to extract the u32 value
342            // The serialized format is [kind_field, value]
343            // We need to deserialize the whole RecordHeader and check by re-roundtripping
344            let deserialized = RecordHeader::try_deserialize(&serialized)?;
345
346            // Re-serialize to verify the index by checking DataTypes match
347            match (kind, deserialized.kind) {
348                (
349                    RecordKind::DataWithMerklePayment(expected_type),
350                    RecordKind::DataWithMerklePayment(actual_type),
351                ) => {
352                    assert_eq!(expected_type, actual_type);
353                    // Verify the index by checking the data type offset
354                    let actual_index =
355                        RECORD_KIND_MERKLE_PAYMENT_STARTING_INDEX + actual_type.get_index();
356                    assert_eq!(
357                        actual_index, expected_index,
358                        "RecordKind {kind:?} should serialize to index {expected_index}"
359                    );
360                }
361                _ => panic!("Expected DataWithMerklePayment variant"),
362            }
363        }
364
365        Ok(())
366    }
367
368    #[test]
369    fn test_record_kind_index_ranges() -> Result<()> {
370        // Test DataOnly: 0-3
371        assert_eq!(
372            RecordKind::DataOnly(DataTypes::Chunk),
373            RecordHeader::try_deserialize(
374                &RecordHeader {
375                    kind: RecordKind::DataOnly(DataTypes::Chunk)
376                }
377                .try_serialize()?
378            )?
379            .kind
380        );
381
382        // Test DataWithPayment: 10-13
383        assert_eq!(
384            RecordKind::DataWithPayment(DataTypes::Chunk),
385            RecordHeader::try_deserialize(
386                &RecordHeader {
387                    kind: RecordKind::DataWithPayment(DataTypes::Chunk)
388                }
389                .try_serialize()?
390            )?
391            .kind
392        );
393
394        // Test DataWithMerklePayment: 20-23
395        assert_eq!(
396            RecordKind::DataWithMerklePayment(DataTypes::Chunk),
397            RecordHeader::try_deserialize(
398                &RecordHeader {
399                    kind: RecordKind::DataWithMerklePayment(DataTypes::Chunk)
400                }
401                .try_serialize()?
402            )?
403            .kind
404        );
405
406        Ok(())
407    }
408
409    #[test]
410    fn test_merkle_payment_roundtrip() -> Result<()> {
411        // Test that all Merkle payment variants can roundtrip serialize/deserialize
412        let data_types = vec![
413            DataTypes::Chunk,
414            DataTypes::GraphEntry,
415            DataTypes::Pointer,
416            DataTypes::Scratchpad,
417        ];
418
419        for data_type in data_types {
420            let original = RecordKind::DataWithMerklePayment(data_type);
421            let header = RecordHeader { kind: original };
422
423            let serialized = header.try_serialize()?;
424            let deserialized = RecordHeader::try_deserialize(&serialized)?;
425
426            assert_eq!(
427                original, deserialized.kind,
428                "Merkle payment variant for {data_type:?} should roundtrip correctly"
429            );
430        }
431
432        Ok(())
433    }
434}