Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

plonk: proof-system: prover: Emit LinkingHint with proof #41

Merged
merged 1 commit into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 41 additions & 46 deletions plonk/src/proof_system/proof_linking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,14 @@ use jf_primitives::{
rescue::RescueParameter,
};
use mpc_relation::{
errors::CircuitError,
gadgets::ecc::SWToTEConParam,
proof_linking::{GroupLayout, PROOF_LINK_WIRE_IDX},
traits::LinkGroup,
PlonkCircuit,
};

use crate::{errors::PlonkError, transcript::PlonkTranscript};

use super::{
structs::{Proof, ProvingKey, VerifyingKey},
structs::{LinkingHint, Proof, ProvingKey, VerifyingKey},
PlonkKzgSnark,
};

Expand All @@ -51,37 +48,29 @@ where
{
/// Link two proofs on a given domain
pub fn link_proofs<T: PlonkTranscript<F>>(
lhs_circuit: &PlonkCircuit<E::ScalarField>,
rhs_circuit: &PlonkCircuit<E::ScalarField>,
lhs_proof: &Proof<E>,
rhs_proof: &Proof<E>,
lhs_link_hint: &LinkingHint<E>,
rhs_link_hint: &LinkingHint<E>,
group_layout: &GroupLayout,
proving_key: &ProvingKey<E>,
link_group: &LinkGroup,
) -> Result<LinkingProof<E>, PlonkError> {
// Get the placement of the group in the circuit's layout
let circuit_layout = lhs_circuit.gen_circuit_layout().map_err(PlonkError::CircuitError)?;
let group_layout = circuit_layout.group_layouts.get(&link_group.id).ok_or_else(|| {
PlonkError::CircuitError(CircuitError::LinkGroupNotFound(format!(
"link group {} not found in layout",
link_group.id
)))
})?;

// Compute the wiring polynomials that encode the proof-linked values
let a1 = lhs_circuit.get_proof_linking_wire_poly();
let a2 = rhs_circuit.get_proof_linking_wire_poly();
let a1 = &lhs_link_hint.linking_wire_poly;
let a2 = &rhs_link_hint.linking_wire_poly;

// Compute the quotient then commit to it
let quotient = Self::compute_linking_quotient(&a1, &a2, group_layout)?;
let quotient = Self::compute_linking_quotient(a1, a2, group_layout)?;
let quotient_commitment = UnivariateKzgPCS::commit(&proving_key.commit_key, &quotient)
.map_err(PlonkError::PCSError)?;

// Squeeze a challenge for the opening
let opening_challenge =
Self::compute_quotient_challenge::<T>(lhs_proof, rhs_proof, &quotient_commitment)?;
let opening_challenge = Self::compute_quotient_challenge::<T>(
&lhs_link_hint.linking_wire_comm,
&rhs_link_hint.linking_wire_comm,
&quotient_commitment,
)?;
let opening_proof = Self::compute_identity_opening(
&a1,
&a2,
a1,
a2,
&quotient,
opening_challenge,
group_layout,
Expand Down Expand Up @@ -164,15 +153,14 @@ where
/// the proofs _after_ they have committed to the wiring polynomials
/// that are being linked
fn compute_quotient_challenge<T: PlonkTranscript<E::BaseField>>(
lhs_proof: &Proof<E>,
rhs_proof: &Proof<E>,
a1_comm: &Commitment<E>,
a2_comm: &Commitment<E>,
quotient_comm: &Commitment<E>,
) -> Result<E::ScalarField, PlonkError> {
let mut transcript = T::new(b"PlonkLinkingProof");

let a_comm1 = lhs_proof.wires_poly_comms[PROOF_LINK_WIRE_IDX];
let a_comm2 = rhs_proof.wires_poly_comms[PROOF_LINK_WIRE_IDX];
transcript.append_commitments(b"linking_wire_comms", &[a_comm1, a_comm2])?;
// We encode the proof linking gates in the first wire polynomial
transcript.append_commitments(b"linking_wire_comms", &[*a1_comm, *a2_comm])?;
transcript.append_commitment(b"quotient_comm", quotient_comm)?;

transcript.get_and_append_challenge::<E>(b"eta")
Expand Down Expand Up @@ -215,6 +203,11 @@ where
P: SWCurveConfig<BaseField = F, ScalarField = E::ScalarField>,
{
/// Verify a linking proof
///
/// The verifier does not have access to the link hint of the proofs -- this
/// exposes wiring information -- so it is simpler to pass a proof
/// reference directly (which the verifier will have). This avoids the need
/// to index into the commitments at the callsite
pub fn verify_link_proof<T: PlonkTranscript<E::BaseField>>(
r1cs_proof1: &Proof<E>,
r1cs_proof2: &Proof<E>,
Expand All @@ -224,11 +217,11 @@ where
) -> Result<(), PlonkError> {
// Squeeze a challenge for the opening
let quotient_comm = &link_proof.quotient_commitment;
let eta = Self::compute_quotient_challenge::<T>(r1cs_proof1, r1cs_proof2, quotient_comm)?;

// Compute a commitment to the proof-linking identity polynomial
let a1_comm = &r1cs_proof1.wires_poly_comms[PROOF_LINK_WIRE_IDX];
let a2_comm = &r1cs_proof2.wires_poly_comms[PROOF_LINK_WIRE_IDX];
let eta = Self::compute_quotient_challenge::<T>(a1_comm, a2_comm, quotient_comm)?;

// Compute a commitment to the proof-linking identity polynomial
let identity_comm =
Self::compute_identity_commitment(a1_comm, a2_comm, quotient_comm, eta, layout);

Expand Down Expand Up @@ -279,7 +272,7 @@ mod test {

use crate::{
proof_system::{
structs::{Proof, ProvingKey, VerifyingKey},
structs::{LinkingHint, Proof, ProvingKey, VerifyingKey},
PlonkKzgSnark, UniversalSNARK,
},
transcript::SolidityTranscript,
Expand Down Expand Up @@ -322,13 +315,14 @@ mod test {
(circuit, group)
}

/// Generate a proof for a circuit
fn gen_test_proof(circuit: &PlonkCircuit<FrBn254>) -> Proof<Bn254> {
/// Generate a proof and link hint for the circuit by proving its r1cs
/// relation
fn gen_test_proof(circuit: &PlonkCircuit<FrBn254>) -> (Proof<Bn254>, LinkingHint<Bn254>) {
let mut rng = thread_rng();
let (pk, _) = gen_keys(circuit);

PlonkKzgSnark::<Bn254>::prove::<_, _, SolidityTranscript>(
&mut rng, circuit, &pk, None, // extra_init_msg
PlonkKzgSnark::<Bn254>::prove_with_link_hint::<_, _, SolidityTranscript>(
&mut rng, circuit, &pk,
)
.unwrap()
}
Expand All @@ -353,22 +347,23 @@ mod test {
let witness = (0..N).map(|_| u64::rand(&mut rng)).collect_vec();

// Generate the two circuits
let (lhs_circuit, group) = gen_test_circuit(&witness);
let (mut lhs_circuit, group) = gen_test_circuit(&witness);
let (rhs_circuit, _) = gen_test_circuit(&witness);

let circuit_layout = lhs_circuit.gen_circuit_layout().unwrap();
let group_layout = circuit_layout.group_layouts.get(&group.id).unwrap();

// Prove each circuit
let (pk, vk) = gen_keys(&lhs_circuit);
let lhs_proof = gen_test_proof(&lhs_circuit);
let rhs_proof = gen_test_proof(&rhs_circuit);
let (lhs_proof, lhs_hint) = gen_test_proof(&lhs_circuit);
let (rhs_proof, rhs_hint) = gen_test_proof(&rhs_circuit);

// Generate a link proof
let proof = PlonkKzgSnark::link_proofs::<SolidityTranscript>(
&lhs_circuit,
&rhs_circuit,
&lhs_proof,
&rhs_proof,
&lhs_hint,
&rhs_hint,
group_layout,
&pk,
&group,
)
.unwrap();

Expand Down
43 changes: 40 additions & 3 deletions plonk/src/proof_system/snark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
use super::{
prover::Prover,
structs::{
BatchProof, Challenges, Oracles, PlookupProof, PlookupProvingKey, PlookupVerifyingKey,
Proof, ProvingKey, VerifyingKey,
BatchProof, Challenges, LinkingHint, Oracles, PlookupProof, PlookupProvingKey,
PlookupVerifyingKey, Proof, ProvingKey, VerifyingKey,
},
verifier::Verifier,
UniversalSNARK,
Expand Down Expand Up @@ -39,7 +39,8 @@ use jf_primitives::{
};
use jf_utils::par_utils::parallelizable_slice_iter;
use mpc_relation::{
constants::compute_coset_representatives, gadgets::ecc::SWToTEConParam, traits::*,
constants::compute_coset_representatives, gadgets::ecc::SWToTEConParam,
proof_linking::PROOF_LINK_WIRE_IDX, traits::*,
};
#[cfg(feature = "parallel")]
use rayon::prelude::*;
Expand Down Expand Up @@ -76,6 +77,42 @@ where
Ok(batch_proof)
}

/// Generate a proof and return a linking hint alongside
pub fn prove_with_link_hint<C, R, T>(
prng: &mut R,
circuit: &C,
prove_key: &ProvingKey<E>,
) -> Result<(Proof<E>, LinkingHint<E>), PlonkError>
where
C: Arithmetization<E::ScalarField>,
C: Circuit<E::ScalarField, Wire = E::ScalarField, Constant = E::ScalarField>,
R: CryptoRng + RngCore,
T: PlonkTranscript<F>,
{
// Prove the relation
let (batch_proof, oracles, _) =
Self::batch_prove_internal::<_, _, T>(prng, &[circuits], &[prove_keys], None)?;

// Compute the linking hint
let hint = LinkingHint {
linking_wire_poly: oracles[0].wire_polys[PROOF_LINK_WIRE_IDX].clone(),
linking_wire_comm: batch_proof.wires_poly_comms_vec[0][PROOF_LINK_WIRE_IDX],
};

Ok((
Proof {
wires_poly_comms: batch_proof.wires_poly_comms_vec[0].clone(),
prod_perm_poly_comm: batch_proof.prod_perm_poly_comms_vec[0],
split_quot_poly_comms: batch_proof.split_quot_poly_comms,
opening_proof: batch_proof.opening_proof,
shifted_opening_proof: batch_proof.shifted_opening_proof,
poly_evals: batch_proof.poly_evals_vec[0].clone(),
plookup_proof: batch_proof.plookup_proofs_vec[0].clone(),
},
hint,
))
}

/// Verify a single aggregated Plonk proof.
pub fn verify_batch_proof<T>(
verify_keys: &[&VerifyingKey<E>],
Expand Down
13 changes: 13 additions & 0 deletions plonk/src/proof_system/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,19 @@ pub struct Proof<E: Pairing> {
pub plookup_proof: Option<PlookupProof<E>>,
}

/// A proof-linking hint generated by the prover in the course of proving an
/// R1CS relation
///
/// The linking hint contains information about the prover's witness needed to
/// link the proof to another proof of a different circuit
#[derive(Debug, Clone)]
pub struct LinkingHint<E: Pairing> {
/// The wire polynomial that encodes the proof-linking gates for the circuit
pub linking_wire_poly: DensePolynomial<E::ScalarField>,
/// The commitment to the linking wire poly generated while proving
pub linking_wire_comm: Commitment<E>,
}

impl<E, P> TryFrom<Vec<E::BaseField>> for Proof<E>
where
E: Pairing<G1Affine = Affine<P>>,
Expand Down
9 changes: 9 additions & 0 deletions relation/src/constraint_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,13 @@ where
/// Groups without specified layouts will be given a layout when the circuit
/// layout is determined
pub(crate) link_group_layouts: HashMap<String, GroupLayout>,
/// The layout of the circuit if one has been generated
///
/// After applying a layout to the circuit, the circuit's topology changes,
/// so subsequent calls to `gen_circuit_layout` will change the layout.
/// This field is used to cache the layout so that it can be reused even
/// after the layout is applied
pub(crate) layout: Option<CircuitLayout>,

/// The Plonk parameters.
plonk_params: PlonkParams,
Expand Down Expand Up @@ -262,6 +269,7 @@ impl<F: FftField> PlonkCircuit<F> {
eval_domain: Radix2EvaluationDomain::new(1).unwrap(),
link_groups: HashMap::new(),
link_group_layouts: HashMap::new(),
layout: None,
plonk_params,
num_table_elems: 0,
table_gate_ids: vec![],
Expand Down Expand Up @@ -1140,6 +1148,7 @@ impl<F: PrimeField> PlonkCircuit<F> {
// `link_groups` must be empty for both proofs
link_groups: HashMap::new(),
link_group_layouts: HashMap::new(),
layout: None,
plonk_params: self.plonk_params,
num_table_elems: 0,
table_gate_ids: vec![],
Expand Down
8 changes: 7 additions & 1 deletion relation/src/proof_linking/arithmetization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,11 @@ impl GroupLayout {
impl<F: PrimeField> PlonkCircuit<F> {
/// Generate a layout of the circuit, including where proof-linking gates
/// will be placed
pub fn gen_circuit_layout(&self) -> Result<CircuitLayout, CircuitError> {
pub fn gen_circuit_layout(&mut self) -> Result<CircuitLayout, CircuitError> {
if let Some(layout) = &self.layout {
return Ok(layout.clone());
}

// 1. Place the proof linking groups with specific layouts into the circuit
let alignment = self.current_circuit_alignment();
let mut sorted_placements = self
Expand All @@ -82,6 +86,8 @@ impl<F: PrimeField> PlonkCircuit<F> {
CircuitLayout { n_inputs: self.num_inputs(), n_gates: self.num_gates(), group_layouts };

self.validate_layout(&circuit_layout)?;
self.layout = Some(circuit_layout.clone());

Ok(circuit_layout)
}

Expand Down
Loading