Browse Source

examples/halo2: Add circuit skeleton for anonymous voting.

parazyd 4 năm trước cách đây
mục cha
commit
391d38dd48
1 tập tin đã thay đổi với 227 bổ sung2 xóa
  1. 227 2
      example/halo2/src/bin/anonvote.rs

+ 227 - 2
example/halo2/src/bin/anonvote.rs

@@ -1,8 +1,19 @@
 // https://docs.vocdoni.io/architecture/protocol/anonymous-voting/zk-census-proof.html#protocol-design
 // https://docs.vocdoni.io/architecture/protocol/anonymous-voting/zk-census-proof.html#protocol-design
 use anyhow::Result;
 use anyhow::Result;
+use halo2::{
+    circuit::{Layouter, SimpleFloorPlanner},
+    plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Instance as InstanceColumn},
+};
 use halo2_gadgets::{
 use halo2_gadgets::{
+    ecc::chip::{EccChip, EccConfig},
+    poseidon::{Pow5T3Chip as PoseidonChip, Pow5T3Config as PoseidonConfig},
     primitives,
     primitives,
     primitives::poseidon::{ConstantLength, P128Pow5T3},
     primitives::poseidon::{ConstantLength, P128Pow5T3},
+    sinsemilla::{
+        chip::{SinsemillaChip, SinsemillaConfig},
+        merkle::chip::{MerkleChip, MerkleConfig},
+    },
+    utilities::{lookup_range_check::LookupRangeCheckConfig, CellValue, UtilitiesInstructions},
 };
 };
 use pasta_curves::{
 use pasta_curves::{
     arithmetic::{CurveAffine, Field},
     arithmetic::{CurveAffine, Field},
@@ -11,8 +22,222 @@ use pasta_curves::{
 };
 };
 use rand::rngs::OsRng;
 use rand::rngs::OsRng;
 
 
-use drk_halo2::crypto::pedersen_commitment;
-use drk_halo2::{constants::sinsemilla::MERKLE_CRH_PERSONALIZATION, spec::i2lebsp};
+use drk_halo2::{
+    constants::{
+        sinsemilla::{OrchardCommitDomains, OrchardHashDomains, MERKLE_CRH_PERSONALIZATION},
+        OrchardFixedBases,
+    },
+    crypto::pedersen_commitment,
+    spec::i2lebsp,
+};
+
+#[derive(Clone, Debug)]
+struct VoteConfig {
+    primary: Column<InstanceColumn>,
+    advices: [Column<Advice>; 10],
+    ecc_config: EccConfig,
+    merkle_config_1: MerkleConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
+    merkle_config_2: MerkleConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
+    sinsemilla_config_1:
+        SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
+    sinsemilla_config_2:
+        SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
+    poseidon_config: PoseidonConfig<pallas::Base>,
+}
+
+impl VoteConfig {
+    fn ecc_chip(&self) -> EccChip<OrchardFixedBases> {
+        EccChip::construct(self.ecc_config.clone())
+    }
+
+    fn sinsemilla_chip_1(
+        &self,
+    ) -> SinsemillaChip<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases> {
+        SinsemillaChip::construct(self.sinsemilla_config_1.clone())
+    }
+
+    fn sinsemilla_chip_2(
+        &self,
+    ) -> SinsemillaChip<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases> {
+        SinsemillaChip::construct(self.sinsemilla_config_2.clone())
+    }
+
+    fn merkle_chip_1(
+        &self,
+    ) -> MerkleChip<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases> {
+        MerkleChip::construct(self.merkle_config_1.clone())
+    }
+
+    fn merkle_chip_2(
+        &self,
+    ) -> MerkleChip<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases> {
+        MerkleChip::construct(self.merkle_config_2.clone())
+    }
+
+    fn poseidon_chip(&self) -> PoseidonChip<pallas::Base> {
+        PoseidonChip::construct(self.poseidon_config.clone())
+    }
+}
+
+// The public input array offsets
+const VOTE_MERKLE_ROOT_OFFSET: usize = 0;
+const VOTE_NULLIFIER_OFFSET: usize = 1;
+const VOTE_PROCESS_ID_OFFSET: usize = 2;
+const VOTE_COMMITX_OFFSET: usize = 3;
+const VOTE_COMMITY_OFFSET: usize = 4;
+
+#[derive(Default, Debug)]
+struct VoteCircuit {
+    index: Option<pallas::Base>,
+    secret_key: Option<pallas::Base>,
+    merkle_proof: Option<pallas::Base>,
+}
+
+impl UtilitiesInstructions<pallas::Base> for VoteCircuit {
+    type Var = CellValue<pallas::Base>;
+}
+
+impl Circuit<pallas::Base> for VoteCircuit {
+    type Config = VoteConfig;
+    type FloorPlanner = SimpleFloorPlanner;
+
+    fn without_witnesses(&self) -> Self {
+        Self::default()
+    }
+
+    fn configure(meta: &mut ConstraintSystem<pallas::Base>) -> Self::Config {
+        // Advice columns used in the circuit
+        let advices = [
+            meta.advice_column(),
+            meta.advice_column(),
+            meta.advice_column(),
+            meta.advice_column(),
+            meta.advice_column(),
+            meta.advice_column(),
+            meta.advice_column(),
+            meta.advice_column(),
+            meta.advice_column(),
+            meta.advice_column(),
+        ];
+
+        // Fixed columns for the Sinsemilla generator lookup table
+        let table_idx = meta.lookup_table_column();
+        let lookup = (
+            table_idx,
+            meta.lookup_table_column(),
+            meta.lookup_table_column(),
+        );
+
+        // Instance column used for public inputs
+        let primary = meta.instance_column();
+        meta.enable_equality(primary.into());
+
+        // Permutation over all advice columns
+        for advice in advices.iter() {
+            meta.enable_equality((*advice).into());
+        }
+
+        // Poseidon requires four advice columns, while ECC incomplete addition
+        // requires six. We can reduce the proof size by sharing fixed columns
+        // between the ECC and Poseidon chips.
+        // TODO: For multiple invocations they could/should be configured in
+        // parallel rather than sharing perhaps?
+        let lagrange_coeffs = [
+            meta.fixed_column(),
+            meta.fixed_column(),
+            meta.fixed_column(),
+            meta.fixed_column(),
+            meta.fixed_column(),
+            meta.fixed_column(),
+            meta.fixed_column(),
+            meta.fixed_column(),
+        ];
+        let rc_a = lagrange_coeffs[2..5].try_into().unwrap();
+        let rc_b = lagrange_coeffs[5..8].try_into().unwrap();
+
+        // Also use the first Lagrange coefficient column for loading global constants.
+        meta.enable_constant(lagrange_coeffs[0]);
+
+        // Use one of the right-most advice columns for all of our range checks.
+        let range_check = LookupRangeCheckConfig::configure(meta, advices[9], table_idx);
+
+        // Configuration for curve point operations.
+        // This uses 10 advice columns and spans the whole circuit.
+        let ecc_config = EccChip::<OrchardFixedBases>::configure(
+            meta,
+            advices,
+            lagrange_coeffs,
+            range_check.clone(),
+        );
+
+        // Configuration for the Poseidon hash
+        let poseidon_config = PoseidonChip::configure(
+            meta,
+            P128Pow5T3,
+            advices[6..9].try_into().unwrap(),
+            advices[5],
+            rc_a,
+            rc_b,
+        );
+
+        // Configuration for a Sinsemilla hash instantiation and a
+        // Merkle hash instantiation using this Sinsemilla instance.
+        // Since the Sinsemilla config uses only 5 advice columns,
+        // we can fit two instances side-by-side.
+        let (sinsemilla_config_1, merkle_config_1) = {
+            let sinsemilla_config_1 = SinsemillaChip::configure(
+                meta,
+                advices[..5].try_into().unwrap(),
+                advices[6],
+                lagrange_coeffs[0],
+                lookup,
+                range_check.clone(),
+            );
+            let merkle_config_1 = MerkleChip::configure(meta, sinsemilla_config_1.clone());
+            (sinsemilla_config_1, merkle_config_1)
+        };
+
+        // Configuration for a Sinsemilla hash instantiation and a
+        // Merkle hash instantiation using this Sinsemilla instance.
+        // Since the Sinsemilla config uses only 5 advice columns,
+        // we can fit two instances side-by-side.
+        let (sinsemilla_config_2, merkle_config_2) = {
+            let sinsemilla_config_2 = SinsemillaChip::configure(
+                meta,
+                advices[5..].try_into().unwrap(),
+                advices[7],
+                lagrange_coeffs[1],
+                lookup,
+                range_check,
+            );
+            let merkle_config_2 = MerkleChip::configure(meta, sinsemilla_config_2.clone());
+
+            (sinsemilla_config_2, merkle_config_2)
+        };
+
+        VoteConfig {
+            primary,
+            advices,
+            ecc_config,
+            merkle_config_1,
+            merkle_config_2,
+            sinsemilla_config_1,
+            sinsemilla_config_2,
+            poseidon_config,
+        }
+    }
+
+    fn synthesize(
+        &self,
+        config: Self::Config,
+        mut layouter: impl Layouter<pallas::Base>,
+    ) -> Result<(), Error> {
+        // Load the Sinsemilla generator lookup table used by the whole circuit.
+        SinsemillaChip::load(config.sinsemilla_config_1.clone(), &mut layouter)?;
+
+        Ok(())
+    }
+}
 
 
 fn root(path: [pallas::Base; 32], leaf_pos: u32, leaf: pallas::Base) -> pallas::Base {
 fn root(path: [pallas::Base; 32], leaf_pos: u32, leaf: pallas::Base) -> pallas::Base {
     let domain = primitives::sinsemilla::HashDomain::new(MERKLE_CRH_PERSONALIZATION);
     let domain = primitives::sinsemilla::HashDomain::new(MERKLE_CRH_PERSONALIZATION);