Skip to content

Commit

Permalink
Circuit struct serialization (#5565)
Browse files Browse the repository at this point in the history
* move proof generation key extension to ironfish-zkp

* circuit struct serialization

* fix review comments, cleanup
  • Loading branch information
jowparks authored Oct 24, 2024
1 parent 4fdd27c commit 4229f77
Show file tree
Hide file tree
Showing 6 changed files with 591 additions and 4 deletions.
81 changes: 81 additions & 0 deletions ironfish-zkp/src/circuits/mint_asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use bellperson::{
Circuit,
};
use ff::PrimeField;
use std::io::{Read, Write};
use zcash_proofs::{
circuit::ecc,
constants::{PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR},
Expand All @@ -12,6 +13,9 @@ use crate::{
constants::{proof::PUBLIC_KEY_GENERATOR, CRH_IVK_PERSONALIZATION},
ProofGenerationKey,
};
use byteorder::{ReadBytesExt, WriteBytesExt};

use super::util::FromBytes;

pub struct MintAsset {
/// Key required to construct proofs for a particular spending key
Expand All @@ -22,6 +26,39 @@ pub struct MintAsset {
pub public_key_randomness: Option<jubjub::Fr>,
}

impl MintAsset {
pub fn write<W: Write>(&self, mut writer: W) -> std::io::Result<()> {
if let Some(ref proof_generation_key) = self.proof_generation_key {
writer.write_u8(1)?;
writer.write_all(proof_generation_key.to_bytes().as_ref())?;
} else {
writer.write_u8(0)?;
}
if let Some(ref public_key_randomness) = self.public_key_randomness {
writer.write_u8(1)?;
writer.write_all(public_key_randomness.to_bytes().as_ref())?;
} else {
writer.write_u8(0)?;
}
Ok(())
}

pub fn read<R: Read>(mut reader: R) -> std::io::Result<MintAsset> {
let mut proof_generation_key = None;
if reader.read_u8()? == 1 {
proof_generation_key = Some(ProofGenerationKey::read(&mut reader)?);
}
let mut public_key_randomness = None;
if reader.read_u8()? == 1 {
public_key_randomness = Some(jubjub::Fr::read(&mut reader)?);
}
Ok(MintAsset {
proof_generation_key,
public_key_randomness,
})
}
}

impl Circuit<blstrs::Scalar> for MintAsset {
fn synthesize<CS: bellperson::ConstraintSystem<blstrs::Scalar>>(
self,
Expand Down Expand Up @@ -181,4 +218,48 @@ mod test {
// Sanity check
assert!(cs.verify(&public_inputs));
}

#[test]
fn test_mint_asset_read_write() {
// Seed a fixed RNG for determinism in the test
let mut rng = StdRng::seed_from_u64(0);

// Create a MintAsset instance with random data
let proof_generation_key = ProofGenerationKey::new(
jubjub::SubgroupPoint::random(&mut rng),
jubjub::Fr::random(&mut rng),
);
let public_key_randomness = jubjub::Fr::random(&mut rng);

let mint_asset = MintAsset {
proof_generation_key: Some(proof_generation_key.clone()),
public_key_randomness: Some(public_key_randomness),
};

let mut buffer = vec![];
mint_asset.write(&mut buffer).unwrap();

let deserialized_mint_asset = MintAsset::read(&buffer[..]).unwrap();

assert_eq!(
mint_asset.proof_generation_key.clone().unwrap().ak,
deserialized_mint_asset
.proof_generation_key
.clone()
.unwrap()
.ak
);
assert_eq!(
mint_asset.proof_generation_key.clone().unwrap().nsk,
deserialized_mint_asset
.proof_generation_key
.clone()
.unwrap()
.nsk
);
assert_eq!(
mint_asset.public_key_randomness.unwrap(),
deserialized_mint_asset.public_key_randomness.unwrap()
);
}
}
185 changes: 183 additions & 2 deletions ironfish-zkp/src/circuits/output.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use std::io::{Read, Write};

use byteorder::{ReadBytesExt, WriteBytesExt};
use ff::PrimeField;

use bellperson::{gadgets::blake2s, Circuit, ConstraintSystem, SynthesisError};

use group::Curve;
use group::{Curve, GroupEncoding};
use jubjub::SubgroupPoint;

use zcash_proofs::{
Expand All @@ -20,7 +23,7 @@ use crate::{
ProofGenerationKey,
};

use super::util::expose_value_commitment;
use super::util::{expose_value_commitment, FromBytes};
use bellperson::gadgets::boolean;

/// This is a circuit instance inspired from ZCash's `Output` circuit in the Sapling protocol
Expand Down Expand Up @@ -49,6 +52,87 @@ pub struct Output {
pub ar: Option<jubjub::Fr>,
}

impl Output {
pub fn write<W: Write>(&self, mut writer: W) -> std::io::Result<()> {
if let Some(ref value_commitment) = self.value_commitment {
writer.write_u8(1)?;
writer.write_all(value_commitment.to_bytes().as_ref())?;
} else {
writer.write_u8(0)?;
}
writer.write_all(&self.asset_id)?;
if let Some(ref payment_address) = self.payment_address {
writer.write_u8(1)?;
writer.write_all(payment_address.to_bytes().as_ref())?;
} else {
writer.write_u8(0)?;
}
if let Some(ref commitment_randomness) = self.commitment_randomness {
writer.write_u8(1)?;
writer.write_all(commitment_randomness.to_bytes().as_ref())?;
} else {
writer.write_u8(0)?;
}
if let Some(ref esk) = self.esk {
writer.write_u8(1)?;
writer.write_all(esk.to_bytes().as_ref())?;
} else {
writer.write_u8(0)?;
}
if let Some(ref proof_generation_key) = self.proof_generation_key {
writer.write_u8(1)?;
writer.write_all(proof_generation_key.to_bytes().as_ref())?;
} else {
writer.write_u8(0)?;
}
if let Some(ref ar) = self.ar {
writer.write_u8(1)?;
writer.write_all(ar.to_bytes().as_ref())?;
} else {
writer.write_u8(0)?;
}
Ok(())
}

pub fn read<R: Read>(mut reader: R) -> std::io::Result<Output> {
let mut value_commitment = None;
if reader.read_u8()? == 1 {
value_commitment = Some(ValueCommitment::read(&mut reader)?);
}
let mut asset_id = [0u8; ASSET_ID_LENGTH];
reader.read_exact(&mut asset_id)?;
let mut payment_address = None;
if reader.read_u8()? == 1 {
payment_address = Some(SubgroupPoint::read(&mut reader)?);
}
let mut commitment_randomness = None;
if reader.read_u8()? == 1 {
commitment_randomness = Some(jubjub::Fr::read(&mut reader)?);
}
let mut esk = None;
if reader.read_u8()? == 1 {
esk = Some(jubjub::Fr::read(&mut reader)?);
}
let mut proof_generation_key = None;
if reader.read_u8()? == 1 {
proof_generation_key = Some(ProofGenerationKey::read(&mut reader)?);
}
let mut ar = None;
if reader.read_u8()? == 1 {
ar = Some(jubjub::Fr::read(&mut reader)?);
}
Ok(Output {
value_commitment,
asset_id,
payment_address,
commitment_randomness,
esk,
proof_generation_key,
ar,
})
}
}

impl Circuit<blstrs::Scalar> for Output {
fn synthesize<CS: ConstraintSystem<blstrs::Scalar>>(
self,
Expand Down Expand Up @@ -318,6 +402,10 @@ mod test {
ar: Some(ar),
};

let mut writer = vec![];
instance.write(&mut writer).unwrap();
let _output = Output::read(&writer[..]).unwrap();

instance.synthesize(&mut cs).unwrap();

assert!(cs.is_satisfied());
Expand Down Expand Up @@ -366,4 +454,97 @@ mod test {
}
}
}

#[test]
fn test_output_read_write() {
let mut rng = StdRng::seed_from_u64(0);

for _ in 0..5 {
let mut asset_id = [0u8; 32];
let asset_generator = loop {
rng.fill(&mut asset_id[..]);

if let Some(point) = asset_hash_to_point(&asset_id) {
break point;
}
};

let value_commitment_randomness = jubjub::Fr::random(&mut rng);
let note_commitment_randomness = jubjub::Fr::random(&mut rng);
let value_commitment = ValueCommitment {
value: rng.next_u64(),
randomness: value_commitment_randomness,
asset_generator,
};

let nsk = jubjub::Fr::random(&mut rng);
let ak = jubjub::SubgroupPoint::random(&mut rng);
let esk = jubjub::Fr::random(&mut rng);
let ar = jubjub::Fr::random(&mut rng);

let proof_generation_key = ProofGenerationKey::new(ak, nsk);

let viewing_key = proof_generation_key.to_viewing_key();

let payment_address = *PUBLIC_KEY_GENERATOR * viewing_key.ivk().0;

let output = Output {
value_commitment: Some(value_commitment.clone()),
payment_address: Some(payment_address),
commitment_randomness: Some(note_commitment_randomness),
esk: Some(esk),
asset_id,
proof_generation_key: Some(proof_generation_key.clone()),
ar: Some(ar),
};

// Ser/de
let mut writer = vec![];
output.write(&mut writer).unwrap();
let deserialized_output: Output = Output::read(&writer[..]).unwrap();
assert_eq!(
output.value_commitment.clone().unwrap().value,
deserialized_output.value_commitment.clone().unwrap().value
);
assert_eq!(
output.value_commitment.clone().unwrap().randomness,
deserialized_output
.value_commitment
.clone()
.unwrap()
.randomness
);
assert_eq!(
output.value_commitment.clone().unwrap().asset_generator,
deserialized_output
.value_commitment
.clone()
.unwrap()
.asset_generator
);

assert_eq!(output.asset_id, deserialized_output.asset_id);
assert_eq!(output.payment_address, deserialized_output.payment_address);
assert_eq!(
output.commitment_randomness,
deserialized_output.commitment_randomness
);
assert_eq!(output.esk, deserialized_output.esk);

assert_eq!(
output.proof_generation_key.clone().unwrap().ak,
deserialized_output.proof_generation_key.clone().unwrap().ak
);
assert_eq!(
output.proof_generation_key.clone().unwrap().nsk,
deserialized_output
.proof_generation_key
.clone()
.unwrap()
.nsk
);

assert_eq!(output.ar, deserialized_output.ar);
}
}
}
Loading

0 comments on commit 4229f77

Please sign in to comment.