Преглед изворни кода

check the DAO bulla is valid inside DAO::propose()

narodnik пре 4 година
родитељ
комит
88cb9c720b

+ 16 - 2
bin/daod/proof/dao-propose-main.zk

@@ -2,6 +2,15 @@ constant "DaoProposeMain" {
 }
 
 contract "DaoProposeMain" {
+    # proposal params
+    #Base proposal_x,
+    #Base proposal_y,
+    #Base proposal_amount,
+    #Base proposal_serial,
+    #Base proposal_token_id,
+    #Base proposal_blind,
+
+    # DAO params
     Base dao_proposer_limit,
     Base dao_quorum,
     Base dao_approval_ratio,
@@ -9,10 +18,13 @@ contract "DaoProposeMain" {
     Base dao_public_x,
     Base dao_public_y,
     Base dao_bulla_blind,
+
+	Uint32 dao_leaf_pos,
+	MerklePath dao_path,
 }
 
 circuit "DaoProposeMain" {
-    bulla = poseidon_hash(
+    dao_bulla = poseidon_hash(
         dao_proposer_limit,
         dao_quorum,
         dao_approval_ratio,
@@ -23,6 +35,8 @@ circuit "DaoProposeMain" {
         # @tmp-workaround
         dao_bulla_blind,
     );
-    constrain_instance(bulla);
+	dao_root = calculate_merkle_root(dao_leaf_pos, dao_path, dao_bulla);
+	constrain_instance(dao_root);
+    # Proves this DAO is valid
 }
 

+ 24 - 6
bin/daod/src/dao_contract/propose/validate.rs

@@ -2,7 +2,8 @@ use crate::{
     dao_contract::{DaoBulla, State},
     demo::{CallDataBase, StateRegistry, Transaction},
 };
-use darkfi::crypto::types::DrkCircuitField;
+use darkfi::crypto::{merkle_node::MerkleNode, types::DrkCircuitField};
+use log::{debug, error};
 use pasta_curves::{
     arithmetic::CurveAffine,
     group::{ff::Field, Curve},
@@ -10,21 +11,22 @@ use pasta_curves::{
 };
 use std::any::{Any, TypeId};
 
+const TARGET: &str = "dao_contract::propose::validate::state_transition()";
+
 #[derive(Debug, Clone, thiserror::Error)]
 pub enum Error {
-    #[error("Malformed packet")]
-    MalformedPacket,
+    #[error("Invalid DAO merkle root")]
+    InvalidDaoMerkleRoot,
 }
 type Result<T> = std::result::Result<T, Error>;
 
 pub struct CallData {
-    // TODO: remove this! only tmp
-    pub dao_bulla: pallas::Base,
+    pub dao_merkle_root: MerkleNode,
 }
 
 impl CallDataBase for CallData {
     fn zk_public_values(&self) -> Vec<Vec<DrkCircuitField>> {
-        vec![vec![self.dao_bulla]]
+        vec![vec![self.dao_merkle_root.0]]
     }
 
     fn zk_proof_addrs(&self) -> Vec<String> {
@@ -41,6 +43,22 @@ pub fn state_transition(
     func_call_index: usize,
     parent_tx: &Transaction,
 ) -> Result<Update> {
+    let func_call = &parent_tx.func_calls[func_call_index];
+    let call_data = func_call.call_data.as_any();
+
+    assert_eq!((&*call_data).type_id(), TypeId::of::<CallData>());
+    let call_data = call_data.downcast_ref::<CallData>();
+
+    // This will be inside wasm so unwrap is fine.
+    let call_data = call_data.unwrap();
+
+    let state = states.lookup::<State>(&"DAO".to_string()).unwrap();
+
+    // Is the DAO bulla generated in the ZK proof valid
+    if !state.is_valid_dao_merkle(&call_data.dao_merkle_root) {
+        return Err(Error::InvalidDaoMerkleRoot)
+    }
+
     Ok(Update {})
 }
 

+ 25 - 3
bin/daod/src/dao_contract/propose/wallet.rs

@@ -26,7 +26,7 @@ use darkfi::{
 
 use crate::{
     dao_contract::propose::validate::CallData,
-    demo::{CallDataBase, FuncCall, ZkContractInfo, ZkContractTable},
+    demo::{CallDataBase, FuncCall, StateRegistry, ZkContractInfo, ZkContractTable},
     money_contract,
     util::poseidon_hash,
 };
@@ -59,10 +59,19 @@ pub struct Builder {
     pub inputs: Vec<Input>,
     pub proposal: Proposal,
     pub dao: DaoParams,
+    pub dao_leaf_position: incrementalmerkletree::Position,
+    pub dao_merkle_path: Vec<MerkleNode>,
+    pub dao_merkle_root: MerkleNode,
 }
 
 impl Builder {
     pub fn build(self, zk_bins: &ZkContractTable) -> FuncCall {
+        let proposal_dest_coords = self.proposal.dest.0.to_affine().coordinates().unwrap();
+        let proposal_dest_x = *proposal_dest_coords.x();
+        let proposal_dest_y = *proposal_dest_coords.y();
+
+        let proposal_amount = pallas::Base::from(self.proposal.amount);
+
         let dao_proposer_limit = pallas::Base::from(self.dao.proposer_limit);
         let dao_quorum = pallas::Base::from(self.dao.quorum);
         let dao_approval_ratio = pallas::Base::from(self.dao.approval_ratio);
@@ -83,6 +92,8 @@ impl Builder {
             self.dao.bulla_blind,
         ]);
 
+        let dao_leaf_position: u64 = self.dao_leaf_position.into();
+
         let zk_info = zk_bins.lookup(&"dao-propose-main".to_string()).unwrap();
         let zk_info = if let ZkContractInfo::Binary(info) = zk_info {
             info
@@ -91,6 +102,15 @@ impl Builder {
         };
         let zk_bin = zk_info.bincode.clone();
         let prover_witnesses = vec![
+            // proposal params
+            //Witness::Base(Value::known(proposal_dest_x)),
+            //Witness::Base(Value::known(proposal_dest_y)),
+            //Witness::Base(Value::known(proposal_amount)),
+            //Witness::Base(Value::known(self.proposal.serial)),
+            //Witness::Base(Value::known(self.proposal.token_id)),
+            //Witness::Base(Value::known(self.proposal.blind)),
+
+            // DAO params
             Witness::Base(Value::known(dao_proposer_limit)),
             Witness::Base(Value::known(dao_quorum)),
             Witness::Base(Value::known(dao_approval_ratio)),
@@ -98,15 +118,17 @@ impl Builder {
             Witness::Base(Value::known(dao_public_x)),
             Witness::Base(Value::known(dao_public_y)),
             Witness::Base(Value::known(self.dao.bulla_blind)),
+            Witness::Uint32(Value::known(dao_leaf_position.try_into().unwrap())),
+            Witness::MerklePath(Value::known(self.dao_merkle_path.try_into().unwrap())),
         ];
-        let public_inputs = vec![dao_bulla];
+        let public_inputs = vec![self.dao_merkle_root.0];
         let circuit = ZkCircuit::new(prover_witnesses, zk_bin);
 
         let proving_key = &zk_info.proving_key;
         let main_proof = Proof::create(proving_key, &[circuit], &public_inputs, &mut OsRng)
             .expect("DAO::propose() proving error!");
 
-        let call_data = CallData { dao_bulla };
+        let call_data = CallData { dao_merkle_root: self.dao_merkle_root };
 
         FuncCall {
             contract_id: "DAO".to_string(),

+ 28 - 1
bin/daod/src/dao_contract/state.rs

@@ -1,6 +1,18 @@
+use incrementalmerkletree::{bridgetree::BridgeTree, Tree};
 use pasta_curves::pallas;
 use std::any::{Any, TypeId};
 
+use darkfi::{
+    crypto::{
+        constants::MERKLE_DEPTH,
+        keypair::{Keypair, PublicKey, SecretKey},
+        merkle_node::MerkleNode,
+        nullifier::Nullifier,
+        proof::VerifyingKey,
+    },
+    node::state::{ProgramState, StateUpdate},
+};
+
 use crate::{
     dao_contract::mint::validate::CallData,
     demo::{StateRegistry, Transaction},
@@ -10,17 +22,32 @@ use crate::{
 #[derive(Clone)]
 pub struct DaoBulla(pub pallas::Base);
 
+type MerkleTree = BridgeTree<MerkleNode, MERKLE_DEPTH>;
+
 /// This DAO state is for all DAOs on the network. There should only be a single instance.
 pub struct State {
     dao_bullas: Vec<DaoBulla>,
+    pub dao_tree: MerkleTree,
+    pub dao_roots: Vec<MerkleNode>,
 }
 
 impl State {
     pub fn new() -> Box<dyn Any> {
-        Box::new(Self { dao_bullas: Vec::new() })
+        Box::new(Self {
+            dao_bullas: Vec::new(),
+            dao_tree: MerkleTree::new(100),
+            dao_roots: Vec::new(),
+        })
     }
 
     pub fn add_bulla(&mut self, bulla: DaoBulla) {
+        let node = MerkleNode(bulla.0);
         self.dao_bullas.push(bulla);
+        self.dao_tree.append(&node);
+        self.dao_roots.push(self.dao_tree.root(0).unwrap());
+    }
+
+    pub fn is_valid_dao_merkle(&self, root: &MerkleNode) -> bool {
+        self.dao_roots.iter().any(|m| m == root)
     }
 }

+ 26 - 3
bin/daod/src/demo.rs

@@ -301,6 +301,18 @@ pub async fn demo() -> Result<()> {
     tx.zk_verify(&zk_bins);
 
     // Wallet stuff
+
+    // In your wallet, wait until you see the tx confirmed before doing anything below
+    // So for example keep track of tx hash
+    //assert_eq!(tx.hash(), tx_hash);
+
+    // We need to witness() the value in our local merkle tree
+    // Must be called as soon as this DAO bulla is added to the state
+    let dao_leaf_position = {
+        let state = states.lookup_mut::<dao_contract::State>(&"DAO".to_string()).unwrap();
+        state.dao_tree.witness().unwrap()
+    };
+
     // It might just be easier to hash it ourselves from keypair and blind...
     let dao_bulla = {
         assert_eq!(tx.func_calls.len(), 1);
@@ -554,7 +566,7 @@ pub async fn demo() -> Result<()> {
 
     let user_keypair = Keypair::random(&mut OsRng);
 
-    let (leaf_position, merkle_path) = {
+    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[0].leaf_position.clone();
@@ -568,8 +580,16 @@ pub async fn demo() -> Result<()> {
     let input = dao_contract::propose::wallet::Input {
         secret: gov_keypair_1.secret,
         note: gov_recv[0].note.clone(),
-        leaf_position,
-        merkle_path,
+        leaf_position: money_leaf_position,
+        merkle_path: money_merkle_path,
+    };
+
+    let (dao_merkle_path, dao_merkle_root) = {
+        let state = states.lookup::<dao_contract::State>(&"DAO".to_string()).unwrap();
+        let tree = &state.dao_tree;
+        let root = tree.root(0).unwrap();
+        let merkle_path = tree.authentication_path(dao_leaf_position, &root).unwrap();
+        (merkle_path, root)
     };
 
     let builder = dao_contract::propose::wallet::Builder {
@@ -589,6 +609,9 @@ pub async fn demo() -> Result<()> {
             public_key: dao_keypair.public,
             bulla_blind: dao_bulla_blind,
         },
+        dao_leaf_position,
+        dao_merkle_path,
+        dao_merkle_root,
     };
 
     let func_call = builder.build(&zk_bins);