Sfoglia il codice sorgente

dao cleanup for vote() and exec() phases

x 3 anni fa
parent
commit
e723d40004

+ 3 - 3
src/contract/dao/src/dao_client/exec.rs

@@ -31,7 +31,7 @@ use darkfi::{
 };
 
 use super::{DaoInfo, ProposalInfo};
-use crate::dao_model::DaoExecParams;
+use crate::dao_model::ExecCallParams;
 
 pub struct ExecCall {
     pub proposal: ProposalInfo,
@@ -55,7 +55,7 @@ impl ExecCall {
         self,
         exec_zkbin: &ZkBinary,
         exec_pk: &ProvingKey,
-    ) -> Result<(DaoExecParams, Vec<Proof>)> {
+    ) -> Result<(ExecCallParams, Vec<Proof>)> {
         debug!(target: "dao", "build()");
         let mut proofs = vec![];
 
@@ -185,7 +185,7 @@ impl ExecCall {
             .expect("DAO::exec() proving error!)");
         proofs.push(input_proof);
 
-        let params = DaoExecParams {
+        let params = ExecCallParams {
             proposal: proposal_bulla,
             coin_0,
             coin_1,

+ 3 - 3
src/contract/dao/src/dao_client/mint.rs

@@ -25,7 +25,7 @@ use darkfi_sdk::crypto::{pallas, poseidon_hash, PublicKey, TokenId};
 use log::debug;
 use rand::rngs::OsRng;
 
-use crate::dao_model::DaoMintParams;
+use crate::dao_model::MintCallParams;
 
 #[derive(Clone)]
 pub struct DaoInfo {
@@ -42,7 +42,7 @@ pub fn make_mint_call(
     dao: &DaoInfo,
     dao_mint_zkbin: &ZkBinary,
     dao_mint_pk: &ProvingKey,
-) -> Result<(DaoMintParams, Vec<Proof>)> {
+) -> Result<(MintCallParams, Vec<Proof>)> {
     debug!(target: "dao", "Building DAO contract mint transaction");
 
     let dao_proposer_limit = pallas::Base::from(dao.proposer_limit);
@@ -80,7 +80,7 @@ pub fn make_mint_call(
     let circuit = ZkCircuit::new(prover_witnesses, dao_mint_zkbin.clone());
     let proof = Proof::create(dao_mint_pk, &[circuit], &public, &mut OsRng)?;
 
-    let dao_mint_params = DaoMintParams { dao_bulla: dao_bulla.into() };
+    let dao_mint_params = MintCallParams { dao_bulla: dao_bulla.into() };
 
     Ok((dao_mint_params, vec![proof]))
 }

+ 4 - 4
src/contract/dao/src/dao_client/propose.rs

@@ -30,7 +30,7 @@ use darkfi::{
 };
 
 use crate::{
-    dao_model::{DaoProposeParams, ProposeInput},
+    dao_model::{ProposeCallParams, ProposeCallParamsInput},
     note,
 };
 
@@ -75,7 +75,7 @@ impl ProposeCall {
         burn_pk: &ProvingKey,
         main_zkbin: &ZkBinary,
         main_pk: &ProvingKey,
-    ) -> Result<(DaoProposeParams, Vec<Proof>)> {
+    ) -> Result<(ProposeCallParams, Vec<Proof>)> {
         let mut proofs = vec![];
 
         let gov_token_blind = pallas::Base::random(&mut OsRng);
@@ -163,7 +163,7 @@ impl ProposeCall {
                 .expect("DAO::propose() proving error!");
             proofs.push(input_proof);
 
-            let input = ProposeInput { value_commit, merkle_root, signature_public };
+            let input = ProposeCallParamsInput { value_commit, merkle_root, signature_public };
             inputs.push(input);
         }
 
@@ -249,7 +249,7 @@ impl ProposeCall {
 
         let note = ProposeNote { proposal: self.proposal };
         let enc_note = note::encrypt(&note, &self.dao.public_key).unwrap();
-        let params = DaoProposeParams {
+        let params = ProposeCallParams {
             dao_merkle_root: self.dao_merkle_root,
             proposal_bulla,
             token_commit,

+ 4 - 4
src/contract/dao/src/dao_client/vote.rs

@@ -32,7 +32,7 @@ use darkfi::{
 
 use super::{DaoInfo, ProposalInfo};
 use crate::{
-    dao_model::{DaoVoteParams, VoteCallInput},
+    dao_model::{VoteCallParams, VoteCallParamsInput},
     note,
 };
 
@@ -75,7 +75,7 @@ impl VoteCall {
         burn_pk: &ProvingKey,
         main_zkbin: &ZkBinary,
         main_pk: &ProvingKey,
-    ) -> Result<(DaoVoteParams, Vec<Proof>)> {
+    ) -> Result<(VoteCallParams, Vec<Proof>)> {
         debug!(target: "dao", "build()");
         let mut proofs = vec![];
 
@@ -168,7 +168,7 @@ impl VoteCall {
                 .expect("DAO::vote() proving error!");
             proofs.push(input_proof);
 
-            let input = VoteCallInput {
+            let input = VoteCallParamsInput {
                 nullifier: Nullifier::from(nullifier),
                 vote_commit,
                 merkle_root,
@@ -270,7 +270,7 @@ impl VoteCall {
         let note = VoteNote { vote: self.vote, vote_value, vote_value_blind };
         let enc_note = note::encrypt(&note, &self.vote_keypair.public).unwrap();
 
-        let params = DaoVoteParams {
+        let params = VoteCallParams {
             token_commit,
             proposal_bulla,
             yes_vote_commit,

+ 58 - 51
src/contract/dao/src/dao_model.rs

@@ -34,67 +34,56 @@ impl From<pallas::Base> for DaoBulla {
     }
 }
 
-#[derive(SerialEncodable, SerialDecodable)]
-pub struct ProposalVotes {
-    // TODO: Might be more logical to have `yes_votes_commit` and `no_votes_commit`
-    /// Weighted vote commit
-    pub yes_votes_commit: pallas::Point,
-    /// All value staked in the vote
-    pub all_votes_commit: pallas::Point,
-    /// Vote nullifiers
-    pub vote_nullifiers: Vec<Nullifier>,
-}
+// DAO::mint()
 
-impl ProposalVotes {
-    pub fn nullifier_exists(&self, nullifier: &Nullifier) -> bool {
-        self.vote_nullifiers.iter().any(|n| n == nullifier)
-    }
-}
-
-impl Default for ProposalVotes {
-    fn default() -> Self {
-        Self {
-            yes_votes_commit: pallas::Point::identity(),
-            all_votes_commit: pallas::Point::identity(),
-            vote_nullifiers: vec![],
-        }
-    }
+#[derive(SerialEncodable, SerialDecodable)]
+pub struct MintCallParams {
+    pub dao_bulla: DaoBulla,
 }
 
 #[derive(SerialEncodable, SerialDecodable)]
-pub struct DaoMintParams {
+pub struct MintCallUpdate {
     pub dao_bulla: DaoBulla,
 }
 
+// DAO::propose()
+
 #[derive(SerialEncodable, SerialDecodable)]
-pub struct DaoMintUpdate {
-    pub dao_bulla: DaoBulla,
+pub struct ProposeCallParams {
+    pub dao_merkle_root: MerkleNode,
+    pub token_commit: pallas::Base,
+    pub proposal_bulla: pallas::Base,
+    pub ciphertext: Vec<u8>,
+    pub ephem_public: PublicKey,
+    pub inputs: Vec<ProposeCallParamsInput>,
 }
 
 #[derive(Clone, SerialEncodable, SerialDecodable)]
-pub struct ProposeInput {
+pub struct ProposeCallParamsInput {
     pub value_commit: pallas::Point,
     pub merkle_root: MerkleNode,
     pub signature_public: PublicKey,
 }
 
 #[derive(SerialEncodable, SerialDecodable)]
-pub struct DaoProposeParams {
-    pub dao_merkle_root: MerkleNode,
-    pub token_commit: pallas::Base,
+pub struct ProposeCallUpdate {
     pub proposal_bulla: pallas::Base,
-    pub ciphertext: Vec<u8>,
-    pub ephem_public: PublicKey,
-    pub inputs: Vec<ProposeInput>,
 }
 
+// DAO::vote()
+
 #[derive(SerialEncodable, SerialDecodable)]
-pub struct DaoProposeUpdate {
+pub struct VoteCallParams {
+    pub token_commit: pallas::Base,
     pub proposal_bulla: pallas::Base,
+    pub yes_vote_commit: pallas::Point,
+    pub ciphertext: Vec<u8>,
+    pub ephem_public: PublicKey,
+    pub inputs: Vec<VoteCallParamsInput>,
 }
 
 #[derive(SerialEncodable, SerialDecodable)]
-pub struct VoteCallInput {
+pub struct VoteCallParamsInput {
     pub nullifier: Nullifier,
     pub vote_commit: pallas::Point,
     pub merkle_root: MerkleNode,
@@ -102,27 +91,45 @@ pub struct VoteCallInput {
 }
 
 #[derive(SerialEncodable, SerialDecodable)]
-pub struct DaoVoteParams {
-    pub token_commit: pallas::Base,
+pub struct VoteCallUpdate {
     pub proposal_bulla: pallas::Base,
-    pub yes_vote_commit: pallas::Point,
-    pub ciphertext: Vec<u8>,
-    pub ephem_public: PublicKey,
-    pub inputs: Vec<VoteCallInput>,
+    pub proposal_votes: BlindAggregateVote,
+    pub vote_nullifiers: Vec<Nullifier>,
 }
 
+/// Represents a single or multiple blinded votes. These can be summed together.
 #[derive(SerialEncodable, SerialDecodable)]
-pub struct DaoVoteUpdate {
-    pub proposal_bulla: pallas::Base,
-    // bad but lets get it just working...
-    pub proposal_votes: ProposalVotes,
-    //pub vote_nullifiers: Vec<Nullifier>,
-    //pub yes_vote_commit: pallas::Point,
-    //pub all_vote_commit: pallas::Point,
+pub struct BlindAggregateVote {
+    /// Weighted vote commit
+    pub yes_votes_commit: pallas::Point,
+    /// All value staked in the vote
+    pub all_votes_commit: pallas::Point,
+}
+
+impl BlindAggregateVote {
+    //pub fn nullifier_exists(&self, nullifier: &Nullifier) -> bool {
+    //    self.vote_nullifiers.iter().any(|n| n == nullifier)
+    //}
+
+    pub fn combine(&mut self, other: BlindAggregateVote) {
+        self.yes_votes_commit += other.yes_votes_commit;
+        self.all_votes_commit += other.all_votes_commit;
+    }
+}
+
+impl Default for BlindAggregateVote {
+    fn default() -> Self {
+        Self {
+            yes_votes_commit: pallas::Point::identity(),
+            all_votes_commit: pallas::Point::identity(),
+        }
+    }
 }
 
+// DAO::exec()
+
 #[derive(SerialEncodable, SerialDecodable)]
-pub struct DaoExecParams {
+pub struct ExecCallParams {
     pub proposal: pallas::Base,
     pub coin_0: pallas::Base,
     pub coin_1: pallas::Base,
@@ -132,6 +139,6 @@ pub struct DaoExecParams {
 }
 
 #[derive(SerialEncodable, SerialDecodable)]
-pub struct DaoExecUpdate {
+pub struct ExecCallUpdate {
     pub proposal: pallas::Base,
 }

+ 42 - 34
src/contract/dao/src/entrypoint.rs

@@ -38,8 +38,8 @@ use darkfi_money_contract::{
 
 use crate::{
     dao_model::{
-        DaoExecParams, DaoExecUpdate, DaoMintParams, DaoMintUpdate, DaoProposeParams,
-        DaoProposeUpdate, DaoVoteParams, DaoVoteUpdate, ProposalVotes,
+        BlindAggregateVote, ExecCallParams, ExecCallUpdate, MintCallParams, MintCallUpdate,
+        ProposeCallParams, ProposeCallUpdate, VoteCallParams, VoteCallUpdate,
     },
     DaoFunction, DAO_CONTRACT_ZKAS_DAO_EXEC_NS, DAO_CONTRACT_ZKAS_DAO_MINT_NS,
     DAO_CONTRACT_ZKAS_DAO_PROPOSE_BURN_NS, DAO_CONTRACT_ZKAS_DAO_PROPOSE_MAIN_NS,
@@ -151,7 +151,7 @@ fn init_contract(cid: ContractId, _ix: &[u8]) -> ContractResult {
         Err(_) => db_init(cid, DAO_PROPOSAL_ROOTS_TREE)?,
     };*/
 
-    // Set up a database tree to hold proposal votes (k: proposalbulla, v: ProposalVotes)
+    // Set up a database tree to hold proposal votes (k: proposalbulla, v: BlindAggregateVote)
     let _ = match db_lookup(cid, DAO_PROPOSAL_VOTES_TREE) {
         Ok(v) => v,
         Err(_) => db_init(cid, DAO_PROPOSAL_VOTES_TREE)?,
@@ -173,11 +173,11 @@ fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
 
     match DaoFunction::try_from(self_.data[0])? {
         DaoFunction::Mint => {
-            let params: DaoMintParams = deserialize(&self_.data[1..])?;
+            let params: MintCallParams = deserialize(&self_.data[1..])?;
 
             // No checks in Mint, just return the update.
             // TODO: Should it check that there isn't an existing one?
-            let update = DaoMintUpdate { dao_bulla: params.dao_bulla };
+            let update = MintCallUpdate { dao_bulla: params.dao_bulla };
             let mut update_data = vec![];
             update_data.write_u8(DaoFunction::Mint as u8)?;
             update.encode(&mut update_data)?;
@@ -188,7 +188,7 @@ fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
         }
 
         DaoFunction::Propose => {
-            let params: DaoProposeParams = deserialize(&self_.data[1..])?;
+            let params: ProposeCallParams = deserialize(&self_.data[1..])?;
 
             // Check the Merkle roots for the input coins are valid
             let money_cid = *MONEY_CONTRACT_ID;
@@ -210,7 +210,7 @@ fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
             // TODO: Look at gov tokens avoid using already spent ones
             // Need to spend original coin and generate 2 nullifiers?
 
-            let update = DaoProposeUpdate { proposal_bulla: params.proposal_bulla };
+            let update = ProposeCallUpdate { proposal_bulla: params.proposal_bulla };
             let mut update_data = vec![];
             update_data.write_u8(DaoFunction::Propose as u8)?;
             update.encode(&mut update_data)?;
@@ -221,7 +221,7 @@ fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
         }
 
         DaoFunction::Vote => {
-            let params: DaoVoteParams = deserialize(&self_.data[1..])?;
+            let params: VoteCallParams = deserialize(&self_.data[1..])?;
 
             let money_cid = *MONEY_CONTRACT_ID;
 
@@ -231,13 +231,15 @@ fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
                 msg!("Invalid proposal {:?}", params.proposal_bulla);
                 return Err(ContractError::Custom(4))
             };
-            let mut proposal_votes: ProposalVotes = deserialize(&proposal_votes)?;
+            let mut proposal_votes: BlindAggregateVote = deserialize(&proposal_votes)?;
 
             // Check the Merkle roots and nullifiers for the input coins are valid
             let money_roots_db = db_lookup(money_cid, MONEY_CONTRACT_COIN_ROOTS_TREE)?;
             let money_nullifier_db = db_lookup(money_cid, MONEY_CONTRACT_NULLIFIERS_TREE)?;
             let dao_vote_nulls_db = db_lookup(cid, DAO_VOTE_NULLS)?;
 
+            let mut vote_nullifiers = vec![];
+
             for input in &params.inputs {
                 if !db_contains_key(money_roots_db, &serialize(&input.merkle_root))? {
                     msg!("Invalid input Merkle root: {:?}", input.merkle_root);
@@ -249,24 +251,27 @@ fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
                     return Err(ContractError::Custom(6))
                 }
 
-                if proposal_votes.vote_nullifiers.contains(&input.nullifier) ||
-                    db_contains_key(dao_vote_nulls_db, &serialize(&input.nullifier))?
+                // Prefix nullifier with proposal bulla so nullifiers from different proposals
+                // don't interfere with each other.
+                let null_key = serialize(&(params.proposal_bulla, input.nullifier));
+
+                if vote_nullifiers.contains(&input.nullifier) ||
+                    db_contains_key(dao_vote_nulls_db, &null_key)?
                 {
                     msg!("Attempted double vote");
                     return Err(ContractError::Custom(7))
                 }
 
                 proposal_votes.all_votes_commit += input.vote_commit;
-                proposal_votes.vote_nullifiers.push(input.nullifier);
+                vote_nullifiers.push(input.nullifier);
             }
 
             proposal_votes.yes_votes_commit += params.yes_vote_commit;
 
-            let update = DaoVoteUpdate {
+            let update = VoteCallUpdate {
                 proposal_bulla: params.proposal_bulla,
-                proposal_votes, //vote_nullifiers,
-                                //yes_vote_commit: params.yes_vote_commit,
-                                //all_vote_commit,
+                proposal_votes,
+                vote_nullifiers,
             };
             let mut update_data = vec![];
             update_data.write_u8(DaoFunction::Vote as u8)?;
@@ -278,7 +283,7 @@ fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
         }
 
         DaoFunction::Exec => {
-            let params: DaoExecParams = deserialize(&self_.data[1..])?;
+            let params: ExecCallParams = deserialize(&self_.data[1..])?;
 
             // =============================
             // Enforce tx has correct format
@@ -317,13 +322,13 @@ fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
                 msg!("Proposal {:?} not found in db", params.proposal);
                 return Err(ContractError::Custom(1));
             };
-            let proposal_votes: ProposalVotes = deserialize(&proposal_votes)?;
+            let proposal_votes: BlindAggregateVote = deserialize(&proposal_votes)?;
 
-            // 4. Check yes_votes_commit and all_votes_commit are the same as in ProposalVotes
+            // 4. Check yes_votes_commit and all_votes_commit are the same as in BlindAggregateVote
             assert!(proposal_votes.yes_votes_commit == params.yes_votes_commit);
             assert!(proposal_votes.all_votes_commit == params.all_votes_commit);
 
-            let update = DaoExecUpdate { proposal: params.proposal };
+            let update = ExecCallUpdate { proposal: params.proposal };
             let mut update_data = vec![];
             update_data.write_u8(DaoFunction::Exec as u8)?;
             update.encode(&mut update_data)?;
@@ -338,7 +343,7 @@ fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
 fn process_update(cid: ContractId, ix: &[u8]) -> ContractResult {
     match DaoFunction::try_from(ix[0])? {
         DaoFunction::Mint => {
-            let update: DaoMintUpdate = deserialize(&ix[1..])?;
+            let update: MintCallUpdate = deserialize(&ix[1..])?;
 
             let bulla_db = db_lookup(cid, DAO_BULLA_TREE)?;
             let roots_db = db_lookup(cid, DAO_ROOTS_TREE)?;
@@ -350,7 +355,7 @@ fn process_update(cid: ContractId, ix: &[u8]) -> ContractResult {
         }
 
         DaoFunction::Propose => {
-            let update: DaoProposeUpdate = deserialize(&ix[1..])?;
+            let update: ProposeCallUpdate = deserialize(&ix[1..])?;
 
             //let proposal_tree_db = db_lookup(cid, DAO_PROPOSAL_TREE)?;
             //let proposal_root_db = db_lookup(cid, DAO_PROPOSAL_ROOTS_TREE)?;
@@ -366,19 +371,18 @@ fn process_update(cid: ContractId, ix: &[u8]) -> ContractResult {
             )?;
             */
 
-            let pv = ProposalVotes::default();
+            let pv = BlindAggregateVote::default();
             db_set(proposal_vote_db, &serialize(&update.proposal_bulla), &serialize(&pv))?;
 
             Ok(())
         }
 
         DaoFunction::Vote => {
-            let update: DaoVoteUpdate = deserialize(&ix[1..])?;
+            let update: VoteCallUpdate = deserialize(&ix[1..])?;
 
             // Perform this code:
-            //votes_info.yes_votes_commit += self.yes_vote_commit;
-            //votes_info.all_votes_commit += self.all_vote_commit;
-            //votes_info.vote_nulls.append(&mut self.vote_nulls);
+            //   total_yes_votes_commit += update.yes_vote_commit
+            //   total_all_votes_commit += update.all_vote_commit
 
             let proposal_vote_db = db_lookup(cid, DAO_PROPOSAL_VOTES_TREE)?;
             db_set(
@@ -387,17 +391,21 @@ fn process_update(cid: ContractId, ix: &[u8]) -> ContractResult {
                 &serialize(&update.proposal_votes),
             )?;
 
+            // We are essentially doing: vote_nulls.append(update.nulls)
+
             let dao_vote_nulls_db = db_lookup(cid, DAO_VOTE_NULLS)?;
 
-            for nullifier in update.proposal_votes.vote_nullifiers {
-                db_set(dao_vote_nulls_db, &serialize(&nullifier), &[])?;
+            for nullifier in update.vote_nullifiers {
+                // Uniqueness is enforced for (proposal_bulla, nullifier)
+                let key = serialize(&(update.proposal_bulla, nullifier));
+                db_set(dao_vote_nulls_db, &key, &[])?;
             }
 
             Ok(())
         }
 
         DaoFunction::Exec => {
-            let update: DaoExecUpdate = deserialize(&ix[1..])?;
+            let update: ExecCallUpdate = deserialize(&ix[1..])?;
 
             // Remove proposal from db
             let proposal_vote_db = db_lookup(cid, DAO_PROPOSAL_VOTES_TREE)?;
@@ -416,7 +424,7 @@ fn get_metadata(_: ContractId, ix: &[u8]) -> ContractResult {
 
     match DaoFunction::try_from(self_.data[0])? {
         DaoFunction::Mint => {
-            let params: DaoMintParams = deserialize(&self_.data[1..])?;
+            let params: MintCallParams = deserialize(&self_.data[1..])?;
 
             let mut zk_public_values: Vec<(String, Vec<pallas::Base>)> = vec![];
             // TODO: Why no signatures? Should it be signed with the DAO keypair?
@@ -435,7 +443,7 @@ fn get_metadata(_: ContractId, ix: &[u8]) -> ContractResult {
         }
 
         DaoFunction::Propose => {
-            let params: DaoProposeParams = deserialize(&self_.data[1..])?;
+            let params: ProposeCallParams = deserialize(&self_.data[1..])?;
             assert!(!params.inputs.is_empty());
 
             let mut zk_public_values: Vec<(String, Vec<pallas::Base>)> = vec![];
@@ -485,7 +493,7 @@ fn get_metadata(_: ContractId, ix: &[u8]) -> ContractResult {
         }
 
         DaoFunction::Vote => {
-            let params: DaoVoteParams = deserialize(&self_.data[1..])?;
+            let params: VoteCallParams = deserialize(&self_.data[1..])?;
             assert!(!params.inputs.is_empty());
 
             let mut zk_public_values: Vec<(String, Vec<pallas::Base>)> = vec![];
@@ -539,7 +547,7 @@ fn get_metadata(_: ContractId, ix: &[u8]) -> ContractResult {
         }
 
         DaoFunction::Exec => {
-            let params: DaoExecParams = deserialize(&self_.data[1..])?;
+            let params: ExecCallParams = deserialize(&self_.data[1..])?;
 
             let mut zk_public_values: Vec<(String, Vec<pallas::Base>)> = vec![];
             let signature_pubkeys: Vec<PublicKey> = vec![];