Ver código fonte

daod: bang out some vote/wallet.rs, validate.rs boilerplate + fix proof

lunar-mining 4 anos atrás
pai
commit
b0503f0d1e

+ 1 - 1
bin/daod/proof/dao-vote-main.zk

@@ -51,7 +51,7 @@ circuit "DaoVoteMain" {
     # This is the main check
     # TODO: vote option should be 0 or 1
     #
-    # assert!(vote_option == 0 OR vote_option == 1)
+    # assert!(vote_option == 0 || vote_option == 1)
     #
 }
 

+ 2 - 2
bin/daod/src/dao_contract/vote/mod.rs

@@ -1,2 +1,2 @@
-//pub mod validate;
-//pub mod wallet;
+pub mod validate;
+pub mod wallet;

+ 54 - 0
bin/daod/src/dao_contract/vote/validate.rs

@@ -0,0 +1,54 @@
+use darkfi::{
+    crypto::{
+        keypair::PublicKey, merkle_node::MerkleNode, schnorr, schnorr::SchnorrPublic,
+        types::DrkCircuitField, Proof,
+    },
+    util::serial::{Encodable, SerialDecodable, SerialEncodable, VarInt},
+    Error as DarkFiError,
+};
+use log::{debug, error};
+use pasta_curves::{
+    arithmetic::CurveAffine,
+    group::{ff::Field, Curve, Group},
+    pallas,
+};
+use std::any::{Any, TypeId};
+
+use crate::{
+    dao_contract::{DaoBulla, State as DaoState},
+    demo::{CallDataBase, StateRegistry, Transaction},
+    money_contract::state::State as MoneyState,
+    note::EncryptedNote2,
+};
+
+pub struct CallData {
+    pub header: Header,
+    pub inputs: Vec<Input>,
+    pub signatures: Vec<schnorr::Signature>,
+}
+
+impl CallDataBase for CallData {
+    fn zk_public_values(&self) -> Vec<Vec<DrkCircuitField>> {
+        vec![]
+    }
+
+    fn zk_proof_addrs(&self) -> Vec<String> {
+        vec![]
+    }
+
+    fn as_any(&self) -> &dyn Any {
+        self
+    }
+}
+
+#[derive(Clone, SerialEncodable, SerialDecodable)]
+pub struct Header {
+    pub enc_note: EncryptedNote2,
+}
+
+#[derive(Clone, SerialEncodable, SerialDecodable)]
+pub struct Input {
+    pub value_commit: pallas::Point,
+    pub merkle_root: MerkleNode,
+    pub signature_public: PublicKey,
+}

+ 254 - 0
bin/daod/src/dao_contract/vote/wallet.rs

@@ -0,0 +1,254 @@
+use halo2_proofs::circuit::Value;
+use incrementalmerkletree::Hashable;
+use pasta_curves::{
+    arithmetic::CurveAffine,
+    group::{ff::Field, Curve, Group},
+    pallas,
+};
+use rand::rngs::OsRng;
+
+use darkfi::{
+    crypto::{
+        burn_proof::create_burn_proof,
+        keypair::{Keypair, PublicKey, SecretKey},
+        merkle_node::MerkleNode,
+        mint_proof::create_mint_proof,
+        proof::ProvingKey,
+        schnorr::SchnorrSecret,
+        types::{
+            DrkCircuitField, DrkCoinBlind, DrkSerial, DrkSpendHook, DrkTokenId, DrkUserData,
+            DrkUserDataBlind, DrkValueBlind,
+        },
+        util::{pedersen_commitment_base, pedersen_commitment_u64},
+        Proof,
+    },
+    util::serial::{Encodable, SerialDecodable, SerialEncodable},
+    zk::vm::{Witness, ZkCircuit},
+};
+
+use crate::{
+    dao_contract::vote::validate::{CallData, Header, Input},
+    demo::{CallDataBase, FuncCall, StateRegistry, ZkContractInfo, ZkContractTable},
+    money_contract, note,
+    util::poseidon_hash,
+};
+
+#[derive(SerialEncodable, SerialDecodable)]
+pub struct Note {
+    vote: Vote,
+    value: u64,
+}
+
+#[derive(SerialEncodable, SerialDecodable)]
+// All info needed for vote and value commits
+pub struct Vote {
+    pub value_blind: pallas::Scalar,
+    pub vote_option: bool,
+    pub vote_option_blind: pallas::Scalar,
+    //TODO: gov_token_id: pallas::Base,
+}
+
+pub struct BuilderInput {
+    pub secret: SecretKey,
+    pub note: money_contract::transfer::wallet::Note,
+    pub leaf_position: incrementalmerkletree::Position,
+    pub merkle_path: Vec<MerkleNode>,
+}
+
+// TODO: Pass DAO and Proposal into Builder.
+// Inside ZKproof, check proposal is correct.
+pub struct Builder {
+    pub inputs: Vec<BuilderInput>,
+    pub vote: Vote,
+    pub vote_keypair: Keypair,
+}
+
+impl Builder {
+    pub fn build(self, zk_bins: &ZkContractTable) -> FuncCall {
+        let mut proofs = vec![];
+
+        let gov_token_blind = pallas::Base::random(&mut OsRng);
+        let vote_blind = pallas::Scalar::random(&mut OsRng);
+
+        let mut inputs = vec![];
+        let mut total_value = 0;
+        let mut total_value_blind = pallas::Scalar::from(0);
+        let mut signature_secrets = vec![];
+
+        for input in self.inputs {
+            let value_blind = pallas::Scalar::random(&mut OsRng);
+
+            total_value += input.note.value;
+            total_value_blind += value_blind;
+
+            let signature_secret = SecretKey::random(&mut OsRng);
+            let signature_public = PublicKey::from_secret(signature_secret);
+
+            let zk_info = zk_bins.lookup(&"dao-vote-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(0))),
+                Witness::Base(Value::known(pallas::Base::from(0))),
+                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(value_blind)),
+                Witness::Base(Value::known(gov_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, gov_token_blind]);
+            //TODO: assert_eq!(self.dao.gov_token_id, note.token_id);
+
+            let value_commit = pedersen_commitment_u64(note.value, value_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 input_proof = Proof::create(proving_key, &[circuit], &public_inputs, &mut OsRng)
+                .expect("DAO::vote() proving error!");
+            proofs.push(input_proof);
+
+            // First we make the tx then sign after
+            signature_secrets.push(signature_secret);
+
+            let input = Input { value_commit, merkle_root, signature_public };
+            inputs.push(input);
+        }
+
+        //TODO: let token_commit = poseidon_hash::<2>([self.dao.gov_token_id, gov_token_blind]);
+
+        let vote = self.vote.vote_option as u64;
+        assert!(vote == 0 || vote == 1);
+
+        let weighted_vote = vote * total_value;
+
+        let vote_commit = pedersen_commitment_u64(weighted_vote, vote_blind);
+        let vote_coords = vote_commit.to_affine().coordinates().unwrap();
+        let vote_commit_x = *vote_coords.x();
+        let vote_commit_y = *vote_coords.y();
+        let vote = pallas::Base::from(vote);
+
+        let total_value_commit = pedersen_commitment_u64(total_value, total_value_blind);
+        let total_value_coords = total_value_commit.to_affine().coordinates().unwrap();
+        let total_value_x = *total_value_coords.x();
+        let total_value_y = *total_value_coords.y();
+        let value_base = pallas::Base::from(total_value);
+
+        let zk_info = zk_bins.lookup(&"dao-vote-main".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();
+
+        let prover_witnesses = vec![
+            // Total number of gov tokens allocated
+            Witness::Base(Value::known(value_base)),
+            Witness::Scalar(Value::known(total_value_blind)),
+            // Vote
+            Witness::Base(Value::known(vote)),
+            Witness::Scalar(Value::known(vote_blind)),
+            // TODO: gov token
+        ];
+
+        let public_inputs = vec![
+            //TODO: token_commit
+            total_value_x,
+            total_value_y,
+            vote_commit_x,
+            vote_commit_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::vote() proving error!");
+        proofs.push(main_proof);
+
+        let note = Note { vote: self.vote, value: total_value };
+        let enc_note = note::encrypt(&note, &self.vote_keypair.public).unwrap();
+
+        let header = Header { enc_note };
+
+        let mut unsigned_tx_data = vec![];
+        header.encode(&mut unsigned_tx_data).expect("failed to encode data");
+        inputs.encode(&mut unsigned_tx_data).expect("failed to encode inputs");
+        proofs.encode(&mut unsigned_tx_data).expect("failed to encode proofs");
+
+        let mut signatures = vec![];
+        for signature_secret in &signature_secrets {
+            let signature = signature_secret.sign(&unsigned_tx_data[..]);
+            signatures.push(signature);
+        }
+
+        let call_data = CallData { header, inputs, signatures };
+
+        FuncCall {
+            contract_id: "DAO".to_string(),
+            func_id: "DAO::vote()".to_string(),
+            call_data: Box::new(call_data),
+            proofs: vec![],
+        }
+    }
+}

+ 35 - 1
bin/daod/src/demo.rs

@@ -255,7 +255,6 @@ pub async fn demo() -> Result<()> {
     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);
-
     debug!(target: "demo", "Loading dao-vote-main.zk");
     let zk_dao_vote_main_bincode = include_bytes!("../proof/dao-vote-main.zk.bin");
     let zk_dao_vote_main_bin = ZkBinary::decode(zk_dao_vote_main_bincode)?;
@@ -706,5 +705,40 @@ pub async fn demo() -> Result<()> {
     // beginning of gov period
     // Cannot use nullifiers from before voting period
 
+    debug!(target: "demo", "Stage 5. Start voting");
+
+    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();
+        let root = tree.root(0).unwrap();
+        let merkle_path = tree.authentication_path(leaf_position, &root).unwrap();
+        (leaf_position, merkle_path)
+    };
+
+    let input = dao_contract::vote::wallet::BuilderInput {
+        secret: gov_keypair_2.secret,
+        note: gov_recv[1].note.clone(),
+        leaf_position: money_leaf_position,
+        merkle_path: money_merkle_path,
+    };
+
+    let vote_option: bool = true;
+
+    assert!(vote_option == true || vote_option == false);
+
+    // We create a new keypair to encrypt the vote.
+    let vote_keypair = Keypair::random(&mut OsRng);
+
+    let builder = dao_contract::vote::wallet::Builder {
+        inputs: vec![input],
+        vote: dao_contract::vote::wallet::Vote {
+            value_blind: pallas::Scalar::random(&mut OsRng),
+            vote_option,
+            vote_option_blind: pallas::Scalar::random(&mut OsRng),
+        },
+        vote_keypair,
+    };
+
     Ok(())
 }