coconut_crypto/helpers/
with_schnorr_response.rs1use alloc::vec::Vec;
2use ark_ec::AffineRepr;
3use ark_serialize::*;
4use core::{cmp::Ordering, ops::Range};
5use serde::{de::DeserializeOwned, Deserialize, Serialize};
6use serde_with::serde_as;
7use utils::{aliases::CanonicalSerDe, serde_utils::ArkObjectBytes};
8
9use schnorr_pok::{
10 error::SchnorrError, SchnorrChallengeContributor, SchnorrCommitment, SchnorrResponse,
11};
12
13use super::WithSchnorrAndBlindings;
14
15#[serde_as]
17#[derive(
18 Clone, Debug, PartialEq, Eq, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize,
19)]
20#[serde(bound = "V: Serialize + DeserializeOwned")]
21pub struct WithSchnorrResponse<G: AffineRepr, V: CanonicalSerDe> {
22 #[serde_as(as = "ArkObjectBytes")]
23 pub commitment: G,
24 pub response: SchnorrResponse<G>,
25 pub committed_message_indices: IndiceRange,
26 pub value: V,
27}
28
29#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
30pub struct IndiceRange(Range<usize>);
31utils::impl_deref! { IndiceRange(Range<usize>) }
32
33impl CanonicalSerialize for IndiceRange {
34 fn serialized_size(&self, compress: Compress) -> usize {
35 self.start.serialized_size(compress) + self.end.serialized_size(compress)
36 }
37
38 fn serialize_with_mode<W: Write>(
39 &self,
40 mut writer: W,
41 compress: Compress,
42 ) -> Result<(), SerializationError> {
43 self.start.serialize_with_mode(&mut writer, compress)?;
44 self.end.serialize_with_mode(&mut writer, compress)?;
45
46 Ok(())
47 }
48}
49
50impl Valid for IndiceRange {
51 fn check(&self) -> Result<(), SerializationError> {
52 if self.start > self.end {
53 Err(SerializationError::InvalidData)
54 } else {
55 Ok(())
56 }
57 }
58}
59
60impl CanonicalDeserialize for IndiceRange {
61 fn deserialize_with_mode<R: Read>(
62 mut reader: R,
63 compress: Compress,
64 validate: Validate,
65 ) -> Result<Self, SerializationError> {
66 let start = usize::deserialize_with_mode(&mut reader, compress, validate)?;
67 let end = usize::deserialize_with_mode(&mut reader, compress, validate)?;
68
69 Ok(Self(start..end))
70 }
71}
72
73impl<G, V> WithSchnorrResponse<G, V>
74where
75 G: AffineRepr,
76 V: CanonicalSerDe + Clone,
77{
78 pub fn new(
80 response: SchnorrResponse<G>,
81 &WithSchnorrAndBlindings {
82 schnorr: SchnorrCommitment { t, .. },
83 ref value,
84 }: &WithSchnorrAndBlindings<G, V>,
85 committed_message_indices: Range<usize>,
86 ) -> Self {
87 WithSchnorrResponse {
88 response,
89 commitment: t,
90 value: value.clone(),
91 committed_message_indices: IndiceRange(committed_message_indices),
92 }
93 }
94}
95
96impl<G, V> SchnorrChallengeContributor for WithSchnorrResponse<G, V>
97where
98 G: AffineRepr,
99 V: CanonicalSerDe,
100{
101 fn challenge_contribution<W: Write>(&self, mut writer: W) -> Result<(), SchnorrError> {
103 self.value
104 .serialize_compressed(&mut writer)
105 .map_err(SchnorrError::Serialization)?;
106 self.commitment
107 .serialize_compressed(&mut writer)
108 .map_err(SchnorrError::Serialization)
109 }
110}
111
112impl<G, V> WithSchnorrResponse<G, V>
113where
114 G: AffineRepr,
115 V: CanonicalSerDe,
116{
117 pub(crate) fn response_for_message(
121 &self,
122 msg_idx: usize,
123 unique_revealed_msg_indices: impl IntoIterator<Item = usize>,
124 ) -> Result<&G::ScalarField, SchnorrError> {
125 let mut adjusted_msg_idx = msg_idx;
126
127 let out_of_bounds = move || {
128 SchnorrError::IndexOutOfBounds(
129 msg_idx,
130 self.committed_message_indices
131 .end
132 .saturating_sub(self.committed_message_indices.start),
133 )
134 };
135
136 for idx in unique_revealed_msg_indices {
137 match idx.cmp(&msg_idx) {
138 Ordering::Equal => Err(SchnorrError::InvalidResponse)?,
139 Ordering::Less => {
140 adjusted_msg_idx = adjusted_msg_idx.checked_sub(1).ok_or_else(out_of_bounds)?
141 }
142 _ => {}
143 }
144 }
145
146 self.committed_message_indices
147 .start
148 .checked_add(adjusted_msg_idx)
149 .ok_or_else(out_of_bounds)
150 .and_then(|adjusted_msg_idx_with_applied_offset| {
151 self.response
152 .get_response(adjusted_msg_idx_with_applied_offset)
153 })
154 }
155}