You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

233 lines
6.9 KiB

  1. //! Benchmarks Nova's prover for proving SHA-256 with varying sized messages.
  2. //! We run a single step with the step performing the entire computation.
  3. //! This code invokes a hand-written SHA-256 gadget from bellman/bellperson.
  4. //! It also uses code from bellman/bellperson to compare circuit-generated digest with sha2 crate's output
  5. #![allow(non_snake_case)]
  6. type G1 = pasta_curves::pallas::Point;
  7. type G2 = pasta_curves::vesta::Point;
  8. use ::bellperson::{
  9. gadgets::{
  10. boolean::{AllocatedBit, Boolean},
  11. num::{AllocatedNum, Num},
  12. sha256::sha256,
  13. Assignment,
  14. },
  15. ConstraintSystem, SynthesisError,
  16. };
  17. use core::time::Duration;
  18. use criterion::*;
  19. use ff::{PrimeField, PrimeFieldBits};
  20. use nova_snark::{
  21. traits::{
  22. circuit::{StepCircuit, TrivialTestCircuit},
  23. Group,
  24. },
  25. PublicParams, RecursiveSNARK,
  26. };
  27. use sha2::{Digest, Sha256};
  28. #[derive(Clone, Debug)]
  29. struct Sha256Circuit<Scalar: PrimeField> {
  30. preimage: Vec<u8>,
  31. digest: Scalar,
  32. }
  33. impl<Scalar: PrimeField + PrimeFieldBits> StepCircuit<Scalar> for Sha256Circuit<Scalar> {
  34. fn arity(&self) -> usize {
  35. 1
  36. }
  37. fn synthesize<CS: ConstraintSystem<Scalar>>(
  38. &self,
  39. cs: &mut CS,
  40. _z: &[AllocatedNum<Scalar>],
  41. ) -> Result<Vec<AllocatedNum<Scalar>>, SynthesisError> {
  42. let mut z_out: Vec<AllocatedNum<Scalar>> = Vec::new();
  43. let bit_values: Vec<_> = self
  44. .preimage
  45. .clone()
  46. .into_iter()
  47. .flat_map(|byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8))
  48. .map(Some)
  49. .collect();
  50. assert_eq!(bit_values.len(), self.preimage.len() * 8);
  51. let preimage_bits = bit_values
  52. .into_iter()
  53. .enumerate()
  54. .map(|(i, b)| AllocatedBit::alloc(cs.namespace(|| format!("preimage bit {i}")), b))
  55. .map(|b| b.map(Boolean::from))
  56. .collect::<Result<Vec<_>, _>>()?;
  57. let hash_bits = sha256(cs.namespace(|| "sha256"), &preimage_bits)?;
  58. for (i, hash_bits) in hash_bits.chunks(256_usize).enumerate() {
  59. let mut num = Num::<Scalar>::zero();
  60. let mut coeff = Scalar::ONE;
  61. for bit in hash_bits {
  62. num = num.add_bool_with_coeff(CS::one(), bit, coeff);
  63. coeff = coeff.double();
  64. }
  65. let hash = AllocatedNum::alloc(cs.namespace(|| format!("input {i}")), || {
  66. Ok(*num.get_value().get()?)
  67. })?;
  68. // num * 1 = hash
  69. cs.enforce(
  70. || format!("packing constraint {i}"),
  71. |_| num.lc(Scalar::ONE),
  72. |lc| lc + CS::one(),
  73. |lc| lc + hash.get_variable(),
  74. );
  75. z_out.push(hash);
  76. }
  77. // sanity check with the hasher
  78. let mut hasher = Sha256::new();
  79. hasher.update(&self.preimage);
  80. let hash_result = hasher.finalize();
  81. let mut s = hash_result
  82. .iter()
  83. .flat_map(|&byte| (0..8).rev().map(move |i| (byte >> i) & 1u8 == 1u8));
  84. for b in hash_bits {
  85. match b {
  86. Boolean::Is(b) => {
  87. assert!(s.next().unwrap() == b.get_value().unwrap());
  88. }
  89. Boolean::Not(b) => {
  90. assert!(s.next().unwrap() != b.get_value().unwrap());
  91. }
  92. Boolean::Constant(_b) => {
  93. panic!("Can't reach here")
  94. }
  95. }
  96. }
  97. Ok(z_out)
  98. }
  99. fn output(&self, _z: &[Scalar]) -> Vec<Scalar> {
  100. vec![self.digest]
  101. }
  102. }
  103. type C1 = Sha256Circuit<<G1 as Group>::Scalar>;
  104. type C2 = TrivialTestCircuit<<G2 as Group>::Scalar>;
  105. criterion_group! {
  106. name = recursive_snark;
  107. config = Criterion::default().warm_up_time(Duration::from_millis(3000));
  108. targets = bench_recursive_snark
  109. }
  110. criterion_main!(recursive_snark);
  111. fn bench_recursive_snark(c: &mut Criterion) {
  112. let bytes_to_scalar = |bytes: [u8; 32]| -> <G1 as Group>::Scalar {
  113. let mut bytes_le = bytes;
  114. bytes_le.reverse();
  115. <G1 as Group>::Scalar::from_repr(bytes_le).unwrap()
  116. };
  117. let decode_hex = |s: &str| -> <G1 as Group>::Scalar {
  118. let bytes = (0..s.len())
  119. .step_by(2)
  120. .map(|i| u8::from_str_radix(&s[i..i + 2], 16))
  121. .collect::<Result<Vec<u8>, _>>()
  122. .unwrap();
  123. let bytes_arr: [u8; 32] = bytes.try_into().unwrap();
  124. bytes_to_scalar(bytes_arr)
  125. };
  126. // Test vectors
  127. let circuits = vec![
  128. Sha256Circuit {
  129. preimage: vec![0u8; 64],
  130. digest: decode_hex("12df9ae4958c1957170f9b04c4bc00c27315c5d75a391f4b672f952842bfa5ac"),
  131. },
  132. Sha256Circuit {
  133. preimage: vec![0u8; 128],
  134. digest: decode_hex("13abfac9782cb9c13c4508bde596f1914fe2f744f6a661c0c9a16659745c4e1b"),
  135. },
  136. Sha256Circuit {
  137. preimage: vec![0u8; 256],
  138. digest: decode_hex("0f5a007b5aef126a58f9bbd937842967c44253e7f97d98b5cd10bfe44d6782c8"),
  139. },
  140. Sha256Circuit {
  141. preimage: vec![0u8; 512],
  142. digest: decode_hex("06a6cfaad91d49366f18443cd4e11576ff27c174bb9fe2bc54735a79e3e456e0"),
  143. },
  144. Sha256Circuit {
  145. preimage: vec![0u8; 1024],
  146. digest: decode_hex("3763c73508f5fbb36daae8257d6c5c07db08ec5df0549ccf692b9fa218fd0ef7"),
  147. },
  148. Sha256Circuit {
  149. preimage: vec![0u8; 2048],
  150. digest: decode_hex("35c18d6c3cf49e42b3ffcb54ea04bdc16617efba0e673abc8c858257955005a5"),
  151. },
  152. Sha256Circuit {
  153. preimage: vec![0u8; 4096],
  154. digest: decode_hex("25349112d1bd5ba15e3e2d3effa01af1da02c097ce6208cdf28f34b74d35feb2"),
  155. },
  156. Sha256Circuit {
  157. preimage: vec![0u8; 8192],
  158. digest: decode_hex("22bc891155c7d423039a2206ed4a5342755948baeb13a54b61dbead7c3d3b8f6"),
  159. },
  160. Sha256Circuit {
  161. preimage: vec![0u8; 16384],
  162. digest: decode_hex("3fda713dc72ddcd42ce625c75f7e41d526d30647278a3dfcda95904e59ade7f1"),
  163. },
  164. Sha256Circuit {
  165. preimage: vec![0u8; 32768],
  166. digest: decode_hex("1e2091bd3e3cedffebb7316b52414fff82511cbd232561874a4ae11ae2040ac1"),
  167. },
  168. Sha256Circuit {
  169. preimage: vec![0u8; 65536],
  170. digest: decode_hex("0c33953975c438ce357912f27b0fbcf98bae6eb68a1a913386672ee406a4f479"),
  171. },
  172. ];
  173. for circuit_primary in circuits {
  174. let mut group = c.benchmark_group(format!(
  175. "NovaProve-Sha256-message-len-{}",
  176. circuit_primary.preimage.len()
  177. ));
  178. group.sample_size(10);
  179. // Produce public parameters
  180. let pp =
  181. PublicParams::<G1, G2, C1, C2>::setup(circuit_primary.clone(), TrivialTestCircuit::default());
  182. let circuit_secondary = TrivialTestCircuit::default();
  183. let z0_primary = vec![<G1 as Group>::Scalar::from(2u64)];
  184. let z0_secondary = vec![<G2 as Group>::Scalar::from(2u64)];
  185. group.bench_function("Prove", |b| {
  186. b.iter(|| {
  187. let mut recursive_snark = RecursiveSNARK::new(
  188. black_box(&pp),
  189. black_box(&circuit_primary),
  190. black_box(&circuit_secondary),
  191. black_box(z0_primary.clone()),
  192. black_box(z0_secondary.clone()),
  193. );
  194. // produce a recursive SNARK for a step of the recursion
  195. assert!(recursive_snark
  196. .prove_step(
  197. black_box(&pp),
  198. black_box(&circuit_primary),
  199. black_box(&circuit_secondary),
  200. black_box(z0_primary.clone()),
  201. black_box(z0_secondary.clone()),
  202. )
  203. .is_ok());
  204. })
  205. });
  206. group.finish();
  207. }
  208. }