solana_zk_token_sdk/instruction/batched_range_proof/
batched_range_proof_u256.rs

1//! The 256-bit batched range proof instruction.
2
3#[cfg(not(target_os = "solana"))]
4use {
5    crate::{
6        encryption::pedersen::{PedersenCommitment, PedersenOpening},
7        errors::{ProofGenerationError, ProofVerificationError},
8        instruction::batched_range_proof::{MAX_COMMITMENTS, MAX_SINGLE_BIT_LENGTH},
9        range_proof::RangeProof,
10    },
11    std::convert::TryInto,
12};
13use {
14    crate::{
15        instruction::{batched_range_proof::BatchedRangeProofContext, ProofType, ZkProofData},
16        zk_token_elgamal::pod,
17    },
18    bytemuck_derive::{Pod, Zeroable},
19};
20
21#[cfg(not(target_os = "solana"))]
22const BATCHED_RANGE_PROOF_U256_BIT_LENGTH: usize = 256;
23
24/// The instruction data that is needed for the
25/// `ProofInstruction::BatchedRangeProofU256Data` instruction.
26///
27/// It includes the cryptographic proof as well as the context data information needed to verify
28/// the proof.
29#[derive(Clone, Copy, Pod, Zeroable)]
30#[repr(C)]
31pub struct BatchedRangeProofU256Data {
32    /// The context data for a batched range proof
33    pub context: BatchedRangeProofContext,
34
35    /// The batched range proof
36    pub proof: pod::RangeProofU256,
37}
38
39#[cfg(not(target_os = "solana"))]
40impl BatchedRangeProofU256Data {
41    pub fn new(
42        commitments: Vec<&PedersenCommitment>,
43        amounts: Vec<u64>,
44        bit_lengths: Vec<usize>,
45        openings: Vec<&PedersenOpening>,
46    ) -> Result<Self, ProofGenerationError> {
47        // each bit length must be at most 128
48        if bit_lengths
49            .iter()
50            .any(|length| *length > MAX_SINGLE_BIT_LENGTH)
51        {
52            return Err(ProofGenerationError::IllegalCommitmentLength);
53        }
54
55        // the sum of the bit lengths must be 256
56        let batched_bit_length = bit_lengths
57            .iter()
58            .try_fold(0_usize, |acc, &x| acc.checked_add(x))
59            .ok_or(ProofGenerationError::IllegalAmountBitLength)?;
60        if batched_bit_length != BATCHED_RANGE_PROOF_U256_BIT_LENGTH {
61            return Err(ProofGenerationError::IllegalAmountBitLength);
62        }
63
64        let context =
65            BatchedRangeProofContext::new(&commitments, &amounts, &bit_lengths, &openings)?;
66
67        let mut transcript = context.new_transcript();
68        let proof = RangeProof::new(amounts, bit_lengths, openings, &mut transcript)?
69            .try_into()
70            .map_err(|_| ProofGenerationError::ProofLength)?;
71
72        Ok(Self { context, proof })
73    }
74}
75
76impl ZkProofData<BatchedRangeProofContext> for BatchedRangeProofU256Data {
77    const PROOF_TYPE: ProofType = ProofType::BatchedRangeProofU256;
78
79    fn context_data(&self) -> &BatchedRangeProofContext {
80        &self.context
81    }
82
83    #[cfg(not(target_os = "solana"))]
84    fn verify_proof(&self) -> Result<(), ProofVerificationError> {
85        let (commitments, bit_lengths) = self.context.try_into()?;
86        let num_commitments = commitments.len();
87
88        if bit_lengths
89            .iter()
90            .any(|length| *length > MAX_SINGLE_BIT_LENGTH)
91        {
92            return Err(ProofVerificationError::IllegalCommitmentLength);
93        }
94
95        if num_commitments > MAX_COMMITMENTS || num_commitments != bit_lengths.len() {
96            return Err(ProofVerificationError::IllegalCommitmentLength);
97        }
98
99        let mut transcript = self.context_data().new_transcript();
100        let proof: RangeProof = self.proof.try_into()?;
101
102        proof
103            .verify(commitments.iter().collect(), bit_lengths, &mut transcript)
104            .map_err(|e| e.into())
105    }
106}
107
108#[cfg(test)]
109mod test {
110    use {
111        super::*,
112        crate::{
113            encryption::pedersen::Pedersen, errors::ProofVerificationError,
114            range_proof::errors::RangeProofVerificationError,
115        },
116    };
117
118    #[test]
119    fn test_batched_range_proof_256_instruction_correctness() {
120        let amount_1 = 4294967295_u64;
121        let amount_2 = 77_u64;
122        let amount_3 = 99_u64;
123        let amount_4 = 99_u64;
124        let amount_5 = 11_u64;
125        let amount_6 = 33_u64;
126        let amount_7 = 99_u64;
127        let amount_8 = 99_u64;
128
129        let (commitment_1, opening_1) = Pedersen::new(amount_1);
130        let (commitment_2, opening_2) = Pedersen::new(amount_2);
131        let (commitment_3, opening_3) = Pedersen::new(amount_3);
132        let (commitment_4, opening_4) = Pedersen::new(amount_4);
133        let (commitment_5, opening_5) = Pedersen::new(amount_5);
134        let (commitment_6, opening_6) = Pedersen::new(amount_6);
135        let (commitment_7, opening_7) = Pedersen::new(amount_7);
136        let (commitment_8, opening_8) = Pedersen::new(amount_8);
137
138        let proof_data = BatchedRangeProofU256Data::new(
139            vec![
140                &commitment_1,
141                &commitment_2,
142                &commitment_3,
143                &commitment_4,
144                &commitment_5,
145                &commitment_6,
146                &commitment_7,
147                &commitment_8,
148            ],
149            vec![
150                amount_1, amount_2, amount_3, amount_4, amount_5, amount_6, amount_7, amount_8,
151            ],
152            vec![32, 32, 32, 32, 32, 32, 32, 32],
153            vec![
154                &opening_1, &opening_2, &opening_3, &opening_4, &opening_5, &opening_6, &opening_7,
155                &opening_8,
156            ],
157        )
158        .unwrap();
159
160        assert!(proof_data.verify_proof().is_ok());
161
162        let amount_1 = 4294967296_u64; // not representable as an 8-bit number
163        let amount_2 = 77_u64;
164        let amount_3 = 99_u64;
165        let amount_4 = 99_u64;
166        let amount_5 = 11_u64;
167        let amount_6 = 33_u64;
168        let amount_7 = 99_u64;
169        let amount_8 = 99_u64;
170
171        let (commitment_1, opening_1) = Pedersen::new(amount_1);
172        let (commitment_2, opening_2) = Pedersen::new(amount_2);
173        let (commitment_3, opening_3) = Pedersen::new(amount_3);
174        let (commitment_4, opening_4) = Pedersen::new(amount_4);
175        let (commitment_5, opening_5) = Pedersen::new(amount_5);
176        let (commitment_6, opening_6) = Pedersen::new(amount_6);
177        let (commitment_7, opening_7) = Pedersen::new(amount_7);
178        let (commitment_8, opening_8) = Pedersen::new(amount_8);
179
180        let proof_data = BatchedRangeProofU256Data::new(
181            vec![
182                &commitment_1,
183                &commitment_2,
184                &commitment_3,
185                &commitment_4,
186                &commitment_5,
187                &commitment_6,
188                &commitment_7,
189                &commitment_8,
190            ],
191            vec![
192                amount_1, amount_2, amount_3, amount_4, amount_5, amount_6, amount_7, amount_8,
193            ],
194            vec![32, 32, 32, 32, 32, 32, 32, 32],
195            vec![
196                &opening_1, &opening_2, &opening_3, &opening_4, &opening_5, &opening_6, &opening_7,
197                &opening_8,
198            ],
199        )
200        .unwrap();
201
202        assert_eq!(
203            proof_data.verify_proof().unwrap_err(),
204            ProofVerificationError::RangeProof(RangeProofVerificationError::AlgebraicRelation),
205        );
206    }
207}