Skip to content

Commit

Permalink
Add methods to ProposedTransaction and description builders to build …
Browse files Browse the repository at this point in the history
…zk circuits
  • Loading branch information
andiflabs committed Feb 12, 2025
1 parent f0a55f6 commit 1c2e7f1
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 28 deletions.
16 changes: 12 additions & 4 deletions ironfish-rust/src/transaction/mints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,25 @@ impl MintBuilder {
self
}

pub fn build_circuit(
&self,
proof_generation_key: &ProofGenerationKey,
public_key_randomness: &ironfish_jubjub::Fr,
) -> MintAsset {
MintAsset {
proof_generation_key: Some(proof_generation_key.clone()),
public_key_randomness: Some(*public_key_randomness),
}
}

pub fn build(
&self,
proof_generation_key: &ProofGenerationKey,
public_address: &PublicAddress,
public_key_randomness: &ironfish_jubjub::Fr,
randomized_public_key: &redjubjub::PublicKey,
) -> Result<UnsignedMintDescription, IronfishError> {
let circuit = MintAsset {
proof_generation_key: Some(proof_generation_key.clone()),
public_key_randomness: Some(*public_key_randomness),
};
let circuit = self.build_circuit(proof_generation_key, public_key_randomness);

let proof = groth16::create_random_proof(circuit, &SAPLING.mint_params, &mut thread_rng())?;

Expand Down
31 changes: 20 additions & 11 deletions ironfish-rust/src/transaction/outputs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,24 @@ impl OutputBuilder {
ExtendedPoint::from(self.value_commitment.commitment())
}

pub(crate) fn build_circuit(
&self,
proof_generation_key: &ProofGenerationKey,
public_key_randomness: &ironfish_jubjub::Fr,
) -> (Output, EphemeralKeyPair) {
let key_pair = EphemeralKeyPair::new();
let circuit = Output {
value_commitment: Some(self.value_commitment.clone()),
payment_address: Some(self.note.owner.0),
commitment_randomness: Some(self.note.randomness),
esk: Some(*key_pair.secret()),
asset_id: *self.note.asset_id().as_bytes(),
proof_generation_key: Some(proof_generation_key.clone()),
ar: Some(*public_key_randomness),
};
(circuit, key_pair)
}

/// Construct and return the committed [`OutputDescription`] for this receiving calculation.
///
/// The [`OutputDescription`] is the publicly visible form of the new note, not
Expand All @@ -88,17 +106,8 @@ impl OutputBuilder {
public_key_randomness: &ironfish_jubjub::Fr,
randomized_public_key: &redjubjub::PublicKey,
) -> Result<OutputDescription, IronfishError> {
let diffie_hellman_keys = EphemeralKeyPair::new();

let circuit = Output {
value_commitment: Some(self.value_commitment.clone()),
payment_address: Some(self.note.owner.0),
commitment_randomness: Some(self.note.randomness),
esk: Some(*diffie_hellman_keys.secret()),
asset_id: *self.note.asset_id().as_bytes(),
proof_generation_key: Some(proof_generation_key.clone()),
ar: Some(*public_key_randomness),
};
let (circuit, diffie_hellman_keys) =
self.build_circuit(proof_generation_key, public_key_randomness);

let proof =
groth16::create_random_proof(circuit, &SAPLING.output_params, &mut thread_rng())?;
Expand Down
46 changes: 45 additions & 1 deletion ironfish-rust/src/transaction/proposed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::{
asset_identifier::{AssetIdentifier, NATIVE_ASSET},
},
errors::{IronfishError, IronfishErrorKind},
keys::{PublicAddress, SaplingKey},
keys::{EphemeralKeyPair, PublicAddress, SaplingKey},
note::Note,
transaction::{
burns::{BurnBuilder, BurnDescription},
Expand All @@ -32,6 +32,7 @@ use group::GroupEncoding;
use ironfish_jubjub::ExtendedPoint;
use ironfish_zkp::{
constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR},
proofs,
redjubjub::{self, Signature},
ProofGenerationKey,
};
Expand Down Expand Up @@ -195,6 +196,49 @@ impl ProposedTransaction {
Ok(())
}

#[allow(clippy::type_complexity)]
pub fn build_circuits(
&mut self,
proof_authorizing_key: ironfish_jubjub::Fr,
view_key: ViewKey,
intended_transaction_fee: i64,
change_goes_to: Option<PublicAddress>,
) -> Result<
(
Vec<proofs::Spend>,
Vec<(proofs::Output, EphemeralKeyPair)>,
Vec<proofs::MintAsset>,
),
IronfishError,
> {
let public_address = view_key.public_address()?;
let proof_generation_key =
ProofGenerationKey::new(view_key.authorizing_key, proof_authorizing_key);

let is_miners_fee = self.outputs.iter().any(|output| output.get_is_miners_fee());
if !is_miners_fee {
self.add_change_notes(change_goes_to, public_address, intended_transaction_fee)?;
}

let spend_circuits = self
.spends
.iter()
.map(|spend| spend.build_circuit(&proof_generation_key, &self.public_key_randomness))
.collect();
let output_circuits = self
.outputs
.iter()
.map(|output| output.build_circuit(&proof_generation_key, &self.public_key_randomness))
.collect();
let mint_circuits = self
.mints
.iter()
.map(|mint| mint.build_circuit(&proof_generation_key, &self.public_key_randomness))
.collect();

Ok((spend_circuits, output_circuits, mint_circuits))
}

pub fn build(
&mut self,
proof_authorizing_key: ironfish_jubjub::Fr,
Expand Down
32 changes: 20 additions & 12 deletions ironfish-rust/src/transaction/spends.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ impl SpendBuilder {
/// This is the only time this API thinks about the merkle tree. The witness
/// contains the root-hash at the time the witness was created and the path
/// to verify the location of that note in the tree.
pub(crate) fn new<W: WitnessTrait + ?Sized>(note: Note, witness: &W) -> Self {
pub fn new<W: WitnessTrait + ?Sized>(note: Note, witness: &W) -> Self {
let value_commitment = ValueCommitment::new(note.value, note.asset_generator());

SpendBuilder {
Expand All @@ -89,12 +89,29 @@ impl SpendBuilder {
ExtendedPoint::from(self.value_commitment.commitment())
}

pub fn build_circuit(
&self,
proof_generation_key: &ProofGenerationKey,
public_key_randomness: &Fr,
) -> Spend {
Spend {
value_commitment: Some(self.value_commitment.clone()),
proof_generation_key: Some(proof_generation_key.clone()),
payment_address: Some(self.note.owner.0),
auth_path: self.auth_path.clone(),
commitment_randomness: Some(self.note.randomness),
anchor: Some(self.root_hash),
ar: Some(*public_key_randomness),
sender_address: Some(self.note.sender.0),
}
}

/// Sign this spend with the private key, and return a [`SpendDescription`]
/// suitable for serialization.
///
/// Verifies the proof before returning to prevent posting broken
/// transactions
pub(crate) fn build(
pub fn build(
&self,
proof_generation_key: &ProofGenerationKey,
view_key: &ViewKey,
Expand All @@ -103,16 +120,7 @@ impl SpendBuilder {
) -> Result<UnsignedSpendDescription, IronfishError> {
let value_commitment_point = self.value_commitment_point();

let circuit = Spend {
value_commitment: Some(self.value_commitment.clone()),
proof_generation_key: Some(proof_generation_key.clone()),
payment_address: Some(self.note.owner.0),
auth_path: self.auth_path.clone(),
commitment_randomness: Some(self.note.randomness),
anchor: Some(self.root_hash),
ar: Some(*public_key_randomness),
sender_address: Some(self.note.sender.0),
};
let circuit = self.build_circuit(proof_generation_key, public_key_randomness);

// Proof that the spend was valid and successful for the provided owner
// and note.
Expand Down

0 comments on commit 1c2e7f1

Please sign in to comment.