Quellcode durchsuchen

[zk/circuit/lead_contract] integrate optimized lead circuit

mohab metwally vor 3 Jahren
Ursprung
Commit
61363b450f

+ 1 - 1
example/crypsinous.rs

@@ -179,7 +179,7 @@ async fn start(args: NetCli, ex: Arc<Executor<'_>>) -> Result<()> {
     let epoch_consensus = EpochConsensus::new(Some(slots), Some(epochs), Some(ticks), Some(reward));
     let epoch_consensus = EpochConsensus::new(Some(slots), Some(epochs), Some(ticks), Some(reward));
 
 
     //proof's number of rows
     //proof's number of rows
-    let k: u32 = 13;
+    let k: u32 = 11;
     let path = args.path.clone();
     let path = args.path.clone();
     let id = Timestamp::current_time().0;
     let id = Timestamp::current_time().0;
 
 

+ 8 - 5
src/consensus/coins.rs

@@ -221,6 +221,7 @@ fn create_leadcoin(
     //random commitment blinding values
     //random commitment blinding values
     let mut rng = thread_rng();
     let mut rng = thread_rng();
     let one = pallas::Base::one();
     let one = pallas::Base::one();
+    let zero = pallas::Base::zero();
     let c_cm1_blind: DrkValueBlind = pallas::Scalar::random(&mut rng);
     let c_cm1_blind: DrkValueBlind = pallas::Scalar::random(&mut rng);
     let c_cm2_blind: DrkValueBlind = pallas::Scalar::random(&mut rng);
     let c_cm2_blind: DrkValueBlind = pallas::Scalar::random(&mut rng);
 
 
@@ -240,9 +241,9 @@ fn create_leadcoin(
     let c_pk_y = c_pk_coord.y();
     let c_pk_y = c_pk_coord.y();
 
 
     let c_seed = pallas::Base::from(seed);
     let c_seed = pallas::Base::from(seed);
-    let sn_msg = [c_seed, c_root_sk.inner()];
+    let sn_msg = [c_seed, c_root_sk.inner(), zero.clone(), one.clone()];
     let c_sn: pallas::Base =
     let c_sn: pallas::Base =
-        poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<2>, 3, 2>::init()
+        poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<4>, 3, 2>::init()
             .hash(sn_msg);
             .hash(sn_msg);
 
 
     let coin_commit_msg_input =
     let coin_commit_msg_input =
@@ -252,7 +253,9 @@ fn create_leadcoin(
             .hash(coin_commit_msg_input);
             .hash(coin_commit_msg_input);
     let c_cm: pallas::Point = pedersen_commitment_base(coin_commit_msg, c_cm1_blind);
     let c_cm: pallas::Point = pedersen_commitment_base(coin_commit_msg, c_cm1_blind);
     let c_cm_coordinates = c_cm.to_affine().coordinates().unwrap();
     let c_cm_coordinates = c_cm.to_affine().coordinates().unwrap();
-    let c_cm_base: pallas::Base = c_cm_coordinates.x() * c_cm_coordinates.y();
+    let c_cm_msg = [*c_cm_coordinates.x(), *c_cm_coordinates.y()];
+    let c_cm_base: pallas::Base =
+        poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<2>, 3, 2>::init().hash(c_cm_msg);
     let c_cm_node = MerkleNode::from(c_cm_base);
     let c_cm_node = MerkleNode::from(c_cm_base);
     tree_cm.append(&c_cm_node.clone());
     tree_cm.append(&c_cm_node.clone());
     let leaf_position = tree_cm.witness();
     let leaf_position = tree_cm.witness();
@@ -274,9 +277,9 @@ fn create_leadcoin(
     };
     };
     */
     */
 
 
-    let coin_nonce2_msg = [c_seed, c_root_sk.inner()];
+    let coin_nonce2_msg = [c_seed, c_root_sk.inner(), one.clone(), one.clone()];
     let c_seed2: pallas::Base =
     let c_seed2: pallas::Base =
-        poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<2>, 3, 2>::init()
+        poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<4>, 3, 2>::init()
             .hash(coin_nonce2_msg);
             .hash(coin_nonce2_msg);
 
 
     let coin2_commit_msg_input =
     let coin2_commit_msg_input =

+ 1 - 1
src/consensus/mod.rs

@@ -92,7 +92,7 @@ lazy_static! {
     pub static ref DELTA: u64 = 20;
     pub static ref DELTA: u64 = 20;
 
 
     /// Leader proof rows number
     /// Leader proof rows number
-    pub static ref LEADER_PROOF_K: u32 = 13;
+    pub static ref LEADER_PROOF_K: u32 = 11;
 
 
     // TODO: Describe constants meaning in comment
     // TODO: Describe constants meaning in comment
     pub static ref RADIX_BITS: usize = 76;
     pub static ref RADIX_BITS: usize = 76;

+ 10 - 12
src/crypto/leadcoin.rs

@@ -96,23 +96,21 @@ impl LeadCoin {
         //
         //
         let rho_pt: pallas::Point = pedersen_commitment_base(lottery_msg, mod_r_p(rho_mu));
         let rho_pt: pallas::Point = pedersen_commitment_base(lottery_msg, mod_r_p(rho_mu));
         LeadContract {
         LeadContract {
-            path: Value::known(self.path.unwrap()),
-            sk: Value::known(self.keypair.unwrap().secret.inner()),
-            root_sk: Value::known(self.root_sk.unwrap()),
-            path_sk: Value::known(self.path_sk.unwrap()),
-            coin_timestamp: Value::known(self.tau.unwrap()), //
-            coin_nonce: Value::known(self.nonce.unwrap()),
+            coin1_commit_merkle_path: Value::known(self.path.unwrap()),
+            coin1_commit_root: Value::known(self.root_cm.unwrap()),
+            coin1_commit_leaf_pos: Value::known(self.idx),
+            coin1_sk: Value::known(self.keypair.unwrap().secret.inner()),
+            coin1_sk_root: Value::known(self.root_sk.unwrap()),
+            coin1_sk_merkle_path: Value::known(self.path_sk.unwrap()),
+            coin1_timestamp: Value::known(self.tau.unwrap()), //
+            coin1_nonce: Value::known(self.nonce.unwrap()),
             coin1_blind: Value::known(self.c1_blind.unwrap()),
             coin1_blind: Value::known(self.c1_blind.unwrap()),
-            coin1_sn: Value::known(self.sn.unwrap()),
-            value: Value::known(pallas::Base::from(self.value.unwrap())),
+            coin1_serial: Value::known(self.sn.unwrap()),
+            coin1_value: Value::known(pallas::Base::from(self.value.unwrap())),
             coin2_blind: Value::known(self.c2_blind.unwrap()),
             coin2_blind: Value::known(self.c2_blind.unwrap()),
             coin2_commit: Value::known(self.cm2.unwrap()),
             coin2_commit: Value::known(self.cm2.unwrap()),
-            cm_pos: Value::known(self.idx),
-            //sn_c1: Value::known(self.sn.unwrap()),
-            slot: Value::known(self.sl.unwrap()),
             mau_rho: Value::known(mod_r_p(self.rho_mu.unwrap())),
             mau_rho: Value::known(mod_r_p(self.rho_mu.unwrap())),
             mau_y: Value::known(mod_r_p(self.y_mu.unwrap())),
             mau_y: Value::known(mod_r_p(self.y_mu.unwrap())),
-            root_cm: Value::known(self.root_cm.unwrap()),
             sigma1: Value::known(self.sigma1.unwrap()),
             sigma1: Value::known(self.sigma1.unwrap()),
             sigma2: Value::known(self.sigma2.unwrap()),
             sigma2: Value::known(self.sigma2.unwrap()),
             rho: Value::known(rho_pt),
             rho: Value::known(rho_pt),

+ 459 - 399
src/zk/circuit/lead_contract.rs

@@ -20,7 +20,9 @@ use darkfi_sdk::crypto::{
     constants::{
     constants::{
         sinsemilla::{OrchardCommitDomains, OrchardHashDomains},
         sinsemilla::{OrchardCommitDomains, OrchardHashDomains},
         util::gen_const_array,
         util::gen_const_array,
-        NullifierK, OrchardFixedBases, OrchardFixedBasesFull, MERKLE_DEPTH_ORCHARD,
+        NullifierK, OrchardFixedBases,
+        OrchardFixedBasesFull::ValueCommitR,
+        MERKLE_DEPTH_ORCHARD,
     },
     },
     MerkleNode,
     MerkleNode,
 };
 };
@@ -30,8 +32,8 @@ use halo2_gadgets::{
         FixedPoint, FixedPointBaseField, NonIdentityPoint, ScalarFixed,
         FixedPoint, FixedPointBaseField, NonIdentityPoint, ScalarFixed,
     },
     },
     poseidon::{
     poseidon::{
-        primitives as poseidon, Hash as PoseidonHash, Pow5Chip as PoseidonChip,
-        Pow5Config as PoseidonConfig,
+        primitives::{ConstantLength, P128Pow5T3},
+        Hash as PoseidonHash, Pow5Chip as PoseidonChip, Pow5Config as PoseidonConfig,
     },
     },
     sinsemilla::{
     sinsemilla::{
         chip::{SinsemillaChip, SinsemillaConfig},
         chip::{SinsemillaChip, SinsemillaConfig},
@@ -40,39 +42,50 @@ use halo2_gadgets::{
             MerklePath,
             MerklePath,
         },
         },
     },
     },
-    utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions},
+    utilities::lookup_range_check::LookupRangeCheckConfig,
 };
 };
 use halo2_proofs::{
 use halo2_proofs::{
-    circuit::{AssignedCell, Layouter, SimpleFloorPlanner, Value},
-    pasta::{group::Curve, pallas, Fp},
-    plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Instance as InstanceColumn},
+    circuit::{floor_planner, AssignedCell, Layouter, Value},
+    pasta::{group::Curve, pallas},
+    plonk,
+    plonk::{Advice, Circuit, Column, ConstraintSystem, Instance as InstanceColumn},
 };
 };
 
 
-use crate::zk::gadget::{
-    arithmetic::{ArithChip, ArithConfig, ArithInstruction},
-    less_than::{LessThanChip, LessThanConfig},
-    native_range_check::NativeRangeCheckChip,
+use crate::zk::{
+    assign_free_advice,
+    gadget::{
+        arithmetic::{ArithChip, ArithConfig, ArithInstruction},
+        less_than::{LessThanChip, LessThanConfig},
+        native_range_check::NativeRangeCheckChip,
+    },
 };
 };
 
 
-const WINDOW_SIZE: usize = 3;
-const NUM_OF_BITS: usize = 254;
-const NUM_OF_WINDOWS: usize = 85;
+
+/// Public input offset for the lead coin C2 nonce
+const LEADCOIN_C2_NONCE_OFFSET: usize = 0;
+/// Public input offset for lead coin public key X coordinate
+const LEADCOIN_PK_X_OFFSET: usize = 1;
+/// Public input offset for lead coin public key Y coordinate
+const LEADCOIN_PK_Y_OFFSET: usize = 2;
+/// Public input offset for the lottery target lhs
+const LEADCOIN_Y_BASE_OFFSET: usize = 3;
+/// Derivation prefix for the nullifier PRF
 const PRF_NULLIFIER_PREFIX: u64 = 0;
 const PRF_NULLIFIER_PREFIX: u64 = 0;
 
 
+/// Circuit configuration for the crypsinous leader proof
 #[derive(Clone, Debug)]
 #[derive(Clone, Debug)]
 pub struct LeadConfig {
 pub struct LeadConfig {
     primary: Column<InstanceColumn>,
     primary: Column<InstanceColumn>,
-    advices: [Column<Advice>; 10],
+    advices: [Column<Advice>; 14],
     ecc_config: EccConfig<OrchardFixedBases>,
     ecc_config: EccConfig<OrchardFixedBases>,
     poseidon_config: PoseidonConfig<pallas::Base, 3, 2>,
     poseidon_config: PoseidonConfig<pallas::Base, 3, 2>,
-    merkle_config_1: MerkleConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
-    merkle_config_2: MerkleConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
     sinsemilla_config_1:
     sinsemilla_config_1:
         SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
         SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
-    _sinsemilla_config_2:
+    sinsemilla_config_2:
         SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
         SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
-
-    lessthan_config: LessThanConfig<WINDOW_SIZE, NUM_OF_BITS, NUM_OF_WINDOWS>,
+    merkle_config_1: MerkleConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
+    merkle_config_2: MerkleConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
+    lessthan_config: LessThanConfig<3, 253, 85>,
     arith_config: ArithConfig,
     arith_config: ArithConfig,
 }
 }
 
 
@@ -97,7 +110,7 @@ impl LeadConfig {
         MerkleChip::construct(self.merkle_config_2.clone())
         MerkleChip::construct(self.merkle_config_2.clone())
     }
     }
 
 
-    fn lessthan_chip(&self) -> LessThanChip<WINDOW_SIZE, NUM_OF_BITS, NUM_OF_WINDOWS> {
+    fn lessthan_chip(&self) -> LessThanChip<3, 253, 85> {
         LessThanChip::construct(self.lessthan_config.clone())
         LessThanChip::construct(self.lessthan_config.clone())
     }
     }
 
 
@@ -106,69 +119,62 @@ impl LeadConfig {
     }
     }
 }
 }
 
 
-const LEAD_COIN_NONCE2_OFFSET: usize = 0;
-const LEAD_COIN_PK_X_OFFSET: usize = 1;
-const LEAD_COIN_PK_Y_OFFSET: usize = 2;
-const LEAD_Y_COMMIT_BASE_OFFSET: usize = 3;
-
-#[derive(Default, Debug)]
+/// Circuit implementation for the crypsinous leader proof
+#[derive(Default, Clone, Debug)]
 pub struct LeadContract {
 pub struct LeadContract {
-    pub path: Value<[MerkleNode; MERKLE_DEPTH_ORCHARD]>,
-    /// path of coin1 commitment
-    pub sk: Value<pallas::Base>,
-    /// coin secret key
-    pub root_sk: Value<pallas::Base>,
-    /// root to secret key
-    pub path_sk: Value<[MerkleNode; MERKLE_DEPTH_ORCHARD]>,
-    /// path to the secret key rooted by `root_sk`
-    pub coin_timestamp: Value<pallas::Base>,
-    /// $\tau$ or coin index
-    pub coin_nonce: Value<pallas::Base>,
-    /// coin nonce $\rho_{c1}$
+    /// Merkle path to the commitment for `coin_1`
+    pub coin1_commit_merkle_path: Value<[MerkleNode; MERKLE_DEPTH_ORCHARD]>,
+    /// Merkle root to the commitment of `coin_1` in the Merkle tree of commitments
+    pub coin1_commit_root: Value<pallas::Base>,
+    /// `coin_1` leaf position in the Merkle tree of coin commitments
+    pub coin1_commit_leaf_pos: Value<u32>,
+    /// `coin_1` secret key.
+    pub coin1_sk: Value<pallas::Base>,
+    /// Merkle root of the `coin_1` secret key in the Merkle tree of secret keys.
+    pub coin1_sk_root: Value<pallas::Base>,
+    /// Merkle path to the secret key of `coin_1` in the Merkle tree of secret keys.
+    pub coin1_sk_merkle_path: Value<[MerkleNode; MERKLE_DEPTH_ORCHARD]>,
+    /// $\tau$ (in the crypsinous paper), can be slot index, or coin timestamp.
+    /// Used only in the public key calculation.
+    pub coin1_timestamp: Value<pallas::Base>,
+    /// `coin_1` nonce, a random sampled value. `coin_2` nonce is calculated
+    /// inside the circuit as `Hash(coin1_nonce || coin1_sk_root)`, assuming
+    /// `Hash` is a function that satisfies a PRF definition.
+    pub coin1_nonce: Value<pallas::Base>,
+    /// Blinding factor for the commitment of `coin_1`
     pub coin1_blind: Value<pallas::Scalar>,
     pub coin1_blind: Value<pallas::Scalar>,
-    /// coin blind r
-    pub coin1_sn: Value<pallas::Base>,
-    /// coin serial number
-    pub value: Value<pallas::Base>,
-    /// coin value
+    /// Serial number for `coin_1`
+    pub coin1_serial: Value<pallas::Base>,
+    /// Value of `coin_1`
+    pub coin1_value: Value<pallas::Base>,
+    /// Blinding factor for the commitment of `coin_2`
     pub coin2_blind: Value<pallas::Scalar>,
     pub coin2_blind: Value<pallas::Scalar>,
-    /// coin2 blind r2
+    /// `coin_2` pedersen commitment point
     pub coin2_commit: Value<pallas::Point>,
     pub coin2_commit: Value<pallas::Point>,
-    /// coin2 commitment
-    pub cm_pos: Value<u32>,
-    /// coin position in the merkle tree
-    pub slot: Value<pallas::Base>,
-    /// slot index
+    /// Random value derived from `eta` used for constraining `rho`
     pub mau_rho: Value<pallas::Scalar>,
     pub mau_rho: Value<pallas::Scalar>,
-    /// random value derived from eta used for constraining \rho
+    /// Random value derived from `eta` used for calculating `y`.
     pub mau_y: Value<pallas::Scalar>,
     pub mau_y: Value<pallas::Scalar>,
-    /// random value derived from eta used for calculating y
-    pub root_cm: Value<pallas::Base>,
-    /// root to commitment in the mekle tree
+    /// First coefficient in 1-term T (target function) approximation.
+    /// sigma1 and sigma2 is not the capital sigma from the paper, but
+    /// the whole coefficient multiplied with the absolute stake.
     pub sigma1: Value<pallas::Base>,
     pub sigma1: Value<pallas::Base>,
-    /// first coefficient in 1-term T (target function) approximation
+    /// Second coefficient in 2-term T (target function) approximation
     pub sigma2: Value<pallas::Base>,
     pub sigma2: Value<pallas::Base>,
-    /// second coefficient in 2-term T (target function) approximation
-    /// constrained nonce $\rho$
+    /// Constrained nonce `rho`.
     pub rho: Value<pallas::Point>,
     pub rho: Value<pallas::Point>,
-    //pub eta : Option<u32>,
-    //pub h : Option<u32>, // hash of this data
-    //pub ptr: Option<u32>, //hash of the previous block
-}
-
-impl UtilitiesInstructions<pallas::Base> for LeadContract {
-    type Var = AssignedCell<Fp, Fp>;
 }
 }
 
 
 impl Circuit<pallas::Base> for LeadContract {
 impl Circuit<pallas::Base> for LeadContract {
     type Config = LeadConfig;
     type Config = LeadConfig;
-    type FloorPlanner = SimpleFloorPlanner;
+    type FloorPlanner = floor_planner::V1;
 
 
     fn without_witnesses(&self) -> Self {
     fn without_witnesses(&self) -> Self {
         Self::default()
         Self::default()
     }
     }
 
 
     fn configure(meta: &mut ConstraintSystem<pallas::Base>) -> Self::Config {
     fn configure(meta: &mut ConstraintSystem<pallas::Base>) -> Self::Config {
+        // Advice columns used in the circuit
         let advices = [
         let advices = [
             meta.advice_column(),
             meta.advice_column(),
             meta.advice_column(),
             meta.advice_column(),
@@ -180,14 +186,21 @@ impl Circuit<pallas::Base> for LeadContract {
             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 table_idx = meta.lookup_table_column();
         let lookup = (table_idx, meta.lookup_table_column(), 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();
         let primary = meta.instance_column();
         meta.enable_equality(primary);
         meta.enable_equality(primary);
 
 
+        // Permutation over all advice columns
         for advice in advices.iter() {
         for advice in advices.iter() {
             meta.enable_equality(*advice);
             meta.enable_equality(*advice);
         }
         }
@@ -202,28 +215,35 @@ impl Circuit<pallas::Base> for LeadContract {
             meta.fixed_column(),
             meta.fixed_column(),
             meta.fixed_column(),
             meta.fixed_column(),
         ];
         ];
-
         let rc_a = lagrange_coeffs[2..5].try_into().unwrap();
         let rc_a = lagrange_coeffs[2..5].try_into().unwrap();
         let rc_b = lagrange_coeffs[5..8].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]);
         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);
         let range_check = LookupRangeCheckConfig::configure(meta, advices[9], table_idx);
 
 
+        // Configuration for curve point operations. This uses 10 advice columns.
         let ecc_config = EccChip::<OrchardFixedBases>::configure(
         let ecc_config = EccChip::<OrchardFixedBases>::configure(
             meta,
             meta,
-            advices[0..10].try_into().expect("wrong slice size"),
+            advices[..10].try_into().unwrap(),
             lagrange_coeffs,
             lagrange_coeffs,
             range_check,
             range_check,
         );
         );
 
 
-        let poseidon_config = PoseidonChip::configure::<poseidon::P128Pow5T3>(
+        let poseidon_config = PoseidonChip::configure::<P128Pow5T3>(
             meta,
             meta,
-            advices[6..9].try_into().unwrap(),
-            advices[5],
+            advices[10..13].try_into().unwrap(),
+            advices[13],
             rc_a,
             rc_a,
             rc_b,
             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, merkle_config_1) = {
             let sinsemilla_config_1 = SinsemillaChip::configure(
             let sinsemilla_config_1 = SinsemillaChip::configure(
                 meta,
                 meta,
@@ -247,34 +267,34 @@ impl Circuit<pallas::Base> for LeadContract {
                 range_check,
                 range_check,
             );
             );
             let merkle_config_2 = MerkleChip::configure(meta, sinsemilla_config_2.clone());
             let merkle_config_2 = MerkleChip::configure(meta, sinsemilla_config_2.clone());
-
             (sinsemilla_config_2, merkle_config_2)
             (sinsemilla_config_2, merkle_config_2)
         };
         };
 
 
+        // Lookup table for native range checks and less-than check
         let k_values_table = meta.lookup_table_column();
         let k_values_table = meta.lookup_table_column();
-
         let lessthan_config = {
         let lessthan_config = {
-            let a = meta.advice_column();
-            let b = meta.advice_column();
-            let a_offset = meta.advice_column();
-
-            let z1 = meta.advice_column();
-            let z2 = meta.advice_column();
-
+            /*
+            let a = advices[10];
+            let b = advices[11];
+            let a_offset = advices[12];
+            let z1 = advices[13];
+            let z2 = advices[14];
+            */
+            let a = advices[9];
+            let b = advices[10];
+            let a_offset = advices[11];
+            let z1 = advices[12];
+            let z2 = advices[13];
             let constants = meta.fixed_column();
             let constants = meta.fixed_column();
             meta.enable_constant(constants);
             meta.enable_constant(constants);
+            meta.enable_equality(a);
+            meta.enable_equality(b);
+            meta.enable_equality(a_offset);
 
 
-            LessThanChip::<WINDOW_SIZE, NUM_OF_BITS, NUM_OF_WINDOWS>::configure(
-                meta,
-                a,
-                b,
-                a_offset,
-                z1,
-                z2,
-                k_values_table,
-            )
+            LessThanChip::<3, 253, 85>::configure(meta, a, b, a_offset, z1, z2, k_values_table)
         };
         };
 
 
+        // Configuration for the arithmetic chip
         let arith_config = ArithChip::configure(meta, advices[7], advices[8], advices[6]);
         let arith_config = ArithChip::configure(meta, advices[7], advices[8], advices[6]);
 
 
         LeadConfig {
         LeadConfig {
@@ -282,10 +302,10 @@ impl Circuit<pallas::Base> for LeadContract {
             advices,
             advices,
             ecc_config,
             ecc_config,
             poseidon_config,
             poseidon_config,
+            sinsemilla_config_1,
+            sinsemilla_config_2,
             merkle_config_1,
             merkle_config_1,
             merkle_config_2,
             merkle_config_2,
-            sinsemilla_config_1,
-            _sinsemilla_config_2: sinsemilla_config_2,
             lessthan_config,
             lessthan_config,
             arith_config,
             arith_config,
         }
         }
@@ -295,406 +315,446 @@ impl Circuit<pallas::Base> for LeadContract {
         &self,
         &self,
         config: Self::Config,
         config: Self::Config,
         mut layouter: impl Layouter<pallas::Base>,
         mut layouter: impl Layouter<pallas::Base>,
-    ) -> Result<(), Error> {
-        let less_than_chip = config.lessthan_chip();
-        NativeRangeCheckChip::<WINDOW_SIZE, NUM_OF_BITS, NUM_OF_WINDOWS>::load_k_table(
+    ) -> Result<(), plonk::Error> {
+        // Initialize necessary chips
+        let lessthan_chip = config.lessthan_chip();
+        NativeRangeCheckChip::<3, 253, 85>::load_k_table(
             &mut layouter,
             &mut layouter,
             config.lessthan_config.k_values_table,
             config.lessthan_config.k_values_table,
         )?;
         )?;
         SinsemillaChip::load(config.sinsemilla_config_1.clone(), &mut layouter)?;
         SinsemillaChip::load(config.sinsemilla_config_1.clone(), &mut layouter)?;
         let ecc_chip = config.ecc_chip();
         let ecc_chip = config.ecc_chip();
-        let ar_chip = config.arith_chip();
-        let _ps_chip = config.poseidon_chip();
-
-        // ===============
-        // load witnesses
-        // ===============
-
-        // prefix to the pseudo-random-function that prefix input
-        // to the nullifier poseidon hash
-        let prf_nullifier_prefix_base = self.load_private(
-            layouter.namespace(|| "PRF NULLIFIER PREFIX BASE"),
-            config.advices[0],
+        let arith_chip = config.arith_chip();
+
+        // ====================================
+        // Load witness values into the circuit
+        // ====================================
+        let prf_nullifier_prefix_base = assign_free_advice(
+            layouter.namespace(|| "witness nullifier prefix"),
+            config.advices[8],
             Value::known(pallas::Base::from(PRF_NULLIFIER_PREFIX)),
             Value::known(pallas::Base::from(PRF_NULLIFIER_PREFIX)),
         )?;
         )?;
-        // staking coin nonce
-        let coin_nonce: AssignedCell<Fp, Fp> = self.load_private(
-            layouter.namespace(|| "load coin nonce"),
-            config.advices[0],
-            self.coin_nonce,
+
+        let coin1_commit_merkle_path: Value<[pallas::Base; MERKLE_DEPTH_ORCHARD]> = self
+            .coin1_commit_merkle_path
+            .map(|typed_path| gen_const_array(|i| typed_path[i].inner()));
+
+        let coin1_commit_root = assign_free_advice(
+            layouter.namespace(|| "witness coin_commitment_root"),
+            config.advices[8],
+            self.coin1_commit_root,
+        )?;
+
+        let coin1_sk = assign_free_advice(
+            layouter.namespace(|| "witness coin1_sk"),
+            config.advices[8],
+            self.coin1_sk,
+        )?;
+
+        let coin1_sk_root = assign_free_advice(
+            layouter.namespace(|| "witness coin1_sk_root"),
+            config.advices[8],
+            self.coin1_sk_root,
+        )?;
+
+        let coin1_sk_merkle_path: Value<[pallas::Base; MERKLE_DEPTH_ORCHARD]> =
+            self.coin1_sk_merkle_path.map(|typed_path| gen_const_array(|i| typed_path[i].inner()));
+
+        let coin1_timestamp = assign_free_advice(
+            layouter.namespace(|| "witness coin1_timestamp"),
+            config.advices[8],
+            self.coin1_timestamp,
+        )?;
+
+        let coin1_nonce = assign_free_advice(
+            layouter.namespace(|| "witness coin1_nonce"),
+            config.advices[8],
+            self.coin1_nonce,
         )?;
         )?;
 
 
-        let coin1_sn: AssignedCell<Fp, Fp> = self.load_private(
-            layouter.namespace(|| "load coin1 sn"),
-            config.advices[0],
-            self.coin1_sn,
+        let coin1_blind = ScalarFixed::new(
+            ecc_chip.clone(),
+            layouter.namespace(|| "witness coin1_blind"),
+            self.coin1_blind,
         )?;
         )?;
 
 
-        // staking coin value
-        let coin_value = self.load_private(
-            layouter.namespace(|| "load coin value"),
-            config.advices[0],
-            self.value,
+        let coin1_serial = assign_free_advice(
+            layouter.namespace(|| "witness coin1_serial"),
+            config.advices[8],
+            self.coin1_serial,
         )?;
         )?;
 
 
-        // staking coin secret key
-        let _root_sk =
-            self.load_private(layouter.namespace(|| "root sk"), config.advices[0], self.root_sk)?;
+        let coin1_value = assign_free_advice(
+            layouter.namespace(|| "witness coin1_value"),
+            config.advices[8],
+            self.coin1_value,
+        )?;
 
 
-        let root_cm =
-            self.load_private(layouter.namespace(|| ""), config.advices[0], self.root_cm)?;
+        let coin2_blind = ScalarFixed::new(
+            ecc_chip.clone(),
+            layouter.namespace(|| "witness coin2_blind"),
+            self.coin2_blind,
+        )?;
 
 
-        // staking coin secret key
-        let sk: AssignedCell<Fp, Fp> =
-            self.load_private(layouter.namespace(|| "sk"), config.advices[0], self.sk)?;
+        let coin2_commit = NonIdentityPoint::new(
+            ecc_chip.clone(),
+            layouter.namespace(|| "witness coin2_commit"),
+            self.coin2_commit.as_ref().map(|cm| cm.to_affine()),
+        )?;
 
 
-        let sigma1 = self.load_private(
-            layouter.namespace(|| "load sigma1 "),
-            config.advices[0],
+        let mau_rho = ScalarFixed::new(
+            ecc_chip.clone(),
+            layouter.namespace(|| "witness mau_rho"),
+            self.mau_rho,
+        )?;
+
+        let mau_y =
+            ScalarFixed::new(ecc_chip.clone(), layouter.namespace(|| "witness mau_y"), self.mau_y)?;
+
+        let sigma1 = assign_free_advice(
+            layouter.namespace(|| "witness sigma1"),
+            config.advices[8],
             self.sigma1,
             self.sigma1,
         )?;
         )?;
 
 
-        let sigma2 = self.load_private(
-            layouter.namespace(|| "load sigma2 "),
-            config.advices[0],
+        let sigma2 = assign_free_advice(
+            layouter.namespace(|| "witness sigma2"),
+            config.advices[8],
             self.sigma2,
             self.sigma2,
         )?;
         )?;
 
 
-        let one = self.load_private(
-            layouter.namespace(|| "one"),
-            config.advices[0],
+        let rho = NonIdentityPoint::new(
+            ecc_chip.clone(),
+            layouter.namespace(|| "witness rho"),
+            self.rho.as_ref().map(|cm| cm.to_affine()),
+        )?;
+
+        let zero = assign_free_advice(
+            layouter.namespace(|| "witness constant zero"),
+            config.advices[8],
+            Value::known(pallas::Base::zero()),
+        )?;
+
+        let one = assign_free_advice(
+            layouter.namespace(|| "witness constant one"),
+            config.advices[8],
             Value::known(pallas::Base::one()),
             Value::known(pallas::Base::one()),
         )?;
         )?;
 
 
-        // the original crypsinous coin pk is as follows.
-        // coin public key pk=PRF_{root_sk}(tau)
-        // coin public key is pseudo random hash of concatenation of the following:
-        // coin timestamp, and root of coin's secret key.
-        // staking coin timestamp
-        //let coin_timestamp = self.load_private(
-        //layouter.namespace(|| "load coin time stamp"),
-        //config.advices[0],
-        //self.coin_timestamp,
-        //)?;
-        //let coin_pk_commit: AssignedCell<Fp, Fp> = {
-        //    let poseidon_message = [coin_timestamp, _root_sk.clone()];
-        //  //let poseidon_hasher = PoseidonHash::<
-        //      _,
-        //      _,
-        //      poseidon::P128Pow5T3,
-        //      poseidon::ConstantLength<2>,
-        //      3,
-        //      2,
-        //  >::init(
-        //      config.poseidon_chip(), layouter.namespace(|| "Poseidon init")
-        //  )?;
-        //
-        //  let poseidon_output =
-        //      poseidon_hasher.hash(layouter.namespace(|| "Poseidon hash"), poseidon_message)?;
-        //  let poseidon_output: AssignedCell<Fp, Fp> = poseidon_output;
-        //  poseidon_output
-        //};
-        // darkfi coin pk is based off secret key:
-        // pk = G(nullifierK) * sk
-        // the later is implemented for the sake of conversion between
-        // lead coin  and owncoin.
+        // ========================
+        // Derive coin's public key
+        // ========================
         let coin_pk = {
         let coin_pk = {
             let coin_pk_commit_v = FixedPointBaseField::from_inner(ecc_chip.clone(), NullifierK);
             let coin_pk_commit_v = FixedPointBaseField::from_inner(ecc_chip.clone(), NullifierK);
-            coin_pk_commit_v.mul(layouter.namespace(|| "coin pk commit v"), sk)?
+            coin_pk_commit_v.mul(layouter.namespace(|| "coin_1sk * NullifierK"), coin1_sk)?
         };
         };
-        let coin_pk_x = coin_pk.inner().x();
-        let coin_pk_y = coin_pk.inner().y();
-        // coin c1 serial number sn=PRF_{root_sk}(nonce)
-        // coin's serial number is derived from coin nonce (sampled at random)
-        // and root of the coin's secret key sampled an random.
-        let sn_commit: AssignedCell<Fp, Fp> = {
-            let poseidon_message = [coin_nonce.clone(), _root_sk.clone()];
-            let poseidon_hasher = PoseidonHash::<
-                _,
-                _,
-                poseidon::P128Pow5T3,
-                poseidon::ConstantLength<2>,
-                3,
-                2,
-            >::init(
-                config.poseidon_chip(), layouter.namespace(|| "Poseidon init")
-            )?;
 
 
-            let poseidon_output =
-                poseidon_hasher.hash(layouter.namespace(|| "Poseidon hash"), poseidon_message)?;
-            let poseidon_output: AssignedCell<Fp, Fp> = poseidon_output;
-            poseidon_output
+
+
+        // Coin `c1` serial number:
+        // sn=PRF_{root_sk}(nonce)
+        // Coin's serial number is derived from coin nonce (sampled at random)
+        // and root of the coin's secret key sampled at random.
+        let sn_commit: AssignedCell<pallas::Base, pallas::Base> = {
+            // For derivation here, we append one 0 and one 1 to the hashed message.
+            // TODO: Add these constants to ouroboros/consts.rs
+            let poseidon_message =
+                [coin1_nonce.clone(), coin1_sk_root.clone(), zero.clone(), one.clone()];
+            let poseidon_hasher = PoseidonHash::<_, _, P128Pow5T3, ConstantLength<4>, 3, 2>::init(
+                config.poseidon_chip(),
+                layouter.namespace(|| "sn_commit poseidon init"),
+            )?;
+            let poseidon_output = poseidon_hasher
+                .hash(layouter.namespace(|| "sn_commit poseidon hash"), poseidon_message)?;
+            poseidon_output.into()
         };
         };
-        // commitment to the staking coin
-        // coin commiment H=COMMIT(PRF(prefix||pk||V||nonce), r)
-        let com = {
-            // coin c1 nullifier is a commitment of the following
-            // nullifier input
-            let nullifier_msg: AssignedCell<Fp, Fp> = {
+
+
+
+        // ==============================
+        // Commitment to the staking coin
+        // ==============================
+        // coin commitment H=Commit(PRF(prefix||pk||V||nonce), r)
+        let coin_commitment_v = {
+            // Coin c1 nullifier is a commitment of the following:
+            let nullifier_msg: AssignedCell<pallas::Base, pallas::Base> = {
                 let poseidon_message = [
                 let poseidon_message = [
                     prf_nullifier_prefix_base.clone(),
                     prf_nullifier_prefix_base.clone(),
-                    coin_pk_x.clone(),
-                    coin_pk_y.clone(),
-                    coin_value.clone(),
-                    coin_nonce.clone(),
-                    one.clone(),
+                    coin_pk.inner().x(),
+                    coin_pk.inner().y(),
+                    coin1_value.clone(),
+                    coin1_nonce.clone(),
+                    one.clone(), // One is here because of poseidon odd-n bug
                 ];
                 ];
-                let poseidon_hasher = PoseidonHash::<
-                    _,
-                    _,
-                    poseidon::P128Pow5T3,
-                    poseidon::ConstantLength<6>,
-                    3,
-                    2,
-                >::init(
-                    config.poseidon_chip(),
-                    layouter.namespace(|| "Poseidon init"),
-                )?;
-
+                let poseidon_hasher =
+                    PoseidonHash::<_, _, P128Pow5T3, ConstantLength<6>, 3, 2>::init(
+                        config.poseidon_chip(),
+                        layouter.namespace(|| "nullifier poseidon init"),
+                    )?;
                 let poseidon_output = poseidon_hasher
                 let poseidon_output = poseidon_hasher
-                    .hash(layouter.namespace(|| "Poseidon hash"), poseidon_message)?;
-                let poseidon_output: AssignedCell<Fp, Fp> = poseidon_output;
-                poseidon_output
+                    .hash(layouter.namespace(|| "nullifier poseidon hash"), poseidon_message)?;
+                poseidon_output.into()
             };
             };
-            let coin_commit_v = FixedPointBaseField::from_inner(ecc_chip.clone(), NullifierK);
-            coin_commit_v.mul(layouter.namespace(|| "coin commit v"), nullifier_msg)?
+
+            let v = FixedPointBaseField::from_inner(ecc_chip.clone(), NullifierK);
+            v.mul(layouter.namespace(|| "nullifier_msg * NullifierK"), nullifier_msg)?
         };
         };
 
 
-        // r*G_2
-        let (blind, _) = {
-            let rcv = ScalarFixed::new(
-                ecc_chip.clone(),
-                layouter.namespace(|| "coin1 blind scalar"),
-                self.coin1_blind,
-            )?;
-            let coin_commit_r =
-                FixedPoint::from_inner(ecc_chip.clone(), OrchardFixedBasesFull::ValueCommitR);
-            coin_commit_r.mul(layouter.namespace(|| "coin serial number commit R"), rcv)?
+        let (coin_commitment_r, _) = {
+            let r = FixedPoint::from_inner(ecc_chip.clone(), ValueCommitR);
+            r.mul(layouter.namespace(|| "coin1_blind * ValueCommitR"), coin1_blind)?
         };
         };
 
 
-        let coin_commit = com.add(layouter.namespace(|| "nonce commit"), &blind)?;
-
-        // nonce2  =  PRF_{root_sk}(coin_nonce)
-        // poured coin nonce as a poseidon of the previous nonce, and
-        // root of secret key.
-        let coin2_nonce: AssignedCell<Fp, Fp> = {
-            let poseidon_message = [coin_nonce.clone(), _root_sk.clone()];
-            let poseidon_hasher = PoseidonHash::<
-                _,
-                _,
-                poseidon::P128Pow5T3,
-                poseidon::ConstantLength<2>,
-                3,
-                2,
-            >::init(
-                config.poseidon_chip(), layouter.namespace(|| "Poseidon init")
+        let coin_commitment = coin_commitment_v.add(
+            layouter.namespace(|| "coin_commitment_v + coin_commitment_r"),
+            &coin_commitment_r,
+        )?;
+
+        // ================================================
+        // Validate Merkle path to staked coin's commitment
+        // ================================================
+        let merkle_inputs = MerklePath::construct(
+            [config.merkle_chip_1(), config.merkle_chip_2()],
+            OrchardHashDomains::MerkleCrh,
+            self.coin1_commit_leaf_pos,
+            coin1_commit_merkle_path,
+        );
+
+        let coin1_commit_hash: AssignedCell<pallas::Base, pallas::Base> = {
+            let poseidon_message = [coin_commitment.inner().x(), coin_commitment.inner().y()];
+            let poseidon_hasher = PoseidonHash::<_, _, P128Pow5T3, ConstantLength<2>, 3, 2>::init(
+                config.poseidon_chip(),
+                layouter.namespace(|| "coin1_commit_hash poseidon init"),
             )?;
             )?;
+            let poseidon_output = poseidon_hasher
+                .hash(layouter.namespace(|| "coin1_commit_hash poseidon hash"), poseidon_message)?;
+            poseidon_output.into()
+        };
+
+        let coin1_cm_root = merkle_inputs.calculate_root(
+            layouter.namespace(|| "calculate merkle root for coin1 commitment"),
+            coin1_commit_hash,
+        )?;
+
+
 
 
-            let poseidon_output =
-                poseidon_hasher.hash(layouter.namespace(|| "Poseidon hash"), poseidon_message)?;
-            let poseidon_output: AssignedCell<Fp, Fp> = poseidon_output;
-            poseidon_output
+        // ===========================
+        // Derivation of coin2's nonce
+        // ===========================
+        let coin2_nonce: AssignedCell<pallas::Base, pallas::Base> = {
+            // For derivation here, we append 1 two times to the hashed message.
+            // TODO: Add these constants to ouroboros/consts.rs
+            let poseidon_message =
+                [coin1_nonce.clone(), coin1_sk_root.clone(), one.clone(), one.clone()];
+            let poseidon_hasher = PoseidonHash::<_, _, P128Pow5T3, ConstantLength<4>, 3, 2>::init(
+                config.poseidon_chip(),
+                layouter.namespace(|| "coin2_nonce poseidon init"),
+            )?;
+            let poseidon_output = poseidon_hasher
+                .hash(layouter.namespace(|| "coin2_nonce poseidon hash"), poseidon_message)?;
+            poseidon_output.into()
         };
         };
-        // coin2 commiment H=COMMIT(PRF(pk||V||nonce2), r2)
-        // poured coin's commitment is a nullifier
-        let com2 = {
-            // coin2's commitment input body as a poseidon of input concatenation of
-            // public key, stake, and poured coin's nonce.
-            let nullifier2_msg: AssignedCell<Fp, Fp> = {
+
+
+        // ================
+        // Coin2 commitment
+        // ================
+        // H=Commit(PRF(pk||V||nonce2), r2)
+        // Poured coin's commitment is a nullifier
+        let coin2_commitment_v = {
+            // coin2's commitment input body as a poseidon hash of input
+            // concatenation of public key, stake, and poured coin's nonce.
+            let nullifier2: AssignedCell<pallas::Base, pallas::Base> = {
                 let poseidon_message = [
                 let poseidon_message = [
                     prf_nullifier_prefix_base,
                     prf_nullifier_prefix_base,
-                    coin_pk_x.clone(),
-                    coin_pk_y.clone(),
-                    coin_value.clone(),
+                    coin_pk.inner().x(),
+                    coin_pk.inner().y(),
+                    coin1_value.clone(),
                     coin2_nonce.clone(),
                     coin2_nonce.clone(),
-                    one.clone(),
+                    one.clone(), // Used here because of poseidon odd-n bug
                 ];
                 ];
-                let poseidon_hasher = PoseidonHash::<
-                    _,
-                    _,
-                    poseidon::P128Pow5T3,
-                    poseidon::ConstantLength<6>,
-                    3,
-                    2,
-                >::init(
-                    config.poseidon_chip(),
-                    layouter.namespace(|| "Poseidon init"),
+                let poseidon_hasher =
+                    PoseidonHash::<_, _, P128Pow5T3, ConstantLength<6>, 3, 2>::init(
+                        config.poseidon_chip(),
+                        layouter.namespace(|| "coin2_commitment_v poseidon init"),
+                    )?;
+                let poseidon_output = poseidon_hasher.hash(
+                    layouter.namespace(|| "coin2_commitment_v poseidon hash"),
+                    poseidon_message,
                 )?;
                 )?;
-
-                let poseidon_output = poseidon_hasher
-                    .hash(layouter.namespace(|| "Poseidon hash"), poseidon_message)?;
-                let poseidon_output: AssignedCell<Fp, Fp> = poseidon_output;
-                poseidon_output
+                poseidon_output.into()
             };
             };
-            let coin_commit_v = FixedPointBaseField::from_inner(ecc_chip.clone(), NullifierK);
-            coin_commit_v.mul(layouter.namespace(|| "coin commit v"), nullifier2_msg)?
-        };
-        // r*G_2
-        let (blind, _) = {
-            let coin2_blind = ScalarFixed::new(
-                ecc_chip.clone(),
-                layouter.namespace(|| "coin2 blind scalar"),
-                self.coin2_blind,
-            )?;
-            let coin_commit_r =
-                FixedPoint::from_inner(ecc_chip.clone(), OrchardFixedBasesFull::ValueCommitR);
-            coin_commit_r.mul(layouter.namespace(|| "coin serial number commit R"), coin2_blind)?
+
+            let v = FixedPointBaseField::from_inner(ecc_chip.clone(), NullifierK);
+            v.mul(layouter.namespace(|| "nullifier2 * NullifierK"), nullifier2)?
         };
         };
-        let coin2_commit = com2.add(layouter.namespace(|| "nonce commit"), &blind)?;
 
 
-        // path is valid path to staked coin's commitment
-        let path: Value<[pallas::Base; MERKLE_DEPTH_ORCHARD]> =
-            self.path.map(|typed_path| gen_const_array(|i| typed_path[i].inner()));
+        let (coin2_commitment_r, _) = {
+            let r = FixedPoint::from_inner(ecc_chip.clone(), ValueCommitR);
+            r.mul(layouter.namespace(|| "coin2_blind * ValueCommitR"), coin2_blind)?
+        };
 
 
-        let merkle_inputs = MerklePath::construct(
-            [config.merkle_chip_1(), config.merkle_chip_2()],
-            OrchardHashDomains::MerkleCrh,
-            self.cm_pos,
-            path,
-        );
+        let coin2_commitment = coin2_commitment_v.add(
+            layouter.namespace(|| "coin2_commitment_v + coin2_commitment_r"),
+            &coin2_commitment_r,
+        )?;
 
 
-        //TODO (fix) replace mul by hash
-        let coin_commit_prod: AssignedCell<Fp, Fp> = {
-            let coin_commit_coordinates = coin_commit.inner();
 
 
-            let res: AssignedCell<Fp, Fp> = ar_chip.mul(
-                layouter.namespace(|| ""),
-                &coin_commit_coordinates.x(),
-                &coin_commit_coordinates.y(),
-            )?;
-            res
-        };
 
 
-        let coin_cm_root = merkle_inputs
-            .calculate_root(layouter.namespace(|| "calculate root"), coin_commit_prod)?;
+        // ==================================
         // lhs of the leader election lottery
         // lhs of the leader election lottery
-        // *  y as COMIT(root_sk||nonce, mau_y)
-        // beging the commitment to the coin's secret key, coin's nonce, and
-        // random value deriven from the epoch sampled random eta.
-        let lottery_commit_msg: AssignedCell<Fp, Fp> = {
-            let poseidon_message = [_root_sk, coin_nonce];
-            let poseidon_hasher = PoseidonHash::<
-                _,
-                _,
-                poseidon::P128Pow5T3,
-                poseidon::ConstantLength<2>,
-                3,
-                2,
-            >::init(
-                config.poseidon_chip(), layouter.namespace(|| "Poseidon init")
+        // ==================================
+        // * y as Commit(root_sk||nonce, mau_y)
+        // Commitment to the coin's secret key, coin's nonce, and random value
+        // derived from the epoch sampled random eta.
+        let lottery_commit_msg: AssignedCell<pallas::Base, pallas::Base> = {
+            let poseidon_message = [coin1_sk_root.clone(), coin1_nonce.clone()];
+            let poseidon_hasher = PoseidonHash::<_, _, P128Pow5T3, ConstantLength<2>, 3, 2>::init(
+                config.poseidon_chip(),
+                layouter.namespace(|| "lottery_commit_msg poseidon init"),
+            )?;
+            let poseidon_output = poseidon_hasher.hash(
+                layouter.namespace(|| "lottery_commit_msg poseidon hash"),
+                poseidon_message,
             )?;
             )?;
+            poseidon_output.into()
+        };
 
 
-            let poseidon_output =
-                poseidon_hasher.hash(layouter.namespace(|| "Poseidon hash"), poseidon_message)?;
-            let poseidon_output: AssignedCell<Fp, Fp> = poseidon_output;
-            poseidon_output
+        let lottery_commit_v = {
+            let v = FixedPointBaseField::from_inner(ecc_chip.clone(), NullifierK);
+            v.mul(layouter.namespace(|| "lottery_commit_msg * NullifierK"), lottery_commit_msg)?
         };
         };
 
 
-        let com = {
-            let y_commit_v = FixedPointBaseField::from_inner(ecc_chip.clone(), NullifierK);
-            y_commit_v.mul(layouter.namespace(|| "coin commit v"), lottery_commit_msg)?
+        let (lottery_commit_r, _) = {
+            let r = FixedPoint::from_inner(ecc_chip.clone(), ValueCommitR);
+            r.mul(layouter.namespace(|| "mau_y * ValueCommitR"), mau_y)?
         };
         };
 
 
-        // r*G_2
-        let (blind, _) = {
-            let mau_y = ScalarFixed::new(
-                ecc_chip.clone(),
-                layouter.namespace(|| "mau_y scalar"),
-                self.mau_y,
+        let y_commit = lottery_commit_v
+            .add(layouter.namespace(|| "lottery_commit_v + lottery_commit_r"), &lottery_commit_r)?;
+
+        // Hash the coordinates to get a base field element
+        let y_commit_base: AssignedCell<pallas::Base, pallas::Base> = {
+            let poseidon_message = [y_commit.inner().x(), y_commit.inner().y()];
+            let poseidon_hasher = PoseidonHash::<_, _, P128Pow5T3, ConstantLength<2>, 3, 2>::init(
+                config.poseidon_chip(),
+                layouter.namespace(|| "lottery_commit coords poseidon init"),
             )?;
             )?;
-            let y_commit_r =
-                FixedPoint::from_inner(ecc_chip.clone(), OrchardFixedBasesFull::ValueCommitR);
-            y_commit_r.mul(layouter.namespace(|| "coin serial number commit R"), mau_y)?
-        };
-        let y_commit = com.add(layouter.namespace(|| "nonce commit"), &blind)?;
-        let y_commit_base: AssignedCell<Fp, Fp> = {
-            let y_commit_base_x = y_commit.inner().x();
-            let y_commit_base_y = y_commit.inner().y();
-            let y_coord = [y_commit_base_x, y_commit_base_y];
-            let poseidon_hasher = PoseidonHash::<
-                _,
-                _,
-                poseidon::P128Pow5T3,
-                poseidon::ConstantLength<2>,
-                3,
-                2,
-            >::init(
-                config.poseidon_chip(), layouter.namespace(|| "Poseidon init")
+            let poseidon_output = poseidon_hasher.hash(
+                layouter.namespace(|| "lottery_commit coords poseidon hash"),
+                poseidon_message,
             )?;
             )?;
-
-            let poseidon_output =
-                poseidon_hasher.hash(layouter.namespace(|| "Poseidon hash"), y_coord)?;
-            let poseidon_output: AssignedCell<Fp, Fp> = poseidon_output;
-            poseidon_output
+            poseidon_output.into()
         };
         };
-        // constraint rho as COMIT(PRF(root_sk||nonce), rho_mu)
-        // r*G_2
-        let (blind, _) = {
+
+        // y_commit also becomes V of the following pedersen commitment for rho
+        let (rho_cm, _) = {
             let mau_rho = ScalarFixed::new(
             let mau_rho = ScalarFixed::new(
                 ecc_chip.clone(),
                 ecc_chip.clone(),
                 layouter.namespace(|| "mau_rho scalar"),
                 layouter.namespace(|| "mau_rho scalar"),
                 self.mau_rho,
                 self.mau_rho,
             )?;
             )?;
             let rho_commit_r =
             let rho_commit_r =
-                FixedPoint::from_inner(ecc_chip.clone(), OrchardFixedBasesFull::ValueCommitR);
+                FixedPoint::from_inner(ecc_chip.clone(), ValueCommitR);
             rho_commit_r.mul(layouter.namespace(|| "coin serial number commit R"), mau_rho)?
             rho_commit_r.mul(layouter.namespace(|| "coin serial number commit R"), mau_rho)?
         };
         };
-        let rho_commit = com.add(layouter.namespace(|| "nonce commit"), &blind)?;
-        let rho = NonIdentityPoint::new(
-            ecc_chip.clone(),
-            layouter.namespace(|| "witness rho"),
-            self.rho.map(|x| x.to_affine()),
-        )?;
-        let term1 =
-            ar_chip.mul(layouter.namespace(|| "calculate term1"), &sigma1, &coin_value.clone())?;
+        let rho_commit = lottery_commit_v.add(layouter.namespace(|| "nonce commit"), &rho_cm)?;
+
 
 
-        let term2_1 = ar_chip.mul(
-            layouter.namespace(|| "calculate term2_1"),
+        // Calculate term1 and term2 for the lottery
+        let term1 = arith_chip.mul(
+            layouter.namespace(|| "term1 = sigma1 * coin1_value"),
+            &sigma1,
+            &coin1_value,
+        )?;
+        let term2_1 = arith_chip.mul(
+            layouter.namespace(|| "term2_1 = sigma2 * coin1_value"),
             &sigma2,
             &sigma2,
-            &coin_value.clone(),
+            &coin1_value,
+        )?;
+        let term2 = arith_chip.mul(
+            layouter.namespace(|| "term2 = term2_1 * coin1_value"),
+            &term2_1,
+            &coin1_value,
         )?;
         )?;
 
 
-        let term2 =
-            ar_chip.mul(layouter.namespace(|| "calculate term2"), &term2_1, &coin_value.clone())?;
-
-        let target = ar_chip.add(layouter.namespace(|| "calculate target"), &term1, &term2)?;
-        let target: Value<pallas::Base> = target.value().cloned();
+        // Calculate lottery target
+        let target =
+            arith_chip.add(layouter.namespace(|| "target = term1 + term2"), &term1, &term2)?;
 
 
-        let y: Value<pallas::Base> = y_commit_base.value().cloned();
 
 
-        less_than_chip.witness_less_than(
+        // Constrain y < target
+        lessthan_chip.copy_less_than(
             layouter.namespace(|| "y < target"),
             layouter.namespace(|| "y < target"),
-            y,
+            y_commit_base.clone(),
             target,
             target,
             0,
             0,
             true,
             true,
         )?;
         )?;
 
 
-        layouter.constrain_instance(coin2_nonce.cell(), config.primary, LEAD_COIN_NONCE2_OFFSET)?;
 
 
-        layouter.constrain_instance(coin_pk_x.cell(), config.primary, LEAD_COIN_PK_X_OFFSET)?;
-        layouter.constrain_instance(coin_pk_y.cell(), config.primary, LEAD_COIN_PK_Y_OFFSET)?;
+        // Constrain derived `sn_commit` to be equal to witnessed `coin1_serial`.
+        layouter.assign_region(
+            || "sn_commit equality",
+            |mut region| {
+                region.constrain_equal(sn_commit.cell(), coin1_serial.cell())?;
+                region.constrain_equal(coin1_cm_root.cell(), coin1_commit_root.cell())
+            },
+        );
+
+        // Constrain equality between witnessed and derived commitment
+        coin2_commitment.constrain_equal(layouter.namespace(|| "coin2_commit equality"), &coin2_commit)?;
 
 
+        // Constrain derived rho_commit to witnessed rho
+        rho_commit.constrain_equal(layouter.namespace(|| "rho equality"), &rho)?;
+
+        // Constrain coin's public key coordinates with public inputs
         layouter.constrain_instance(
         layouter.constrain_instance(
-            y_commit_base.cell(),
+            coin_pk.inner().x().cell(),
             config.primary,
             config.primary,
-            LEAD_Y_COMMIT_BASE_OFFSET,
+            LEADCOIN_PK_X_OFFSET,
         )?;
         )?;
-
-        rho_commit.constrain_equal(layouter.namespace(|| ""), &rho)?;
-        let ref_coin2_cm = NonIdentityPoint::new(
-            ecc_chip.clone(),
-            layouter.namespace(|| "witness coin2 cm"),
-            self.coin2_commit.map(|x| x.to_affine()),
+        layouter.constrain_instance(
+            coin_pk.inner().y().cell(),
+            config.primary,
+            LEADCOIN_PK_Y_OFFSET,
         )?;
         )?;
-        coin2_commit.constrain_equal(layouter.namespace(|| ""), &ref_coin2_cm)?;
-        layouter.assign_region(
-            || "",
-            |mut region| {
-                region.constrain_equal(sn_commit.cell(), coin1_sn.cell())?;
-                region.constrain_equal(coin_cm_root.cell(), root_cm.cell())
-            },
+        // Constrain coin2_nonce with associated public input
+        layouter.constrain_instance(
+            coin2_nonce.cell(),
+            config.primary,
+            LEADCOIN_C2_NONCE_OFFSET,
+        )?;
+
+        // Constrain y to its respective public input
+        layouter.constrain_instance(
+            y_commit_base.cell(),
+            config.primary,
+            LEADCOIN_Y_BASE_OFFSET,
         )?;
         )?;
+        Ok(())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::Result;
+    use halo2_proofs::dev::CircuitLayout;
+    use plotters::prelude::*;
+
+    #[test]
+    fn test_leader_circuit() -> Result<()> {
+        let k = 11;
+        let circuit = LeadContract::default();
+
+        let root = BitMapBackend::new("target/leader_circuit_layout.png", (3840, 2160))
+            .into_drawing_area();
+        root.fill(&WHITE).unwrap();
+        let root = root.titled("Lead Circuit Layout", ("sans-serif", 60)).unwrap();
+        CircuitLayout::default()
+            //.view_width(0..10)
+            .render(k, &circuit, &root).unwrap();
 
 
         Ok(())
         Ok(())
     }
     }

+ 0 - 758
src/zk/circuit/lead_contract_opti.rs

@@ -1,758 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2022 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use darkfi_sdk::crypto::{
-    constants::{
-        sinsemilla::{OrchardCommitDomains, OrchardHashDomains},
-        util::gen_const_array,
-        NullifierK, OrchardFixedBases,
-        OrchardFixedBasesFull::ValueCommitR,
-        MERKLE_DEPTH_ORCHARD,
-    },
-    MerkleNode,
-};
-use halo2_gadgets::{
-    ecc::{
-        chip::{EccChip, EccConfig},
-        FixedPoint, FixedPointBaseField, NonIdentityPoint, ScalarFixed,
-    },
-    poseidon::{
-        primitives::{ConstantLength, P128Pow5T3},
-        Hash as PoseidonHash, Pow5Chip as PoseidonChip, Pow5Config as PoseidonConfig,
-    },
-    sinsemilla::{
-        chip::{SinsemillaChip, SinsemillaConfig},
-        merkle::{
-            chip::{MerkleChip, MerkleConfig},
-            MerklePath,
-        },
-    },
-    utilities::lookup_range_check::LookupRangeCheckConfig,
-};
-use halo2_proofs::{
-    circuit::{floor_planner, AssignedCell, Layouter, Value},
-    pasta::{group::Curve, pallas},
-    plonk,
-    plonk::{Advice, Circuit, Column, ConstraintSystem, Instance as InstanceColumn},
-};
-
-use crate::zk::{
-    assign_free_advice,
-    gadget::{
-        arithmetic::{ArithChip, ArithConfig, ArithInstruction},
-        less_than::{LessThanChip, LessThanConfig},
-        native_range_check::NativeRangeCheckChip,
-    },
-};
-
-/// Public input offset for lead coin public key X coordinate
-const LEADCOIN_PK_X_OFFSET: usize = 0;
-/// Public input offset for lead coin public key Y coordinate
-const LEADCOIN_PK_Y_OFFSET: usize = 1;
-/// Public input offset for the lead coin C2 nonce
-const LEADCOIN_C2_NONCE_OFFSET: usize = 2;
-/// Public input offset for the lottery target lhs
-const LEADCOIN_Y_BASE_OFFSET: usize = 3;
-/// Derivation prefix for the nullifier PRF
-const PRF_NULLIFIER_PREFIX: u64 = 0;
-
-/// Circuit configuration for the crypsinous leader proof
-#[derive(Clone, Debug)]
-pub struct LeaderConfig {
-    primary: Column<InstanceColumn>,
-    advices: [Column<Advice>; 14],
-    ecc_config: EccConfig<OrchardFixedBases>,
-    poseidon_config: PoseidonConfig<pallas::Base, 3, 2>,
-    sinsemilla_config_1:
-        SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
-    sinsemilla_config_2:
-        SinsemillaConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
-    merkle_config_1: MerkleConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
-    merkle_config_2: MerkleConfig<OrchardHashDomains, OrchardCommitDomains, OrchardFixedBases>,
-    lessthan_config: LessThanConfig<3, 253, 85>,
-    arith_config: ArithConfig,
-}
-
-impl LeaderConfig {
-    fn ecc_chip(&self) -> EccChip<OrchardFixedBases> {
-        EccChip::construct(self.ecc_config.clone())
-    }
-
-    fn poseidon_chip(&self) -> PoseidonChip<pallas::Base, 3, 2> {
-        PoseidonChip::construct(self.poseidon_config.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 lessthan_chip(&self) -> LessThanChip<3, 253, 85> {
-        LessThanChip::construct(self.lessthan_config.clone())
-    }
-
-    fn arith_chip(&self) -> ArithChip {
-        ArithChip::construct(self.arith_config.clone())
-    }
-}
-
-/// Circuit implementation for the crypsinous leader proof
-#[derive(Default, Clone, Debug)]
-pub struct LeaderCircuit {
-    /// Merkle path to the commitment for `coin_1`
-    pub coin1_commit_merkle_path: Value<[MerkleNode; MERKLE_DEPTH_ORCHARD]>,
-    /// Merkle root to the commitment of `coin_1` in the Merkle tree of commitments
-    pub coin1_commitment_root: Value<pallas::Base>,
-    /// `coin_1` leaf position in the Merkle tree of coin commitments
-    pub coin1_leaf_pos: Value<u32>,
-    /// `coin_1` secret key.
-    pub coin1_sk: Value<pallas::Base>,
-    /// Merkle root of the `coin_1` secret key in the Merkle tree of secret keys.
-    pub coin1_sk_root: Value<pallas::Base>,
-    /// Merkle path to the secret key of `coin_1` in the Merkle tree of secret keys.
-    pub coin1_sk_merkle_path: Value<[MerkleNode; MERKLE_DEPTH_ORCHARD]>,
-    /// $\tau$ (in the crypsinous paper), can be slot index, or coin timestamp.
-    /// Used only in the public key calculation.
-    pub coin1_timestamp: Value<pallas::Base>,
-    /// `coin_1` nonce, a random sampled value. `coin_2` nonce is calculated
-    /// inside the circuit as `Hash(coin1_nonce || coin1_sk_root)`, assuming
-    /// `Hash` is a function that satisfies a PRF definition.
-    pub coin1_nonce: Value<pallas::Base>,
-    /// Blinding factor for the commitment of `coin_1`
-    pub coin1_blind: Value<pallas::Scalar>,
-    /// Serial number for `coin_1`
-    pub coin1_serial: Value<pallas::Base>,
-    /// Value of `coin_1`
-    pub coin1_value: Value<pallas::Base>,
-    /// Blinding factor for the commitment of `coin_2`
-    pub coin2_blind: Value<pallas::Scalar>,
-    /// `coin_2` pedersen commitment point
-    pub coin2_commit: Value<pallas::Point>,
-    /// eta is calculated as a random value using some parameters.
-    /// It is defined as true randomness from global random clock.
-    pub eta: Value<pallas::Base>,
-    /// Random value derived from `eta` used for constraining `rho`
-    pub mau_rho: Value<pallas::Scalar>,
-    /// Random value derived from `eta` used for calculating `y`.
-    pub mau_y: Value<pallas::Scalar>,
-    /// First coefficient in 1-term T (target function) approximation.
-    /// sigma1 and sigma2 is not the capital sigma from the paper, but
-    /// the whole coefficient multiplied with the absolute stake.
-    pub sigma1: Value<pallas::Base>,
-    /// Second coefficient in 2-term T (target function) approximation
-    pub sigma2: Value<pallas::Base>,
-    /// Constrained nonce `rho`.
-    pub rho: Value<pallas::Point>,
-}
-
-impl Circuit<pallas::Base> for LeaderCircuit {
-    type Config = LeaderConfig;
-    type FloorPlanner = floor_planner::V1;
-
-    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(),
-            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);
-
-        // Permutation over all advice columns
-        for advice in advices.iter() {
-            meta.enable_equality(*advice);
-        }
-
-        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.
-        let ecc_config = EccChip::<OrchardFixedBases>::configure(
-            meta,
-            advices[..10].try_into().unwrap(),
-            lagrange_coeffs,
-            range_check,
-        );
-
-        let poseidon_config = PoseidonChip::configure::<P128Pow5T3>(
-            meta,
-            advices[10..13].try_into().unwrap(),
-            advices[13],
-            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,
-            );
-            let merkle_config_1 = MerkleChip::configure(meta, sinsemilla_config_1.clone());
-            (sinsemilla_config_1, merkle_config_1)
-        };
-
-        let (sinsemilla_config_2, merkle_config_2) = {
-            let sinsemilla_config_2 = SinsemillaChip::configure(
-                meta,
-                advices[5..10].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)
-        };
-
-        // Lookup table for native range checks and less-than check
-        let k_values_table = meta.lookup_table_column();
-        let lessthan_config = {
-            /*
-            let a = advices[10];
-            let b = advices[11];
-            let a_offset = advices[12];
-            let z1 = advices[13];
-            let z2 = advices[14];
-            */
-            let a = advices[9];
-            let b = advices[10];
-            let a_offset = advices[11];
-            let z1 = advices[12];
-            let z2 = advices[13];
-            let constants = meta.fixed_column();
-            meta.enable_constant(constants);
-            meta.enable_equality(a);
-            meta.enable_equality(b);
-            meta.enable_equality(a_offset);
-
-            LessThanChip::<3, 253, 85>::configure(meta, a, b, a_offset, z1, z2, k_values_table)
-        };
-
-        // Configuration for the arithmetic chip
-        let arith_config = ArithChip::configure(meta, advices[7], advices[8], advices[6]);
-
-        LeaderConfig {
-            primary,
-            advices,
-            ecc_config,
-            poseidon_config,
-            sinsemilla_config_1,
-            sinsemilla_config_2,
-            merkle_config_1,
-            merkle_config_2,
-            lessthan_config,
-            arith_config,
-        }
-    }
-
-    fn synthesize(
-        &self,
-        config: Self::Config,
-        mut layouter: impl Layouter<pallas::Base>,
-    ) -> Result<(), plonk::Error> {
-        // Initialize necessary chips
-        let lessthan_chip = config.lessthan_chip();
-        NativeRangeCheckChip::<3, 253, 85>::load_k_table(
-            &mut layouter,
-            config.lessthan_config.k_values_table,
-        )?;
-        SinsemillaChip::load(config.sinsemilla_config_1.clone(), &mut layouter)?;
-        let ecc_chip = config.ecc_chip();
-        let arith_chip = config.arith_chip();
-
-        // ====================================
-        // Load witness values into the circuit
-        // ====================================
-        let prf_nullifier_prefix_base = assign_free_advice(
-            layouter.namespace(|| "witness nullifier prefix"),
-            config.advices[8],
-            Value::known(pallas::Base::from(PRF_NULLIFIER_PREFIX)),
-        )?;
-
-        let coin1_commit_merkle_path: Value<[pallas::Base; MERKLE_DEPTH_ORCHARD]> = self
-            .coin1_commit_merkle_path
-            .map(|typed_path| gen_const_array(|i| typed_path[i].inner()));
-
-        let coin1_commitment_root = assign_free_advice(
-            layouter.namespace(|| "witness coin_commitment_root"),
-            config.advices[8],
-            self.coin1_commitment_root,
-        )?;
-
-        let coin1_sk = assign_free_advice(
-            layouter.namespace(|| "witness coin1_sk"),
-            config.advices[8],
-            self.coin1_sk,
-        )?;
-
-        let coin1_sk_root = assign_free_advice(
-            layouter.namespace(|| "witness coin1_sk_root"),
-            config.advices[8],
-            self.coin1_sk_root,
-        )?;
-
-        let coin1_sk_merkle_path: Value<[pallas::Base; MERKLE_DEPTH_ORCHARD]> =
-            self.coin1_sk_merkle_path.map(|typed_path| gen_const_array(|i| typed_path[i].inner()));
-
-        let coin1_timestamp = assign_free_advice(
-            layouter.namespace(|| "witness coin1_timestamp"),
-            config.advices[8],
-            self.coin1_timestamp,
-        )?;
-
-        let coin1_nonce = assign_free_advice(
-            layouter.namespace(|| "witness coin1_nonce"),
-            config.advices[8],
-            self.coin1_nonce,
-        )?;
-
-        let coin1_blind = ScalarFixed::new(
-            ecc_chip.clone(),
-            layouter.namespace(|| "witness coin1_blind"),
-            self.coin1_blind,
-        )?;
-
-        let coin1_serial = assign_free_advice(
-            layouter.namespace(|| "witness coin1_serial"),
-            config.advices[8],
-            self.coin1_serial,
-        )?;
-
-        let coin1_value = assign_free_advice(
-            layouter.namespace(|| "witness coin1_value"),
-            config.advices[8],
-            self.coin1_value,
-        )?;
-
-        let coin2_blind = ScalarFixed::new(
-            ecc_chip.clone(),
-            layouter.namespace(|| "witness coin2_blind"),
-            self.coin2_blind,
-        )?;
-
-        let coin2_commit = NonIdentityPoint::new(
-            ecc_chip.clone(),
-            layouter.namespace(|| "witness coin2_commit"),
-            self.coin2_commit.as_ref().map(|cm| cm.to_affine()),
-        )?;
-
-        let eta = assign_free_advice(
-            layouter.namespace(|| "witness eta randomness"),
-            config.advices[8],
-            self.eta,
-        )?;
-
-        let mau_rho = ScalarFixed::new(
-            ecc_chip.clone(),
-            layouter.namespace(|| "witness mau_rho"),
-            self.mau_rho,
-        )?;
-
-        let mau_y =
-            ScalarFixed::new(ecc_chip.clone(), layouter.namespace(|| "witness mau_y"), self.mau_y)?;
-
-        let sigma1 = assign_free_advice(
-            layouter.namespace(|| "witness sigma1"),
-            config.advices[8],
-            self.sigma1,
-        )?;
-
-        let sigma2 = assign_free_advice(
-            layouter.namespace(|| "witness sigma2"),
-            config.advices[8],
-            self.sigma2,
-        )?;
-
-        let rho = NonIdentityPoint::new(
-            ecc_chip.clone(),
-            layouter.namespace(|| "witness rho"),
-            self.rho.as_ref().map(|cm| cm.to_affine()),
-        )?;
-
-        let zero = assign_free_advice(
-            layouter.namespace(|| "witness constant zero"),
-            config.advices[8],
-            Value::known(pallas::Base::zero()),
-        )?;
-
-        let one = assign_free_advice(
-            layouter.namespace(|| "witness constant one"),
-            config.advices[8],
-            Value::known(pallas::Base::one()),
-        )?;
-
-        // ========================
-        // Derive coin's public key
-        // ========================
-        let coin_pk = {
-            let coin_pk_commit_v = FixedPointBaseField::from_inner(ecc_chip.clone(), NullifierK);
-            coin_pk_commit_v.mul(layouter.namespace(|| "coin_1sk * NullifierK"), coin1_sk)?
-        };
-
-        // Constrain coin's public key coordinates with public inputs
-        layouter.constrain_instance(
-            coin_pk.inner().x().cell(),
-            config.primary,
-            LEADCOIN_PK_X_OFFSET,
-        )?;
-        layouter.constrain_instance(
-            coin_pk.inner().y().cell(),
-            config.primary,
-            LEADCOIN_PK_Y_OFFSET,
-        )?;
-
-        // Coin `c1` serial number:
-        // sn=PRF_{root_sk}(nonce)
-        // Coin's serial number is derived from coin nonce (sampled at random)
-        // and root of the coin's secret key sampled at random.
-        let sn_commit: AssignedCell<pallas::Base, pallas::Base> = {
-            // For derivation here, we append one 0 and one 1 to the hashed message.
-            // TODO: Add these constants to ouroboros/consts.rs
-            let poseidon_message =
-                [coin1_nonce.clone(), coin1_sk_root.clone(), zero.clone(), one.clone()];
-            let poseidon_hasher = PoseidonHash::<_, _, P128Pow5T3, ConstantLength<4>, 3, 2>::init(
-                config.poseidon_chip(),
-                layouter.namespace(|| "sn_commit poseidon init"),
-            )?;
-            let poseidon_output = poseidon_hasher
-                .hash(layouter.namespace(|| "sn_commit poseidon hash"), poseidon_message)?;
-            poseidon_output.into()
-        };
-
-        // Constrain derived `sn_commit` to be equal to witnessed `coin1_serial`.
-        layouter.assign_region(
-            || "sn_commit equality",
-            |mut region| region.constrain_equal(sn_commit.cell(), coin1_serial.cell()),
-        )?;
-
-        // ==============================
-        // Commitment to the staking coin
-        // ==============================
-        // coin commitment H=Commit(PRF(prefix||pk||V||nonce), r)
-        let coin_commitment_v = {
-            // Coin c1 nullifier is a commitment of the following:
-            let nullifier_msg: AssignedCell<pallas::Base, pallas::Base> = {
-                let poseidon_message = [
-                    prf_nullifier_prefix_base.clone(),
-                    coin_pk.inner().x(),
-                    coin_pk.inner().y(),
-                    coin1_value.clone(),
-                    coin1_nonce.clone(),
-                    one.clone(), // One is here because of poseidon odd-n bug
-                ];
-                let poseidon_hasher =
-                    PoseidonHash::<_, _, P128Pow5T3, ConstantLength<6>, 3, 2>::init(
-                        config.poseidon_chip(),
-                        layouter.namespace(|| "nullifier poseidon init"),
-                    )?;
-                let poseidon_output = poseidon_hasher
-                    .hash(layouter.namespace(|| "nullifier poseidon hash"), poseidon_message)?;
-                poseidon_output.into()
-            };
-
-            let v = FixedPointBaseField::from_inner(ecc_chip.clone(), NullifierK);
-            v.mul(layouter.namespace(|| "nullifier_msg * NullifierK"), nullifier_msg)?
-        };
-
-        let (coin_commitment_r, _) = {
-            let r = FixedPoint::from_inner(ecc_chip.clone(), ValueCommitR);
-            r.mul(layouter.namespace(|| "coin1_blind * ValueCommitR"), coin1_blind)?
-        };
-
-        let coin_commitment = coin_commitment_v.add(
-            layouter.namespace(|| "coin_commitment_v + coin_commitment_r"),
-            &coin_commitment_r,
-        )?;
-
-        // ================================================
-        // Validate Merkle path to staked coin's commitment
-        // ================================================
-        let merkle_inputs = MerklePath::construct(
-            [config.merkle_chip_1(), config.merkle_chip_2()],
-            OrchardHashDomains::MerkleCrh,
-            self.coin1_leaf_pos,
-            coin1_commit_merkle_path,
-        );
-
-        let coin1_commit_hash: AssignedCell<pallas::Base, pallas::Base> = {
-            let poseidon_message = [coin_commitment.inner().x(), coin_commitment.inner().y()];
-            let poseidon_hasher = PoseidonHash::<_, _, P128Pow5T3, ConstantLength<2>, 3, 2>::init(
-                config.poseidon_chip(),
-                layouter.namespace(|| "coin1_commit_hash poseidon init"),
-            )?;
-            let poseidon_output = poseidon_hasher
-                .hash(layouter.namespace(|| "coin1_commit_hash poseidon hash"), poseidon_message)?;
-            poseidon_output.into()
-        };
-
-        let coin1_cm_root = merkle_inputs.calculate_root(
-            layouter.namespace(|| "calculate merkle root for coin1 commitment"),
-            coin1_commit_hash,
-        )?;
-
-        // Constrain equality of derived root with witnessed root
-        layouter.assign_region(
-            || "coin1_cm_root equality",
-            |mut region| region.constrain_equal(coin1_cm_root.cell(), coin1_commitment_root.cell()),
-        )?;
-
-        // ===========================
-        // Derivation of coin2's nonce
-        // ===========================
-        let coin2_nonce: AssignedCell<pallas::Base, pallas::Base> = {
-            // For derivation here, we append 1 two times to the hashed message.
-            // TODO: Add these constants to ouroboros/consts.rs
-            let poseidon_message =
-                [coin1_nonce.clone(), coin1_sk_root.clone(), one.clone(), one.clone()];
-            let poseidon_hasher = PoseidonHash::<_, _, P128Pow5T3, ConstantLength<4>, 3, 2>::init(
-                config.poseidon_chip(),
-                layouter.namespace(|| "coin2_nonce poseidon init"),
-            )?;
-            let poseidon_output = poseidon_hasher
-                .hash(layouter.namespace(|| "coin2_nonce poseidon hash"), poseidon_message)?;
-            poseidon_output.into()
-        };
-
-        // Constrain coin2_nonce with associated public input
-        layouter.constrain_instance(
-            coin2_nonce.cell(),
-            config.primary,
-            LEADCOIN_C2_NONCE_OFFSET,
-        )?;
-
-        // ================
-        // Coin2 commitment
-        // ================
-        // H=Commit(PRF(pk||V||nonce2), r2)
-        // Poured coin's commitment is a nullifier
-        let coin2_commitment_v = {
-            // coin2's commitment input body as a poseidon hash of input
-            // concatenation of public key, stake, and poured coin's nonce.
-            let nullifier2: AssignedCell<pallas::Base, pallas::Base> = {
-                let poseidon_message = [
-                    prf_nullifier_prefix_base,
-                    coin_pk.inner().x(),
-                    coin_pk.inner().y(),
-                    coin1_value.clone(),
-                    coin2_nonce.clone(),
-                    one.clone(), // Used here because of poseidon odd-n bug
-                ];
-                let poseidon_hasher =
-                    PoseidonHash::<_, _, P128Pow5T3, ConstantLength<6>, 3, 2>::init(
-                        config.poseidon_chip(),
-                        layouter.namespace(|| "coin2_commitment_v poseidon init"),
-                    )?;
-                let poseidon_output = poseidon_hasher.hash(
-                    layouter.namespace(|| "coin2_commitment_v poseidon hash"),
-                    poseidon_message,
-                )?;
-                poseidon_output.into()
-            };
-
-            let v = FixedPointBaseField::from_inner(ecc_chip.clone(), NullifierK);
-            v.mul(layouter.namespace(|| "nullifier2 * NullifierK"), nullifier2)?
-        };
-
-        let (coin2_commitment_r, _) = {
-            let r = FixedPoint::from_inner(ecc_chip.clone(), ValueCommitR);
-            r.mul(layouter.namespace(|| "coin2_blind * ValueCommitR"), coin2_blind)?
-        };
-
-        let coin2_commitment = coin2_commitment_v.add(
-            layouter.namespace(|| "coin2_commitment_v + coin2_commitment_r"),
-            &coin2_commitment_r,
-        )?;
-
-        // Constrain equality between witnessed and derived commitment
-        coin2_commitment
-            .constrain_equal(layouter.namespace(|| "coin2_commit equality"), &coin2_commit)?;
-
-        // ==================================
-        // lhs of the leader election lottery
-        // ==================================
-        // * y as Commit(root_sk||nonce, mau_y)
-        // Commitment to the coin's secret key, coin's nonce, and random value
-        // derived from the epoch sampled random eta.
-        let lottery_commit_msg: AssignedCell<pallas::Base, pallas::Base> = {
-            let poseidon_message = [coin1_sk_root.clone(), coin1_nonce.clone()];
-            let poseidon_hasher = PoseidonHash::<_, _, P128Pow5T3, ConstantLength<2>, 3, 2>::init(
-                config.poseidon_chip(),
-                layouter.namespace(|| "lottery_commit_msg poseidon init"),
-            )?;
-            let poseidon_output = poseidon_hasher.hash(
-                layouter.namespace(|| "lottery_commit_msg poseidon hash"),
-                poseidon_message,
-            )?;
-            poseidon_output.into()
-        };
-
-        let lottery_commit_v = {
-            let v = FixedPointBaseField::from_inner(ecc_chip.clone(), NullifierK);
-            v.mul(layouter.namespace(|| "lottery_commit_msg * NullifierK"), lottery_commit_msg)?
-        };
-
-        let (lottery_commit_r, _) = {
-            let r = FixedPoint::from_inner(ecc_chip.clone(), ValueCommitR);
-            r.mul(layouter.namespace(|| "mau_y * ValueCommitR"), mau_y)?
-        };
-
-        let y_commit = lottery_commit_v
-            .add(layouter.namespace(|| "lottery_commit_v + lottery_commit_r"), &lottery_commit_r)?;
-
-        // Hash the coordinates to get a base field element
-        let y_commit_base: AssignedCell<pallas::Base, pallas::Base> = {
-            let poseidon_message = [y_commit.inner().x(), y_commit.inner().y()];
-            let poseidon_hasher = PoseidonHash::<_, _, P128Pow5T3, ConstantLength<2>, 3, 2>::init(
-                config.poseidon_chip(),
-                layouter.namespace(|| "lottery_commit coords poseidon init"),
-            )?;
-            let poseidon_output = poseidon_hasher.hash(
-                layouter.namespace(|| "lottery_commit coords poseidon hash"),
-                poseidon_message,
-            )?;
-            poseidon_output.into()
-        };
-
-        // Constrain y to its respective public input
-        layouter.constrain_instance(
-            y_commit_base.cell(),
-            config.primary,
-            LEADCOIN_Y_BASE_OFFSET,
-        )?;
-
-        // y_commit also becomes V of the following pedersen commitment for rho
-        let (rho_commit_r, _) = {
-            let r = FixedPoint::from_inner(ecc_chip.clone(), ValueCommitR);
-            r.mul(layouter.namespace(|| "mau_rho * ValueCommitR"), mau_rho)?
-        };
-
-        let rho_commit =
-            y_commit.add(layouter.namespace(|| "y_commit + rho_commit_r"), &rho_commit_r)?;
-
-        // Constrain derived rho_commit to witnessed rho
-        rho_commit.constrain_equal(layouter.namespace(|| "rho equality"), &rho)?;
-
-        // Calculate term1 and term2 for the lottery
-        let term1 = arith_chip.mul(
-            layouter.namespace(|| "term1 = sigma1 * coin1_value"),
-            &sigma1,
-            &coin1_value,
-        )?;
-        let term2_1 = arith_chip.mul(
-            layouter.namespace(|| "term2_1 = sigma2 * coin1_value"),
-            &sigma2,
-            &coin1_value,
-        )?;
-        let term2 = arith_chip.mul(
-            layouter.namespace(|| "term2 = term2_1 * coin1_value"),
-            &term2_1,
-            &coin1_value,
-        )?;
-
-        // Calculate lottery target
-        let target =
-            arith_chip.add(layouter.namespace(|| "target = term1 + term2"), &term1, &term2)?;
-
-        // Constrain y < target
-        lessthan_chip.copy_less_than(
-            layouter.namespace(|| "y < target"),
-            y_commit_base,
-            target,
-            0,
-            true,
-        )?;
-
-        // At this point we've enforced all of our constraints
-        Ok(())
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::Result;
-    use halo2_proofs::dev::CircuitLayout;
-    use plotters::prelude::*;
-
-    #[test]
-    fn test_leader_circuit() -> Result<()> {
-        let k = 11;
-        let circuit = LeaderCircuit::default();
-
-        let root = BitMapBackend::new("target/leader_circuit_layout.png", (3840, 2160))
-            .into_drawing_area();
-        root.fill(&WHITE).unwrap();
-        let root = root.titled("Leader Circuit Layout", ("sans-serif", 60)).unwrap();
-        CircuitLayout::default().render(k, &circuit, &root).unwrap();
-
-        Ok(())
-    }
-}