add circuit to verify signatures (SignatureVerifyGadget) (#167)

* implement circuit to verify signature (proof-based signature), ie. a 1-level recursion verification

* as agreed in the call, rename Gate -> Gadget when it's not a 'gate'

* make SignatureVerifyGadget conditional on the selector input

* small naming polish

* sigverifygadget: add s computation in-circuit, connect pk,msg,s to internalproof's public_inputs

* optimize signature verify

---------

Co-authored-by: Eduard S. <eduardsanou@posteo.net>
This commit is contained in:
arnaucube 2025-04-01 01:36:37 +02:00 committed by GitHub
parent d00ff95f41
commit 0637f52573
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 325 additions and 42 deletions

View file

@ -21,7 +21,7 @@ use crate::backends::plonky2::circuits::common::{
}; };
use crate::backends::plonky2::primitives::merkletree::MerkleTree; use crate::backends::plonky2::primitives::merkletree::MerkleTree;
use crate::backends::plonky2::primitives::merkletree::{ use crate::backends::plonky2::primitives::merkletree::{
MerkleProofExistenceGate, MerkleProofExistenceTarget, MerkleProofExistenceGadget, MerkleProofExistenceTarget,
}; };
use crate::middleware::{ use crate::middleware::{
hash_str, AnchoredKey, NativeOperation, NativePredicate, Params, PodType, Statement, hash_str, AnchoredKey, NativeOperation, NativePredicate, Params, PodType, Statement,
@ -34,18 +34,18 @@ use super::common::Flattenable;
// SignedPod verification // SignedPod verification
// //
struct SignedPodVerifyGate { struct SignedPodVerifyGadget {
params: Params, params: Params,
} }
impl SignedPodVerifyGate { impl SignedPodVerifyGadget {
fn eval(&self, builder: &mut CircuitBuilder<F, D>) -> Result<SignedPodVerifyTarget> { fn eval(&self, builder: &mut CircuitBuilder<F, D>) -> Result<SignedPodVerifyTarget> {
// 2. Verify id // 2. Verify id
let id = builder.add_virtual_hash(); let id = builder.add_virtual_hash();
let mut mt_proofs = Vec::new(); let mut mt_proofs = Vec::new();
for _ in 0..self.params.max_signed_pod_values { for _ in 0..self.params.max_signed_pod_values {
let mt_proof = MerkleProofExistenceGate { let mt_proof = MerkleProofExistenceGadget {
max_depth: self.params.max_depth_mt_gate, max_depth: self.params.max_depth_mt_gadget,
} }
.eval(builder)?; .eval(builder)?;
builder.connect_hashes(id, mt_proof.root); builder.connect_hashes(id, mt_proof.root);
@ -100,7 +100,7 @@ impl SignedPodVerifyTarget {
fn set_targets(&self, pw: &mut PartialWitness<F>, input: &SignedPodVerifyInput) -> Result<()> { fn set_targets(&self, pw: &mut PartialWitness<F>, input: &SignedPodVerifyInput) -> Result<()> {
assert!(input.kvs.len() <= self.params.max_signed_pod_values); assert!(input.kvs.len() <= self.params.max_signed_pod_values);
let tree = MerkleTree::new(self.params.max_depth_mt_gate, &input.kvs)?; let tree = MerkleTree::new(self.params.max_depth_mt_gadget, &input.kvs)?;
// First handle the type entry, then the rest of the entries, and finally pad with // First handle the type entry, then the rest of the entries, and finally pad with
// repetitions of the type entry (which always exists) // repetitions of the type entry (which always exists)
@ -125,11 +125,11 @@ impl SignedPodVerifyTarget {
// MainPod verification // MainPod verification
// //
struct OperationVerifyGate { struct OperationVerifyGadget {
params: Params, params: Params,
} }
impl OperationVerifyGate { impl OperationVerifyGadget {
fn eval( fn eval(
&self, &self,
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder<F, D>,
@ -359,17 +359,17 @@ impl OperationVerifyTarget {
} }
} }
struct MainPodVerifyGate { struct MainPodVerifyGadget {
params: Params, params: Params,
} }
impl MainPodVerifyGate { impl MainPodVerifyGadget {
fn eval(&self, builder: &mut CircuitBuilder<F, D>) -> Result<MainPodVerifyTarget> { fn eval(&self, builder: &mut CircuitBuilder<F, D>) -> Result<MainPodVerifyTarget> {
let params = &self.params; let params = &self.params;
// 1. Verify all input signed pods // 1. Verify all input signed pods
let mut signed_pods = Vec::new(); let mut signed_pods = Vec::new();
for _ in 0..params.max_input_signed_pods { for _ in 0..params.max_input_signed_pods {
let signed_pod = SignedPodVerifyGate { let signed_pod = SignedPodVerifyGadget {
params: params.clone(), params: params.clone(),
} }
.eval(builder)?; .eval(builder)?;
@ -421,7 +421,7 @@ impl MainPodVerifyGate {
let mut op_verifications = Vec::new(); let mut op_verifications = Vec::new();
for (i, (st, op)) in input_statements.iter().zip(operations.iter()).enumerate() { for (i, (st, op)) in input_statements.iter().zip(operations.iter()).enumerate() {
let prev_statements = &statements[..input_statements_offset + i - 1]; let prev_statements = &statements[..input_statements_offset + i - 1];
let op_verification = OperationVerifyGate { let op_verification = OperationVerifyGadget {
params: params.clone(), params: params.clone(),
} }
.eval(builder, st, op, prev_statements)?; .eval(builder, st, op, prev_statements)?;
@ -478,7 +478,7 @@ pub struct MainPodVerifyCircuit {
impl MainPodVerifyCircuit { impl MainPodVerifyCircuit {
pub fn eval(&self, builder: &mut CircuitBuilder<F, D>) -> Result<MainPodVerifyTarget> { pub fn eval(&self, builder: &mut CircuitBuilder<F, D>) -> Result<MainPodVerifyTarget> {
let main_pod = MainPodVerifyGate { let main_pod = MainPodVerifyGadget {
params: self.params.clone(), params: self.params.clone(),
} }
.eval(builder)?; .eval(builder)?;
@ -505,7 +505,7 @@ mod tests {
let config = CircuitConfig::standard_recursion_config(); let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config); let mut builder = CircuitBuilder::<F, D>::new(config);
let signed_pod_verify = SignedPodVerifyGate { params }.eval(&mut builder)?; let signed_pod_verify = SignedPodVerifyGadget { params }.eval(&mut builder)?;
let mut pw = PartialWitness::<F>::new(); let mut pw = PartialWitness::<F>::new();
let kvs = [ let kvs = [
@ -543,7 +543,7 @@ mod tests {
.map(|_| builder.add_virtual_statement(&params)) .map(|_| builder.add_virtual_statement(&params))
.collect(); .collect();
let operation_verify = OperationVerifyGate { let operation_verify = OperationVerifyGadget {
params: params.clone(), params: params.clone(),
} }
.eval( .eval(

View file

@ -10,7 +10,6 @@ use std::iter::IntoIterator;
use crate::backends::counter; use crate::backends::counter;
use crate::backends::plonky2::basetypes::{hash_fields, Hash, Value, EMPTY_HASH, F}; use crate::backends::plonky2::basetypes::{hash_fields, Hash, Value, EMPTY_HASH, F};
// mod merkletree_circuit;
pub use super::merkletree_circuit::*; pub use super::merkletree_circuit::*;
/// Implements the MerkleTree specified at /// Implements the MerkleTree specified at

View file

@ -27,11 +27,11 @@ use crate::backends::plonky2::basetypes::{Hash, Value, D, EMPTY_HASH, EMPTY_VALU
use crate::backends::plonky2::circuits::common::{CircuitBuilderPod, ValueTarget}; use crate::backends::plonky2::circuits::common::{CircuitBuilderPod, ValueTarget};
use crate::backends::plonky2::primitives::merkletree::MerkleProof; use crate::backends::plonky2::primitives::merkletree::MerkleProof;
/// `MerkleProofGate` allows to verify both proofs of existence and proofs /// `MerkleProofGadget` allows to verify both proofs of existence and proofs
/// non-existence with the same circuit. /// non-existence with the same circuit.
/// If only proofs of existence are needed, use `MerkleProofExistenceGate`, /// If only proofs of existence are needed, use `MerkleProofExistenceGadget`,
/// which requires less amount of constraints. /// which requires less amount of constraints.
pub struct MerkleProofGate { pub struct MerkleProofGadget {
pub max_depth: usize, pub max_depth: usize,
} }
@ -47,7 +47,7 @@ pub struct MerkleProofTarget {
pub other_value: ValueTarget, pub other_value: ValueTarget,
} }
impl MerkleProofGate { impl MerkleProofGadget {
/// creates the targets and defines the logic of the circuit /// creates the targets and defines the logic of the circuit
pub fn eval(&self, builder: &mut CircuitBuilder<F, D>) -> Result<MerkleProofTarget> { pub fn eval(&self, builder: &mut CircuitBuilder<F, D>) -> Result<MerkleProofTarget> {
// create the targets // create the targets
@ -179,7 +179,7 @@ impl MerkleProofTarget {
/// `MerkleProofExistenceCircuit` allows to verify proofs of existence only. If /// `MerkleProofExistenceCircuit` allows to verify proofs of existence only. If
/// proofs of non-existence are needed, use `MerkleProofCircuit`. /// proofs of non-existence are needed, use `MerkleProofCircuit`.
pub struct MerkleProofExistenceGate { pub struct MerkleProofExistenceGadget {
pub max_depth: usize, pub max_depth: usize,
} }
@ -191,7 +191,7 @@ pub struct MerkleProofExistenceTarget {
pub siblings: Vec<HashOutTarget>, pub siblings: Vec<HashOutTarget>,
} }
impl MerkleProofExistenceGate { impl MerkleProofExistenceGadget {
/// creates the targets and defines the logic of the circuit /// creates the targets and defines the logic of the circuit
pub fn eval(&self, builder: &mut CircuitBuilder<F, D>) -> Result<MerkleProofExistenceTarget> { pub fn eval(&self, builder: &mut CircuitBuilder<F, D>) -> Result<MerkleProofExistenceTarget> {
// create the targets // create the targets
@ -486,7 +486,7 @@ pub mod tests {
let mut builder = CircuitBuilder::<F, D>::new(config); let mut builder = CircuitBuilder::<F, D>::new(config);
let mut pw = PartialWitness::<F>::new(); let mut pw = PartialWitness::<F>::new();
let targets = MerkleProofGate { max_depth }.eval(&mut builder)?; let targets = MerkleProofGadget { max_depth }.eval(&mut builder)?;
targets.set_targets(&mut pw, existence, tree.root(), proof, key, value)?; targets.set_targets(&mut pw, existence, tree.root(), proof, key, value)?;
// generate & verify proof // generate & verify proof
@ -525,7 +525,7 @@ pub mod tests {
let mut builder = CircuitBuilder::<F, D>::new(config); let mut builder = CircuitBuilder::<F, D>::new(config);
let mut pw = PartialWitness::<F>::new(); let mut pw = PartialWitness::<F>::new();
let targets = MerkleProofExistenceGate { max_depth }.eval(&mut builder)?; let targets = MerkleProofExistenceGadget { max_depth }.eval(&mut builder)?;
targets.set_targets(&mut pw, tree.root(), proof, key, value)?; targets.set_targets(&mut pw, tree.root(), proof, key, value)?;
// generate & verify proof // generate & verify proof
@ -592,7 +592,7 @@ pub mod tests {
let mut builder = CircuitBuilder::<F, D>::new(config); let mut builder = CircuitBuilder::<F, D>::new(config);
let mut pw = PartialWitness::<F>::new(); let mut pw = PartialWitness::<F>::new();
let targets = MerkleProofGate { max_depth }.eval(&mut builder)?; let targets = MerkleProofGadget { max_depth }.eval(&mut builder)?;
targets.set_targets(&mut pw, proof.existence, tree.root(), proof, key, value)?; targets.set_targets(&mut pw, proof.existence, tree.root(), proof, key, value)?;
// generate & verify proof // generate & verify proof
@ -633,7 +633,7 @@ pub mod tests {
let mut builder = CircuitBuilder::<F, D>::new(config); let mut builder = CircuitBuilder::<F, D>::new(config);
let mut pw = PartialWitness::<F>::new(); let mut pw = PartialWitness::<F>::new();
let targets = MerkleProofGate { max_depth }.eval(&mut builder)?; let targets = MerkleProofGadget { max_depth }.eval(&mut builder)?;
targets.set_targets(&mut pw, true, tree2.root(), proof, key, value)?; targets.set_targets(&mut pw, true, tree2.root(), proof, key, value)?;
// generate proof, expecting it to fail (since we're using the wrong // generate proof, expecting it to fail (since we're using the wrong

View file

@ -1,3 +1,4 @@
pub mod merkletree; pub mod merkletree;
mod merkletree_circuit; mod merkletree_circuit;
pub mod signature; pub mod signature;
mod signature_circuit;

View file

@ -1,6 +1,7 @@
//! Proof-based signatures using Plonky2 proofs, following //! Proof-based signatures using Plonky2 proofs, following
//! https://eprint.iacr.org/2024/1553 . //! https://eprint.iacr.org/2024/1553 .
use anyhow::Result; use anyhow::Result;
use lazy_static::lazy_static;
use plonky2::{ use plonky2::{
field::types::Sample, field::types::Sample,
hash::{ hash::{
@ -21,23 +22,31 @@ use plonky2::{
use crate::backends::plonky2::basetypes::{Proof, Value, C, D, F, VALUE_SIZE}; use crate::backends::plonky2::basetypes::{Proof, Value, C, D, F, VALUE_SIZE};
use lazy_static::lazy_static; pub use super::signature_circuit::*;
lazy_static! { lazy_static! {
static ref PP: ProverParams = Signature::prover_params().unwrap(); /// Signature prover parameters
static ref VP: VerifierParams = Signature::verifier_params().unwrap(); pub static ref PP: ProverParams = Signature::prover_params().unwrap();
/// Signature verifier parameters
pub static ref VP: VerifierParams = Signature::verifier_params().unwrap();
/// DUMMY_SIGNATURE is used for conditionals where we want to use a `selector` to enable or
/// disable signature verification.
pub static ref DUMMY_SIGNATURE: Signature = dummy_signature().unwrap();
/// DUMMY_PUBLIC_INPUTS accompanies the DUMMY_SIGNATURE.
pub static ref DUMMY_PUBLIC_INPUTS: Vec<F> = dummy_public_inputs().unwrap();
} }
pub struct ProverParams { pub struct ProverParams {
prover: ProverCircuitData<F, C, D>, prover: ProverCircuitData<F, C, D>,
circuit: SignatureCircuit, circuit: SignatureInternalCircuit,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct VerifierParams(VerifierCircuitData<F, C, D>); pub struct VerifierParams(pub(crate) VerifierCircuitData<F, C, D>);
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct SecretKey(Value); pub struct SecretKey(pub(crate) Value);
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct PublicKey(pub(crate) Value); pub struct PublicKey(pub(crate) Value);
@ -90,12 +99,12 @@ impl Signature {
Ok((pp, vp)) Ok((pp, vp))
} }
fn builder() -> Result<(CircuitBuilder<F, D>, SignatureCircuit)> { fn builder() -> Result<(CircuitBuilder<F, D>, SignatureInternalCircuit)> {
// notice that we use the 'zk' config // notice that we use the 'zk' config
let config = CircuitConfig::standard_recursion_zk_config(); let config = CircuitConfig::standard_recursion_zk_config();
let mut builder = CircuitBuilder::<F, D>::new(config); let mut builder = CircuitBuilder::<F, D>::new(config);
let circuit = SignatureCircuit::add_targets(&mut builder)?; let circuit = SignatureInternalCircuit::add_targets(&mut builder)?;
Ok((builder, circuit)) Ok((builder, circuit))
} }
@ -113,21 +122,35 @@ impl Signature {
} }
} }
/// The SignatureCircuit implements the circuit used for the proof of the fn dummy_public_inputs() -> Result<Vec<F>> {
/// argument described at https://eprint.iacr.org/2024/1553. let sk = SecretKey(Value::from(0));
let pk = sk.public_key();
let msg = Value::from(0);
let s = Value(PoseidonHash::hash_no_pad(&[pk.0 .0, msg.0].concat()).elements);
Ok([pk.0 .0, msg.0, s.0].concat())
}
fn dummy_signature() -> Result<Signature> {
let sk = SecretKey(Value::from(0));
let msg = Value::from(0);
sk.sign(msg)
}
/// The SignatureInternalCircuit implements the circuit used for the proof of
/// the argument described at https://eprint.iacr.org/2024/1553.
/// ///
/// The circuit proves that for the given public inputs (pk, msg, s), the Prover /// The circuit proves that for the given public inputs (pk, msg, s), the Prover
/// knows the secret (sk) such that: /// knows the secret (sk) such that:
/// i) pk == H(sk) /// i) pk == H(sk)
/// ii) s == H(pk, msg) /// ii) s == H(pk, msg)
struct SignatureCircuit { struct SignatureInternalCircuit {
sk_targ: Vec<Target>, sk_targ: Vec<Target>,
pk_targ: HashOutTarget, pk_targ: HashOutTarget,
msg_targ: Vec<Target>, msg_targ: Vec<Target>,
s_targ: HashOutTarget, s_targ: HashOutTarget,
} }
impl SignatureCircuit { impl SignatureInternalCircuit {
/// creates the targets and defines the logic of the circuit /// creates the targets and defines the logic of the circuit
fn add_targets(builder: &mut CircuitBuilder<F, D>) -> Result<Self> { fn add_targets(builder: &mut CircuitBuilder<F, D>) -> Result<Self> {
// create the targets // create the targets
@ -182,7 +205,6 @@ pub mod tests {
use super::*; use super::*;
// Note: this test must be run with the `--release` flag.
#[test] #[test]
fn test_signature() -> Result<()> { fn test_signature() -> Result<()> {
let sk = SecretKey::new(); let sk = SecretKey::new();
@ -203,4 +225,14 @@ pub mod tests {
Ok(()) Ok(())
} }
#[test]
fn test_dummy_signature() -> Result<()> {
let sk = SecretKey(Value::from(0));
let pk = sk.public_key();
let msg = Value::from(0);
DUMMY_SIGNATURE.clone().verify(&pk, msg)?;
Ok(())
}
} }

View file

@ -0,0 +1,251 @@
#![allow(unused)]
use anyhow::Result;
use lazy_static::lazy_static;
use plonky2::{
field::types::Field,
hash::{
hash_types::{HashOut, HashOutTarget},
poseidon::PoseidonHash,
},
iop::{
target::{BoolTarget, Target},
witness::{PartialWitness, WitnessWrite},
},
plonk::circuit_builder::CircuitBuilder,
plonk::circuit_data::{
CircuitConfig, CircuitData, ProverCircuitData, VerifierCircuitData, VerifierCircuitTarget,
},
plonk::config::Hasher,
plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget},
};
use super::signature::{PublicKey, SecretKey, Signature, DUMMY_PUBLIC_INPUTS, DUMMY_SIGNATURE};
use crate::backends::plonky2::basetypes::{
Hash, Proof, Value, C, D, EMPTY_HASH, EMPTY_VALUE, F, VALUE_SIZE,
};
use crate::backends::plonky2::circuits::common::{CircuitBuilderPod, ValueTarget};
lazy_static! {
/// SignatureVerifyGadget VerifierCircuitData
pub static ref S_VD: VerifierCircuitData<F,C,D> = SignatureVerifyGadget::verifier_data().unwrap();
}
pub struct SignatureVerifyGadget {}
pub struct SignatureVerifyTarget {
// verifier_data of the SignatureInternalCircuit
verifier_data_targ: VerifierCircuitTarget,
selector: BoolTarget,
pk: ValueTarget,
msg: ValueTarget,
// proof of the SignatureInternalCircuit (=signature::Signature.0)
proof: ProofWithPublicInputsTarget<D>,
}
impl SignatureVerifyGadget {
pub fn verifier_data() -> Result<VerifierCircuitData<F, C, D>> {
// notice that we use the 'zk' config
let config = CircuitConfig::standard_recursion_zk_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let circuit = SignatureVerifyGadget {}.eval(&mut builder)?;
let circuit_data = builder.build::<C>();
Ok(circuit_data.verifier_data())
}
}
impl SignatureVerifyGadget {
/// creates the targets and defines the logic of the circuit
pub fn eval(&self, builder: &mut CircuitBuilder<F, D>) -> Result<SignatureVerifyTarget> {
let selector = builder.add_virtual_bool_target_safe();
let common_data = super::signature::VP.0.common.clone();
// targets related to the 'public inputs' for the verification of the
// `SignatureInternalCircuit` proof.
let pk_targ = builder.add_virtual_value();
let msg_targ = builder.add_virtual_value();
let inp: Vec<Target> = [pk_targ.elements.to_vec(), msg_targ.elements.to_vec()].concat();
let s_targ = builder.hash_n_to_hash_no_pad::<PoseidonHash>(inp);
let verifier_data_targ =
builder.add_virtual_verifier_data(common_data.config.fri_config.cap_height);
let proof_targ = builder.add_virtual_proof_with_pis(&common_data);
let dummy_pi = DUMMY_PUBLIC_INPUTS.clone();
let pk_targ_dummy =
builder.constant_value(Value(dummy_pi[..VALUE_SIZE].try_into().unwrap()));
let msg_targ_dummy = builder.constant_value(Value(
dummy_pi[VALUE_SIZE..VALUE_SIZE * 2].try_into().unwrap(),
));
let s_targ_dummy =
builder.constant_value(Value(dummy_pi[VALUE_SIZE * 2..].try_into().unwrap()));
// connect the {pk, msg, s} with the proof_targ.public_inputs conditionally
let pk_targ_connect = builder.select_value(selector, pk_targ, pk_targ_dummy);
let msg_targ_connect = builder.select_value(selector, msg_targ, msg_targ_dummy);
let s_targ_connect = builder.select_value(
selector,
ValueTarget {
elements: s_targ.elements,
},
s_targ_dummy,
);
for i in 0..VALUE_SIZE {
builder.connect(pk_targ_connect.elements[i], proof_targ.public_inputs[i]);
builder.connect(
msg_targ_connect.elements[i],
proof_targ.public_inputs[VALUE_SIZE + i],
);
builder.connect(
s_targ_connect.elements[i],
proof_targ.public_inputs[(2 * VALUE_SIZE) + i],
);
}
builder.verify_proof::<C>(&proof_targ, &verifier_data_targ, &common_data);
Ok(SignatureVerifyTarget {
verifier_data_targ,
selector,
pk: pk_targ,
msg: msg_targ,
proof: proof_targ,
})
}
}
impl SignatureVerifyTarget {
/// assigns the given values to the targets
pub fn set_targets(
&self,
pw: &mut PartialWitness<F>,
selector: bool,
pk: PublicKey,
msg: Value,
signature: Signature,
) -> Result<()> {
pw.set_bool_target(self.selector, selector)?;
pw.set_target_arr(&self.pk.elements, &pk.0 .0)?;
pw.set_target_arr(&self.msg.elements, &msg.0)?;
// note that this hash is checked again in-circuit at the `SignatureInternalCircuit`
let s = Value(PoseidonHash::hash_no_pad(&[pk.0 .0, msg.0].concat()).elements);
let public_inputs: Vec<F> = [pk.0 .0, msg.0, s.0].concat();
if selector {
pw.set_proof_with_pis_target(
&self.proof,
&ProofWithPublicInputs {
proof: signature.0,
public_inputs,
},
)?;
} else {
pw.set_proof_with_pis_target(
&self.proof,
&ProofWithPublicInputs {
proof: DUMMY_SIGNATURE.0.clone(),
public_inputs: DUMMY_PUBLIC_INPUTS.clone(),
},
)?;
}
pw.set_verifier_data_target(
&self.verifier_data_targ,
&super::signature::VP.0.verifier_only,
)?;
Ok(())
}
}
#[cfg(test)]
pub mod tests {
use crate::backends::plonky2::basetypes::Hash;
use crate::backends::plonky2::primitives::signature::SecretKey;
use super::*;
#[test]
fn test_signature_gadget() -> Result<()> {
// generate a valid signature
let sk = SecretKey::new();
let pk = sk.public_key();
let msg = Value::from(42);
let sig = sk.sign(msg)?;
sig.verify(&pk, msg)?;
// circuit
let config = CircuitConfig::standard_recursion_zk_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let mut pw = PartialWitness::<F>::new();
let targets = SignatureVerifyGadget {}.eval(&mut builder)?;
targets.set_targets(&mut pw, true, pk, msg, sig)?;
// generate & verify proof
let data = builder.build::<C>();
let proof = data.prove(pw)?;
data.verify(proof.clone())?;
// verify the proof with the lazy_static loaded verifier_data (S_VD)
S_VD.verify(ProofWithPublicInputs {
proof: proof.proof.clone(),
public_inputs: vec![],
})?;
Ok(())
}
#[test]
fn test_signature_gadget_disabled() -> Result<()> {
// generate a valid signature
let sk = SecretKey::new();
let pk = sk.public_key();
let msg = Value::from(42);
let sig = sk.sign(msg)?;
// verification should pass
sig.verify(&pk, msg)?;
// replace the message, so that verifications should fail
let msg = Value::from(24);
// expect signature native verification to fail
let v = sig.verify(&pk, Value::from(24));
assert!(v.is_err(), "should fail to verify");
// circuit
let config = CircuitConfig::standard_recursion_zk_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let mut pw = PartialWitness::<F>::new();
let targets = SignatureVerifyGadget {}.eval(&mut builder)?;
targets.set_targets(&mut pw, true, pk.clone(), msg, sig.clone())?; // selector=true
// generate proof, and expect it to fail
let data = builder.build::<C>();
assert!(data.prove(pw).is_err()); // expect prove to fail
// build the circuit again, but now disable the selector that disables
// the in-circuit signature verification
let config = CircuitConfig::standard_recursion_zk_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let mut pw = PartialWitness::<F>::new();
let targets = SignatureVerifyGadget {}.eval(&mut builder)?;
targets.set_targets(&mut pw, false, pk, msg, sig)?; // selector=false
// generate & verify proof
let data = builder.build::<C>();
let proof = data.prove(pw)?;
data.verify(proof.clone())?;
// verify the proof with the lazy_static loaded verifier_data (S_VD)
S_VD.verify(ProofWithPublicInputs {
proof: proof.proof.clone(),
public_inputs: vec![],
})?;
Ok(())
}
}

View file

@ -105,8 +105,8 @@ pub struct Params {
// in a custom predicate // in a custom predicate
pub max_custom_predicate_arity: usize, pub max_custom_predicate_arity: usize,
pub max_custom_batch_size: usize, pub max_custom_batch_size: usize,
// maximum depth for merkle tree gates // maximum depth for merkle tree gadget
pub max_depth_mt_gate: usize, pub max_depth_mt_gadget: usize,
} }
impl Default for Params { impl Default for Params {
@ -121,7 +121,7 @@ impl Default for Params {
max_operation_args: 5, max_operation_args: 5,
max_custom_predicate_arity: 5, max_custom_predicate_arity: 5,
max_custom_batch_size: 5, max_custom_batch_size: 5,
max_depth_mt_gate: 32, max_depth_mt_gadget: 32,
} }
} }
} }