Procházet zdrojové kódy

dao_demo: cast votes from command-line.

lunar-mining před 3 roky
rodič
revize
4d06b1aede

+ 21 - 5
bin/dao/dao-cli/src/main.rs

@@ -23,6 +23,8 @@ pub enum CliDaoSubCommands {
     },
     /// Mint tokens
     Addr {},
+    GetVotes {},
+    GetProposals {},
     Mint {
         /// Number of treasury tokens to mint.
         token_supply: u64,
@@ -53,7 +55,11 @@ pub enum CliDaoSubCommands {
         amount: u64,
     },
     /// Vote
-    Vote {},
+    Vote {
+        nym: String,
+
+        vote: String,
+    },
     /// Execute
     Exec {},
 }
@@ -100,9 +106,19 @@ async fn start(options: CliDao) -> Result<()> {
             println!("DAO public address: {}", &reply.to_string());
             return Ok(())
         }
+        Some(CliDaoSubCommands::GetVotes {}) => {
+            let reply = client.get_votes().await?;
+            println!("{}", &reply.to_string());
+            return Ok(())
+        }
+        Some(CliDaoSubCommands::GetProposals {}) => {
+            let reply = client.get_proposals().await?;
+            println!("{}", &reply.to_string());
+            return Ok(())
+        }
         Some(CliDaoSubCommands::Mint { token_supply, dao_addr }) => {
             let reply = client.mint(token_supply, dao_addr).await?;
-            println!("New DAO balance: {}", &reply.to_string());
+            println!("{}", &reply.as_str().unwrap().to_string());
             return Ok(())
         }
         Some(CliDaoSubCommands::Keygen { nym }) => {
@@ -112,7 +128,7 @@ async fn start(options: CliDao) -> Result<()> {
         }
         Some(CliDaoSubCommands::Airdrop { nym, value }) => {
             let reply = client.airdrop(nym, value).await?;
-            println!("New user balance: {}", &reply.to_string());
+            println!("{}", &reply.as_str().unwrap().to_string());
             return Ok(())
         }
         Some(CliDaoSubCommands::DaoBalance {}) => {
@@ -135,8 +151,8 @@ async fn start(options: CliDao) -> Result<()> {
             println!("Proposal bulla: {}", &reply.to_string());
             return Ok(())
         }
-        Some(CliDaoSubCommands::Vote {}) => {
-            let reply = client.vote().await?;
+        Some(CliDaoSubCommands::Vote { nym, vote }) => {
+            let reply = client.vote(nym, vote).await?;
             println!("Server replied: {}", &reply.to_string());
             return Ok(())
         }

+ 16 - 2
bin/dao/dao-cli/src/rpc.rs

@@ -84,8 +84,22 @@ impl Rpc {
 
     // --> {"jsonrpc": "2.0", "method": "vote", "params": [], "id": 42}
     // <-- {"jsonrpc": "2.0", "result": "voting...", "id": 42}
-    pub async fn vote(&self) -> Result<Value> {
-        let req = JsonRequest::new("vote", json!([]));
+    pub async fn vote(&self, nym: String, vote: String) -> Result<Value> {
+        let req = JsonRequest::new("vote", json!([nym, vote]));
+        self.client.request(req).await
+    }
+
+    // --> {"jsonrpc": "2.0", "method": "exec", "params": [], "id": 42}
+    // <-- {"jsonrpc": "2.0", "result": "executing...", "id": 42}
+    pub async fn get_votes(&self) -> Result<Value> {
+        let req = JsonRequest::new("get_votes", json!([]));
+        self.client.request(req).await
+    }
+
+    // --> {"jsonrpc": "2.0", "method": "exec", "params": [], "id": 42}
+    // <-- {"jsonrpc": "2.0", "result": "executing...", "id": 42}
+    pub async fn get_proposals(&self) -> Result<Value> {
+        let req = JsonRequest::new("get_proposals", json!([]));
         self.client.request(req).await
     }
 

+ 53 - 23
bin/dao/daod/src/main.rs

@@ -321,24 +321,21 @@ impl Client {
         Ok(())
     }
 
-    // TODO: error handling
     fn propose(
         &mut self,
         recipient: PublicKey,
         token_id: pallas::Base,
         amount: u64,
         sender: String,
-    ) -> Result<(Proposal, pallas::Base)> {
+    ) -> Result<pallas::Base> {
         let params = self.dao_wallet.params[0].clone();
 
         let dao_leaf_position = self.dao_wallet.leaf_position;
 
-        // To be able to make a proposal, we must prove we have ownership of governance tokens,
-        // and that the quantity of governance tokens is within the accepted proposal limit.
+        // To be able to make a proposal, we must prove we have ownership
+        // of governance tokens, and that the quantity of governance
+        // tokens is within the accepted proposer limit.
         let mut sender_wallet = self.money_wallets.get_mut(&sender).unwrap();
-        //let own_coin = sender_wallet.balances()?;
-        //let (money_leaf_position, money_merkle_path) =
-        //    sender_wallet.get_path(&self.states, &own_coin)?;
 
         let tx = sender_wallet.propose_tx(
             params.clone(),
@@ -350,19 +347,47 @@ impl Client {
             &mut self.states,
         )?;
 
-        // bang!
         self.validate(&tx)?;
         self.update_wallets().unwrap();
 
-        let (proposal, proposal_bulla) = self.dao_wallet.read_proposal(&tx)?;
+        let proposal_bulla = self.dao_wallet.store_proposal(&tx)?;
 
-        Ok((proposal, proposal_bulla))
+        Ok(proposal_bulla)
     }
 
     fn get_addr_from_nym(&self, nym: String) -> Result<PublicKey> {
         let wallet = self.money_wallets.get(&nym).unwrap();
         Ok(wallet.get_public_key())
     }
+
+    fn cast_vote(&mut self, nym: String, vote: bool) -> Result<()> {
+        let dao_key = self.dao_wallet.keypair;
+        let proposal = self.dao_wallet.proposals[0].clone();
+        let dao_params = self.dao_wallet.params[0].clone();
+        let dao_keypair = self.dao_wallet.keypair;
+
+        let mut voter_wallet = self.money_wallets.get_mut(&nym).unwrap();
+
+        let tx = voter_wallet
+            .vote_tx(
+                vote,
+                dao_key,
+                proposal,
+                dao_params,
+                dao_keypair,
+                &self.zk_bins,
+                &mut self.states,
+            )
+            .unwrap();
+
+        self.validate(&tx).unwrap();
+        // Do we need this here cos no value is actually spent?
+        self.update_wallets().unwrap();
+
+        self.dao_wallet.store_vote(&tx).unwrap();
+
+        Ok(())
+    }
 }
 
 struct DaoWallet {
@@ -373,6 +398,7 @@ struct DaoWallet {
     bullas: Vec<DaoBulla>,
     params: Vec<DaoParams>,
     own_coins: Vec<(OwnCoin, bool)>,
+    proposals: Vec<Proposal>,
     vote_notes: Vec<dao_contract::vote::wallet::Note>,
 }
 impl DaoWallet {
@@ -384,6 +410,7 @@ impl DaoWallet {
         let bullas = Vec::new();
         let params = Vec::new();
         let own_coins: Vec<(OwnCoin, bool)> = Vec::new();
+        let proposals: Vec<Proposal> = Vec::new();
         let vote_notes = Vec::new();
 
         Self {
@@ -394,6 +421,7 @@ impl DaoWallet {
             bullas,
             params,
             own_coins,
+            proposals,
             vote_notes,
         }
     }
@@ -461,7 +489,7 @@ impl DaoWallet {
         Ok(balances)
     }
 
-    fn read_proposal(&self, tx: &Transaction) -> Result<(Proposal, pallas::Base)> {
+    fn store_proposal(&mut self, tx: &Transaction) -> Result<pallas::Base> {
         let (proposal, proposal_bulla) = {
             let func_call = &tx.func_calls[0];
             let call_data = func_call.call_data.as_any();
@@ -481,11 +509,12 @@ impl DaoWallet {
         debug!(target: "demo", "  token_id: {:?}", proposal.token_id);
         debug!(target: "demo", "Proposal bulla: {:?}", proposal_bulla);
 
-        Ok((proposal, proposal_bulla))
+        self.proposals.push(proposal);
+        Ok(proposal_bulla)
     }
 
     // We decrypt the votes in a transaction and add it to the wallet.
-    fn read_vote(&mut self, tx: &Transaction) -> Result<()> {
+    fn store_vote(&mut self, tx: &Transaction) -> Result<()> {
         let vote_note = {
             let func_call = &tx.func_calls[0];
             let call_data = func_call.call_data.as_any();
@@ -500,15 +529,17 @@ impl DaoWallet {
 
         self.vote_notes.push(vote_note);
 
-        // TODO: this should print from the CLI rather than use debug statements.
-        // TODO: maybe this its own method? get votes
-        //debug!(target: "demo", "User voted!");
-        //debug!(target: "demo", "  vote_option: {}", vote_note.vote.vote_option);
-        //debug!(target: "demo", "  value: {}", vote_note.vote_value);
-
         Ok(())
     }
 
+    fn get_proposals(&self) -> &Vec<Proposal> {
+        &self.proposals
+    }
+
+    fn get_votes(&self) -> &Vec<dao_contract::vote::wallet::Note> {
+        &self.vote_notes
+    }
+
     // TODO: Explicit error handling.
     fn get_treasury_path(
         &self,
@@ -774,15 +805,13 @@ impl MoneyWallet {
         Ok((money_leaf_position, money_merkle_path))
     }
 
-    // TODO: User must have the values Proposal and DaoParams in order to cast a vote.
-    // These should be encoded to base58 and printed to command-line when a DAO is made (DaoParams)
-    // and a Proposal is made (Proposal). Then the user loads a base58 string into the vote request.
     fn vote_tx(
         &mut self,
         vote_option: bool,
         dao_key: Keypair,
         proposal: Proposal,
         dao_params: DaoParams,
+        dao_keypair: Keypair,
         zk_bins: &ZkContractTable,
         states: &mut StateRegistry,
     ) -> Result<Transaction> {
@@ -811,7 +840,8 @@ impl MoneyWallet {
                     vote_option,
                     vote_option_blind: pallas::Scalar::random(&mut OsRng),
                 },
-                vote_keypair: self.keypair,
+                // For this demo votes are encrypted for the DAO.
+                vote_keypair: dao_keypair,
                 proposal: proposal.clone(),
                 dao: dao_params.clone(),
             }

+ 56 - 30
bin/dao/daod/src/rpc.rs

@@ -39,6 +39,8 @@ impl RequestHandler for JsonRpcInterface {
         match req.method.as_str() {
             Some("create") => return self.create_dao(req.id, params).await,
             Some("get_dao_addr") => return self.get_dao_addr(req.id, params).await,
+            Some("get_votes") => return self.get_votes(req.id, params).await,
+            Some("get_proposals") => return self.get_proposals(req.id, params).await,
             Some("dao_balance") => return self.dao_balance(req.id, params).await,
             Some("dao_bulla") => return self.dao_bulla(req.id, params).await,
             Some("user_balance") => return self.user_balance(req.id, params).await,
@@ -92,7 +94,42 @@ impl JsonRpcInterface {
         JsonResponse::new(json!(addr), id).into()
     }
 
+    // --> {"method": "get_dao_addr", "params": []}
+    // <-- {"result": "getting dao public addr..."}
+    async fn get_votes(&self, id: Value, params: &[Value]) -> JsonResult {
+        let mut client = self.client.lock().await;
+        let vote_notes = client.dao_wallet.get_votes();
+        let mut vote_data = vec![];
+
+        for note in vote_notes {
+            let vote_option = note.vote.vote_option;
+            let vote_value = note.vote_value;
+            vote_data.push((vote_option, vote_value));
+        }
+
+        JsonResponse::new(json!(vote_data), id).into()
+    }
+
+    // --> {"method": "get_dao_addr", "params": []}
+    // <-- {"result": "getting dao public addr..."}
+    async fn get_proposals(&self, id: Value, params: &[Value]) -> JsonResult {
+        let mut client = self.client.lock().await;
+        let proposals = client.dao_wallet.get_proposals();
+        let mut proposal_data = vec![];
+
+        for proposal in proposals {
+            let dest = proposal.dest;
+            let amount = proposal.amount;
+            let token_id = proposal.token_id;
+            let token_id: String = bs58::encode(token_id.to_repr()).into_string();
+            proposal_data.push((dest, amount, token_id));
+        }
+
+        JsonResponse::new(json!(proposal_data), id).into()
+    }
+
     async fn dao_balance(&self, id: Value, params: &[Value]) -> JsonResult {
+        // TODO: token id
         let mut client = self.client.lock().await;
         let balance = client.dao_wallet.balances().unwrap();
         JsonResponse::new(json!(balance), id).into()
@@ -131,7 +168,7 @@ impl JsonRpcInterface {
         client.mint_treasury(*XDRK_ID, token_supply, dao_addr).unwrap();
         //let balance = client.query_dao_balance().unwrap();
 
-        JsonResponse::new(json!("minted treasury"), id).into()
+        JsonResponse::new(json!("DAO treasury minted successfully."), id).into()
     }
 
     // Create a new wallet for governance tokens.
@@ -161,7 +198,7 @@ impl JsonRpcInterface {
         client.airdrop_user(value, *GDRK_ID, nym.clone()).unwrap();
         //let balance = client.query_balance(nym.clone()).unwrap();
 
-        JsonResponse::new(json!("tokens airdropped"), id).into()
+        JsonResponse::new(json!("Tokens airdropped successfully."), id).into()
     }
     // --> {"method": "create_proposal", "params": []}
     // <-- {"result": "creating proposal..."}
@@ -175,40 +212,29 @@ impl JsonRpcInterface {
 
         let recv_addr = PublicKey::from_str(recipient).unwrap();
 
-        let (proposal, proposal_bulla) =
-            client.propose(recv_addr, *XDRK_ID, amount, sender).unwrap();
+        let proposal_bulla = client.propose(recv_addr, *XDRK_ID, amount, sender).unwrap();
         let bulla: String = bs58::encode(proposal_bulla.to_repr()).into_string();
-        let token_id: String = bs58::encode(proposal.token_id.to_repr()).into_string();
-        let addr: String = bs58::encode(proposal.dest.to_bytes()).into_string();
 
-        let mut proposal_vec = Vec::new();
-
-        proposal_vec.push("Proposal now active!".to_string());
-        proposal_vec.push(format!("destination: {:?}", addr.to_string()));
-        proposal_vec.push(format!("amount: {:?}", proposal.amount.to_string()));
-        proposal_vec.push(format!("token_id: {:?}", token_id));
-        proposal_vec.push(format!("bulla: {:?}", bulla));
-
-        JsonResponse::new(json!(proposal_vec), id).into()
+        JsonResponse::new(json!(bulla), id).into()
     }
     // --> {"method": "vote", "params": []}
     // <-- {"result": "voting..."}
-    // TODO: pass string 'alice', dao bulla, and Proposal
-    // TODO: must pass yes or no, convert to bool
-    async fn vote(&self, id: Value, _params: &[Value]) -> JsonResult {
+    async fn vote(&self, id: Value, params: &[Value]) -> JsonResult {
         let mut client = self.client.lock().await;
-        // let dao_params = self.client.client.dao_wallet.params.get(bulla);
-        // let dao_key = self.client.client.dao_wallet.keypair.private;
-        //
-        // client.client.money_wallets.get(alice) {
-        //      Some(wallet) => {
-        //      wallet.vote(dao_params)
-        //      let tx = wallet.vote(dao_params, vote_option, proposal)
-        //      client.client.validate(tx);
-        //      client.client.dao_wallet.read_vote(tx);
-        //      }
-        // }
-        //
+
+        let nym = params[0].as_str().unwrap().to_string();
+        let vote_str = params[1].as_str().unwrap();
+
+        // This would be cleaner as a match statement,
+        // but we need to sort out error handling first.
+        let mut vote_bool = true;
+
+        if vote_str == "yes" {}
+        if vote_str == "no" {
+            vote_bool = false
+        }
+
+        client.cast_vote(nym, vote_bool).unwrap();
         JsonResponse::new(json!("voted"), id).into()
     }
     // --> {"method": "execute", "params": []}

+ 0 - 1
bin/dao/daod/src/util.rs

@@ -164,7 +164,6 @@ impl Transaction {
             func_call.encode(&mut unsigned_tx_data).expect("failed to encode data");
             let signature_pub_keys = func_call.call_data.signature_public_keys();
             for signature_pub_key in signature_pub_keys {
-                debug!(target: "dao-demo::util::verify_sigs()", "{:?}", signature_pub_key);
                 let verify_result = signature_pub_key.verify(&unsigned_tx_data[..], &signature);
                 assert!(verify_result, "verify sigs[{}] failed", i);
             }