builder.rs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. use bellman::groth16;
  2. use bls12_381::Bls12;
  3. use ff::Field;
  4. use rand::rngs::OsRng;
  5. use super::{
  6. partial::{PartialTransaction, PartialTransactionClearInput, PartialTransactionInput},
  7. Transaction, TransactionClearInput, TransactionInput, TransactionOutput,
  8. };
  9. use crate::crypto::{
  10. create_mint_proof, create_spend_proof, merkle::MerklePath, merkle_node::MerkleNode, note::Note,
  11. schnorr,
  12. };
  13. use crate::serial::Encodable;
  14. pub struct TransactionBuilder {
  15. pub clear_inputs: Vec<TransactionBuilderClearInputInfo>,
  16. pub inputs: Vec<TransactionBuilderInputInfo>,
  17. pub outputs: Vec<TransactionBuilderOutputInfo>,
  18. }
  19. pub struct TransactionBuilderClearInputInfo {
  20. pub value: u64,
  21. pub signature_secret: jubjub::Fr,
  22. }
  23. pub struct TransactionBuilderInputInfo {
  24. pub merkle_path: MerklePath<MerkleNode>,
  25. pub secret: jubjub::Fr,
  26. pub note: Note,
  27. }
  28. pub struct TransactionBuilderOutputInfo {
  29. pub value: u64,
  30. pub public: jubjub::SubgroupPoint,
  31. }
  32. impl TransactionBuilder {
  33. fn compute_remainder_blind(
  34. clear_inputs: &Vec<PartialTransactionClearInput>,
  35. input_blinds: &Vec<jubjub::Fr>,
  36. output_blinds: &Vec<jubjub::Fr>,
  37. ) -> jubjub::Fr {
  38. let mut total = jubjub::Fr::zero();
  39. for input in clear_inputs {
  40. total += input.valcom_blind;
  41. }
  42. for input_blind in input_blinds {
  43. total += input_blind;
  44. }
  45. for output_blind in output_blinds {
  46. total -= output_blind;
  47. }
  48. total
  49. }
  50. pub fn build(
  51. self,
  52. mint_params: &groth16::Parameters<Bls12>,
  53. spend_params: &groth16::Parameters<Bls12>,
  54. ) -> Transaction {
  55. let mut clear_inputs = vec![];
  56. for input in &self.clear_inputs {
  57. let signature_public =
  58. zcash_primitives::constants::SPENDING_KEY_GENERATOR * input.signature_secret;
  59. let valcom_blind: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
  60. let clear_input = PartialTransactionClearInput {
  61. value: input.value,
  62. valcom_blind,
  63. signature_public,
  64. };
  65. clear_inputs.push(clear_input);
  66. }
  67. let mut inputs = vec![];
  68. let mut input_blinds = vec![];
  69. let mut signature_secrets = vec![];
  70. for input in &self.inputs {
  71. input_blinds.push(input.note.valcom_blind.clone());
  72. let signature_secret: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
  73. // make proof
  74. // TODO: Some stupid glue code. Need to sort this out
  75. let auth_path: Vec<(bls12_381::Scalar, bool)> = input
  76. .merkle_path
  77. .auth_path
  78. .iter()
  79. .map(|(node, b)| ((*node).into(), *b))
  80. .collect();
  81. let (proof, revealed) = create_spend_proof(
  82. &spend_params,
  83. input.note.value,
  84. input.note.valcom_blind,
  85. input.note.serial,
  86. input.note.coin_blind,
  87. input.secret,
  88. auth_path,
  89. signature_secret.clone(),
  90. );
  91. // First we make the tx then sign after
  92. let signature_secret = schnorr::SecretKey(signature_secret);
  93. signature_secrets.push(signature_secret);
  94. let input = PartialTransactionInput {
  95. spend_proof: proof,
  96. revealed,
  97. };
  98. inputs.push(input);
  99. }
  100. let mut outputs = vec![];
  101. let mut output_blinds = vec![];
  102. for (i, output) in self.outputs.iter().enumerate() {
  103. let valcom_blind = if i == self.outputs.len() - 1 {
  104. Self::compute_remainder_blind(&clear_inputs, &input_blinds, &output_blinds)
  105. } else {
  106. jubjub::Fr::random(&mut OsRng)
  107. };
  108. output_blinds.push(valcom_blind);
  109. let serial: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
  110. let coin_blind: jubjub::Fr = jubjub::Fr::random(&mut OsRng);
  111. let (mint_proof, revealed) = create_mint_proof(
  112. mint_params,
  113. output.value,
  114. valcom_blind.clone(),
  115. serial.clone(),
  116. coin_blind.clone(),
  117. output.public.clone(),
  118. );
  119. // Encrypted note
  120. let note = Note {
  121. serial,
  122. value: output.value,
  123. coin_blind,
  124. valcom_blind,
  125. };
  126. let encrypted_note = note.encrypt(&output.public).unwrap();
  127. let output = TransactionOutput {
  128. mint_proof,
  129. revealed,
  130. enc_note: encrypted_note,
  131. };
  132. outputs.push(output);
  133. }
  134. let partial_tx = PartialTransaction {
  135. clear_inputs,
  136. inputs,
  137. outputs,
  138. };
  139. let mut unsigned_tx_data = vec![];
  140. partial_tx
  141. .encode(&mut unsigned_tx_data)
  142. .expect("TODO handle this");
  143. let mut clear_inputs = vec![];
  144. for (input, info) in partial_tx.clear_inputs.into_iter().zip(self.clear_inputs) {
  145. let secret = schnorr::SecretKey(info.signature_secret.clone());
  146. let signature = secret.sign(&unsigned_tx_data[..]);
  147. let input = TransactionClearInput::from_partial(input, signature);
  148. clear_inputs.push(input);
  149. }
  150. let mut inputs = vec![];
  151. for (input, signature_secret) in partial_tx
  152. .inputs
  153. .into_iter()
  154. .zip(signature_secrets.into_iter())
  155. {
  156. let signature = signature_secret.sign(&unsigned_tx_data[..]);
  157. let input = TransactionInput::from_partial(input, signature);
  158. inputs.push(input);
  159. }
  160. Transaction {
  161. clear_inputs,
  162. inputs,
  163. outputs: partial_tx.outputs,
  164. }
  165. }
  166. }