circuit.rs 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. use darkfi::zk::assign_free_advice;
  2. use darkfi_sdk::{
  3. crypto::{
  4. constants::{
  5. sinsemilla::{OrchardCommitDomains, OrchardHashDomains},
  6. OrchardFixedBases,
  7. },
  8. pasta_prelude::Curve,
  9. },
  10. pasta::pallas,
  11. };
  12. use halo2_gadgets::{
  13. ecc::{
  14. chip::{EccChip, EccConfig},
  15. NonIdentityPoint, ScalarVar,
  16. },
  17. sinsemilla::chip::{SinsemillaChip, SinsemillaConfig},
  18. utilities::lookup_range_check::LookupRangeCheckConfig,
  19. };
  20. use halo2_proofs::{
  21. circuit::{floor_planner, Layouter, Value},
  22. plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Instance as InstanceColumn},
  23. };
  24. #[derive(Clone, Debug)]
  25. pub struct EcipConfig {
  26. primary: Column<InstanceColumn>,
  27. advices: [Column<Advice>; 10],
  28. ecc_config: EccConfig<OrchardFixedBases>,
  29. sinsemilla_config:
  30. SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
  31. }
  32. impl EcipConfig {
  33. fn ecc_chip(&self) -> EccChip<OrchardFixedBases> {
  34. EccChip::construct(self.ecc_config.clone())
  35. }
  36. }
  37. #[derive(Default, Debug)]
  38. pub struct EcipCircuit {
  39. pub g1: Value<pallas::Point>,
  40. pub s1: Value<pallas::Base>,
  41. }
  42. impl Circuit<pallas::Base> for EcipCircuit {
  43. type Config = EcipConfig;
  44. type FloorPlanner = floor_planner::V1;
  45. fn without_witnesses(&self) -> Self {
  46. Self::default()
  47. }
  48. fn configure(meta: &mut ConstraintSystem<pallas::Base>) -> Self::Config {
  49. // Advice columns used in the circuit
  50. let advices = [
  51. meta.advice_column(),
  52. meta.advice_column(),
  53. meta.advice_column(),
  54. meta.advice_column(),
  55. meta.advice_column(),
  56. meta.advice_column(),
  57. meta.advice_column(),
  58. meta.advice_column(),
  59. meta.advice_column(),
  60. meta.advice_column(),
  61. ];
  62. // Fixed columns for the Sinsemilla generator lookup table
  63. let table_idx = meta.lookup_table_column();
  64. let lookup = (table_idx, meta.lookup_table_column(), meta.lookup_table_column());
  65. // Instance column used for public inputs
  66. let primary = meta.instance_column();
  67. meta.enable_equality(primary);
  68. // Permutation over all advice columns
  69. for advice in advices.iter() {
  70. meta.enable_equality(*advice);
  71. }
  72. // Fixed columns for the ECC chip
  73. let lagrange_coeffs = [
  74. meta.fixed_column(),
  75. meta.fixed_column(),
  76. meta.fixed_column(),
  77. meta.fixed_column(),
  78. meta.fixed_column(),
  79. meta.fixed_column(),
  80. meta.fixed_column(),
  81. meta.fixed_column(),
  82. ];
  83. // Use the first Lagrange coefficient column for loading global constants.
  84. meta.enable_constant(lagrange_coeffs[0]);
  85. // Use one of the right-most advice columns for all of our range checks.
  86. let range_check = LookupRangeCheckConfig::configure(meta, advices[9], table_idx);
  87. // Sinsemilla configuration, used for the lookup table
  88. let sinsemilla_config = SinsemillaChip::configure(
  89. meta,
  90. advices[..5].try_into().unwrap(),
  91. advices[6],
  92. lagrange_coeffs[0],
  93. lookup,
  94. range_check,
  95. );
  96. // Configuration for curve point operations.
  97. // This uses 10 advice columns and spans the whole circuit.
  98. let ecc_config =
  99. EccChip::<OrchardFixedBases>::configure(meta, advices, lagrange_coeffs, range_check);
  100. EcipConfig { primary, advices, ecc_config, sinsemilla_config }
  101. }
  102. fn synthesize(
  103. &self,
  104. config: Self::Config,
  105. mut layouter: impl Layouter<pallas::Base>,
  106. ) -> Result<(), Error> {
  107. // Load the Sinsemilla generator lookup table used by the whole circuit
  108. SinsemillaChip::load(config.sinsemilla_config.clone(), &mut layouter)?;
  109. let g1 = NonIdentityPoint::new(
  110. config.ecc_chip(),
  111. layouter.namespace(|| "Witness g1"),
  112. self.g1.as_ref().map(|cm| cm.to_affine()),
  113. )?;
  114. let s1 =
  115. assign_free_advice(layouter.namespace(|| "Witness s1"), config.advices[0], self.s1)?;
  116. let s1 =
  117. ScalarVar::from_base(config.ecc_chip(), layouter.namespace(|| "mod_r_p(s1)"), &s1)?;
  118. let (r, _) = g1.mul(layouter.namespace(|| "g1 * s1"), s1)?;
  119. let r_x = r.inner().x();
  120. let r_y = r.inner().y();
  121. layouter.constrain_instance(r_x.cell(), config.primary, 0)?;
  122. layouter.constrain_instance(r_y.cell(), config.primary, 1)?;
  123. Ok(())
  124. }
  125. }
  126. #[cfg(test)]
  127. mod tests {
  128. use super::*;
  129. use darkfi_sdk::crypto::{pasta_prelude::Group, util::mod_r_p};
  130. use halo2_proofs::{
  131. arithmetic::{CurveAffine, Field},
  132. dev::MockProver,
  133. };
  134. use rand::rngs::OsRng;
  135. #[test]
  136. fn test_circuit() {
  137. let k = 11;
  138. let g1 = pallas::Point::random(&mut OsRng);
  139. let s1 = pallas::Base::random(&mut OsRng);
  140. let circuit = EcipCircuit { g1: Value::known(g1), s1: Value::known(s1) };
  141. let g1s1 = g1 * mod_r_p(s1);
  142. let g1s1_coords = g1s1.to_affine().coordinates().unwrap();
  143. let public_inputs = vec![*g1s1_coords.x(), *g1s1_coords.y()];
  144. let prover = MockProver::run(k, &circuit, vec![public_inputs]).unwrap();
  145. prover.assert_satisfied();
  146. }
  147. }