Просмотр исходного кода

consensus: Begin leadcoin API cleanup.

parazyd 3 лет назад
Родитель
Сommit
ff5d31ca3d

+ 1 - 7
src/blockchain/mod.rs

@@ -62,8 +62,6 @@ pub struct Blockchain {
 }
 
 impl Blockchain {
-    //FIXME why the blockchain taking genesis_data on the constructor as a hash?
-    //genesis data are supposed to be a a hash?
     /// Instantiate a new `Blockchain` with the given `sled` database.
     pub fn new(db: &sled::Db, genesis_ts: Timestamp, genesis_data: blake3::Hash) -> Result<Self> {
         let headers = HeaderStore::new(db, genesis_ts, genesis_data)?;
@@ -94,6 +92,7 @@ impl Blockchain {
     pub fn add(&self, blocks: &[BlockInfo]) -> Result<Vec<blake3::Hash>> {
         let mut ret = Vec::with_capacity(blocks.len());
 
+        // TODO: Make db writes here completely atomic
         for block in blocks {
             // Store transactions
             let _tx_hashes = self.transactions.insert(&block.txs)?;
@@ -103,16 +102,11 @@ impl Blockchain {
             ret.push(headerhash[0]);
 
             // Store block
-            //let _block = Block::new(headerhash[0], tx_hashes, block.m.clone());
-            //self.blocks.insert(&[_block])?;
             let blk: Block = Block::from(block.clone());
             self.blocks.insert(&[blk])?;
 
             // Store block order
             self.order.insert(&[block.header.slot], &[headerhash[0]])?;
-
-            // NOTE: The nullifiers and Merkle roots are applied in the state
-            // transition apply function.
         }
 
         Ok(ret)

+ 13 - 159
src/consensus/coins.rs

@@ -22,7 +22,7 @@ use darkfi_sdk::{
         pedersen::{pedersen_commitment_base, pedersen_commitment_u64},
         poseidon_hash,
         util::mod_r_p,
-        Keypair, MerkleNode, Nullifier, SecretKey, TokenId,
+        MerkleNode, Nullifier, SecretKey, TokenId,
     },
     incrementalmerkletree::{bridgetree::BridgeTree, Tree},
     pasta::{
@@ -37,13 +37,12 @@ use log::info;
 use rand::{rngs::OsRng, thread_rng, Rng};
 
 use super::{
-    utils::fbig2base, Float10, EPOCH_LENGTH, LOTTERY_HEAD_START, P, PRF_NULLIFIER_PREFIX,
-    RADIX_BITS, REWARD,
+    leadcoin::LeadCoin, utils::fbig2base, Float10, EPOCH_LENGTH, LOTTERY_HEAD_START, P, RADIX_BITS,
+    REWARD,
 };
 use crate::{
     crypto::{
         coin::{Coin, OwnCoin},
-        leadcoin::LeadCoin,
         note::Note,
         types::{DrkCoinBlind, DrkSerial, DrkValueBlind},
     },
@@ -129,7 +128,7 @@ fn create_coins(
             let index = i as usize;
             let mut slot_coins = vec![];
             for elem in owned {
-                let coin = create_leadcoin(
+                let coin = LeadCoin::new(
                     eta,
                     sigma1,
                     sigma2,
@@ -150,7 +149,7 @@ fn create_coins(
         for i in 0..*EPOCH_LENGTH {
             let index = i as usize;
             // Compete with zero stake
-            let coin = create_leadcoin(
+            let coin = LeadCoin::new(
                 eta,
                 sigma1,
                 sigma2,
@@ -211,146 +210,6 @@ fn create_coins_sks() -> (Vec<SecretKey>, Vec<MerkleNode>, Vec<[MerkleNode; MERK
     (sks, root_sks, path_sks)
 }
 
-/// Generate lead coin for provided sigmas and secret keys.
-fn create_leadcoin(
-    eta: pallas::Base,
-    sigma1: pallas::Base,
-    sigma2: pallas::Base,
-    value: u64,
-    i: usize,
-    c_root_sk: MerkleNode,
-    c_path_sk: [MerkleNode; MERKLE_DEPTH_ORCHARD],
-    seed: u64,
-    sk: SecretKey,
-    tree_cm: &mut BridgeTree<MerkleNode, MERKLE_DEPTH>,
-) -> LeadCoin {
-    // keypair
-    let keypair: Keypair = Keypair::new(sk);
-    //random commitment blinding values
-    let mut rng = thread_rng();
-    let one = pallas::Base::one();
-    let zero = pallas::Base::zero();
-    let c_cm1_blind: DrkValueBlind = pallas::Scalar::random(&mut rng);
-    let c_cm2_blind: DrkValueBlind = pallas::Scalar::random(&mut rng);
-
-    let c_v = pallas::Base::from(value);
-    // coin relative slot index in the epoch
-    let c_sl = pallas::Base::from(u64::try_from(i).unwrap());
-    //
-    //let's assume it's sl for simplicity
-    let c_tau = pallas::Base::from(u64::try_from(i).unwrap());
-    //
-
-    //let coin_pk_msg = [c_tau, c_root_sk.inner()];
-    //let c_pk: pallas::Base = poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<2>, 3, 2>::init().hash(coin_pk_msg);
-
-    let c_pk: pallas::Point = keypair.public.inner();
-    let c_pk_coord = c_pk.to_affine().coordinates().unwrap();
-    let c_pk_x = c_pk_coord.x();
-    let c_pk_y = c_pk_coord.y();
-    info!("coin pk [{}] x: {:?}", i, c_pk_x);
-    info!("coin pk [{}] y: {:?}", i, c_pk_y);
-
-    let c_seed = pallas::Base::from(seed);
-    let sn_msg = [c_seed, c_root_sk.inner(), zero, one];
-    let c_sn: pallas::Base =
-        poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<4>, 3, 2>::init()
-            .hash(sn_msg);
-
-    let coin_commit_msg_input =
-        [pallas::Base::from(*PRF_NULLIFIER_PREFIX), *c_pk_x, *c_pk_y, c_v, c_seed, one];
-    let coin_commit_msg: pallas::Base =
-        poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<6>, 3, 2>::init()
-            .hash(coin_commit_msg_input);
-    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_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);
-    tree_cm.append(&c_cm_node.clone());
-    let leaf_position = tree_cm.witness().unwrap();
-    let leaf_position_usize: usize = leaf_position.into();
-    //info!("leaf position odd parity: {:?}", leaf_position.is_odd());
-    let c_root_cm = tree_cm.root(0).unwrap();
-    let c_cm_path = tree_cm.authentication_path(leaf_position, &c_root_cm).unwrap();
-
-    /*
-    let c_root_cm = {
-        let mut current = MerkleNode::from(c_cm_base);
-        let pos = leaf_position.unwrap();
-        for (level, sibling) in c_cm_path.iter().enumerate() {
-            let level = level as u8;
-            current = if i & (1 << level) == 0 {
-                MerkleNode::combine(level.into(), &current, sibling)
-            } else {
-                MerkleNode::combine(level.into(), sibling, &current)
-            };
-        }
-        current
-    };
-    */
-
-    let coin_nonce2_msg = [c_seed, c_root_sk.inner(), one, one];
-    let c_seed2: pallas::Base =
-        poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<4>, 3, 2>::init()
-            .hash(coin_nonce2_msg);
-    info!("coin2 seed [{}] : {:?}", i, c_seed2);
-    let coin2_commit_msg_input =
-        [pallas::Base::from(*PRF_NULLIFIER_PREFIX), *c_pk_x, *c_pk_y, c_v, c_seed2, one];
-    let coin2_commit_msg: pallas::Base =
-        poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<6>, 3, 2>::init()
-            .hash(coin2_commit_msg_input);
-    let c_cm2 = pedersen_commitment_base(coin2_commit_msg, c_cm2_blind);
-
-    // election seeds
-    let (y_mu, rho_mu) = create_coins_election_seeds(eta, c_sl);
-    let coin = LeadCoin {
-        value: Some(value),
-        cm: Some(c_cm),
-        cm2: Some(c_cm2),
-        idx: u32::try_from(leaf_position_usize).unwrap(), //TODO should be abs slot
-        sl: Some(c_sl),
-        tau: Some(c_tau),
-        nonce: Some(c_seed),
-        nonce_cm: Some(c_seed2),
-        sn: Some(c_sn),
-        keypair: Some(keypair),
-        root_cm: Some(c_root_cm.inner()),
-        root_sk: Some(c_root_sk.inner()),
-        path: Some(c_cm_path.as_slice().try_into().unwrap()),
-        path_sk: Some(c_path_sk),
-        c1_blind: Some(c_cm1_blind),
-        c2_blind: Some(c_cm2_blind),
-        y_mu: Some(y_mu),
-        rho_mu: Some(rho_mu),
-        sigma1: Some(sigma1),
-        sigma2: Some(sigma2),
-    };
-    coin
-}
-
-fn create_coins_election_seeds(
-    eta: pallas::Base,
-    slot: pallas::Base,
-) -> (pallas::Base, pallas::Base) {
-    let election_seed_nonce: pallas::Base = pallas::Base::from(3);
-    let election_seed_lead: pallas::Base = pallas::Base::from(22);
-
-    // mu_rho
-    let nonce_mu_msg = [election_seed_nonce, eta, slot];
-    let nonce_mu: pallas::Base =
-        poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<3>, 3, 2>::init()
-            .hash(nonce_mu_msg);
-    // mu_y
-    let lead_mu_msg = [election_seed_lead, eta, slot];
-    let lead_mu: pallas::Base =
-        poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<3>, 3, 2>::init()
-            .hash(lead_mu_msg);
-    (lead_mu, nonce_mu)
-}
-
 /// Check that the provided participant/stakeholder coins win the slot lottery.
 /// If the stakeholder have multiple competing winning coins, only the highest value coin is selected,
 /// since the stakeholder can't give more than a proof per block(slot).
@@ -366,11 +225,9 @@ pub fn is_leader(slot: u64, epoch_coins: &Vec<Vec<LeadCoin>>) -> (bool, usize) {
     let mut highest_stake = 0;
     let mut highest_stake_idx: usize = 0;
     for (winning_idx, coin) in competing_coins.iter().enumerate() {
-        let y_exp = [coin.root_sk.unwrap(), coin.nonce.unwrap()];
-        let y_exp_hash: pallas::Base =
-            poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<2>, 3, 2>::init()
-                .hash(y_exp);
-        let y_coordinates = pedersen_commitment_base(y_exp_hash, mod_r_p(coin.y_mu.unwrap()))
+        let y_exp = [coin.coin1_sk_root.inner(), coin.nonce];
+        let y_exp_hash = poseidon_hash(y_exp);
+        let y_coordinates = pedersen_commitment_base(y_exp_hash, mod_r_p(coin.y_mu))
             .to_affine()
             .coordinates()
             .unwrap();
@@ -378,13 +235,10 @@ pub fn is_leader(slot: u64, epoch_coins: &Vec<Vec<LeadCoin>>) -> (bool, usize) {
         let y_x: pallas::Base = *y_coordinates.x();
         let y_y: pallas::Base = *y_coordinates.y();
         let y_coord_arr = [y_x, y_y];
-        let y: pallas::Base =
-            poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<2>, 3, 2>::init()
-                .hash(y_coord_arr);
+        let y = poseidon_hash(y_coord_arr);
         //
-        let val_base = pallas::Base::from(coin.value.unwrap());
-        let target_base =
-            coin.sigma1.unwrap() * val_base + coin.sigma2.unwrap() * val_base * val_base;
+        let val_base = pallas::Base::from(coin.value);
+        let target_base = coin.sigma1 * val_base + coin.sigma2 * val_base * val_base;
         info!("y: {:?}", y);
         info!("T: {:?}", target_base);
         let first_winning = y < target_base;
@@ -392,8 +246,8 @@ pub fn is_leader(slot: u64, epoch_coins: &Vec<Vec<LeadCoin>>) -> (bool, usize) {
             highest_stake_idx = winning_idx;
         }
         won |= first_winning;
-        if won && coin.value.unwrap() > highest_stake {
-            highest_stake = coin.value.unwrap();
+        if won && coin.value > highest_stake {
+            highest_stake = coin.value;
             highest_stake_idx = winning_idx;
         }
     }

+ 274 - 0
src/consensus/leadcoin.rs

@@ -0,0 +1,274 @@
+/* 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::{
+        pedersen::pedersen_commitment_base, poseidon_hash, util::mod_r_p, MerkleNode, PublicKey,
+        SecretKey,
+    },
+    pasta::{arithmetic::CurveAffine, group::Curve, pallas},
+};
+use halo2_proofs::{arithmetic::Field, circuit::Value};
+use incrementalmerkletree::{bridgetree::BridgeTree, Tree};
+use log::debug;
+use rand::rngs::OsRng;
+
+use super::PRF_NULLIFIER_PREFIX;
+use crate::{
+    crypto::{proof::ProvingKey, Proof},
+    zk::circuit::LeadContract,
+    Result,
+};
+
+pub const MERKLE_DEPTH_LEADCOIN: usize = 32;
+pub const MERKLE_DEPTH: u8 = 32;
+
+// TODO: Unify item names with the names in the ZK proof (those are more descriptive)
+/// Structure representing the consensus leader coin
+#[derive(Debug, Clone, Copy)]
+pub struct LeadCoin {
+    /// Coin's stake value
+    pub value: u64,
+    /// Commitment for coin1
+    pub coin1_commitment: pallas::Point,
+    /// Commitment for coin2 (poured coin)
+    pub coin2_commitment: pallas::Point,
+    /// Coin index
+    pub idx: u32,
+    /// Coin slot ID,
+    pub sl: pallas::Base,
+    /// Coin timestamp
+    pub tau: pallas::Base,
+    /// Coin nonce
+    pub nonce: pallas::Base,
+    /// Coin nonce's commitment
+    pub nonce_cm: pallas::Base,
+    /// Coin's serial number
+    pub sn: pallas::Base,
+    /// Merkle root of coin1 commitment
+    pub coin1_commitment_root: MerkleNode,
+    /// Merkle root of the `coin1` secret key
+    pub coin1_sk_root: MerkleNode,
+    /// Merkle path to the coin1's commitment
+    pub coin1_commitment_merkle_path: [MerkleNode; MERKLE_DEPTH_LEADCOIN],
+    /// Merkle path to the secret key of `coin1`
+    pub coin1_sk_merkle_path: [MerkleNode; MERKLE_DEPTH_LEADCOIN],
+    /// coin1 commitment blinding factor
+    pub coin1_blind: pallas::Scalar,
+    /// coin2 commitment blinding factor
+    pub coin2_blind: pallas::Scalar,
+    /// Leader election nonce derived from eta at onset of epoch
+    pub y_mu: pallas::Base,
+    /// Leader election nonce derived from eta at onset of epoch
+    pub rho_mu: pallas::Base,
+    /// First coefficient in 1-term T (target function) approximation.
+    /// NOTE: sigma1 and sigma2 are not the capital sigma from the paper, but
+    /// the whole coefficient multiplied with absolute stake.
+    pub sigma1: pallas::Base,
+    /// Second coefficient in 2-term T (target function) approximation.
+    pub sigma2: pallas::Base,
+    /// Coin's secret key
+    pub secret_key: SecretKey,
+}
+
+impl LeadCoin {
+    /// Create a new `LeadCoin` object using given parameters.
+    pub fn new(
+        // wtf is eta and why is it not in the zk proof?
+        eta: pallas::Base,
+        // First coefficient in 1-term T (target function) approximation.
+        sigma1: pallas::Base,
+        // Second coefficient in 2-term T (target function) approximation.
+        sigma2: pallas::Base,
+        // Stake value
+        value: u64,
+        // Slot index in the epock
+        slot_index: usize,
+        // Merkle root of the `coin_1` secret key in the Merkle tree of secret keys
+        coin1_sk_root: MerkleNode,
+        // Merkle path to the secret key of `coin_1` in the Merkle tree of secret keys
+        coin1_sk_merkle_path: [MerkleNode; MERKLE_DEPTH_LEADCOIN],
+        // what's seed supposed to be?
+        seed: u64,
+        // what is this SecretKey representing?
+        secret_key: SecretKey,
+        // Merkle tree of coin commitments
+        coin_commitment_tree: &mut BridgeTree<MerkleNode, MERKLE_DEPTH>,
+    ) -> Self {
+        // Generate random blinding values for commitments:
+        let coin1_blind = pallas::Scalar::random(&mut OsRng);
+        let coin2_blind = pallas::Scalar::random(&mut OsRng);
+
+        // Derive a public key from the secret key
+        let public_key = PublicKey::from_secret(secret_key);
+        let (coin_pk_x, coin_pk_y) = public_key.xy();
+        debug!("coin_pk[{}] x: {:?}", slot_index, coin_pk_x);
+        debug!("coin_pk[{}] y: {:?}", slot_index, coin_pk_y);
+
+        // Derive a nullifier
+        let sn_msg = [
+            pallas::Base::from(seed),
+            coin1_sk_root.inner(),
+            pallas::Base::zero(),
+            pallas::Base::one(),
+        ];
+        let c_sn = poseidon_hash(sn_msg);
+
+        // Derive input for the commitment of coin1
+        let coin1_commit_msg = [
+            pallas::Base::from(*PRF_NULLIFIER_PREFIX),
+            coin_pk_x,
+            coin_pk_y,
+            pallas::Base::from(value),
+            pallas::Base::from(seed),
+            pallas::Base::one(),
+        ];
+        let coin1_commit_v = poseidon_hash(coin1_commit_msg);
+
+        // Create commitment to coin1
+        let coin1_commitment = pedersen_commitment_base(coin1_commit_v, coin1_blind);
+        // Hash its coordinates to get a base field element
+        let c1_cm_coords = coin1_commitment.to_affine().coordinates().unwrap();
+        let c1_base_msg = [*c1_cm_coords.x(), *c1_cm_coords.y()];
+        let coin1_commitment_base = poseidon_hash(c1_base_msg);
+
+        // Append the element to the Merkle tree
+        coin_commitment_tree.append(&MerkleNode::from(coin1_commitment_base));
+        let leaf_pos = coin_commitment_tree.witness().unwrap();
+        let coin1_commitment_root = coin_commitment_tree.root(0).unwrap();
+        let coin1_commitment_merkle_path =
+            coin_commitment_tree.authentication_path(leaf_pos, &coin1_commitment_root).unwrap();
+
+        // Derive the nonce for coin2
+        let coin2_nonce_msg = [
+            pallas::Base::from(seed),
+            coin1_sk_root.inner(),
+            pallas::Base::one(),
+            pallas::Base::one(),
+        ];
+        let coin2_seed = poseidon_hash(coin2_nonce_msg);
+        debug!("coin2_seed[{}]: {:?}", slot_index, coin2_seed);
+
+        // Derive input for the commitment of coin2
+        let coin2_commit_msg = [
+            pallas::Base::from(*PRF_NULLIFIER_PREFIX),
+            coin_pk_x,
+            coin_pk_y,
+            pallas::Base::from(value),
+            coin2_seed,
+            pallas::Base::one(),
+        ];
+        let coin2_commit_v = poseidon_hash(coin2_commit_msg);
+
+        // Create commitment to coin2
+        let coin2_commitment = pedersen_commitment_base(coin2_commit_v, coin2_blind);
+
+        // Derive election seeds
+        let (y_mu, rho_mu) = Self::election_seeds(eta, pallas::Base::from(slot_index as u64));
+
+        // Return the object
+        Self {
+            value,
+            coin1_commitment,
+            coin2_commitment,
+            // TODO: Should be abs slot
+            idx: u32::try_from(usize::from(leaf_pos)).unwrap(),
+            sl: pallas::Base::from(slot_index as u64),
+            // Assume tau is sl for simplicity
+            tau: pallas::Base::from(slot_index as u64),
+            nonce: pallas::Base::from(seed),
+            nonce_cm: coin2_seed,
+            sn: c_sn,
+            coin1_commitment_root,
+            coin1_sk_root,
+            coin1_commitment_merkle_path: coin1_commitment_merkle_path.try_into().unwrap(),
+            coin1_sk_merkle_path,
+            coin1_blind,
+            coin2_blind,
+            y_mu,
+            rho_mu,
+            sigma1,
+            sigma2,
+            secret_key,
+        }
+    }
+
+    /// Derive election seeds from given parameters
+    fn election_seeds(eta: pallas::Base, slot: pallas::Base) -> (pallas::Base, pallas::Base) {
+        let election_seed_nonce = pallas::Base::from(3);
+        let election_seed_lead = pallas::Base::from(22);
+
+        // mu_y
+        let lead_msg = [election_seed_lead, eta, slot];
+        let lead_mu = poseidon_hash(lead_msg);
+
+        // mu_rho
+        let nonce_msg = [election_seed_nonce, eta, slot];
+        let nonce_mu = poseidon_hash(nonce_msg);
+
+        (lead_mu, nonce_mu)
+    }
+
+    /// Create a vector of `pallas::Base` elements from the `LeadCoin` to be
+    /// used as public inputs for the ZK proof.
+    pub fn public_inputs(&self) -> Vec<pallas::Base> {
+        let lottery_msg_input = [self.coin1_sk_root.inner(), self.nonce];
+        let lottery_msg = poseidon_hash(lottery_msg_input);
+
+        let y = pedersen_commitment_base(lottery_msg, mod_r_p(self.y_mu));
+        let y_coords = y.to_affine().coordinates().unwrap();
+        let y_coords = [*y_coords.x(), *y_coords.y()];
+        let y = poseidon_hash(y_coords);
+
+        let pubkey = PublicKey::from_secret(self.secret_key);
+        let (pub_x, pub_y) = pubkey.xy();
+
+        vec![self.nonce_cm, pub_x, pub_y, y]
+    }
+
+    /// Try to create a ZK proof of consensus leadership
+    pub fn create_lead_proof(&self, pk: &ProvingKey) -> Result<Proof> {
+        // Initialize circuit with witnesses
+        let lottery_msg_input = [self.coin1_sk_root.inner(), self.nonce];
+        let lottery_msg = poseidon_hash(lottery_msg_input);
+        let rho = pedersen_commitment_base(lottery_msg, mod_r_p(self.rho_mu));
+
+        let circuit = LeadContract {
+            coin1_commit_merkle_path: Value::known(self.coin1_commitment_merkle_path),
+            coin1_commit_root: Value::known(self.coin1_commitment_root.inner()),
+            coin1_commit_leaf_pos: Value::known(self.idx),
+            coin1_sk: Value::known(self.secret_key.inner()),
+            coin1_sk_root: Value::known(self.coin1_sk_root.inner()),
+            coin1_sk_merkle_path: Value::known(self.coin1_sk_merkle_path),
+            coin1_timestamp: Value::known(self.tau),
+            coin1_nonce: Value::known(self.nonce),
+            coin1_blind: Value::known(self.coin1_blind),
+            coin1_serial: Value::known(self.sn),
+            coin1_value: Value::known(pallas::Base::from(self.value)),
+            coin2_blind: Value::known(self.coin2_blind),
+            coin2_commit: Value::known(self.coin2_commitment),
+            rho_mu: Value::known(mod_r_p(self.rho_mu)),
+            y_mu: Value::known(mod_r_p(self.y_mu)),
+            sigma1: Value::known(self.sigma1),
+            sigma2: Value::known(self.sigma2),
+            rho: Value::known(rho),
+        };
+
+        let proof = Proof::create(pk, &[circuit], &self.public_inputs(), &mut OsRng)?;
+        Ok(proof)
+    }
+}

+ 11 - 7
src/consensus/metadata.rs

@@ -21,17 +21,16 @@ use darkfi_sdk::{
     pasta::pallas,
 };
 use darkfi_serial::{SerialDecodable, SerialEncodable};
+use log::error;
 use rand::rngs::OsRng;
 
-use super::Participant;
+use super::{leadcoin::LeadCoin, Participant};
 use crate::{
     crypto::{
-        lead_proof,
-        leadcoin::LeadCoin,
         proof::{Proof, ProvingKey, VerifyingKey},
         types::*,
     },
-    VerifyResult,
+    Result,
 };
 
 /// This struct represents [`Block`](super::Block) information used by the consensus protocol.
@@ -118,12 +117,17 @@ pub struct LeadProof {
 
 impl LeadProof {
     pub fn new(pk: &ProvingKey, coin: LeadCoin) -> Self {
-        let proof = lead_proof::create_lead_proof(pk, coin).unwrap();
+        let proof = coin.create_lead_proof(pk).unwrap();
         Self { proof }
     }
 
-    pub fn verify(&self, vk: &VerifyingKey, public_inputs: &[DrkCircuitField]) -> VerifyResult<()> {
-        lead_proof::verify_lead_proof(vk, &self.proof, public_inputs)
+    pub fn verify(&self, vk: &VerifyingKey, public_inputs: &[DrkCircuitField]) -> Result<()> {
+        if let Err(e) = self.proof.verify(vk, public_inputs) {
+            error!("Verification of consensus lead proof failed: {}", e);
+            return Err(e.into())
+        }
+
+        Ok(())
     }
 }
 

+ 5 - 9
src/consensus/state.rs

@@ -38,17 +38,13 @@ use pasta_curves::{group::ff::PrimeField, pallas};
 use rand::rngs::OsRng;
 
 use super::{
-    coins, Block, BlockInfo, BlockProposal, Header, LeadProof, Metadata, Participant,
-    ProposalChain, DELTA, EPOCH_LENGTH, LEADER_PROOF_K,
+    coins, leadcoin::LeadCoin, Block, BlockInfo, BlockProposal, Header, LeadProof, Metadata,
+    Participant, ProposalChain, DELTA, EPOCH_LENGTH, LEADER_PROOF_K,
 };
 
 use crate::{
     blockchain::Blockchain,
-    crypto::{
-        lead_proof,
-        leadcoin::LeadCoin,
-        proof::{ProvingKey, VerifyingKey},
-    },
+    crypto::proof::{ProvingKey, VerifyingKey},
     net,
     node::{
         state::{state_transition, ProgramState, StateUpdate},
@@ -410,7 +406,7 @@ impl ValidatorState {
         let coin = self.consensus.coins[relative_slot][idx];
         // TODO: Generate new LeadCoin from newlly minted coin, will reuse original coin for now
         //let coin2 = something();
-        let proof = lead_proof::create_lead_proof(&self.proving_key, coin)?;
+        let proof = coin.create_lead_proof(&self.proving_key)?;
         let participants = self.consensus.participants.values().cloned().collect();
         let metadata = Metadata::new(
             signed_proposal,
@@ -418,7 +414,7 @@ impl ValidatorState {
             coin.public_inputs(),
             coin.public_inputs(),
             idx,
-            coin.sn.unwrap(),
+            coin.sn,
             eta,
             LeadProof::from(proof),
             participants,

+ 0 - 52
src/crypto/lead_proof.rs

@@ -1,52 +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 log::error;
-
-use rand::rngs::OsRng;
-
-use crate::{
-    crypto::{
-        leadcoin::LeadCoin,
-        proof::{Proof, ProvingKey, VerifyingKey},
-        types::*,
-    },
-    Result, VerifyFailed, VerifyResult,
-};
-
-#[allow(clippy::too_many_arguments)]
-pub fn create_lead_proof(pk: &ProvingKey, coin: LeadCoin) -> Result<Proof> {
-    let contract = coin.create_contract();
-    let public_inputs = coin.public_inputs();
-    let proof = Proof::create(pk, &[contract], &public_inputs, &mut OsRng)?;
-    Ok(proof)
-}
-
-pub fn verify_lead_proof(
-    vk: &VerifyingKey,
-    proof: &Proof,
-    public_inputs: &[DrkCircuitField],
-) -> VerifyResult<()> {
-    match proof.verify(vk, public_inputs) {
-        Ok(()) => Ok(()),
-        Err(e) => {
-            error!("lead verification failed: {}", e);
-            Err(VerifyFailed::InternalError("lead verification failure".to_string()))
-        }
-    }
-}

+ 0 - 117
src/crypto/leadcoin.rs

@@ -1,117 +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::MERKLE_DEPTH_ORCHARD, pedersen::pedersen_commitment_base, util::mod_r_p,
-        Keypair, MerkleNode,
-    },
-    pasta::{arithmetic::CurveAffine, group::Curve, pallas},
-};
-use halo2_gadgets::poseidon::primitives as poseidon;
-use halo2_proofs::circuit::Value;
-
-use crate::zk::circuit::lead_contract::LeadContract;
-
-pub const LEAD_PUBLIC_INPUT_LEN: usize = 4;
-
-#[derive(Debug, Default, Clone, Copy)]
-pub struct LeadCoin {
-    pub value: Option<u64>,             // coin stake
-    pub cm: Option<pallas::Point>,      // coin commitment
-    pub cm2: Option<pallas::Point>,     // poured coin commitment
-    pub idx: u32,                       // coin index
-    pub sl: Option<pallas::Base>,       // coin slot id
-    pub tau: Option<pallas::Base>,      // coin time stamp
-    pub nonce: Option<pallas::Base>,    // coin nonce
-    pub nonce_cm: Option<pallas::Base>, // coin nonce's commitment
-    pub sn: Option<pallas::Base>,       // coin's serial number
-    pub keypair: Option<Keypair>,
-    pub root_cm: Option<pallas::Base>, // root of coin commitment
-    pub root_sk: Option<pallas::Base>, // coin's secret key
-    pub path: Option<[MerkleNode; MERKLE_DEPTH_ORCHARD]>, // path to the coin's commitment
-    pub path_sk: Option<[MerkleNode; MERKLE_DEPTH_ORCHARD]>, // path to the coin's secret key
-    pub c1_blind: Option<pallas::Scalar>, // coin opening
-    pub c2_blind: Option<pallas::Scalar>, // poured coin opening
-    // election seeds
-    pub y_mu: Option<pallas::Base>, // leader election nonce derived from eta at onset of epoch
-    pub rho_mu: Option<pallas::Base>, // leader election nonce derived from eta at onset of epoch
-    pub sigma1: Option<pallas::Base>,
-    pub sigma2: Option<pallas::Base>,
-}
-
-impl LeadCoin {
-    pub fn public_inputs_as_array(&self) -> [pallas::Base; LEAD_PUBLIC_INPUT_LEN] {
-        let po_nonce = self.nonce_cm.unwrap();
-        let po_pk = self.keypair.unwrap().public.inner().to_affine().coordinates().unwrap();
-        let y_mu = self.y_mu.unwrap();
-        let _rho_mu = self.rho_mu.unwrap();
-        let root_sk = self.root_sk.unwrap();
-        let nonce = self.nonce.unwrap();
-        let lottery_msg_input = [root_sk, nonce];
-        let lottery_msg: pallas::Base =
-            poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<2>, 3, 2>::init()
-                .hash(lottery_msg_input);
-        let po_y_pt: pallas::Point = pedersen_commitment_base(lottery_msg, mod_r_p(y_mu));
-        let po_y_x = *po_y_pt.to_affine().coordinates().unwrap().x();
-        let po_y_y = *po_y_pt.to_affine().coordinates().unwrap().y();
-        let y_coord_arr = [po_y_x, po_y_y];
-        let po_y: pallas::Base =
-            poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<2>, 3, 2>::init()
-                .hash(y_coord_arr);
-        let public_inputs: [pallas::Base; LEAD_PUBLIC_INPUT_LEN] =
-            [po_nonce, *po_pk.x(), *po_pk.y(), po_y];
-        public_inputs
-    }
-
-    pub fn public_inputs(&self) -> Vec<pallas::Base> {
-        self.public_inputs_as_array().to_vec()
-    }
-
-    pub fn create_contract(&self) -> LeadContract {
-        let rho_mu = self.rho_mu.unwrap();
-        let root_sk = self.root_sk.unwrap();
-        let nonce = self.nonce.unwrap();
-        let lottery_msg_input = [root_sk, nonce];
-        let lottery_msg: pallas::Base =
-            poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<2>, 3, 2>::init()
-                .hash(lottery_msg_input);
-        //
-        let rho_pt: pallas::Point = pedersen_commitment_base(lottery_msg, mod_r_p(rho_mu));
-        LeadContract {
-            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_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_commit: Value::known(self.cm2.unwrap()),
-            mau_rho: Value::known(mod_r_p(self.rho_mu.unwrap())),
-            mau_y: Value::known(mod_r_p(self.y_mu.unwrap())),
-            sigma1: Value::known(self.sigma1.unwrap()),
-            sigma2: Value::known(self.sigma2.unwrap()),
-            rho: Value::known(rho_pt),
-        }
-    }
-}

+ 0 - 3
src/crypto/mod.rs

@@ -32,6 +32,3 @@ pub use proof::Proof;
 
 pub use burn_proof::BurnRevealedValues;
 pub use mint_proof::MintRevealedValues;
-
-pub mod lead_proof;
-pub mod leadcoin;

+ 10 - 14
src/sdk/src/crypto/pedersen.rs

@@ -16,14 +16,18 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use super::constants::{
-    fixed_bases::{
-        VALUE_COMMITMENT_PERSONALIZATION, VALUE_COMMITMENT_R_BYTES, VALUE_COMMITMENT_V_BYTES,
+use halo2_gadgets::ecc::chip::FixedPoint;
+use pasta_curves::{arithmetic::CurveExt, pallas};
+
+use super::{
+    constants::{
+        fixed_bases::{
+            VALUE_COMMITMENT_PERSONALIZATION, VALUE_COMMITMENT_R_BYTES, VALUE_COMMITMENT_V_BYTES,
+        },
+        NullifierK,
     },
-    NullifierK,
+    util::mod_r_p,
 };
-use halo2_gadgets::ecc::chip::FixedPoint;
-use pasta_curves::{arithmetic::CurveExt, group::ff::PrimeField, pallas};
 
 pub type ValueBlind = pallas::Scalar;
 pub type ValueCommit = pallas::Point;
@@ -47,11 +51,3 @@ pub fn pedersen_commitment_u64(value: u64, blind: ValueBlind) -> ValueCommit {
 
     V * mod_r_p(pallas::Base::from(value)) + R * blind
 }
-
-/// Converts from pallas::Base to pallas::Scalar (aka $x \pmod{r_\mathbb{P}}$).
-///
-/// This requires no modular reduction because Pallas' base field is smaller than its
-/// scalar field.
-pub fn mod_r_p(x: pallas::Base) -> pallas::Scalar {
-    pallas::Scalar::from_repr(x.to_repr()).unwrap()
-}

+ 1 - 1
src/sdk/src/tx.rs

@@ -22,7 +22,7 @@ use super::crypto::ContractId;
 
 /// A ContractCall is the part of a transaction that executes a certain
 /// `contract_id` with `data` as the call's payload.
-#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
+#[derive(Debug, Clone, Eq, PartialEq, SerialEncodable, SerialDecodable)]
 pub struct ContractCall {
     pub contract_id: ContractId,
     pub data: Vec<u8>,

+ 1 - 1
src/tx2/mod.rs

@@ -30,7 +30,7 @@ use crate::{crypto::Proof, Error, Result};
 
 /// A Transaction contains an arbitrary number of `ContractCall` objects,
 /// along with corresponding ZK proofs and Schnorr signatures.
-#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
+#[derive(Debug, Clone, Eq, PartialEq, SerialEncodable, SerialDecodable)]
 pub struct Transaction {
     /// Calls executed in this transaction
     pub calls: Vec<ContractCall>,

+ 10 - 15
src/zk/circuit/lead_contract.rs

@@ -152,9 +152,9 @@ pub struct LeadContract {
     /// `coin_2` pedersen commitment point
     pub coin2_commit: Value<pallas::Point>,
     /// Random value derived from `eta` used for constraining `rho`
-    pub mau_rho: Value<pallas::Scalar>,
+    pub rho_mu: Value<pallas::Scalar>,
     /// Random value derived from `eta` used for calculating `y`.
-    pub mau_y: Value<pallas::Scalar>,
+    pub y_mu: 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.
@@ -402,14 +402,14 @@ impl Circuit<pallas::Base> for LeadContract {
             self.coin2_commit.as_ref().map(|cm| cm.to_affine()),
         )?;
 
-        let _mau_rho = ScalarFixed::new(
+        let rho_mu = ScalarFixed::new(
             ecc_chip.clone(),
-            layouter.namespace(|| "witness mau_rho"),
-            self.mau_rho,
+            layouter.namespace(|| "witness rho_mu"),
+            self.rho_mu,
         )?;
 
-        let mau_y =
-            ScalarFixed::new(ecc_chip.clone(), layouter.namespace(|| "witness mau_y"), self.mau_y)?;
+        let y_mu =
+            ScalarFixed::new(ecc_chip.clone(), layouter.namespace(|| "witness y_mu"), self.y_mu)?;
 
         let sigma1 = assign_free_advice(
             layouter.namespace(|| "witness sigma1"),
@@ -594,7 +594,7 @@ impl Circuit<pallas::Base> for LeadContract {
         // ==================================
         // lhs of the leader election lottery
         // ==================================
-        // * y as Commit(root_sk||nonce, mau_y)
+        // * y as Commit(root_sk||nonce, y_mu)
         // 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> = {
@@ -615,7 +615,7 @@ impl Circuit<pallas::Base> for LeadContract {
 
         let (lottery_commit_r, _) = {
             let r = FixedPoint::from_inner(ecc_chip.clone(), ValueCommitR);
-            r.mul(layouter.namespace(|| "mau_y * ValueCommitR"), mau_y)?
+            r.mul(layouter.namespace(|| "y_mu * ValueCommitR"), y_mu)?
         };
 
         let y_commit = lottery_commit_v
@@ -637,13 +637,8 @@ impl Circuit<pallas::Base> for LeadContract {
 
         // y_commit also becomes V of the following pedersen commitment for rho
         let (rho_cm, _) = {
-            let mau_rho = ScalarFixed::new(
-                ecc_chip.clone(),
-                layouter.namespace(|| "mau_rho scalar"),
-                self.mau_rho,
-            )?;
             let rho_commit_r = FixedPoint::from_inner(ecc_chip, 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"), rho_mu)?
         };
         let rho_commit = lottery_commit_v.add(layouter.namespace(|| "nonce commit"), &rho_cm)?;