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

dao: use ElGamalEncryptedNote for DAO::vote() instead of AEAD

zero 2 лет назад
Родитель
Сommit
e0ecada541

+ 31 - 2
src/contract/dao/proof/dao-vote-main.zk

@@ -4,6 +4,7 @@ field = "pallas";
 constant "DaoVoteMain" {
     EcFixedPoint VALUE_COMMIT_RANDOM,
     EcFixedPointShort VALUE_COMMIT_VALUE,
+    EcFixedPointBase NULLIFIER_K,
 }
 
 witness "DaoVoteMain" {
@@ -20,8 +21,7 @@ witness "DaoVoteMain" {
     Base dao_approval_ratio_quot,
     Base dao_approval_ratio_base,
     Base gov_token_id,
-    Base dao_public_x,
-    Base dao_public_y,
+    EcNiPoint dao_public_key,
     Base dao_bulla_blind,
 
     # Is the vote yes or no
@@ -37,12 +37,21 @@ witness "DaoVoteMain" {
 
     # Check whether the proposal has expired or not
     Base current_day,
+
+    Base ephem_secret,
 }
 
 circuit "DaoVoteMain" {
     token_commit = poseidon_hash(gov_token_id, gov_token_blind);
     constrain_instance(token_commit);
 
+    # cast to EcPoint
+    # (otherwise zkas refuses to compile)
+    ONE = witness_base(1);
+    dao_pubkey = ec_mul_var_base(ONE, dao_public_key);
+    dao_public_x = ec_get_x(dao_pubkey);
+    dao_public_y = ec_get_y(dao_pubkey);
+
     dao_bulla = poseidon_hash(
         dao_proposer_limit,
         dao_quorum,
@@ -87,4 +96,24 @@ circuit "DaoVoteMain" {
     end_time = base_add(proposal_current_day, proposal_duration_days);
     less_than_strict(current_day, end_time);
     constrain_instance(current_day);
+
+    # Verifiable encryption
+    ephem_public = ec_mul_base(ephem_secret, NULLIFIER_K);
+    constrain_instance(ec_get_x(ephem_public));
+    constrain_instance(ec_get_y(ephem_public));
+    shared_point = ec_mul_var_base(ephem_secret, dao_public_key);
+    shared_secret = poseidon_hash(
+        ec_get_x(shared_point),
+        ec_get_y(shared_point),
+    );
+    const_1 = witness_base(1);
+    const_2 = witness_base(2);
+    # Token ID
+    shared_secret_1 = poseidon_hash(shared_secret, const_1);
+    enc_vote_option = base_add(vote_option, shared_secret_1);
+    constrain_instance(enc_vote_option);
+    # Serial
+    shared_secret_2 = poseidon_hash(shared_secret, const_2);
+    enc_all_vote_value = base_add(all_vote_value, shared_secret_2);
+    constrain_instance(enc_all_vote_value);
 }

+ 39 - 12
src/contract/dao/src/client/vote.rs

@@ -21,8 +21,10 @@ use darkfi_sdk::{
     bridgetree,
     bridgetree::Hashable,
     crypto::{
-        note::AeadEncryptedNote, pasta_prelude::*, pedersen_commitment_u64, poseidon_hash, Keypair,
-        MerkleNode, Nullifier, PublicKey, SecretKey,
+        note::{AeadEncryptedNote, ElGamalEncryptedNote},
+        pasta_prelude::*,
+        pedersen_commitment_u64, poseidon_hash, Keypair, MerkleNode, Nullifier, PublicKey,
+        SecretKey,
     },
     pasta::pallas,
 };
@@ -60,9 +62,9 @@ pub struct DaoVoteCall {
     pub inputs: Vec<DaoVoteInput>,
     pub vote_option: bool,
     pub yes_vote_blind: pallas::Scalar,
-    pub vote_keypair: Keypair,
     pub proposal: DaoProposal,
     pub dao: Dao,
+    pub dao_keypair: Keypair,
     pub current_day: u64,
 }
 
@@ -175,7 +177,7 @@ impl DaoVoteCall {
         let dao_quorum = pallas::Base::from(self.dao.quorum);
         let dao_approval_ratio_quot = pallas::Base::from(self.dao.approval_ratio_quot);
         let dao_approval_ratio_base = pallas::Base::from(self.dao.approval_ratio_base);
-        let (dao_pub_x, dao_pub_y) = self.dao.public_key.xy();
+        let dao_public_key = self.dao.public_key.inner();
 
         let vote_option = self.vote_option as u64;
         assert!(vote_option == 0 || vote_option == 1);
@@ -187,6 +189,12 @@ impl DaoVoteCall {
         let all_vote_commit = pedersen_commitment_u64(all_vote_value, all_vote_blind);
         let all_vote_commit_coords = all_vote_commit.to_affine().coordinates().unwrap();
 
+        let vote_option = pallas::Base::from(vote_option);
+        let all_vote_value_fp = pallas::Base::from(all_vote_value);
+        let ephem_secret = SecretKey::random(&mut OsRng);
+        let ephem_pubkey = PublicKey::from_secret(ephem_secret.into());
+        let (ephem_x, ephem_y) = ephem_pubkey.xy();
+
         let current_day = pallas::Base::from(self.current_day);
         let prover_witnesses = vec![
             // proposal params
@@ -201,24 +209,33 @@ impl DaoVoteCall {
             Witness::Base(Value::known(dao_approval_ratio_quot)),
             Witness::Base(Value::known(dao_approval_ratio_base)),
             Witness::Base(Value::known(self.dao.gov_token_id.inner())),
-            Witness::Base(Value::known(dao_pub_x)),
-            Witness::Base(Value::known(dao_pub_y)),
+            Witness::EcNiPoint(Value::known(dao_public_key)),
             Witness::Base(Value::known(self.dao.bulla_blind)),
             // Vote
-            Witness::Base(Value::known(pallas::Base::from(vote_option))),
+            Witness::Base(Value::known(vote_option)),
             Witness::Scalar(Value::known(self.yes_vote_blind)),
             // Total number of gov tokens allocated
-            Witness::Base(Value::known(pallas::Base::from(all_vote_value))),
+            Witness::Base(Value::known(all_vote_value_fp)),
             Witness::Scalar(Value::known(all_vote_blind)),
             // gov token
             Witness::Base(Value::known(gov_token_blind)),
             // time checks
             Witness::Base(Value::known(current_day)),
+            // verifiable encryption
+            Witness::Base(Value::known(ephem_secret.inner())),
         ];
 
         assert_eq!(self.dao.to_bulla(), self.proposal.dao_bulla);
         let proposal_bulla = self.proposal.to_bulla();
 
+        let note = [
+            vote_option,
+            //self.yes_vote_blind,
+            all_vote_value_fp,
+            //all_vote_blind,
+        ];
+        let enc_note = ElGamalEncryptedNote::encrypt(note, &ephem_secret, &self.dao_keypair.public);
+
         let public_inputs = vec![
             token_commit,
             proposal_bulla.inner(),
@@ -227,6 +244,10 @@ impl DaoVoteCall {
             *all_vote_commit_coords.x(),
             *all_vote_commit_coords.y(),
             current_day,
+            ephem_x,
+            ephem_y,
+            enc_note.encrypted_values[0],
+            enc_note.encrypted_values[1],
         ];
 
         let circuit = ZkCircuit::new(prover_witnesses, main_zkbin);
@@ -242,11 +263,17 @@ impl DaoVoteCall {
             all_vote_value,
             all_vote_blind,
         };
-        let enc_note =
-            AeadEncryptedNote::encrypt(&note, &self.vote_keypair.public, &mut OsRng).unwrap();
+        let enc_note_old =
+            AeadEncryptedNote::encrypt(&note, &self.dao_keypair.public, &mut OsRng).unwrap();
 
-        let params =
-            DaoVoteParams { token_commit, proposal_bulla, yes_vote_commit, note: enc_note, inputs };
+        let params = DaoVoteParams {
+            token_commit,
+            proposal_bulla,
+            yes_vote_commit,
+            note: enc_note,
+            note_old: enc_note_old,
+            inputs,
+        };
 
         Ok((params, proofs))
     }

+ 5 - 0
src/contract/dao/src/entrypoint/vote.rs

@@ -86,6 +86,7 @@ pub(crate) fn dao_vote_get_metadata(
     let yes_vote_commit_coords = params.yes_vote_commit.to_affine().coordinates().unwrap();
     let all_vote_commit_coords = all_vote_commit.to_affine().coordinates().unwrap();
 
+    let (ephem_x, ephem_y) = params.note.ephem_public.xy();
     zk_public_inputs.push((
         DAO_CONTRACT_ZKAS_DAO_VOTE_MAIN_NS.to_string(),
         vec![
@@ -96,6 +97,10 @@ pub(crate) fn dao_vote_get_metadata(
             *all_vote_commit_coords.x(),
             *all_vote_commit_coords.y(),
             pallas::Base::from(current_day),
+            ephem_x,
+            ephem_y,
+            params.note.encrypted_values[0],
+            params.note.encrypted_values[1],
         ],
     ));
 

+ 5 - 3
src/contract/dao/src/model.rs

@@ -20,8 +20,9 @@ use core::str::FromStr;
 
 use darkfi_sdk::{
     crypto::{
-        note::AeadEncryptedNote, pasta_prelude::*, poseidon_hash, MerkleNode, Nullifier, PublicKey,
-        TokenId,
+        note::{AeadEncryptedNote, ElGamalEncryptedNote},
+        pasta_prelude::*,
+        poseidon_hash, MerkleNode, Nullifier, PublicKey, TokenId,
     },
     error::ContractError,
     pasta::pallas,
@@ -279,7 +280,8 @@ pub struct DaoVoteParams {
     /// Commitment for yes votes
     pub yes_vote_commit: pallas::Point,
     /// Encrypted note
-    pub note: AeadEncryptedNote,
+    pub note: ElGamalEncryptedNote<2>,
+    pub note_old: AeadEncryptedNote,
     /// Inputs for the vote
     pub inputs: Vec<DaoVoteParamsInput>,
 }

+ 8 - 6
src/contract/dao/tests/integration.rs

@@ -216,9 +216,9 @@ fn integration_test() -> Result<()> {
         info!("[Alice] Building vote tx (yes)");
         let (alice_vote_tx, alice_vote_params) = th.dao_vote(
             &Holder::Alice,
-            &dao_keypair,
             true,
             &dao,
+            &dao_keypair,
             &propose_info,
             &propose_params.proposal_bulla,
         )?;
@@ -226,9 +226,9 @@ fn integration_test() -> Result<()> {
         info!("[Bob] Building vote tx (no)");
         let (bob_vote_tx, bob_vote_params) = th.dao_vote(
             &Holder::Bob,
-            &dao_keypair,
             false,
             &dao,
+            &dao_keypair,
             &propose_info,
             &propose_params.proposal_bulla,
         )?;
@@ -236,9 +236,9 @@ fn integration_test() -> Result<()> {
         info!("[Charlie] Building vote tx (yes)");
         let (charlie_vote_tx, charlie_vote_params) = th.dao_vote(
             &Holder::Charlie,
-            &dao_keypair,
             true,
             &dao,
+            &dao_keypair,
             &propose_info,
             &propose_params.proposal_bulla,
         )?;
@@ -255,10 +255,12 @@ fn integration_test() -> Result<()> {
         }
 
         // Gather and decrypt all vote notes
-        let vote_note_1: DaoVoteNote = alice_vote_params.note.decrypt(&dao_keypair.secret).unwrap();
-        let vote_note_2: DaoVoteNote = bob_vote_params.note.decrypt(&dao_keypair.secret).unwrap();
+        let vote_note_1: DaoVoteNote =
+            alice_vote_params.note_old.decrypt(&dao_keypair.secret).unwrap();
+        let vote_note_2: DaoVoteNote =
+            bob_vote_params.note_old.decrypt(&dao_keypair.secret).unwrap();
         let vote_note_3: DaoVoteNote =
-            charlie_vote_params.note.decrypt(&dao_keypair.secret).unwrap();
+            charlie_vote_params.note_old.decrypt(&dao_keypair.secret).unwrap();
 
         // Count the votes
         let mut total_yes_vote_value = 0;

+ 2 - 2
src/contract/test-harness/src/dao_vote.rs

@@ -43,9 +43,9 @@ impl TestHarness {
     pub fn dao_vote(
         &mut self,
         voter: &Holder,
-        dao_kp: &Keypair,
         vote_option: bool,
         dao: &Dao,
+        dao_keypair: &Keypair,
         proposal: &DaoProposal,
         proposal_bulla: &DaoProposalBulla,
     ) -> Result<(Transaction, DaoVoteParams)> {
@@ -84,9 +84,9 @@ impl TestHarness {
             inputs: vec![input],
             vote_option,
             yes_vote_blind: pallas::Scalar::random(&mut OsRng),
-            vote_keypair: *dao_kp,
             proposal: proposal.clone(),
             dao: dao.clone(),
+            dao_keypair: dao_keypair.clone(),
             current_day,
         };
 

+ 6 - 6
src/sdk/src/crypto/note.rs

@@ -93,7 +93,7 @@ impl<const N: usize> ElGamalEncryptedNote<N> {
         values: [pallas::Base; N],
         ephem_secret: &SecretKey,
         public: &PublicKey,
-    ) -> Result<Self, ContractError> {
+    ) -> Self {
         // Derive shared secret using DH
         let ephem_public = PublicKey::from_secret(*ephem_secret);
         let (ss_x, ss_y) = PublicKey::from(public.inner() * mod_r_p(ephem_secret.inner())).xy();
@@ -109,10 +109,10 @@ impl<const N: usize> ElGamalEncryptedNote<N> {
             encrypted_values[i] = values[i] + blinds[i];
         }
 
-        Ok(Self { encrypted_values, ephem_public })
+        Self { encrypted_values, ephem_public }
     }
 
-    pub fn decrypt(&self, secret: &SecretKey) -> Result<[pallas::Base; N], ContractError> {
+    pub fn decrypt(&self, secret: &SecretKey) -> [pallas::Base; N] {
         // Derive shared secret using DH
         let (ss_x, ss_y) =
             PublicKey::from(self.ephem_public.inner() * mod_r_p(secret.inner())).xy();
@@ -128,7 +128,7 @@ impl<const N: usize> ElGamalEncryptedNote<N> {
             decrypted_values[i] = self.encrypted_values[i] - blinds[i];
         }
 
-        Ok(decrypted_values)
+        decrypted_values
     }
 }
 
@@ -162,9 +162,9 @@ mod tests {
         let ephem_secret = SecretKey::random(&mut OsRng);
 
         let encrypted_note =
-            ElGamalEncryptedNote::encrypt(plain_values, &ephem_secret, &keypair.public).unwrap();
+            ElGamalEncryptedNote::encrypt(plain_values, &ephem_secret, &keypair.public);
 
-        let decrypted_values = encrypted_note.decrypt(&keypair.secret).unwrap();
+        let decrypted_values = encrypted_note.decrypt(&keypair.secret);
 
         assert_eq!(plain_values, decrypted_values);
     }