diff --git a/taiga_halo2/Cargo.toml b/taiga_halo2/Cargo.toml index 94fbc8e7..d1c87f29 100644 --- a/taiga_halo2/Cargo.toml +++ b/taiga_halo2/Cargo.toml @@ -18,6 +18,8 @@ dyn-clone = "1.0" reddsa = "0.5" vamp-ir = { git = "https://github.com/anoma/vamp-ir.git", rev = "778d5b53548cae5a2776c3bfd01fa9afa4543470"} bincode = "2.0.0-rc.1" +borsh = {version = "0.9", features = ["const-generics"]} +byteorder = "1" [dev-dependencies] criterion = "0.3" diff --git a/taiga_halo2/src/action.rs b/taiga_halo2/src/action.rs index 8286cf93..5d478632 100644 --- a/taiga_halo2/src/action.rs +++ b/taiga_halo2/src/action.rs @@ -2,13 +2,16 @@ use crate::{ circuit::action_circuit::ActionCircuit, constant::TAIGA_COMMITMENT_TREE_DEPTH, merkle_tree::MerklePath, - note::{InputNoteProvingInfo, Note, NoteCommitment, OutputNoteProvingInfo}, + note::{InputNoteProvingInfo, Note, OutputNoteProvingInfo}, nullifier::Nullifier, value_commitment::ValueCommitment, }; +use borsh::{BorshDeserialize, BorshSerialize}; +use ff::PrimeField; use halo2_proofs::arithmetic::Field; use pasta_curves::pallas; use rand::RngCore; +use std::io; /// The action result used in transaction. #[derive(Copy, Debug, Clone)] @@ -18,7 +21,7 @@ pub struct ActionInstance { /// The nullifier of input note. pub nf: Nullifier, /// The commitment of the output note. - pub cm: NoteCommitment, + pub cm_x: pallas::Base, /// net value commitment pub cv_net: ValueCommitment, // TODO: The EncryptedNote. @@ -38,13 +41,47 @@ impl ActionInstance { vec![ self.nf.inner(), self.anchor, - self.cm.get_x(), + self.cm_x, self.cv_net.get_x(), self.cv_net.get_y(), ] } } +impl BorshSerialize for ActionInstance { + fn serialize(&self, writer: &mut W) -> borsh::maybestd::io::Result<()> { + writer.write_all(&self.anchor.to_repr())?; + writer.write_all(&self.nf.to_bytes())?; + writer.write_all(&self.cm_x.to_repr())?; + writer.write_all(&self.cv_net.to_bytes())?; + Ok(()) + } +} + +impl BorshDeserialize for ActionInstance { + fn deserialize(buf: &mut &[u8]) -> borsh::maybestd::io::Result { + let anchor_bytes = <[u8; 32]>::deserialize(buf)?; + let anchor = Option::from(pallas::Base::from_repr(anchor_bytes)) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "anchor not in field"))?; + let nf_bytes = <[u8; 32]>::deserialize(buf)?; + let nf = Option::from(Nullifier::from_bytes(nf_bytes)) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "nf not in field"))?; + let cm_x_bytes = <[u8; 32]>::deserialize(buf)?; + let cm_x = Option::from(pallas::Base::from_repr(cm_x_bytes)) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "cm_x not in field"))?; + let cv_net_bytes = <[u8; 32]>::deserialize(buf)?; + let cv_net = Option::from(ValueCommitment::from_bytes(cv_net_bytes)) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "cv_net not in field"))?; + + Ok(ActionInstance { + anchor, + nf, + cm_x, + cv_net, + }) + } +} + impl ActionInfo { pub fn new( input: InputNoteProvingInfo, @@ -84,12 +121,12 @@ impl ActionInfo { "The nf of input note should be equal to the rho of output note" ); - let output_cm = self.output.note.commitment(); + let cm_x = self.output.note.commitment().get_x(); let cv_net = ValueCommitment::new(&self.input.note, &self.output.note, &self.rcv); let action = ActionInstance { nf, - cm: output_cm, + cm_x, anchor: self.input.root, cv_net, }; diff --git a/taiga_halo2/src/binding_signature.rs b/taiga_halo2/src/binding_signature.rs index 1357fd13..afd275ad 100644 --- a/taiga_halo2/src/binding_signature.rs +++ b/taiga_halo2/src/binding_signature.rs @@ -1,9 +1,11 @@ use crate::constant::NOTE_COMMITMENT_R_GENERATOR; +use borsh::{BorshDeserialize, BorshSerialize}; use pasta_curves::group::cofactor::CofactorCurveAffine; use pasta_curves::group::{ff::PrimeField, GroupEncoding}; use pasta_curves::pallas; use rand::{CryptoRng, RngCore}; use reddsa::{private, Error, SigType, Signature, SigningKey, VerificationKey}; +use std::io; #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum TaigaBinding {} @@ -35,6 +37,30 @@ pub struct BindingSigningKey(SigningKey); #[derive(Clone, Debug, PartialEq)] pub struct BindingVerificationKey(VerificationKey); +impl BindingSignature { + pub fn to_bytes(&self) -> [u8; 64] { + self.0.into() + } + + pub fn from_bytes(bytes: [u8; 64]) -> Self { + let sig = Signature::::from(bytes); + Self(sig) + } +} + +impl BorshSerialize for BindingSignature { + fn serialize(&self, writer: &mut W) -> borsh::maybestd::io::Result<()> { + writer.write_all(&self.to_bytes()) + } +} + +impl BorshDeserialize for BindingSignature { + fn deserialize(buf: &mut &[u8]) -> borsh::maybestd::io::Result { + let sig_bytes = <[u8; 64]>::deserialize(buf)?; + Ok(Self::from_bytes(sig_bytes)) + } +} + impl BindingSigningKey { pub fn sign(&self, rng: R, msg: &[u8]) -> BindingSignature { BindingSignature(self.0.sign(rng, msg)) @@ -43,6 +69,30 @@ impl BindingSigningKey { pub fn get_vk(&self) -> BindingVerificationKey { BindingVerificationKey(VerificationKey::from(&self.0)) } + + pub fn to_bytes(&self) -> [u8; 32] { + self.0.into() + } + + pub fn from_bytes(bytes: [u8; 32]) -> Result { + let key = SigningKey::::try_from(bytes)?; + Ok(Self(key)) + } +} + +impl BorshSerialize for BindingSigningKey { + fn serialize(&self, writer: &mut W) -> borsh::maybestd::io::Result<()> { + writer.write_all(&self.to_bytes()) + } +} + +impl BorshDeserialize for BindingSigningKey { + fn deserialize(buf: &mut &[u8]) -> borsh::maybestd::io::Result { + let key_bytes = <[u8; 32]>::deserialize(buf)?; + Self::from_bytes(key_bytes).map_err(|_| { + io::Error::new(io::ErrorKind::InvalidData, "BindingSigningKey not in field") + }) + } } impl From for BindingSigningKey { diff --git a/taiga_halo2/src/circuit/vp_circuit.rs b/taiga_halo2/src/circuit/vp_circuit.rs index d15c4dcc..0022a506 100644 --- a/taiga_halo2/src/circuit/vp_circuit.rs +++ b/taiga_halo2/src/circuit/vp_circuit.rs @@ -24,7 +24,10 @@ use crate::{ vp_vk::ValidityPredicateVerifyingKey, }; use bincode::error::DecodeError; +use borsh::{BorshDeserialize, BorshSerialize}; +use byteorder::{ReadBytesExt, WriteBytesExt}; use dyn_clone::{clone_trait_object, DynClone}; +use ff::PrimeField; use halo2_gadgets::{ecc::chip::EccChip, sinsemilla::chip::SinsemillaChip}; use halo2_proofs::{ circuit::{AssignedCell, Layouter, Value}, @@ -38,6 +41,7 @@ use pasta_curves::{pallas, vesta}; use rand::rngs::OsRng; use std::collections::HashMap; use std::fs::File; +use std::io; use std::path::PathBuf; use vamp_ir::halo2::synth::{make_constant, Halo2Module}; use vamp_ir::util::read_inputs_from_file; @@ -74,6 +78,48 @@ impl VPVerifyingInfo { } } +impl BorshSerialize for VPVerifyingInfo { + fn serialize(&self, writer: &mut W) -> borsh::maybestd::io::Result<()> { + // Write vk + self.vk.write(writer)?; + // Write proof + self.proof.serialize(writer)?; + // Write instance + assert!(self.instance.len() < 256); + writer.write_u8(self.instance.len() as u8)?; + for ele in self.instance.iter() { + writer.write_all(&ele.to_repr())?; + } + Ok(()) + } +} + +impl BorshDeserialize for VPVerifyingInfo { + fn deserialize(buf: &mut &[u8]) -> borsh::maybestd::io::Result { + // TODO: Read vk + use crate::circuit::vp_examples::TrivialValidityPredicateCircuit; + let params = SETUP_PARAMS_MAP.get(&VP_CIRCUIT_PARAMS_SIZE).unwrap(); + let vk = VerifyingKey::read::<_, TrivialValidityPredicateCircuit>(buf, params)?; + // Read proof + let proof = Proof::deserialize(buf)?; + // Read instance + let instance_len = buf.read_u8()?; + let instance: Vec<_> = (0..instance_len) + .map(|_| { + let bytes = <[u8; 32]>::deserialize(buf)?; + Option::from(pallas::Base::from_repr(bytes)).ok_or_else(|| { + io::Error::new(io::ErrorKind::InvalidData, "instance not in field") + }) + }) + .collect::>()?; + Ok(VPVerifyingInfo { + vk, + proof, + instance, + }) + } +} + pub trait ValidityPredicateConfig { fn configure_note(meta: &mut ConstraintSystem) -> NoteConfig { let instances = meta.instance_column(); diff --git a/taiga_halo2/src/executable.rs b/taiga_halo2/src/executable.rs index c8dadb0a..3f3ec0fb 100644 --- a/taiga_halo2/src/executable.rs +++ b/taiga_halo2/src/executable.rs @@ -1,14 +1,11 @@ -use crate::{ - error::TransactionError, note::NoteCommitment, nullifier::Nullifier, - value_commitment::ValueCommitment, -}; +use crate::{error::TransactionError, nullifier::Nullifier, value_commitment::ValueCommitment}; use pasta_curves::pallas; // Executable is an unified interface for partial transaction, which is the atomic executable uinit. pub trait Executable { fn execute(&self) -> Result<(), TransactionError>; fn get_nullifiers(&self) -> Vec; - fn get_output_cms(&self) -> Vec; + fn get_output_cms(&self) -> Vec; fn get_value_commitments(&self) -> Vec; fn get_anchors(&self) -> Vec; } diff --git a/taiga_halo2/src/note.rs b/taiga_halo2/src/note.rs index e3b92e3a..94a6225f 100644 --- a/taiga_halo2/src/note.rs +++ b/taiga_halo2/src/note.rs @@ -13,14 +13,17 @@ use crate::{ }; use bitvec::{array::BitArray, order::Lsb0}; use blake2b_simd::Params as Blake2bParams; +use borsh::{BorshDeserialize, BorshSerialize}; +use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use core::iter; -use ff::FromUniformBytes; +use ff::{FromUniformBytes, PrimeField}; use halo2_proofs::arithmetic::Field; use pasta_curves::{ group::{ff::PrimeFieldBits, Group, GroupEncoding}, pallas, }; use rand::{Rng, RngCore}; +use std::io; /// A commitment to a note. #[derive(Copy, Debug, Clone)] @@ -295,6 +298,100 @@ impl Note { } } +impl BorshSerialize for Note { + fn serialize(&self, writer: &mut W) -> borsh::maybestd::io::Result<()> { + // Write app_vk + writer.write_all(&self.note_type.app_vk.to_repr())?; + // Write app_data_static + writer.write_all(&self.note_type.app_data_static.to_repr())?; + // Write app_data_dynamic + writer.write_all(&self.app_data_dynamic.to_repr())?; + // Write note value + writer.write_u64::(self.value)?; + // Write nk_com + match self.nk_com { + NullifierKeyCom::Closed(nk_com) => { + writer.write_u8(1)?; + writer.write_all(&nk_com.to_repr()) + } + NullifierKeyCom::Open(nk) => { + writer.write_u8(2)?; + writer.write_all(&nk.to_bytes()) + } + }?; + // Write rho + writer.write_all(&self.rho.to_bytes())?; + // Write psi + writer.write_all(&self.psi.to_repr())?; + // Write rcm + writer.write_all(&self.rcm.to_repr())?; + // Write is_merkle_checked + writer.write_u8(if self.is_merkle_checked { 1 } else { 0 })?; + + Ok(()) + } +} + +impl BorshDeserialize for Note { + fn deserialize(buf: &mut &[u8]) -> borsh::maybestd::io::Result { + // Read app_vk + let app_vk_bytes = <[u8; 32]>::deserialize(buf)?; + let app_vk = Option::from(pallas::Base::from_repr(app_vk_bytes)) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "app_vk not in field"))?; + // Read app_data_static + let app_data_static_bytes = <[u8; 32]>::deserialize(buf)?; + let app_data_static = Option::from(pallas::Base::from_repr(app_data_static_bytes)) + .ok_or_else(|| { + io::Error::new(io::ErrorKind::InvalidData, "app_data_static not in field") + })?; + // Read app_data_dynamic + let app_data_dynamic_bytes = <[u8; 32]>::deserialize(buf)?; + let app_data_dynamic = Option::from(pallas::Base::from_repr(app_data_dynamic_bytes)) + .ok_or_else(|| { + io::Error::new(io::ErrorKind::InvalidData, "app_data_dynamic not in field") + })?; + // Read note value + let value = buf.read_u64::()?; + // Read nk_com + let nk_com_type = buf.read_u8()?; + let nk_com_bytes = <[u8; 32]>::deserialize(buf)?; + let nk_com = Option::from(pallas::Base::from_repr(nk_com_bytes)) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "nk_com not in field"))?; + let nk_com = if nk_com_type == 0x01 { + NullifierKeyCom::from_closed(nk_com) + } else { + NullifierKeyCom::from_open(NullifierDerivingKey::new(nk_com)) + }; + // Read rho + let rho_bytes = <[u8; 32]>::deserialize(buf)?; + let rho = Option::from(Nullifier::from_bytes(rho_bytes)) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "rho not in field"))?; + // Read psi + let psi_bytes = <[u8; 32]>::deserialize(buf)?; + let psi = Option::from(pallas::Base::from_repr(psi_bytes)) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "psi not in field"))?; + // Read rcm + let rcm_bytes = <[u8; 32]>::deserialize(buf)?; + let rcm = Option::from(pallas::Base::from_repr(rcm_bytes)) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "rcm not in field"))?; + // Read is_merkle_checked + let is_merkle_checked_byte = buf.read_u8()?; + let is_merkle_checked = is_merkle_checked_byte == 0x01; + // Construct note + Ok(Note::from_full( + app_vk, + app_data_static, + app_data_dynamic, + value, + nk_com, + rho, + is_merkle_checked, + psi, + rcm, + )) + } +} + impl ValueBase { pub fn new(vk: pallas::Base, data: pallas::Base) -> Self { Self { @@ -438,3 +535,27 @@ impl OutputNoteProvingInfo { OutputNoteProvingInfo::new(padding_note, trivail_vp, vec![]) } } + +#[test] +fn note_serialization_test() { + use rand::rngs::OsRng; + let mut rng = OsRng; + + let input_note = Note::dummy(&mut rng); + { + // BorshSerialize + let borsh = input_note.try_to_vec().unwrap(); + // BorshDeserialize + let de_note: Note = BorshDeserialize::deserialize(&mut borsh.as_ref()).unwrap(); + assert_eq!(input_note, de_note); + } + + let output_note = Note::dummy_output(&mut rng, input_note.rho); + { + // BorshSerialize + let borsh = output_note.try_to_vec().unwrap(); + // BorshDeserialize + let de_note: Note = BorshDeserialize::deserialize(&mut borsh.as_ref()).unwrap(); + assert_eq!(output_note, de_note); + } +} diff --git a/taiga_halo2/src/nullifier.rs b/taiga_halo2/src/nullifier.rs index 2ea9b9aa..20a4700a 100644 --- a/taiga_halo2/src/nullifier.rs +++ b/taiga_halo2/src/nullifier.rs @@ -8,11 +8,13 @@ use pasta_curves::group::cofactor::CofactorCurveAffine; use pasta_curves::group::ff::PrimeField; use pasta_curves::pallas; use rand::RngCore; +use subtle::CtOption; /// The unique nullifier. #[derive(Copy, Debug, Clone, PartialEq, Eq)] pub struct Nullifier(pallas::Base); +// TODO: remove it #[derive(Copy, Debug, Clone, PartialEq, Eq)] pub struct NullifierDerivingKey(pallas::Base); @@ -50,6 +52,10 @@ impl Nullifier { pub fn to_bytes(&self) -> [u8; 32] { self.0.to_repr() } + + pub fn from_bytes(bytes: [u8; 32]) -> CtOption { + pallas::Base::from_repr(bytes).map(Nullifier) + } } impl Default for Nullifier { @@ -74,6 +80,14 @@ impl NullifierDerivingKey { pub fn inner(&self) -> pallas::Base { self.0 } + + pub fn to_bytes(&self) -> [u8; 32] { + self.0.to_repr() + } + + pub fn from_bytes(bytes: [u8; 32]) -> CtOption { + pallas::Base::from_repr(bytes).map(NullifierDerivingKey) + } } impl Default for NullifierDerivingKey { diff --git a/taiga_halo2/src/proof.rs b/taiga_halo2/src/proof.rs index 257955ec..c6874efe 100644 --- a/taiga_halo2/src/proof.rs +++ b/taiga_halo2/src/proof.rs @@ -1,3 +1,4 @@ +use borsh::{BorshDeserialize, BorshSerialize}; use halo2_proofs::{ plonk::{self, Circuit, ProvingKey, SingleVerifier, VerifyingKey}, poly::commitment::Params, @@ -6,7 +7,7 @@ use halo2_proofs::{ use pasta_curves::{pallas, vesta}; use rand::RngCore; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)] pub struct Proof(Vec); impl Proof { diff --git a/taiga_halo2/src/shielded_ptx.rs b/taiga_halo2/src/shielded_ptx.rs index e3ca7dab..062ad640 100644 --- a/taiga_halo2/src/shielded_ptx.rs +++ b/taiga_halo2/src/shielded_ptx.rs @@ -6,10 +6,11 @@ use crate::constant::{ }; use crate::error::TransactionError; use crate::executable::Executable; -use crate::note::{InputNoteProvingInfo, NoteCommitment, OutputNoteProvingInfo}; +use crate::note::{InputNoteProvingInfo, OutputNoteProvingInfo}; use crate::nullifier::Nullifier; use crate::proof::Proof; use crate::value_commitment::ValueCommitment; +use borsh::{BorshDeserialize, BorshSerialize}; use halo2_proofs::plonk::Error; use pasta_curves::pallas; use rand::RngCore; @@ -21,13 +22,13 @@ pub struct ShieldedPartialTransaction { outputs: [NoteVPVerifyingInfoSet; NUM_NOTE], } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)] pub struct ActionVerifyingInfo { action_proof: Proof, action_instance: ActionInstance, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)] pub struct NoteVPVerifyingInfoSet { app_vp_verifying_info: VPVerifyingInfo, app_dynamic_vp_verifying_info: Vec, @@ -138,8 +139,8 @@ impl ShieldedPartialTransaction { for vp_info in self.outputs.iter() { for cms in vp_info.get_note_commitments().iter() { // Check the vp actually uses the output notes from action circuits. - if !((action_cms[0].get_x() == cms[0] && action_cms[1].get_x() == cms[1]) - || (action_cms[0].get_x() == cms[1] && action_cms[1].get_x() == cms[0])) + if !((action_cms[0] == cms[0] && action_cms[1] == cms[1]) + || (action_cms[0] == cms[1] && action_cms[1] == cms[0])) { return Err(TransactionError::InconsistentOutputNoteCommitment); } @@ -156,7 +157,7 @@ impl ShieldedPartialTransaction { } // Check the owned_note_id that vp uses is consistent with the cm from the action circuit - if owned_note_id != action_cm.get_x() { + if owned_note_id != *action_cm { return Err(TransactionError::InconsistentOwnedNotePubID); } } @@ -179,10 +180,10 @@ impl Executable for ShieldedPartialTransaction { .collect() } - fn get_output_cms(&self) -> Vec { + fn get_output_cms(&self) -> Vec { self.actions .iter() - .map(|action| action.action_instance.cm) + .map(|action| action.action_instance.cm_x) .collect() } @@ -201,6 +202,42 @@ impl Executable for ShieldedPartialTransaction { } } +impl BorshSerialize for ShieldedPartialTransaction { + fn serialize(&self, writer: &mut W) -> borsh::maybestd::io::Result<()> { + for action in self.actions.iter() { + action.serialize(writer)?; + } + + for input in self.inputs.iter() { + input.serialize(writer)?; + } + + for output in self.outputs.iter() { + output.serialize(writer)?; + } + + Ok(()) + } +} + +impl BorshDeserialize for ShieldedPartialTransaction { + fn deserialize(buf: &mut &[u8]) -> borsh::maybestd::io::Result { + let actions: Vec<_> = (0..NUM_NOTE) + .map(|_| ActionVerifyingInfo::deserialize(buf)) + .collect::>()?; + let inputs: Vec<_> = (0..NUM_NOTE) + .map(|_| NoteVPVerifyingInfoSet::deserialize(buf)) + .collect::>()?; + let outputs: Vec<_> = (0..NUM_NOTE) + .map(|_| NoteVPVerifyingInfoSet::deserialize(buf)) + .collect::>()?; + Ok(ShieldedPartialTransaction { + actions: actions.try_into().unwrap(), + inputs: inputs.try_into().unwrap(), + outputs: outputs.try_into().unwrap(), + }) + } +} impl ActionVerifyingInfo { pub fn create(action_info: ActionInfo, mut rng: R) -> Result { let (action_instance, circuit) = action_info.build(); diff --git a/taiga_halo2/src/transaction.rs b/taiga_halo2/src/transaction.rs index fdd41fe0..e0392f68 100644 --- a/taiga_halo2/src/transaction.rs +++ b/taiga_halo2/src/transaction.rs @@ -2,19 +2,19 @@ use crate::binding_signature::{BindingSignature, BindingSigningKey, BindingVerif use crate::constant::TRANSACTION_BINDING_HASH_PERSONALIZATION; use crate::error::TransactionError; use crate::executable::Executable; -use crate::note::NoteCommitment; use crate::nullifier::Nullifier; use crate::shielded_ptx::ShieldedPartialTransaction; use crate::transparent_ptx::{OutputResource, TransparentPartialTransaction}; use crate::value_commitment::ValueCommitment; use blake2b_simd::Params as Blake2bParams; +use borsh::{BorshDeserialize, BorshSerialize}; use pasta_curves::{ group::{ff::PrimeField, Group}, pallas, }; use rand::{CryptoRng, RngCore}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, BorshDeserialize, BorshSerialize)] pub struct Transaction { // TODO: Other parameters to be added. shielded_ptx_bundle: Option, @@ -23,25 +23,25 @@ pub struct Transaction { signature: InProgressBindingSignature, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, BorshDeserialize, BorshSerialize)] pub enum InProgressBindingSignature { Authorized(BindingSignature), Unauthorized(BindingSigningKey), } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, BorshDeserialize, BorshSerialize)] pub struct ShieldedPartialTxBundle { partial_txs: Vec, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct ShieldedResult { anchors: Vec, nullifiers: Vec, - output_cms: Vec, + output_cms: Vec, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, BorshDeserialize, BorshSerialize)] pub struct TransparentPartialTxBundle { partial_txs: Vec, } @@ -167,8 +167,8 @@ impl Transaction { bundle.get_nullifiers().iter().for_each(|nf| { h.update(&nf.to_bytes()); }); - bundle.get_output_cms().iter().for_each(|cm| { - h.update(&cm.to_bytes()); + bundle.get_output_cms().iter().for_each(|cm_x| { + h.update(&cm_x.to_repr()); }); bundle.get_value_commitments().iter().for_each(|vc| { h.update(&vc.to_bytes()); @@ -184,7 +184,7 @@ impl Transaction { h.update(&nf.to_bytes()); }); bundle.get_output_cms().iter().for_each(|cm| { - h.update(&cm.to_bytes()); + h.update(&cm.to_repr()); }); bundle.get_value_commitments().iter().for_each(|vc| { h.update(&vc.to_bytes()); @@ -241,7 +241,7 @@ impl ShieldedPartialTxBundle { .collect() } - pub fn get_output_cms(&self) -> Vec { + pub fn get_output_cms(&self) -> Vec { self.partial_txs .iter() .flat_map(|ptx| ptx.get_output_cms()) @@ -302,7 +302,7 @@ impl TransparentPartialTxBundle { .collect() } - pub fn get_output_cms(&self) -> Vec { + pub fn get_output_cms(&self) -> Vec { self.partial_txs .iter() .flat_map(|ptx| ptx.get_output_cms()) @@ -353,5 +353,10 @@ fn test_halo2_transaction() { transparent_ptx_bundle, r_vec, ); - tx.execute().unwrap(); + let (shielded_ret, _) = tx.execute().unwrap(); + + let borsh = tx.try_to_vec().unwrap(); + let de_tx: Transaction = BorshDeserialize::deserialize(&mut borsh.as_ref()).unwrap(); + let (de_shielded_ret, _) = de_tx.execute().unwrap(); + assert_eq!(shielded_ret, de_shielded_ret); } diff --git a/taiga_halo2/src/transparent_ptx.rs b/taiga_halo2/src/transparent_ptx.rs index bca6f71f..2bac5a3a 100644 --- a/taiga_halo2/src/transparent_ptx.rs +++ b/taiga_halo2/src/transparent_ptx.rs @@ -1,10 +1,11 @@ use crate::{ - error::TransactionError, executable::Executable, note::NoteCommitment, nullifier::Nullifier, + error::TransactionError, executable::Executable, nullifier::Nullifier, value_commitment::ValueCommitment, }; +use borsh::{BorshDeserialize, BorshSerialize}; use pasta_curves::pallas; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, BorshDeserialize, BorshSerialize)] pub struct TransparentPartialTransaction { pub inputs: Vec, pub outputs: Vec, @@ -20,7 +21,7 @@ impl Executable for TransparentPartialTransaction { unimplemented!() } - fn get_output_cms(&self) -> Vec { + fn get_output_cms(&self) -> Vec { unimplemented!() } @@ -33,7 +34,7 @@ impl Executable for TransparentPartialTransaction { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, BorshDeserialize, BorshSerialize)] pub struct InputResource { pub resource_logic: ResourceLogic, pub prefix: ContentHash, @@ -42,21 +43,21 @@ pub struct InputResource { pub resource_data_dynamic: ResourceDataDynamic, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, BorshDeserialize, BorshSerialize)] pub struct OutputResource { pub resource_logic: ResourceLogic, pub resource_data_static: ResourceDataStatic, pub resource_data_dynamic: ResourceDataDynamic, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, BorshDeserialize, BorshSerialize)] pub struct ResourceLogic {} -#[derive(Debug, Clone)] +#[derive(Debug, Clone, BorshDeserialize, BorshSerialize)] pub struct ContentHash {} -#[derive(Debug, Clone)] +#[derive(Debug, Clone, BorshDeserialize, BorshSerialize)] pub struct ResourceDataStatic {} -#[derive(Debug, Clone)] +#[derive(Debug, Clone, BorshDeserialize, BorshSerialize)] pub struct ResourceDataDynamic {} diff --git a/taiga_halo2/src/value_commitment.rs b/taiga_halo2/src/value_commitment.rs index 3c965ed1..e5d36e41 100644 --- a/taiga_halo2/src/value_commitment.rs +++ b/taiga_halo2/src/value_commitment.rs @@ -4,6 +4,7 @@ use halo2_proofs::arithmetic::CurveAffine; use pasta_curves::group::cofactor::CofactorCurveAffine; use pasta_curves::group::{Curve, Group, GroupEncoding}; use pasta_curves::pallas; +use subtle::CtOption; #[derive(Copy, Clone, Debug)] pub struct ValueCommitment(pallas::Point); @@ -42,4 +43,8 @@ impl ValueCommitment { pub fn to_bytes(&self) -> [u8; 32] { self.0.to_bytes() } + + pub fn from_bytes(bytes: [u8; 32]) -> CtOption { + pallas::Point::from_bytes(&bytes).map(ValueCommitment) + } }