use core::marker::PhantomData; use plonky2::field::extension::Extendable; use plonky2::field::secp256k1_scalar::Secp256K1Scalar; use plonky2::hash::hash_types::RichField; use plonky2::plonk::circuit_builder::CircuitBuilder; use crate::curve::curve_types::Curve; use crate::curve::secp256k1::Secp256K1; use crate::gadgets::curve::{AffinePointTarget, CircuitBuilderCurve}; use crate::gadgets::curve_fixed_base::fixed_base_curve_mul_circuit; use crate::gadgets::glv::CircuitBuilderGlv; use crate::gadgets::nonnative::{CircuitBuilderNonNative, NonNativeTarget}; #[derive(Clone, Debug)] pub struct ECDSASecretKeyTarget(pub NonNativeTarget); #[derive(Clone, Debug)] pub struct ECDSAPublicKeyTarget(pub AffinePointTarget); #[derive(Clone, Debug)] pub struct ECDSASignatureTarget { pub r: NonNativeTarget, pub s: NonNativeTarget, } pub fn verify_message_circuit, const D: usize>( builder: &mut CircuitBuilder, msg: NonNativeTarget, sig: ECDSASignatureTarget, pk: ECDSAPublicKeyTarget, ) { let ECDSASignatureTarget { r, s } = sig; builder.curve_assert_valid(&pk.0); let c = builder.inv_nonnative(&s); let u1 = builder.mul_nonnative(&msg, &c); let u2 = builder.mul_nonnative(&r, &c); let point1 = fixed_base_curve_mul_circuit(builder, Secp256K1::GENERATOR_AFFINE, &u1); let point2 = builder.glv_mul(&pk.0, &u2); let point = builder.curve_add(&point1, &point2); let x = NonNativeTarget:: { value: point.x.value, _phantom: PhantomData, }; builder.connect_nonnative(&r, &x); } #[cfg(test)] mod tests { use anyhow::Result; use plonky2::field::types::Sample; use plonky2::iop::witness::PartialWitness; use plonky2::plonk::circuit_data::CircuitConfig; use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; use super::*; use crate::curve::curve_types::CurveScalar; use crate::curve::ecdsa::{sign_message, ECDSAPublicKey, ECDSASecretKey, ECDSASignature}; fn test_ecdsa_circuit_with_config(config: CircuitConfig) -> Result<()> { const D: usize = 2; type C = PoseidonGoldilocksConfig; type F = >::F; type Curve = Secp256K1; let pw = PartialWitness::new(); let mut builder = CircuitBuilder::::new(config); let msg = Secp256K1Scalar::rand(); let msg_target = builder.constant_nonnative(msg); let sk = ECDSASecretKey::(Secp256K1Scalar::rand()); let pk = ECDSAPublicKey((CurveScalar(sk.0) * Curve::GENERATOR_PROJECTIVE).to_affine()); let pk_target = ECDSAPublicKeyTarget(builder.constant_affine_point(pk.0)); let sig = sign_message(msg, sk); let ECDSASignature { r, s } = sig; let r_target = builder.constant_nonnative(r); let s_target = builder.constant_nonnative(s); let sig_target = ECDSASignatureTarget { r: r_target, s: s_target, }; verify_message_circuit(&mut builder, msg_target, sig_target, pk_target); dbg!(builder.num_gates()); let data = builder.build::(); let proof = data.prove(pw).unwrap(); data.verify(proof) } #[test] #[ignore] fn test_ecdsa_circuit_narrow() -> Result<()> { test_ecdsa_circuit_with_config(CircuitConfig::standard_ecc_config()) } #[test] #[ignore] fn test_ecdsa_circuit_wide() -> Result<()> { test_ecdsa_circuit_with_config(CircuitConfig::wide_ecc_config()) } }