ソースを参照

daod: create 3 voters, tally the votes and test everything is correct

lunar-mining 4 年 前
コミット
4629ec29e3

+ 1 - 1
bin/daod/src/dao_contract/propose/wallet.rs

@@ -45,7 +45,7 @@ pub struct BuilderInput {
     pub merkle_path: Vec<MerkleNode>,
     pub merkle_path: Vec<MerkleNode>,
 }
 }
 
 
-#[derive(SerialEncodable, SerialDecodable)]
+#[derive(SerialEncodable, SerialDecodable, Clone)]
 pub struct Proposal {
 pub struct Proposal {
     pub dest: PublicKey,
     pub dest: PublicKey,
     pub amount: u64,
     pub amount: u64,

+ 3 - 2
bin/daod/src/dao_contract/vote/validate.rs

@@ -208,11 +208,12 @@ pub fn state_transition(
     })
     })
 }
 }
 
 
+#[derive(Clone)]
 pub struct Update {
 pub struct Update {
     proposal_bulla: pallas::Base,
     proposal_bulla: pallas::Base,
     vote_nulls: Vec<Nullifier>,
     vote_nulls: Vec<Nullifier>,
-    vote_commit: pallas::Point,
-    value_commit: pallas::Point,
+    pub vote_commit: pallas::Point,
+    pub value_commit: pallas::Point,
 }
 }
 
 
 pub fn apply(states: &mut StateRegistry, mut update: Update) {
 pub fn apply(states: &mut StateRegistry, mut update: Update) {

+ 3 - 3
bin/daod/src/dao_contract/vote/wallet.rs

@@ -77,7 +77,6 @@ impl Builder {
         let mut proofs = vec![];
         let mut proofs = vec![];
 
 
         let gov_token_blind = pallas::Base::random(&mut OsRng);
         let gov_token_blind = pallas::Base::random(&mut OsRng);
-        let vote_blind = pallas::Scalar::random(&mut OsRng);
 
 
         let mut inputs = vec![];
         let mut inputs = vec![];
         let mut value = 0;
         let mut value = 0;
@@ -237,7 +236,8 @@ impl Builder {
 
 
         let weighted_vote = vote * value;
         let weighted_vote = vote * value;
 
 
-        let vote_commit = pedersen_commitment_u64(weighted_vote, vote_blind);
+        let vote_commit = pedersen_commitment_u64(weighted_vote, self.vote.vote_option_blind);
+        debug!(target: "demo::dao_contract::vote::wallet::Builder", "vote commit: {:?}", vote_commit);
         let vote_coords = vote_commit.to_affine().coordinates().unwrap();
         let vote_coords = vote_commit.to_affine().coordinates().unwrap();
         let vote_commit_x = *vote_coords.x();
         let vote_commit_x = *vote_coords.x();
         let vote_commit_y = *vote_coords.y();
         let vote_commit_y = *vote_coords.y();
@@ -275,7 +275,7 @@ impl Builder {
             Witness::Base(Value::known(self.dao.bulla_blind)),
             Witness::Base(Value::known(self.dao.bulla_blind)),
             // Vote
             // Vote
             Witness::Base(Value::known(vote)),
             Witness::Base(Value::known(vote)),
-            Witness::Scalar(Value::known(vote_blind)),
+            Witness::Scalar(Value::known(self.vote.vote_option_blind)),
             // Total number of gov tokens allocated
             // Total number of gov tokens allocated
             Witness::Base(Value::known(value_base)),
             Witness::Base(Value::known(value_base)),
             Witness::Scalar(Value::known(value_blind)),
             Witness::Scalar(Value::known(value_blind)),

+ 219 - 8
bin/daod/src/demo.rs

@@ -6,7 +6,7 @@ use incrementalmerkletree::{bridgetree::BridgeTree, Tree};
 use log::debug;
 use log::debug;
 use pasta_curves::{
 use pasta_curves::{
     arithmetic::CurveAffine,
     arithmetic::CurveAffine,
-    group::{ff::Field, Curve},
+    group::{ff::Field, Curve, Group},
     pallas,
     pallas,
 };
 };
 use rand::rngs::OsRng;
 use rand::rngs::OsRng;
@@ -26,6 +26,7 @@ use darkfi::{
         proof::{ProvingKey, VerifyingKey},
         proof::{ProvingKey, VerifyingKey},
         token_id::generate_id,
         token_id::generate_id,
         types::{DrkCircuitField, DrkSpendHook, DrkUserData, DrkValue},
         types::{DrkCircuitField, DrkSpendHook, DrkUserData, DrkValue},
+        util::pedersen_commitment_u64,
         OwnCoin, OwnCoins, Proof,
         OwnCoin, OwnCoins, Proof,
     },
     },
     node::state::{ProgramState, StateUpdate},
     node::state::{ProgramState, StateUpdate},
@@ -714,19 +715,23 @@ pub async fn demo() -> Result<()> {
 
 
     debug!(target: "demo", "Stage 5. Start voting");
     debug!(target: "demo", "Stage 5. Start voting");
 
 
+    // We save updates here for testing.
+    let mut updates = Vec::new();
+
+    // User 1: YES
+
     let (money_leaf_position, money_merkle_path) = {
     let (money_leaf_position, money_merkle_path) = {
         let state = states.lookup::<money_contract::State>(&"Money".to_string()).unwrap();
         let state = states.lookup::<money_contract::State>(&"Money".to_string()).unwrap();
         let tree = &state.tree;
         let tree = &state.tree;
-        let leaf_position = gov_recv[1].leaf_position.clone();
+        let leaf_position = gov_recv[0].leaf_position.clone();
         let root = tree.root(0).unwrap();
         let root = tree.root(0).unwrap();
         let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
         let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
         (leaf_position, merkle_path)
         (leaf_position, merkle_path)
     };
     };
 
 
-    debug!(target: "demo", "Creating inputs...");
     let input = dao_contract::vote::wallet::BuilderInput {
     let input = dao_contract::vote::wallet::BuilderInput {
-        secret: gov_keypair_2.secret,
-        note: gov_recv[1].note.clone(),
+        secret: gov_keypair_1.secret,
+        note: gov_recv[0].note.clone(),
         leaf_position: money_leaf_position,
         leaf_position: money_leaf_position,
         merkle_path: money_merkle_path,
         merkle_path: money_merkle_path,
     };
     };
@@ -738,7 +743,6 @@ pub async fn demo() -> Result<()> {
     // We create a new keypair to encrypt the vote.
     // We create a new keypair to encrypt the vote.
     let vote_keypair_1 = Keypair::random(&mut OsRng);
     let vote_keypair_1 = Keypair::random(&mut OsRng);
 
 
-    debug!(target: "demo", "Creating builder...");
     let builder = dao_contract::vote::wallet::Builder {
     let builder = dao_contract::vote::wallet::Builder {
         inputs: vec![input],
         inputs: vec![input],
         vote: dao_contract::vote::wallet::Vote {
         vote: dao_contract::vote::wallet::Vote {
@@ -746,7 +750,7 @@ pub async fn demo() -> Result<()> {
             vote_option_blind: pallas::Scalar::random(&mut OsRng),
             vote_option_blind: pallas::Scalar::random(&mut OsRng),
         },
         },
         vote_keypair: vote_keypair_1,
         vote_keypair: vote_keypair_1,
-        proposal,
+        proposal: proposal.clone(),
         dao: dao_params.clone(),
         dao: dao_params.clone(),
     };
     };
     debug!(target: "demo", "build()...");
     debug!(target: "demo", "build()...");
@@ -762,7 +766,8 @@ pub async fn demo() -> Result<()> {
 
 
             let update = dao_contract::vote::validate::state_transition(&states, idx, &tx)
             let update = dao_contract::vote::validate::state_transition(&states, idx, &tx)
                 .expect("dao_contract::vote::validate::state_transition() failed!");
                 .expect("dao_contract::vote::validate::state_transition() failed!");
-            dao_contract::vote::validate::apply(&mut states, update);
+            dao_contract::vote::validate::apply(&mut states, update.clone());
+            updates.push(update);
         }
         }
     }
     }
 
 
@@ -789,5 +794,211 @@ pub async fn demo() -> Result<()> {
     debug!(target: "demo", "  vote_option: {}", vote_note_1.vote.vote_option);
     debug!(target: "demo", "  vote_option: {}", vote_note_1.vote.vote_option);
     debug!(target: "demo", "  value: {}", vote_note_1.value);
     debug!(target: "demo", "  value: {}", vote_note_1.value);
 
 
+    // User 2: NO
+
+    let (money_leaf_position, money_merkle_path) = {
+        let state = states.lookup::<money_contract::State>(&"Money".to_string()).unwrap();
+        let tree = &state.tree;
+        let leaf_position = gov_recv[1].leaf_position.clone();
+        let root = tree.root(0).unwrap();
+        let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
+        (leaf_position, merkle_path)
+    };
+
+    let input = dao_contract::vote::wallet::BuilderInput {
+        secret: gov_keypair_2.secret,
+        note: gov_recv[1].note.clone(),
+        leaf_position: money_leaf_position,
+        merkle_path: money_merkle_path,
+    };
+
+    let vote_option: bool = false;
+
+    assert!(vote_option == true || vote_option == false);
+
+    // We create a new keypair to encrypt the vote.
+    let vote_keypair_2 = Keypair::random(&mut OsRng);
+
+    let builder = dao_contract::vote::wallet::Builder {
+        inputs: vec![input],
+        vote: dao_contract::vote::wallet::Vote {
+            vote_option,
+            vote_option_blind: pallas::Scalar::random(&mut OsRng),
+        },
+        vote_keypair: vote_keypair_2,
+        proposal: proposal.clone(),
+        dao: dao_params.clone(),
+    };
+    debug!(target: "demo", "build()...");
+    let func_call = builder.build(&zk_bins);
+
+    let tx = Transaction { func_calls: vec![func_call] };
+
+    //// Validator
+
+    for (idx, func_call) in tx.func_calls.iter().enumerate() {
+        if func_call.func_id == "DAO::vote()" {
+            debug!(target: "demo", "dao_contract::vote::state_transition()");
+
+            let update = dao_contract::vote::validate::state_transition(&states, idx, &tx)
+                .expect("dao_contract::vote::validate::state_transition() failed!");
+            dao_contract::vote::validate::apply(&mut states, update.clone());
+            updates.push(update);
+        }
+    }
+
+    tx.zk_verify(&zk_bins);
+
+    //// Wallet
+
+    // Secret vote info. Needs to be revealed at some point.
+    // TODO: look into verifiable encryption for notes
+    // TODO: look into timelock puzzle as a possibility
+    let vote_note_2 = {
+        assert_eq!(tx.func_calls.len(), 1);
+        let func_call = &tx.func_calls[0];
+        let call_data = func_call.call_data.as_any();
+        assert_eq!((&*call_data).type_id(), TypeId::of::<dao_contract::vote::validate::CallData>());
+        let call_data = call_data.downcast_ref::<dao_contract::vote::validate::CallData>().unwrap();
+
+        let header = &call_data.header;
+        let note: dao_contract::vote::wallet::Note =
+            header.enc_note.decrypt(&vote_keypair_2.secret).unwrap();
+        note
+    };
+    debug!(target: "demo", "User 2 voted!");
+    debug!(target: "demo", "  vote_option: {}", vote_note_2.vote.vote_option);
+    debug!(target: "demo", "  value: {}", vote_note_2.value);
+
+    // User 3: YES
+
+    let (money_leaf_position, money_merkle_path) = {
+        let state = states.lookup::<money_contract::State>(&"Money".to_string()).unwrap();
+        let tree = &state.tree;
+        let leaf_position = gov_recv[2].leaf_position.clone();
+        let root = tree.root(0).unwrap();
+        let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
+        (leaf_position, merkle_path)
+    };
+
+    let input = dao_contract::vote::wallet::BuilderInput {
+        secret: gov_keypair_3.secret,
+        note: gov_recv[2].note.clone(),
+        leaf_position: money_leaf_position,
+        merkle_path: money_merkle_path,
+    };
+
+    let vote_option: bool = true;
+
+    assert!(vote_option == true || vote_option == false);
+
+    // We create a new keypair to encrypt the vote.
+    let vote_keypair_3 = Keypair::random(&mut OsRng);
+
+    let builder = dao_contract::vote::wallet::Builder {
+        inputs: vec![input],
+        vote: dao_contract::vote::wallet::Vote {
+            vote_option,
+            vote_option_blind: pallas::Scalar::random(&mut OsRng),
+        },
+        vote_keypair: vote_keypair_3,
+        proposal: proposal.clone(),
+        dao: dao_params.clone(),
+    };
+    debug!(target: "demo", "build()...");
+    let func_call = builder.build(&zk_bins);
+
+    let tx = Transaction { func_calls: vec![func_call] };
+
+    //// Validator
+
+    for (idx, func_call) in tx.func_calls.iter().enumerate() {
+        if func_call.func_id == "DAO::vote()" {
+            debug!(target: "demo", "dao_contract::vote::state_transition()");
+
+            let update = dao_contract::vote::validate::state_transition(&states, idx, &tx)
+                .expect("dao_contract::vote::validate::state_transition() failed!");
+            dao_contract::vote::validate::apply(&mut states, update.clone());
+            updates.push(update);
+        }
+    }
+
+    tx.zk_verify(&zk_bins);
+
+    //// Wallet
+
+    // Secret vote info. Needs to be revealed at some point.
+    // TODO: look into verifiable encryption for notes
+    // TODO: look into timelock puzzle as a possibility
+    let vote_note_3 = {
+        assert_eq!(tx.func_calls.len(), 1);
+        let func_call = &tx.func_calls[0];
+        let call_data = func_call.call_data.as_any();
+        assert_eq!((&*call_data).type_id(), TypeId::of::<dao_contract::vote::validate::CallData>());
+        let call_data = call_data.downcast_ref::<dao_contract::vote::validate::CallData>().unwrap();
+
+        let header = &call_data.header;
+        let note: dao_contract::vote::wallet::Note =
+            header.enc_note.decrypt(&vote_keypair_3.secret).unwrap();
+        note
+    };
+    debug!(target: "demo", "User 3 voted!");
+    debug!(target: "demo", "  vote_option: {}", vote_note_3.vote.vote_option);
+    debug!(target: "demo", "  value: {}", vote_note_3.value);
+
+    // Every votes produces a semi-homomorphic encryption of their vote.
+    // Which is either yes or no
+    // We copy the state tree for the governance token so coins can be used
+    // to vote on other proposals at the same time.
+    // With their vote, they produce a ZK proof + nullifier
+    // The votes are unblinded by MPC to a selected party at the end of the
+    // voting period.
+    // (that's if we want votes to be hidden during voting)
+
+    let mut win_votes = 0;
+    let mut total_votes = 0;
+    let mut total_vote_blinds = pallas::Scalar::from(0);
+    let mut total_value_blinds = pallas::Scalar::from(0);
+    let mut total_value_commit = pallas::Point::identity();
+    let mut total_vote_commit = pallas::Point::identity();
+
+    assert!(updates.len() == 3);
+
+    for (i, (note, update)) in
+        [vote_note_1, vote_note_2, vote_note_3].iter().zip(updates).enumerate()
+    {
+        let value_commit = pedersen_commitment_u64(note.value, note.value_blind);
+        assert!(update.value_commit == value_commit);
+
+        total_value_commit += value_commit;
+        total_value_blinds += note.value_blind;
+
+        let vote_commit = pedersen_commitment_u64(
+            note.vote.vote_option as u64 * note.value,
+            note.vote.vote_option_blind,
+        );
+
+        assert!(update.vote_commit == vote_commit);
+
+        total_vote_commit += vote_commit;
+        total_vote_blinds += note.vote.vote_option_blind;
+
+        let vote_option = note.vote.vote_option;
+
+        if vote_option {
+            win_votes += note.value;
+        }
+        total_votes += note.value;
+
+        let vote_result: String = if vote_option { "yes".to_string() } else { "no".to_string() };
+
+        debug!("Voter {} voted {}", i, vote_result);
+    }
+
+    debug!("Outcome = {} / {}", win_votes, total_votes);
+
+    assert!(total_value_commit == pedersen_commitment_u64(total_votes, total_value_blinds));
+    assert!(total_vote_commit == pedersen_commitment_u64(win_votes, total_vote_blinds));
+
     Ok(())
     Ok(())
 }
 }