diff --git a/src/bin/tx.rs b/src/bin/tx.rs index eda5dee4f..6dee0997b 100644 --- a/src/bin/tx.rs +++ b/src/bin/tx.rs @@ -51,6 +51,7 @@ fn main() { clear_inputs: vec![tx::TransactionBuilderClearInputInfo { value: 110, signature_secret: cashier_secret }], inputs: vec![], outputs: vec![tx::TransactionBuilderOutputInfo { value: 110, public }], + clear_outputs: vec![] }; // We will 'compile' the tx, and then serialize it to this Vec @@ -159,6 +160,7 @@ fn main() { // We can add more outputs to this list. // The only constraint is that sum(value in) == sum(value out) outputs: vec![tx::TransactionBuilderOutputInfo { value: 110, public: public2 }], + clear_outputs: vec![] }; // Build the tx let mut tx_data = vec![]; @@ -171,5 +173,7 @@ fn main() { let tx = tx::Transaction::decode(&tx_data[..]).unwrap(); assert!(tx.verify(&mint_pvk, &spend_pvk)); } + + // Step 4 withdraw the funds } diff --git a/src/tx.rs b/src/tx.rs index 266f6d4d8..d136362b1 100644 --- a/src/tx.rs +++ b/src/tx.rs @@ -22,6 +22,7 @@ pub struct TransactionBuilder { pub clear_inputs: Vec, pub inputs: Vec, pub outputs: Vec, + pub clear_outputs: Vec, } impl TransactionBuilder { @@ -29,6 +30,7 @@ impl TransactionBuilder { clear_inputs: &Vec, input_blinds: &Vec, output_blinds: &Vec, + clear_outputs: &Vec, ) -> jubjub::Fr { let mut total = jubjub::Fr::zero(); @@ -44,6 +46,10 @@ impl TransactionBuilder { total -= output_blind; } + for output in clear_outputs { + total -= output.valcom_blind; + } + total } @@ -98,11 +104,13 @@ impl TransactionBuilder { inputs.push(input); } + let last_output_index = self.outputs.len() + self.clear_outputs.len() - 1; + 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) + let valcom_blind = if i == last_output_index { + Self::compute_remainder_blind(&clear_inputs, &input_blinds, &output_blinds, &vec![]) } else { jubjub::Fr::random(&mut OsRng) }; @@ -139,10 +147,28 @@ impl TransactionBuilder { outputs.push(output); } + let mut clear_outputs = vec![]; + + for (i, output) in self.clear_outputs.into_iter().enumerate() { + let valcom_blind = if self.outputs.len() + i == last_output_index { + Self::compute_remainder_blind(&clear_inputs, &input_blinds, &output_blinds, &clear_outputs) + } else { + jubjub::Fr::random(&mut OsRng) + }; + + let output = TransactionClearOutput { + value: output.value, + valcom_blind, + instructions: output.instructions + }; + clear_outputs.push(output); + } + let partial_tx = PartialTransaction { clear_inputs, inputs, outputs, + clear_outputs }; let mut unsigned_tx_data = vec![]; @@ -173,10 +199,16 @@ impl TransactionBuilder { clear_inputs, inputs, outputs: partial_tx.outputs, + clear_outputs: partial_tx.clear_outputs } } } +pub struct TransactionBuilderClearOutputInfo { + pub value: u64, + pub instructions: String +} + pub struct TransactionBuilderClearInputInfo { pub value: u64, pub signature_secret: jubjub::Fr, @@ -199,6 +231,7 @@ pub struct PartialTransaction { pub clear_inputs: Vec, pub inputs: Vec, pub outputs: Vec, + pub clear_outputs: Vec, } impl Encodable for PartialTransaction { @@ -206,7 +239,8 @@ impl Encodable for PartialTransaction { let mut len = 0; len += self.clear_inputs.encode(&mut s)?; len += self.inputs.encode(&mut s)?; - len += self.outputs.encode(s)?; + len += self.outputs.encode(&mut s)?; + len += self.clear_outputs.encode(&mut s)?; Ok(len) } } @@ -216,7 +250,8 @@ impl Decodable for PartialTransaction { Ok(Self { clear_inputs: Decodable::decode(&mut d)?, inputs: Decodable::decode(&mut d)?, - outputs: Decodable::decode(d)?, + outputs: Decodable::decode(&mut d)?, + clear_outputs: Decodable::decode(&mut d)?, }) } } @@ -225,6 +260,7 @@ pub struct Transaction { pub clear_inputs: Vec, pub inputs: Vec, pub outputs: Vec, + pub clear_outputs: Vec } impl Encodable for Transaction { @@ -232,7 +268,8 @@ impl Encodable for Transaction { let mut len = 0; len += self.clear_inputs.encode(&mut s)?; len += self.inputs.encode(&mut s)?; - len += self.outputs.encode(s)?; + len += self.outputs.encode(&mut s)?; + len += self.clear_outputs.encode(&mut s)?; Ok(len) } } @@ -242,7 +279,8 @@ impl Decodable for Transaction { Ok(Self { clear_inputs: Decodable::decode(&mut d)?, inputs: Decodable::decode(&mut d)?, - outputs: Decodable::decode(d)?, + outputs: Decodable::decode(&mut d)?, + clear_outputs: Decodable::decode(&mut d)?, }) } } @@ -252,7 +290,8 @@ impl Transaction { let mut len = 0; len += self.clear_inputs.encode_without_signature(&mut s)?; len += self.inputs.encode_without_signature(&mut s)?; - len += self.outputs.encode(s)?; + len += self.outputs.encode(&mut s)?; + len += self.clear_outputs.encode(&mut s)?; Ok(len) } @@ -274,6 +313,7 @@ impl Transaction { } for input in &self.inputs { if !verify_spend_proof(spend_pvk, &input.spend_proof, &input.revealed) { + println!("spend fail"); return false; } valcom_total += &input.revealed.value_commit; @@ -285,6 +325,9 @@ impl Transaction { } valcom_total -= &output.revealed.value_commit; } + for output in &self.clear_outputs { + valcom_total -= Self::compute_value_commit(output.value, &output.valcom_blind); + } // Verify signatures let mut unsigned_tx_data = vec![]; @@ -507,3 +550,32 @@ impl Decodable for TransactionOutput { }) } } + +pub struct TransactionClearOutput { + pub value: u64, + pub valcom_blind: jubjub::Fr, + pub instructions: String +} + +impl_vec!(TransactionClearOutput); + +impl Encodable for TransactionClearOutput { + 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.instructions.encode(s)?; + Ok(len) + } +} + +impl Decodable for TransactionClearOutput { + fn decode(mut d: D) -> Result { + Ok(Self { + value: Decodable::decode(&mut d)?, + valcom_blind: Decodable::decode(&mut d)?, + instructions: Decodable::decode(&mut d)?, + }) + } +} +