wallet.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. use darkfi_serial::{SerialDecodable, SerialEncodable};
  2. use halo2_proofs::circuit::Value;
  3. use incrementalmerkletree::Hashable;
  4. use log::debug;
  5. use pasta_curves::{
  6. arithmetic::CurveAffine,
  7. group::{ff::Field, Curve},
  8. pallas,
  9. };
  10. use rand::rngs::OsRng;
  11. use darkfi::{
  12. crypto::{
  13. keypair::{Keypair, PublicKey, SecretKey},
  14. merkle_node::MerkleNode,
  15. nullifier::Nullifier,
  16. util::{pedersen_commitment_u64, poseidon_hash},
  17. Proof,
  18. },
  19. zk::vm::{Witness, ZkCircuit},
  20. };
  21. use crate::{
  22. contract::{
  23. dao_contract::{
  24. mint::wallet::DaoParams,
  25. propose::wallet::Proposal,
  26. vote::validate::{CallData, Header, Input},
  27. CONTRACT_ID,
  28. },
  29. money_contract,
  30. },
  31. note,
  32. util::{FuncCall, ZkContractInfo, ZkContractTable},
  33. };
  34. #[derive(SerialEncodable, SerialDecodable)]
  35. pub struct Note {
  36. pub vote: Vote,
  37. pub vote_value: u64,
  38. pub vote_value_blind: pallas::Scalar,
  39. }
  40. #[derive(SerialEncodable, SerialDecodable)]
  41. pub struct Vote {
  42. pub vote_option: bool,
  43. pub vote_option_blind: pallas::Scalar,
  44. }
  45. pub struct BuilderInput {
  46. pub secret: SecretKey,
  47. pub note: money_contract::transfer::wallet::Note,
  48. pub leaf_position: incrementalmerkletree::Position,
  49. pub merkle_path: Vec<MerkleNode>,
  50. pub signature_secret: SecretKey,
  51. }
  52. // TODO: should be token locking voting?
  53. // Inside ZKproof, check proposal is correct.
  54. pub struct Builder {
  55. pub inputs: Vec<BuilderInput>,
  56. pub vote: Vote,
  57. pub vote_keypair: Keypair,
  58. pub proposal: Proposal,
  59. pub dao: DaoParams,
  60. }
  61. impl Builder {
  62. pub fn build(self, zk_bins: &ZkContractTable) -> FuncCall {
  63. debug!(target: "dao_contract::vote::wallet::Builder", "build()");
  64. let mut proofs = vec![];
  65. let gov_token_blind = pallas::Base::random(&mut OsRng);
  66. let mut inputs = vec![];
  67. let mut vote_value = 0;
  68. let mut vote_value_blind = pallas::Scalar::from(0);
  69. for input in self.inputs {
  70. let value_blind = pallas::Scalar::random(&mut OsRng);
  71. vote_value += input.note.value;
  72. vote_value_blind += value_blind;
  73. let signature_public = PublicKey::from_secret(input.signature_secret);
  74. let zk_info = zk_bins.lookup(&"dao-vote-burn".to_string()).unwrap();
  75. let zk_info = if let ZkContractInfo::Binary(info) = zk_info {
  76. info
  77. } else {
  78. panic!("Not binary info")
  79. };
  80. let zk_bin = zk_info.bincode.clone();
  81. // Note from the previous output
  82. let note = input.note;
  83. let leaf_pos: u64 = input.leaf_position.into();
  84. let prover_witnesses = vec![
  85. Witness::Base(Value::known(input.secret.inner())),
  86. Witness::Base(Value::known(note.serial)),
  87. Witness::Base(Value::known(pallas::Base::from(0))),
  88. Witness::Base(Value::known(pallas::Base::from(0))),
  89. Witness::Base(Value::known(pallas::Base::from(note.value))),
  90. Witness::Base(Value::known(note.token_id)),
  91. Witness::Base(Value::known(note.coin_blind)),
  92. Witness::Scalar(Value::known(vote_value_blind)),
  93. Witness::Base(Value::known(gov_token_blind)),
  94. Witness::Uint32(Value::known(leaf_pos.try_into().unwrap())),
  95. Witness::MerklePath(Value::known(input.merkle_path.clone().try_into().unwrap())),
  96. Witness::Base(Value::known(input.signature_secret.inner())),
  97. ];
  98. let public_key = PublicKey::from_secret(input.secret);
  99. let coords = public_key.0.to_affine().coordinates().unwrap();
  100. let coin = poseidon_hash::<8>([
  101. *coords.x(),
  102. *coords.y(),
  103. pallas::Base::from(note.value),
  104. note.token_id,
  105. note.serial,
  106. pallas::Base::from(0),
  107. pallas::Base::from(0),
  108. note.coin_blind,
  109. ]);
  110. let merkle_root = {
  111. let position: u64 = input.leaf_position.into();
  112. let mut current = MerkleNode(coin);
  113. for (level, sibling) in input.merkle_path.iter().enumerate() {
  114. let level = level as u8;
  115. current = if position & (1 << level) == 0 {
  116. MerkleNode::combine(level.into(), &current, sibling)
  117. } else {
  118. MerkleNode::combine(level.into(), sibling, &current)
  119. };
  120. }
  121. current
  122. };
  123. let token_commit = poseidon_hash::<2>([note.token_id, gov_token_blind]);
  124. assert_eq!(self.dao.gov_token_id, note.token_id);
  125. let nullifier =
  126. Nullifier::from(poseidon_hash::<2>([input.secret.inner(), note.serial]));
  127. let vote_commit = pedersen_commitment_u64(note.value, vote_value_blind);
  128. let vote_commit_coords = vote_commit.to_affine().coordinates().unwrap();
  129. let sigpub_coords = signature_public.0.to_affine().coordinates().unwrap();
  130. let public_inputs = vec![
  131. nullifier.inner(),
  132. *vote_commit_coords.x(),
  133. *vote_commit_coords.y(),
  134. token_commit,
  135. merkle_root.0,
  136. *sigpub_coords.x(),
  137. *sigpub_coords.y(),
  138. ];
  139. let circuit = ZkCircuit::new(prover_witnesses, zk_bin);
  140. let proving_key = &zk_info.proving_key;
  141. debug!(target: "dao_contract::vote::wallet::Builder", "input_proof Proof::create()");
  142. let input_proof = Proof::create(proving_key, &[circuit], &public_inputs, &mut OsRng)
  143. .expect("DAO::vote() proving error!");
  144. proofs.push(input_proof);
  145. let input = Input { nullifier, vote_commit, merkle_root, signature_public };
  146. inputs.push(input);
  147. }
  148. let token_commit = poseidon_hash::<2>([self.dao.gov_token_id, gov_token_blind]);
  149. let proposal_dest_coords = self.proposal.dest.0.to_affine().coordinates().unwrap();
  150. let proposal_amount = pallas::Base::from(self.proposal.amount);
  151. let dao_proposer_limit = pallas::Base::from(self.dao.proposer_limit);
  152. let dao_quorum = pallas::Base::from(self.dao.quorum);
  153. let dao_approval_ratio_quot = pallas::Base::from(self.dao.approval_ratio_quot);
  154. let dao_approval_ratio_base = pallas::Base::from(self.dao.approval_ratio_base);
  155. let dao_pubkey_coords = self.dao.public_key.0.to_affine().coordinates().unwrap();
  156. let dao_bulla = poseidon_hash::<8>([
  157. dao_proposer_limit,
  158. dao_quorum,
  159. dao_approval_ratio_quot,
  160. dao_approval_ratio_base,
  161. self.dao.gov_token_id,
  162. *dao_pubkey_coords.x(),
  163. *dao_pubkey_coords.y(),
  164. self.dao.bulla_blind,
  165. ]);
  166. let proposal_bulla = poseidon_hash::<8>([
  167. *proposal_dest_coords.x(),
  168. *proposal_dest_coords.y(),
  169. proposal_amount,
  170. self.proposal.serial,
  171. self.proposal.token_id,
  172. dao_bulla,
  173. self.proposal.blind,
  174. // @tmp-workaround
  175. self.proposal.blind,
  176. ]);
  177. let vote_option = self.vote.vote_option as u64;
  178. assert!(vote_option == 0 || vote_option == 1);
  179. let yes_vote_commit =
  180. pedersen_commitment_u64(vote_option * vote_value, self.vote.vote_option_blind);
  181. let yes_vote_commit_coords = yes_vote_commit.to_affine().coordinates().unwrap();
  182. let all_vote_commit = pedersen_commitment_u64(vote_value, vote_value_blind);
  183. let all_vote_commit_coords = all_vote_commit.to_affine().coordinates().unwrap();
  184. let zk_info = zk_bins.lookup(&"dao-vote-main".to_string()).unwrap();
  185. let zk_info = if let ZkContractInfo::Binary(info) = zk_info {
  186. info
  187. } else {
  188. panic!("Not binary info")
  189. };
  190. let zk_bin = zk_info.bincode.clone();
  191. let prover_witnesses = vec![
  192. // proposal params
  193. Witness::Base(Value::known(*proposal_dest_coords.x())),
  194. Witness::Base(Value::known(*proposal_dest_coords.y())),
  195. Witness::Base(Value::known(proposal_amount)),
  196. Witness::Base(Value::known(self.proposal.serial)),
  197. Witness::Base(Value::known(self.proposal.token_id)),
  198. Witness::Base(Value::known(self.proposal.blind)),
  199. // DAO params
  200. Witness::Base(Value::known(dao_proposer_limit)),
  201. Witness::Base(Value::known(dao_quorum)),
  202. Witness::Base(Value::known(dao_approval_ratio_quot)),
  203. Witness::Base(Value::known(dao_approval_ratio_base)),
  204. Witness::Base(Value::known(self.dao.gov_token_id)),
  205. Witness::Base(Value::known(*dao_pubkey_coords.x())),
  206. Witness::Base(Value::known(*dao_pubkey_coords.y())),
  207. Witness::Base(Value::known(self.dao.bulla_blind)),
  208. // Vote
  209. Witness::Base(Value::known(pallas::Base::from(vote_option))),
  210. Witness::Scalar(Value::known(self.vote.vote_option_blind)),
  211. // Total number of gov tokens allocated
  212. Witness::Base(Value::known(pallas::Base::from(vote_value))),
  213. Witness::Scalar(Value::known(vote_value_blind)),
  214. // gov token
  215. Witness::Base(Value::known(gov_token_blind)),
  216. ];
  217. let public_inputs = vec![
  218. token_commit,
  219. proposal_bulla,
  220. // this should be a value commit??
  221. *yes_vote_commit_coords.x(),
  222. *yes_vote_commit_coords.y(),
  223. *all_vote_commit_coords.x(),
  224. *all_vote_commit_coords.y(),
  225. ];
  226. let circuit = ZkCircuit::new(prover_witnesses, zk_bin);
  227. let proving_key = &zk_info.proving_key;
  228. debug!(target: "dao_contract::vote::wallet::Builder", "main_proof = Proof::create()");
  229. let main_proof = Proof::create(proving_key, &[circuit], &public_inputs, &mut OsRng)
  230. .expect("DAO::vote() proving error!");
  231. proofs.push(main_proof);
  232. let note = Note { vote: self.vote, vote_value, vote_value_blind };
  233. let enc_note = note::encrypt(&note, &self.vote_keypair.public).unwrap();
  234. let header = Header { token_commit, proposal_bulla, yes_vote_commit, enc_note };
  235. let call_data = CallData { header, inputs };
  236. FuncCall {
  237. contract_id: *CONTRACT_ID,
  238. func_id: *super::FUNC_ID,
  239. call_data: Box::new(call_data),
  240. proofs,
  241. }
  242. }
  243. }