diff --git a/src/crypto/nullifier.rs b/src/crypto/nullifier.rs new file mode 100644 index 000000000..b1049707f --- /dev/null +++ b/src/crypto/nullifier.rs @@ -0,0 +1,10 @@ +pub struct Nullifier { + pub repr: [u8; 32], +} + +impl Nullifier { + pub fn new(repr: [u8; 32]) -> Self { + Self { repr } + } +} + diff --git a/src/tx/builder.rs b/src/tx/builder.rs new file mode 100644 index 000000000..b0e35c620 --- /dev/null +++ b/src/tx/builder.rs @@ -0,0 +1,194 @@ +use bellman::groth16; +use bls12_381::Bls12; +use rand::rngs::OsRng; +use std::collections::HashMap; +use std::io; +use ff::Field; + +use crate::error::{Error, Result}; +use crate::serial::{Decodable, Encodable, VarInt}; +use super::{TransactionClearInput, TransactionInput, TransactionOutput, Transaction, partial::{PartialTransactionClearInput, PartialTransactionInput, PartialTransaction}}; +use crate::crypto::{ + coin::Coin, + create_mint_proof, create_spend_proof, load_params, + merkle::CommitmentTree, + note::{EncryptedNote, Note}, + save_params, schnorr, setup_mint_prover, setup_spend_prover, verify_mint_proof, + verify_spend_proof, MintRevealedValues, SpendRevealedValues, +}; + +pub struct TransactionBuilder { + pub clear_inputs: Vec, + pub inputs: Vec, + pub outputs: Vec, +} + +pub struct TransactionBuilderClearInputInfo { + pub value: u64, + pub signature_secret: jubjub::Fr, +} + +pub struct TransactionBuilderInputInfo { + pub merkle_path: Vec<(bls12_381::Scalar, bool)>, + pub secret: jubjub::Fr, + pub note: Note, +} + +pub struct TransactionBuilderOutputInfo { + pub value: u64, + pub public: jubjub::SubgroupPoint, +} + +impl TransactionBuilder { + fn compute_remainder_blind( + clear_inputs: &Vec, + input_blinds: &Vec, + output_blinds: &Vec, + ) -> jubjub::Fr { + let mut total = jubjub::Fr::zero(); + + for input in clear_inputs { + total += input.valcom_blind; + } + + for input_blind in input_blinds { + total += input_blind; + } + + for output_blind in output_blinds { + total -= output_blind; + } + + total + } + + pub fn build( + self, + mint_params: &groth16::Parameters, + spend_params: &groth16::Parameters, + ) -> Transaction { + let mut clear_inputs = vec![]; + for input in &self.clear_inputs { + let signature_public = + zcash_primitives::constants::SPENDING_KEY_GENERATOR * input.signature_secret; + + let valcom_blind: jubjub::Fr = jubjub::Fr::random(&mut OsRng); + let clear_input = PartialTransactionClearInput { + value: input.value, + valcom_blind, + signature_public, + }; + clear_inputs.push(clear_input); + } + + let mut inputs = vec![]; + let mut input_blinds = vec![]; + let mut signature_secrets = vec![]; + for input in &self.inputs { + input_blinds.push(input.note.valcom_blind.clone()); + + let signature_secret: jubjub::Fr = jubjub::Fr::random(&mut OsRng); + + // make proof + + let (proof, revealed) = create_spend_proof( + &spend_params, + input.note.value, + input.note.valcom_blind, + input.note.serial, + input.note.coin_blind, + input.secret, + input.merkle_path.clone(), + signature_secret.clone(), + ); + + // First we make the tx then sign after + let signature_secret = schnorr::SecretKey(signature_secret); + signature_secrets.push(signature_secret); + + let input = PartialTransactionInput { + spend_proof: proof, + revealed, + }; + inputs.push(input); + } + + let mut outputs = vec![]; + let mut output_blinds = vec![]; + for (i, output) in self.outputs.iter().enumerate() { + let valcom_blind = if i == self.outputs.len() - 1 { + Self::compute_remainder_blind(&clear_inputs, &input_blinds, &output_blinds) + } else { + jubjub::Fr::random(&mut OsRng) + }; + output_blinds.push(valcom_blind); + + let serial: jubjub::Fr = jubjub::Fr::random(&mut OsRng); + let coin_blind: jubjub::Fr = jubjub::Fr::random(&mut OsRng); + + let (mint_proof, revealed) = create_mint_proof( + mint_params, + output.value, + valcom_blind.clone(), + serial.clone(), + coin_blind.clone(), + output.public.clone(), + ); + + // Encrypted note + + let note = Note { + serial, + value: output.value, + coin_blind, + valcom_blind, + }; + + let encrypted_note = note.encrypt(&output.public).unwrap(); + + let output = TransactionOutput { + mint_proof, + revealed, + enc_note: encrypted_note, + }; + outputs.push(output); + } + + let partial_tx = PartialTransaction { + clear_inputs, + inputs, + outputs, + }; + + let mut unsigned_tx_data = vec![]; + partial_tx + .encode(&mut unsigned_tx_data) + .expect("TODO handle this"); + + let mut clear_inputs = vec![]; + for (input, info) in partial_tx.clear_inputs.into_iter().zip(self.clear_inputs) { + let secret = schnorr::SecretKey(info.signature_secret.clone()); + let signature = secret.sign(&unsigned_tx_data[..]); + let input = TransactionClearInput::from_partial(input, signature); + clear_inputs.push(input); + } + + let mut inputs = vec![]; + for (input, signature_secret) in partial_tx + .inputs + .into_iter() + .zip(signature_secrets.into_iter()) + { + let signature = signature_secret.sign(&unsigned_tx_data[..]); + let input = TransactionInput::from_partial(input, signature); + inputs.push(input); + } + + Transaction { + clear_inputs, + inputs, + outputs: partial_tx.outputs, + } + } +} + diff --git a/src/tx.rs b/src/tx/mod.rs similarity index 51% rename from src/tx.rs rename to src/tx/mod.rs index de7f00d36..cbc061b37 100644 --- a/src/tx.rs +++ b/src/tx/mod.rs @@ -1,3 +1,6 @@ +pub mod builder; +pub mod partial; + use bellman::groth16; use bls12_381::Bls12; use ff::Field; @@ -18,212 +21,9 @@ use crate::error::{Error, Result}; use crate::impl_vec; use crate::serial::{Decodable, Encodable, VarInt}; use crate::state; +use self::partial::{PartialTransactionClearInput, PartialTransactionInput}; -pub struct TransactionBuilder { - pub clear_inputs: Vec, - pub inputs: Vec, - pub outputs: Vec, -} - -impl TransactionBuilder { - fn compute_remainder_blind( - clear_inputs: &Vec, - input_blinds: &Vec, - output_blinds: &Vec, - ) -> jubjub::Fr { - let mut total = jubjub::Fr::zero(); - - for input in clear_inputs { - total += input.valcom_blind; - } - - for input_blind in input_blinds { - total += input_blind; - } - - for output_blind in output_blinds { - total -= output_blind; - } - - total - } - - pub fn build( - self, - mint_params: &groth16::Parameters, - spend_params: &groth16::Parameters, - ) -> Transaction { - let mut clear_inputs = vec![]; - for input in &self.clear_inputs { - let signature_public = - zcash_primitives::constants::SPENDING_KEY_GENERATOR * input.signature_secret; - - let valcom_blind: jubjub::Fr = jubjub::Fr::random(&mut OsRng); - let clear_input = PartialTransactionClearInput { - value: input.value, - valcom_blind, - signature_public, - }; - clear_inputs.push(clear_input); - } - - let mut inputs = vec![]; - let mut input_blinds = vec![]; - let mut signature_secrets = vec![]; - for input in &self.inputs { - input_blinds.push(input.note.valcom_blind.clone()); - - let signature_secret: jubjub::Fr = jubjub::Fr::random(&mut OsRng); - - // make proof - - let (proof, revealed) = create_spend_proof( - &spend_params, - input.note.value, - input.note.valcom_blind, - input.note.serial, - input.note.coin_blind, - input.secret, - input.merkle_path.clone(), - signature_secret.clone(), - ); - - // First we make the tx then sign after - let signature_secret = schnorr::SecretKey(signature_secret); - signature_secrets.push(signature_secret); - - let input = PartialTransactionInput { - spend_proof: proof, - revealed, - }; - inputs.push(input); - } - - let mut outputs = vec![]; - let mut output_blinds = vec![]; - for (i, output) in self.outputs.iter().enumerate() { - let valcom_blind = if i == self.outputs.len() - 1 { - Self::compute_remainder_blind(&clear_inputs, &input_blinds, &output_blinds) - } else { - jubjub::Fr::random(&mut OsRng) - }; - output_blinds.push(valcom_blind); - - let serial: jubjub::Fr = jubjub::Fr::random(&mut OsRng); - let coin_blind: jubjub::Fr = jubjub::Fr::random(&mut OsRng); - - let (mint_proof, revealed) = create_mint_proof( - mint_params, - output.value, - valcom_blind.clone(), - serial.clone(), - coin_blind.clone(), - output.public.clone(), - ); - - // Encrypted note - - let note = Note { - serial, - value: output.value, - coin_blind, - valcom_blind, - }; - - let encrypted_note = note.encrypt(&output.public).unwrap(); - - let output = TransactionOutput { - mint_proof, - revealed, - enc_note: encrypted_note, - }; - outputs.push(output); - } - - let partial_tx = PartialTransaction { - clear_inputs, - inputs, - outputs, - }; - - let mut unsigned_tx_data = vec![]; - partial_tx - .encode(&mut unsigned_tx_data) - .expect("TODO handle this"); - - let mut clear_inputs = vec![]; - for (input, info) in partial_tx.clear_inputs.into_iter().zip(self.clear_inputs) { - let secret = schnorr::SecretKey(info.signature_secret.clone()); - let signature = secret.sign(&unsigned_tx_data[..]); - let input = TransactionClearInput::from_partial(input, signature); - clear_inputs.push(input); - } - - let mut inputs = vec![]; - for (input, signature_secret) in partial_tx - .inputs - .into_iter() - .zip(signature_secrets.into_iter()) - { - let signature = signature_secret.sign(&unsigned_tx_data[..]); - let input = TransactionInput::from_partial(input, signature); - inputs.push(input); - } - - Transaction { - clear_inputs, - inputs, - outputs: partial_tx.outputs, - } - } -} - -pub struct TransactionBuilderClearOutputInfo { - pub value: u64, - pub instructions: String, -} - -pub struct TransactionBuilderClearInputInfo { - pub value: u64, - pub signature_secret: jubjub::Fr, -} - -pub struct TransactionBuilderInputInfo { - pub merkle_path: Vec<(bls12_381::Scalar, bool)>, - pub secret: jubjub::Fr, - pub note: Note, -} - -pub struct TransactionBuilderOutputInfo { - pub value: u64, - pub public: jubjub::SubgroupPoint, -} - -pub struct PartialTransaction { - pub clear_inputs: Vec, - pub inputs: Vec, - pub outputs: Vec, -} - -impl Encodable for PartialTransaction { - fn encode(&self, mut s: S) -> Result { - let mut len = 0; - len += self.clear_inputs.encode(&mut s)?; - len += self.inputs.encode(&mut s)?; - len += self.outputs.encode(s)?; - Ok(len) - } -} - -impl Decodable for PartialTransaction { - fn decode(mut d: D) -> Result { - Ok(Self { - clear_inputs: Decodable::decode(&mut d)?, - inputs: Decodable::decode(&mut d)?, - outputs: Decodable::decode(d)?, - }) - } -} +pub use self::builder::{TransactionBuilder, TransactionBuilderClearInputInfo, TransactionBuilderInputInfo, TransactionBuilderOutputInfo}; pub struct Transaction { pub clear_inputs: Vec, @@ -231,24 +31,23 @@ pub struct Transaction { pub outputs: Vec, } -impl Encodable for Transaction { - fn encode(&self, mut s: S) -> Result { - let mut len = 0; - len += self.clear_inputs.encode(&mut s)?; - len += self.inputs.encode(&mut s)?; - len += self.outputs.encode(s)?; - Ok(len) - } +pub struct TransactionClearInput { + pub value: u64, + pub valcom_blind: jubjub::Fr, + pub signature_public: jubjub::SubgroupPoint, + pub signature: schnorr::Signature, } -impl Decodable for Transaction { - fn decode(mut d: D) -> Result { - Ok(Self { - clear_inputs: Decodable::decode(&mut d)?, - inputs: Decodable::decode(&mut d)?, - outputs: Decodable::decode(d)?, - }) - } +pub struct TransactionInput { + pub spend_proof: groth16::Proof, + pub revealed: SpendRevealedValues, + pub signature: schnorr::Signature, +} + +pub struct TransactionOutput { + pub mint_proof: groth16::Proof, + pub revealed: MintRevealedValues, + pub enc_note: EncryptedNote, } impl Transaction { @@ -314,13 +113,6 @@ impl Transaction { } } -pub struct TransactionClearInput { - pub value: u64, - pub valcom_blind: jubjub::Fr, - pub signature_public: jubjub::SubgroupPoint, - pub signature: schnorr::Signature, -} - impl TransactionClearInput { fn from_partial(partial: PartialTransactionClearInput, signature: schnorr::Signature) -> Self { Self { @@ -340,25 +132,42 @@ impl TransactionClearInput { } } -macro_rules! impl_vec_without_signature { - ($type: ty) => { - impl EncodableWithoutSignature for Vec<$type> { - #[inline] - fn encode_without_signature(&self, mut s: S) -> Result { - let mut len = 0; - len += VarInt(self.len() as u64).encode(&mut s)?; - for c in self.iter() { - len += c.encode_without_signature(&mut s)?; - } - Ok(len) - } +impl TransactionInput { + fn from_partial(partial: PartialTransactionInput, signature: schnorr::Signature) -> Self { + Self { + spend_proof: partial.spend_proof, + revealed: partial.revealed, + signature, } - }; + } + + fn encode_without_signature(&self, mut s: S) -> Result { + let mut len = 0; + len += self.spend_proof.encode(&mut s)?; + len += self.revealed.encode(&mut s)?; + Ok(len) + } } -impl_vec_without_signature!(TransactionClearInput); +impl Encodable for Transaction { + fn encode(&self, mut s: S) -> Result { + let mut len = 0; + len += self.clear_inputs.encode(&mut s)?; + len += self.inputs.encode(&mut s)?; + len += self.outputs.encode(s)?; + Ok(len) + } +} -impl_vec!(TransactionClearInput); +impl Decodable for Transaction { + fn decode(mut d: D) -> Result { + Ok(Self { + clear_inputs: Decodable::decode(&mut d)?, + inputs: Decodable::decode(&mut d)?, + outputs: Decodable::decode(d)?, + }) + } +} impl Encodable for TransactionClearInput { fn encode(&self, mut s: S) -> Result { @@ -382,88 +191,6 @@ impl Decodable for TransactionClearInput { } } -pub struct PartialTransactionClearInput { - pub value: u64, - pub valcom_blind: jubjub::Fr, - pub signature_public: jubjub::SubgroupPoint, -} - -impl_vec!(PartialTransactionClearInput); - -impl Encodable for PartialTransactionClearInput { - fn encode(&self, mut s: S) -> Result { - let mut len = 0; - len += self.value.encode(&mut s)?; - len += self.valcom_blind.encode(&mut s)?; - len += self.signature_public.encode(&mut s)?; - Ok(len) - } -} - -impl Decodable for PartialTransactionClearInput { - fn decode(mut d: D) -> Result { - Ok(Self { - value: Decodable::decode(&mut d)?, - valcom_blind: Decodable::decode(&mut d)?, - signature_public: Decodable::decode(&mut d)?, - }) - } -} - -pub struct PartialTransactionInput { - pub spend_proof: groth16::Proof, - pub revealed: SpendRevealedValues, -} - -impl Encodable for PartialTransactionInput { - fn encode(&self, mut s: S) -> Result { - let mut len = 0; - len += self.spend_proof.encode(&mut s)?; - len += self.revealed.encode(s)?; - Ok(len) - } -} - -impl Decodable for PartialTransactionInput { - fn decode(mut d: D) -> Result { - Ok(Self { - spend_proof: Decodable::decode(&mut d)?, - revealed: Decodable::decode(d)?, - }) - } -} - -impl_vec!(PartialTransactionInput); - -pub struct TransactionInput { - pub spend_proof: groth16::Proof, - pub revealed: SpendRevealedValues, - pub signature: schnorr::Signature, -} - -impl TransactionInput { - fn from_partial(partial: PartialTransactionInput, signature: schnorr::Signature) -> Self { - Self { - spend_proof: partial.spend_proof, - revealed: partial.revealed, - signature, - } - } - - fn encode_without_signature(&self, mut s: S) -> Result { - let mut len = 0; - len += self.spend_proof.encode(&mut s)?; - len += self.revealed.encode(&mut s)?; - Ok(len) - } -} - -trait EncodableWithoutSignature { - fn encode_without_signature(&self, s: S) -> Result; -} - -impl_vec_without_signature!(TransactionInput); - impl Encodable for TransactionInput { fn encode(&self, mut s: S) -> Result { let mut len = 0; @@ -484,16 +211,6 @@ impl Decodable for TransactionInput { } } -impl_vec!(TransactionInput); - -pub struct TransactionOutput { - pub mint_proof: groth16::Proof, - pub revealed: MintRevealedValues, - pub enc_note: EncryptedNote, -} - -impl_vec!(TransactionOutput); - impl Encodable for TransactionOutput { fn encode(&self, mut s: S) -> Result { let mut len = 0; @@ -513,3 +230,30 @@ impl Decodable for TransactionOutput { }) } } + +trait EncodableWithoutSignature { + fn encode_without_signature(&self, s: S) -> Result; +} + +macro_rules! impl_vec_without_signature { + ($type: ty) => { + impl EncodableWithoutSignature for Vec<$type> { + #[inline] + fn encode_without_signature(&self, mut s: S) -> Result { + let mut len = 0; + len += VarInt(self.len() as u64).encode(&mut s)?; + for c in self.iter() { + len += c.encode_without_signature(&mut s)?; + } + Ok(len) + } + } + }; +} + +impl_vec_without_signature!(TransactionClearInput); +impl_vec_without_signature!(TransactionInput); +impl_vec!(TransactionClearInput); +impl_vec!(TransactionInput); +impl_vec!(TransactionOutput); + diff --git a/src/tx/partial.rs b/src/tx/partial.rs new file mode 100644 index 000000000..db8559365 --- /dev/null +++ b/src/tx/partial.rs @@ -0,0 +1,99 @@ +use bellman::groth16; +use bls12_381::Bls12; +use rand::rngs::OsRng; +use std::collections::HashMap; +use std::io; +use ff::Field; + +use crate::error::{Error, Result}; +use crate::impl_vec; +use crate::serial::{Decodable, Encodable, VarInt}; +use super::{TransactionClearInput, TransactionInput, TransactionOutput, Transaction}; +use crate::crypto::{ + coin::Coin, + create_mint_proof, create_spend_proof, load_params, + merkle::CommitmentTree, + note::{EncryptedNote, Note}, + save_params, schnorr, setup_mint_prover, setup_spend_prover, verify_mint_proof, + verify_spend_proof, MintRevealedValues, SpendRevealedValues, +}; + +pub struct PartialTransaction { + pub clear_inputs: Vec, + pub inputs: Vec, + pub outputs: Vec, +} + +pub struct PartialTransactionClearInput { + pub value: u64, + pub valcom_blind: jubjub::Fr, + pub signature_public: jubjub::SubgroupPoint, +} + +pub struct PartialTransactionInput { + pub spend_proof: groth16::Proof, + pub revealed: SpendRevealedValues, +} + +impl Encodable for PartialTransaction { + fn encode(&self, mut s: S) -> Result { + let mut len = 0; + len += self.clear_inputs.encode(&mut s)?; + len += self.inputs.encode(&mut s)?; + len += self.outputs.encode(s)?; + Ok(len) + } +} + +impl Decodable for PartialTransaction { + fn decode(mut d: D) -> Result { + Ok(Self { + clear_inputs: Decodable::decode(&mut d)?, + inputs: Decodable::decode(&mut d)?, + outputs: Decodable::decode(d)?, + }) + } +} + +impl Encodable for PartialTransactionClearInput { + fn encode(&self, mut s: S) -> Result { + let mut len = 0; + len += self.value.encode(&mut s)?; + len += self.valcom_blind.encode(&mut s)?; + len += self.signature_public.encode(&mut s)?; + Ok(len) + } +} + +impl Decodable for PartialTransactionClearInput { + fn decode(mut d: D) -> Result { + Ok(Self { + value: Decodable::decode(&mut d)?, + valcom_blind: Decodable::decode(&mut d)?, + signature_public: Decodable::decode(&mut d)?, + }) + } +} + + +impl Encodable for PartialTransactionInput { + fn encode(&self, mut s: S) -> Result { + let mut len = 0; + len += self.spend_proof.encode(&mut s)?; + len += self.revealed.encode(s)?; + Ok(len) + } +} + +impl Decodable for PartialTransactionInput { + fn decode(mut d: D) -> Result { + Ok(Self { + spend_proof: Decodable::decode(&mut d)?, + revealed: Decodable::decode(d)?, + }) + } +} + +impl_vec!(PartialTransactionClearInput); +impl_vec!(PartialTransactionInput); +