diff --git a/taiga_halo2/examples/tx_examples/cascaded_partial_transactions.rs b/taiga_halo2/examples/tx_examples/cascaded_partial_transactions.rs index f8ef030e..617ae0ce 100644 --- a/taiga_halo2/examples/tx_examples/cascaded_partial_transactions.rs +++ b/taiga_halo2/examples/tx_examples/cascaded_partial_transactions.rs @@ -176,7 +176,7 @@ pub fn create_transaction(mut rng: R) -> Transaction { // Create the final transaction let shielded_tx_bundle = ShieldedPartialTxBundle::new(vec![ptx_1, ptx_2]); let transparent_ptx_bundle = TransparentPartialTxBundle::default(); - Transaction::build(&mut rng, shielded_tx_bundle, transparent_ptx_bundle) + Transaction::build(&mut rng, shielded_tx_bundle, transparent_ptx_bundle).unwrap() } #[test] diff --git a/taiga_halo2/examples/tx_examples/partial_fulfillment_token_swap.rs b/taiga_halo2/examples/tx_examples/partial_fulfillment_token_swap.rs index 0bfbc74e..9f64b44b 100644 --- a/taiga_halo2/examples/tx_examples/partial_fulfillment_token_swap.rs +++ b/taiga_halo2/examples/tx_examples/partial_fulfillment_token_swap.rs @@ -200,7 +200,7 @@ pub fn create_token_swap_transaction(mut rng: R) -> Tran // Solver creates the final transaction let shielded_tx_bundle = ShieldedPartialTxBundle::new(vec![alice_ptx, bob_ptx, solver_ptx]); let transparent_ptx_bundle = TransparentPartialTxBundle::default(); - Transaction::build(&mut rng, shielded_tx_bundle, transparent_ptx_bundle) + Transaction::build(&mut rng, shielded_tx_bundle, transparent_ptx_bundle).unwrap() } #[test] diff --git a/taiga_halo2/examples/tx_examples/token_swap_with_intent.rs b/taiga_halo2/examples/tx_examples/token_swap_with_intent.rs index 507cb92f..6d93c251 100644 --- a/taiga_halo2/examples/tx_examples/token_swap_with_intent.rs +++ b/taiga_halo2/examples/tx_examples/token_swap_with_intent.rs @@ -277,7 +277,7 @@ pub fn create_token_swap_intent_transaction(mut rng: R) // Solver creates the final transaction let shielded_tx_bundle = ShieldedPartialTxBundle::new(vec![alice_ptx, bob_ptx, solver_ptx]); let transparent_ptx_bundle = TransparentPartialTxBundle::default(); - Transaction::build(&mut rng, shielded_tx_bundle, transparent_ptx_bundle) + Transaction::build(&mut rng, shielded_tx_bundle, transparent_ptx_bundle).unwrap() } #[test] diff --git a/taiga_halo2/examples/tx_examples/token_swap_without_intent.rs b/taiga_halo2/examples/tx_examples/token_swap_without_intent.rs index c23a1819..b1d17e16 100644 --- a/taiga_halo2/examples/tx_examples/token_swap_without_intent.rs +++ b/taiga_halo2/examples/tx_examples/token_swap_without_intent.rs @@ -69,7 +69,7 @@ pub fn create_token_swap_transaction(mut rng: R) -> Tran // Solver creates the final transaction let shielded_tx_bundle = ShieldedPartialTxBundle::new(vec![alice_ptx, bob_ptx, carol_ptx]); let transparent_ptx_bundle = TransparentPartialTxBundle::default(); - Transaction::build(&mut rng, shielded_tx_bundle, transparent_ptx_bundle) + Transaction::build(&mut rng, shielded_tx_bundle, transparent_ptx_bundle).unwrap() } #[test] diff --git a/taiga_halo2/src/error.rs b/taiga_halo2/src/error.rs index 8c18fa03..e881e1fe 100644 --- a/taiga_halo2/src/error.rs +++ b/taiga_halo2/src/error.rs @@ -22,6 +22,8 @@ pub enum TransactionError { MissingTransparentResourceNullifierKey, /// Transparent resource merkle path is missing MissingTransparentResourceMerklePath, + /// Shielded partial Tx binding signature r is missing + MissingPartialTxBindingSignatureR, } impl Display for TransactionError { @@ -47,6 +49,9 @@ impl Display for TransactionError { MissingTransparentResourceMerklePath => { f.write_str("Transparent resource merkle path is missing") } + MissingPartialTxBindingSignatureR => { + f.write_str("Shielded partial Tx binding signature r is missing") + } } } } diff --git a/taiga_halo2/src/shielded_ptx.rs b/taiga_halo2/src/shielded_ptx.rs index 1027b01d..1316173f 100644 --- a/taiga_halo2/src/shielded_ptx.rs +++ b/taiga_halo2/src/shielded_ptx.rs @@ -34,7 +34,7 @@ pub struct ShieldedPartialTransaction { actions: [ActionVerifyingInfo; NUM_NOTE], inputs: [NoteVPVerifyingInfoSet; NUM_NOTE], outputs: [NoteVPVerifyingInfoSet; NUM_NOTE], - binding_sig_r: pallas::Scalar, + binding_sig_r: Option, hints: Vec, } @@ -68,7 +68,7 @@ struct ShieldedPartialTransactionProxy { actions: Vec, inputs: Vec, outputs: Vec, - binding_sig_r: pallas::Scalar, + binding_sig_r: Option, hints: Vec, } @@ -102,7 +102,7 @@ impl ShieldedPartialTransaction { actions: actions.try_into().unwrap(), inputs: inputs.try_into().unwrap(), outputs: outputs.try_into().unwrap(), - binding_sig_r: rcv_sum, + binding_sig_r: Some(rcv_sum), hints, } } @@ -146,7 +146,7 @@ impl ShieldedPartialTransaction { actions: actions.try_into().unwrap(), inputs: inputs.try_into().unwrap(), outputs: outputs.try_into().unwrap(), - binding_sig_r: rcv_sum, + binding_sig_r: Some(rcv_sum), hints, } } @@ -245,13 +245,18 @@ impl ShieldedPartialTransaction { } } - pub fn get_binding_sig_r(&self) -> pallas::Scalar { + pub fn get_binding_sig_r(&self) -> Option { self.binding_sig_r } pub fn get_hints(&self) -> Vec { self.hints.clone() } + + pub fn clean_private_info(&mut self) { + self.binding_sig_r = None; + self.hints = vec![]; + } } impl ShieldedPartialTransactionProxy { @@ -309,6 +314,7 @@ impl Executable for ShieldedPartialTransaction { #[cfg(feature = "borsh")] impl BorshSerialize for ShieldedPartialTransaction { fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + use byteorder::WriteBytesExt; for action in self.actions.iter() { action.serialize(writer)?; } @@ -321,7 +327,16 @@ impl BorshSerialize for ShieldedPartialTransaction { output.serialize(writer)?; } - writer.write_all(&self.binding_sig_r.to_repr())?; + // Write binding_sig_r + match self.binding_sig_r { + None => { + writer.write_u8(0)?; + } + Some(r) => { + writer.write_u8(1)?; + writer.write_all(&r.to_repr())?; + } + }; self.hints.serialize(writer)?; @@ -332,6 +347,7 @@ impl BorshSerialize for ShieldedPartialTransaction { #[cfg(feature = "borsh")] impl BorshDeserialize for ShieldedPartialTransaction { fn deserialize_reader(reader: &mut R) -> std::io::Result { + use byteorder::ReadBytesExt; let actions: Vec<_> = (0..NUM_NOTE) .map(|_| ActionVerifyingInfo::deserialize_reader(reader)) .collect::>()?; @@ -341,14 +357,21 @@ impl BorshDeserialize for ShieldedPartialTransaction { let outputs: Vec<_> = (0..NUM_NOTE) .map(|_| NoteVPVerifyingInfoSet::deserialize_reader(reader)) .collect::>()?; - let binding_sig_r_bytes = <[u8; 32]>::deserialize_reader(reader)?; - let binding_sig_r = Option::from(pallas::Scalar::from_repr(binding_sig_r_bytes)) - .ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::InvalidData, - "binding_sig_r not in field", - ) - })?; + let binding_sig_r_type = reader.read_u8()?; + let binding_sig_r = if binding_sig_r_type == 0 { + None + } else { + let binding_sig_r_bytes = <[u8; 32]>::deserialize_reader(reader)?; + let r = + Option::from(pallas::Scalar::from_repr(binding_sig_r_bytes)).ok_or_else(|| { + std::io::Error::new( + std::io::ErrorKind::InvalidData, + "binding_sig_r not in field", + ) + })?; + Some(r) + }; + let hints = Vec::::deserialize_reader(reader)?; Ok(ShieldedPartialTransaction { actions: actions.try_into().unwrap(), diff --git a/taiga_halo2/src/taiga_api.rs b/taiga_halo2/src/taiga_api.rs index bb27fe62..59d52e45 100644 --- a/taiga_halo2/src/taiga_api.rs +++ b/taiga_halo2/src/taiga_api.rs @@ -1,9 +1,9 @@ #[cfg(feature = "borsh")] use crate::{ - action::ActionInfo, circuit::vp_bytecode::ApplicationByteCode, error::TransactionError, - transaction::TransactionResult, + action::ActionInfo, circuit::vp_bytecode::ApplicationByteCode, transaction::TransactionResult, }; use crate::{ + error::TransactionError, note::{Note, RandomSeed}, nullifier::{Nullifier, NullifierKeyContainer}, shielded_ptx::ShieldedPartialTransaction, @@ -133,7 +133,7 @@ pub fn note_deserialize(bytes: Vec) -> std::io::Result { /// | output2 static vp proof | VPVerifyingInfo | 158216 | /// | output2 dynamic vp num(by borsh) | u32 | 4 | /// | output2 dynamic vp proofs | VPVerifyingInfo | 158216 * num | -/// | binding_sig_r | pallas::Scalar | 32 | +/// | binding_sig_r | Option| 1 or (1 + 32) | /// | hints | Vec | - | /// /// Note: Ultimately, vp proofs won't go to the ptx. It's verifier proofs instead. @@ -191,7 +191,7 @@ pub fn create_transaction( shielded_ptxs: Vec, // TODO: add transparent_ptxs // transparent_ptxs: Vec, -) -> Transaction { +) -> Result { let rng = OsRng; let shielded_ptx_bundle = ShieldedPartialTxBundle::new(shielded_ptxs); // empty transparent_ptx_bundle diff --git a/taiga_halo2/src/transaction.rs b/taiga_halo2/src/transaction.rs index 4e5d0c30..7f76c42e 100644 --- a/taiga_halo2/src/transaction.rs +++ b/taiga_halo2/src/transaction.rs @@ -59,20 +59,21 @@ impl Transaction { // Generate the transaction pub fn build( rng: R, - shielded_ptx_bundle: ShieldedPartialTxBundle, + mut shielded_ptx_bundle: ShieldedPartialTxBundle, transparent_ptx_bundle: TransparentPartialTxBundle, - ) -> Self { + ) -> Result { assert!(!(shielded_ptx_bundle.is_empty() && transparent_ptx_bundle.is_empty())); - let shielded_sk = shielded_ptx_bundle.get_bindig_sig_r(); + let shielded_sk = shielded_ptx_bundle.get_binding_sig_r()?; let binding_sk = BindingSigningKey::from(shielded_sk); let sig_hash = Self::digest(&shielded_ptx_bundle, &transparent_ptx_bundle); let signature = binding_sk.sign(rng, &sig_hash); + shielded_ptx_bundle.clean_private_info(); - Self { + Ok(Self { shielded_ptx_bundle, transparent_ptx_bundle, signature, - } + }) } #[allow(clippy::type_complexity)] @@ -216,10 +217,21 @@ impl ShieldedPartialTxBundle { self.0.is_empty() } - pub fn get_bindig_sig_r(&self) -> pallas::Scalar { - self.0.iter().fold(pallas::Scalar::zero(), |acc, ptx| { - acc + ptx.get_binding_sig_r() - }) + pub fn get_binding_sig_r(&self) -> Result { + let mut sum = pallas::Scalar::zero(); + for ptx in self.0.iter() { + if let Some(r) = ptx.get_binding_sig_r() { + sum += r; + } else { + return Err(TransactionError::MissingPartialTxBindingSignatureR); + } + } + + Ok(sum) + } + + pub fn clean_private_info(&mut self) { + self.0.iter_mut().for_each(|ptx| ptx.clean_private_info()); } pub fn new(partial_txs: Vec) -> Self { @@ -342,7 +354,7 @@ pub mod testing { let shielded_ptx_bundle = create_shielded_ptx_bundle(1); let transparent_ptx_bundle = create_transparent_ptx_bundle(1); - let tx = Transaction::build(rng, shielded_ptx_bundle, transparent_ptx_bundle); + let tx = Transaction::build(rng, shielded_ptx_bundle, transparent_ptx_bundle).unwrap(); let _ret = tx.execute().unwrap(); #[cfg(feature = "borsh")]