Skip to content

Commit

Permalink
(De)serialization of Transaction, PartialTransaction and Note (#183)
Browse files Browse the repository at this point in the history
* add note serialization

* add VPVerifyingInfo serialization

* add ActionVerifyingInfo  serialization

* add tx and ptx serialization

* add a tx (de)serialization test
  • Loading branch information
XuyangSong authored Jul 11, 2023
1 parent ffc416d commit cce706e
Show file tree
Hide file tree
Showing 12 changed files with 358 additions and 42 deletions.
2 changes: 2 additions & 0 deletions taiga_halo2/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
47 changes: 42 additions & 5 deletions taiga_halo2/src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -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.
Expand All @@ -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<W: std::io::Write>(&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<Self> {
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<R: RngCore>(
input: InputNoteProvingInfo,
Expand Down Expand Up @@ -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,
};
Expand Down
50 changes: 50 additions & 0 deletions taiga_halo2/src/binding_signature.rs
Original file line number Diff line number Diff line change
@@ -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 {}
Expand Down Expand Up @@ -35,6 +37,30 @@ pub struct BindingSigningKey(SigningKey<TaigaBinding>);
#[derive(Clone, Debug, PartialEq)]
pub struct BindingVerificationKey(VerificationKey<TaigaBinding>);

impl BindingSignature {
pub fn to_bytes(&self) -> [u8; 64] {
self.0.into()
}

pub fn from_bytes(bytes: [u8; 64]) -> Self {
let sig = Signature::<TaigaBinding>::from(bytes);
Self(sig)
}
}

impl BorshSerialize for BindingSignature {
fn serialize<W: std::io::Write>(&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<Self> {
let sig_bytes = <[u8; 64]>::deserialize(buf)?;
Ok(Self::from_bytes(sig_bytes))
}
}

impl BindingSigningKey {
pub fn sign<R: RngCore + CryptoRng>(&self, rng: R, msg: &[u8]) -> BindingSignature {
BindingSignature(self.0.sign(rng, msg))
Expand All @@ -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<Self, Error> {
let key = SigningKey::<TaigaBinding>::try_from(bytes)?;
Ok(Self(key))
}
}

impl BorshSerialize for BindingSigningKey {
fn serialize<W: std::io::Write>(&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<Self> {
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<pallas::Scalar> for BindingSigningKey {
Expand Down
46 changes: 46 additions & 0 deletions taiga_halo2/src/circuit/vp_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand All @@ -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;
Expand Down Expand Up @@ -74,6 +78,48 @@ impl VPVerifyingInfo {
}
}

impl BorshSerialize for VPVerifyingInfo {
fn serialize<W: std::io::Write>(&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<Self> {
// 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::<Result<_, _>>()?;
Ok(VPVerifyingInfo {
vk,
proof,
instance,
})
}
}

pub trait ValidityPredicateConfig {
fn configure_note(meta: &mut ConstraintSystem<pallas::Base>) -> NoteConfig {
let instances = meta.instance_column();
Expand Down
7 changes: 2 additions & 5 deletions taiga_halo2/src/executable.rs
Original file line number Diff line number Diff line change
@@ -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<Nullifier>;
fn get_output_cms(&self) -> Vec<NoteCommitment>;
fn get_output_cms(&self) -> Vec<pallas::Base>;
fn get_value_commitments(&self) -> Vec<ValueCommitment>;
fn get_anchors(&self) -> Vec<pallas::Base>;
}
Loading

0 comments on commit cce706e

Please sign in to comment.