# transcript.rs - source
Source of the Rust file `crates/core/src/transcript.rs`.

Skip to main content

#
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}