wallet.rs 9.9 KB

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