diff --git a/Cargo.lock b/Cargo.lock index 71f3630c19..09cea63e42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5085,6 +5085,7 @@ name = "penumbra-proof-params" version = "0.59.0" dependencies = [ "anyhow", + "ark-ec", "ark-ff", "ark-groth16", "ark-r1cs-std", @@ -5126,12 +5127,19 @@ dependencies = [ name = "penumbra-proof-setup" version = "0.59.0" dependencies = [ + "anyhow", "ark-ec", "ark-ff", + "ark-groth16", + "ark-poly", + "ark-r1cs-std", + "ark-relations", "ark-serialize", + "ark-snark", "blake2b_simd 0.5.11", "criterion 0.4.0", "decaf377 0.5.0", + "rand_chacha 0.3.1", "rand_core 0.6.4", ] diff --git a/crates/core/component/dex/src/batch_swap_output_data.rs b/crates/core/component/dex/src/batch_swap_output_data.rs index b99dc86091..c6e7b440ee 100644 --- a/crates/core/component/dex/src/batch_swap_output_data.rs +++ b/crates/core/component/dex/src/batch_swap_output_data.rs @@ -297,12 +297,12 @@ impl TryFrom for BatchSwapOutputData { #[cfg(test)] mod tests { - use ark_groth16::{r1cs_to_qap::LibsnarkReduction, Groth16, ProvingKey, VerifyingKey}; + use ark_groth16::{r1cs_to_qap::LibsnarkReduction, Groth16}; use ark_relations::r1cs::ConstraintSynthesizer; use ark_snark::SNARK; use decaf377::Bls12_377; use penumbra_asset::asset; - use penumbra_proof_params::ParameterSetup; + use penumbra_proof_params::{generate_test_parameters, DummyWitness}; use rand_core::OsRng; use super::*; @@ -371,8 +371,8 @@ mod tests { } } - impl ParameterSetup for ProRataOutputCircuit { - fn generate_test_parameters() -> (ProvingKey, VerifyingKey) { + impl DummyWitness for ProRataOutputCircuit { + fn with_dummy_witness() -> Self { let trading_pair = TradingPair { asset_1: asset::Cache::with_known_assets() .get_unit("upenumbra") @@ -383,7 +383,7 @@ mod tests { .unwrap() .id(), }; - let circuit = ProRataOutputCircuit { + Self { delta_1_i: Amount::from(1u32), delta_2_i: Amount::from(1u32), lambda_1_i: Amount::from(1u32), @@ -399,12 +399,7 @@ mod tests { trading_pair, epoch_starting_height: 1, }, - }; - let (pk, vk) = Groth16::::circuit_specific_setup( - circuit, &mut OsRng, - ) - .expect("can perform circuit specific setup"); - (pk, vk) + } } } @@ -441,8 +436,8 @@ mod tests { bsod, }; - let (pk, vk) = ProRataOutputCircuit::generate_test_parameters(); let mut rng = OsRng; + let (pk, vk) = generate_test_parameters::(&mut rng); let proof = Groth16::::prove(&pk, circuit, &mut rng) .expect("should be able to form proof"); diff --git a/crates/core/component/dex/src/swap/proof.rs b/crates/core/component/dex/src/swap/proof.rs index 83fdc1ecb4..52598bc727 100644 --- a/crates/core/component/dex/src/swap/proof.rs +++ b/crates/core/component/dex/src/swap/proof.rs @@ -1,6 +1,6 @@ use ark_ff::ToConstraintField; use ark_groth16::{ - r1cs_to_qap::LibsnarkReduction, Groth16, PreparedVerifyingKey, Proof, ProvingKey, VerifyingKey, + r1cs_to_qap::LibsnarkReduction, Groth16, PreparedVerifyingKey, Proof, ProvingKey, }; use ark_r1cs_std::prelude::*; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef}; @@ -14,7 +14,6 @@ use penumbra_fee::Fee; use penumbra_proto::{core::crypto::v1alpha1 as pb, DomainType, TypeUrl}; use penumbra_tct as tct; use penumbra_tct::r1cs::StateCommitmentVar; -use rand_core::OsRng; use penumbra_asset::{ asset, @@ -29,7 +28,7 @@ use crate::{ TradingPair, }; -use penumbra_proof_params::{ParameterSetup, GROTH16_PROOF_LENGTH_BYTES}; +use penumbra_proof_params::{DummyWitness, GROTH16_PROOF_LENGTH_BYTES}; pub struct SwapCircuit { /// The swap plaintext. @@ -102,8 +101,8 @@ impl ConstraintSynthesizer for SwapCircuit { } } -impl ParameterSetup for SwapCircuit { - fn generate_test_parameters() -> (ProvingKey, VerifyingKey) { +impl DummyWitness for SwapCircuit { + fn with_dummy_witness() -> Self { let a = asset::Cache::with_known_assets() .get_unit("upenumbra") .unwrap(); @@ -134,17 +133,13 @@ impl ParameterSetup for SwapCircuit { rseed: Rseed([1u8; 32]), }; - let circuit = SwapCircuit { + Self { swap_plaintext: swap_plaintext.clone(), fee_blinding: Fr::from(1), swap_commitment: swap_plaintext.swap_commitment(), fee_commitment: balance::Commitment(decaf377::basepoint()), balance_commitment: balance::Commitment(decaf377::basepoint()), - }; - let (pk, vk) = - Groth16::::circuit_specific_setup(circuit, &mut OsRng) - .expect("can perform circuit specific setup"); - (pk, vk) + } } } @@ -251,7 +246,9 @@ mod tests { use penumbra_asset::{Balance, Value}; use penumbra_keys::keys::{SeedPhrase, SpendKey}; use penumbra_num::Amount; + use penumbra_proof_params::generate_prepared_test_parameters; use proptest::prelude::*; + use rand_core::OsRng; fn fr_strategy() -> BoxedStrategy { any::<[u8; 32]>() @@ -263,9 +260,9 @@ mod tests { #![proptest_config(ProptestConfig::with_cases(2))] #[test] fn swap_proof_happy_path(fee_blinding in fr_strategy(), value1_amount in 2..200u64) { - let (pk, vk) = SwapCircuit::generate_prepared_test_parameters(); - let mut rng = OsRng; + let (pk, vk) = generate_prepared_test_parameters::(&mut rng); + let seed_phrase = SeedPhrase::generate(&mut rng); let sk_recipient = SpendKey::from_seed_phrase(seed_phrase, 0); diff --git a/crates/core/component/dex/src/swap_claim/proof.rs b/crates/core/component/dex/src/swap_claim/proof.rs index f084625328..12cd673d60 100644 --- a/crates/core/component/dex/src/swap_claim/proof.rs +++ b/crates/core/component/dex/src/swap_claim/proof.rs @@ -1,6 +1,6 @@ use ark_ff::ToConstraintField; use ark_groth16::{ - r1cs_to_qap::LibsnarkReduction, Groth16, PreparedVerifyingKey, Proof, ProvingKey, VerifyingKey, + r1cs_to_qap::LibsnarkReduction, Groth16, PreparedVerifyingKey, Proof, ProvingKey, }; use ark_r1cs_std::prelude::*; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef}; @@ -11,7 +11,6 @@ use penumbra_fee::Fee; use penumbra_proto::{core::crypto::v1alpha1 as pb, DomainType, TypeUrl}; use penumbra_tct as tct; use penumbra_tct::r1cs::StateCommitmentVar; -use rand_core::OsRng; use penumbra_asset::{ asset::{self}, @@ -31,7 +30,7 @@ use crate::{ BatchSwapOutputData, TradingPair, }; -use penumbra_proof_params::{ParameterSetup, GROTH16_PROOF_LENGTH_BYTES}; +use penumbra_proof_params::{DummyWitness, GROTH16_PROOF_LENGTH_BYTES}; /// SwapClaim consumes an existing Swap NFT so they are most similar to Spend operations, /// however the note commitment proof needs to be for a specific block due to clearing prices @@ -204,8 +203,8 @@ impl ConstraintSynthesizer for SwapClaimCircuit { } } -impl ParameterSetup for SwapClaimCircuit { - fn generate_test_parameters() -> (ProvingKey, VerifyingKey) { +impl DummyWitness for SwapClaimCircuit { + fn with_dummy_witness() -> Self { let trading_pair = TradingPair { asset_1: asset::Cache::with_known_assets() .get_unit("upenumbra") @@ -264,7 +263,7 @@ impl ParameterSetup for SwapClaimCircuit { let note_commitment_2 = tct::StateCommitment(Fq::from(2)); let (lambda_1, lambda_2) = output_data.pro_rata_outputs((delta_1_i, delta_2_i)); - let circuit = SwapClaimCircuit { + Self { swap_plaintext, state_commitment_proof, anchor, @@ -278,11 +277,7 @@ impl ParameterSetup for SwapClaimCircuit { note_blinding_2, note_commitment_1, note_commitment_2, - }; - let (pk, vk) = - Groth16::::circuit_specific_setup(circuit, &mut OsRng) - .expect("can perform circuit specific setup"); - (pk, vk) + } } } @@ -406,15 +401,17 @@ mod tests { use ark_ff::UniformRand; use penumbra_keys::keys::{SeedPhrase, SpendKey}; use penumbra_num::Amount; + use penumbra_proof_params::generate_prepared_test_parameters; use proptest::prelude::*; + use rand_core::OsRng; proptest! { #![proptest_config(ProptestConfig::with_cases(2))] #[test] fn swap_claim_proof_happy_path_filled(seed_phrase_randomness in any::<[u8; 32]>(), value1_amount in 2..200u64) { - let (pk, vk) = SwapClaimCircuit::generate_prepared_test_parameters(); - let mut rng = OsRng; + let (pk, vk) = generate_prepared_test_parameters::(&mut rng); + let seed_phrase = SeedPhrase::from_randomness(&seed_phrase_randomness); let sk_recipient = SpendKey::from_seed_phrase(seed_phrase, 0); @@ -500,9 +497,8 @@ mod tests { #[test] fn swap_claim_proof_happy_path_unfilled() { - let (pk, vk) = SwapClaimCircuit::generate_prepared_test_parameters(); - let mut rng = OsRng; + let (pk, vk) = generate_prepared_test_parameters::(&mut rng); let seed_phrase = SeedPhrase::generate(rng); let sk_recipient = SpendKey::from_seed_phrase(seed_phrase, 0); diff --git a/crates/core/component/governance/proptest-regressions/delegator_vote/proof.txt b/crates/core/component/governance/proptest-regressions/delegator_vote/proof.txt new file mode 100644 index 0000000000..fcb89c22f5 --- /dev/null +++ b/crates/core/component/governance/proptest-regressions/delegator_vote/proof.txt @@ -0,0 +1,7 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc 26bb258b706f59489cba08e6eb9724f26b3c062baf6fb3d97f662f4892bed416 # shrinks to seed_phrase_randomness = [13, 16, 140, 52, 135, 171, 74, 227, 99, 253, 177, 7, 160, 177, 69, 45, 9, 54, 15, 250, 71, 192, 136, 62, 225, 240, 158, 59, 43, 90, 232, 192], spend_auth_randomizer = BigInt([13101869489432526996, 5587207668677778290, 13142428231073505608, 232443455065964708]), value_amount = 1637377837, num_commitments = 1951 diff --git a/crates/core/component/governance/src/delegator_vote/proof.rs b/crates/core/component/governance/src/delegator_vote/proof.rs index d9b9a4e28c..0b10134c01 100644 --- a/crates/core/component/governance/src/delegator_vote/proof.rs +++ b/crates/core/component/governance/src/delegator_vote/proof.rs @@ -8,7 +8,7 @@ use decaf377::FieldExt; use decaf377::{r1cs::FqVar, Bls12_377, Fq, Fr}; use ark_ff::ToConstraintField; -use ark_groth16::{Groth16, PreparedVerifyingKey, Proof, ProvingKey, VerifyingKey}; +use ark_groth16::{Groth16, PreparedVerifyingKey, Proof, ProvingKey}; use ark_r1cs_std::prelude::AllocVar; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef}; use ark_snark::SNARK; @@ -16,7 +16,6 @@ use decaf377_rdsa::{SpendAuth, VerificationKey}; use penumbra_proto::{core::crypto::v1alpha1 as pb, DomainType, TypeUrl}; use penumbra_tct as tct; use penumbra_tct::r1cs::StateCommitmentVar; -use rand_core::OsRng; use tct::r1cs::PositionVar; use penumbra_asset::{balance, balance::commitment::BalanceCommitmentVar, Value}; @@ -24,7 +23,7 @@ use penumbra_keys::keys::{ AuthorizationKeyVar, IncomingViewingKeyVar, NullifierKey, NullifierKeyVar, RandomizedVerificationKey, SeedPhrase, SpendAuthRandomizerVar, SpendKey, }; -use penumbra_proof_params::{ParameterSetup, VerifyingKeyExt, GROTH16_PROOF_LENGTH_BYTES}; +use penumbra_proof_params::{DummyWitness, VerifyingKeyExt, GROTH16_PROOF_LENGTH_BYTES}; use penumbra_sct::{Nullifier, NullifierVar}; use penumbra_shielded_pool::{note, Note, Rseed}; @@ -184,8 +183,8 @@ impl ConstraintSynthesizer for DelegatorVoteCircuit { } } -impl ParameterSetup for DelegatorVoteCircuit { - fn generate_test_parameters() -> (ProvingKey, VerifyingKey) { +impl DummyWitness for DelegatorVoteCircuit { + fn with_dummy_witness() -> Self { let seed_phrase = SeedPhrase::from_randomness(&[b'f'; 32]); let sk_sender = SpendKey::from_seed_phrase(seed_phrase, 0); let fvk_sender = sk_sender.full_viewing_key(); @@ -212,7 +211,7 @@ impl ParameterSetup for DelegatorVoteCircuit { let state_commitment_proof = sct.witness(note_commitment).unwrap(); let start_position = state_commitment_proof.position(); - let circuit = DelegatorVoteCircuit { + Self { state_commitment_proof, note, v_blinding, @@ -224,11 +223,7 @@ impl ParameterSetup for DelegatorVoteCircuit { nullifier, rk, start_position, - }; - let (pk, vk) = - Groth16::::circuit_specific_setup(circuit, &mut OsRng) - .expect("can perform circuit specific setup"); - (pk, vk) + } } } @@ -364,8 +359,10 @@ mod tests { use decaf377::{Fq, Fr}; use penumbra_asset::{asset, Value}; use penumbra_keys::keys::{SeedPhrase, SpendKey}; + use penumbra_proof_params::generate_prepared_test_parameters; use penumbra_sct::Nullifier; use proptest::prelude::*; + use rand_core::OsRng; fn fr_strategy() -> BoxedStrategy { any::<[u8; 32]>() @@ -377,8 +374,8 @@ mod tests { #![proptest_config(ProptestConfig::with_cases(1))] #[test] fn delegator_vote_happy_path(seed_phrase_randomness in any::<[u8; 32]>(), spend_auth_randomizer in fr_strategy(), value_amount in 1..2000000000u64, num_commitments in 0..2000u64) { - let (pk, vk) = DelegatorVoteCircuit::generate_prepared_test_parameters(); let mut rng = OsRng; + let (pk, vk) = generate_prepared_test_parameters::(&mut rng); let seed_phrase = SeedPhrase::from_randomness(&seed_phrase_randomness); let sk_sender = SpendKey::from_seed_phrase(seed_phrase, 0); @@ -448,8 +445,8 @@ mod tests { #[test] #[should_panic] fn delegator_vote_invalid_start_position(seed_phrase_randomness in any::<[u8; 32]>(), spend_auth_randomizer in fr_strategy(), value_amount in 1..2000000000u64, num_commitments in 1000..2000u64) { - let (pk, vk) = DelegatorVoteCircuit::generate_prepared_test_parameters(); let mut rng = OsRng; + let (pk, vk) = generate_prepared_test_parameters::(&mut rng); let seed_phrase = SeedPhrase::from_randomness(&seed_phrase_randomness); let sk_sender = SpendKey::from_seed_phrase(seed_phrase, 0); diff --git a/crates/core/component/shielded-pool/proptest-regressions/spend/proof.txt b/crates/core/component/shielded-pool/proptest-regressions/spend/proof.txt new file mode 100644 index 0000000000..f9149c41ea --- /dev/null +++ b/crates/core/component/shielded-pool/proptest-regressions/spend/proof.txt @@ -0,0 +1,7 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc 012a287e1f34e2196d0936bf83eebf318ed7e32b5770ca09fbf3c5a42532a7d9 # shrinks to seed_phrase_randomness = [0, 147, 120, 164, 112, 216, 214, 148, 86, 42, 58, 15, 58, 68, 159, 66, 185, 30, 143, 239, 67, 91, 135, 64, 3, 229, 245, 127, 95, 253, 45, 222], incorrect_seed_phrase_randomness = [2, 213, 138, 24, 182, 233, 46, 0, 14, 87, 160, 137, 130, 75, 91, 220, 38, 17, 219, 177, 185, 25, 15, 48, 127, 232, 65, 170, 107, 241, 10, 238], spend_auth_randomizer = BigInt([11157411907499961512, 3695312772271047997, 1336443116240752099, 89537063999159638]), value_amount = 57, v_blinding = BigInt([4043089063310689772, 3831438451173389786, 16670315344678707725, 206829689043777334]) diff --git a/crates/core/component/shielded-pool/src/nullifier_derivation.rs b/crates/core/component/shielded-pool/src/nullifier_derivation.rs index bc4f93d2be..51d53686eb 100644 --- a/crates/core/component/shielded-pool/src/nullifier_derivation.rs +++ b/crates/core/component/shielded-pool/src/nullifier_derivation.rs @@ -6,20 +6,19 @@ use decaf377::{Bls12_377, Fq}; use ark_ff::ToConstraintField; use ark_groth16::{ - r1cs_to_qap::LibsnarkReduction, Groth16, PreparedVerifyingKey, Proof, ProvingKey, VerifyingKey, + r1cs_to_qap::LibsnarkReduction, Groth16, PreparedVerifyingKey, Proof, ProvingKey, }; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef}; use ark_snark::SNARK; use penumbra_proto::{core::crypto::v1alpha1 as pb, DomainType, TypeUrl}; use penumbra_tct as tct; use rand::{CryptoRng, Rng}; -use rand_core::OsRng; use tct::StateCommitment; use crate::{Note, Rseed}; use penumbra_asset::Value; use penumbra_keys::keys::{NullifierKey, NullifierKeyVar, SeedPhrase, SpendKey}; -use penumbra_proof_params::{ParameterSetup, VerifyingKeyExt, GROTH16_PROOF_LENGTH_BYTES}; +use penumbra_proof_params::{DummyWitness, VerifyingKeyExt, GROTH16_PROOF_LENGTH_BYTES}; use penumbra_sct::{Nullifier, NullifierVar}; /// Groth16 proof for correct nullifier derivation. @@ -28,7 +27,6 @@ pub struct NullifierDerivationCircuit { // Witnesses /// The nullifier deriving key. nk: NullifierKey, - // Public inputs /// the position of the spent note. pub position: tct::Position, @@ -73,8 +71,8 @@ impl ConstraintSynthesizer for NullifierDerivationCircuit { } } -impl ParameterSetup for NullifierDerivationCircuit { - fn generate_test_parameters() -> (ProvingKey, VerifyingKey) { +impl DummyWitness for NullifierDerivationCircuit { + fn with_dummy_witness() -> Self { let seed_phrase = SeedPhrase::from_randomness(&[b'f'; 32]); let sk_sender = SpendKey::from_seed_phrase(seed_phrase, 0); let fvk_sender = sk_sender.full_viewing_key(); @@ -95,16 +93,12 @@ impl ParameterSetup for NullifierDerivationCircuit { let state_commitment_proof = sct.witness(note_commitment).unwrap(); let position = state_commitment_proof.position(); - let circuit = NullifierDerivationCircuit { + Self { note_commitment, nk, nullifier, position, - }; - let (pk, vk) = - Groth16::::circuit_specific_setup(circuit, &mut OsRng) - .expect("can perform circuit specific setup"); - (pk, vk) + } } } @@ -196,6 +190,7 @@ mod tests { use super::*; use penumbra_asset::{asset, Value}; use penumbra_keys::keys::{SeedPhrase, SpendKey}; + use penumbra_proof_params::generate_prepared_test_parameters; use penumbra_sct::Nullifier; use penumbra_tct as tct; use proptest::prelude::*; @@ -207,9 +202,9 @@ mod tests { #![proptest_config(ProptestConfig::with_cases(2))] #[test] fn nullifier_derivation_proof_happy_path(seed_phrase_randomness in any::<[u8; 32]>(), value_amount in 2..200u64) { - let (pk, vk) = NullifierDerivationCircuit::generate_prepared_test_parameters(); - let mut rng = OsRng; + let (pk, vk) = generate_prepared_test_parameters::(&mut rng); + let seed_phrase = SeedPhrase::from_randomness(&seed_phrase_randomness); let sk_sender = SpendKey::from_seed_phrase(seed_phrase, 0); diff --git a/crates/core/component/shielded-pool/src/output/proof.rs b/crates/core/component/shielded-pool/src/output/proof.rs index 9e27b9e3ab..d48f0d207c 100644 --- a/crates/core/component/shielded-pool/src/output/proof.rs +++ b/crates/core/component/shielded-pool/src/output/proof.rs @@ -10,14 +10,13 @@ use decaf377_fmd as fmd; use decaf377_ka as ka; use ark_ff::ToConstraintField; -use ark_groth16::{Groth16, PreparedVerifyingKey, Proof, ProvingKey, VerifyingKey}; +use ark_groth16::{Groth16, PreparedVerifyingKey, Proof, ProvingKey}; use ark_r1cs_std::prelude::*; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef}; use ark_snark::SNARK; use penumbra_keys::{keys::Diversifier, Address}; use penumbra_proto::{core::crypto::v1alpha1 as pb, DomainType, TypeUrl}; use penumbra_tct::r1cs::StateCommitmentVar; -use rand_core::OsRng; use crate::{note, Note, Rseed}; use penumbra_asset::{ @@ -25,7 +24,7 @@ use penumbra_asset::{ balance::{commitment::BalanceCommitmentVar, BalanceVar}, Value, }; -use penumbra_proof_params::{ParameterSetup, VerifyingKeyExt, GROTH16_PROOF_LENGTH_BYTES}; +use penumbra_proof_params::{DummyWitness, VerifyingKeyExt, GROTH16_PROOF_LENGTH_BYTES}; /// Public: /// * vcm (value commitment) @@ -94,8 +93,8 @@ impl ConstraintSynthesizer for OutputCircuit { } } -impl ParameterSetup for OutputCircuit { - fn generate_test_parameters() -> (ProvingKey, VerifyingKey) { +impl DummyWitness for OutputCircuit { + fn with_dummy_witness() -> Self { let diversifier_bytes = [1u8; 16]; let pk_d_bytes = decaf377::basepoint().vartime_compress().0; let clue_key_bytes = [1; 32]; @@ -113,16 +112,12 @@ impl ParameterSetup for OutputCircuit { ) .expect("can make a note"); let v_blinding = Fr::from(1); - let circuit = OutputCircuit { + OutputCircuit { note: note.clone(), note_commitment: note.commit(), v_blinding, balance_commitment: balance::Commitment(decaf377::basepoint()), - }; - let (pk, vk) = - Groth16::::circuit_specific_setup(circuit, &mut OsRng) - .expect("can perform circuit specific setup"); - (pk, vk) + } } } @@ -224,6 +219,7 @@ mod tests { use decaf377::{Fq, Fr}; use penumbra_asset::{asset, Balance, Value}; use penumbra_keys::keys::{SeedPhrase, SpendKey}; + use penumbra_proof_params::generate_prepared_test_parameters; use proptest::prelude::*; use penumbra_proto::core::crypto::v1alpha1 as pb; @@ -249,9 +245,9 @@ mod tests { #![proptest_config(ProptestConfig::with_cases(2))] #[test] fn output_proof_happy_path(seed_phrase_randomness in any::<[u8; 32]>(), v_blinding in fr_strategy(), value_amount in 2..200u64) { - let (pk, vk) = OutputCircuit::generate_prepared_test_parameters(); - let mut rng = OsRng; + let (pk, vk) = generate_prepared_test_parameters::(&mut rng); + let seed_phrase = SeedPhrase::from_randomness(&seed_phrase_randomness); let sk_recipient = SpendKey::from_seed_phrase(seed_phrase, 0); @@ -293,8 +289,8 @@ mod tests { #![proptest_config(ProptestConfig::with_cases(2))] #[test] fn output_proof_verification_note_commitment_integrity_failure(seed_phrase_randomness in any::<[u8; 32]>(), v_blinding in fr_strategy(), value_amount in 2..200u64, note_blinding in fq_strategy()) { - let (pk, vk) = OutputCircuit::generate_prepared_test_parameters(); let mut rng = OsRng; + let (pk, vk) = generate_prepared_test_parameters::(&mut rng); let seed_phrase = SeedPhrase::from_randomness(&seed_phrase_randomness); let sk_recipient = SpendKey::from_seed_phrase(seed_phrase, 0); @@ -342,8 +338,8 @@ mod tests { #![proptest_config(ProptestConfig::with_cases(2))] #[test] fn output_proof_verification_balance_commitment_integrity_failure(seed_phrase_randomness in any::<[u8; 32]>(), v_blinding in fr_strategy(), value_amount in 2..200u64, incorrect_v_blinding in fr_strategy()) { - let (pk, vk) = OutputCircuit::generate_prepared_test_parameters(); let mut rng = OsRng; + let (pk, vk) = generate_prepared_test_parameters::(&mut rng); let seed_phrase = SeedPhrase::from_randomness(&seed_phrase_randomness); let sk_recipient = SpendKey::from_seed_phrase(seed_phrase, 0); diff --git a/crates/core/component/shielded-pool/src/spend/proof.rs b/crates/core/component/shielded-pool/src/spend/proof.rs index 7d440b6b1d..3b73fa698f 100644 --- a/crates/core/component/shielded-pool/src/spend/proof.rs +++ b/crates/core/component/shielded-pool/src/spend/proof.rs @@ -11,7 +11,7 @@ use decaf377::{r1cs::FqVar, Bls12_377, Fq, Fr}; use ark_ff::ToConstraintField; use ark_groth16::{ - r1cs_to_qap::LibsnarkReduction, Groth16, PreparedVerifyingKey, Proof, ProvingKey, VerifyingKey, + r1cs_to_qap::LibsnarkReduction, Groth16, PreparedVerifyingKey, Proof, ProvingKey, }; use ark_r1cs_std::prelude::AllocVar; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef}; @@ -20,7 +20,6 @@ use decaf377_rdsa::{SpendAuth, VerificationKey}; use penumbra_proto::{core::crypto::v1alpha1 as pb, DomainType, TypeUrl}; use penumbra_tct as tct; use penumbra_tct::r1cs::StateCommitmentVar; -use rand_core::OsRng; use crate::{note, Note, Rseed}; use penumbra_asset::{balance, balance::commitment::BalanceCommitmentVar, Value}; @@ -28,7 +27,7 @@ use penumbra_keys::keys::{ AuthorizationKeyVar, IncomingViewingKeyVar, NullifierKey, NullifierKeyVar, RandomizedVerificationKey, SeedPhrase, SpendAuthRandomizerVar, SpendKey, }; -use penumbra_proof_params::{ParameterSetup, VerifyingKeyExt, GROTH16_PROOF_LENGTH_BYTES}; +use penumbra_proof_params::{DummyWitness, VerifyingKeyExt, GROTH16_PROOF_LENGTH_BYTES}; use penumbra_sct::{Nullifier, NullifierVar}; /// Groth16 proof for spending existing notes. @@ -169,8 +168,8 @@ impl ConstraintSynthesizer for SpendCircuit { } } -impl ParameterSetup for SpendCircuit { - fn generate_test_parameters() -> (ProvingKey, VerifyingKey) { +impl DummyWitness for SpendCircuit { + fn with_dummy_witness() -> Self { let seed_phrase = SeedPhrase::from_randomness(&[b'f'; 32]); let sk_sender = SpendKey::from_seed_phrase(seed_phrase, 0); let fvk_sender = sk_sender.full_viewing_key(); @@ -196,7 +195,7 @@ impl ParameterSetup for SpendCircuit { sct.insert(tct::Witness::Keep, note_commitment).unwrap(); let state_commitment_proof = sct.witness(note_commitment).unwrap(); - let circuit = SpendCircuit { + Self { state_commitment_proof, note, v_blinding, @@ -207,11 +206,7 @@ impl ParameterSetup for SpendCircuit { balance_commitment: balance::Commitment(decaf377::basepoint()), nullifier, rk, - }; - let (pk, vk) = - Groth16::::circuit_specific_setup(circuit, &mut OsRng) - .expect("can perform circuit specific setup"); - (pk, vk) + } } } @@ -332,6 +327,7 @@ mod tests { keys::{SeedPhrase, SpendKey}, Address, }; + use penumbra_proof_params::generate_prepared_test_parameters; use penumbra_sct::Nullifier; use penumbra_tct::StateCommitment; use proptest::prelude::*; @@ -355,8 +351,8 @@ mod tests { #[test] /// Check that the `SpendProof` verification succeeds. fn spend_proof_verification_success(seed_phrase_randomness in any::<[u8; 32]>(), spend_auth_randomizer in fr_strategy(), value_amount in 2..2000000000u64, num_commitments in 1..2000u64, v_blinding in fr_strategy()) { - let (pk, vk) = SpendCircuit::generate_prepared_test_parameters(); let mut rng = OsRng; + let (pk, vk) = generate_prepared_test_parameters::(&mut rng); let seed_phrase = SeedPhrase::from_randomness(&seed_phrase_randomness); let sk_sender = SpendKey::from_seed_phrase(seed_phrase, 0); @@ -420,8 +416,8 @@ mod tests { /// Check that the `SpendProof` verification fails when using an incorrect /// TCT root (`anchor`). fn spend_proof_verification_merkle_path_integrity_failure(seed_phrase_randomness in any::<[u8; 32]>(), spend_auth_randomizer in fr_strategy(), value_amount in 2..200u64, v_blinding in fr_strategy()) { - let (pk, vk) = SpendCircuit::generate_prepared_test_parameters(); let mut rng = OsRng; + let (pk, vk) = generate_prepared_test_parameters::(&mut rng); let seed_phrase = SeedPhrase::from_randomness(&seed_phrase_randomness); let sk_sender = SpendKey::from_seed_phrase(seed_phrase, 0); @@ -478,8 +474,8 @@ mod tests { #[test] /// Check that the `SpendProof` verification fails when the diversified address is wrong. fn spend_proof_verification_diversified_address_integrity_failure(seed_phrase_randomness in any::<[u8; 32]>(), incorrect_seed_phrase_randomness in any::<[u8; 32]>(), spend_auth_randomizer in fr_strategy(), value_amount in 2..200u64, v_blinding in fr_strategy()) { - let (pk, vk) = SpendCircuit::generate_prepared_test_parameters(); let mut rng = OsRng; + let (pk, vk) = generate_prepared_test_parameters::(&mut rng); let seed_phrase = SeedPhrase::from_randomness(&seed_phrase_randomness); let sk_sender = SpendKey::from_seed_phrase(seed_phrase, 0); @@ -540,8 +536,8 @@ mod tests { /// Check that the `SpendProof` verification fails, when using an /// incorrect nullifier. fn spend_proof_verification_nullifier_integrity_failure(seed_phrase_randomness in any::<[u8; 32]>(), spend_auth_randomizer in fr_strategy(), value_amount in 2..200u64, v_blinding in fr_strategy()) { - let (pk, vk) = SpendCircuit::generate_prepared_test_parameters(); let mut rng = OsRng; + let (pk, vk) = generate_prepared_test_parameters::(&mut rng); let seed_phrase = SeedPhrase::from_randomness(&seed_phrase_randomness); let sk_sender = SpendKey::from_seed_phrase(seed_phrase, 0); @@ -599,8 +595,8 @@ mod tests { /// Check that the `SpendProof` verification fails when using balance /// commitments with different blinding factors. fn spend_proof_verification_balance_commitment_integrity_failure(seed_phrase_randomness in any::<[u8; 32]>(), spend_auth_randomizer in fr_strategy(), value_amount in 2..200u64, v_blinding in fr_strategy(), incorrect_blinding_factor in fr_strategy()) { - let (pk, vk) = SpendCircuit::generate_prepared_test_parameters(); let mut rng = OsRng; + let (pk, vk) = generate_prepared_test_parameters::(&mut rng); let seed_phrase = SeedPhrase::from_randomness(&seed_phrase_randomness); let sk_sender = SpendKey::from_seed_phrase(seed_phrase, 0); @@ -657,8 +653,8 @@ mod tests { #[test] /// Check that the `SpendProof` verification fails when the incorrect randomizable verification key is used. fn spend_proof_verification_fails_rk_integrity(seed_phrase_randomness in any::<[u8; 32]>(), spend_auth_randomizer in fr_strategy(), value_amount in 2..200u64, v_blinding in fr_strategy(), incorrect_spend_auth_randomizer in fr_strategy()) { - let (pk, vk) = SpendCircuit::generate_prepared_test_parameters(); let mut rng = OsRng; + let (pk, vk) = generate_prepared_test_parameters::(&mut rng); let seed_phrase = SeedPhrase::from_randomness(&seed_phrase_randomness); let sk_sender = SpendKey::from_seed_phrase(seed_phrase, 0); @@ -718,8 +714,8 @@ mod tests { #[test] /// Check that the `SpendProof` verification always suceeds for dummy (zero value) spends. fn spend_proof_dummy_verification_suceeds(seed_phrase_randomness in any::<[u8; 32]>(), spend_auth_randomizer in fr_strategy(), v_blinding in fr_strategy()) { - let (pk, vk) = SpendCircuit::generate_prepared_test_parameters(); let mut rng = OsRng; + let (pk, vk) = generate_prepared_test_parameters::(&mut rng); let seed_phrase = SeedPhrase::from_randomness(&seed_phrase_randomness); let sk_sender = SpendKey::from_seed_phrase(seed_phrase, 0); @@ -823,8 +819,8 @@ mod tests { } } - impl ParameterSetup for MerkleProofCircuit { - fn generate_test_parameters() -> (ProvingKey, VerifyingKey) { + impl DummyWitness for MerkleProofCircuit { + fn with_dummy_witness() -> Self { let seed_phrase = SeedPhrase::from_randomness(&[b'f'; 32]); let sk_sender = SpendKey::from_seed_phrase(seed_phrase, 0); let fvk_sender = sk_sender.full_viewing_key(); @@ -846,18 +842,14 @@ mod tests { let epoch = Fq::from(position.epoch()); let block = Fq::from(position.block()); let commitment_index = Fq::from(position.commitment()); - let circuit = MerkleProofCircuit { + + Self { state_commitment_proof, anchor, epoch, block, commitment_index, - }; - let (pk, vk) = Groth16::::circuit_specific_setup( - circuit, &mut OsRng, - ) - .expect("can perform circuit specific setup"); - (pk, vk) + } } } @@ -873,8 +865,8 @@ mod tests { #[test] fn merkle_proof_verification_succeeds() { - let (pk, vk) = MerkleProofCircuit::generate_prepared_test_parameters(); let mut rng = OsRng; + let (pk, vk) = generate_prepared_test_parameters::(&mut rng); let seed_phrase = SeedPhrase::from_randomness(&[b'f'; 32]); let sk_sender = SpendKey::from_seed_phrase(seed_phrase, 0); diff --git a/crates/core/component/stake/src/undelegate_claim/proof.rs b/crates/core/component/stake/src/undelegate_claim/proof.rs index 82e05fef89..43ce23e584 100644 --- a/crates/core/component/stake/src/undelegate_claim/proof.rs +++ b/crates/core/component/stake/src/undelegate_claim/proof.rs @@ -4,12 +4,11 @@ use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use decaf377::Bls12_377; use ark_ff::ToConstraintField; -use ark_groth16::{Groth16, PreparedVerifyingKey, Proof, ProvingKey, VerifyingKey}; +use ark_groth16::{Groth16, PreparedVerifyingKey, Proof, ProvingKey}; use ark_r1cs_std::prelude::*; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef}; use ark_snark::SNARK; use penumbra_proto::{core::crypto::v1alpha1 as pb, DomainType, TypeUrl}; -use rand_core::OsRng; use decaf377::{FieldExt, Fq, Fr}; use penumbra_asset::{ @@ -17,7 +16,7 @@ use penumbra_asset::{ STAKING_TOKEN_ASSET_ID, }; use penumbra_num::{Amount, AmountVar}; -use penumbra_proof_params::{ParameterSetup, VerifyingKeyExt, GROTH16_PROOF_LENGTH_BYTES}; +use penumbra_proof_params::{DummyWitness, VerifyingKeyExt, GROTH16_PROOF_LENGTH_BYTES}; use crate::{Penalty, PenaltyVar}; @@ -71,8 +70,8 @@ impl ConstraintSynthesizer for UndelegateClaimCircuit { } } -impl ParameterSetup for UndelegateClaimCircuit { - fn generate_test_parameters() -> (ProvingKey, VerifyingKey) { +impl DummyWitness for UndelegateClaimCircuit { + fn with_dummy_witness() -> Self { let penalty = Penalty(1); let balance_blinding = Fr::from(1); let unbonding_amount = Amount::from(1u64); @@ -83,17 +82,13 @@ impl ParameterSetup for UndelegateClaimCircuit { }) .commit(balance_blinding); - let circuit = UndelegateClaimCircuit { + Self { penalty, unbonding_amount, balance_blinding, balance_commitment, unbonding_id, - }; - let (pk, vk) = - Groth16::::circuit_specific_setup(circuit, &mut OsRng) - .expect("can perform circuit specific setup"); - (pk, vk) + } } } @@ -193,6 +188,7 @@ mod tests { use decaf377::{Fq, Fr}; use decaf377_rdsa as rdsa; use penumbra_num::Amount; + use penumbra_proof_params::generate_prepared_test_parameters; use proptest::prelude::*; use rand_core::OsRng; @@ -208,7 +204,8 @@ mod tests { #![proptest_config(ProptestConfig::with_cases(2))] #[test] fn undelegate_claim_proof_happy_path(validator_randomness in fr_strategy(), balance_blinding in fr_strategy(), value1_amount in 2..200u64, penalty_amount in 0..200u64) { - let (pk, vk) = UndelegateClaimCircuit::generate_prepared_test_parameters(); + let mut rng = OsRng; + let (pk, vk) = generate_prepared_test_parameters::(&mut rng); let sk = rdsa::SigningKey::new_from_field(validator_randomness); let validator_identity = IdentityKey((&sk).into()); @@ -221,8 +218,8 @@ mod tests { let balance = penalty.balance_for_claim(unbonding_id, unbonding_amount); let balance_commitment = balance.commit(balance_blinding); - let blinding_r = Fq::rand(&mut OsRng); - let blinding_s = Fq::rand(&mut OsRng); + let blinding_r = Fq::rand(&mut rng); + let blinding_s = Fq::rand(&mut rng); let proof = UndelegateClaimProof::prove( blinding_r, blinding_s, diff --git a/crates/core/num/proptest-regressions/fixpoint.txt b/crates/core/num/proptest-regressions/fixpoint.txt new file mode 100644 index 0000000000..a4e44ef9d9 --- /dev/null +++ b/crates/core/num/proptest-regressions/fixpoint.txt @@ -0,0 +1,8 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc 0da3090b86c70239cd477d1c4bf283de9e7bc359dc842467d5f2028d938016ea # shrinks to a_int = 297204749142505869655703100272241 +cc dbfe8bcd7a883aaee5dffa9f9e81380f7aa04eeaccbd4171df1c4498d43c91fc # shrinks to c_int = 10327655905594138128345035477989 diff --git a/crates/crypto/proof-params/Cargo.toml b/crates/crypto/proof-params/Cargo.toml index af777b1aa2..fc23f344c5 100644 --- a/crates/crypto/proof-params/Cargo.toml +++ b/crates/crypto/proof-params/Cargo.toml @@ -15,6 +15,7 @@ penumbra-keys = { path = "../../core/keys/" } decaf377 = { version = "0.5", features = ["r1cs"] } # Crates.io deps +ark-ec = "0.4.2" ark-ff = {version = "0.4", default-features = false} ark-std = {version = "0.4", default-features = false} ark-serialize = "0.4" diff --git a/crates/crypto/proof-params/src/gen/delegator_vote_id.rs b/crates/crypto/proof-params/src/gen/delegator_vote_id.rs index eb971f8116..e45c687f7a 100644 --- a/crates/crypto/proof-params/src/gen/delegator_vote_id.rs +++ b/crates/crypto/proof-params/src/gen/delegator_vote_id.rs @@ -1,3 +1,3 @@ -pub const PROVING_KEY_ID: &'static str = "groth16pk1h3y7ezzulg5v3vxnkzegtkuzvgw7z8gpql6hyadf3mq97jn92a3sa9w0l8"; -pub const VERIFICATION_KEY_ID: &'static str = "groth16vk1jffc7gpnqchvrzyc3lz58gax7ct6272f92sen46kn8gwsa2qjevsukf9y5"; +pub const PROVING_KEY_ID: &'static str = "groth16pk1m7jhhvthvatj6t8fze30l7ukatkrcmz4z27mjtepsh4tua8h7xdsw4x6e2"; +pub const VERIFICATION_KEY_ID: &'static str = "groth16vk1thsgrgd8n272q2q2e5qx64gas2nwk448scvah5jggaaxess6fe4qjdags6"; diff --git a/crates/crypto/proof-params/src/gen/delegator_vote_pk.bin b/crates/crypto/proof-params/src/gen/delegator_vote_pk.bin index ad04f2a8a0..53ca8e0664 100644 --- a/crates/crypto/proof-params/src/gen/delegator_vote_pk.bin +++ b/crates/crypto/proof-params/src/gen/delegator_vote_pk.bin @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2cec9320e5f97f51e0aa79f40d8fb9d1602f9e078c2c5f64bf286d78903a4b0a +oid sha256:3c297ca537b7c2b87628c21c60e3516e835fdbb2ae58ba4889569b548e36a2fb size 21943632 diff --git a/crates/crypto/proof-params/src/gen/delegator_vote_vk.param b/crates/crypto/proof-params/src/gen/delegator_vote_vk.param index 4fc6b81177..66f263d3d0 100644 Binary files a/crates/crypto/proof-params/src/gen/delegator_vote_vk.param and b/crates/crypto/proof-params/src/gen/delegator_vote_vk.param differ diff --git a/crates/crypto/proof-params/src/gen/nullifier_derivation_id.rs b/crates/crypto/proof-params/src/gen/nullifier_derivation_id.rs index 1aed81de4d..5a7d71cbba 100644 --- a/crates/crypto/proof-params/src/gen/nullifier_derivation_id.rs +++ b/crates/crypto/proof-params/src/gen/nullifier_derivation_id.rs @@ -1,3 +1,3 @@ -pub const PROVING_KEY_ID: &'static str = "groth16pk1h373jzuvnxqf20fz9pn34kmptcfrpr2h079aufpqfjtus8fj7vcqv9u2ek"; -pub const VERIFICATION_KEY_ID: &'static str = "groth16vk1vrg23xpeq2psf00qjp06eu278pjzrnzrf54jswv8qfz0fqnua67qae4hkh"; +pub const PROVING_KEY_ID: &'static str = "groth16pk1p6c5s0adrld6f7u5rfnng6qqeuyzag8evukxu7ygww9jq4cspvxstcc4fq"; +pub const VERIFICATION_KEY_ID: &'static str = "groth16vk15v4wmq7lm2jf9tgg5u2p45qur8f3krwa7t59459gf4grzwrun96qa84cce"; diff --git a/crates/crypto/proof-params/src/gen/nullifier_derivation_pk.bin b/crates/crypto/proof-params/src/gen/nullifier_derivation_pk.bin index 5a55bdf497..cfccee0877 100644 --- a/crates/crypto/proof-params/src/gen/nullifier_derivation_pk.bin +++ b/crates/crypto/proof-params/src/gen/nullifier_derivation_pk.bin @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:88093928aee2eaa79a7b42872777372e0766bbf090ae296f00283ab1c2cbbb1b +oid sha256:8ae9468b9bc810831a2c79ed22d4380313b1b08415e5290ed71623afef30fa30 size 232368 diff --git a/crates/crypto/proof-params/src/gen/nullifier_derivation_vk.param b/crates/crypto/proof-params/src/gen/nullifier_derivation_vk.param index 020865656c..8d3ede1842 100644 Binary files a/crates/crypto/proof-params/src/gen/nullifier_derivation_vk.param and b/crates/crypto/proof-params/src/gen/nullifier_derivation_vk.param differ diff --git a/crates/crypto/proof-params/src/gen/output_id.rs b/crates/crypto/proof-params/src/gen/output_id.rs index b8c634ca31..3d9f0c3e06 100644 --- a/crates/crypto/proof-params/src/gen/output_id.rs +++ b/crates/crypto/proof-params/src/gen/output_id.rs @@ -1,3 +1,3 @@ -pub const PROVING_KEY_ID: &'static str = "groth16pk1v4m2v4hzxxhg5lvjkyxs8ak5tv5tvx7m3qdklq9snahf0pty5uts6fnwge"; -pub const VERIFICATION_KEY_ID: &'static str = "groth16vk1cfrw355f98tsfjy7msagxw9rrx488ume80q7adsatzadsmjuj3ds38rzq3"; +pub const PROVING_KEY_ID: &'static str = "groth16pk19t564cp86lwumxj3tlpy8cvws625mgc7zsvjql3q4xp0xqyqjsysrx58qp"; +pub const VERIFICATION_KEY_ID: &'static str = "groth16vk1l3l83h35nqwxt03wnfxvu8h0e6cf5d9ryd7agf6jelzxd54zwlqs52rqnp"; diff --git a/crates/crypto/proof-params/src/gen/output_pk.bin b/crates/crypto/proof-params/src/gen/output_pk.bin index 593cdb254c..504c50e086 100644 --- a/crates/crypto/proof-params/src/gen/output_pk.bin +++ b/crates/crypto/proof-params/src/gen/output_pk.bin @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c43179a34eba46357499f7c0a331964f3e9ce013157e2d463ea50988dbc675df +oid sha256:0cc4fc086d2b9cd28776e4327b32de08dfd3830bc7159fe7c4479405302a20a7 size 7257360 diff --git a/crates/crypto/proof-params/src/gen/output_vk.param b/crates/crypto/proof-params/src/gen/output_vk.param index df47cd7b59..7364af70ea 100644 Binary files a/crates/crypto/proof-params/src/gen/output_vk.param and b/crates/crypto/proof-params/src/gen/output_vk.param differ diff --git a/crates/crypto/proof-params/src/gen/spend_id.rs b/crates/crypto/proof-params/src/gen/spend_id.rs index 1893f5b9fd..937a591c16 100644 --- a/crates/crypto/proof-params/src/gen/spend_id.rs +++ b/crates/crypto/proof-params/src/gen/spend_id.rs @@ -1,3 +1,3 @@ -pub const PROVING_KEY_ID: &'static str = "groth16pk133vhjxu6cu5xwd239fa2gjyk6v0s82p527kd5n8x3wpjjhaf4g3sypwhf0"; -pub const VERIFICATION_KEY_ID: &'static str = "groth16vk1haf7gc88ynzlt96tfmkwt7yj8yvuz72artw42d2a40vramvtjuaq5yw53j"; +pub const PROVING_KEY_ID: &'static str = "groth16pk1gtyrkevnutrsk7lp087xfpr0jypa36rfq5g3pu6xp40vs3yee2pqxmrsal"; +pub const VERIFICATION_KEY_ID: &'static str = "groth16vk1zg535r8f4k4lmd272f5yx3emacumvjftdxf5twex6f5gnv6yuz5qd6cu6j"; diff --git a/crates/crypto/proof-params/src/gen/spend_pk.bin b/crates/crypto/proof-params/src/gen/spend_pk.bin index 19679616db..1601562005 100644 --- a/crates/crypto/proof-params/src/gen/spend_pk.bin +++ b/crates/crypto/proof-params/src/gen/spend_pk.bin @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:02df7b48c991a9b9dd0493db057fd3d90236443d9d993850bbd4ac85cd955074 +oid sha256:d1b1bac611b6394731c46fda860b1db54e2b4efdc374e94fe70b08036dfbf060 size 21187632 diff --git a/crates/crypto/proof-params/src/gen/spend_vk.param b/crates/crypto/proof-params/src/gen/spend_vk.param index 279ebbc7f6..264059ff83 100644 Binary files a/crates/crypto/proof-params/src/gen/spend_vk.param and b/crates/crypto/proof-params/src/gen/spend_vk.param differ diff --git a/crates/crypto/proof-params/src/gen/swap_id.rs b/crates/crypto/proof-params/src/gen/swap_id.rs index 5dcb89e5a8..523d8c6049 100644 --- a/crates/crypto/proof-params/src/gen/swap_id.rs +++ b/crates/crypto/proof-params/src/gen/swap_id.rs @@ -1,3 +1,3 @@ -pub const PROVING_KEY_ID: &'static str = "groth16pk1l5fhnhuqfgyw8qf2yt9hrl0m6ywpz4xuydrqh40yg2wjzvz9kdqqjl0ndn"; -pub const VERIFICATION_KEY_ID: &'static str = "groth16vk1ecjxhxd5tfk8wzjc3cxh9lvztldh987z8nahrlrvygqmql4mrsnq96w3my"; +pub const PROVING_KEY_ID: &'static str = "groth16pk1d0vee4rvjqzshqmm0wr43hgvh8gkhuk8k39rtz2c8lf6rq8wlqqqzf6sxj"; +pub const VERIFICATION_KEY_ID: &'static str = "groth16vk1qpt68l5cxgldttu9yyly53f6rwfw22ghsnnt6ngr7r85fs882u4s7gfamd"; diff --git a/crates/crypto/proof-params/src/gen/swap_pk.bin b/crates/crypto/proof-params/src/gen/swap_pk.bin index 8e2f0257f4..dd77f90a1b 100644 --- a/crates/crypto/proof-params/src/gen/swap_pk.bin +++ b/crates/crypto/proof-params/src/gen/swap_pk.bin @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0879eb02ecd31853f212e7fed6af45c0319c47c8aeefbae73f875e406b1d68f9 +oid sha256:e53ccd620a5b37655f8d8ff36bae482bcc672e76119591c3659e9db4c1286f24 size 14019984 diff --git a/crates/crypto/proof-params/src/gen/swap_vk.param b/crates/crypto/proof-params/src/gen/swap_vk.param index 7919fda36d..6cfe0b508b 100644 Binary files a/crates/crypto/proof-params/src/gen/swap_vk.param and b/crates/crypto/proof-params/src/gen/swap_vk.param differ diff --git a/crates/crypto/proof-params/src/gen/swapclaim_id.rs b/crates/crypto/proof-params/src/gen/swapclaim_id.rs index e0dbf12ead..d0811bf423 100644 --- a/crates/crypto/proof-params/src/gen/swapclaim_id.rs +++ b/crates/crypto/proof-params/src/gen/swapclaim_id.rs @@ -1,3 +1,3 @@ -pub const PROVING_KEY_ID: &'static str = "groth16pk144df93s6sx45hazc9kscepc57ryg3dsu8x5zmwjuukjwqm0nerfqgm6fn6"; -pub const VERIFICATION_KEY_ID: &'static str = "groth16vk10acmaq4vgvc2tssxdrz0zcu8ml32pr82dznayfkf4ure5q8el3lsxwkfhd"; +pub const PROVING_KEY_ID: &'static str = "groth16pk1hzet7nat3x5sqefnjr9ejj5kf3kwvnmal53dx54vfsfz09u6u4hqdaxh0w"; +pub const VERIFICATION_KEY_ID: &'static str = "groth16vk170tsjdyg289uelph6aup72hlc6zypmqhqm50hvn2xjmw0jfpxa0sd48sne"; diff --git a/crates/crypto/proof-params/src/gen/swapclaim_pk.bin b/crates/crypto/proof-params/src/gen/swapclaim_pk.bin index aef512c386..b221c65694 100644 --- a/crates/crypto/proof-params/src/gen/swapclaim_pk.bin +++ b/crates/crypto/proof-params/src/gen/swapclaim_pk.bin @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f37401d71f5558b2ca2fe6f60ea51622cbb8ebb3c484cde95c08830388aaf08c +oid sha256:5448a0edf41913848f5510f80c0dbac4c7a07bec86f4684c729234c2f8ed9b60 size 22569072 diff --git a/crates/crypto/proof-params/src/gen/swapclaim_vk.param b/crates/crypto/proof-params/src/gen/swapclaim_vk.param index 01b5831808..6533c97281 100644 Binary files a/crates/crypto/proof-params/src/gen/swapclaim_vk.param and b/crates/crypto/proof-params/src/gen/swapclaim_vk.param differ diff --git a/crates/crypto/proof-params/src/gen/undelegateclaim_id.rs b/crates/crypto/proof-params/src/gen/undelegateclaim_id.rs index eb771ebacf..987f034668 100644 --- a/crates/crypto/proof-params/src/gen/undelegateclaim_id.rs +++ b/crates/crypto/proof-params/src/gen/undelegateclaim_id.rs @@ -1,3 +1,3 @@ -pub const PROVING_KEY_ID: &'static str = "groth16pk1qg3prfk6ythey0l6r46jvnpql4w7f86t448wyj0latzqkhl4steqyrsdsj"; -pub const VERIFICATION_KEY_ID: &'static str = "groth16vk1gqf4kfh8d9k6s5kakxj48j3r7jxexzv0l0alr8srver25xl6ef0qa3mngh"; +pub const PROVING_KEY_ID: &'static str = "groth16pk13ujm9ju0dqn73pwsfexmxwdwtqmjfycvpj0dpqt4cc64sc2jw8ksrnqqrm"; +pub const VERIFICATION_KEY_ID: &'static str = "groth16vk1cn0geask0dt6vvd22vjalekgxuxfuh36q9sncnaxw70xg3rn47zqy9lxth"; diff --git a/crates/crypto/proof-params/src/gen/undelegateclaim_pk.bin b/crates/crypto/proof-params/src/gen/undelegateclaim_pk.bin index 5f0ba29714..73a5323d60 100644 --- a/crates/crypto/proof-params/src/gen/undelegateclaim_pk.bin +++ b/crates/crypto/proof-params/src/gen/undelegateclaim_pk.bin @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:10b777ebbf34ceaa1c3be1c38326d343b77a4704435a6ed6882f72bf9bf6c86b +oid sha256:f1cb5474ff3edc0fb1d1a7da794900ceddd286edb44af4da3fb401d8ef28926e size 8015280 diff --git a/crates/crypto/proof-params/src/gen/undelegateclaim_vk.param b/crates/crypto/proof-params/src/gen/undelegateclaim_vk.param index 2c4600dc3c..fcfeb90333 100644 Binary files a/crates/crypto/proof-params/src/gen/undelegateclaim_vk.param and b/crates/crypto/proof-params/src/gen/undelegateclaim_vk.param differ diff --git a/crates/crypto/proof-params/src/lib.rs b/crates/crypto/proof-params/src/lib.rs index af1c9eaa32..978144183e 100644 --- a/crates/crypto/proof-params/src/lib.rs +++ b/crates/crypto/proof-params/src/lib.rs @@ -8,7 +8,10 @@ pub const GROTH16_PROOF_LENGTH_BYTES: usize = 192; mod traits; -pub use traits::{ParameterSetup, ProvingKeyExt, VerifyingKeyExt}; +pub use traits::{ + generate_constraint_matrices, generate_prepared_test_parameters, generate_test_parameters, + DummyWitness, ProvingKeyExt, VerifyingKeyExt, +}; #[cfg(feature = "proving-keys")] mod proving_keys; diff --git a/crates/crypto/proof-params/src/traits.rs b/crates/crypto/proof-params/src/traits.rs index aa30b2a88f..58e19ff818 100644 --- a/crates/crypto/proof-params/src/traits.rs +++ b/crates/crypto/proof-params/src/traits.rs @@ -1,16 +1,70 @@ -use ark_groth16::{PreparedVerifyingKey, ProvingKey, VerifyingKey}; +use ark_ec::pairing::Pairing; +use ark_groth16::{ + r1cs_to_qap::LibsnarkReduction, Groth16, PreparedVerifyingKey, ProvingKey, VerifyingKey, +}; +use ark_relations::r1cs::{self, ConstraintMatrices, ConstraintSynthesizer}; use ark_serialize::CanonicalSerialize; +use ark_snark::SNARK; use decaf377::Bls12_377; +use rand_core::CryptoRngCore; -/// Must be implemented to generate proving and verification keys for a circuit. -pub trait ParameterSetup { - fn generate_test_parameters() -> (ProvingKey, VerifyingKey); +/// This trait characterizes circuits which can generate constraints. +pub trait DummyWitness: ConstraintSynthesizer<::ScalarField> { + /// This will create a circuit with dummy witness values, for constraint synthesis + /// + /// (The reason this is needed is because constraint synthesis encapsulates both the act + /// of generating the constraints, but also that of providing the witness values when proving). + /// ((For the record, I am not a fan of this)). + fn with_dummy_witness() -> Self; +} - fn generate_prepared_test_parameters( - ) -> (ProvingKey, PreparedVerifyingKey) { - let (pk, vk) = Self::generate_test_parameters(); - (pk, vk.into()) - } +/// Generate constraint matrices from a circuit type. +/// +/// This is useful because it provides a way to get the actual constraints +/// associated with some circuit, without actually generating a proving key via a trusted setup. +/// This is what you need for doing a setup ceremony, among other things. +pub fn generate_constraint_matrices( +) -> ConstraintMatrices<::ScalarField> { + let circuit = T::with_dummy_witness(); + + let cs = r1cs::ConstraintSystem::new_ref(); + cs.set_optimization_goal(r1cs::OptimizationGoal::Constraints); + cs.set_mode(r1cs::SynthesisMode::Setup); + // For why this is ok, see `generate_test_parameters`. + circuit + .generate_constraints(cs.clone()) + .expect("can generate constraints from circuit"); + cs.finalize(); + + // I honestly don't know why this would fail. + // But if it does, it's not at runtime in a node. + cs.to_matrices() + .expect("can convert R1CS constraints into matrices") +} + +/// Generate parameters for proving and verifying, for *tests*. +/// +/// These parameters should not be used for actual production code, +/// because the randomness may not have been securely destroyed. +pub fn generate_test_parameters( + rng: &mut impl CryptoRngCore, +) -> (ProvingKey, VerifyingKey) { + let circuit = T::with_dummy_witness(); + + // Unwrapping here is ok because: + // 1. This code is not run in node software at run time (or shouldn't be) + // 2. If this fails, there's a bug in one of our circuits (which is bad) + Groth16::::circuit_specific_setup(circuit, rng) + .expect("can generate constraints from circuit") +} + +/// A variant of `generate_test_parameters` which spits out a verifying key with some +/// precomputation. +pub fn generate_prepared_test_parameters( + rng: &mut impl CryptoRngCore, +) -> (ProvingKey, PreparedVerifyingKey) { + let (pk, vk) = generate_test_parameters::(rng); + (pk, vk.into()) } pub trait VerifyingKeyExt { diff --git a/crates/crypto/proof-setup/Cargo.toml b/crates/crypto/proof-setup/Cargo.toml index f7b81794cd..11775ad768 100644 --- a/crates/crypto/proof-setup/Cargo.toml +++ b/crates/crypto/proof-setup/Cargo.toml @@ -4,15 +4,22 @@ version = "0.59.0" edition = "2021" [dependencies] +anyhow = "1.0" ark-ec = "0.4.2" ark-ff = "0.4.2" +ark-groth16 = "0.4.0" +ark-poly = "0.4.2" +ark-relations = "0.4" ark-serialize = "0.4.2" blake2b_simd = "0.5" rand_core = { version = "0.6", features = ["getrandom"] } decaf377 = "0.5" [dev-dependencies] +ark-r1cs-std = "0.4.0" +ark-snark = "0.4.0" criterion = { version = "0.4", features = ["html_reports"] } +rand_chacha = "0.3.1" [[bench]] name = "phase1" diff --git a/crates/crypto/proof-setup/benches/phase1.rs b/crates/crypto/proof-setup/benches/phase1.rs index a7929c71db..4695883684 100644 --- a/crates/crypto/proof-setup/benches/phase1.rs +++ b/crates/crypto/proof-setup/benches/phase1.rs @@ -3,14 +3,14 @@ use rand_core::OsRng; use penumbra_proof_setup::{ log::{ContributionHash, Hashable}, - phase1::{CRSElements, Contribution, RawContribution}, + {Phase1CRSElements, Phase1Contribution, Phase1RawContribution}, }; -fn run_phase1_prove(parent: ContributionHash, old: &CRSElements) -> Contribution { - Contribution::make(&mut OsRng, parent, old) +fn run_phase1_prove(parent: ContributionHash, old: &Phase1CRSElements) -> Phase1Contribution { + Phase1Contribution::make(&mut OsRng, parent, old) } -fn run_phase1_verify(contribution: RawContribution, parent: &CRSElements) { +fn run_phase1_verify(contribution: Phase1RawContribution, parent: &Phase1CRSElements) { let validated_contribution = contribution .validate(&mut OsRng) .expect("this is a valid contribution"); @@ -20,15 +20,15 @@ fn run_phase1_verify(contribution: RawContribution, parent: &CRSElements) { fn phase1_benchmarks(c: &mut Criterion) { // Generate contribution for degree = 37,061 // (size of largest proof) - let d = 37_655; - let root = CRSElements::root(d); + let d = 1 << 16; + let root = Phase1CRSElements::root(d); let root_hash = root.hash(); c.bench_function("phase 1 prove", |b| { b.iter(|| run_phase1_prove(root_hash, &root)) }); - let new_contribution = Contribution::make(&mut OsRng, root_hash, &root); + let new_contribution = Phase1Contribution::make(&mut OsRng, root_hash, &root); c.bench_function("phase 1 verify", |b| { b.iter(|| run_phase1_verify(new_contribution.clone().into(), &root)) }); diff --git a/crates/crypto/proof-setup/src/group.rs b/crates/crypto/proof-setup/src/group.rs index 874ef5cff8..1a44bea798 100644 --- a/crates/crypto/proof-setup/src/group.rs +++ b/crates/crypto/proof-setup/src/group.rs @@ -112,10 +112,10 @@ make_batched_pairing_checker!( ); /// The size of the hash we use. -pub const HASH_SIZE: usize = 32; +pub(crate) const HASH_SIZE: usize = 32; /// The hash output we use when we need bytes. -pub type Hash = [u8; 32]; +pub(crate) type Hash = [u8; 32]; /// A utility struct for hashing group elements and producing fields. /// @@ -126,7 +126,7 @@ pub type Hash = [u8; 32]; /// of this elements. One place where you still need manual effort on the user's end /// is when you're hashing a variable number of elements. #[derive(Clone)] -pub struct GroupHasher { +pub(crate) struct GroupHasher { state: blake2b_simd::State, } diff --git a/crates/crypto/proof-setup/src/lib.rs b/crates/crypto/proof-setup/src/lib.rs index 3c9c34a09a..1c400995d4 100644 --- a/crates/crypto/proof-setup/src/lib.rs +++ b/crates/crypto/proof-setup/src/lib.rs @@ -2,5 +2,371 @@ mod dlog; mod group; pub mod log; -pub mod phase1; -pub mod phase2; +mod phase1; +mod phase2; + +use ark_ec::{CurveGroup, Group}; +use ark_ff::Zero; +use ark_groth16::data_structures::ProvingKey; +use ark_groth16::VerifyingKey; +use ark_poly::EvaluationDomain; +use ark_poly::Radix2EvaluationDomain; +use ark_relations::r1cs::ConstraintMatrices; + +use decaf377::Bls12_377; + +pub use phase1::CRSElements as Phase1CRSElements; +pub use phase1::Contribution as Phase1Contribution; +pub use phase1::RawContribution as Phase1RawContribution; + +pub use phase2::CRSElements as Phase2CRSElements; +pub use phase2::Contribution as Phase2Contribution; +pub use phase2::RawContribution as Phase2RawContribution; + +use group::{F, G1, G2}; + +use anyhow::{anyhow, Result}; + +pub struct ExtraTransitionInformation { + /// The u polynomials evaluated at [x]. + u_1: Vec, + /// The v polynomials evaluted at [x]. + v_1: Vec, + /// The v polynomials evaluted at [x]_2. + v_2: Vec, + /// The p polynomials evaluated at [x] + p_1: Vec, +} + +// Compute the degree associated with a given circuit. +// +// This degree can then be used for both phases. +pub fn circuit_degree(circuit: &ConstraintMatrices) -> Result { + let circuit_size = circuit.num_constraints + circuit.num_instance_variables; + Radix2EvaluationDomain::::compute_size_of_domain(circuit_size) + .ok_or(anyhow!("Circuit of size {} is too large", circuit_size)) +} + +/// Transition between phase1 and phase2, using the circuit. +/// +/// This will also produce extra elements needed later when combining the elements +/// into the actual proving key. +pub fn transition( + phase1: &phase1::CRSElements, + circuit: &ConstraintMatrices, +) -> Result<(ExtraTransitionInformation, phase2::CRSElements)> { + // To understand this function, it can be good to recap the relationship between + // R1CS constraints and QAP constraints. + // + // While we call the constaints a "circuit", they're really an R1CS system. + // The circuit contains matrices A, B, C. Each of these matrices has the same size. + // The number of columns is the number of variables in our circuit, + // along with an additional column for each constraint, representing an + // "internal variable". + // The number of rows is simply the number of constraints in our circuit. + // + // To transform the circuit into a QAP, each column of a matrix becomes a polynomial. + // For the matrix A, we have uᵢ(X), for B, vᵢ(X), and C, wᵢ(X). + // We also have a domain, a list of field elements, such that evaluting each polynomial + // on the domain produces the entries of the corresponding matrix column. + // + // Furthermore, we also pad the matrices before this transformation. + // The bottom of A is filled with: + // 1 0 0 ... + // 0 1 0 ... + // 0 0 1 ... + // ......... + // based on the number of instance variables. This is to avoid + // potential malleability issues. See: https://geometry.xyz/notebook/groth16-malleability. + // B and C are simply padded with 0 to match this size. + // + // Finally, the domain might be larger than the size of these matrices, + // and so we simply add rows filled with 0 to match the size of the domain. + // + // Now, we don't need to calculate these polynomials directly, instead what we care about + // is the polynomials pᵢ(X) = β uᵢ(X) + α vᵢ(X) + wᵢ(X), evaluated at [x], + // and the evaluations [t(x)xⁱ], where t is a polynomial that's 0 everywhere over the + // domain. + // + // Our strategy here is to make use of lagrange polynomials Lⱼ(X), which + // are 0 everywhere over the domain except for the jth entry. + // Specifically, if we have the evaluations Lⱼ([x]), we can then calculate + // each pᵢ([x]) by a linear combination over these evaluations, + // using the coefficients in that matrix column. + // Note that the matrices are very sparse, so there are only a linear + // number of such operations to do, instead of a quadratic number. + // + // For calculating the lagrange polynomials, we want to avoid a quadratic + // solution. To do so, we need to exploit the structure of the domain + // we use: specifically, the fact that we can do fast fourier transforms (FFTs) + // in it. (See section 3 of https://eprint.iacr.org/2017/602 for the origin + // of most of this exposition). + // + // Our domain consists of successive powers of a root of unity: 1, ω, ω², ..., + // such that ωᵈ = 1. The number of entries in the domain is d, in total. + // This is useful, because we can quickly convert a polynomial p(X), represented + // as a table of coefficients, into an evaluation table p(ωⁱ) (i ∈ [d]). + // + // We can also use this for evaluating the lagrange polynomials. + // First, note the following equality: + // Lⱼ(X) = d⁻¹∑ᵢ (Xω⁻ʲ)ⁱ + // (because the sum over all roots of unity is 0). + // Defining: + // Pₓ(Y) := d⁻¹∑ᵢ xⁱ Yⁱ + // we can write Lⱼ(x) = Lⱼ(x) = Pₓ(ω⁻ʲ). + // + // What we want is [Lⱼ(x)] for each j. With this equality, we see that we + // can get this by doing an FFT over Pₓ(Y). This will also work even if + // the coefficients are group elements, and not scalars. + // (We also need to reverse the order of the resulting FFT). + // + // This is also equivalent to doing an inverse FFT, and not including + // the inverse of d in the polynomial. + // + // The structure of the domain also helps us with the vanishing polynomial, t. + // The polynomial (Xᵈ − 1) is 0 everywhere over the domain, and give us + // a simple expression for t(X). The evaluations [t(x)xⁱ] can then be obtained + // by using simple indexing. + // + // Ok, that was the explanation, now onto the code. + let circuit_size = circuit.num_constraints + circuit.num_instance_variables; + let domain: Radix2EvaluationDomain = + Radix2EvaluationDomain::new(circuit_size).ok_or(anyhow!( + "Failed to create evaluation domain size (at least) {}", + circuit_size + ))?; + let domain_size = domain.size(); + // 0. Check that the phase1 degree is large enough. + if phase1.degree < domain_size { + return Err(anyhow!( + "Phase1 elements not large enough: expected >= {}, found {}", + domain_size, + phase1.degree + )); + } + + // 1. Get the lagrange coefficients over [x]. + // 1.1. Setup a polynomial that's x^i at each coefficient. + let mut extracting_poly: Vec<_> = phase1.raw.x_1.iter().copied().take(domain_size).collect(); + domain.ifft_in_place(&mut extracting_poly); + let lagrange_1 = extracting_poly; + + // 2. Do the same for [x]_2. + let mut extracting_poly: Vec<_> = phase1.raw.x_2.iter().copied().take(domain_size).collect(); + domain.ifft_in_place(&mut extracting_poly); + let lagrange_2 = extracting_poly; + + // 3. Do the same for [αx]. + let mut extracting_poly: Vec<_> = phase1 + .raw + .alpha_x_1 + .iter() + .copied() + .take(domain_size) + .collect(); + domain.ifft_in_place(&mut extracting_poly); + let alpha_lagrange_1 = extracting_poly; + + // 4. Do the same for [βx]. + let mut extracting_poly: Vec<_> = phase1 + .raw + .beta_x_1 + .iter() + .copied() + .take(domain_size) + .collect(); + domain.ifft_in_place(&mut extracting_poly); + let beta_lagrange_1 = extracting_poly; + + // 5. Accumulate the p_i polynomials evaluated over [x]. + // This indexing is copied from ark_groth16/r1cs_to_qap.rs.html#106. + // (I spent a full massage chair cycle thinking about this and couldn't figure out + // why exactly they do it this way, but mirroring the code we're trying to be + // compatible with is a good idea). + let qap_num_variables = (circuit.num_instance_variables - 1) + circuit.num_witness_variables; + let mut p_1 = vec![G1::zero(); qap_num_variables + 1]; + // Also take this opportunity to accumulate the raw u_1, v_1, and v_2 polynomials + let mut u_1 = vec![G1::zero(); qap_num_variables + 1]; + let mut v_1 = vec![G1::zero(); qap_num_variables + 1]; + let mut v_2 = vec![G2::zero(); qap_num_variables + 1]; + + // This is where we add the identity matrix block at the end to avoid malleability + // shenanigans. + { + let start = 0; + let end = circuit.num_instance_variables; + let num_constraints = circuit.num_constraints; + // One deviation if you're reading the arkworks code is that we're modifying + // the entire p polynomial, and not u (which they call 'a'), but this effectively does + // the same thing, because the other polynomials are set to 0 at these points. + p_1[start..end] + .copy_from_slice(&beta_lagrange_1[(start + num_constraints)..(end + num_constraints)]); + // We also modify u in the same way + u_1[start..end] + .copy_from_slice(&lagrange_1[(start + num_constraints)..(end + num_constraints)]) + } + + // Could zip here, but this looks cleaner to me. + for i in 0..circuit.num_constraints { + for &(ref coeff, j) in &circuit.a[i] { + p_1[j] += beta_lagrange_1[i] * coeff; + u_1[j] += lagrange_1[i] * coeff; + } + for &(ref coeff, j) in &circuit.b[i] { + p_1[j] += alpha_lagrange_1[i] * coeff; + v_1[j] += lagrange_1[i] * coeff; + v_2[j] += lagrange_2[i] * coeff; + } + for &(ref coeff, j) in &circuit.c[i] { + p_1[j] += lagrange_1[i] * coeff; + } + } + + // 5. Calculate the t polynomial, evaluated multiplied by successive powers. + let t: Vec<_> = (0..(domain_size - 1)) + .map(|i| phase1.raw.x_1[i + domain_size] - phase1.raw.x_1[i]) + .collect(); + + Ok(( + ExtraTransitionInformation { + u_1, + v_1, + v_2, + p_1: p_1.clone(), + }, + phase2::CRSElements { + raw: phase2::RawCRSElements { + delta_1: G1::generator(), + delta_2: G2::generator(), + inv_delta_p_1: p_1, + inv_delta_t_1: t, + }, + }, + )) +} + +/// Combine the outputs from all phases into a proving key. +/// +/// This proving key also contains a verifying key inherently. +pub fn combine( + circuit: &ConstraintMatrices, + phase1out: &Phase1CRSElements, + phase2out: &Phase2CRSElements, + extra: &ExtraTransitionInformation, +) -> ProvingKey { + let vk = VerifyingKey { + alpha_g1: phase1out.raw.alpha_1.into_affine(), + beta_g2: phase1out.raw.beta_2.into_affine(), + gamma_g2: G2::generator().into_affine(), + delta_g2: phase2out.raw.delta_2.into_affine(), + gamma_abc_g1: G1::normalize_batch(&extra.p_1[..circuit.num_instance_variables]), + }; + ProvingKey { + vk, + beta_g1: phase1out.raw.beta_1.into_affine(), + delta_g1: phase2out.raw.delta_1.into_affine(), + a_query: G1::normalize_batch(&extra.u_1), + b_g1_query: G1::normalize_batch(&extra.v_1), + b_g2_query: G2::normalize_batch(&extra.v_2), + h_query: G1::normalize_batch(&phase2out.raw.inv_delta_t_1), + l_query: G1::normalize_batch( + &phase2out.raw.inv_delta_p_1[circuit.num_instance_variables..], + ), + } +} + +#[cfg(test)] +mod test { + use ark_ff::One; + use ark_groth16::{r1cs_to_qap::LibsnarkReduction, Groth16}; + use ark_r1cs_std::{ + eq::EqGadget, + fields::fp::FpVar, + prelude::{AllocVar, Boolean}, + }; + use ark_relations::r1cs::{self, ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef}; + use ark_snark::SNARK; + use rand_core::{OsRng, SeedableRng}; + + use crate::log::Hashable; + + use super::*; + + #[derive(Clone, Copy, Debug)] + struct TestCircuit { + x: F, + y: F, + pub x_plus_y: F, + } + + impl ConstraintSynthesizer for TestCircuit { + fn generate_constraints(self, cs: ConstraintSystemRef) -> r1cs::Result<()> { + // Witnesses + let x_var = FpVar::new_witness(cs.clone(), || Ok(self.x))?; + let y_var = FpVar::new_witness(cs.clone(), || Ok(self.y))?; + + // Public Inputs + let x_plus_y_var = FpVar::new_input(cs, || Ok(self.x_plus_y))?; + + let add_output = x_var + y_var; + + x_plus_y_var.conditional_enforce_equal(&add_output, &Boolean::TRUE) + } + } + + #[test] + fn test_can_generate_keys_through_ceremony() -> anyhow::Result<()> { + let circuit = TestCircuit { + x: F::one(), + y: F::one(), + x_plus_y: F::from(2u64), + }; + let cs = ConstraintSystem::new_ref(); + cs.set_optimization_goal(r1cs::OptimizationGoal::Constraints); + cs.set_mode(r1cs::SynthesisMode::Setup); + circuit.generate_constraints(cs.clone())?; + cs.finalize(); + + let matrices = cs + .to_matrices() + .ok_or(anyhow!("Failed to generate constraint matrices."))?; + + let mut rng = rand_chacha::ChaChaRng::seed_from_u64(1776); + + let degree = circuit_degree(&matrices)?; + let phase1root = Phase1CRSElements::root(degree); + + // Doing two contributions to make sure there's not some weird bug there + let phase1contribution = Phase1Contribution::make(&mut rng, phase1root.hash(), &phase1root); + let phase1contribution = Phase1Contribution::make( + &mut rng, + phase1contribution.hash(), + &phase1contribution.new_elements, + ); + + let (extra, phase2root) = transition(&phase1contribution.new_elements, &matrices)?; + + let phase2contribution = Phase2Contribution::make(&mut rng, phase2root.hash(), &phase2root); + let phase2contribution = Phase2Contribution::make( + &mut rng, + phase2contribution.hash(), + &phase2contribution.new_elements, + ); + + let pk = combine( + &matrices, + &phase1contribution.new_elements, + &phase2contribution.new_elements, + &extra, + ); + + let proof = Groth16::::prove(&pk, circuit, &mut OsRng) + .map_err(|err| anyhow!(err))?; + + let public_inputs = vec![circuit.x_plus_y]; + let ok = Groth16::::verify(&pk.vk, &public_inputs, &proof)?; + assert!(ok); + + Ok(()) + } +} diff --git a/crates/crypto/proof-setup/src/phase1.rs b/crates/crypto/proof-setup/src/phase1.rs index ffcf799091..26a710827e 100644 --- a/crates/crypto/proof-setup/src/phase1.rs +++ b/crates/crypto/proof-setup/src/phase1.rs @@ -170,8 +170,8 @@ impl Hashable for RawCRSElements { /// Not all elements of the final CRS are present here. #[derive(Clone, Debug)] pub struct CRSElements { - degree: usize, - raw: RawCRSElements, + pub(crate) degree: usize, + pub(crate) raw: RawCRSElements, } impl CRSElements { diff --git a/crates/crypto/proof-setup/src/phase2.rs b/crates/crypto/proof-setup/src/phase2.rs index 35bf59b053..95855898fb 100644 --- a/crates/crypto/proof-setup/src/phase2.rs +++ b/crates/crypto/proof-setup/src/phase2.rs @@ -1,5 +1,4 @@ //! This module is very similar to the one for phase1, so reading that one might be useful. - use ark_ec::Group; use ark_ff::{fields::Field, UniformRand, Zero}; use rand_core::{CryptoRngCore, OsRng}; @@ -82,7 +81,7 @@ impl Hashable for RawCRSElements { /// When combined with the elements of phase 1, the entire CRS will be present. #[derive(Clone, Debug)] pub struct CRSElements { - raw: RawCRSElements, + pub(crate) raw: RawCRSElements, } impl Hashable for CRSElements { @@ -91,7 +90,7 @@ impl Hashable for CRSElements { } } -/// Represents a raw, unvalidated contribution. +/// Represents a raw, unvalidatedontribution. #[derive(Clone, Debug)] pub struct RawContribution { pub parent: ContributionHash, diff --git a/tools/parameter-setup/Cargo.toml b/tools/parameter-setup/Cargo.toml index c653573113..c3d4427591 100644 --- a/tools/parameter-setup/Cargo.toml +++ b/tools/parameter-setup/Cargo.toml @@ -6,6 +6,7 @@ publish = false [dependencies] penumbra-proof-params = { path = "../../crates/crypto/proof-params" } +penumbra-proof-setup = { path = "../../crates/crypto/proof-setup" } penumbra-dex = { path = "../../crates/core/component/dex/" } penumbra-dao = { path = "../../crates/core/component/dao/", features = ["component"] } penumbra-governance = { path = "../../crates/core/component/governance/" } @@ -14,3 +15,4 @@ penumbra-stake = { path = "../../crates/core/component/stake/", features = ["com ark-groth16 = "0.4" ark-serialize = "0.4" decaf377 = { version = "0.5", features = ["r1cs"] } +rand_core = "0.6.4" diff --git a/tools/parameter-setup/src/main.rs b/tools/parameter-setup/src/main.rs index 2c61802ade..3cda374ce9 100644 --- a/tools/parameter-setup/src/main.rs +++ b/tools/parameter-setup/src/main.rs @@ -9,9 +9,54 @@ use ark_serialize::CanonicalSerialize; use decaf377::Bls12_377; use penumbra_dex::{swap::proof::SwapCircuit, swap_claim::proof::SwapClaimCircuit}; use penumbra_governance::DelegatorVoteCircuit; -use penumbra_proof_params::{ParameterSetup, ProvingKeyExt, VerifyingKeyExt}; +use penumbra_proof_params::{ + generate_constraint_matrices, DummyWitness, ProvingKeyExt, VerifyingKeyExt, +}; +use penumbra_proof_setup::{ + circuit_degree, combine, log::Hashable, transition, Phase1CRSElements, Phase1Contribution, + Phase2Contribution, +}; use penumbra_shielded_pool::{NullifierDerivationCircuit, OutputCircuit, SpendCircuit}; use penumbra_stake::UndelegateClaimCircuit; +use rand_core::OsRng; + +fn generate_parameters() -> (ProvingKey, VerifyingKey) { + let matrices = generate_constraint_matrices::(); + + let mut rng = OsRng; + + let degree = circuit_degree(&matrices).expect("failed to calculate degree of circuit"); + let phase1root = Phase1CRSElements::root(degree); + + // Doing two contributions to make sure there's not some weird bug there + let phase1contribution = Phase1Contribution::make(&mut rng, phase1root.hash(), &phase1root); + let phase1contribution = Phase1Contribution::make( + &mut rng, + phase1contribution.hash(), + &phase1contribution.new_elements, + ); + + let (extra, phase2root) = transition(&phase1contribution.new_elements, &matrices) + .expect("failed to transition between setup phases"); + + let phase2contribution = Phase2Contribution::make(&mut rng, phase2root.hash(), &phase2root); + let phase2contribution = Phase2Contribution::make( + &mut rng, + phase2contribution.hash(), + &phase2contribution.new_elements, + ); + + let pk = combine( + &matrices, + &phase1contribution.new_elements, + &phase2contribution.new_elements, + &extra, + ); + + let vk = pk.vk.clone(); + + (pk, vk) +} fn main() -> Result<()> { let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); @@ -28,23 +73,22 @@ fn main() -> Result<()> { // Generate the parameters for the current proofs and serialize them // to files in the target directory. - let (spend_pk, spend_vk) = SpendCircuit::generate_test_parameters(); + let (spend_pk, spend_vk) = generate_parameters::(); write_params(&target_dir, "spend", &spend_pk, &spend_vk)?; - let (output_pk, output_vk) = OutputCircuit::generate_test_parameters(); + let (output_pk, output_vk) = generate_parameters::(); write_params(&target_dir, "output", &output_pk, &output_vk)?; - let (swap_pk, swap_vk) = SwapCircuit::generate_test_parameters(); + let (swap_pk, swap_vk) = generate_parameters::(); write_params(&target_dir, "swap", &swap_pk, &swap_vk)?; - let (swapclaim_pk, swapclaim_vk) = SwapClaimCircuit::generate_test_parameters(); + let (swapclaim_pk, swapclaim_vk) = generate_parameters::(); write_params(&target_dir, "swapclaim", &swapclaim_pk, &swapclaim_vk)?; - let (undelegateclaim_pk, undelegateclaim_vk) = - UndelegateClaimCircuit::generate_test_parameters(); + let (undelegateclaim_pk, undelegateclaim_vk) = generate_parameters::(); write_params( &target_dir, "undelegateclaim", &undelegateclaim_pk, &undelegateclaim_vk, )?; - let (delegator_vote_pk, delegator_vote_vk) = DelegatorVoteCircuit::generate_test_parameters(); + let (delegator_vote_pk, delegator_vote_vk) = generate_parameters::(); write_params( &target_dir, "delegator_vote", @@ -52,7 +96,7 @@ fn main() -> Result<()> { &delegator_vote_vk, )?; let (nullifier_derivation_pk, nullifier_derivation_vk) = - NullifierDerivationCircuit::generate_test_parameters(); + generate_parameters::(); write_params( &target_dir, "nullifier_derivation",