proposal_v1.rs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2023 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. //! This API is crufty. Please rework it into something nice to read and nice to use.
  19. use darkfi::{
  20. consensus::SlotCheckpoint,
  21. zk::{halo2::Value, Proof, ProvingKey, Witness, ZkCircuit},
  22. zkas::ZkBinary,
  23. Result,
  24. };
  25. use darkfi_money_contract::{
  26. client::{MoneyNote, OwnCoin},
  27. model::{ConsensusUnstakeParamsV1, Input, Output, StakeInput},
  28. };
  29. use darkfi_sdk::{
  30. crypto::{
  31. note::AeadEncryptedNote, pasta_prelude::*, pedersen_commitment_base,
  32. pedersen_commitment_u64, poseidon_hash, Coin, MerkleTree, Nullifier, PublicKey, SecretKey,
  33. CONSENSUS_CONTRACT_ID, DARK_TOKEN_ID,
  34. },
  35. incrementalmerkletree::Tree,
  36. pasta::pallas,
  37. };
  38. use log::{debug, info};
  39. use rand::rngs::OsRng;
  40. use crate::{
  41. client::{
  42. stake_v1::{TransactionBuilderOutputInfo as StakeTBOI, TransactionBuilderOutputInfo},
  43. unstake_v1::{create_unstake_burn_proof, TransactionBuilderInputInfo as UnstakeTBII},
  44. },
  45. model::{
  46. ConsensusProposalMintParamsV1, ConsensusProposalRewardParamsV1, HEADSTART, MU_RHO_PREFIX,
  47. MU_Y_PREFIX, REWARD, REWARD_PALLAS, SEED_PREFIX, SERIAL_PREFIX, ZERO,
  48. },
  49. };
  50. pub struct ConsensusProposalCallDebris {
  51. pub unstake_params: ConsensusUnstakeParamsV1,
  52. pub unstake_proofs: Vec<Proof>,
  53. pub reward_params: ConsensusProposalRewardParamsV1,
  54. pub reward_proofs: Vec<Proof>,
  55. pub stake_params: ConsensusProposalMintParamsV1,
  56. pub stake_proofs: Vec<Proof>,
  57. pub signature_secret: SecretKey,
  58. }
  59. pub struct ConsensusProposalRewardRevealed {
  60. pub nullifier: Nullifier,
  61. pub value_commit: pallas::Point,
  62. pub new_serial_commit: pallas::Point,
  63. pub new_value_commit: pallas::Point,
  64. pub mu_y: pallas::Base,
  65. pub y: pallas::Base,
  66. pub mu_rho: pallas::Base,
  67. pub rho: pallas::Base,
  68. pub sigma1: pallas::Base,
  69. pub sigma2: pallas::Base,
  70. }
  71. impl ConsensusProposalRewardRevealed {
  72. pub fn to_vec(&self) -> Vec<pallas::Base> {
  73. let value_coords = self.value_commit.to_affine().coordinates().unwrap();
  74. let new_serial_coords = self.new_serial_commit.to_affine().coordinates().unwrap();
  75. let new_value_coords = self.new_value_commit.to_affine().coordinates().unwrap();
  76. // NOTE: It's important to keep these in the same order
  77. // as the `constrain_instance` calls in the zkas code.
  78. vec![
  79. self.nullifier.inner(),
  80. *value_coords.x(),
  81. *value_coords.y(),
  82. *new_serial_coords.x(),
  83. *new_serial_coords.y(),
  84. *new_value_coords.x(),
  85. *new_value_coords.y(),
  86. self.mu_y,
  87. self.y,
  88. self.mu_rho,
  89. self.rho,
  90. self.sigma1,
  91. self.sigma2,
  92. HEADSTART,
  93. ]
  94. }
  95. }
  96. pub struct ConsensusProposalMintRevealed {
  97. pub coin: Coin,
  98. pub value_commit: pallas::Point,
  99. pub token_commit: pallas::Point,
  100. pub serial_commit: pallas::Point,
  101. }
  102. impl ConsensusProposalMintRevealed {
  103. pub fn to_vec(&self) -> Vec<pallas::Base> {
  104. let valcom_coords = self.value_commit.to_affine().coordinates().unwrap();
  105. let tokcom_coords = self.token_commit.to_affine().coordinates().unwrap();
  106. let sercom_coords = self.serial_commit.to_affine().coordinates().unwrap();
  107. // NOTE: It's important to keep these in the same order
  108. // as the `constrain_instance` calls in the zkas code.
  109. vec![
  110. self.coin.inner(),
  111. *valcom_coords.x(),
  112. *valcom_coords.y(),
  113. *tokcom_coords.x(),
  114. *tokcom_coords.y(),
  115. *sercom_coords.x(),
  116. *sercom_coords.y(),
  117. ]
  118. }
  119. }
  120. /// Struct holding necessary information to build a proposal transaction.
  121. pub struct ConsensusProposalCallBuilder {
  122. /// `OwnCoin` we're given to use in this builder
  123. pub coin: OwnCoin,
  124. /// Recipient's public key
  125. pub recipient: PublicKey,
  126. /// Rewarded slot checkpoint
  127. pub slot_checkpoint: SlotCheckpoint,
  128. /// Merkle tree of coins used to create inclusion proofs
  129. pub tree: MerkleTree,
  130. /// `Burn_V1` zkas circuit ZkBinary
  131. pub burn_zkbin: ZkBinary,
  132. /// Proving key for the `Burn_V1` zk circuit
  133. pub burn_pk: ProvingKey,
  134. /// `ProposalReward_V1` zkas circuit ZkBinary
  135. pub reward_zkbin: ZkBinary,
  136. /// Proving key for the `Reward_V1` zk circuit
  137. pub reward_pk: ProvingKey,
  138. /// `ProposalMint_V1` zkas circuit ZkBinary
  139. pub mint_zkbin: ZkBinary,
  140. /// Proving key for the `Mint_V1` zk circuit
  141. pub mint_pk: ProvingKey,
  142. }
  143. impl ConsensusProposalCallBuilder {
  144. pub fn build(&self) -> Result<ConsensusProposalCallDebris> {
  145. debug!("Building Consensus::ProposalBurnV1 contract call for proposal");
  146. let value = self.coin.note.value;
  147. let token_id = self.coin.note.token_id;
  148. assert!(value != 0);
  149. assert!(token_id == *DARK_TOKEN_ID);
  150. debug!("Building anonymous input for proposal");
  151. let leaf_position = self.coin.leaf_position;
  152. let root = self.tree.root(0).unwrap();
  153. let merkle_path = self.tree.authentication_path(leaf_position, &root).unwrap();
  154. let input = UnstakeTBII {
  155. leaf_position,
  156. merkle_path,
  157. secret: self.coin.secret,
  158. note: self.coin.note.clone(),
  159. };
  160. debug!("Finished building input for proposal");
  161. let value_blind = pallas::Scalar::random(&mut OsRng);
  162. let token_blind = pallas::Scalar::random(&mut OsRng);
  163. let signature_secret = SecretKey::random(&mut OsRng);
  164. let user_data_blind = pallas::Base::random(&mut OsRng);
  165. info!("Creating unstake burn proof for input for proposal");
  166. let (proof, public_inputs) = create_unstake_burn_proof(
  167. &self.burn_zkbin,
  168. &self.burn_pk,
  169. &input,
  170. value_blind,
  171. token_blind,
  172. user_data_blind,
  173. signature_secret,
  174. )?;
  175. let input = Input {
  176. value_commit: public_inputs.value_commit,
  177. token_commit: public_inputs.token_commit,
  178. nullifier: public_inputs.nullifier,
  179. merkle_root: public_inputs.merkle_root,
  180. spend_hook: public_inputs.spend_hook,
  181. user_data_enc: public_inputs.user_data_enc,
  182. signature_public: public_inputs.signature_public,
  183. };
  184. // We now fill this with necessary stuff
  185. let unstake_params = ConsensusUnstakeParamsV1 { token_blind, input: input.clone() };
  186. let unstake_proofs = vec![proof];
  187. let unstake_input = input;
  188. debug!("Building Consensus::ProposalMintV1 contract call for proposal");
  189. let new_value = value + REWARD;
  190. let nullifier = public_inputs.nullifier;
  191. let merkle_root = public_inputs.merkle_root;
  192. let signature_public = public_inputs.signature_public;
  193. debug!("Building anonymous output for proposal");
  194. let output = StakeTBOI { value: new_value, token_id, public_key: self.recipient };
  195. debug!("Finished building output for proposal");
  196. let burnt_secret_key = self.coin.secret.inner();
  197. let burnt_serial = self.coin.note.serial;
  198. let spend_hook = CONSENSUS_CONTRACT_ID.inner();
  199. let user_data = pallas::Base::random(&mut OsRng);
  200. let coin_blind = pallas::Base::random(&mut OsRng);
  201. let serial_blind = pallas::Scalar::random(&mut OsRng);
  202. info!("Creating stake mint proof for output for proposal");
  203. let (proof, public_inputs, serial) = create_proposal_mint_proof(
  204. &self.mint_zkbin,
  205. &self.mint_pk,
  206. &output,
  207. value_blind,
  208. token_blind,
  209. serial_blind,
  210. burnt_secret_key,
  211. burnt_serial,
  212. spend_hook,
  213. user_data,
  214. coin_blind,
  215. )?;
  216. // Encrypted note
  217. let note = MoneyNote {
  218. serial,
  219. value: output.value,
  220. token_id: output.token_id,
  221. spend_hook,
  222. user_data,
  223. coin_blind,
  224. value_blind,
  225. token_blind,
  226. memo: vec![],
  227. };
  228. let encrypted_note = AeadEncryptedNote::encrypt(&note, &output.public_key, &mut OsRng)?;
  229. let output = Output {
  230. value_commit: public_inputs.value_commit,
  231. token_commit: public_inputs.token_commit,
  232. coin: public_inputs.coin,
  233. note: encrypted_note,
  234. };
  235. let input = StakeInput {
  236. token_blind,
  237. value_commit: public_inputs.value_commit,
  238. nullifier,
  239. merkle_root,
  240. signature_public,
  241. };
  242. // We now fill this with necessary stuff
  243. let serial_commit = public_inputs.serial_commit;
  244. let stake_params = ConsensusProposalMintParamsV1 {
  245. input: input.clone(),
  246. output: output.clone(),
  247. serial_commit: serial_commit.clone(),
  248. };
  249. let stake_proofs = vec![proof];
  250. let stake_input = input;
  251. debug!("Building Consensus::ProposalRewardV1 contract call for proposal");
  252. let secret_key = self.coin.secret.inner();
  253. let serial = self.coin.note.serial;
  254. let (proof, public_inputs) = create_proposal_reward_proof(
  255. &self.reward_zkbin,
  256. &self.reward_pk,
  257. &self.slot_checkpoint,
  258. secret_key,
  259. serial,
  260. value,
  261. value_blind,
  262. serial_blind,
  263. )?;
  264. // We now fill this with necessary stuff
  265. let new_serial_commit = serial_commit;
  266. let slot = self.slot_checkpoint.slot;
  267. let y = public_inputs.y;
  268. let rho = public_inputs.rho;
  269. let reward_params = ConsensusProposalRewardParamsV1 {
  270. unstake_input,
  271. stake_input,
  272. output,
  273. new_serial_commit,
  274. slot,
  275. y,
  276. rho,
  277. };
  278. let reward_proofs = vec![proof];
  279. // Now we should have all the params, zk proofs and signature secret.
  280. // We return it all and let the caller deal with it.
  281. let debris = ConsensusProposalCallDebris {
  282. unstake_params,
  283. unstake_proofs,
  284. reward_params,
  285. reward_proofs,
  286. stake_params,
  287. stake_proofs,
  288. signature_secret,
  289. };
  290. Ok(debris)
  291. }
  292. }
  293. pub fn create_proposal_reward_proof(
  294. zkbin: &ZkBinary,
  295. pk: &ProvingKey,
  296. slot_checkpoint: &SlotCheckpoint,
  297. secret_key: pallas::Base,
  298. serial: pallas::Base,
  299. value: u64,
  300. value_blind: pallas::Scalar,
  301. new_serial_blind: pallas::Scalar,
  302. ) -> Result<(Proof, ConsensusProposalRewardRevealed)> {
  303. // Proof parameters
  304. let nullifier = Nullifier::from(poseidon_hash([secret_key, serial]));
  305. let value_commit = pedersen_commitment_u64(value, value_blind);
  306. let new_serial = poseidon_hash([SERIAL_PREFIX, secret_key, serial, ZERO]);
  307. let new_serial_commit = pedersen_commitment_base(new_serial, new_serial_blind);
  308. let new_value_commit = pedersen_commitment_u64(value + REWARD, value_blind);
  309. let slot_pallas = pallas::Base::from(slot_checkpoint.slot);
  310. let seed = poseidon_hash([SEED_PREFIX, serial, ZERO]);
  311. let mu_y = poseidon_hash([MU_Y_PREFIX, slot_checkpoint.eta, slot_pallas]);
  312. let y = poseidon_hash([seed, mu_y]);
  313. let mu_rho = poseidon_hash([MU_RHO_PREFIX, slot_checkpoint.eta, slot_pallas]);
  314. let rho = poseidon_hash([seed, mu_rho]);
  315. let (sigma1, sigma2) = (slot_checkpoint.sigma1, slot_checkpoint.sigma2);
  316. // Generate public inputs, witnesses and proof
  317. let public_inputs = ConsensusProposalRewardRevealed {
  318. nullifier,
  319. value_commit,
  320. new_serial_commit,
  321. new_value_commit,
  322. mu_y,
  323. y,
  324. mu_rho,
  325. rho,
  326. sigma1,
  327. sigma2,
  328. };
  329. let prover_witnesses = vec![
  330. Witness::Base(Value::known(secret_key)),
  331. Witness::Base(Value::known(serial)),
  332. Witness::Base(Value::known(pallas::Base::from(value))),
  333. Witness::Base(Value::known(REWARD_PALLAS)),
  334. Witness::Scalar(Value::known(value_blind)),
  335. Witness::Scalar(Value::known(new_serial_blind)),
  336. Witness::Base(Value::known(mu_y)),
  337. Witness::Base(Value::known(mu_rho)),
  338. Witness::Base(Value::known(sigma1)),
  339. Witness::Base(Value::known(sigma2)),
  340. Witness::Base(Value::known(HEADSTART)),
  341. ];
  342. let circuit = ZkCircuit::new(prover_witnesses, zkbin.clone());
  343. let proof = Proof::create(pk, &[circuit], &public_inputs.to_vec(), &mut OsRng)?;
  344. Ok((proof, public_inputs))
  345. }
  346. pub fn create_proposal_mint_proof(
  347. zkbin: &ZkBinary,
  348. pk: &ProvingKey,
  349. output: &TransactionBuilderOutputInfo,
  350. value_blind: pallas::Scalar,
  351. token_blind: pallas::Scalar,
  352. serial_blind: pallas::Scalar,
  353. burnt_secret_key: pallas::Base,
  354. burnt_serial: pallas::Base,
  355. spend_hook: pallas::Base,
  356. user_data: pallas::Base,
  357. coin_blind: pallas::Base,
  358. ) -> Result<(Proof, ConsensusProposalMintRevealed, pallas::Base)> {
  359. let serial = poseidon_hash([SERIAL_PREFIX, burnt_secret_key, burnt_serial, ZERO]);
  360. let value_commit = pedersen_commitment_u64(output.value, value_blind);
  361. let token_commit = pedersen_commitment_base(output.token_id.inner(), token_blind);
  362. let serial_commit = pedersen_commitment_base(serial, serial_blind);
  363. let (pub_x, pub_y) = output.public_key.xy();
  364. let coin = Coin::from(poseidon_hash([
  365. pub_x,
  366. pub_y,
  367. pallas::Base::from(output.value),
  368. output.token_id.inner(),
  369. serial,
  370. spend_hook,
  371. user_data,
  372. coin_blind,
  373. ]));
  374. let public_inputs =
  375. ConsensusProposalMintRevealed { coin, value_commit, token_commit, serial_commit };
  376. let prover_witnesses = vec![
  377. Witness::Base(Value::known(pub_x)),
  378. Witness::Base(Value::known(pub_y)),
  379. Witness::Base(Value::known(pallas::Base::from(output.value))),
  380. Witness::Base(Value::known(output.token_id.inner())),
  381. Witness::Base(Value::known(burnt_secret_key)),
  382. Witness::Base(Value::known(burnt_serial)),
  383. Witness::Base(Value::known(coin_blind)),
  384. Witness::Base(Value::known(spend_hook)),
  385. Witness::Base(Value::known(user_data)),
  386. Witness::Scalar(Value::known(value_blind)),
  387. Witness::Scalar(Value::known(token_blind)),
  388. Witness::Scalar(Value::known(serial_blind)),
  389. ];
  390. let circuit = ZkCircuit::new(prover_witnesses, zkbin.clone());
  391. let proof = Proof::create(pk, &[circuit], &public_inputs.to_vec(), &mut OsRng)?;
  392. Ok((proof, public_inputs, serial))
  393. }