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

dao_demo: execute DAO proposal from command-line. Includes a bug fix for proof/dao-exec.zk and exex contracts.

lunar-mining 3 лет назад
Родитель
Сommit
4585177ec3

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

@@ -61,7 +61,9 @@ pub enum CliDaoSubCommands {
         vote: String,
     },
     /// Execute
-    Exec {},
+    Exec {
+        bulla: String,
+    },
 }
 
 /// DAO cli
@@ -156,8 +158,8 @@ async fn start(options: CliDao) -> Result<()> {
             println!("Server replied: {}", &reply.to_string());
             return Ok(())
         }
-        Some(CliDaoSubCommands::Exec {}) => {
-            let reply = client.exec().await?;
+        Some(CliDaoSubCommands::Exec { bulla }) => {
+            let reply = client.exec(bulla).await?;
             println!("Server replied: {}", &reply.to_string());
             return Ok(())
         }

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

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

+ 1 - 1
bin/dao/daod/proof/dao-exec.zk

@@ -91,7 +91,7 @@ circuit "DaoExec" {
        proposal_token_id,
        dao_serial,
        dao_spend_hook,
-       proposal_bulla,
+       dao_bulla,
        dao_coin_blind,
     );
     constrain_instance(coin_1);

+ 2 - 0
bin/dao/daod/src/contract/dao_contract/exec/validate.rs

@@ -17,6 +17,8 @@ use crate::{
     util::{CallDataBase, HashableBase, StateRegistry, Transaction, UpdateBase},
 };
 
+use log::debug;
+
 type Result<T> = std::result::Result<T, Error>;
 
 #[derive(Debug, Clone, thiserror::Error)]

+ 2 - 3
bin/dao/daod/src/contract/dao_contract/exec/wallet.rs

@@ -40,7 +40,6 @@ pub struct Builder {
 impl Builder {
     pub fn build(self, zk_bins: &ZkContractTable) -> FuncCall {
         debug!(target: "dao_contract::exec::wallet::Builder", "build()");
-        debug!(target: "dao_contract::exec::wallet", "proposalserial{:?}", self.proposal.serial);
         let mut proofs = vec![];
 
         let proposal_dest_coords = self.proposal.dest.0.to_affine().coordinates().unwrap();
@@ -100,7 +99,7 @@ impl Builder {
             self.proposal.token_id,
             self.dao_serial,
             self.hook_dao_exec,
-            proposal_bulla,
+            dao_bulla,
             self.dao_coin_blind,
         ]);
 
@@ -123,6 +122,7 @@ impl Builder {
         let zk_bin = zk_info.bincode.clone();
 
         let prover_witnesses = vec![
+            //
             // proposal params
             Witness::Base(Value::known(*proposal_dest_coords.x())),
             Witness::Base(Value::known(*proposal_dest_coords.y())),
@@ -173,7 +173,6 @@ impl Builder {
         ];
 
         let circuit = ZkCircuit::new(prover_witnesses, zk_bin);
-        debug!(target: "example_contract::foo::wallet::Builder", "input_proof Proof::create()");
         let proving_key = &zk_info.proving_key;
         let input_proof = Proof::create(proving_key, &[circuit], &public_inputs, &mut OsRng)
             .expect("DAO::exec() proving error!)");

+ 2 - 0
bin/dao/daod/src/contract/dao_contract/vote/validate.rs

@@ -22,6 +22,8 @@ use crate::{
     util::{CallDataBase, StateRegistry, Transaction, UpdateBase},
 };
 
+use log::debug;
+
 #[derive(Debug, Clone, thiserror::Error)]
 pub enum Error {
     #[error("Invalid proposal")]

+ 7 - 1
bin/dao/daod/src/contract/money_contract/transfer/wallet.rs

@@ -1,4 +1,8 @@
-use pasta_curves::group::ff::Field;
+use pasta_curves::{
+    arithmetic::CurveAffine,
+    group::{ff::Field, Curve},
+    pallas,
+};
 use rand::rngs::OsRng;
 
 use darkfi::{
@@ -25,6 +29,8 @@ use crate::{
     util::{FuncCall, ZkContractInfo, ZkContractTable},
 };
 
+use log::debug;
+
 #[derive(Clone, SerialEncodable, SerialDecodable)]
 pub struct Note {
     pub serial: DrkSerial,

+ 61 - 16
bin/dao/daod/src/main.rs

@@ -167,7 +167,6 @@ impl Client {
             call_data.dao_bulla.clone()
         };
 
-        // TODO: instead of this print statement, return DAO bulla to CLI
         debug!(target: "demo", "Create DAO bulla: {:?}", dao_bulla.0);
 
         // We create a hashmap so we can easily retrieve DAO values for the demo.
@@ -388,6 +387,21 @@ impl Client {
 
         Ok(())
     }
+
+    fn exec_proposal(&mut self, bulla: pallas::Base) -> Result<()> {
+        let proposal = self.dao_wallet.proposals[0].clone();
+        let dao_params = self.dao_wallet.params[0].clone();
+
+        let tx = self
+            .dao_wallet
+            .exec_tx(proposal, bulla, dao_params, &self.zk_bins, &mut self.states)
+            .unwrap();
+
+        self.validate(&tx).unwrap();
+        self.update_wallets().unwrap();
+
+        Ok(())
+    }
 }
 
 struct DaoWallet {
@@ -395,6 +409,7 @@ struct DaoWallet {
     signature_secret: SecretKey,
     bulla_blind: pallas::Base,
     leaf_position: Position,
+    proposal_bullas: Vec<pallas::Base>,
     bullas: Vec<DaoBulla>,
     params: Vec<DaoParams>,
     own_coins: Vec<(OwnCoin, bool)>,
@@ -407,6 +422,7 @@ impl DaoWallet {
         let signature_secret = SecretKey::random(&mut OsRng);
         let bulla_blind = pallas::Base::random(&mut OsRng);
         let leaf_position = Position::zero();
+        let proposal_bullas = Vec::new();
         let bullas = Vec::new();
         let params = Vec::new();
         let own_coins: Vec<(OwnCoin, bool)> = Vec::new();
@@ -418,6 +434,7 @@ impl DaoWallet {
             signature_secret,
             bulla_blind,
             leaf_position,
+            proposal_bullas,
             bullas,
             params,
             own_coins,
@@ -510,6 +527,8 @@ impl DaoWallet {
         debug!(target: "demo", "Proposal bulla: {:?}", proposal_bulla);
 
         self.proposals.push(proposal);
+        self.proposal_bullas.push(proposal_bulla);
+
         Ok(proposal_bulla)
     }
 
@@ -559,7 +578,7 @@ impl DaoWallet {
         Ok((money_leaf_position, money_merkle_path))
     }
 
-    fn build_exec_tx(
+    fn exec_tx(
         &self,
         proposal: Proposal,
         proposal_bulla: pallas::Base,
@@ -567,6 +586,8 @@ impl DaoWallet {
         zk_bins: &ZkContractTable,
         states: &mut StateRegistry,
     ) -> Result<Transaction> {
+        let dao_bulla = self.bullas[0].clone();
+
         let mut inputs = Vec::new();
         let mut total_input_value = 0;
         // TODO: move these to DAO struct?
@@ -581,8 +602,10 @@ impl DaoWallet {
         let input_value_blind = pallas::Scalar::random(&mut OsRng);
         let dao_serial = pallas::Base::random(&mut OsRng);
         let dao_coin_blind = pallas::Base::random(&mut OsRng);
+        // disabled
+        let user_spend_hook = pallas::Base::from(0);
+        let user_data = pallas::Base::from(0);
 
-        // We must prove we have sufficient governance tokens to execute this.
         for (coin, is_spent) in &self.own_coins {
             let is_spent = is_spent.clone();
             if is_spent {
@@ -621,22 +644,20 @@ impl DaoWallet {
                         public: proposal.dest,
                         serial: proposal.serial,
                         coin_blind: proposal.blind,
-                        spend_hook: pallas::Base::from(0),
-                        user_data: pallas::Base::from(0),
+                        spend_hook: user_spend_hook,
+                        user_data,
                     },
                     // Change back to DAO
                     money_contract::transfer::wallet::BuilderOutputInfo {
                         value: total_input_value - proposal.amount,
-                        // TODO: this should be the token id of the treasury,
-                        // rather than the token id of the proposal.
-                        // total_id: own_coin.token_id,
-                        token_id: proposal.token_id,
+                        // TODO: Token id of the treasury.
+                        token_id: *XDRK_ID,
                         public: self.keypair.public,
                         // ?
                         serial: dao_serial,
                         coin_blind: dao_coin_blind,
                         spend_hook: *dao_contract::exec::FUNC_ID,
-                        user_data: proposal_bulla,
+                        user_data: dao_bulla.0,
                     },
                 ],
             }
@@ -646,20 +667,43 @@ impl DaoWallet {
 
         let mut yes_votes_value = 0;
         let mut yes_votes_blind = pallas::Scalar::from(0);
+        let mut yes_votes_commit = pallas::Point::identity();
 
         let mut all_votes_value = 0;
         let mut all_votes_blind = pallas::Scalar::from(0);
+        let mut all_votes_commit = pallas::Point::identity();
+
+        for (i, note) in self.vote_notes.iter().enumerate() {
+            let vote_commit = pedersen_commitment_u64(note.vote_value, note.vote_value_blind);
+
+            all_votes_commit += vote_commit;
+            all_votes_blind += note.vote_value_blind;
+
+            let yes_vote_commit = pedersen_commitment_u64(
+                note.vote.vote_option as u64 * note.vote_value,
+                note.vote.vote_option_blind,
+            );
+
+            yes_votes_commit += yes_vote_commit;
+            yes_votes_blind += note.vote.vote_option_blind;
 
-        for note in &self.vote_notes {
-            if note.vote.vote_option {
-                // this is a yes vote
+            let vote_option = note.vote.vote_option;
+
+            if vote_option {
                 yes_votes_value += note.vote_value;
-                yes_votes_blind += note.vote_value_blind;
             }
             all_votes_value += note.vote_value;
-            all_votes_blind += note.vote_value_blind;
+            let vote_result: String =
+                if vote_option { "yes".to_string() } else { "no".to_string() };
+
+            debug!("Voter {} voted {}", i, vote_result);
         }
 
+        debug!("Outcome = {} / {}", yes_votes_value, all_votes_value);
+
+        assert!(all_votes_commit == pedersen_commitment_u64(all_votes_value, all_votes_blind));
+        assert!(yes_votes_commit == pedersen_commitment_u64(yes_votes_value, yes_votes_blind));
+
         let builder = {
             dao_contract::exec::wallet::Builder {
                 proposal: proposal.clone(),
@@ -672,7 +716,7 @@ impl DaoWallet {
                 user_coin_blind,
                 dao_serial,
                 dao_coin_blind,
-                input_value: proposal.amount,
+                input_value: total_input_value,
                 input_value_blind,
                 hook_dao_exec: *dao_contract::exec::FUNC_ID,
                 signature_secret: exec_signature_secret,
@@ -846,6 +890,7 @@ impl MoneyWallet {
                 dao: dao_params.clone(),
             }
         };
+
         let func_call = builder.build(zk_bins);
         let func_calls = vec![func_call];
 

+ 7 - 3
bin/dao/daod/src/rpc.rs

@@ -239,10 +239,14 @@ impl JsonRpcInterface {
     }
     // --> {"method": "execute", "params": []}
     // <-- {"result": "executing..."}
-    async fn execute(&self, id: Value, _params: &[Value]) -> JsonResult {
+    async fn execute(&self, id: Value, params: &[Value]) -> JsonResult {
         let mut client = self.client.lock().await;
-        // client.client.dao_wallet.build_exec_tx(proposal, proposal_bulla)
-        //client.exec().unwrap();
+
+        let bulla_str = params[0].as_str().unwrap();
+        let bulla: pallas::Base = parse_b58(bulla_str).unwrap();
+
+        client.exec_proposal(bulla);
+
         JsonResponse::new(json!("executed"), id).into()
     }
 }

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

@@ -1244,6 +1244,7 @@ pub async fn demo() -> Result<()> {
                 serial: dao_serial,
                 coin_blind: dao_coin_blind,
                 spend_hook: *dao_contract::exec::FUNC_ID,
+                // TODO: should be DAO bulla
                 user_data: proposal_bulla,
             },
         ],