1use std::{collections::HashSet, ops::Range};
4
5use jf_merkle_tree::MerkleTreeScheme;
6use jf_utils::canonical;
7use serde::{Deserialize, Serialize};
8
9use crate::{
10 avid_m::{
11 config::AvidMConfig,
12 namespaced::{NsAvidMCommit, NsAvidMScheme, NsAvidMShare},
13 AvidMCommit, AvidMParam, AvidMScheme, AvidMShare, Config, MerkleProof, MerkleTree, F,
14 },
15 VerificationResult, VidError, VidResult, VidScheme,
16};
17
18#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
31pub struct AvidMBadEncodingProof {
32 #[serde(with = "canonical")]
34 recovered_poly: Vec<F>,
35 #[serde(with = "canonical")]
37 raw_shares: Vec<(usize, MerkleProof)>,
38}
39
40impl AvidMScheme {
41 pub fn proof_of_incorrect_encoding(
44 param: &AvidMParam,
45 commit: &AvidMCommit,
46 shares: &[AvidMShare],
47 ) -> VidResult<AvidMBadEncodingProof> {
48 let shares = shares
50 .iter()
51 .filter(|share| {
52 AvidMScheme::verify_share(param, commit, share).is_ok_and(|r| r.is_ok())
53 })
54 .cloned()
55 .collect::<Vec<_>>();
56 let witness = Self::recover_fields(param, &shares)?;
59 let (mt, _) = Self::raw_encode(param, &witness)?;
60 if mt.commitment() == commit.commit {
61 return Err(VidError::Argument(
62 "Cannot generate the proof of incorrect encoding: encoding is good.".to_string(),
63 ));
64 }
65
66 let mut raw_shares = vec![];
67 let mut visited_indices = HashSet::new();
68 for share in shares {
69 for (index, mt_proof) in share
70 .content
71 .range
72 .clone()
73 .zip(share.content.mt_proofs.iter())
74 {
75 if index > param.total_weights {
76 return Err(VidError::InvalidShare);
77 }
78 if visited_indices.contains(&index) {
79 return Err(VidError::InvalidShare);
80 }
81 raw_shares.push((index, mt_proof.clone()));
82 visited_indices.insert(index);
83 if raw_shares.len() == param.recovery_threshold {
84 break;
85 }
86 }
87 if raw_shares.len() == param.recovery_threshold {
88 break;
89 }
90 }
91 if raw_shares.len() != param.recovery_threshold {
92 return Err(VidError::InsufficientShares);
93 }
94
95 Ok(AvidMBadEncodingProof {
96 recovered_poly: witness,
97 raw_shares,
98 })
99 }
100}
101
102impl AvidMBadEncodingProof {
103 pub fn verify(
105 &self,
106 param: &AvidMParam,
107 commit: &AvidMCommit,
108 ) -> VidResult<VerificationResult> {
109 if self.raw_shares.len() != param.recovery_threshold {
111 return Err(VidError::InvalidParam);
112 }
113 if self.recovered_poly.len() > param.recovery_threshold {
114 return Err(VidError::InvalidParam);
116 }
117 let (mt, raw_shares) = AvidMScheme::raw_encode(param, &self.recovered_poly)?;
118 if mt.commitment() == commit.commit {
119 return Ok(Err(()));
120 }
121 let mut visited_indices = HashSet::new();
122 for (index, proof) in self.raw_shares.iter() {
123 if *index >= param.total_weights || visited_indices.contains(index) {
124 return Err(VidError::InvalidShare);
125 }
126 let digest = Config::raw_share_digest(&raw_shares[*index])?;
127 if MerkleTree::verify(&commit.commit, *index as u64, &digest, proof)?.is_err() {
128 return Ok(Err(()));
129 }
130 visited_indices.insert(*index);
131 }
132 Ok(Ok(()))
133 }
134}
135
136#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
140pub struct NsAvidMBadEncodingProof {
141 pub ns_index: usize,
143 pub ns_commit: AvidMCommit,
145 pub ns_mt_proof: MerkleProof,
147 pub ns_proof: AvidMBadEncodingProof,
149}
150
151impl NsAvidMScheme {
152 pub fn proof_of_incorrect_encoding_for_namespace(
154 param: &AvidMParam,
155 ns_index: usize,
156 commit: &NsAvidMCommit,
157 shares: &[NsAvidMShare],
158 ) -> VidResult<NsAvidMBadEncodingProof> {
159 if shares.is_empty() {
160 return Err(VidError::InsufficientShares);
161 }
162 if shares.iter().any(|share| !share.contains_ns(ns_index)) {
163 return Err(VidError::IndexOutOfBound);
164 }
165 let mt = MerkleTree::from_elems(
166 None,
167 shares[0].ns_commits().iter().map(|commit| commit.commit),
168 )?;
169 if mt.commitment() != commit.commit {
170 return Err(VidError::InvalidParam);
171 }
172 let (ns_commit, ns_mt_proof) = mt
173 .lookup(ns_index as u64)
174 .expect_ok()
175 .expect("MT lookup shouldn't fail");
176 let ns_commit = AvidMCommit { commit: *ns_commit };
177 let shares = shares
178 .iter()
179 .filter_map(|share| share.inner_ns_share(ns_index))
180 .collect::<Vec<_>>();
181 Ok(NsAvidMBadEncodingProof {
182 ns_index,
183 ns_commit,
184 ns_mt_proof,
185 ns_proof: AvidMScheme::proof_of_incorrect_encoding(param, &ns_commit, &shares)?,
186 })
187 }
188
189 pub fn proof_of_incorrect_encoding(
191 param: &AvidMParam,
192 commit: &NsAvidMCommit,
193 shares: &[NsAvidMShare],
194 ) -> VidResult<NsAvidMBadEncodingProof> {
195 if shares.is_empty() {
196 return Err(VidError::InsufficientShares);
197 }
198 for ns_index in 0..shares[0].ns_commits().len() {
199 let result =
200 Self::proof_of_incorrect_encoding_for_namespace(param, ns_index, commit, shares);
201 if matches!(
203 result,
204 Ok(_)
205 | Err(VidError::InvalidShare)
206 | Err(VidError::IndexOutOfBound)
207 | Err(VidError::InsufficientShares)
208 ) {
209 return result;
210 }
211 }
212 Err(VidError::InvalidParam)
213 }
214}
215
216impl NsAvidMBadEncodingProof {
217 pub fn verify(
219 &self,
220 param: &AvidMParam,
221 commit: &NsAvidMCommit,
222 ) -> VidResult<VerificationResult> {
223 if MerkleTree::verify(
224 &commit.commit,
225 self.ns_index as u64,
226 &self.ns_commit.commit,
227 &self.ns_mt_proof,
228 )?
229 .is_err()
230 {
231 return Ok(Err(()));
232 }
233 self.ns_proof.verify(param, &self.ns_commit)
234 }
235}
236
237#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
241pub struct NsProof {
242 pub ns_index: usize,
244 #[serde(with = "base64_bytes")]
246 pub ns_payload: Vec<u8>,
247 pub ns_proof: MerkleProof,
249}
250
251impl NsAvidMScheme {
252 pub fn namespace_proof(
255 param: &AvidMParam,
256 payload: &[u8],
257 ns_index: usize,
258 ns_table: impl IntoIterator<Item = Range<usize>>,
259 ) -> VidResult<NsProof> {
260 let ns_table = ns_table.into_iter().collect::<Vec<_>>();
261 let ns_payload_range = ns_table[ns_index].clone();
262 let ns_commits = ns_table
263 .into_iter()
264 .map(|ns_range| {
265 AvidMScheme::commit(param, &payload[ns_range]).map(|commit| commit.commit)
266 })
267 .collect::<Result<Vec<_>, _>>()?;
268 let mt = MerkleTree::from_elems(None, &ns_commits)?;
269 Ok(NsProof {
270 ns_index,
271 ns_payload: payload[ns_payload_range].to_vec(),
272 ns_proof: mt
273 .lookup(ns_index as u64)
274 .expect_ok()
275 .expect("MT lookup shouldn't fail")
276 .1,
277 })
278 }
279
280 pub fn verify_namespace_proof(
282 param: &AvidMParam,
283 commit: &NsAvidMCommit,
284 proof: &NsProof,
285 ) -> VidResult<VerificationResult> {
286 let ns_commit = AvidMScheme::commit(param, &proof.ns_payload)?;
287 Ok(MerkleTree::verify(
288 &commit.commit,
289 proof.ns_index as u64,
290 &ns_commit.commit,
291 &proof.ns_proof,
292 )?)
293 }
294}
295
296#[cfg(test)]
297mod tests {
298 use ark_poly::EvaluationDomain;
299 use jf_merkle_tree::MerkleTreeScheme;
300 use rand::{seq::SliceRandom, Rng};
301
302 use crate::{
303 avid_m::{
304 config::AvidMConfig,
305 namespaced::{NsAvidMCommit, NsAvidMScheme, NsAvidMShare},
306 proofs::AvidMBadEncodingProof,
307 radix2_domain, AvidMCommit, AvidMScheme, AvidMShare, Config, MerkleTree, RawAvidMShare,
308 F,
309 },
310 utils::bytes_to_field,
311 VidScheme,
312 };
313
314 #[test]
315 fn test_proof_of_incorrect_encoding() {
316 let mut rng = jf_utils::test_rng();
317 let param = AvidMScheme::setup(5usize, 10usize).unwrap();
318 let weights = [1u32; 10];
319 let payload_byte_len = bytes_to_field::elem_byte_capacity::<F>() * 4;
320 let domain = radix2_domain::<F>(param.total_weights).unwrap();
321
322 let high_degree_polynomial = vec![F::from(1u64); 10];
323 let mal_payload: Vec<_> = domain
324 .fft(&high_degree_polynomial)
325 .into_iter()
326 .take(param.total_weights)
327 .map(|v| vec![v])
328 .collect();
329
330 let mt = MerkleTree::from_elems(
331 None,
332 mal_payload
333 .iter()
334 .map(|v| Config::raw_share_digest(v).unwrap()),
335 )
336 .unwrap();
337
338 let (commit, mut shares) =
339 AvidMScheme::distribute_shares(¶m, &weights, mt, mal_payload, payload_byte_len)
340 .unwrap();
341
342 shares.shuffle(&mut rng);
343
344 assert!(AvidMScheme::proof_of_incorrect_encoding(¶m, &commit, &shares[..1]).is_err());
346
347 let proof =
349 AvidMScheme::proof_of_incorrect_encoding(¶m, &commit, &shares[..5]).unwrap();
350 assert!(proof.verify(¶m, &commit).unwrap().is_ok());
351
352 let payload = [1u8; 50];
354 let (commit, mut shares) = AvidMScheme::disperse(¶m, &weights, &payload).unwrap();
355 shares.shuffle(&mut rng);
356 assert!(AvidMScheme::proof_of_incorrect_encoding(¶m, &commit, &shares).is_err());
357
358 let witness = AvidMScheme::pad_to_fields(¶m, &payload);
359 let bad_proof = AvidMBadEncodingProof {
360 recovered_poly: witness.clone(),
361 raw_shares: shares
362 .iter()
363 .map(|share| (share.index as usize, share.content.mt_proofs[0].clone()))
364 .collect(),
365 };
366 assert!(bad_proof.verify(¶m, &commit).is_err());
367
368 let mut bad_witness = vec![F::from(0u64); 5];
370 bad_witness[0] = shares[0].content.payload[0][0];
371 let bad_proof2 = AvidMBadEncodingProof {
372 recovered_poly: bad_witness,
373 raw_shares: std::iter::repeat_n(bad_proof.raw_shares[0].clone(), 6).collect(),
374 };
375 assert!(bad_proof2.verify(¶m, &commit).is_err());
376 }
377
378 #[test]
379 fn test_ns_proof() {
380 let param = AvidMScheme::setup(5usize, 10usize).unwrap();
381 let payload = vec![1u8; 100];
382 let ns_table = vec![(0..10), (10..21), (21..33), (33..48), (48..100)];
383 let commit = NsAvidMScheme::commit(¶m, &payload, ns_table.clone()).unwrap();
384
385 for (i, _) in ns_table.iter().enumerate() {
386 let proof =
387 NsAvidMScheme::namespace_proof(¶m, &payload, i, ns_table.clone()).unwrap();
388 assert!(
389 NsAvidMScheme::verify_namespace_proof(¶m, &commit, &proof)
390 .unwrap()
391 .is_ok()
392 );
393 }
394 let mut proof =
395 NsAvidMScheme::namespace_proof(¶m, &payload, 1, ns_table.clone()).unwrap();
396 proof.ns_index = 0;
397 assert!(
398 NsAvidMScheme::verify_namespace_proof(¶m, &commit, &proof)
399 .unwrap()
400 .is_err()
401 );
402 proof.ns_index = 1;
403 proof.ns_payload[0] = 0u8;
404 assert!(
405 NsAvidMScheme::verify_namespace_proof(¶m, &commit, &proof)
406 .unwrap()
407 .is_err()
408 );
409 proof.ns_index = 100;
410 assert!(
411 NsAvidMScheme::verify_namespace_proof(¶m, &commit, &proof)
412 .unwrap()
413 .is_err()
414 );
415 }
416
417 #[test]
418 fn test_ns_proof_of_incorrect_encoding() {
419 let mut rng = jf_utils::test_rng();
420 let param = AvidMScheme::setup(5usize, 10usize).unwrap();
421 let mut payload = [1u8; 100];
422 rng.fill(&mut payload[..]);
423 let distribution = [1u32; 10];
424 let ns_table = [(0..10), (10..21), (21..33), (33..48), (48..100)];
425 let domain = radix2_domain::<F>(param.total_weights).unwrap();
426
427 let mut ns_commits = vec![];
429 let mut disperses = vec![];
430 let mut ns_lens = vec![];
431 for ns_range in ns_table.iter() {
432 ns_lens.push(ns_range.len());
433 if ns_range.start == 10 {
434 let high_degree_polynomial = vec![F::from(1u64); 10];
436 let mal_payload: Vec<_> = domain
437 .fft(&high_degree_polynomial)
438 .into_iter()
439 .take(param.total_weights)
440 .map(|v| vec![v])
441 .collect();
442 let bad_mt = MerkleTree::from_elems(
443 None,
444 mal_payload
445 .iter()
446 .map(|v| Config::raw_share_digest(v).unwrap()),
447 )
448 .unwrap();
449 ns_commits.push(bad_mt.commitment());
450 let shares: Vec<_> = mal_payload
451 .into_iter()
452 .enumerate()
453 .map(|(i, v)| AvidMShare {
454 index: i as u32,
455 payload_byte_len: ns_range.len(),
456 content: RawAvidMShare {
457 range: (i..i + 1),
458 payload: vec![v],
459 mt_proofs: vec![bad_mt.lookup(i as u64).expect_ok().unwrap().1],
460 },
461 })
462 .collect();
463 disperses.push(shares);
464 } else {
465 let (commit, shares) =
466 AvidMScheme::disperse(¶m, &distribution, &payload[ns_range.clone()])
467 .unwrap();
468 ns_commits.push(commit.commit);
469 disperses.push(shares);
470 }
471 }
472 let commit = NsAvidMCommit {
473 commit: MerkleTree::from_elems(None, &ns_commits)
474 .unwrap()
475 .commitment(),
476 };
477 let ns_commits: Vec<_> = ns_commits
478 .into_iter()
479 .map(|comm| AvidMCommit { commit: comm })
480 .collect();
481 let mut shares = vec![NsAvidMShare::default(); disperses[0].len()];
482 shares.iter_mut().for_each(|share| {
483 share.index = disperses[0][0].index;
484 share.ns_commits = ns_commits.clone();
485 share.ns_lens = ns_lens.clone();
486 });
487 disperses.into_iter().for_each(|ns_disperse| {
488 shares
489 .iter_mut()
490 .zip(ns_disperse)
491 .for_each(|(share, ns_share)| share.content.push(ns_share.content))
492 });
493
494 let proof =
496 NsAvidMScheme::proof_of_incorrect_encoding_for_namespace(¶m, 1, &commit, &shares)
497 .unwrap();
498 assert!(proof.verify(¶m, &commit).unwrap().is_ok());
499
500 for ns_index in [0, 2, 3, 4] {
502 assert!(NsAvidMScheme::proof_of_incorrect_encoding_for_namespace(
503 ¶m, ns_index, &commit, &shares
504 )
505 .is_err());
506 }
507
508 let proof = NsAvidMScheme::proof_of_incorrect_encoding(¶m, &commit, &shares).unwrap();
509 assert_eq!(proof.ns_index, 1);
510 assert!(proof.verify(¶m, &commit).unwrap().is_ok());
511 }
512}