Skip to content

Commit

Permalink
Implement spend and mint description builders
Browse files Browse the repository at this point in the history
These are feature-gated and disabled by default because they do not work
in a web environment. In order to work, users need to patch `yastl` so
that it runs tasks serially rather than in parallel (because there's no
support for threads in a web environment). Also, even after patching,
generating a zero-knowledge proof currently takes an absurd amount of
time in a web browser.
  • Loading branch information
andiflabs committed Feb 13, 2025
1 parent e627c46 commit 9d46b87
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 0 deletions.
1 change: 1 addition & 0 deletions ironfish-rust-wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ default = ["transaction-proofs"]

download-params = ["ironfish/download-params"]
note-encryption-stats = ["ironfish/note-encryption-stats"]
transaction-builders = ["transaction-proofs"]
transaction-proofs = ["ironfish/transaction-proofs"]

[dependencies]
Expand Down
80 changes: 80 additions & 0 deletions ironfish-rust-wasm/src/transaction/mints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ use crate::{
use ironfish::{errors::IronfishErrorKind, transaction::TransactionVersion};
use wasm_bindgen::prelude::*;

#[cfg(feature = "transaction-builders")]
use crate::{keys::ProofGenerationKey, primitives::Fr};

wasm_bindgen_wrapper! {
#[derive(Clone, Debug)]
pub struct MintDescription(ironfish::transaction::mints::MintDescription);
Expand Down Expand Up @@ -126,3 +129,80 @@ impl UnsignedMintDescription {
self.0.add_signature(signature.into()).into()
}
}

#[cfg(feature = "transaction-builders")]
wasm_bindgen_wrapper! {
#[derive(Clone, Debug)]
pub struct MintBuilder(ironfish::transaction::mints::MintBuilder);
}

#[wasm_bindgen]
#[cfg(feature = "transaction-builders")]
impl MintBuilder {
#[wasm_bindgen(constructor)]
pub fn new(asset: Asset, value: u64) -> Self {
Self(ironfish::transaction::mints::MintBuilder::new(
asset.into(),
value,
))
}

#[wasm_bindgen]
pub fn build(
self,
proof_generation_key: &ProofGenerationKey,
public_address: &PublicAddress,
public_key_randomness: &Fr,
randomized_public_key: &PublicKey,
) -> Result<UnsignedMintDescription, IronfishError> {
self.0
.build(
proof_generation_key.as_ref(),
public_address.as_ref(),
public_key_randomness.as_ref(),
randomized_public_key.as_ref(),
)
.map(|d| d.into())
.map_err(|e| e.into())
}
}

#[cfg(test)]
mod tests {
#[cfg(feature = "transaction-builders")]
mod builder {
use crate::{assets::Asset, keys::SaplingKey, transaction::MintBuilder};
use rand::{thread_rng, Rng};
use wasm_bindgen_test::wasm_bindgen_test;

#[test]
#[wasm_bindgen_test]
fn build() {
let key = SaplingKey::random();
let asset = Asset::from_parts(key.public_address(), "asset name", "asset metadata")
.expect("failed to create asset");
let randomized_public_key_pair = key.view_key().randomized_public_key_pair();

let unsigned = MintBuilder::new(asset, 123)
.build(
&key.proof_generation_key(),
&key.public_address(),
&randomized_public_key_pair.public_key_randomness(),
&randomized_public_key_pair.randomized_public_key(),
)
.expect("failed to build mint description");

let sign_hash: [u8; 32] = thread_rng().gen();
let signed = unsigned
.sign(&key, &sign_hash)
.expect("failed to sign mint description");

signed
.verify_signature(
&sign_hash,
&randomized_public_key_pair.randomized_public_key(),
)
.expect("signature verification failed");
}
}
}
3 changes: 3 additions & 0 deletions ironfish-rust-wasm/src/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ pub use outputs::OutputDescription;
pub use spends::{SpendDescription, UnsignedSpendDescription};
pub use unsigned::UnsignedTransaction;

#[cfg(feature = "transaction-builders")]
pub use self::{mints::MintBuilder, spends::SpendBuilder};

wasm_bindgen_wrapper! {
#[derive(Clone, Debug)]
pub struct Transaction(ironfish::Transaction);
Expand Down
115 changes: 115 additions & 0 deletions ironfish-rust-wasm/src/transaction/spends.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ use crate::{
use ironfish::errors::IronfishErrorKind;
use wasm_bindgen::prelude::*;

#[cfg(feature = "transaction-builders")]
use crate::{
keys::{ProofGenerationKey, ViewKey},
note::Note,
primitives::Fr,
witness::Witness,
};

wasm_bindgen_wrapper! {
#[derive(Clone, Debug)]
pub struct SpendDescription(ironfish::SpendDescription);
Expand Down Expand Up @@ -119,3 +127,110 @@ impl UnsignedSpendDescription {
self.0.add_signature(signature.into()).into()
}
}

#[cfg(feature = "transaction-builders")]
wasm_bindgen_wrapper! {
#[derive(Clone, Debug)]
pub struct SpendBuilder(ironfish::transaction::spends::SpendBuilder);
}

#[wasm_bindgen]
#[cfg(feature = "transaction-builders")]
impl SpendBuilder {
#[wasm_bindgen(constructor)]
pub fn new(note: Note, witness: &Witness) -> Self {
Self(ironfish::transaction::spends::SpendBuilder::new(
note.into(),
witness.as_ref(),
))
}

#[wasm_bindgen]
pub fn build(
self,
proof_generation_key: &ProofGenerationKey,
view_key: &ViewKey,
public_key_randomness: &Fr,
randomized_public_key: &PublicKey,
) -> Result<UnsignedSpendDescription, IronfishError> {
self.0
.build(
proof_generation_key.as_ref(),
view_key.as_ref(),
public_key_randomness.as_ref(),
randomized_public_key.as_ref(),
)
.map(|d| d.into())
.map_err(|e| e.into())
}
}

#[cfg(test)]
#[cfg(feature = "transaction-builders")]
mod tests {
mod builder {
use crate::{
assets::AssetIdentifier,
keys::SaplingKey,
merkle_note::MerkleNoteHash,
note::Note,
primitives::Scalar,
transaction::SpendBuilder,
witness::{Witness, WitnessNode},
};
use rand::{thread_rng, Rng};
use wasm_bindgen_test::wasm_bindgen_test;

fn random_witness(note: &Note) -> Witness {
let depth = 32;
let zero = Scalar::zero();
let auth_path = vec![WitnessNode::left(zero); depth];
let root_hash = {
let mut cur_hash = MerkleNoteHash::from_value(note.commitment_point());
for (i, node) in auth_path.iter().enumerate() {
cur_hash = MerkleNoteHash::combine_hash(i, &cur_hash, &node.hash())
}
cur_hash
};

Witness::new(depth, root_hash, auth_path)
}

#[test]
#[wasm_bindgen_test]
fn build() {
let owner_key = SaplingKey::random();
let sender_key = SaplingKey::random();
let note = Note::from_parts(
owner_key.public_address(),
123,
"some memo",
AssetIdentifier::native(),
sender_key.public_address(),
);
let witness = random_witness(&note);
let randomized_public_key_pair = owner_key.view_key().randomized_public_key_pair();

let unsigned = SpendBuilder::new(note, &witness)
.build(
&owner_key.proof_generation_key(),
&owner_key.view_key(),
&randomized_public_key_pair.public_key_randomness(),
&randomized_public_key_pair.randomized_public_key(),
)
.expect("failed to build spend description");

let sign_hash: [u8; 32] = thread_rng().gen();
let signed = unsigned
.sign(&owner_key, &sign_hash)
.expect("failed to sign mint description");

signed
.verify_signature(
&sign_hash,
&randomized_public_key_pair.randomized_public_key(),
)
.expect("signature verification failed");
}
}
}

0 comments on commit 9d46b87

Please sign in to comment.