fee_v1.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2024 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. use std::collections::HashMap;
  19. use darkfi::{
  20. blockchain::BlockchainOverlayPtr,
  21. tx::TransactionBuilder,
  22. validator::verification::verify_transaction,
  23. zk::{halo2::Value, Proof, ProvingKey, VerifyingKey, Witness, ZkCircuit},
  24. zkas::ZkBinary,
  25. ClientFailed, Result,
  26. };
  27. use darkfi_sdk::{
  28. bridgetree::{self, Hashable},
  29. crypto::{
  30. note::AeadEncryptedNote,
  31. pasta_prelude::{Curve, CurveAffine, Field},
  32. pedersen_commitment_u64, poseidon_hash, FuncId, Keypair, MerkleNode, MerkleTree, Nullifier,
  33. PublicKey, SecretKey,
  34. },
  35. pasta::pallas,
  36. };
  37. use log::{error, info};
  38. use rand::rngs::OsRng;
  39. use crate::{
  40. client::{compute_remainder_blind, Coin, MoneyNote, OwnCoin},
  41. model::{CoinAttributes, Input, MoneyFeeParamsV1, NullifierAttributes, Output, DARK_TOKEN_ID},
  42. };
  43. /// Append a fee-paying call to the given `TransactionBuilder`.
  44. ///
  45. /// * `keypair`: Caller's keypair
  46. /// * `coin`: `OwnCoin` to use in this builder
  47. /// * `tree`: Merkle tree of coins used to create inclusion proofs
  48. /// * `fee_zkbin`: `Fee_V1` zkas circuit ZkBinary
  49. /// * `fee_pk`: `Fee_V1` zk circuit proving key
  50. /// * `tx_builder`: `TransactionBuilder of the tx we want to pay fee for
  51. /// * `overlay`: `BlockchainOverlayPtr` against which to verify the tx
  52. /// * `time_keeper`: `TimeKeeper` needed for tx verification
  53. /// * `verifying_keys`: ZK verifying keys needed for tx verification
  54. #[allow(clippy::too_many_arguments)]
  55. pub async fn append_fee_call(
  56. keypair: &Keypair,
  57. coin: &OwnCoin,
  58. tree: MerkleTree,
  59. fee_zkbin: &ZkBinary,
  60. fee_pk: &ProvingKey,
  61. tx_builder: &mut TransactionBuilder,
  62. overlay: &BlockchainOverlayPtr,
  63. verifying_block_height: u64,
  64. verifying_keys: &mut HashMap<[u8; 32], HashMap<String, VerifyingKey>>,
  65. ) -> Result<(MoneyFeeParamsV1, FeeCallSecrets)> {
  66. assert!(coin.note.value > 0);
  67. assert_eq!(coin.note.token_id, *DARK_TOKEN_ID);
  68. assert_eq!(coin.note.user_data, pallas::Base::ZERO);
  69. assert_eq!(coin.note.spend_hook, FuncId::none());
  70. // First we will verify the fee-less transaction to see how much gas
  71. // it uses for execution and verification.
  72. let tx = tx_builder.build()?;
  73. let gas_used =
  74. verify_transaction(overlay, verifying_block_height, &tx, verifying_keys, false).await?;
  75. // TODO: We could actually take a set of coins and then find one with
  76. // enough value, instead of expecting one. It depends, the API
  77. // is a bit weird.
  78. // TODO: FIXME: Proper fee pricing
  79. if coin.note.value < gas_used {
  80. error!(
  81. target: "money_contract::client::fee_v1",
  82. "Not enough value in given OwnCoin for fee, have {}, need {}",
  83. coin.note.value, gas_used,
  84. );
  85. return Err(ClientFailed::NotEnoughValue(coin.note.value).into())
  86. }
  87. let change_value = coin.note.value - gas_used;
  88. let input = FeeCallInput {
  89. leaf_position: coin.leaf_position,
  90. merkle_path: tree.witness(coin.leaf_position, 0).unwrap(),
  91. secret: coin.secret,
  92. note: coin.note.clone(),
  93. user_data_blind: pallas::Base::random(&mut OsRng),
  94. };
  95. let output = FeeCallOutput {
  96. public_key: keypair.public,
  97. value: change_value,
  98. token_id: coin.note.token_id,
  99. spend_hook: FuncId::none(),
  100. user_data: pallas::Base::ZERO,
  101. blind: pallas::Base::random(&mut OsRng),
  102. };
  103. let token_blind = pallas::Base::random(&mut OsRng);
  104. let input_value_blind = pallas::Scalar::random(&mut OsRng);
  105. let fee_value_blind = pallas::Scalar::random(&mut OsRng);
  106. let output_value_blind = compute_remainder_blind(&[], &[input_value_blind], &[fee_value_blind]);
  107. let signature_secret = SecretKey::random(&mut OsRng);
  108. info!(target: "money_contract::client::fee_v1", "Creating Fee_V1 ZK proof...");
  109. let (proof, public_inputs) = create_fee_proof(
  110. fee_zkbin,
  111. fee_pk,
  112. &input,
  113. input_value_blind,
  114. &output,
  115. output_value_blind,
  116. output.spend_hook,
  117. output.user_data,
  118. output.blind,
  119. token_blind,
  120. signature_secret,
  121. )?;
  122. // Encrypted note for the output
  123. let note = MoneyNote {
  124. value: output.value,
  125. token_id: output.token_id,
  126. spend_hook: output.spend_hook,
  127. user_data: output.user_data,
  128. coin_blind: output.blind,
  129. value_blind: output_value_blind,
  130. token_blind,
  131. memo: vec![],
  132. };
  133. let encrypted_note = AeadEncryptedNote::encrypt(&note, &output.public_key, &mut OsRng)?;
  134. let params = MoneyFeeParamsV1 {
  135. input: Input {
  136. value_commit: public_inputs.input_value_commit,
  137. token_commit: public_inputs.token_commit,
  138. nullifier: public_inputs.nullifier,
  139. merkle_root: public_inputs.merkle_root,
  140. user_data_enc: public_inputs.input_user_data_enc,
  141. signature_public: public_inputs.signature_public,
  142. },
  143. output: Output {
  144. value_commit: public_inputs.output_value_commit,
  145. token_commit: public_inputs.token_commit,
  146. coin: public_inputs.output_coin,
  147. note: encrypted_note,
  148. },
  149. fee_value_blind,
  150. token_blind,
  151. };
  152. let secrets =
  153. FeeCallSecrets { proof, signature_secret, note, input_value_blind, output_value_blind };
  154. // TODO: Append call to tx builder
  155. Ok((params, secrets))
  156. }
  157. /// Private values related to the Fee call
  158. pub struct FeeCallSecrets {
  159. /// The ZK proof created in this builder
  160. pub proof: Proof,
  161. /// The ephemeral secret key created for tx signining
  162. pub signature_secret: SecretKey,
  163. /// Decrypted note associated with the output
  164. pub note: MoneyNote,
  165. /// The value blind created for the input
  166. pub input_value_blind: pallas::Scalar,
  167. /// The value blind created for the output
  168. pub output_value_blind: pallas::Scalar,
  169. }
  170. /// Revealed public inputs of the `Fee_V1` ZK proof
  171. pub struct FeeRevealed {
  172. /// Input's Nullifier
  173. pub nullifier: Nullifier,
  174. /// Input's value commitment
  175. pub input_value_commit: pallas::Point,
  176. /// Token commitment
  177. pub token_commit: pallas::Base,
  178. /// Merkle root for input coin
  179. pub merkle_root: MerkleNode,
  180. /// Input's spend hook
  181. pub input_spend_hook: FuncId,
  182. /// Encrypted user data for input coin
  183. pub input_user_data_enc: pallas::Base,
  184. /// Public key used to sign transaction
  185. pub signature_public: PublicKey,
  186. /// Output coin commitment
  187. pub output_coin: Coin,
  188. /// Output value commitment
  189. pub output_value_commit: pallas::Point,
  190. }
  191. impl FeeRevealed {
  192. /// Transform the struct into a `Vec<pallas::Base>` ready for
  193. /// proof verification.
  194. pub fn to_vec(&self) -> Vec<pallas::Base> {
  195. let input_vc_coords = self.input_value_commit.to_affine().coordinates().unwrap();
  196. let output_vc_coords = self.output_value_commit.to_affine().coordinates().unwrap();
  197. let sigpub_coords = self.signature_public.inner().to_affine().coordinates().unwrap();
  198. // NOTE: It's important to keep these in the same order
  199. // as the `constrain_instance` calls in the zkas code.
  200. vec![
  201. self.nullifier.inner(),
  202. *input_vc_coords.x(),
  203. *input_vc_coords.y(),
  204. self.token_commit,
  205. self.merkle_root.inner(),
  206. self.input_user_data_enc,
  207. self.input_spend_hook.inner(),
  208. *sigpub_coords.x(),
  209. *sigpub_coords.y(),
  210. self.output_coin.inner(),
  211. *output_vc_coords.x(),
  212. *output_vc_coords.y(),
  213. ]
  214. }
  215. }
  216. struct FeeCallInput {
  217. leaf_position: bridgetree::Position,
  218. merkle_path: Vec<MerkleNode>,
  219. secret: SecretKey,
  220. note: MoneyNote,
  221. user_data_blind: pallas::Base,
  222. }
  223. type FeeCallOutput = CoinAttributes;
  224. /// Create the `Fee_V1` ZK proof given parameters
  225. #[allow(clippy::too_many_arguments)]
  226. fn create_fee_proof(
  227. zkbin: &ZkBinary,
  228. pk: &ProvingKey,
  229. input: &FeeCallInput,
  230. input_value_blind: pallas::Scalar,
  231. output: &FeeCallOutput,
  232. output_value_blind: pallas::Scalar,
  233. output_spend_hook: FuncId,
  234. output_user_data: pallas::Base,
  235. output_coin_blind: pallas::Base,
  236. token_blind: pallas::Base,
  237. signature_secret: SecretKey,
  238. ) -> Result<(Proof, FeeRevealed)> {
  239. let public_key = PublicKey::from_secret(input.secret);
  240. let signature_public = PublicKey::from_secret(signature_secret);
  241. // Create input coin
  242. let input_coin = CoinAttributes {
  243. public_key,
  244. value: input.note.value,
  245. token_id: input.note.token_id,
  246. spend_hook: input.note.spend_hook,
  247. user_data: input.note.user_data,
  248. blind: input.note.coin_blind,
  249. }
  250. .to_coin();
  251. let nullifier =
  252. NullifierAttributes { secret_key: input.secret, coin: input_coin }.to_nullifier();
  253. let merkle_root = {
  254. let position: u64 = input.leaf_position.into();
  255. let mut current = MerkleNode::from(input_coin.inner());
  256. for (level, sibling) in input.merkle_path.iter().enumerate() {
  257. let level = level as u8;
  258. current = if position & (1 << level) == 0 {
  259. MerkleNode::combine(level.into(), &current, sibling)
  260. } else {
  261. MerkleNode::combine(level.into(), sibling, &current)
  262. };
  263. }
  264. current
  265. };
  266. let input_user_data_enc = poseidon_hash([input.note.user_data, input.user_data_blind]);
  267. let input_value_commit = pedersen_commitment_u64(input.note.value, input_value_blind);
  268. let output_value_commit = pedersen_commitment_u64(output.value, output_value_blind);
  269. let token_commit = poseidon_hash([input.note.token_id.inner(), token_blind]);
  270. // Create output coin
  271. let output_coin = CoinAttributes {
  272. public_key: output.public_key,
  273. value: output.value,
  274. token_id: output.token_id,
  275. spend_hook: output_spend_hook,
  276. user_data: output_user_data,
  277. blind: output_coin_blind,
  278. }
  279. .to_coin();
  280. let public_inputs = FeeRevealed {
  281. nullifier,
  282. input_value_commit,
  283. token_commit,
  284. merkle_root,
  285. input_spend_hook: input.note.spend_hook,
  286. input_user_data_enc,
  287. signature_public,
  288. output_coin,
  289. output_value_commit,
  290. };
  291. let prover_witnesses = vec![
  292. Witness::Base(Value::known(input.secret.inner())),
  293. Witness::Uint32(Value::known(u64::from(input.leaf_position).try_into().unwrap())),
  294. Witness::MerklePath(Value::known(input.merkle_path.clone().try_into().unwrap())),
  295. Witness::Base(Value::known(signature_secret.inner())),
  296. Witness::Base(Value::known(pallas::Base::from(input.note.value))),
  297. Witness::Scalar(Value::known(input_value_blind)),
  298. Witness::Base(Value::known(input.note.spend_hook.inner())),
  299. Witness::Base(Value::known(input.note.user_data)),
  300. Witness::Base(Value::known(input.note.coin_blind)),
  301. Witness::Base(Value::known(input.user_data_blind)),
  302. Witness::Base(Value::known(pallas::Base::from(output.value))),
  303. Witness::Base(Value::known(output_spend_hook.inner())),
  304. Witness::Base(Value::known(output_user_data)),
  305. Witness::Scalar(Value::known(output_value_blind)),
  306. Witness::Base(Value::known(output_coin_blind)),
  307. Witness::Base(Value::known(input.note.token_id.inner())),
  308. Witness::Base(Value::known(token_blind)),
  309. ];
  310. let circuit = ZkCircuit::new(prover_witnesses, zkbin);
  311. let proof = Proof::create(pk, &[circuit], &public_inputs.to_vec(), &mut OsRng)?;
  312. Ok((proof, public_inputs))
  313. }