1mod commit;
22pub mod encoding;
23pub mod hash;
24mod proof;
25mod tls;
26
27use std::{fmt, ops::Range};
28
29use rangeset::{Difference, IndexRanges, RangeSet, Union};
30use serde::{Deserialize, Serialize};
31
32use crate::connection::TranscriptLength;
33
34pub use commit::{
35 TranscriptCommitConfig, TranscriptCommitConfigBuilder, TranscriptCommitConfigBuilderError,
36 TranscriptCommitRequest, TranscriptCommitment, TranscriptCommitmentKind, TranscriptSecret,
37};
38pub use proof::{
39 TranscriptProof, TranscriptProofBuilder, TranscriptProofBuilderError, TranscriptProofError,
40};
41pub use tls::{ContentType, Record, TlsTranscript};
42
43#[derive(Clone, Serialize, Deserialize)]
46pub struct Transcript {
47 sent: Vec<u8>,
49 received: Vec<u8>,
51}
52
53opaque_debug::implement!(Transcript);
54
55impl Transcript {
56 pub fn new(sent: impl Into<Vec<u8>>, received: impl Into<Vec<u8>>) -> Self {
58 Self {
59 sent: sent.into(),
60 received: received.into(),
61 }
62 }
63
64 pub fn sent(&self) -> &[u8] {
66 &self.sent
67 }
68
69 pub fn received(&self) -> &[u8] {
71 &self.received
72 }
73
74 #[allow(clippy::len_without_is_empty)]
76 pub fn len(&self) -> (usize, usize) {
77 (self.sent.len(), self.received.len())
78 }
79
80 pub(crate) fn len_of_direction(&self, direction: Direction) -> usize {
82 match direction {
83 Direction::Sent => self.sent.len(),
84 Direction::Received => self.received.len(),
85 }
86 }
87
88 pub fn length(&self) -> TranscriptLength {
90 TranscriptLength {
91 sent: self.sent.len() as u32,
92 received: self.received.len() as u32,
93 }
94 }
95
96 pub fn get(&self, direction: Direction, idx: &RangeSet<usize>) -> Option<Subsequence> {
99 let data = match direction {
100 Direction::Sent => &self.sent,
101 Direction::Received => &self.received,
102 };
103
104 if idx.end().unwrap_or(0) > data.len() {
105 return None;
106 }
107
108 Some(
109 Subsequence::new(idx.clone(), data.index_ranges(idx))
110 .expect("data is same length as index"),
111 )
112 }
113
114 pub fn to_partial(
125 &self,
126 sent_idx: RangeSet<usize>,
127 recv_idx: RangeSet<usize>,
128 ) -> PartialTranscript {
129 let mut sent = vec![0; self.sent.len()];
130 let mut received = vec![0; self.received.len()];
131
132 for range in sent_idx.iter_ranges() {
133 sent[range.clone()].copy_from_slice(&self.sent[range]);
134 }
135
136 for range in recv_idx.iter_ranges() {
137 received[range.clone()].copy_from_slice(&self.received[range]);
138 }
139
140 PartialTranscript {
141 sent,
142 received,
143 sent_authed_idx: sent_idx,
144 received_authed_idx: recv_idx,
145 }
146 }
147}
148
149#[derive(Debug, Clone, Serialize, Deserialize)]
154#[serde(try_from = "CompressedPartialTranscript")]
155#[serde(into = "CompressedPartialTranscript")]
156#[cfg_attr(test, derive(PartialEq))]
157pub struct PartialTranscript {
158 sent: Vec<u8>,
160 received: Vec<u8>,
162 sent_authed_idx: RangeSet<usize>,
164 received_authed_idx: RangeSet<usize>,
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize)]
170#[serde(try_from = "validation::CompressedPartialTranscriptUnchecked")]
171pub struct CompressedPartialTranscript {
172 sent_authed: Vec<u8>,
174 received_authed: Vec<u8>,
176 sent_idx: RangeSet<usize>,
178 recv_idx: RangeSet<usize>,
180 sent_total: usize,
182 recv_total: usize,
184}
185
186impl From<PartialTranscript> for CompressedPartialTranscript {
187 fn from(uncompressed: PartialTranscript) -> Self {
188 Self {
189 sent_authed: uncompressed
190 .sent
191 .index_ranges(&uncompressed.sent_authed_idx),
192 received_authed: uncompressed
193 .received
194 .index_ranges(&uncompressed.received_authed_idx),
195 sent_idx: uncompressed.sent_authed_idx,
196 recv_idx: uncompressed.received_authed_idx,
197 sent_total: uncompressed.sent.len(),
198 recv_total: uncompressed.received.len(),
199 }
200 }
201}
202
203impl From<CompressedPartialTranscript> for PartialTranscript {
204 fn from(compressed: CompressedPartialTranscript) -> Self {
205 let mut sent = vec![0; compressed.sent_total];
206 let mut received = vec![0; compressed.recv_total];
207
208 let mut offset = 0;
209
210 for range in compressed.sent_idx.iter_ranges() {
211 sent[range.clone()]
212 .copy_from_slice(&compressed.sent_authed[offset..offset + range.len()]);
213 offset += range.len();
214 }
215
216 let mut offset = 0;
217
218 for range in compressed.recv_idx.iter_ranges() {
219 received[range.clone()]
220 .copy_from_slice(&compressed.received_authed[offset..offset + range.len()]);
221 offset += range.len();
222 }
223
224 Self {
225 sent,
226 received,
227 sent_authed_idx: compressed.sent_idx,
228 received_authed_idx: compressed.recv_idx,
229 }
230 }
231}
232
233impl PartialTranscript {
234 pub fn new(sent_len: usize, received_len: usize) -> Self {
241 Self {
242 sent: vec![0; sent_len],
243 received: vec![0; received_len],
244 sent_authed_idx: RangeSet::default(),
245 received_authed_idx: RangeSet::default(),
246 }
247 }
248
249 pub fn len_sent(&self) -> usize {
251 self.sent.len()
252 }
253
254 pub fn len_received(&self) -> usize {
256 self.received.len()
257 }
258
259 pub fn is_complete(&self) -> bool {
261 self.sent_authed_idx.len() == self.sent.len()
262 && self.received_authed_idx.len() == self.received.len()
263 }
264
265 pub fn contains(&self, direction: Direction, idx: &RangeSet<usize>) -> bool {
267 match direction {
268 Direction::Sent => idx.end().unwrap_or(0) <= self.sent.len(),
269 Direction::Received => idx.end().unwrap_or(0) <= self.received.len(),
270 }
271 }
272
273 pub fn sent_unsafe(&self) -> &[u8] {
281 &self.sent
282 }
283
284 pub fn received_unsafe(&self) -> &[u8] {
292 &self.received
293 }
294
295 pub fn sent_authed(&self) -> &RangeSet<usize> {
297 &self.sent_authed_idx
298 }
299
300 pub fn received_authed(&self) -> &RangeSet<usize> {
302 &self.received_authed_idx
303 }
304
305 pub fn sent_unauthed(&self) -> RangeSet<usize> {
307 (0..self.sent.len()).difference(&self.sent_authed_idx)
308 }
309
310 pub fn received_unauthed(&self) -> RangeSet<usize> {
312 (0..self.received.len()).difference(&self.received_authed_idx)
313 }
314
315 pub fn iter(&self, direction: Direction) -> impl Iterator<Item = u8> + '_ {
317 let (data, authed) = match direction {
318 Direction::Sent => (&self.sent, &self.sent_authed_idx),
319 Direction::Received => (&self.received, &self.received_authed_idx),
320 };
321
322 authed.iter().map(|i| data[i])
323 }
324
325 pub fn union_transcript(&mut self, other: &PartialTranscript) {
331 assert_eq!(
332 self.sent.len(),
333 other.sent.len(),
334 "sent data are not the same length"
335 );
336 assert_eq!(
337 self.received.len(),
338 other.received.len(),
339 "received data are not the same length"
340 );
341
342 for range in other
343 .sent_authed_idx
344 .difference(&self.sent_authed_idx)
345 .iter_ranges()
346 {
347 self.sent[range.clone()].copy_from_slice(&other.sent[range]);
348 }
349
350 for range in other
351 .received_authed_idx
352 .difference(&self.received_authed_idx)
353 .iter_ranges()
354 {
355 self.received[range.clone()].copy_from_slice(&other.received[range]);
356 }
357
358 self.sent_authed_idx = self.sent_authed_idx.union(&other.sent_authed_idx);
359 self.received_authed_idx = self.received_authed_idx.union(&other.received_authed_idx);
360 }
361
362 pub fn union_subsequence(&mut self, direction: Direction, seq: &Subsequence) {
368 match direction {
369 Direction::Sent => {
370 seq.copy_to(&mut self.sent);
371 self.sent_authed_idx = self.sent_authed_idx.union(&seq.idx);
372 }
373 Direction::Received => {
374 seq.copy_to(&mut self.received);
375 self.received_authed_idx = self.received_authed_idx.union(&seq.idx);
376 }
377 }
378 }
379
380 pub fn set_unauthed(&mut self, value: u8) {
386 for range in self.sent_unauthed().iter_ranges() {
387 self.sent[range].fill(value);
388 }
389 for range in self.received_unauthed().iter_ranges() {
390 self.received[range].fill(value);
391 }
392 }
393
394 pub fn set_unauthed_range(&mut self, value: u8, direction: Direction, range: Range<usize>) {
402 match direction {
403 Direction::Sent => {
404 for range in range.difference(&self.sent_authed_idx).iter_ranges() {
405 self.sent[range].fill(value);
406 }
407 }
408 Direction::Received => {
409 for range in range.difference(&self.received_authed_idx).iter_ranges() {
410 self.received[range].fill(value);
411 }
412 }
413 }
414 }
415}
416
417#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
422pub enum Direction {
423 Sent = 0x00,
425 Received = 0x01,
427}
428
429impl fmt::Display for Direction {
430 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
431 match self {
432 Direction::Sent => write!(f, "sent"),
433 Direction::Received => write!(f, "received"),
434 }
435 }
436}
437
438#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
440#[serde(try_from = "validation::SubsequenceUnchecked")]
441pub struct Subsequence {
442 idx: RangeSet<usize>,
444 data: Vec<u8>,
446}
447
448impl Subsequence {
449 pub fn new(idx: RangeSet<usize>, data: Vec<u8>) -> Result<Self, InvalidSubsequence> {
451 if idx.len() != data.len() {
452 return Err(InvalidSubsequence(
453 "index length does not match data length",
454 ));
455 }
456
457 Ok(Self { idx, data })
458 }
459
460 pub fn index(&self) -> &RangeSet<usize> {
462 &self.idx
463 }
464
465 pub fn data(&self) -> &[u8] {
467 &self.data
468 }
469
470 #[allow(clippy::len_without_is_empty)]
472 pub fn len(&self) -> usize {
473 self.data.len()
474 }
475
476 pub fn into_parts(self) -> (RangeSet<usize>, Vec<u8>) {
478 (self.idx, self.data)
479 }
480
481 pub(crate) fn copy_to(&self, dest: &mut [u8]) {
487 let mut offset = 0;
488 for range in self.idx.iter_ranges() {
489 dest[range.clone()].copy_from_slice(&self.data[offset..offset + range.len()]);
490 offset += range.len();
491 }
492 }
493}
494
495#[derive(Debug, thiserror::Error)]
497#[error("invalid subsequence: {0}")]
498pub struct InvalidSubsequence(&'static str);
499
500mod validation {
501 use super::*;
502
503 #[derive(Debug, Deserialize)]
504 pub(super) struct SubsequenceUnchecked {
505 idx: RangeSet<usize>,
506 data: Vec<u8>,
507 }
508
509 impl TryFrom<SubsequenceUnchecked> for Subsequence {
510 type Error = InvalidSubsequence;
511
512 fn try_from(unchecked: SubsequenceUnchecked) -> Result<Self, Self::Error> {
513 Self::new(unchecked.idx, unchecked.data)
514 }
515 }
516
517 #[derive(Debug, thiserror::Error)]
519 #[error("invalid compressed partial transcript: {0}")]
520 pub struct InvalidCompressedPartialTranscript(&'static str);
521
522 #[derive(Debug, Deserialize)]
523 #[cfg_attr(test, derive(Serialize))]
524 pub(super) struct CompressedPartialTranscriptUnchecked {
525 sent_authed: Vec<u8>,
526 received_authed: Vec<u8>,
527 sent_idx: RangeSet<usize>,
528 recv_idx: RangeSet<usize>,
529 sent_total: usize,
530 recv_total: usize,
531 }
532
533 impl TryFrom<CompressedPartialTranscriptUnchecked> for CompressedPartialTranscript {
534 type Error = InvalidCompressedPartialTranscript;
535
536 fn try_from(unchecked: CompressedPartialTranscriptUnchecked) -> Result<Self, Self::Error> {
537 if unchecked.sent_authed.len() != unchecked.sent_idx.len()
538 || unchecked.received_authed.len() != unchecked.recv_idx.len()
539 {
540 return Err(InvalidCompressedPartialTranscript(
541 "lengths of index and data don't match",
542 ));
543 }
544
545 if unchecked.sent_idx.end().unwrap_or(0) > unchecked.sent_total
546 || unchecked.recv_idx.end().unwrap_or(0) > unchecked.recv_total
547 {
548 return Err(InvalidCompressedPartialTranscript(
549 "ranges are not in bounds of the data",
550 ));
551 }
552
553 Ok(Self {
554 received_authed: unchecked.received_authed,
555 recv_idx: unchecked.recv_idx,
556 recv_total: unchecked.recv_total,
557 sent_authed: unchecked.sent_authed,
558 sent_idx: unchecked.sent_idx,
559 sent_total: unchecked.sent_total,
560 })
561 }
562 }
563
564 #[cfg(test)]
565 mod tests {
566 use rstest::{fixture, rstest};
567
568 use super::*;
569
570 #[fixture]
571 fn partial_transcript() -> CompressedPartialTranscriptUnchecked {
572 CompressedPartialTranscriptUnchecked {
573 received_authed: vec![1, 2, 3, 11, 12, 13],
574 sent_authed: vec![4, 5, 6, 14, 15, 16],
575 recv_idx: RangeSet::from([1..4, 11..14]),
576 sent_idx: RangeSet::from([4..7, 14..17]),
577 sent_total: 20,
578 recv_total: 20,
579 }
580 }
581
582 #[rstest]
583 fn test_partial_transcript_valid(partial_transcript: CompressedPartialTranscriptUnchecked) {
584 let bytes = bincode::serialize(&partial_transcript).unwrap();
585 let transcript: Result<CompressedPartialTranscript, Box<bincode::ErrorKind>> =
586 bincode::deserialize(&bytes);
587 assert!(transcript.is_ok());
588 }
589
590 #[rstest]
591 fn test_partial_transcript_invalid_lengths(
594 mut partial_transcript: CompressedPartialTranscriptUnchecked,
595 ) {
596 let mut old = partial_transcript.sent_authed;
598 old.extend([1]);
599 partial_transcript.sent_authed = old;
600
601 let bytes = bincode::serialize(&partial_transcript).unwrap();
602 let transcript: Result<CompressedPartialTranscript, Box<bincode::ErrorKind>> =
603 bincode::deserialize(&bytes);
604 assert!(transcript.is_err());
605 }
606
607 #[rstest]
608 fn test_partial_transcript_invalid_ranges(
610 mut partial_transcript: CompressedPartialTranscriptUnchecked,
611 ) {
612 let end = partial_transcript
614 .sent_idx
615 .iter_ranges()
616 .next_back()
617 .unwrap()
618 .end;
619
620 partial_transcript.sent_total = end - 1;
621
622 let bytes = bincode::serialize(&partial_transcript).unwrap();
623 let transcript: Result<CompressedPartialTranscript, Box<bincode::ErrorKind>> =
624 bincode::deserialize(&bytes);
625 assert!(transcript.is_err());
626 }
627 }
628}
629
630#[cfg(test)]
631mod tests {
632 use rstest::{fixture, rstest};
633
634 use super::*;
635
636 #[fixture]
637 fn transcript() -> Transcript {
638 Transcript::new(
639 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
640 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
641 )
642 }
643
644 #[fixture]
645 fn partial_transcript() -> PartialTranscript {
646 transcript().to_partial(RangeSet::from([1..4, 6..9]), RangeSet::from([2..5, 7..10]))
647 }
648
649 #[rstest]
650 fn test_transcript_get_subsequence(transcript: Transcript) {
651 let subseq = transcript
652 .get(Direction::Received, &RangeSet::from([0..4, 7..10]))
653 .unwrap();
654 assert_eq!(subseq.data, vec![0, 1, 2, 3, 7, 8, 9]);
655
656 let subseq = transcript
657 .get(Direction::Sent, &RangeSet::from([0..4, 9..12]))
658 .unwrap();
659 assert_eq!(subseq.data, vec![0, 1, 2, 3, 9, 10, 11]);
660
661 let subseq = transcript.get(Direction::Received, &RangeSet::from([0..4, 7..10, 11..13]));
662 assert_eq!(subseq, None);
663
664 let subseq = transcript.get(Direction::Sent, &RangeSet::from([0..4, 7..10, 11..13]));
665 assert_eq!(subseq, None);
666 }
667
668 #[rstest]
669 fn test_partial_transcript_serialization_ok(partial_transcript: PartialTranscript) {
670 let bytes = bincode::serialize(&partial_transcript).unwrap();
671 let deserialized_transcript: PartialTranscript = bincode::deserialize(&bytes).unwrap();
672 assert_eq!(partial_transcript, deserialized_transcript);
673 }
674
675 #[rstest]
676 fn test_transcript_to_partial_success(transcript: Transcript) {
677 let partial = transcript.to_partial(RangeSet::from(0..2), RangeSet::from(3..7));
678 assert_eq!(partial.sent_unsafe(), [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
679 assert_eq!(
680 partial.received_unsafe(),
681 [0, 0, 0, 3, 4, 5, 6, 0, 0, 0, 0, 0]
682 );
683 }
684
685 #[rstest]
686 #[should_panic]
687 fn test_transcript_to_partial_failure(transcript: Transcript) {
688 let _ = transcript.to_partial(RangeSet::from(0..14), RangeSet::from(3..7));
689 }
690
691 #[rstest]
692 fn test_partial_transcript_contains(transcript: Transcript) {
693 let partial = transcript.to_partial(RangeSet::from(0..2), RangeSet::from(3..7));
694 assert!(partial.contains(Direction::Sent, &RangeSet::from([0..5, 7..10])));
695 assert!(!partial.contains(Direction::Received, &RangeSet::from([4..6, 7..13])))
696 }
697
698 #[rstest]
699 fn test_partial_transcript_unauthed(transcript: Transcript) {
700 let partial = transcript.to_partial(RangeSet::from(0..2), RangeSet::from(3..7));
701 assert_eq!(partial.sent_unauthed(), RangeSet::from(2..12));
702 assert_eq!(partial.received_unauthed(), RangeSet::from([0..3, 7..12]));
703 }
704
705 #[rstest]
706 fn test_partial_transcript_union_success(transcript: Transcript) {
707 let mut simple_partial = transcript.to_partial(RangeSet::from(0..2), RangeSet::from(3..7));
709
710 let other_simple_partial =
711 transcript.to_partial(RangeSet::from(3..5), RangeSet::from(1..2));
712
713 simple_partial.union_transcript(&other_simple_partial);
714
715 assert_eq!(
716 simple_partial.sent_unsafe(),
717 [0, 1, 0, 3, 4, 0, 0, 0, 0, 0, 0, 0]
718 );
719 assert_eq!(
720 simple_partial.received_unsafe(),
721 [0, 1, 0, 3, 4, 5, 6, 0, 0, 0, 0, 0]
722 );
723 assert_eq!(simple_partial.sent_authed(), &RangeSet::from([0..2, 3..5]));
724 assert_eq!(
725 simple_partial.received_authed(),
726 &RangeSet::from([1..2, 3..7])
727 );
728
729 let another_simple_partial =
732 transcript.to_partial(RangeSet::from(1..4), RangeSet::from(6..9));
733
734 simple_partial.union_transcript(&another_simple_partial);
735
736 assert_eq!(
737 simple_partial.sent_unsafe(),
738 [0, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0]
739 );
740 assert_eq!(
741 simple_partial.received_unsafe(),
742 [0, 1, 0, 3, 4, 5, 6, 7, 8, 0, 0, 0]
743 );
744 assert_eq!(simple_partial.sent_authed(), &RangeSet::from(0..5));
745 assert_eq!(
746 simple_partial.received_authed(),
747 &RangeSet::from([1..2, 3..9])
748 );
749
750 let mut overlap_partial = transcript.to_partial(RangeSet::from(4..6), RangeSet::from(3..7));
752
753 let other_overlap_partial =
754 transcript.to_partial(RangeSet::from(3..5), RangeSet::from(5..9));
755
756 overlap_partial.union_transcript(&other_overlap_partial);
757
758 assert_eq!(
759 overlap_partial.sent_unsafe(),
760 [0, 0, 0, 3, 4, 5, 0, 0, 0, 0, 0, 0]
761 );
762 assert_eq!(
763 overlap_partial.received_unsafe(),
764 [0, 0, 0, 3, 4, 5, 6, 7, 8, 0, 0, 0]
765 );
766 assert_eq!(overlap_partial.sent_authed(), &RangeSet::from([3..5, 4..6]));
767 assert_eq!(
768 overlap_partial.received_authed(),
769 &RangeSet::from([3..7, 5..9])
770 );
771
772 let mut equal_partial = transcript.to_partial(RangeSet::from(4..6), RangeSet::from(3..7));
774
775 let other_equal_partial = transcript.to_partial(RangeSet::from(4..6), RangeSet::from(3..7));
776
777 equal_partial.union_transcript(&other_equal_partial);
778
779 assert_eq!(
780 equal_partial.sent_unsafe(),
781 [0, 0, 0, 0, 4, 5, 0, 0, 0, 0, 0, 0]
782 );
783 assert_eq!(
784 equal_partial.received_unsafe(),
785 [0, 0, 0, 3, 4, 5, 6, 0, 0, 0, 0, 0]
786 );
787 assert_eq!(equal_partial.sent_authed(), &RangeSet::from(4..6));
788 assert_eq!(equal_partial.received_authed(), &RangeSet::from(3..7));
789
790 let mut subset_partial =
792 transcript.to_partial(RangeSet::from(4..10), RangeSet::from(3..11));
793
794 let other_subset_partial =
795 transcript.to_partial(RangeSet::from(6..9), RangeSet::from(5..6));
796
797 subset_partial.union_transcript(&other_subset_partial);
798
799 assert_eq!(
800 subset_partial.sent_unsafe(),
801 [0, 0, 0, 0, 4, 5, 6, 7, 8, 9, 0, 0]
802 );
803 assert_eq!(
804 subset_partial.received_unsafe(),
805 [0, 0, 0, 3, 4, 5, 6, 7, 8, 9, 10, 0]
806 );
807 assert_eq!(subset_partial.sent_authed(), &RangeSet::from(4..10));
808 assert_eq!(subset_partial.received_authed(), &RangeSet::from(3..11));
809 }
810
811 #[rstest]
812 #[should_panic]
813 fn test_partial_transcript_union_failure(transcript: Transcript) {
814 let mut partial = transcript.to_partial(RangeSet::from(4..10), RangeSet::from(3..11));
815
816 let other_transcript = Transcript::new(
817 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
818 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
819 );
820
821 let other_partial = other_transcript.to_partial(RangeSet::from(6..9), RangeSet::from(5..6));
822
823 partial.union_transcript(&other_partial);
824 }
825
826 #[rstest]
827 fn test_partial_transcript_union_subseq_success(transcript: Transcript) {
828 let mut partial = transcript.to_partial(RangeSet::from(4..10), RangeSet::from(3..11));
829 let sent_seq =
830 Subsequence::new(RangeSet::from([0..3, 5..7]), [0, 1, 2, 5, 6].into()).unwrap();
831 let recv_seq =
832 Subsequence::new(RangeSet::from([0..4, 5..7]), [0, 1, 2, 3, 5, 6].into()).unwrap();
833
834 partial.union_subsequence(Direction::Sent, &sent_seq);
835 partial.union_subsequence(Direction::Received, &recv_seq);
836
837 assert_eq!(partial.sent_unsafe(), [0, 1, 2, 0, 4, 5, 6, 7, 8, 9, 0, 0]);
838 assert_eq!(
839 partial.received_unsafe(),
840 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0]
841 );
842 assert_eq!(partial.sent_authed(), &RangeSet::from([0..3, 4..10]));
843 assert_eq!(partial.received_authed(), &RangeSet::from(0..11));
844
845 let other_sent_seq = Subsequence::new(RangeSet::from(0..3), [3, 2, 1].into()).unwrap();
847
848 partial.union_subsequence(Direction::Sent, &other_sent_seq);
849 assert_eq!(partial.sent_unsafe(), [3, 2, 1, 0, 4, 5, 6, 7, 8, 9, 0, 0]);
850 assert_eq!(partial.sent_authed(), &RangeSet::from([0..3, 4..10]));
851 }
852
853 #[rstest]
854 #[should_panic]
855 fn test_partial_transcript_union_subseq_failure(transcript: Transcript) {
856 let mut partial = transcript.to_partial(RangeSet::from(4..10), RangeSet::from(3..11));
857
858 let sent_seq =
859 Subsequence::new(RangeSet::from([0..3, 13..15]), [0, 1, 2, 5, 6].into()).unwrap();
860
861 partial.union_subsequence(Direction::Sent, &sent_seq);
862 }
863
864 #[rstest]
865 fn test_partial_transcript_set_unauthed_range(transcript: Transcript) {
866 let mut partial = transcript.to_partial(RangeSet::from(4..10), RangeSet::from(3..7));
867
868 partial.set_unauthed_range(7, Direction::Sent, 2..5);
869 partial.set_unauthed_range(5, Direction::Sent, 0..2);
870 partial.set_unauthed_range(3, Direction::Received, 4..6);
871 partial.set_unauthed_range(1, Direction::Received, 3..7);
872
873 assert_eq!(partial.sent_unsafe(), [5, 5, 7, 7, 4, 5, 6, 7, 8, 9, 0, 0]);
874 assert_eq!(
875 partial.received_unsafe(),
876 [0, 0, 0, 3, 4, 5, 6, 0, 0, 0, 0, 0]
877 );
878 }
879
880 #[rstest]
881 #[should_panic]
882 fn test_subsequence_new_invalid_len() {
883 let _ = Subsequence::new(RangeSet::from([0..3, 5..8]), [0, 1, 2, 5, 6].into()).unwrap();
884 }
885
886 #[rstest]
887 #[should_panic]
888 fn test_subsequence_copy_to_invalid_len() {
889 let seq = Subsequence::new(RangeSet::from([0..3, 5..7]), [0, 1, 2, 5, 6].into()).unwrap();
890
891 let mut data: [u8; 3] = [0, 1, 2];
892 seq.copy_to(&mut data);
893 }
894}