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