From 6894e5bb023a0bd86abffce85a08adb70f1dab73 Mon Sep 17 00:00:00 2001 From: Lucas Meier Date: Tue, 12 Mar 2024 14:32:31 -0700 Subject: [PATCH] Link nullifier key to claim address in swap claim Closes #3978 --- crates/bench/benches/swap_claim.rs | 2 + crates/bin/pcli/tests/proof.rs | 2 + .../core/component/dex/src/swap_claim/plan.rs | 14 +++-- .../component/dex/src/swap_claim/proof.rs | 54 +++++++++++++++++-- 4 files changed, 64 insertions(+), 8 deletions(-) diff --git a/crates/bench/benches/swap_claim.rs b/crates/bench/benches/swap_claim.rs index e6b5c6fcfb..11049c5fd7 100644 --- a/crates/bench/benches/swap_claim.rs +++ b/crates/bench/benches/swap_claim.rs @@ -32,6 +32,7 @@ fn swap_claim_proving_time(c: &mut Criterion) { let ivk_recipient = fvk_recipient.incoming(); let (claim_address, _dtk_d) = ivk_recipient.payment_address(0u32.into()); let nk = *sk_recipient.nullifier_key(); + let ak = *sk_recipient.spend_auth_key().into(); let gm = asset::Cache::with_known_assets().get_unit("gm").unwrap(); let gn = asset::Cache::with_known_assets().get_unit("gn").unwrap(); @@ -91,6 +92,7 @@ fn swap_claim_proving_time(c: &mut Criterion) { let private = SwapClaimProofPrivate { swap_plaintext, state_commitment_proof, + ak, nk, lambda_1, lambda_2, diff --git a/crates/bin/pcli/tests/proof.rs b/crates/bin/pcli/tests/proof.rs index 1e7e029f65..a1f8d25596 100644 --- a/crates/bin/pcli/tests/proof.rs +++ b/crates/bin/pcli/tests/proof.rs @@ -240,6 +240,7 @@ fn swap_claim_parameters_vs_current_swap_claim_circuit() { let ivk_recipient = fvk_recipient.incoming(); let (claim_address, _dtk_d) = ivk_recipient.payment_address(0u32.into()); let nk = *sk_recipient.nullifier_key(); + let ak = *sk_recipient.spend_auth_key().into(); let gm = asset::Cache::with_known_assets().get_unit("gm").unwrap(); let gn = asset::Cache::with_known_assets().get_unit("gn").unwrap(); @@ -299,6 +300,7 @@ fn swap_claim_parameters_vs_current_swap_claim_circuit() { let private = SwapClaimProofPrivate { swap_plaintext, state_commitment_proof, + ak, nk, lambda_1, lambda_2, diff --git a/crates/core/component/dex/src/swap_claim/plan.rs b/crates/core/component/dex/src/swap_claim/plan.rs index b8dd56e31f..83d521f380 100644 --- a/crates/core/component/dex/src/swap_claim/plan.rs +++ b/crates/core/component/dex/src/swap_claim/plan.rs @@ -44,7 +44,7 @@ impl SwapClaimPlan { ) -> SwapClaim { SwapClaim { body: self.swap_claim_body(fvk), - proof: self.swap_claim_proof(state_commitment_proof, fvk.nullifier_key()), + proof: self.swap_claim_proof(state_commitment_proof, fvk), epoch_duration: self.epoch_duration, } } @@ -54,7 +54,7 @@ impl SwapClaimPlan { pub fn swap_claim_proof( &self, state_commitment_proof: &tct::Proof, - nk: &NullifierKey, + fvk: &FullViewingKey, ) -> SwapClaimProof { let (lambda_1, lambda_2) = self .output_data @@ -66,8 +66,11 @@ impl SwapClaimPlan { let note_commitment_1 = output_1_note.commit(); let note_commitment_2 = output_2_note.commit(); - let nullifier = - Nullifier::derive(nk, self.position, &self.swap_plaintext.swap_commitment()); + let nullifier = Nullifier::derive( + fvk.nullifier_key(), + self.position, + &self.swap_plaintext.swap_commitment(), + ); SwapClaimProof::prove( self.proof_blinding_r, self.proof_blinding_s, @@ -83,7 +86,8 @@ impl SwapClaimPlan { SwapClaimProofPrivate { swap_plaintext: self.swap_plaintext.clone(), state_commitment_proof: state_commitment_proof.clone(), - nk: *nk, + nk: *fvk.nullifier_key(), + ak: *fvk.spend_verification_key(), lambda_1, lambda_2, note_blinding_1, diff --git a/crates/core/component/dex/src/swap_claim/proof.rs b/crates/core/component/dex/src/swap_claim/proof.rs index 972ff6fe09..c8698d131c 100644 --- a/crates/core/component/dex/src/swap_claim/proof.rs +++ b/crates/core/component/dex/src/swap_claim/proof.rs @@ -7,7 +7,11 @@ use ark_r1cs_std::prelude::*; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_snark::SNARK; -use decaf377::{r1cs::FqVar, Bls12_377, Fq}; +use decaf377::{ + r1cs::{ElementVar, FqVar}, + Bls12_377, Fq, +}; +use decaf377_rdsa::{SpendAuth, VerificationKey}; use penumbra_fee::Fee; use penumbra_proto::{core::component::dex::v1 as pb, DomainType}; use penumbra_tct as tct; @@ -17,7 +21,10 @@ use penumbra_asset::{ asset::{self, Id}, Value, ValueVar, }; -use penumbra_keys::keys::{Bip44Path, NullifierKey, NullifierKeyVar, SeedPhrase, SpendKey}; +use penumbra_keys::keys::{ + AuthorizationKeyVar, Bip44Path, IncomingViewingKeyVar, NullifierKey, NullifierKeyVar, + SeedPhrase, SpendKey, +}; use penumbra_num::{Amount, AmountVar}; use penumbra_sct::{Nullifier, NullifierVar}; use penumbra_shielded_pool::{ @@ -59,6 +66,8 @@ pub struct SwapClaimProofPrivate { pub swap_plaintext: SwapPlaintext, /// Inclusion proof for the swap commitment pub state_commitment_proof: tct::Proof, + /// The spend verification key + pub ak: VerificationKey, // The nullifier deriving key for the Swap NFT note. pub nk: NullifierKey, /// Output amount 1 @@ -76,6 +85,8 @@ fn check_satisfaction( public: &SwapClaimProofPublic, private: &SwapClaimProofPrivate, ) -> Result<()> { + use penumbra_keys::FullViewingKey; + let swap_commitment = private.swap_plaintext.swap_commitment(); if swap_commitment != private.state_commitment_proof.commitment() { anyhow::bail!("swap commitment integrity check failed"); @@ -92,6 +103,22 @@ fn check_satisfaction( anyhow::bail!("nullifier did not match public input"); } + let fvk = FullViewingKey::from_components(private.ak, private.nk); + let ivk = fvk.incoming(); + let transmission_key = ivk.diversified_public(private.swap_plaintext.diversified_generator()); + anyhow::ensure!( + transmission_key == *private.swap_plaintext.transmission_key(), + "transmission key did not match swap plaintext" + ); + anyhow::ensure!( + !private.swap_plaintext.diversified_generator().is_identity(), + "diversified generator is identity" + ); + anyhow::ensure!( + !private.ak.is_identity(), + "diversified generator is identity" + ); + if private.swap_plaintext.claim_fee != public.claim_fee { anyhow::bail!("claim fee did not match public input"); } @@ -176,6 +203,7 @@ impl ConstraintSynthesizer for SwapClaimCircuit { let merkle_path_var = tct::r1cs::MerkleAuthPathVar::new_witness(cs.clone(), || { Ok(self.private.state_commitment_proof) })?; + let ak_var = AuthorizationKeyVar::new_witness(cs.clone(), || Ok(self.private.ak))?; let nk_var = NullifierKeyVar::new_witness(cs.clone(), || Ok(self.private.nk))?; let lambda_1_i_var = AmountVar::new_witness(cs.clone(), || Ok(self.private.lambda_1))?; let lambda_2_i_var = AmountVar::new_witness(cs.clone(), || Ok(self.private.lambda_2))?; @@ -211,6 +239,18 @@ impl ConstraintSynthesizer for SwapClaimCircuit { let nullifier_var = NullifierVar::derive(&nk_var, &position_var, &claimed_swap_commitment)?; nullifier_var.enforce_equal(&claimed_nullifier_var)?; + // Connection between nullifier key and address + let ivk = IncomingViewingKeyVar::derive(&nk_var, &ak_var)?; + let computed_transmission_key = + ivk.diversified_public(&swap_plaintext_var.claim_address.diversified_generator)?; + computed_transmission_key + .enforce_equal(&swap_plaintext_var.claim_address.transmission_key)?; + // Check the diversified base is not identity. + let identity = ElementVar::new_constant(cs.clone(), decaf377::Element::default())?; + identity.enforce_not_equal(&swap_plaintext_var.claim_address.diversified_generator)?; + // Check the ak is not identity. + identity.enforce_not_equal(&ak_var.inner)?; + // Fee consistency check. claimed_fee_var.enforce_equal(&swap_plaintext_var.claim_fee)?; @@ -281,6 +321,7 @@ impl DummyWitness for SwapClaimCircuit { let fvk_sender = sk_sender.full_viewing_key(); let ivk_sender = fvk_sender.incoming(); let (address, _dtk_d) = ivk_sender.payment_address(0u32.into()); + let ak = *fvk_sender.spend_verification_key(); let nk = *sk_sender.nullifier_key(); let delta_1_i = 10u64.into(); @@ -338,6 +379,7 @@ impl DummyWitness for SwapClaimCircuit { swap_plaintext, state_commitment_proof, nk, + ak, lambda_1, lambda_2, note_blinding_1, @@ -561,6 +603,7 @@ mod tests { let ivk_recipient = fvk_recipient.incoming(); let (claim_address, _dtk_d) = ivk_recipient.payment_address(0u32.into()); let nk = *sk_recipient.nullifier_key(); + let ak = *fvk_recipient.spend_verification_key(); let gm = asset::Cache::with_known_assets().get_unit("gm").unwrap(); let gn = asset::Cache::with_known_assets().get_unit("gn").unwrap(); @@ -621,6 +664,7 @@ mod tests { let private = SwapClaimProofPrivate { swap_plaintext, state_commitment_proof, + ak, nk, lambda_1, lambda_2, @@ -691,6 +735,7 @@ mod tests { let ivk_recipient = fvk_recipient.incoming(); let (claim_address, _dtk_d) = ivk_recipient.payment_address(0u32.into()); let nk = *sk_recipient.nullifier_key(); + let ak = *fvk_recipient.spend_verification_key(); let gm = asset::Cache::with_known_assets().get_unit("gm").unwrap(); let gn = asset::Cache::with_known_assets().get_unit("gn").unwrap(); @@ -751,6 +796,7 @@ mod tests { let private = SwapClaimProofPrivate { swap_plaintext, state_commitment_proof, + ak, nk, lambda_1, lambda_2, @@ -779,7 +825,8 @@ mod tests { let fvk_recipient = sk_recipient.full_viewing_key(); let ivk_recipient = fvk_recipient.incoming(); let (claim_address, _dtk_d) = ivk_recipient.payment_address(0u32.into()); - let nk = *sk_recipient.nullifier_key(); + let nk = *fvk_recipient.nullifier_key(); + let ak = *fvk_recipient.spend_verification_key(); let gm = asset::Cache::with_known_assets().get_unit("gm").unwrap(); let gn = asset::Cache::with_known_assets().get_unit("gn").unwrap(); @@ -849,6 +896,7 @@ mod tests { let private = SwapClaimProofPrivate { swap_plaintext, state_commitment_proof, + ak, nk, lambda_1, lambda_2,