tlsn_core/
transcript.rs

1//! Transcript types.
2//!
3//! All application data communicated over a TLS connection is referred to as a
4//! [`Transcript`]. A transcript is essentially just two vectors of bytes, each
5//! corresponding to a [`Direction`].
6//!
7//! TLS operates over a bidirectional byte stream, and thus there are no
8//! application layer semantics present in the transcript. For example, HTTPS is
9//! an application layer protocol that runs *over TLS* so there is no concept of
10//! "requests" or "responses" in the transcript itself. These semantics must be
11//! recovered by parsing the application data and relating it to the bytes
12//! in the transcript.
13//!
14//! ## Selective Disclosure
15//!
16//! Using a [`TranscriptProof`] a Prover can selectively disclose parts of a
17//! transcript to a Verifier in the form of a [`PartialTranscript`]. A Verifier
18//! always learns the length of the transcript, but sensitive data can be
19//! withheld.
20
21mod commit;
22pub mod hash;
23mod proof;
24mod tls;
25
26use std::{fmt, ops::Range};
27
28use rangeset::{
29    iter::RangeIterator,
30    ops::{Index, Set},
31    set::RangeSet,
32};
33use serde::{Deserialize, Serialize};
34
35use crate::connection::TranscriptLength;
36
37pub use commit::{
38    TranscriptCommitConfig, TranscriptCommitConfigBuilder, TranscriptCommitConfigBuilderError,
39    TranscriptCommitRequest, TranscriptCommitment, TranscriptCommitmentKind, TranscriptSecret,
40};
41pub use proof::{
42    TranscriptProof, TranscriptProofBuilder, TranscriptProofBuilderError, TranscriptProofError,
43};
44pub use tls::{ContentType, Record, TlsTranscript};
45
46/// A transcript contains the plaintext of all application data communicated
47/// between the Prover and the Server.
48#[derive(Clone, Serialize, Deserialize)]
49pub struct Transcript {
50    /// Data sent from the Prover to the Server.
51    sent: Vec<u8>,
52    /// Data received by the Prover from the Server.
53    received: Vec<u8>,
54}
55
56opaque_debug::implement!(Transcript);
57
58impl Transcript {
59    /// Creates a new transcript.
60    pub fn new(sent: impl Into<Vec<u8>>, received: impl Into<Vec<u8>>) -> Self {
61        Self {
62            sent: sent.into(),
63            received: received.into(),
64        }
65    }
66
67    /// Returns a reference to the sent data.
68    pub fn sent(&self) -> &[u8] {
69        &self.sent
70    }
71
72    /// Returns a reference to the received data.
73    pub fn received(&self) -> &[u8] {
74        &self.received
75    }
76
77    /// Returns the length of the sent and received data, respectively.
78    #[allow(clippy::len_without_is_empty)]
79    pub fn len(&self) -> (usize, usize) {
80        (self.sent.len(), self.received.len())
81    }
82
83    /// Returns the length of the transcript in the given direction.
84    pub(crate) fn len_of_direction(&self, direction: Direction) -> usize {
85        match direction {
86            Direction::Sent => self.sent.len(),
87            Direction::Received => self.received.len(),
88        }
89    }
90
91    /// Returns the transcript length.
92    pub fn length(&self) -> TranscriptLength {
93        TranscriptLength {
94            sent: self.sent.len() as u32,
95            received: self.received.len() as u32,
96        }
97    }
98
99    /// Returns the subsequence of the transcript with the provided index,
100    /// returning `None` if the index is out of bounds.
101    pub fn get(&self, direction: Direction, idx: &RangeSet<usize>) -> Option<Subsequence> {
102        let data = match direction {
103            Direction::Sent => &self.sent,
104            Direction::Received => &self.received,
105        };
106
107        if idx.end().unwrap_or(0) > data.len() {
108            return None;
109        }
110
111        Some(
112            Subsequence::new(
113                idx.clone(),
114                data.index(idx).fold(Vec::new(), |mut acc, s| {
115                    acc.extend_from_slice(s);
116                    acc
117                }),
118            )
119            .expect("data is same length as index"),
120        )
121    }
122
123    /// Returns a partial transcript containing the provided indices.
124    ///
125    /// # Panics
126    ///
127    /// Panics if the indices are out of bounds.
128    ///
129    /// # Arguments
130    ///
131    /// * `sent_idx` - The indices of the sent data to include.
132    /// * `recv_idx` - The indices of the received data to include.
133    pub fn to_partial(
134        &self,
135        sent_idx: RangeSet<usize>,
136        recv_idx: RangeSet<usize>,
137    ) -> PartialTranscript {
138        let mut sent = vec![0; self.sent.len()];
139        let mut received = vec![0; self.received.len()];
140
141        for range in sent_idx.iter() {
142            sent[range.clone()].copy_from_slice(&self.sent[range]);
143        }
144
145        for range in recv_idx.iter() {
146            received[range.clone()].copy_from_slice(&self.received[range]);
147        }
148
149        PartialTranscript {
150            sent,
151            received,
152            sent_authed_idx: sent_idx,
153            received_authed_idx: recv_idx,
154        }
155    }
156}
157
158/// A partial transcript.
159///
160/// A partial transcript is a transcript which may not have all the data
161/// authenticated.
162#[derive(Debug, Clone, Serialize, Deserialize)]
163#[serde(try_from = "CompressedPartialTranscript")]
164#[serde(into = "CompressedPartialTranscript")]
165#[cfg_attr(test, derive(PartialEq))]
166pub struct PartialTranscript {
167    /// Data sent from the Prover to the Server.
168    sent: Vec<u8>,
169    /// Data received by the Prover from the Server.
170    received: Vec<u8>,
171    /// Index of `sent` which have been authenticated.
172    sent_authed_idx: RangeSet<usize>,
173    /// Index of `received` which have been authenticated.
174    received_authed_idx: RangeSet<usize>,
175}
176
177/// `PartialTranscript` in a compressed form.
178#[derive(Debug, Clone, Serialize, Deserialize)]
179#[serde(try_from = "validation::CompressedPartialTranscriptUnchecked")]
180pub struct CompressedPartialTranscript {
181    /// Sent data which has been authenticated.
182    sent_authed: Vec<u8>,
183    /// Received data which has been authenticated.
184    received_authed: Vec<u8>,
185    /// Index of `sent_authed`.
186    sent_idx: RangeSet<usize>,
187    /// Index of `received_authed`.
188    recv_idx: RangeSet<usize>,
189    /// Total bytelength of sent data in the original partial transcript.
190    sent_total: usize,
191    /// Total bytelength of received data in the original partial transcript.
192    recv_total: usize,
193}
194
195impl From<PartialTranscript> for CompressedPartialTranscript {
196    fn from(uncompressed: PartialTranscript) -> Self {
197        Self {
198            sent_authed: uncompressed.sent.index(&uncompressed.sent_authed_idx).fold(
199                Vec::new(),
200                |mut acc, s| {
201                    acc.extend_from_slice(s);
202                    acc
203                },
204            ),
205            received_authed: uncompressed
206                .received
207                .index(&uncompressed.received_authed_idx)
208                .fold(Vec::new(), |mut acc, s| {
209                    acc.extend_from_slice(s);
210                    acc
211                }),
212            sent_idx: uncompressed.sent_authed_idx,
213            recv_idx: uncompressed.received_authed_idx,
214            sent_total: uncompressed.sent.len(),
215            recv_total: uncompressed.received.len(),
216        }
217    }
218}
219
220impl From<CompressedPartialTranscript> for PartialTranscript {
221    fn from(compressed: CompressedPartialTranscript) -> Self {
222        let mut sent = vec![0; compressed.sent_total];
223        let mut received = vec![0; compressed.recv_total];
224
225        let mut offset = 0;
226
227        for range in compressed.sent_idx.iter() {
228            sent[range.clone()]
229                .copy_from_slice(&compressed.sent_authed[offset..offset + range.len()]);
230            offset += range.len();
231        }
232
233        let mut offset = 0;
234
235        for range in compressed.recv_idx.iter() {
236            received[range.clone()]
237                .copy_from_slice(&compressed.received_authed[offset..offset + range.len()]);
238            offset += range.len();
239        }
240
241        Self {
242            sent,
243            received,
244            sent_authed_idx: compressed.sent_idx,
245            received_authed_idx: compressed.recv_idx,
246        }
247    }
248}
249
250impl PartialTranscript {
251    /// Creates a new partial transcript initalized to all 0s.
252    ///
253    /// # Arguments
254    ///
255    /// * `sent_len` - The length of the sent data.
256    /// * `received_len` - The length of the received data.
257    pub fn new(sent_len: usize, received_len: usize) -> Self {
258        Self {
259            sent: vec![0; sent_len],
260            received: vec![0; received_len],
261            sent_authed_idx: RangeSet::default(),
262            received_authed_idx: RangeSet::default(),
263        }
264    }
265
266    /// Returns the length of the sent transcript.
267    pub fn len_sent(&self) -> usize {
268        self.sent.len()
269    }
270
271    /// Returns the length of the received transcript.
272    pub fn len_received(&self) -> usize {
273        self.received.len()
274    }
275
276    /// Returns whether the transcript is complete.
277    pub fn is_complete(&self) -> bool {
278        self.sent_authed_idx.len() == self.sent.len()
279            && self.received_authed_idx.len() == self.received.len()
280    }
281
282    /// Returns whether the index is in bounds of the transcript.
283    pub fn contains(&self, direction: Direction, idx: &RangeSet<usize>) -> bool {
284        match direction {
285            Direction::Sent => idx.end().unwrap_or(0) <= self.sent.len(),
286            Direction::Received => idx.end().unwrap_or(0) <= self.received.len(),
287        }
288    }
289
290    /// Returns a reference to the sent data.
291    ///
292    /// # Warning
293    ///
294    /// Not all of the data in the transcript may have been authenticated. See
295    /// [sent_authed](PartialTranscript::sent_authed) for a set of ranges which
296    /// have been.
297    pub fn sent_unsafe(&self) -> &[u8] {
298        &self.sent
299    }
300
301    /// Returns a reference to the received data.
302    ///
303    /// # Warning
304    ///
305    /// Not all of the data in the transcript may have been authenticated. See
306    /// [received_authed](PartialTranscript::received_authed) for a set of
307    /// ranges which have been.
308    pub fn received_unsafe(&self) -> &[u8] {
309        &self.received
310    }
311
312    /// Returns the index of sent data which have been authenticated.
313    pub fn sent_authed(&self) -> &RangeSet<usize> {
314        &self.sent_authed_idx
315    }
316
317    /// Returns the index of received data which have been authenticated.
318    pub fn received_authed(&self) -> &RangeSet<usize> {
319        &self.received_authed_idx
320    }
321
322    /// Returns the index of sent data which haven't been authenticated.
323    pub fn sent_unauthed(&self) -> RangeSet<usize> {
324        (0..self.sent.len())
325            .difference(&self.sent_authed_idx)
326            .into_set()
327    }
328
329    /// Returns the index of received data which haven't been authenticated.
330    pub fn received_unauthed(&self) -> RangeSet<usize> {
331        (0..self.received.len())
332            .difference(&self.received_authed_idx)
333            .into_set()
334    }
335
336    /// Returns an iterator over the authenticated data in the transcript.
337    pub fn iter(&self, direction: Direction) -> impl Iterator<Item = u8> + '_ {
338        let (data, authed) = match direction {
339            Direction::Sent => (&self.sent, &self.sent_authed_idx),
340            Direction::Received => (&self.received, &self.received_authed_idx),
341        };
342
343        authed.iter_values().map(move |i| data[i])
344    }
345
346    /// Unions the authenticated data of this transcript with another.
347    ///
348    /// # Panics
349    ///
350    /// Panics if the other transcript is not the same length.
351    pub fn union_transcript(&mut self, other: &PartialTranscript) {
352        assert_eq!(
353            self.sent.len(),
354            other.sent.len(),
355            "sent data are not the same length"
356        );
357        assert_eq!(
358            self.received.len(),
359            other.received.len(),
360            "received data are not the same length"
361        );
362
363        for range in other.sent_authed_idx.difference(&self.sent_authed_idx) {
364            self.sent[range.clone()].copy_from_slice(&other.sent[range]);
365        }
366
367        for range in other
368            .received_authed_idx
369            .difference(&self.received_authed_idx)
370        {
371            self.received[range.clone()].copy_from_slice(&other.received[range]);
372        }
373
374        self.sent_authed_idx.union_mut(&other.sent_authed_idx);
375        self.received_authed_idx
376            .union_mut(&other.received_authed_idx);
377    }
378
379    /// Unions an authenticated subsequence into this transcript.
380    ///
381    /// # Panics
382    ///
383    /// Panics if the subsequence is outside the bounds of the transcript.
384    pub fn union_subsequence(&mut self, direction: Direction, seq: &Subsequence) {
385        match direction {
386            Direction::Sent => {
387                seq.copy_to(&mut self.sent);
388                self.sent_authed_idx.union_mut(&seq.idx);
389            }
390            Direction::Received => {
391                seq.copy_to(&mut self.received);
392                self.received_authed_idx.union_mut(&seq.idx);
393            }
394        }
395    }
396
397    /// Sets all bytes in the transcript which haven't been authenticated.
398    ///
399    /// # Arguments
400    ///
401    /// * `value` - The value to set the unauthenticated bytes to
402    pub fn set_unauthed(&mut self, value: u8) {
403        for range in self.sent_unauthed().iter() {
404            self.sent[range].fill(value);
405        }
406        for range in self.received_unauthed().iter() {
407            self.received[range].fill(value);
408        }
409    }
410
411    /// Sets all bytes in the transcript which haven't been authenticated within
412    /// the given range.
413    ///
414    /// # Arguments
415    ///
416    /// * `value` - The value to set the unauthenticated bytes to
417    /// * `range` - The range of bytes to set
418    pub fn set_unauthed_range(&mut self, value: u8, direction: Direction, range: Range<usize>) {
419        match direction {
420            Direction::Sent => {
421                for r in range.difference(&self.sent_authed_idx) {
422                    self.sent[r].fill(value);
423                }
424            }
425            Direction::Received => {
426                for r in range.difference(&self.received_authed_idx) {
427                    self.received[r].fill(value);
428                }
429            }
430        }
431    }
432}
433
434/// The direction of data communicated over a TLS connection.
435///
436/// This is used to differentiate between data sent from the Prover to the TLS
437/// peer, and data received by the Prover from the TLS peer (client or server).
438#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
439pub enum Direction {
440    /// Sent from the Prover to the TLS peer.
441    Sent = 0x00,
442    /// Received by the prover from the TLS peer.
443    Received = 0x01,
444}
445
446impl fmt::Display for Direction {
447    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
448        match self {
449            Direction::Sent => write!(f, "sent"),
450            Direction::Received => write!(f, "received"),
451        }
452    }
453}
454
455/// Transcript subsequence.
456#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
457#[serde(try_from = "validation::SubsequenceUnchecked")]
458pub struct Subsequence {
459    /// Index of the subsequence.
460    idx: RangeSet<usize>,
461    /// Data of the subsequence.
462    data: Vec<u8>,
463}
464
465impl Subsequence {
466    /// Creates a new subsequence.
467    pub fn new(idx: RangeSet<usize>, data: Vec<u8>) -> Result<Self, InvalidSubsequence> {
468        if idx.len() != data.len() {
469            return Err(InvalidSubsequence(
470                "index length does not match data length",
471            ));
472        }
473
474        Ok(Self { idx, data })
475    }
476
477    /// Returns the index of the subsequence.
478    pub fn index(&self) -> &RangeSet<usize> {
479        &self.idx
480    }
481
482    /// Returns the data of the subsequence.
483    pub fn data(&self) -> &[u8] {
484        &self.data
485    }
486
487    /// Returns the length of the subsequence.
488    #[allow(clippy::len_without_is_empty)]
489    pub fn len(&self) -> usize {
490        self.data.len()
491    }
492
493    /// Returns the inner parts of the subsequence.
494    pub fn into_parts(self) -> (RangeSet<usize>, Vec<u8>) {
495        (self.idx, self.data)
496    }
497
498    /// Copies the subsequence data into the given destination.
499    ///
500    /// # Panics
501    ///
502    /// Panics if the subsequence ranges are out of bounds.
503    pub(crate) fn copy_to(&self, dest: &mut [u8]) {
504        let mut offset = 0;
505        for range in self.idx.iter() {
506            dest[range.clone()].copy_from_slice(&self.data[offset..offset + range.len()]);
507            offset += range.len();
508        }
509    }
510}
511
512/// Invalid subsequence error.
513#[derive(Debug, thiserror::Error)]
514#[error("invalid subsequence: {0}")]
515pub struct InvalidSubsequence(&'static str);
516
517mod validation {
518    use super::*;
519
520    #[derive(Debug, Deserialize)]
521    pub(super) struct SubsequenceUnchecked {
522        idx: RangeSet<usize>,
523        data: Vec<u8>,
524    }
525
526    impl TryFrom<SubsequenceUnchecked> for Subsequence {
527        type Error = InvalidSubsequence;
528
529        fn try_from(unchecked: SubsequenceUnchecked) -> Result<Self, Self::Error> {
530            Self::new(unchecked.idx, unchecked.data)
531        }
532    }
533
534    /// Invalid compressed partial transcript error.
535    #[derive(Debug, thiserror::Error)]
536    #[error("invalid compressed partial transcript: {0}")]
537    pub struct InvalidCompressedPartialTranscript(&'static str);
538
539    #[derive(Debug, Deserialize)]
540    #[cfg_attr(test, derive(Serialize))]
541    pub(super) struct CompressedPartialTranscriptUnchecked {
542        sent_authed: Vec<u8>,
543        received_authed: Vec<u8>,
544        sent_idx: RangeSet<usize>,
545        recv_idx: RangeSet<usize>,
546        sent_total: usize,
547        recv_total: usize,
548    }
549
550    impl TryFrom<CompressedPartialTranscriptUnchecked> for CompressedPartialTranscript {
551        type Error = InvalidCompressedPartialTranscript;
552
553        fn try_from(unchecked: CompressedPartialTranscriptUnchecked) -> Result<Self, Self::Error> {
554            if unchecked.sent_authed.len() != unchecked.sent_idx.len()
555                || unchecked.received_authed.len() != unchecked.recv_idx.len()
556            {
557                return Err(InvalidCompressedPartialTranscript(
558                    "lengths of index and data don't match",
559                ));
560            }
561
562            if unchecked.sent_idx.end().unwrap_or(0) > unchecked.sent_total
563                || unchecked.recv_idx.end().unwrap_or(0) > unchecked.recv_total
564            {
565                return Err(InvalidCompressedPartialTranscript(
566                    "ranges are not in bounds of the data",
567                ));
568            }
569
570            Ok(Self {
571                received_authed: unchecked.received_authed,
572                recv_idx: unchecked.recv_idx,
573                recv_total: unchecked.recv_total,
574                sent_authed: unchecked.sent_authed,
575                sent_idx: unchecked.sent_idx,
576                sent_total: unchecked.sent_total,
577            })
578        }
579    }
580
581    #[cfg(test)]
582    mod tests {
583        use rstest::{fixture, rstest};
584
585        use super::*;
586
587        #[fixture]
588        fn partial_transcript() -> CompressedPartialTranscriptUnchecked {
589            CompressedPartialTranscriptUnchecked {
590                received_authed: vec![1, 2, 3, 11, 12, 13],
591                sent_authed: vec![4, 5, 6, 14, 15, 16],
592                recv_idx: RangeSet::from([1..4, 11..14]),
593                sent_idx: RangeSet::from([4..7, 14..17]),
594                sent_total: 20,
595                recv_total: 20,
596            }
597        }
598
599        #[rstest]
600        fn test_partial_transcript_valid(partial_transcript: CompressedPartialTranscriptUnchecked) {
601            let bytes = bincode::serialize(&partial_transcript).unwrap();
602            let transcript: Result<CompressedPartialTranscript, Box<bincode::ErrorKind>> =
603                bincode::deserialize(&bytes);
604            assert!(transcript.is_ok());
605        }
606
607        #[rstest]
608        // Expect to fail since the length of data and the length of the index do not
609        // match.
610        fn test_partial_transcript_invalid_lengths(
611            mut partial_transcript: CompressedPartialTranscriptUnchecked,
612        ) {
613            // Add an extra byte to the data.
614            let mut old = partial_transcript.sent_authed;
615            old.extend([1]);
616            partial_transcript.sent_authed = old;
617
618            let bytes = bincode::serialize(&partial_transcript).unwrap();
619            let transcript: Result<CompressedPartialTranscript, Box<bincode::ErrorKind>> =
620                bincode::deserialize(&bytes);
621            assert!(transcript.is_err());
622        }
623
624        #[rstest]
625        // Expect to fail since the index is out of bounds.
626        fn test_partial_transcript_invalid_ranges(
627            mut partial_transcript: CompressedPartialTranscriptUnchecked,
628        ) {
629            // Change the total to be less than the last range's end bound.
630            let end = partial_transcript.sent_idx.iter().next_back().unwrap().end;
631
632            partial_transcript.sent_total = end - 1;
633
634            let bytes = bincode::serialize(&partial_transcript).unwrap();
635            let transcript: Result<CompressedPartialTranscript, Box<bincode::ErrorKind>> =
636                bincode::deserialize(&bytes);
637            assert!(transcript.is_err());
638        }
639    }
640}
641
642#[cfg(test)]
643mod tests {
644    use rstest::{fixture, rstest};
645
646    use super::*;
647
648    #[fixture]
649    fn transcript() -> Transcript {
650        Transcript::new(
651            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
652            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
653        )
654    }
655
656    #[fixture]
657    fn partial_transcript() -> PartialTranscript {
658        transcript().to_partial(RangeSet::from([1..4, 6..9]), RangeSet::from([2..5, 7..10]))
659    }
660
661    #[rstest]
662    fn test_transcript_get_subsequence(transcript: Transcript) {
663        let subseq = transcript
664            .get(Direction::Received, &RangeSet::from([0..4, 7..10]))
665            .unwrap();
666        assert_eq!(subseq.data, vec![0, 1, 2, 3, 7, 8, 9]);
667
668        let subseq = transcript
669            .get(Direction::Sent, &RangeSet::from([0..4, 9..12]))
670            .unwrap();
671        assert_eq!(subseq.data, vec![0, 1, 2, 3, 9, 10, 11]);
672
673        let subseq = transcript.get(Direction::Received, &RangeSet::from([0..4, 7..10, 11..13]));
674        assert_eq!(subseq, None);
675
676        let subseq = transcript.get(Direction::Sent, &RangeSet::from([0..4, 7..10, 11..13]));
677        assert_eq!(subseq, None);
678    }
679
680    #[rstest]
681    fn test_partial_transcript_serialization_ok(partial_transcript: PartialTranscript) {
682        let bytes = bincode::serialize(&partial_transcript).unwrap();
683        let deserialized_transcript: PartialTranscript = bincode::deserialize(&bytes).unwrap();
684        assert_eq!(partial_transcript, deserialized_transcript);
685    }
686
687    #[rstest]
688    fn test_transcript_to_partial_success(transcript: Transcript) {
689        let partial = transcript.to_partial(RangeSet::from(0..2), RangeSet::from(3..7));
690        assert_eq!(partial.sent_unsafe(), [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
691        assert_eq!(
692            partial.received_unsafe(),
693            [0, 0, 0, 3, 4, 5, 6, 0, 0, 0, 0, 0]
694        );
695    }
696
697    #[rstest]
698    #[should_panic]
699    fn test_transcript_to_partial_failure(transcript: Transcript) {
700        let _ = transcript.to_partial(RangeSet::from(0..14), RangeSet::from(3..7));
701    }
702
703    #[rstest]
704    fn test_partial_transcript_contains(transcript: Transcript) {
705        let partial = transcript.to_partial(RangeSet::from(0..2), RangeSet::from(3..7));
706        assert!(partial.contains(Direction::Sent, &RangeSet::from([0..5, 7..10])));
707        assert!(!partial.contains(Direction::Received, &RangeSet::from([4..6, 7..13])))
708    }
709
710    #[rstest]
711    fn test_partial_transcript_unauthed(transcript: Transcript) {
712        let partial = transcript.to_partial(RangeSet::from(0..2), RangeSet::from(3..7));
713        assert_eq!(partial.sent_unauthed(), RangeSet::from(2..12));
714        assert_eq!(partial.received_unauthed(), RangeSet::from([0..3, 7..12]));
715    }
716
717    #[rstest]
718    fn test_partial_transcript_union_success(transcript: Transcript) {
719        // Non overlapping ranges.
720        let mut simple_partial = transcript.to_partial(RangeSet::from(0..2), RangeSet::from(3..7));
721
722        let other_simple_partial =
723            transcript.to_partial(RangeSet::from(3..5), RangeSet::from(1..2));
724
725        simple_partial.union_transcript(&other_simple_partial);
726
727        assert_eq!(
728            simple_partial.sent_unsafe(),
729            [0, 1, 0, 3, 4, 0, 0, 0, 0, 0, 0, 0]
730        );
731        assert_eq!(
732            simple_partial.received_unsafe(),
733            [0, 1, 0, 3, 4, 5, 6, 0, 0, 0, 0, 0]
734        );
735        assert_eq!(simple_partial.sent_authed(), &RangeSet::from([0..2, 3..5]));
736        assert_eq!(
737            simple_partial.received_authed(),
738            &RangeSet::from([1..2, 3..7])
739        );
740
741        // Overwrite with another partial transcript.
742
743        let another_simple_partial =
744            transcript.to_partial(RangeSet::from(1..4), RangeSet::from(6..9));
745
746        simple_partial.union_transcript(&another_simple_partial);
747
748        assert_eq!(
749            simple_partial.sent_unsafe(),
750            [0, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0]
751        );
752        assert_eq!(
753            simple_partial.received_unsafe(),
754            [0, 1, 0, 3, 4, 5, 6, 7, 8, 0, 0, 0]
755        );
756        assert_eq!(simple_partial.sent_authed(), &RangeSet::from(0..5));
757        assert_eq!(
758            simple_partial.received_authed(),
759            &RangeSet::from([1..2, 3..9])
760        );
761
762        // Overlapping ranges.
763        let mut overlap_partial = transcript.to_partial(RangeSet::from(4..6), RangeSet::from(3..7));
764
765        let other_overlap_partial =
766            transcript.to_partial(RangeSet::from(3..5), RangeSet::from(5..9));
767
768        overlap_partial.union_transcript(&other_overlap_partial);
769
770        assert_eq!(
771            overlap_partial.sent_unsafe(),
772            [0, 0, 0, 3, 4, 5, 0, 0, 0, 0, 0, 0]
773        );
774        assert_eq!(
775            overlap_partial.received_unsafe(),
776            [0, 0, 0, 3, 4, 5, 6, 7, 8, 0, 0, 0]
777        );
778        assert_eq!(overlap_partial.sent_authed(), &RangeSet::from([3..5, 4..6]));
779        assert_eq!(
780            overlap_partial.received_authed(),
781            &RangeSet::from([3..7, 5..9])
782        );
783
784        // Equal ranges.
785        let mut equal_partial = transcript.to_partial(RangeSet::from(4..6), RangeSet::from(3..7));
786
787        let other_equal_partial = transcript.to_partial(RangeSet::from(4..6), RangeSet::from(3..7));
788
789        equal_partial.union_transcript(&other_equal_partial);
790
791        assert_eq!(
792            equal_partial.sent_unsafe(),
793            [0, 0, 0, 0, 4, 5, 0, 0, 0, 0, 0, 0]
794        );
795        assert_eq!(
796            equal_partial.received_unsafe(),
797            [0, 0, 0, 3, 4, 5, 6, 0, 0, 0, 0, 0]
798        );
799        assert_eq!(equal_partial.sent_authed(), &RangeSet::from(4..6));
800        assert_eq!(equal_partial.received_authed(), &RangeSet::from(3..7));
801
802        // Subset ranges.
803        let mut subset_partial =
804            transcript.to_partial(RangeSet::from(4..10), RangeSet::from(3..11));
805
806        let other_subset_partial =
807            transcript.to_partial(RangeSet::from(6..9), RangeSet::from(5..6));
808
809        subset_partial.union_transcript(&other_subset_partial);
810
811        assert_eq!(
812            subset_partial.sent_unsafe(),
813            [0, 0, 0, 0, 4, 5, 6, 7, 8, 9, 0, 0]
814        );
815        assert_eq!(
816            subset_partial.received_unsafe(),
817            [0, 0, 0, 3, 4, 5, 6, 7, 8, 9, 10, 0]
818        );
819        assert_eq!(subset_partial.sent_authed(), &RangeSet::from(4..10));
820        assert_eq!(subset_partial.received_authed(), &RangeSet::from(3..11));
821    }
822
823    #[rstest]
824    #[should_panic]
825    fn test_partial_transcript_union_failure(transcript: Transcript) {
826        let mut partial = transcript.to_partial(RangeSet::from(4..10), RangeSet::from(3..11));
827
828        let other_transcript = Transcript::new(
829            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
830            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
831        );
832
833        let other_partial = other_transcript.to_partial(RangeSet::from(6..9), RangeSet::from(5..6));
834
835        partial.union_transcript(&other_partial);
836    }
837
838    #[rstest]
839    fn test_partial_transcript_union_subseq_success(transcript: Transcript) {
840        let mut partial = transcript.to_partial(RangeSet::from(4..10), RangeSet::from(3..11));
841        let sent_seq =
842            Subsequence::new(RangeSet::from([0..3, 5..7]), [0, 1, 2, 5, 6].into()).unwrap();
843        let recv_seq =
844            Subsequence::new(RangeSet::from([0..4, 5..7]), [0, 1, 2, 3, 5, 6].into()).unwrap();
845
846        partial.union_subsequence(Direction::Sent, &sent_seq);
847        partial.union_subsequence(Direction::Received, &recv_seq);
848
849        assert_eq!(partial.sent_unsafe(), [0, 1, 2, 0, 4, 5, 6, 7, 8, 9, 0, 0]);
850        assert_eq!(
851            partial.received_unsafe(),
852            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0]
853        );
854        assert_eq!(partial.sent_authed(), &RangeSet::from([0..3, 4..10]));
855        assert_eq!(partial.received_authed(), &RangeSet::from(0..11));
856
857        // Overwrite with another subseq.
858        let other_sent_seq = Subsequence::new(RangeSet::from(0..3), [3, 2, 1].into()).unwrap();
859
860        partial.union_subsequence(Direction::Sent, &other_sent_seq);
861        assert_eq!(partial.sent_unsafe(), [3, 2, 1, 0, 4, 5, 6, 7, 8, 9, 0, 0]);
862        assert_eq!(partial.sent_authed(), &RangeSet::from([0..3, 4..10]));
863    }
864
865    #[rstest]
866    #[should_panic]
867    fn test_partial_transcript_union_subseq_failure(transcript: Transcript) {
868        let mut partial = transcript.to_partial(RangeSet::from(4..10), RangeSet::from(3..11));
869
870        let sent_seq =
871            Subsequence::new(RangeSet::from([0..3, 13..15]), [0, 1, 2, 5, 6].into()).unwrap();
872
873        partial.union_subsequence(Direction::Sent, &sent_seq);
874    }
875
876    #[rstest]
877    fn test_partial_transcript_set_unauthed_range(transcript: Transcript) {
878        let mut partial = transcript.to_partial(RangeSet::from(4..10), RangeSet::from(3..7));
879
880        partial.set_unauthed_range(7, Direction::Sent, 2..5);
881        partial.set_unauthed_range(5, Direction::Sent, 0..2);
882        partial.set_unauthed_range(3, Direction::Received, 4..6);
883        partial.set_unauthed_range(1, Direction::Received, 3..7);
884
885        assert_eq!(partial.sent_unsafe(), [5, 5, 7, 7, 4, 5, 6, 7, 8, 9, 0, 0]);
886        assert_eq!(
887            partial.received_unsafe(),
888            [0, 0, 0, 3, 4, 5, 6, 0, 0, 0, 0, 0]
889        );
890    }
891
892    #[rstest]
893    #[should_panic]
894    fn test_subsequence_new_invalid_len() {
895        let _ = Subsequence::new(RangeSet::from([0..3, 5..8]), [0, 1, 2, 5, 6].into()).unwrap();
896    }
897
898    #[rstest]
899    #[should_panic]
900    fn test_subsequence_copy_to_invalid_len() {
901        let seq = Subsequence::new(RangeSet::from([0..3, 5..7]), [0, 1, 2, 5, 6].into()).unwrap();
902
903        let mut data: [u8; 3] = [0, 1, 2];
904        seq.copy_to(&mut data);
905    }
906}