builder.rs 5.8 KB

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