mod.rs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. //! The Bulletproofs range-proof implementation over Curve25519 Ristretto points.
  2. //!
  3. //! The implementation is based on the dalek-cryptography bulletproofs
  4. //! [implementation](https://github.com/dalek-cryptography/bulletproofs). Compared to the original
  5. //! implementation by dalek-cryptography:
  6. //! - This implementation focuses on the range proof implementation, while the dalek-cryptography
  7. //! crate additionally implements the general bulletproofs implementation for languages that can be
  8. //! represented by arithmetic circuits as well as MPC.
  9. //! - This implementation implements a non-interactive range proof aggregation that is specified in
  10. //! the original Bulletproofs [paper](https://eprint.iacr.org/2017/1066) (Section 4.3).
  11. //!
  12. #[cfg(not(target_os = "solana"))]
  13. use {
  14. crate::encryption::pedersen::{Pedersen, PedersenCommitment, PedersenOpening},
  15. crate::{
  16. encryption::pedersen::{G, H},
  17. range_proof::{
  18. errors::{RangeProofGenerationError, RangeProofVerificationError},
  19. generators::BulletproofGens,
  20. inner_product::InnerProductProof,
  21. },
  22. transcript::TranscriptProtocol,
  23. },
  24. core::iter,
  25. curve25519_dalek::traits::MultiscalarMul,
  26. curve25519_dalek::{
  27. ristretto::{CompressedRistretto, RistrettoPoint},
  28. scalar::Scalar,
  29. traits::{IsIdentity, VartimeMultiscalarMul},
  30. },
  31. merlin::Transcript,
  32. rand::rngs::OsRng,
  33. subtle::{Choice, ConditionallySelectable},
  34. };
  35. pub mod errors;
  36. #[cfg(not(target_os = "solana"))]
  37. pub mod generators;
  38. #[cfg(not(target_os = "solana"))]
  39. pub mod inner_product;
  40. #[cfg(not(target_os = "solana"))]
  41. pub mod util;
  42. #[allow(non_snake_case)]
  43. #[cfg(not(target_os = "solana"))]
  44. #[derive(Clone)]
  45. pub struct RangeProof {
  46. pub A: CompressedRistretto, // 32 bytes
  47. pub S: CompressedRistretto, // 32 bytes
  48. pub T_1: CompressedRistretto, // 32 bytes
  49. pub T_2: CompressedRistretto, // 32 bytes
  50. pub t_x: Scalar, // 32 bytes
  51. pub t_x_blinding: Scalar, // 32 bytes
  52. pub e_blinding: Scalar, // 32 bytes
  53. pub ipp_proof: InnerProductProof, // 448 bytes for withdraw; 512 for transfer
  54. }
  55. #[allow(non_snake_case)]
  56. #[cfg(not(target_os = "solana"))]
  57. impl RangeProof {
  58. /// Create an aggregated range proof.
  59. ///
  60. /// The proof is created with respect to a vector of Pedersen commitments C_1, ..., C_m. The
  61. /// method itself does not take in these commitments, but the values associated with the commitments:
  62. /// - vector of committed amounts v_1, ..., v_m represented as u64
  63. /// - bit-lengths of the committed amounts
  64. /// - Pedersen openings for each commitments
  65. ///
  66. /// The sum of the bit-lengths of the commitments amounts must be a power-of-two
  67. #[allow(clippy::many_single_char_names)]
  68. #[cfg(not(target_os = "solana"))]
  69. pub fn new(
  70. amounts: Vec<u64>,
  71. bit_lengths: Vec<usize>,
  72. openings: Vec<&PedersenOpening>,
  73. transcript: &mut Transcript,
  74. ) -> Result<Self, RangeProofGenerationError> {
  75. // amounts, bit-lengths, openings must be same length vectors
  76. let m = amounts.len();
  77. if bit_lengths.len() != m || openings.len() != m {
  78. return Err(RangeProofGenerationError::VectorLengthMismatch);
  79. }
  80. // each bit length must be greater than 0 for the proof to make sense
  81. if bit_lengths
  82. .iter()
  83. .any(|bit_length| *bit_length == 0 || *bit_length > u64::BITS as usize)
  84. {
  85. return Err(RangeProofGenerationError::InvalidBitSize);
  86. }
  87. // total vector dimension to compute the ultimate inner product proof for
  88. let nm: usize = bit_lengths.iter().sum();
  89. if !nm.is_power_of_two() {
  90. return Err(RangeProofGenerationError::VectorLengthMismatch);
  91. }
  92. let bp_gens = BulletproofGens::new(nm)
  93. .map_err(|_| RangeProofGenerationError::MaximumGeneratorLengthExceeded)?;
  94. // bit-decompose values and generate their Pedersen vector commitment
  95. let a_blinding = Scalar::random(&mut OsRng);
  96. let mut A = a_blinding * &(*H);
  97. let mut gens_iter = bp_gens.G(nm).zip(bp_gens.H(nm));
  98. for (amount_i, n_i) in amounts.iter().zip(bit_lengths.iter()) {
  99. for j in 0..(*n_i) {
  100. let (G_ij, H_ij) = gens_iter.next().unwrap();
  101. // `j` is guaranteed to be at most `u64::BITS` (a 6-bit number) and therefore,
  102. // casting is lossless and right shift can be safely unwrapped
  103. let v_ij = Choice::from((amount_i.checked_shr(j as u32).unwrap() & 1) as u8);
  104. let mut point = -H_ij;
  105. point.conditional_assign(G_ij, v_ij);
  106. A += point;
  107. }
  108. }
  109. let A = A.compress();
  110. // generate blinding factors and generate their Pedersen vector commitment
  111. let s_L: Vec<Scalar> = (0..nm).map(|_| Scalar::random(&mut OsRng)).collect();
  112. let s_R: Vec<Scalar> = (0..nm).map(|_| Scalar::random(&mut OsRng)).collect();
  113. // generate blinding factor for Pedersen commitment; `s_blinding` should not to be confused
  114. // with blinding factors for the actual inner product vector
  115. let s_blinding = Scalar::random(&mut OsRng);
  116. let S = RistrettoPoint::multiscalar_mul(
  117. iter::once(&s_blinding).chain(s_L.iter()).chain(s_R.iter()),
  118. iter::once(&(*H)).chain(bp_gens.G(nm)).chain(bp_gens.H(nm)),
  119. )
  120. .compress();
  121. // add the Pedersen vector commitments to the transcript (send the commitments to the verifier)
  122. transcript.append_point(b"A", &A);
  123. transcript.append_point(b"S", &S);
  124. // derive challenge scalars from the transcript (receive challenge from the verifier): `y`
  125. // and `z` used for merge multiple inner product relations into one single inner product
  126. let y = transcript.challenge_scalar(b"y");
  127. let z = transcript.challenge_scalar(b"z");
  128. // define blinded vectors:
  129. // - l(x) = (a_L - z*1) + s_L*x
  130. // - r(x) = (y^n * (a_R + z*1) + [z^2*2^n | z^3*2^n | ... | z^m*2^n]) + y^n * s_R*x
  131. let mut l_poly = util::VecPoly1::zero(nm);
  132. let mut r_poly = util::VecPoly1::zero(nm);
  133. let mut i = 0;
  134. let mut exp_z = z * z;
  135. let mut exp_y = Scalar::ONE;
  136. for (amount_i, n_i) in amounts.iter().zip(bit_lengths.iter()) {
  137. let mut exp_2 = Scalar::ONE;
  138. for j in 0..(*n_i) {
  139. // `j` is guaranteed to be at most `u64::BITS` (a 6-bit number) and therefore,
  140. // casting is lossless and right shift can be safely unwrapped
  141. let a_L_j = Scalar::from(amount_i.checked_shr(j as u32).unwrap() & 1);
  142. let a_R_j = a_L_j - Scalar::ONE;
  143. l_poly.0[i] = a_L_j - z;
  144. l_poly.1[i] = s_L[i];
  145. r_poly.0[i] = exp_y * (a_R_j + z) + exp_z * exp_2;
  146. r_poly.1[i] = exp_y * s_R[i];
  147. exp_y *= y;
  148. exp_2 = exp_2 + exp_2;
  149. // `i` is capped by the sum of vectors in `bit_lengths`
  150. i = i.checked_add(1).unwrap();
  151. }
  152. exp_z *= z;
  153. }
  154. // define t(x) = <l(x), r(x)> = t_0 + t_1*x + t_2*x
  155. let t_poly = l_poly
  156. .inner_product(&r_poly)
  157. .ok_or(RangeProofGenerationError::InnerProductLengthMismatch)?;
  158. // generate Pedersen commitment for the coefficients t_1 and t_2
  159. let (T_1, t_1_blinding) = Pedersen::new(t_poly.1);
  160. let (T_2, t_2_blinding) = Pedersen::new(t_poly.2);
  161. let T_1 = T_1.get_point().compress();
  162. let T_2 = T_2.get_point().compress();
  163. transcript.append_point(b"T_1", &T_1);
  164. transcript.append_point(b"T_2", &T_2);
  165. // evaluate t(x) on challenge x and homomorphically compute the openings for
  166. // z^2 * V_1 + z^3 * V_2 + ... + z^{m+1} * V_m + delta(y, z)*G + x*T_1 + x^2*T_2
  167. let x = transcript.challenge_scalar(b"x");
  168. let mut agg_opening = Scalar::ZERO;
  169. let mut exp_z = z;
  170. for opening in openings {
  171. exp_z *= z;
  172. agg_opening += exp_z * opening.get_scalar();
  173. }
  174. let t_blinding_poly = util::Poly2(
  175. agg_opening,
  176. *t_1_blinding.get_scalar(),
  177. *t_2_blinding.get_scalar(),
  178. );
  179. let t_x = t_poly.eval(x);
  180. let t_x_blinding = t_blinding_poly.eval(x);
  181. transcript.append_scalar(b"t_x", &t_x);
  182. transcript.append_scalar(b"t_x_blinding", &t_x_blinding);
  183. // homomorphically compuate the openings for A + x*S
  184. let e_blinding = a_blinding + s_blinding * x;
  185. let l_vec = l_poly.eval(x);
  186. let r_vec = r_poly.eval(x);
  187. transcript.append_scalar(b"e_blinding", &e_blinding);
  188. // compute the inner product argument on the commitment:
  189. // P = <l(x), G> + <r(x), H'> + <l(x), r(x)>*Q
  190. let w = transcript.challenge_scalar(b"w");
  191. let Q = w * &G;
  192. let G_factors: Vec<Scalar> = iter::repeat_n(Scalar::ONE, nm).collect();
  193. let H_factors: Vec<Scalar> = util::exp_iter(y.invert()).take(nm).collect();
  194. // generate challenge `c` for consistency with the verifier's transcript
  195. transcript.challenge_scalar(b"c");
  196. let ipp_proof = InnerProductProof::new(
  197. &Q,
  198. &G_factors,
  199. &H_factors,
  200. bp_gens.G(nm).cloned().collect(),
  201. bp_gens.H(nm).cloned().collect(),
  202. l_vec,
  203. r_vec,
  204. transcript,
  205. )?;
  206. Ok(RangeProof {
  207. A,
  208. S,
  209. T_1,
  210. T_2,
  211. t_x,
  212. t_x_blinding,
  213. e_blinding,
  214. ipp_proof,
  215. })
  216. }
  217. #[allow(clippy::many_single_char_names)]
  218. pub fn verify(
  219. &self,
  220. comms: Vec<&PedersenCommitment>,
  221. bit_lengths: Vec<usize>,
  222. transcript: &mut Transcript,
  223. ) -> Result<(), RangeProofVerificationError> {
  224. // commitments and bit-lengths must be same length vectors
  225. if comms.len() != bit_lengths.len() {
  226. return Err(RangeProofVerificationError::VectorLengthMismatch);
  227. }
  228. let m = bit_lengths.len();
  229. let nm: usize = bit_lengths.iter().sum();
  230. let bp_gens = BulletproofGens::new(nm)
  231. .map_err(|_| RangeProofVerificationError::MaximumGeneratorLengthExceeded)?;
  232. if !nm.is_power_of_two() {
  233. return Err(RangeProofVerificationError::InvalidBitSize);
  234. }
  235. // append proof data to transcript and derive appropriate challenge scalars
  236. transcript.validate_and_append_point(b"A", &self.A)?;
  237. transcript.validate_and_append_point(b"S", &self.S)?;
  238. let y = transcript.challenge_scalar(b"y");
  239. let z = transcript.challenge_scalar(b"z");
  240. let zz = z * z;
  241. let minus_z = -z;
  242. transcript.validate_and_append_point(b"T_1", &self.T_1)?;
  243. transcript.validate_and_append_point(b"T_2", &self.T_2)?;
  244. let x = transcript.challenge_scalar(b"x");
  245. transcript.append_scalar(b"t_x", &self.t_x);
  246. transcript.append_scalar(b"t_x_blinding", &self.t_x_blinding);
  247. transcript.append_scalar(b"e_blinding", &self.e_blinding);
  248. let w = transcript.challenge_scalar(b"w");
  249. let c = transcript.challenge_scalar(b"c"); // challenge value for batching multiscalar mul
  250. // verify inner product proof
  251. let (x_sq, x_inv_sq, s) = self.ipp_proof.verification_scalars(nm, transcript)?;
  252. let s_inv = s.iter().rev();
  253. let a = self.ipp_proof.a;
  254. let b = self.ipp_proof.b;
  255. // construct concat_z_and_2, an iterator of the values of
  256. // z^0 * \vec(2)^n || z^1 * \vec(2)^n || ... || z^(m-1) * \vec(2)^n
  257. let concat_z_and_2: Vec<Scalar> = util::exp_iter(z)
  258. .zip(bit_lengths.iter())
  259. .flat_map(|(exp_z, n_i)| {
  260. util::exp_iter(Scalar::from(2u64))
  261. .take(*n_i)
  262. .map(move |exp_2| exp_2 * exp_z)
  263. })
  264. .collect();
  265. let gs = s.iter().map(|s_i| minus_z - a * s_i);
  266. let hs = s_inv
  267. .zip(util::exp_iter(y.invert()))
  268. .zip(concat_z_and_2.iter())
  269. .map(|((s_i_inv, exp_y_inv), z_and_2)| z + exp_y_inv * (zz * z_and_2 - b * s_i_inv));
  270. let basepoint_scalar =
  271. w * (self.t_x - a * b) + c * (delta(&bit_lengths, &y, &z) - self.t_x);
  272. let value_commitment_scalars = util::exp_iter(z).take(m).map(|z_exp| c * zz * z_exp);
  273. let mega_check = RistrettoPoint::optional_multiscalar_mul(
  274. iter::once(Scalar::ONE)
  275. .chain(iter::once(x))
  276. .chain(iter::once(c * x))
  277. .chain(iter::once(c * x * x))
  278. .chain(iter::once(-self.e_blinding - c * self.t_x_blinding))
  279. .chain(iter::once(basepoint_scalar))
  280. .chain(x_sq.iter().cloned())
  281. .chain(x_inv_sq.iter().cloned())
  282. .chain(gs)
  283. .chain(hs)
  284. .chain(value_commitment_scalars),
  285. iter::once(self.A.decompress())
  286. .chain(iter::once(self.S.decompress()))
  287. .chain(iter::once(self.T_1.decompress()))
  288. .chain(iter::once(self.T_2.decompress()))
  289. .chain(iter::once(Some(*H)))
  290. .chain(iter::once(Some(G)))
  291. .chain(self.ipp_proof.L_vec.iter().map(|L| L.decompress()))
  292. .chain(self.ipp_proof.R_vec.iter().map(|R| R.decompress()))
  293. .chain(bp_gens.G(nm).map(|&x| Some(x)))
  294. .chain(bp_gens.H(nm).map(|&x| Some(x)))
  295. .chain(comms.iter().map(|V| Some(*V.get_point()))),
  296. )
  297. .ok_or(RangeProofVerificationError::MultiscalarMul)?;
  298. if mega_check.is_identity() {
  299. Ok(())
  300. } else {
  301. Err(RangeProofVerificationError::AlgebraicRelation)
  302. }
  303. }
  304. // Following the dalek rangeproof library signature for now. The exact method signature can be
  305. // changed.
  306. pub fn to_bytes(&self) -> Vec<u8> {
  307. let mut buf = Vec::with_capacity(7 * 32 + self.ipp_proof.serialized_size());
  308. buf.extend_from_slice(self.A.as_bytes());
  309. buf.extend_from_slice(self.S.as_bytes());
  310. buf.extend_from_slice(self.T_1.as_bytes());
  311. buf.extend_from_slice(self.T_2.as_bytes());
  312. buf.extend_from_slice(self.t_x.as_bytes());
  313. buf.extend_from_slice(self.t_x_blinding.as_bytes());
  314. buf.extend_from_slice(self.e_blinding.as_bytes());
  315. buf.extend_from_slice(&self.ipp_proof.to_bytes());
  316. buf
  317. }
  318. // Following the dalek rangeproof library signature for now. The exact method signature can be
  319. // changed.
  320. pub fn from_bytes(slice: &[u8]) -> Result<RangeProof, RangeProofVerificationError> {
  321. if !slice.len().is_multiple_of(32) {
  322. return Err(RangeProofVerificationError::Deserialization);
  323. }
  324. if slice.len() < 7 * 32 {
  325. return Err(RangeProofVerificationError::Deserialization);
  326. }
  327. let A = CompressedRistretto(util::read32(&slice[0..]));
  328. let S = CompressedRistretto(util::read32(&slice[32..]));
  329. let T_1 = CompressedRistretto(util::read32(&slice[2 * 32..]));
  330. let T_2 = CompressedRistretto(util::read32(&slice[3 * 32..]));
  331. let t_x = Scalar::from_canonical_bytes(util::read32(&slice[4 * 32..]))
  332. .into_option()
  333. .ok_or(RangeProofVerificationError::Deserialization)?;
  334. let t_x_blinding = Scalar::from_canonical_bytes(util::read32(&slice[5 * 32..]))
  335. .into_option()
  336. .ok_or(RangeProofVerificationError::Deserialization)?;
  337. let e_blinding = Scalar::from_canonical_bytes(util::read32(&slice[6 * 32..]))
  338. .into_option()
  339. .ok_or(RangeProofVerificationError::Deserialization)?;
  340. let ipp_proof = InnerProductProof::from_bytes(&slice[7 * 32..])?;
  341. Ok(RangeProof {
  342. A,
  343. S,
  344. T_1,
  345. T_2,
  346. t_x,
  347. t_x_blinding,
  348. e_blinding,
  349. ipp_proof,
  350. })
  351. }
  352. }
  353. /// Compute
  354. /// \\[
  355. /// \delta(y,z) = (z - z^{2}) \langle \mathbf{1}, {\mathbf{y}}^{n \cdot m} \rangle - \sum_{j=0}^{m-1} z^{j+3} \cdot \langle \mathbf{1}, {\mathbf{2}}^{n \cdot m} \rangle
  356. /// \\]
  357. #[cfg(not(target_os = "solana"))]
  358. fn delta(bit_lengths: &[usize], y: &Scalar, z: &Scalar) -> Scalar {
  359. let nm: usize = bit_lengths.iter().sum();
  360. let sum_y = util::sum_of_powers(y, nm);
  361. let mut agg_delta = (z - z * z) * sum_y;
  362. let mut exp_z = z * z * z;
  363. for n_i in bit_lengths.iter() {
  364. let sum_2 = util::sum_of_powers(&Scalar::from(2u64), *n_i);
  365. agg_delta -= exp_z * sum_2;
  366. exp_z *= z;
  367. }
  368. agg_delta
  369. }
  370. #[cfg(test)]
  371. mod tests {
  372. use super::*;
  373. #[test]
  374. fn test_single_rangeproof() {
  375. let (comm, open) = Pedersen::new(55_u64);
  376. let mut transcript_create = Transcript::new(b"Test");
  377. let mut transcript_verify = Transcript::new(b"Test");
  378. let proof =
  379. RangeProof::new(vec![55], vec![32], vec![&open], &mut transcript_create).unwrap();
  380. assert!(proof
  381. .verify(vec![&comm], vec![32], &mut transcript_verify)
  382. .is_ok());
  383. }
  384. #[test]
  385. fn test_aggregated_rangeproof() {
  386. let (comm_1, open_1) = Pedersen::new(55_u64);
  387. let (comm_2, open_2) = Pedersen::new(77_u64);
  388. let (comm_3, open_3) = Pedersen::new(99_u64);
  389. let mut transcript_create = Transcript::new(b"Test");
  390. let mut transcript_verify = Transcript::new(b"Test");
  391. let proof = RangeProof::new(
  392. vec![55, 77, 99],
  393. vec![64, 32, 32],
  394. vec![&open_1, &open_2, &open_3],
  395. &mut transcript_create,
  396. )
  397. .unwrap();
  398. assert!(proof
  399. .verify(
  400. vec![&comm_1, &comm_2, &comm_3],
  401. vec![64, 32, 32],
  402. &mut transcript_verify,
  403. )
  404. .is_ok());
  405. }
  406. // TODO: write test for serialization/deserialization
  407. }