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::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(¶ms))
|
.map(|_| builder.add_virtual_statement(¶ms))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let operation_verify = OperationVerifyGate {
|
let operation_verify = OperationVerifyGadget {
|
||||||
params: params.clone(),
|
params: params.clone(),
|
||||||
}
|
}
|
||||||
.eval(
|
.eval(
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue