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

attach private inputs from my wallet for DAO::propose() and prove my_tokens > proposer_limit

narodnik пре 4 година
родитељ
комит
67f4a53ed2

+ 61 - 0
bin/daod/proof/dao-propose-burn.zk

@@ -0,0 +1,61 @@
+constant "DaoProposeInput" {
+	EcFixedPointShort VALUE_COMMIT_VALUE,
+	EcFixedPoint VALUE_COMMIT_RANDOM,
+	EcFixedPointBase NULLIFIER_K,
+}
+
+contract "DaoProposeInput" {
+	Base secret,
+	Base serial,
+	Base value,
+	Base token,
+	Base coin_blind,
+	Scalar value_blind,
+	Base token_blind,
+	Uint32 leaf_pos,
+	MerklePath path,
+	Base signature_secret,
+}
+
+circuit "DaoProposeInput" {
+	# Poseidon hash of the nullifier
+	#nullifier = poseidon_hash(secret, serial);
+	#constrain_instance(nullifier);
+
+	# Pedersen commitment for coin's value
+	vcv = ec_mul_short(value, VALUE_COMMIT_VALUE);
+	vcr = ec_mul(value_blind, VALUE_COMMIT_RANDOM);
+	value_commit = ec_add(vcv, vcr);
+	# Since value_commit is a curve point, we fetch its coordinates
+	# and constrain them:
+	value_commit_x = ec_get_x(value_commit);
+	value_commit_y = ec_get_y(value_commit);
+	constrain_instance(value_commit_x);
+	constrain_instance(value_commit_y);
+
+	# Commitment for coin's token ID
+    token_commit = poseidon_hash(token, token_blind);
+    constrain_instance(token_commit);
+
+	# Coin hash
+	pub = ec_mul_base(secret, NULLIFIER_K);
+	pub_x = ec_get_x(pub);
+	pub_y = ec_get_y(pub);
+	C = poseidon_hash(pub_x, pub_y, value, token, serial, coin_blind);
+
+	# Merkle root
+	root = calculate_merkle_root(leaf_pos, path, C);
+	constrain_instance(root);
+
+	# Finally, we derive a public key for the signature and
+	# constrain its coordinates:
+	signature_public = ec_mul_base(signature_secret, NULLIFIER_K);
+	signature_x = ec_get_x(signature_public);
+	signature_y = ec_get_y(signature_public);
+	constrain_instance(signature_x);
+	constrain_instance(signature_y);
+
+	# At this point we've enforced all of our public inputs.
+}
+
+

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

@@ -12,8 +12,8 @@ contract "DaoProposeMain" {
     Base gov_token_blind,
 
     # proposal params
-    Base proposal_x,
-    Base proposal_y,
+    Base proposal_dest_x,
+    Base proposal_dest_y,
     Base proposal_amount,
     Base proposal_serial,
     Base proposal_token_id,
@@ -51,6 +51,19 @@ circuit "DaoProposeMain" {
 	constrain_instance(dao_root);
     # Proves this DAO is valid
 
+    proposal_bulla = poseidon_hash(
+        proposal_dest_x,
+        proposal_dest_y,
+        proposal_amount,
+        proposal_serial,
+        proposal_token_id,
+        dao_bulla,
+        proposal_blind,
+        # @tmp-workaround
+        proposal_blind,
+    );
+    constrain_instance(proposal_bulla);
+
     # Rangeproof check for proposal amount
     # TODO: waiting on this opcode in zkas
     #
@@ -59,6 +72,7 @@ circuit "DaoProposeMain" {
     # Use this temporary workaround. ec_mul_short() does an internal rangeproof
     rangeproof = ec_mul_short(proposal_amount, VALUE_COMMIT_VALUE);
 
+    # This is the main check
     # TODO: check total_funds >= proposer_limit
     #
     #     greater_than_or_equal(total_funds, proposer_limit)

+ 1 - 1
bin/daod/src/dao_contract/mint/validate.rs

@@ -35,7 +35,7 @@ pub fn apply(states: &mut StateRegistry, update: Update) {
     // Lookup dao_contract state from registry
     let state = states.lookup_mut::<State>(&"DAO".to_string()).unwrap();
     // Add dao_bulla to state.dao_bullas
-    state.add_bulla(update.dao_bulla);
+    state.add_dao_bulla(update.dao_bulla);
 }
 
 #[derive(Debug, Clone, thiserror::Error)]

+ 43 - 5
bin/daod/src/dao_contract/propose/validate.rs

@@ -47,22 +47,51 @@ pub struct CallData {
 
 impl CallDataBase for CallData {
     fn zk_public_values(&self) -> Vec<Vec<DrkCircuitField>> {
+        let mut zk_publics = Vec::new();
+
+        for input in &self.inputs {
+            let value_coords = input.value_commit.to_affine().coordinates().unwrap();
+            let value_commit_x = *value_coords.x();
+            let value_commit_y = *value_coords.y();
+
+            let sigpub_coords = input.signature_public.0.to_affine().coordinates().unwrap();
+            let sigpub_x = *sigpub_coords.x();
+            let sigpub_y = *sigpub_coords.y();
+
+            zk_publics.push(vec![
+                value_commit_x,
+                value_commit_y,
+                self.header.token_commit,
+                input.merkle_root.0,
+                sigpub_x,
+                sigpub_y,
+            ]);
+        }
+
         let total_funds_coords = self.header.total_funds_commit.to_affine().coordinates().unwrap();
         let total_funds_x = *total_funds_coords.x();
         let total_funds_y = *total_funds_coords.y();
-        vec![
+        zk_publics.push(
             // dao-propose-main proof
             vec![
                 self.header.token_commit,
                 self.header.dao_merkle_root.0,
+                self.header.proposal_bulla,
                 total_funds_x,
                 total_funds_y,
             ],
-        ]
+        );
+
+        zk_publics
     }
 
     fn zk_proof_addrs(&self) -> Vec<String> {
-        vec!["dao-propose-main".to_string()]
+        let mut zk_addrs = Vec::new();
+        for input in &self.inputs {
+            zk_addrs.push("dao-propose-burn".to_string());
+        }
+        zk_addrs.push("dao-propose-main".to_string());
+        zk_addrs
     }
 
     fn as_any(&self) -> &dyn Any {
@@ -74,12 +103,15 @@ impl CallDataBase for CallData {
 pub struct Header {
     pub dao_merkle_root: MerkleNode,
     pub token_commit: pallas::Base,
+    pub proposal_bulla: pallas::Base,
     // TODO: compute from sum of input commits
     pub total_funds_commit: pallas::Point,
 }
 
 #[derive(Clone, SerialEncodable, SerialDecodable)]
 pub struct Input {
+    pub value_commit: pallas::Point,
+    pub merkle_root: MerkleNode,
     pub signature_public: PublicKey,
 }
 
@@ -119,11 +151,17 @@ pub fn state_transition(
         }
     }
 
-    Ok(Update {})
+    // TODO: look at gov tokens avoid using already spent ones
+    // Need to spend original coin and generate 2 nullifiers?
+
+    Ok(Update { proposal_bulla: call_data.header.proposal_bulla })
 }
 
-pub struct Update {}
+pub struct Update {
+    pub proposal_bulla: pallas::Base,
+}
 
 pub fn apply(states: &mut StateRegistry, update: Update) {
     let state = states.lookup_mut::<State>(&"DAO".to_string()).unwrap();
+    state.add_proposal_bulla(update.proposal_bulla);
 }

+ 111 - 36
bin/daod/src/dao_contract/propose/wallet.rs

@@ -1,4 +1,5 @@
 use halo2_proofs::circuit::Value;
+use incrementalmerkletree::Hashable;
 use pasta_curves::{
     arithmetic::CurveAffine,
     group::{ff::Field, Curve, Group},
@@ -67,6 +68,10 @@ pub struct Builder {
 
 impl Builder {
     pub fn build(self, zk_bins: &ZkContractTable) -> FuncCall {
+        let mut proofs = vec![];
+
+        let token_blind = pallas::Base::random(&mut OsRng);
+
         let mut inputs = vec![];
         let mut total_funds = 0;
         let mut input_funds_blinds = vec![];
@@ -76,40 +81,91 @@ impl Builder {
             input_funds_blinds.push(funds_blind);
 
             let signature_secret = SecretKey::random(&mut OsRng);
+            let signature_public = PublicKey::from_secret(signature_secret);
+
+            let zk_info = zk_bins.lookup(&"dao-propose-burn".to_string()).unwrap();
+            let zk_info = if let ZkContractInfo::Binary(info) = zk_info {
+                info
+            } else {
+                panic!("Not binary info")
+            };
+            let zk_bin = zk_info.bincode.clone();
+
+            // Note from the previous output
+            let note = input.note;
+            let leaf_pos: u64 = input.leaf_position.into();
+
+            let prover_witnesses = vec![
+                Witness::Base(Value::known(input.secret.0)),
+                Witness::Base(Value::known(note.serial)),
+                Witness::Base(Value::known(pallas::Base::from(note.value))),
+                Witness::Base(Value::known(note.token_id)),
+                Witness::Base(Value::known(note.coin_blind)),
+                Witness::Scalar(Value::known(funds_blind)),
+                Witness::Base(Value::known(token_blind)),
+                Witness::Uint32(Value::known(leaf_pos.try_into().unwrap())),
+                Witness::MerklePath(Value::known(input.merkle_path.clone().try_into().unwrap())),
+                Witness::Base(Value::known(signature_secret.0)),
+            ];
+
+            let public_key = PublicKey::from_secret(input.secret);
+            let coords = public_key.0.to_affine().coordinates().unwrap();
+
+            let coin = poseidon_hash::<8>([
+                *coords.x(),
+                *coords.y(),
+                pallas::Base::from(note.value),
+                note.token_id,
+                note.serial,
+                pallas::Base::from(0),
+                pallas::Base::from(0),
+                note.coin_blind,
+            ]);
+
+            let merkle_root = {
+                let position: u64 = input.leaf_position.into();
+                let mut current = MerkleNode(coin);
+                for (level, sibling) in input.merkle_path.iter().enumerate() {
+                    let level = level as u8;
+                    current = if position & (1 << level) == 0 {
+                        MerkleNode::combine(level.into(), &current, sibling)
+                    } else {
+                        MerkleNode::combine(level.into(), sibling, &current)
+                    };
+                }
+                current
+            };
+
+            let token_commit = poseidon_hash::<2>([note.token_id, token_blind]);
+            assert_eq!(self.dao.gov_token_id, note.token_id);
 
-            //let zk_info = zk_bins.lookup(&"money-transfer-burn".to_string()).unwrap();
-            //let zk_info = if let ZkContractInfo::Native(info) = zk_info {
-            //    info
-            //} else {
-            //    panic!("Not native info")
-            //};
-            //let burn_pk = &zk_info.proving_key;
-
-            //// Note from the previous output
-            //let note = input.note;
-
-            //let (burn_proof, revealed) = create_burn_proof(
-            //    burn_pk,
-            //    note.value,
-            //    note.token_id,
-            //    value_blind,
-            //    token_blind,
-            //    note.serial,
-            //    note.spend_hook,
-            //    note.user_data,
-            //    input.user_data_blind,
-            //    note.coin_blind,
-            //    input.secret,
-            //    input.leaf_position,
-            //    input.merkle_path,
-            //    signature_secret,
-            //)?;
-            //proofs.push(burn_proof);
+            let value_commit = pedersen_commitment_u64(note.value, funds_blind);
+            let value_coords = value_commit.to_affine().coordinates().unwrap();
+            let value_commit_x = *value_coords.x();
+            let value_commit_y = *value_coords.y();
+
+            let sigpub_coords = signature_public.0.to_affine().coordinates().unwrap();
+            let sigpub_x = *sigpub_coords.x();
+            let sigpub_y = *sigpub_coords.y();
+
+            let public_inputs = vec![
+                value_commit_x,
+                value_commit_y,
+                token_commit,
+                merkle_root.0,
+                sigpub_x,
+                sigpub_y,
+            ];
+            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!");
 
             // First we make the tx then sign after
             signature_secrets.push(signature_secret);
 
-            let input = Input { signature_public: PublicKey::from_secret(signature_secret) };
+            let input = Input { value_commit, merkle_root, signature_public };
             inputs.push(input);
         }
 
@@ -154,6 +210,18 @@ impl Builder {
 
         let dao_leaf_position: u64 = self.dao_leaf_position.into();
 
+        let proposal_bulla = poseidon_hash::<8>([
+            proposal_dest_x,
+            proposal_dest_y,
+            proposal_amount,
+            self.proposal.serial,
+            self.proposal.token_id,
+            dao_bulla,
+            self.proposal.blind,
+            // @tmp-workaround
+            self.proposal.blind,
+        ]);
+
         let zk_info = zk_bins.lookup(&"dao-propose-main".to_string()).unwrap();
         let zk_info = if let ZkContractInfo::Binary(info) = zk_info {
             info
@@ -185,19 +253,26 @@ impl Builder {
             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![token_commit, self.dao_merkle_root.0, total_funds_x, total_funds_y];
+        let public_inputs = vec![
+            token_commit,
+            self.dao_merkle_root.0,
+            proposal_bulla,
+            total_funds_x,
+            total_funds_y,
+        ];
         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!");
+        proofs.push(main_proof);
 
-        // Create the input signatures
-        let proofs = vec![main_proof];
-
-        let header =
-            Header { dao_merkle_root: self.dao_merkle_root, token_commit, total_funds_commit };
+        let header = Header {
+            dao_merkle_root: self.dao_merkle_root,
+            proposal_bulla,
+            token_commit,
+            total_funds_commit,
+        };
 
         let mut unsigned_tx_data = vec![];
         header.encode(&mut unsigned_tx_data).expect("failed to encode data");

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

@@ -29,6 +29,10 @@ pub struct State {
     dao_bullas: Vec<DaoBulla>,
     pub dao_tree: MerkleTree,
     pub dao_roots: Vec<MerkleNode>,
+
+    proposal_bullas: Vec<pallas::Base>,
+    pub proposal_tree: MerkleTree,
+    pub proposal_roots: Vec<MerkleNode>,
 }
 
 impl State {
@@ -37,17 +41,31 @@ impl State {
             dao_bullas: Vec::new(),
             dao_tree: MerkleTree::new(100),
             dao_roots: Vec::new(),
+            proposal_bullas: Vec::new(),
+            proposal_tree: MerkleTree::new(100),
+            proposal_roots: Vec::new(),
         })
     }
 
-    pub fn add_bulla(&mut self, bulla: DaoBulla) {
+    pub fn add_dao_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 add_proposal_bulla(&mut self, bulla: pallas::Base) {
+        let node = MerkleNode(bulla);
+        self.proposal_bullas.push(bulla);
+        self.proposal_tree.append(&node);
+        self.proposal_roots.push(self.proposal_tree.root(0).unwrap());
+    }
+
     pub fn is_valid_dao_merkle(&self, root: &MerkleNode) -> bool {
         self.dao_roots.iter().any(|m| m == root)
     }
+
+    pub fn is_valid_proposal_merkle(&self, root: &MerkleNode) -> bool {
+        self.proposal_roots.iter().any(|m| m == root)
+    }
 }

+ 4 - 0
bin/daod/src/demo.rs

@@ -237,6 +237,10 @@ pub async fn demo() -> Result<()> {
     let zk_dao_propose_main_bincode = include_bytes!("../proof/dao-propose-main.zk.bin");
     let zk_dao_propose_main_bin = ZkBinary::decode(zk_dao_propose_main_bincode)?;
     zk_bins.add_contract("dao-propose-main".to_string(), zk_dao_propose_main_bin, 13);
+    debug!(target: "demo", "Loading dao-propose-burn.zk");
+    let zk_dao_propose_burn_bincode = include_bytes!("../proof/dao-propose-burn.zk.bin");
+    let zk_dao_propose_burn_bin = ZkBinary::decode(zk_dao_propose_burn_bincode)?;
+    zk_bins.add_contract("dao-propose-burn".to_string(), zk_dao_propose_burn_bin, 13);
 
     // State for money contracts
     let cashier_signature_secret = SecretKey::random(&mut OsRng);

+ 1 - 1
bin/daod/src/main.rs

@@ -18,7 +18,7 @@ mod dao_contract;
 mod demo;
 mod money_contract;
 mod note;
-// mod util;
+mod util;
 use crate::demo::demo;
 
 async fn _start() -> Result<()> {