circuit.rs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2026 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use darkfi::zk::assign_free_advice;
  19. use darkfi_sdk::{
  20. crypto::{
  21. constants::{
  22. sinsemilla::{OrchardCommitDomains, OrchardHashDomains},
  23. OrchardFixedBases,
  24. },
  25. pasta_prelude::Curve,
  26. },
  27. pasta::pallas,
  28. };
  29. use halo2_gadgets::{
  30. ecc::{
  31. chip::{EccChip, EccConfig},
  32. NonIdentityPoint, ScalarVar,
  33. },
  34. sinsemilla::chip::{SinsemillaChip, SinsemillaConfig},
  35. utilities::lookup_range_check::LookupRangeCheckConfig,
  36. };
  37. use halo2_proofs::{
  38. circuit::{floor_planner, Layouter, Value},
  39. plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Instance as InstanceColumn},
  40. };
  41. #[derive(Clone, Debug)]
  42. pub struct EcipConfig {
  43. primary: Column<InstanceColumn>,
  44. advices: [Column<Advice>; 10],
  45. ecc_config: EccConfig<OrchardFixedBases>,
  46. sinsemilla_config:
  47. SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
  48. }
  49. impl EcipConfig {
  50. fn ecc_chip(&self) -> EccChip<OrchardFixedBases> {
  51. EccChip::construct(self.ecc_config.clone())
  52. }
  53. }
  54. #[derive(Default, Debug)]
  55. pub struct EcipCircuit {
  56. pub g1: Value<pallas::Point>,
  57. pub s1: Value<pallas::Base>,
  58. }
  59. impl Circuit<pallas::Base> for EcipCircuit {
  60. type Config = EcipConfig;
  61. type FloorPlanner = floor_planner::V1;
  62. fn without_witnesses(&self) -> Self {
  63. Self::default()
  64. }
  65. fn configure(meta: &mut ConstraintSystem<pallas::Base>) -> Self::Config {
  66. // Advice columns used in the circuit
  67. let advices = [
  68. meta.advice_column(),
  69. meta.advice_column(),
  70. meta.advice_column(),
  71. meta.advice_column(),
  72. meta.advice_column(),
  73. meta.advice_column(),
  74. meta.advice_column(),
  75. meta.advice_column(),
  76. meta.advice_column(),
  77. meta.advice_column(),
  78. ];
  79. // Fixed columns for the Sinsemilla generator lookup table
  80. let table_idx = meta.lookup_table_column();
  81. let lookup = (table_idx, meta.lookup_table_column(), meta.lookup_table_column());
  82. // Instance column used for public inputs
  83. let primary = meta.instance_column();
  84. meta.enable_equality(primary);
  85. // Permutation over all advice columns
  86. for advice in advices.iter() {
  87. meta.enable_equality(*advice);
  88. }
  89. // Fixed columns for the ECC chip
  90. let lagrange_coeffs = [
  91. meta.fixed_column(),
  92. meta.fixed_column(),
  93. meta.fixed_column(),
  94. meta.fixed_column(),
  95. meta.fixed_column(),
  96. meta.fixed_column(),
  97. meta.fixed_column(),
  98. meta.fixed_column(),
  99. ];
  100. // Use the first Lagrange coefficient column for loading global constants.
  101. meta.enable_constant(lagrange_coeffs[0]);
  102. // Use one of the right-most advice columns for all of our range checks.
  103. let range_check = LookupRangeCheckConfig::configure(meta, advices[9], table_idx);
  104. // Sinsemilla configuration, used for the lookup table
  105. let sinsemilla_config = SinsemillaChip::configure(
  106. meta,
  107. advices[..5].try_into().unwrap(),
  108. advices[6],
  109. lagrange_coeffs[0],
  110. lookup,
  111. range_check,
  112. );
  113. // Configuration for curve point operations.
  114. // This uses 10 advice columns and spans the whole circuit.
  115. let ecc_config =
  116. EccChip::<OrchardFixedBases>::configure(meta, advices, lagrange_coeffs, range_check);
  117. EcipConfig { primary, advices, ecc_config, sinsemilla_config }
  118. }
  119. fn synthesize(
  120. &self,
  121. config: Self::Config,
  122. mut layouter: impl Layouter<pallas::Base>,
  123. ) -> Result<(), Error> {
  124. // Load the Sinsemilla generator lookup table used by the whole circuit
  125. SinsemillaChip::load(config.sinsemilla_config.clone(), &mut layouter)?;
  126. let g1 = NonIdentityPoint::new(
  127. config.ecc_chip(),
  128. layouter.namespace(|| "Witness g1"),
  129. self.g1.as_ref().map(|cm| cm.to_affine()),
  130. )?;
  131. let s1 =
  132. assign_free_advice(layouter.namespace(|| "Witness s1"), config.advices[0], self.s1)?;
  133. let s1 =
  134. ScalarVar::from_base(config.ecc_chip(), layouter.namespace(|| "fp_mod_fv(s1)"), &s1)?;
  135. let (r, _) = g1.mul(layouter.namespace(|| "g1 * s1"), s1)?;
  136. let r_x = r.inner().x();
  137. let r_y = r.inner().y();
  138. layouter.constrain_instance(r_x.cell(), config.primary, 0)?;
  139. layouter.constrain_instance(r_y.cell(), config.primary, 1)?;
  140. Ok(())
  141. }
  142. }
  143. #[cfg(test)]
  144. mod tests {
  145. use super::*;
  146. use darkfi_sdk::crypto::{pasta_prelude::Group, util::fp_mod_fv};
  147. use halo2_proofs::{
  148. arithmetic::{CurveAffine, Field},
  149. dev::MockProver,
  150. };
  151. use rand::rngs::OsRng;
  152. #[test]
  153. fn test_circuit() {
  154. let k = 11;
  155. let g1 = pallas::Point::random(&mut OsRng);
  156. let s1 = pallas::Base::random(&mut OsRng);
  157. let circuit = EcipCircuit { g1: Value::known(g1), s1: Value::known(s1) };
  158. let g1s1 = g1 * fp_mod_fv(s1);
  159. let g1s1_coords = g1s1.to_affine().coordinates().unwrap();
  160. let public_inputs = vec![*g1s1_coords.x(), *g1s1_coords.y()];
  161. let prover = MockProver::run(k, &circuit, vec![public_inputs]).unwrap();
  162. prover.assert_satisfied();
  163. }
  164. }