From 17c3c5a545f47bd9a1f6c819b326b083e2a6513f Mon Sep 17 00:00:00 2001 From: winderica Date: Thu, 25 Jun 2026 13:56:14 +0800 Subject: [PATCH] Improve the robustness of poseidon config generation --- Cargo.lock | 1 + crates/primitives/Cargo.toml | 3 +- .../src/transcripts/poseidon/mod.rs | 17 ++++--- .../src/transcripts/poseidon/sponge.rs | 47 ++++++++++--------- 4 files changed, 37 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1dd1e880..f83268b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1124,6 +1124,7 @@ dependencies = [ "ark-crypto-primitives", "ark-ec", "ark-ff", + "ark-grumpkin", "ark-pallas", "ark-poly", "ark-r1cs-std", diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 711de152..951d24a9 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -6,6 +6,7 @@ license.workspace = true repository.workspace = true [dependencies] +ark-bn254 = { workspace = true, features = ["curve", "r1cs"] } ark-crypto-primitives = { workspace = true, features = ["constraints", "sponge", "crh"] } ark-ec = { workspace = true } ark-ff = { workspace = true, features = ["asm"] } @@ -23,7 +24,7 @@ sha3 = { workspace = true } thiserror = { workspace = true } [dev-dependencies] -ark-bn254 = { workspace = true, features = ["curve", "r1cs"] } +ark-grumpkin = { workspace = true, features = ["r1cs"] } ark-pallas = { workspace = true, features = ["curve", "r1cs"] } [target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies] diff --git a/crates/primitives/src/transcripts/poseidon/mod.rs b/crates/primitives/src/transcripts/poseidon/mod.rs index 799915f0..ca9b881a 100644 --- a/crates/primitives/src/transcripts/poseidon/mod.rs +++ b/crates/primitives/src/transcripts/poseidon/mod.rs @@ -95,6 +95,8 @@ fn find_fd_round_numbers( } } + assert_ne!(min_cost, usize::MAX); + (r_f, r_p) } @@ -106,13 +108,14 @@ pub fn poseidon_paper_config( alpha: u64, rate: usize, ) -> PoseidonConfig { + assert_ne!(alpha, 1); assert_eq!( BigUint::from(alpha).gcd(&(-F::one()).into()), BigUint::one() ); let (full_rounds, partial_rounds) = - find_fd_round_numbers::(rate, alpha, SECURITY_BITS, get_sbox_cost, true); - let (ark, mds) = find_poseidon_ark_and_mds::( + find_fd_round_numbers::(rate + 1, alpha, SECURITY_BITS, get_sbox_cost, true); + let (ark, mds) = find_poseidon_ark_and_mds( F::MODULUS_BIT_SIZE as u64, rate, full_rounds, @@ -131,9 +134,9 @@ pub fn poseidon_paper_config( ) } -/// [`poseidon_circom_config`] produces a Poseidon configuration which agrees -/// with Circom's Poseidon(4) when `F` is the scalar field of BN254. -pub fn poseidon_circom_config() -> PoseidonConfig { +/// [`poseidon_circom_config`] produces a Poseidon configuration for BN254's +/// scalar field that agrees with Circom's Poseidon(4). +pub fn poseidon_circom_config() -> PoseidonConfig { // 120 bit security target as in // https://eprint.iacr.org/2019/458.pdf // t = rate + 1 @@ -143,8 +146,8 @@ pub fn poseidon_circom_config() -> PoseidonConfig { let alpha = 5; let rate = 4; - let (ark, mds) = find_poseidon_ark_and_mds::( - F::MODULUS_BIT_SIZE as u64, + let (ark, mds) = find_poseidon_ark_and_mds( + ark_bn254::Fr::MODULUS_BIT_SIZE as u64, rate, full_rounds as u64, partial_rounds as u64, diff --git a/crates/primitives/src/transcripts/poseidon/sponge.rs b/crates/primitives/src/transcripts/poseidon/sponge.rs index eb827a19..daf8bc53 100644 --- a/crates/primitives/src/transcripts/poseidon/sponge.rs +++ b/crates/primitives/src/transcripts/poseidon/sponge.rs @@ -84,9 +84,10 @@ impl TranscriptGadget for PoseidonSpongeVar { #[cfg(test)] mod tests { - use ark_bn254::{Fq, Fr, G1Projective as G1, g1::Config}; + use ark_bn254::{Fr, G1Projective as G1}; use ark_crypto_primitives::sponge::poseidon::{PoseidonSponge, constraints::PoseidonSpongeVar}; use ark_ff::UniformRand; + use ark_grumpkin::Projective as G2; use ark_r1cs_std::{ GR1CSVar, alloc::AllocVar, fields::fp::FpVar, groups::curves::short_weierstrass::ProjectiveVar, @@ -104,7 +105,7 @@ mod tests { // Test with value taken from https://github.com/iden3/circomlibjs/blob/43cc582b100fc3459cf78d903a6f538e5d7f38ee/test/poseidon.js#L32 #[test] fn check_against_circom_poseidon() -> Result<(), Box> { - let config = poseidon_circom_config::(); + let config = poseidon_circom_config(); let mut poseidon_sponge = PoseidonSponge::new(config); let v = vec![1, 2, 3, 4] .into_iter() @@ -125,15 +126,15 @@ mod tests { #[test] fn test_challenge_field_element() -> Result<(), Box> { // Create a transcript outside of the circuit - let config = poseidon_circom_config::(); - let mut tr = PoseidonSponge::::new(config.clone()); + let config = poseidon_circom_config(); + let mut tr = PoseidonSponge::new(config.clone()); tr.add(&Fr::from(42_u32)); let c = tr.challenge_field_element(); // Create a transcript inside of the circuit - let cs = ConstraintSystem::::new_ref(); - let mut tr_var = PoseidonSpongeVar::::new(config); - let v = FpVar::::new_witness(cs.clone(), || Ok(Fr::from(42_u32)))?; + let cs = ConstraintSystem::new_ref(); + let mut tr_var = PoseidonSpongeVar::new(config); + let v = FpVar::new_witness(cs.clone(), || Ok(Fr::from(42_u32)))?; tr_var.add(&v)?; let c_var = tr_var.challenge_field_element()?; @@ -148,15 +149,15 @@ mod tests { let nbits = 128; // Create a transcript outside of the circuit - let config = poseidon_circom_config::(); - let mut tr = PoseidonSponge::::new(config.clone()); - tr.add(&Fq::from(42_u32)); + let config = poseidon_circom_config(); + let mut tr = PoseidonSponge::new(config.clone()); + tr.add(&Fr::from(42_u32)); let c = tr.challenge_bits(nbits); // Create a transcript inside of the circuit - let cs = ConstraintSystem::::new_ref(); - let mut tr_var = PoseidonSpongeVar::::new(config); - let v = FpVar::::new_witness(cs.clone(), || Ok(Fq::from(42_u32)))?; + let cs = ConstraintSystem::new_ref(); + let mut tr_var = PoseidonSpongeVar::new(config); + let v = FpVar::new_witness(cs.clone(), || Ok(Fr::from(42_u32)))?; tr_var.add(&v)?; let c_var = tr_var.challenge_bits(nbits)?; @@ -169,18 +170,18 @@ mod tests { #[test] fn test_absorb_canonical_point() -> Result<(), Box> { // Create a transcript outside of the circuit - let config = poseidon_circom_config::(); - let mut tr = PoseidonSponge::::new(config.clone()); + let config = poseidon_circom_config(); + let mut tr = PoseidonSponge::new(config.clone()); let rng = &mut thread_rng(); - let p = G1::rand(rng); + let p = G2::rand(rng); tr.add(&p); let c = tr.challenge_field_element(); // Create a transcript inside of the circuit - let cs = ConstraintSystem::::new_ref(); - let mut tr_var = PoseidonSpongeVar::::new(config); - let p_var = ProjectiveVar::>::new_witness(cs, || Ok(p))?; + let cs = ConstraintSystem::new_ref(); + let mut tr_var = PoseidonSpongeVar::new(config); + let p_var = ProjectiveVar::new_witness(cs, || Ok(p))?; tr_var.add(&p_var)?; let c_var = tr_var.challenge_field_element()?; @@ -193,8 +194,8 @@ mod tests { #[test] fn test_absorb_emulated_point() -> Result<(), Box> { // Create a transcript outside of the circuit - let config = poseidon_circom_config::(); - let mut tr = PoseidonSponge::::new(config.clone()); + let config = poseidon_circom_config(); + let mut tr = PoseidonSponge::new(config.clone()); let rng = &mut thread_rng(); let p = G1::rand(rng); @@ -202,8 +203,8 @@ mod tests { let c = tr.challenge_field_element(); // Create a transcript inside of the circuit - let cs = ConstraintSystem::::new_ref(); - let mut tr_var = PoseidonSpongeVar::::new(config); + let cs = ConstraintSystem::new_ref(); + let mut tr_var = PoseidonSpongeVar::new(config); let p_var = EmulatedAffineVar::new_witness(cs, || Ok(p))?; tr_var.add(&p_var)?; let c_var = tr_var.challenge_field_element()?;