| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288 |
- use darkfi_serial::{SerialDecodable, SerialEncodable};
- use halo2_proofs::circuit::Value;
- use incrementalmerkletree::Hashable;
- use log::debug;
- use pasta_curves::{
- arithmetic::CurveAffine,
- group::{ff::Field, Curve},
- pallas,
- };
- use rand::rngs::OsRng;
- use darkfi::{
- crypto::{
- keypair::{Keypair, PublicKey, SecretKey},
- merkle_node::MerkleNode,
- nullifier::Nullifier,
- util::{pedersen_commitment_u64, poseidon_hash},
- Proof,
- },
- zk::vm::{Witness, ZkCircuit},
- };
- use crate::{
- contract::{
- dao_contract::{
- mint::wallet::DaoParams,
- propose::wallet::Proposal,
- vote::validate::{CallData, Header, Input},
- CONTRACT_ID,
- },
- money_contract,
- },
- note,
- util::{FuncCall, ZkContractInfo, ZkContractTable},
- };
- #[derive(SerialEncodable, SerialDecodable)]
- pub struct Note {
- pub vote: Vote,
- pub vote_value: u64,
- pub vote_value_blind: pallas::Scalar,
- }
- #[derive(SerialEncodable, SerialDecodable)]
- pub struct Vote {
- pub vote_option: bool,
- pub vote_option_blind: pallas::Scalar,
- }
- pub struct BuilderInput {
- pub secret: SecretKey,
- pub note: money_contract::transfer::wallet::Note,
- pub leaf_position: incrementalmerkletree::Position,
- pub merkle_path: Vec<MerkleNode>,
- pub signature_secret: SecretKey,
- }
- // TODO: should be token locking voting?
- // Inside ZKproof, check proposal is correct.
- pub struct Builder {
- pub inputs: Vec<BuilderInput>,
- pub vote: Vote,
- pub vote_keypair: Keypair,
- pub proposal: Proposal,
- pub dao: DaoParams,
- }
- impl Builder {
- pub fn build(self, zk_bins: &ZkContractTable) -> FuncCall {
- debug!(target: "dao_contract::vote::wallet::Builder", "build()");
- let mut proofs = vec![];
- let gov_token_blind = pallas::Base::random(&mut OsRng);
- let mut inputs = vec![];
- let mut vote_value = 0;
- let mut vote_value_blind = pallas::Scalar::from(0);
- for input in self.inputs {
- let value_blind = pallas::Scalar::random(&mut OsRng);
- vote_value += input.note.value;
- vote_value_blind += value_blind;
- let signature_public = PublicKey::from_secret(input.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.inner())),
- 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(vote_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(input.signature_secret.inner())),
- ];
- 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(), ¤t, sibling)
- } else {
- MerkleNode::combine(level.into(), sibling, ¤t)
- };
- }
- current
- };
- let token_commit = poseidon_hash::<2>([note.token_id, gov_token_blind]);
- assert_eq!(self.dao.gov_token_id, note.token_id);
- let nullifier =
- Nullifier::from(poseidon_hash::<2>([input.secret.inner(), note.serial]));
- let vote_commit = pedersen_commitment_u64(note.value, vote_value_blind);
- let vote_commit_coords = vote_commit.to_affine().coordinates().unwrap();
- let sigpub_coords = signature_public.0.to_affine().coordinates().unwrap();
- let public_inputs = vec![
- nullifier.inner(),
- *vote_commit_coords.x(),
- *vote_commit_coords.y(),
- token_commit,
- merkle_root.0,
- *sigpub_coords.x(),
- *sigpub_coords.y(),
- ];
- let circuit = ZkCircuit::new(prover_witnesses, zk_bin);
- let proving_key = &zk_info.proving_key;
- debug!(target: "dao_contract::vote::wallet::Builder", "input_proof Proof::create()");
- let input_proof = Proof::create(proving_key, &[circuit], &public_inputs, &mut OsRng)
- .expect("DAO::vote() proving error!");
- proofs.push(input_proof);
- let input = Input { nullifier, vote_commit, merkle_root, signature_public };
- inputs.push(input);
- }
- let token_commit = poseidon_hash::<2>([self.dao.gov_token_id, gov_token_blind]);
- let proposal_dest_coords = self.proposal.dest.0.to_affine().coordinates().unwrap();
- 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_quot = pallas::Base::from(self.dao.approval_ratio_quot);
- let dao_approval_ratio_base = pallas::Base::from(self.dao.approval_ratio_base);
- let dao_pubkey_coords = self.dao.public_key.0.to_affine().coordinates().unwrap();
- let dao_bulla = poseidon_hash::<8>([
- dao_proposer_limit,
- dao_quorum,
- dao_approval_ratio_quot,
- dao_approval_ratio_base,
- self.dao.gov_token_id,
- *dao_pubkey_coords.x(),
- *dao_pubkey_coords.y(),
- self.dao.bulla_blind,
- ]);
- let proposal_bulla = poseidon_hash::<8>([
- *proposal_dest_coords.x(),
- *proposal_dest_coords.y(),
- proposal_amount,
- self.proposal.serial,
- self.proposal.token_id,
- dao_bulla,
- self.proposal.blind,
- // @tmp-workaround
- self.proposal.blind,
- ]);
- let vote_option = self.vote.vote_option as u64;
- assert!(vote_option == 0 || vote_option == 1);
- let yes_vote_commit =
- pedersen_commitment_u64(vote_option * vote_value, self.vote.vote_option_blind);
- let yes_vote_commit_coords = yes_vote_commit.to_affine().coordinates().unwrap();
- let all_vote_commit = pedersen_commitment_u64(vote_value, vote_value_blind);
- let all_vote_commit_coords = all_vote_commit.to_affine().coordinates().unwrap();
- 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![
- // proposal params
- Witness::Base(Value::known(*proposal_dest_coords.x())),
- Witness::Base(Value::known(*proposal_dest_coords.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_quot)),
- Witness::Base(Value::known(dao_approval_ratio_base)),
- Witness::Base(Value::known(self.dao.gov_token_id)),
- Witness::Base(Value::known(*dao_pubkey_coords.x())),
- Witness::Base(Value::known(*dao_pubkey_coords.y())),
- Witness::Base(Value::known(self.dao.bulla_blind)),
- // Vote
- Witness::Base(Value::known(pallas::Base::from(vote_option))),
- Witness::Scalar(Value::known(self.vote.vote_option_blind)),
- // Total number of gov tokens allocated
- Witness::Base(Value::known(pallas::Base::from(vote_value))),
- Witness::Scalar(Value::known(vote_value_blind)),
- // gov token
- Witness::Base(Value::known(gov_token_blind)),
- ];
- let public_inputs = vec![
- token_commit,
- proposal_bulla,
- // this should be a value commit??
- *yes_vote_commit_coords.x(),
- *yes_vote_commit_coords.y(),
- *all_vote_commit_coords.x(),
- *all_vote_commit_coords.y(),
- ];
- let circuit = ZkCircuit::new(prover_witnesses, zk_bin);
- let proving_key = &zk_info.proving_key;
- debug!(target: "dao_contract::vote::wallet::Builder", "main_proof = Proof::create()");
- 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, vote_value, vote_value_blind };
- let enc_note = note::encrypt(¬e, &self.vote_keypair.public).unwrap();
- let header = Header { token_commit, proposal_bulla, yes_vote_commit, enc_note };
- let call_data = CallData { header, inputs };
- FuncCall {
- contract_id: *CONTRACT_ID,
- func_id: *super::FUNC_ID,
- call_data: Box::new(call_data),
- proofs,
- }
- }
- }
|