• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

input-output-hk / catalyst-libs / 19767538642

28 Nov 2025 03:07PM UTC coverage: 67.749% (-0.02%) from 67.771%
19767538642

push

github

web-flow
feat(rust): Implement `dht_provide()` and `dht_get_providers()` (#666)

* Implement `dht_provide()` and `dht_get_providers()`

* Cleanup

* Fix lints

* `identity()` returns identity, not peer id

0 of 24 new or added lines in 1 file covered. (0.0%)

11 existing lines in 3 files now uncovered.

13961 of 20607 relevant lines covered (67.75%)

2635.4 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.65
/rust/cbork-utils/src/array.rs
1
//! CBOR array (CBOR major type 4) structure with CBOR decoding and encoding
2
//! functionality. Supports deterministically encoded rules (RFC 8949 Section 4.2) if
3
//! corresponding option is enabled.
4

5
use std::{ops::Deref, vec::IntoIter};
6

7
use crate::{
8
    decode_context::DecodeCtx, decode_helper::get_bytes, deterministic_helper::CBOR_MAX_TINY_VALUE,
9
};
10

11
/// Represents a CBOR array, preserving original decoding order of values.
12
#[derive(Clone, Debug, PartialEq, Eq)]
13
pub struct Array(Vec<Vec<u8>>);
14

15
impl Deref for Array {
16
    type Target = Vec<Vec<u8>>;
17

18
    fn deref(&self) -> &Self::Target {
6✔
19
        &self.0
6✔
20
    }
6✔
21
}
22

23
impl IntoIterator for Array {
24
    type IntoIter = IntoIter<Vec<u8>>;
25
    type Item = Vec<u8>;
26

27
    fn into_iter(self) -> Self::IntoIter {
×
28
        self.0.into_iter()
×
29
    }
×
30
}
31

32
/// Major type indicator for CBOR arrays (major type 4: 100 in top 3 bits)
33
/// As per RFC 8949 Section 4.2, arrays in deterministic encoding must:
34
/// - Have lengths encoded minimally (Section 4.2.1)
35
/// - Use definite-length encoding only (Section 4.2.2)
36
/// - Have all elements themselves deterministically encoded
37
const CBOR_MAJOR_TYPE_ARRAY: u8 = 4 << 5;
38

39
/// Initial byte for a CBOR array whose length is encoded as an 8-bit unsigned integer
40
/// (uint8).
41
///
42
/// This value combines the array major type (4) with the additional information value
43
/// (24) that indicates a uint8 length follows. The resulting byte is:
44
/// - High 3 bits: 100 (major type 4 for array)
45
/// - Low 5 bits: 24 (indicates uint8 length follows)
46
///
47
/// Used when encoding CBOR arrays with lengths between 24 and 255 elements.
48
const CBOR_ARRAY_LENGTH_UINT8: u8 = CBOR_MAJOR_TYPE_ARRAY | 24; // For uint8 length encoding
49

50
/// Decodes a CBOR array with deterministic encoding validation (RFC 8949 Section 4.2)
51
/// Returns the raw bytes of the array elements if it passes all deterministic validation
52
/// rules.
53
///
54
/// From RFC 8949 Section 4.2:
55
/// Arrays must follow these deterministic encoding rules:
56
/// - Array lengths must use minimal encoding (Section 4.2.1)
57
/// - Indefinite-length arrays are not allowed (Section 4.2.2)
58
/// - All array elements must themselves be deterministically encoded
59
///
60
/// # Errors
61
///
62
/// Returns `DeterministicError` if:
63
/// - Input is empty (`UnexpectedEof`)
64
/// - Array uses indefinite-length encoding (`IndefiniteLength`)
65
/// - Array length is not encoded minimally (`NonMinimalInt`)
66
/// - Array element decoding fails (`DecoderError`)
67
/// - Array elements are not deterministically encoded
68
impl minicbor::Decode<'_, DecodeCtx> for Array {
69
    fn decode(
15✔
70
        d: &mut minicbor::Decoder<'_>,
15✔
71
        ctx: &mut DecodeCtx,
15✔
72
    ) -> Result<Self, minicbor::decode::Error> {
15✔
73
        // Capture position before reading the array header
74
        let header_start_pos = d.position();
15✔
75

76
        // Handle both definite and indefinite-length arrays
77
        let length = d.array()?.ok_or_else(|| {
15✔
78
            minicbor::decode::Error::message(
2✔
79
                "Indefinite-length items must be made definite-length items",
80
            )
81
        })?;
2✔
82

83
        ctx.try_check(|| check_array_minimal_length(d, header_start_pos, length))?;
13✔
84

85
        decode_array_elements(d, length, ctx).map(Self)
12✔
86
    }
15✔
87
}
88

89
/// Validates that a CBOR array's length is encoded using the minimal number of bytes as
90
/// required by RFC 8949's deterministic encoding rules.
91
///
92
/// According to the deterministic encoding requirements:
93
/// - The length of an array MUST be encoded using the smallest possible CBOR additional
94
///   information value
95
/// - For values 0 through 23, the additional info byte is used directly
96
/// - For values that fit in 8, 16, 32, or 64 bits, the appropriate multi-byte encoding
97
///   must be used
98
///
99
/// # Specification Reference
100
/// This implementation follows RFC 8949 Section 4.2.1 which requires that:
101
/// "The length of arrays, maps, and strings MUST be encoded using the smallest possible
102
/// CBOR additional information value."
103
fn check_array_minimal_length(
12✔
104
    decoder: &minicbor::Decoder,
12✔
105
    header_start_pos: usize,
12✔
106
    value: u64,
12✔
107
) -> Result<(), minicbor::decode::Error> {
12✔
108
    // For zero length, 0x80 is always the minimal encoding
109
    if value == 0 {
12✔
110
        return Ok(());
1✔
111
    }
11✔
112

113
    let initial_byte = decoder
11✔
114
        .input()
11✔
115
        .get(header_start_pos)
11✔
116
        .copied()
11✔
117
        .ok_or_else(|| {
11✔
118
            minicbor::decode::Error::message("Cannot read initial byte for minimality check")
×
119
        })?;
×
120

121
    // Only check minimality for array length encodings using uint8
122
    // Immediate values (0-23) are already minimal by definition
123
    if initial_byte == CBOR_ARRAY_LENGTH_UINT8 && value <= CBOR_MAX_TINY_VALUE {
11✔
124
        return Err(minicbor::decode::Error::message(
1✔
125
            "array minimal length failure",
1✔
126
        ));
1✔
127
    }
10✔
128

129
    Ok(())
10✔
130
}
12✔
131

132
/// Decodes all elements in the array
133
fn decode_array_elements(
12✔
134
    d: &mut minicbor::Decoder,
12✔
135
    length: u64,
12✔
136
    ctx: &mut DecodeCtx,
12✔
137
) -> Result<Vec<Vec<u8>>, minicbor::decode::Error> {
12✔
138
    let capacity = usize::try_from(length).map_err(|_| {
12✔
139
        minicbor::decode::Error::message("Array length too large for current platform")
×
140
    })?;
×
141
    let mut elements = Vec::with_capacity(capacity);
12✔
142

143
    // Decode each array element
144
    for _ in 0..length {
12✔
145
        // Record the starting position of the element
146
        let element_start = d.position();
27✔
147

148
        // Skip over the element to find its end position
149
        d.skip()?;
27✔
150
        let element_end = d.position();
27✔
151

152
        // The elements themselves must be deterministically encoded (4.2.1)
153
        let element_bytes = get_bytes(d, element_start, element_end)?.to_vec();
27✔
154

155
        elements.push(element_bytes);
27✔
156
    }
157

158
    if matches!(ctx, DecodeCtx::ArrayDeterministic) {
12✔
159
        ctx.try_check(|| validate_array_ordering(&elements))?;
2✔
160
    }
10✔
161

162
    Ok(elements)
11✔
163
}
12✔
164

165
/// Validates array elements are properly ordered according to RFC 8949 Section 4.2.3.
166
///
167
/// Ordering rules:
168
///   1. If two items differ in length → shorter sorts first.
169
///   2. If lengths are equal → compare byte-wise lexicographically.
170
///
171
/// Returns Ok(()) if elements are correctly ordered.
172
/// Returns Err(...) if any adjacent pair violates ordering.
173
fn validate_array_ordering(elements: &[Vec<u8>]) -> Result<(), minicbor::decode::Error> {
2✔
174
    for pair in elements.windows(2) {
4✔
175
        let [prev, current] = pair else {
4✔
176
            // fails if the array has 0-1 element
UNCOV
177
            return Ok(());
×
178
        };
179

180
        let ord = match prev.len().cmp(&current.len()) {
4✔
181
            std::cmp::Ordering::Equal => prev.as_slice().cmp(current.as_slice()),
1✔
182
            other => other,
3✔
183
        };
184

185
        if ord == std::cmp::Ordering::Greater {
4✔
186
            return Err(minicbor::decode::Error::message(
1✔
187
                "Array elements are not ordered deterministically",
1✔
188
            ));
1✔
189
        }
3✔
190
    }
191

192
    Ok(())
1✔
193
}
2✔
194

195
#[cfg(test)]
196
mod tests {
197
    use minicbor::{Decode, Decoder};
198

199
    use super::*;
200

201
    /// Ensures that encoding and decoding an array preserves:
202
    /// - The exact byte representation of elements
203
    /// - The definite length encoding format
204
    /// - The order of elements
205
    #[test]
206
    fn test_array_bytes_roundtrip() {
1✔
207
        // Create a valid deterministic array encoding
208
        let mut decoder = Decoder::new(&[
1✔
209
            0x82, // 2 elements
1✔
210
            0x41, 0x01, // h'01'
1✔
211
            0x42, 0x01, 0x02, // h'0102'
1✔
212
        ]);
1✔
213
        let result = Array::decode(&mut decoder, &mut DecodeCtx::Deterministic).unwrap();
1✔
214

215
        // Verify we got back exactly the same bytes
216
        assert_eq!(
1✔
217
            result,
218
            Array(vec![
1✔
219
                vec![0x41, 0x01],       // h'01'
1✔
220
                vec![0x42, 0x01, 0x02], // h'0102'
1✔
221
            ])
1✔
222
        );
223
    }
1✔
224

225
    /// Test empty array handling - special case mentioned in RFC 8949.
226
    /// An empty array is valid and must still follow length encoding rules
227
    /// from Section 4.2.1.
228
    #[test]
229
    fn test_empty_array() {
1✔
230
        let mut decoder = Decoder::new(&[
1✔
231
            0x80, // Array with 0 elements - encoded with immediate value as per Section 4.2.1
1✔
232
        ]);
1✔
233
        assert!(Array::decode(&mut decoder, &mut DecodeCtx::Deterministic).is_ok());
1✔
234
    }
1✔
235

236
    /// Test minimal length encoding rules for arrays as specified in RFC 8949 Section
237
    /// 4.2.1
238
    ///
239
    /// From RFC 8949 Section 4.2.1:
240
    /// "The length of arrays, maps, strings, and byte strings must be encoded in the
241
    /// smallest possible way. For arrays (major type 4), lengths 0-23 must be encoded
242
    /// in the initial byte."
243
    #[test]
244
    fn test_array_minimal_length_encoding() {
1✔
245
        // Test case 1: Valid minimal encoding (length = 1)
246
        let mut decoder = Decoder::new(&[
1✔
247
            0x81, // Array, length 1 (major type 4 with immediate value 1)
1✔
248
            0x01, // Element: unsigned int 1
1✔
249
        ]);
1✔
250
        assert!(Array::decode(&mut decoder, &mut DecodeCtx::Deterministic).is_ok());
1✔
251

252
        // Test case 2: Invalid non-minimal encoding (using additional info 24 for length 1)
253
        let mut decoder = Decoder::new(&[
1✔
254
            0x98, // Array with additional info = 24 (0x80 | 0x18)
1✔
255
            0x01, // Length encoded as uint8 = 1
1✔
256
            0x01, // Element: unsigned int 1
1✔
257
        ]);
1✔
258
        assert!(Array::decode(&mut decoder.clone(), &mut DecodeCtx::Deterministic).is_err());
1✔
259
        assert!(Array::decode(&mut decoder, &mut DecodeCtx::non_deterministic()).is_ok());
1✔
260
    }
1✔
261

262
    /// Test handling of complex element structures while maintaining deterministic
263
    /// encoding
264
    ///
265
    /// RFC 8949 Section 4.2 requires that all elements be deterministically encoded:
266
    /// "All contained items must also follow the same rules."
267
    #[test]
268
    fn test_array_complex_elements() {
1✔
269
        let mut decoder = Decoder::new(&[
1✔
270
            0x84, // Array with 4 elements
1✔
271
            0x41, 0x01, // Element 1: simple 1-byte string
1✔
272
            0x42, 0x01, 0x02, // Element 2: 2-byte string
1✔
273
            0x62, 0x68, 0x69, // Element 3: "hi"
1✔
274
            0xF9, 0x00, 0x00, // Element 4: float 0.0 half-precision canonical encoding
1✔
275
        ]);
1✔
276
        assert!(Array::decode(&mut decoder, &mut DecodeCtx::Deterministic).is_ok());
1✔
277
    }
1✔
278

279
    /// Test edge cases for array encoding while maintaining compliance with RFC 8949
280
    ///
281
    /// These cases test boundary conditions that must still follow all rules from
282
    /// Section 4.2:
283
    /// - Minimal length encoding (4.2.1)
284
    /// - No indefinite lengths (4.2.2)
285
    /// - Deterministic element encoding
286
    #[test]
287
    fn test_array_edge_cases() {
1✔
288
        // Single element array - must still follow minimal length encoding rules
289
        let mut decoder = Decoder::new(&[
1✔
290
            0x81, // Array with 1 element (using immediate value as per Section 4.2.1)
1✔
291
            0x41, 0x01, // Element: 1-byte string
1✔
292
        ]);
1✔
293
        assert!(Array::decode(&mut decoder, &mut DecodeCtx::Deterministic).is_ok());
1✔
294

295
        // Array with zero-length string element - tests smallest possible element case
296
        let mut decoder = Decoder::new(&[
1✔
297
            0x81, // Array with 1 element
1✔
298
            0x40, // Element: 0-byte string (smallest possible element)
1✔
299
        ]);
1✔
300
        assert!(Array::decode(&mut decoder, &mut DecodeCtx::Deterministic).is_ok());
1✔
301
    }
1✔
302

303
    /// Test array with multiple elements of different types
304
    #[test]
305
    fn test_array_mixed_elements() {
1✔
306
        // Array with integer, string, and nested array elements
307
        let mut decoder = Decoder::new(&[
1✔
308
            0x83, // Array with 3 elements
1✔
309
            0x01, // Element 1: unsigned int 1
1✔
310
            0x41, 0x48, // Element 2: 1-byte string "H"
1✔
311
            0x81, 0x02, // Element 3: nested array with one element (unsigned int 2)
1✔
312
        ]);
1✔
313
        assert!(Array::decode(&mut decoder, &mut DecodeCtx::Deterministic).is_ok());
1✔
314
    }
1✔
315

316
    /// Test array with multiple elements
317
    #[allow(clippy::indexing_slicing)]
318
    #[test]
319
    fn test_array_larger_size() {
1✔
320
        // Test with a simple array of 5 single-byte strings
321
        let mut decoder = Decoder::new(&[
1✔
322
            0x85, // Array with 5 elements
1✔
323
            0x41, 0x01, // Element 1: 1-byte string with value 0x01
1✔
324
            0x41, 0x02, // Element 2: 1-byte string with value 0x02
1✔
325
            0x41, 0x03, // Element 3: 1-byte string with value 0x03
1✔
326
            0x41, 0x04, // Element 4: 1-byte string with value 0x04
1✔
327
            0x41, 0x05, // Element 5: 1-byte string with value 0x05
1✔
328
        ]);
1✔
329
        let result = Array::decode(&mut decoder, &mut DecodeCtx::Deterministic);
1✔
330
        assert!(result.is_ok());
1✔
331

332
        let array = result.unwrap();
1✔
333
        assert_eq!(array.len(), 5);
1✔
334

335
        // Verify the elements are correctly decoded
336
        assert_eq!(array[0], vec![0x41, 0x01]);
1✔
337
        assert_eq!(array[1], vec![0x41, 0x02]);
1✔
338
        assert_eq!(array[2], vec![0x41, 0x03]);
1✔
339
        assert_eq!(array[3], vec![0x41, 0x04]);
1✔
340
        assert_eq!(array[4], vec![0x41, 0x05]);
1✔
341
    }
1✔
342

343
    /// Test indefinite-length array rejection in deterministic mode
344
    /// and acceptance in non-deterministic mode
345
    #[test]
346
    fn test_array_with_indefinite_length() {
1✔
347
        // Indefinite-length array (not allowed in deterministic encoding)
348
        let decoder = Decoder::new(&[
1✔
349
            0x9F, // Array with indefinite length
1✔
350
            0x01, // Element 1
1✔
351
            0x02, // Element 2
1✔
352
            0xFF, // Break code
1✔
353
        ]);
1✔
354
        assert!(Array::decode(&mut decoder.clone(), &mut DecodeCtx::Deterministic).is_err());
1✔
355
        // Even it's non-deterministic, this should fail, as we enforce for the defined length.
356
        assert!(Array::decode(&mut decoder.clone(), &mut DecodeCtx::non_deterministic()).is_err());
1✔
357
    }
1✔
358

359
    #[test]
360
    fn test_deterministic_array_decoding() {
1✔
361
        let invalid_bytes = [
1✔
362
            0x83, // array of length 3
1✔
363
            0x41, 0x02, // element: [0x02]
1✔
364
            0x42, 0x01, 0x01, // element: [0x01, 0x01]
1✔
365
            0x41, 0x01, // element: [0x01]
1✔
366
        ];
1✔
367

368
        let mut decoder = Decoder::new(&invalid_bytes);
1✔
369
        let result = Array::decode(&mut decoder, &mut DecodeCtx::ArrayDeterministic);
1✔
370
        assert!(result.is_err());
1✔
371

372
        // should not affect to other decoding ctx
373
        let mut decoder = Decoder::new(&invalid_bytes);
1✔
374
        let result = Array::decode(&mut decoder, &mut DecodeCtx::Deterministic);
1✔
375
        assert!(result.is_ok());
1✔
376

377
        let valid_bytes = [0x83, 0x41, 0x01, 0x41, 0x02, 0x42, 0x01, 0x01];
1✔
378

379
        let mut decoder = Decoder::new(&valid_bytes);
1✔
380
        let result = Array::decode(&mut decoder, &mut DecodeCtx::ArrayDeterministic);
1✔
381
        assert!(result.is_ok());
1✔
382
    }
1✔
383
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc