1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
use std::io::{Read, Write};

use anyhow::{ensure, Context};
use bellperson::groth16::{self, PreparedVerifyingKey};
use blstrs::Bls12;

use crate::error::Result;

pub struct MultiProof<'a> {
    pub circuit_proofs: Vec<groth16::Proof<Bls12>>,
    pub verifying_key: &'a PreparedVerifyingKey<Bls12>,
}

const GROTH_PROOF_SIZE: usize = 192;

impl<'a> MultiProof<'a> {
    pub fn new(
        groth_proofs: Vec<groth16::Proof<Bls12>>,
        verifying_key: &'a PreparedVerifyingKey<Bls12>,
    ) -> Self {
        MultiProof {
            circuit_proofs: groth_proofs,
            verifying_key,
        }
    }

    pub fn new_from_reader<R: Read>(
        partitions: Option<usize>,
        mut reader: R,
        verifying_key: &'a PreparedVerifyingKey<Bls12>,
    ) -> Result<Self> {
        let num_proofs = partitions.unwrap_or(1);

        let mut proof_vec: Vec<u8> = Vec::with_capacity(num_proofs * GROTH_PROOF_SIZE);
        reader.read_to_end(&mut proof_vec)?;

        Self::new_from_bytes(partitions, &proof_vec, verifying_key)
    }

    // Parallelizing reduces deserialization time for 10 proofs from 13ms to 2ms.
    pub fn new_from_bytes(
        partitions: Option<usize>,
        proof_bytes: &[u8],
        verifying_key: &'a PreparedVerifyingKey<Bls12>,
    ) -> Result<Self> {
        let num_proofs = partitions.unwrap_or(1);

        let proofs = groth16::Proof::read_many(proof_bytes, num_proofs)?;

        ensure!(
            num_proofs == proofs.len(),
            "expected {} proofs but found only {}",
            num_proofs,
            proofs.len()
        );

        Ok(Self::new(proofs, verifying_key))
    }

    pub fn write<W: Write>(&self, mut writer: W) -> Result<()> {
        for proof in &self.circuit_proofs {
            proof.write(&mut writer)?
        }
        Ok(())
    }

    pub fn to_vec(&self) -> Result<Vec<u8>> {
        let mut out = Vec::new();
        self.write(&mut out).context("known allocation target")?;
        Ok(out)
    }

    pub fn len(&self) -> usize {
        self.circuit_proofs.len()
    }

    pub fn is_empty(&self) -> bool {
        self.circuit_proofs.is_empty()
    }
}