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:
parent
d00ff95f41
commit
0637f52573
7 changed files with 325 additions and 42 deletions
|
|
@ -21,7 +21,7 @@ use crate::backends::plonky2::circuits::common::{
|
|||
};
|
||||
use crate::backends::plonky2::primitives::merkletree::MerkleTree;
|
||||
use crate::backends::plonky2::primitives::merkletree::{
|
||||
MerkleProofExistenceGate, MerkleProofExistenceTarget,
|
||||
MerkleProofExistenceGadget, MerkleProofExistenceTarget,
|
||||
};
|
||||
use crate::middleware::{
|
||||
hash_str, AnchoredKey, NativeOperation, NativePredicate, Params, PodType, Statement,
|
||||
|
|
@ -34,18 +34,18 @@ use super::common::Flattenable;
|
|||
// SignedPod verification
|
||||
//
|
||||
|
||||
struct SignedPodVerifyGate {
|
||||
struct SignedPodVerifyGadget {
|
||||
params: Params,
|
||||
}
|
||||
|
||||
impl SignedPodVerifyGate {
|
||||
impl SignedPodVerifyGadget {
|
||||
fn eval(&self, builder: &mut CircuitBuilder<F, D>) -> Result<SignedPodVerifyTarget> {
|
||||
// 2. Verify id
|
||||
let id = builder.add_virtual_hash();
|
||||
let mut mt_proofs = Vec::new();
|
||||
for _ in 0..self.params.max_signed_pod_values {
|
||||
let mt_proof = MerkleProofExistenceGate {
|
||||
max_depth: self.params.max_depth_mt_gate,
|
||||
let mt_proof = MerkleProofExistenceGadget {
|
||||
max_depth: self.params.max_depth_mt_gadget,
|
||||
}
|
||||
.eval(builder)?;
|
||||
builder.connect_hashes(id, mt_proof.root);
|
||||
|
|
@ -100,7 +100,7 @@ impl SignedPodVerifyTarget {
|
|||
|
||||
fn set_targets(&self, pw: &mut PartialWitness<F>, input: &SignedPodVerifyInput) -> Result<()> {
|
||||
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
|
||||
// repetitions of the type entry (which always exists)
|
||||
|
|
@ -125,11 +125,11 @@ impl SignedPodVerifyTarget {
|
|||
// MainPod verification
|
||||
//
|
||||
|
||||
struct OperationVerifyGate {
|
||||
struct OperationVerifyGadget {
|
||||
params: Params,
|
||||
}
|
||||
|
||||
impl OperationVerifyGate {
|
||||
impl OperationVerifyGadget {
|
||||
fn eval(
|
||||
&self,
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
|
|
@ -359,17 +359,17 @@ impl OperationVerifyTarget {
|
|||
}
|
||||
}
|
||||
|
||||
struct MainPodVerifyGate {
|
||||
struct MainPodVerifyGadget {
|
||||
params: Params,
|
||||
}
|
||||
|
||||
impl MainPodVerifyGate {
|
||||
impl MainPodVerifyGadget {
|
||||
fn eval(&self, builder: &mut CircuitBuilder<F, D>) -> Result<MainPodVerifyTarget> {
|
||||
let params = &self.params;
|
||||
// 1. Verify all input signed pods
|
||||
let mut signed_pods = Vec::new();
|
||||
for _ in 0..params.max_input_signed_pods {
|
||||
let signed_pod = SignedPodVerifyGate {
|
||||
let signed_pod = SignedPodVerifyGadget {
|
||||
params: params.clone(),
|
||||
}
|
||||
.eval(builder)?;
|
||||
|
|
@ -421,7 +421,7 @@ impl MainPodVerifyGate {
|
|||
let mut op_verifications = Vec::new();
|
||||
for (i, (st, op)) in input_statements.iter().zip(operations.iter()).enumerate() {
|
||||
let prev_statements = &statements[..input_statements_offset + i - 1];
|
||||
let op_verification = OperationVerifyGate {
|
||||
let op_verification = OperationVerifyGadget {
|
||||
params: params.clone(),
|
||||
}
|
||||
.eval(builder, st, op, prev_statements)?;
|
||||
|
|
@ -478,7 +478,7 @@ pub struct MainPodVerifyCircuit {
|
|||
|
||||
impl MainPodVerifyCircuit {
|
||||
pub fn eval(&self, builder: &mut CircuitBuilder<F, D>) -> Result<MainPodVerifyTarget> {
|
||||
let main_pod = MainPodVerifyGate {
|
||||
let main_pod = MainPodVerifyGadget {
|
||||
params: self.params.clone(),
|
||||
}
|
||||
.eval(builder)?;
|
||||
|
|
@ -505,7 +505,7 @@ mod tests {
|
|||
let config = CircuitConfig::standard_recursion_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 kvs = [
|
||||
|
|
@ -543,7 +543,7 @@ mod tests {
|
|||
.map(|_| builder.add_virtual_statement(¶ms))
|
||||
.collect();
|
||||
|
||||
let operation_verify = OperationVerifyGate {
|
||||
let operation_verify = OperationVerifyGadget {
|
||||
params: params.clone(),
|
||||
}
|
||||
.eval(
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ use std::iter::IntoIterator;
|
|||
use crate::backends::counter;
|
||||
use crate::backends::plonky2::basetypes::{hash_fields, Hash, Value, EMPTY_HASH, F};
|
||||
|
||||
// mod merkletree_circuit;
|
||||
pub use super::merkletree_circuit::*;
|
||||
|
||||
/// Implements the MerkleTree specified at
|
||||
|
|
|
|||
|
|
@ -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::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.
|
||||
/// If only proofs of existence are needed, use `MerkleProofExistenceGate`,
|
||||
/// If only proofs of existence are needed, use `MerkleProofExistenceGadget`,
|
||||
/// which requires less amount of constraints.
|
||||
pub struct MerkleProofGate {
|
||||
pub struct MerkleProofGadget {
|
||||
pub max_depth: usize,
|
||||
}
|
||||
|
||||
|
|
@ -47,7 +47,7 @@ pub struct MerkleProofTarget {
|
|||
pub other_value: ValueTarget,
|
||||
}
|
||||
|
||||
impl MerkleProofGate {
|
||||
impl MerkleProofGadget {
|
||||
/// creates the targets and defines the logic of the circuit
|
||||
pub fn eval(&self, builder: &mut CircuitBuilder<F, D>) -> Result<MerkleProofTarget> {
|
||||
// create the targets
|
||||
|
|
@ -179,7 +179,7 @@ impl MerkleProofTarget {
|
|||
|
||||
/// `MerkleProofExistenceCircuit` allows to verify proofs of existence only. If
|
||||
/// proofs of non-existence are needed, use `MerkleProofCircuit`.
|
||||
pub struct MerkleProofExistenceGate {
|
||||
pub struct MerkleProofExistenceGadget {
|
||||
pub max_depth: usize,
|
||||
}
|
||||
|
||||
|
|
@ -191,7 +191,7 @@ pub struct MerkleProofExistenceTarget {
|
|||
pub siblings: Vec<HashOutTarget>,
|
||||
}
|
||||
|
||||
impl MerkleProofExistenceGate {
|
||||
impl MerkleProofExistenceGadget {
|
||||
/// creates the targets and defines the logic of the circuit
|
||||
pub fn eval(&self, builder: &mut CircuitBuilder<F, D>) -> Result<MerkleProofExistenceTarget> {
|
||||
// create the targets
|
||||
|
|
@ -486,7 +486,7 @@ pub mod tests {
|
|||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
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)?;
|
||||
|
||||
// generate & verify proof
|
||||
|
|
@ -525,7 +525,7 @@ pub mod tests {
|
|||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
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)?;
|
||||
|
||||
// generate & verify proof
|
||||
|
|
@ -592,7 +592,7 @@ pub mod tests {
|
|||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
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)?;
|
||||
|
||||
// generate & verify proof
|
||||
|
|
@ -633,7 +633,7 @@ pub mod tests {
|
|||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
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)?;
|
||||
|
||||
// generate proof, expecting it to fail (since we're using the wrong
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
pub mod merkletree;
|
||||
mod merkletree_circuit;
|
||||
pub mod signature;
|
||||
mod signature_circuit;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//! Proof-based signatures using Plonky2 proofs, following
|
||||
//! https://eprint.iacr.org/2024/1553 .
|
||||
use anyhow::Result;
|
||||
use lazy_static::lazy_static;
|
||||
use plonky2::{
|
||||
field::types::Sample,
|
||||
hash::{
|
||||
|
|
@ -21,23 +22,31 @@ use plonky2::{
|
|||
|
||||
use crate::backends::plonky2::basetypes::{Proof, Value, C, D, F, VALUE_SIZE};
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
pub use super::signature_circuit::*;
|
||||
|
||||
lazy_static! {
|
||||
static ref PP: ProverParams = Signature::prover_params().unwrap();
|
||||
static ref VP: VerifierParams = Signature::verifier_params().unwrap();
|
||||
/// Signature prover parameters
|
||||
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 {
|
||||
prover: ProverCircuitData<F, C, D>,
|
||||
circuit: SignatureCircuit,
|
||||
circuit: SignatureInternalCircuit,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct VerifierParams(VerifierCircuitData<F, C, D>);
|
||||
pub struct VerifierParams(pub(crate) VerifierCircuitData<F, C, D>);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SecretKey(Value);
|
||||
pub struct SecretKey(pub(crate) Value);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PublicKey(pub(crate) Value);
|
||||
|
|
@ -90,12 +99,12 @@ impl Signature {
|
|||
Ok((pp, vp))
|
||||
}
|
||||
|
||||
fn builder() -> Result<(CircuitBuilder<F, D>, SignatureCircuit)> {
|
||||
fn builder() -> Result<(CircuitBuilder<F, D>, SignatureInternalCircuit)> {
|
||||
// notice that we use the 'zk' config
|
||||
let config = CircuitConfig::standard_recursion_zk_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))
|
||||
}
|
||||
|
|
@ -113,21 +122,35 @@ impl Signature {
|
|||
}
|
||||
}
|
||||
|
||||
/// The SignatureCircuit implements the circuit used for the proof of the
|
||||
/// argument described at https://eprint.iacr.org/2024/1553.
|
||||
fn dummy_public_inputs() -> Result<Vec<F>> {
|
||||
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
|
||||
/// knows the secret (sk) such that:
|
||||
/// i) pk == H(sk)
|
||||
/// ii) s == H(pk, msg)
|
||||
struct SignatureCircuit {
|
||||
struct SignatureInternalCircuit {
|
||||
sk_targ: Vec<Target>,
|
||||
pk_targ: HashOutTarget,
|
||||
msg_targ: Vec<Target>,
|
||||
s_targ: HashOutTarget,
|
||||
}
|
||||
|
||||
impl SignatureCircuit {
|
||||
impl SignatureInternalCircuit {
|
||||
/// creates the targets and defines the logic of the circuit
|
||||
fn add_targets(builder: &mut CircuitBuilder<F, D>) -> Result<Self> {
|
||||
// create the targets
|
||||
|
|
@ -182,7 +205,6 @@ pub mod tests {
|
|||
|
||||
use super::*;
|
||||
|
||||
// Note: this test must be run with the `--release` flag.
|
||||
#[test]
|
||||
fn test_signature() -> Result<()> {
|
||||
let sk = SecretKey::new();
|
||||
|
|
@ -203,4 +225,14 @@ pub mod tests {
|
|||
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
251
src/backends/plonky2/primitives/signature_circuit.rs
Normal file
251
src/backends/plonky2/primitives/signature_circuit.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue