1use field_cat::FieldBytes;
10use sha2::{Digest, Sha256};
11
12use crate::error::Error;
13
14#[derive(Debug, Clone)]
38pub struct Transcript {
39 state: Vec<u8>,
40}
41
42impl Transcript {
43 #[must_use]
45 pub fn new(label: &[u8]) -> Self {
46 Self {
47 state: label.to_vec(),
48 }
49 }
50
51 #[must_use]
53 pub fn absorb_bytes(self, data: &[u8]) -> Self {
54 Self {
55 state: self.state.into_iter().chain(data.iter().copied()).collect(),
56 }
57 }
58
59 #[must_use]
61 pub fn absorb_field<F: FieldBytes>(self, elem: &F) -> Self {
62 self.absorb_bytes(&elem.to_le_bytes())
63 }
64
65 pub fn squeeze_challenge<F: FieldBytes>(self) -> Result<(F, Self), Error> {
76 let digest = Sha256::digest(&self.state);
77 let challenge = F::from_le_bytes(digest.as_slice())?;
78 let new_state = self
79 .state
80 .into_iter()
81 .chain(digest.iter().copied())
82 .collect();
83 Ok((challenge, Self { state: new_state }))
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90 use field_cat::{BabyBear, F101};
91
92 #[test]
93 fn deterministic_challenges() -> Result<(), Error> {
94 let t1 = Transcript::new(b"test").absorb_field(&BabyBear::new(42));
95 let t2 = Transcript::new(b"test").absorb_field(&BabyBear::new(42));
96 let (c1, _) = t1.squeeze_challenge::<BabyBear>()?;
97 let (c2, _) = t2.squeeze_challenge::<BabyBear>()?;
98 assert_eq!(c1, c2);
99 Ok(())
100 }
101
102 #[test]
103 fn different_inputs_different_challenges() -> Result<(), Error> {
104 let t1 = Transcript::new(b"test").absorb_field(&BabyBear::new(1));
105 let t2 = Transcript::new(b"test").absorb_field(&BabyBear::new(2));
106 let (c1, _) = t1.squeeze_challenge::<BabyBear>()?;
107 let (c2, _) = t2.squeeze_challenge::<BabyBear>()?;
108 assert_ne!(c1, c2);
109 Ok(())
110 }
111
112 #[test]
113 fn absorb_order_matters() -> Result<(), Error> {
114 let t1 = Transcript::new(b"test")
115 .absorb_field(&F101::new(1))
116 .absorb_field(&F101::new(2));
117 let t2 = Transcript::new(b"test")
118 .absorb_field(&F101::new(2))
119 .absorb_field(&F101::new(1));
120 let (c1, _) = t1.squeeze_challenge::<F101>()?;
121 let (c2, _) = t2.squeeze_challenge::<F101>()?;
122 assert_ne!(c1, c2);
123 Ok(())
124 }
125
126 #[test]
127 fn label_matters() -> Result<(), Error> {
128 let t1 = Transcript::new(b"label_a").absorb_field(&BabyBear::new(42));
129 let t2 = Transcript::new(b"label_b").absorb_field(&BabyBear::new(42));
130 let (c1, _) = t1.squeeze_challenge::<BabyBear>()?;
131 let (c2, _) = t2.squeeze_challenge::<BabyBear>()?;
132 assert_ne!(c1, c2);
133 Ok(())
134 }
135}