mod commit;
#[doc(hidden)]
pub mod encoding;
pub(crate) mod hash;
mod proof;
use std::{fmt, ops::Range};
use serde::{Deserialize, Serialize};
use utils::range::{Difference, IndexRanges, RangeSet, ToRangeSet, Union};
use crate::connection::TranscriptLength;
pub use commit::{
TranscriptCommitConfig, TranscriptCommitConfigBuilder, TranscriptCommitConfigBuilderError,
TranscriptCommitmentKind,
};
pub use proof::{
TranscriptProof, TranscriptProofBuilder, TranscriptProofBuilderError, TranscriptProofError,
};
pub static TX_TRANSCRIPT_ID: &str = "tx";
pub static RX_TRANSCRIPT_ID: &str = "rx";
#[derive(Clone, Serialize, Deserialize)]
pub struct Transcript {
sent: Vec<u8>,
received: Vec<u8>,
}
opaque_debug::implement!(Transcript);
impl Transcript {
pub fn new(sent: impl Into<Vec<u8>>, received: impl Into<Vec<u8>>) -> Self {
Self {
sent: sent.into(),
received: received.into(),
}
}
pub fn sent(&self) -> &[u8] {
&self.sent
}
pub fn received(&self) -> &[u8] {
&self.received
}
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> (usize, usize) {
(self.sent.len(), self.received.len())
}
pub(crate) fn len_of_direction(&self, direction: Direction) -> usize {
match direction {
Direction::Sent => self.sent.len(),
Direction::Received => self.received.len(),
}
}
pub fn length(&self) -> TranscriptLength {
TranscriptLength {
sent: self.sent.len() as u32,
received: self.received.len() as u32,
}
}
pub fn get(&self, direction: Direction, idx: &Idx) -> Option<Subsequence> {
let data = match direction {
Direction::Sent => &self.sent,
Direction::Received => &self.received,
};
if idx.end() > data.len() {
return None;
}
Some(
Subsequence::new(idx.clone(), data.index_ranges(&idx.0))
.expect("data is same length as index"),
)
}
pub fn to_partial(&self, sent_idx: Idx, recv_idx: Idx) -> PartialTranscript {
let mut sent = vec![0; self.sent.len()];
let mut received = vec![0; self.received.len()];
for range in sent_idx.iter_ranges() {
sent[range.clone()].copy_from_slice(&self.sent[range]);
}
for range in recv_idx.iter_ranges() {
received[range.clone()].copy_from_slice(&self.received[range]);
}
PartialTranscript {
sent,
received,
sent_authed: sent_idx,
received_authed: recv_idx,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(try_from = "validation::PartialTranscriptUnchecked")]
pub struct PartialTranscript {
sent: Vec<u8>,
received: Vec<u8>,
sent_authed: Idx,
received_authed: Idx,
}
impl PartialTranscript {
pub fn new(sent_len: usize, received_len: usize) -> Self {
Self {
sent: vec![0; sent_len],
received: vec![0; received_len],
sent_authed: Idx::default(),
received_authed: Idx::default(),
}
}
pub fn len_sent(&self) -> usize {
self.sent.len()
}
pub fn len_received(&self) -> usize {
self.received.len()
}
pub fn is_complete(&self) -> bool {
self.sent_authed.len() == self.sent.len()
&& self.received_authed.len() == self.received.len()
}
pub fn contains(&self, direction: Direction, idx: &Idx) -> bool {
match direction {
Direction::Sent => idx.end() <= self.sent.len(),
Direction::Received => idx.end() <= self.received.len(),
}
}
pub fn sent_unsafe(&self) -> &[u8] {
&self.sent
}
pub fn received_unsafe(&self) -> &[u8] {
&self.received
}
pub fn sent_authed(&self) -> &Idx {
&self.sent_authed
}
pub fn received_authed(&self) -> &Idx {
&self.received_authed
}
pub fn sent_unauthed(&self) -> Idx {
Idx(RangeSet::from(0..self.sent.len()).difference(&self.sent_authed.0))
}
pub fn received_unauthed(&self) -> Idx {
Idx(RangeSet::from(0..self.received.len()).difference(&self.received_authed.0))
}
pub fn iter(&self, direction: Direction) -> impl Iterator<Item = u8> + '_ {
let (data, authed) = match direction {
Direction::Sent => (&self.sent, &self.sent_authed),
Direction::Received => (&self.received, &self.received_authed),
};
authed.0.iter().map(|i| data[i])
}
pub fn union_transcript(&mut self, other: &PartialTranscript) {
assert_eq!(
self.sent.len(),
other.sent.len(),
"sent data are not the same length"
);
assert_eq!(
self.received.len(),
other.received.len(),
"received data are not the same length"
);
for range in other
.sent_authed
.0
.difference(&self.sent_authed.0)
.iter_ranges()
{
self.sent[range.clone()].copy_from_slice(&other.sent[range]);
}
for range in other
.received_authed
.0
.difference(&self.received_authed.0)
.iter_ranges()
{
self.received[range.clone()].copy_from_slice(&other.received[range]);
}
self.sent_authed = self.sent_authed.union(&other.sent_authed);
self.received_authed = self.received_authed.union(&other.received_authed);
}
pub fn union_subsequence(&mut self, direction: Direction, seq: &Subsequence) {
match direction {
Direction::Sent => {
seq.copy_to(&mut self.sent);
self.sent_authed = self.sent_authed.union(&seq.idx);
}
Direction::Received => {
seq.copy_to(&mut self.received);
self.received_authed = self.received_authed.union(&seq.idx);
}
}
}
pub fn set_unauthed(&mut self, value: u8) {
for range in self.sent_unauthed().iter_ranges() {
self.sent[range].fill(value);
}
for range in self.received_unauthed().iter_ranges() {
self.received[range].fill(value);
}
}
pub fn set_unauthed_range(&mut self, value: u8, direction: Direction, range: Range<usize>) {
match direction {
Direction::Sent => {
for range in range.difference(&self.sent_authed.0).iter_ranges() {
self.sent[range].fill(value);
}
}
Direction::Received => {
for range in range.difference(&self.received_authed.0).iter_ranges() {
self.received[range].fill(value);
}
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Direction {
Sent = 0x00,
Received = 0x01,
}
impl fmt::Display for Direction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Direction::Sent => write!(f, "sent"),
Direction::Received => write!(f, "received"),
}
}
}
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Idx(RangeSet<usize>);
impl Idx {
pub fn builder() -> IdxBuilder {
IdxBuilder::default()
}
pub fn empty() -> Self {
Self(RangeSet::default())
}
pub fn new(ranges: impl Into<RangeSet<usize>>) -> Self {
Self(ranges.into())
}
pub fn start(&self) -> usize {
self.0.min().unwrap_or_default()
}
pub fn end(&self) -> usize {
self.0.end().unwrap_or_default()
}
pub fn iter(&self) -> impl Iterator<Item = usize> + '_ {
self.0.iter()
}
pub fn iter_ranges(&self) -> impl Iterator<Item = Range<usize>> + '_ {
self.0.iter_ranges()
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn count(&self) -> usize {
self.0.len_ranges()
}
pub fn union(&self, other: &Idx) -> Idx {
Idx(self.0.union(&other.0))
}
}
#[derive(Debug, Default)]
pub struct IdxBuilder(RangeSet<usize>);
impl IdxBuilder {
pub fn union(self, ranges: &dyn ToRangeSet<usize>) -> Self {
IdxBuilder(self.0.union(&ranges.to_range_set()))
}
pub fn build(self) -> Idx {
Idx(self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(try_from = "validation::SubsequenceUnchecked")]
pub struct Subsequence {
idx: Idx,
data: Vec<u8>,
}
impl Subsequence {
pub fn new(idx: Idx, data: Vec<u8>) -> Result<Self, InvalidSubsequence> {
if idx.len() != data.len() {
return Err(InvalidSubsequence(
"index length does not match data length",
));
}
Ok(Self { idx, data })
}
pub fn index(&self) -> &Idx {
&self.idx
}
pub fn data(&self) -> &[u8] {
&self.data
}
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.data.len()
}
pub fn into_parts(self) -> (Idx, Vec<u8>) {
(self.idx, self.data)
}
pub(crate) fn copy_to(&self, dest: &mut [u8]) {
let mut offset = 0;
for range in self.idx.iter_ranges() {
dest[range.clone()].copy_from_slice(&self.data[offset..offset + range.len()]);
offset += range.len();
}
}
}
#[derive(Debug, thiserror::Error)]
#[error("invalid subsequence: {0}")]
pub struct InvalidSubsequence(&'static str);
#[doc(hidden)]
pub fn get_value_ids(direction: Direction, idx: &Idx) -> impl Iterator<Item = String> + '_ {
let id = match direction {
Direction::Sent => TX_TRANSCRIPT_ID,
Direction::Received => RX_TRANSCRIPT_ID,
};
idx.iter().map(move |idx| format!("{}/{}", id, idx))
}
mod validation {
use super::*;
#[derive(Debug, Deserialize)]
pub(super) struct SubsequenceUnchecked {
idx: Idx,
data: Vec<u8>,
}
impl TryFrom<SubsequenceUnchecked> for Subsequence {
type Error = InvalidSubsequence;
fn try_from(unchecked: SubsequenceUnchecked) -> Result<Self, Self::Error> {
Self::new(unchecked.idx, unchecked.data)
}
}
#[derive(Debug, thiserror::Error)]
#[error("invalid partial transcript: {0}")]
pub struct InvalidPartialTranscript(&'static str);
#[derive(Debug, Deserialize)]
pub(super) struct PartialTranscriptUnchecked {
sent: Vec<u8>,
received: Vec<u8>,
sent_authed: Idx,
received_authed: Idx,
}
impl TryFrom<PartialTranscriptUnchecked> for PartialTranscript {
type Error = InvalidPartialTranscript;
fn try_from(unchecked: PartialTranscriptUnchecked) -> Result<Self, Self::Error> {
if unchecked.sent_authed.end() > unchecked.sent.len()
|| unchecked.received_authed.end() > unchecked.received.len()
{
return Err(InvalidPartialTranscript(
"authenticated ranges are not in bounds of the data",
));
}
let mut sent = vec![0; unchecked.sent.len()];
let mut received = vec![0; unchecked.received.len()];
for range in unchecked.sent_authed.iter_ranges() {
sent[range.clone()].copy_from_slice(&unchecked.sent[range]);
}
for range in unchecked.received_authed.iter_ranges() {
received[range.clone()].copy_from_slice(&unchecked.received[range]);
}
Ok(Self {
sent,
received,
sent_authed: unchecked.sent_authed,
received_authed: unchecked.received_authed,
})
}
}
}
#[cfg(test)]
mod tests {
use rstest::{fixture, rstest};
use super::*;
#[fixture]
fn transcript() -> Transcript {
Transcript::new(
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
)
}
#[rstest]
fn test_transcript_get_subsequence(transcript: Transcript) {
let subseq = transcript
.get(Direction::Received, &Idx(RangeSet::from([0..4, 7..10])))
.unwrap();
assert_eq!(subseq.data, vec![0, 1, 2, 3, 7, 8, 9]);
let subseq = transcript
.get(Direction::Sent, &Idx(RangeSet::from([0..4, 9..12])))
.unwrap();
assert_eq!(subseq.data, vec![0, 1, 2, 3, 9, 10, 11]);
let subseq = transcript.get(
Direction::Received,
&Idx(RangeSet::from([0..4, 7..10, 11..13])),
);
assert_eq!(subseq, None);
let subseq = transcript.get(Direction::Sent, &Idx(RangeSet::from([0..4, 7..10, 11..13])));
assert_eq!(subseq, None);
}
#[rstest]
fn test_transcript_to_partial_success(transcript: Transcript) {
let partial = transcript.to_partial(Idx::new(0..2), Idx::new(3..7));
assert_eq!(partial.sent_unsafe(), [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
assert_eq!(
partial.received_unsafe(),
[0, 0, 0, 3, 4, 5, 6, 0, 0, 0, 0, 0]
);
}
#[rstest]
#[should_panic]
fn test_transcript_to_partial_failure(transcript: Transcript) {
let _ = transcript.to_partial(Idx::new(0..14), Idx::new(3..7));
}
#[rstest]
fn test_partial_transcript_contains(transcript: Transcript) {
let partial = transcript.to_partial(Idx::new(0..2), Idx::new(3..7));
assert!(partial.contains(Direction::Sent, &Idx::new([0..5, 7..10])));
assert!(!partial.contains(Direction::Received, &Idx::new([4..6, 7..13])))
}
#[rstest]
fn test_partial_transcript_unauthed(transcript: Transcript) {
let partial = transcript.to_partial(Idx::new(0..2), Idx::new(3..7));
assert_eq!(partial.sent_unauthed(), Idx::new(2..12));
assert_eq!(partial.received_unauthed(), Idx::new([0..3, 7..12]));
}
#[rstest]
fn test_partial_transcript_union_success(transcript: Transcript) {
let mut simple_partial = transcript.to_partial(Idx::new(0..2), Idx::new(3..7));
let other_simple_partial = transcript.to_partial(Idx::new(3..5), Idx::new(1..2));
simple_partial.union_transcript(&other_simple_partial);
assert_eq!(
simple_partial.sent_unsafe(),
[0, 1, 0, 3, 4, 0, 0, 0, 0, 0, 0, 0]
);
assert_eq!(
simple_partial.received_unsafe(),
[0, 1, 0, 3, 4, 5, 6, 0, 0, 0, 0, 0]
);
assert_eq!(simple_partial.sent_authed(), &Idx::new([0..2, 3..5]));
assert_eq!(simple_partial.received_authed(), &Idx::new([1..2, 3..7]));
let another_simple_partial = transcript.to_partial(Idx::new(1..4), Idx::new(6..9));
simple_partial.union_transcript(&another_simple_partial);
assert_eq!(
simple_partial.sent_unsafe(),
[0, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0]
);
assert_eq!(
simple_partial.received_unsafe(),
[0, 1, 0, 3, 4, 5, 6, 7, 8, 0, 0, 0]
);
assert_eq!(simple_partial.sent_authed(), &Idx::new(0..5));
assert_eq!(simple_partial.received_authed(), &Idx::new([1..2, 3..9]));
let mut overlap_partial = transcript.to_partial(Idx::new(4..6), Idx::new(3..7));
let other_overlap_partial = transcript.to_partial(Idx::new(3..5), Idx::new(5..9));
overlap_partial.union_transcript(&other_overlap_partial);
assert_eq!(
overlap_partial.sent_unsafe(),
[0, 0, 0, 3, 4, 5, 0, 0, 0, 0, 0, 0]
);
assert_eq!(
overlap_partial.received_unsafe(),
[0, 0, 0, 3, 4, 5, 6, 7, 8, 0, 0, 0]
);
assert_eq!(overlap_partial.sent_authed(), &Idx::new([3..5, 4..6]));
assert_eq!(overlap_partial.received_authed(), &Idx::new([3..7, 5..9]));
let mut equal_partial = transcript.to_partial(Idx::new(4..6), Idx::new(3..7));
let other_equal_partial = transcript.to_partial(Idx::new(4..6), Idx::new(3..7));
equal_partial.union_transcript(&other_equal_partial);
assert_eq!(
equal_partial.sent_unsafe(),
[0, 0, 0, 0, 4, 5, 0, 0, 0, 0, 0, 0]
);
assert_eq!(
equal_partial.received_unsafe(),
[0, 0, 0, 3, 4, 5, 6, 0, 0, 0, 0, 0]
);
assert_eq!(equal_partial.sent_authed(), &Idx::new(4..6));
assert_eq!(equal_partial.received_authed(), &Idx::new(3..7));
let mut subset_partial = transcript.to_partial(Idx::new(4..10), Idx::new(3..11));
let other_subset_partial = transcript.to_partial(Idx::new(6..9), Idx::new(5..6));
subset_partial.union_transcript(&other_subset_partial);
assert_eq!(
subset_partial.sent_unsafe(),
[0, 0, 0, 0, 4, 5, 6, 7, 8, 9, 0, 0]
);
assert_eq!(
subset_partial.received_unsafe(),
[0, 0, 0, 3, 4, 5, 6, 7, 8, 9, 10, 0]
);
assert_eq!(subset_partial.sent_authed(), &Idx::new(4..10));
assert_eq!(subset_partial.received_authed(), &Idx::new(3..11));
}
#[rstest]
#[should_panic]
fn test_partial_transcript_union_failure(transcript: Transcript) {
let mut partial = transcript.to_partial(Idx::new(4..10), Idx::new(3..11));
let other_transcript = Transcript::new(
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
);
let other_partial = other_transcript.to_partial(Idx::new(6..9), Idx::new(5..6));
partial.union_transcript(&other_partial);
}
#[rstest]
fn test_partial_transcript_union_subseq_success(transcript: Transcript) {
let mut partial = transcript.to_partial(Idx::new(4..10), Idx::new(3..11));
let sent_seq = Subsequence::new(Idx::new([0..3, 5..7]), [0, 1, 2, 5, 6].into()).unwrap();
let recv_seq = Subsequence::new(Idx::new([0..4, 5..7]), [0, 1, 2, 3, 5, 6].into()).unwrap();
partial.union_subsequence(Direction::Sent, &sent_seq);
partial.union_subsequence(Direction::Received, &recv_seq);
assert_eq!(partial.sent_unsafe(), [0, 1, 2, 0, 4, 5, 6, 7, 8, 9, 0, 0]);
assert_eq!(
partial.received_unsafe(),
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0]
);
assert_eq!(partial.sent_authed(), &Idx::new([0..3, 4..10]));
assert_eq!(partial.received_authed(), &Idx::new(0..11));
let other_sent_seq = Subsequence::new(Idx::new(0..3), [3, 2, 1].into()).unwrap();
partial.union_subsequence(Direction::Sent, &other_sent_seq);
assert_eq!(partial.sent_unsafe(), [3, 2, 1, 0, 4, 5, 6, 7, 8, 9, 0, 0]);
assert_eq!(partial.sent_authed(), &Idx::new([0..3, 4..10]));
}
#[rstest]
#[should_panic]
fn test_partial_transcript_union_subseq_failure(transcript: Transcript) {
let mut partial = transcript.to_partial(Idx::new(4..10), Idx::new(3..11));
let sent_seq = Subsequence::new(Idx::new([0..3, 13..15]), [0, 1, 2, 5, 6].into()).unwrap();
partial.union_subsequence(Direction::Sent, &sent_seq);
}
#[rstest]
fn test_partial_transcript_set_unauthed_range(transcript: Transcript) {
let mut partial = transcript.to_partial(Idx::new(4..10), Idx::new(3..7));
partial.set_unauthed_range(7, Direction::Sent, 2..5);
partial.set_unauthed_range(5, Direction::Sent, 0..2);
partial.set_unauthed_range(3, Direction::Received, 4..6);
partial.set_unauthed_range(1, Direction::Received, 3..7);
assert_eq!(partial.sent_unsafe(), [5, 5, 7, 7, 4, 5, 6, 7, 8, 9, 0, 0]);
assert_eq!(
partial.received_unsafe(),
[0, 0, 0, 3, 4, 5, 6, 0, 0, 0, 0, 0]
);
}
#[rstest]
#[should_panic]
fn test_subsequence_new_invalid_len() {
let _ = Subsequence::new(Idx::new([0..3, 5..8]), [0, 1, 2, 5, 6].into()).unwrap();
}
#[rstest]
#[should_panic]
fn test_subsequence_copy_to_invalid_len() {
let seq = Subsequence::new(Idx::new([0..3, 5..7]), [0, 1, 2, 5, 6].into()).unwrap();
let mut data: [u8; 3] = [0, 1, 2];
seq.copy_to(&mut data);
}
}