From 7793b5a956ba78e7c38a7392bcef1f33745df4b0 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 14 Jul 2021 20:54:30 +0200 Subject: [PATCH] Identify problem in GMiMc --- src/circuit_builder.rs | 73 ++++++++++++++- src/circuit_data.rs | 15 ++-- src/copy_constraint.rs | 21 +++++ src/fri/prover.rs | 4 + src/fri/recursive_verifier.rs | 22 ++++- src/lib.rs | 1 + src/merkle_proofs.rs | 162 ++++++++++++++++------------------ src/prover.rs | 9 +- src/recursive_verifier.rs | 17 +++- src/util/marking.rs | 13 ++- src/witness.rs | 16 ++-- 11 files changed, 227 insertions(+), 126 deletions(-) create mode 100644 src/copy_constraint.rs diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 1e0bfaef..a2c6d935 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -1,5 +1,6 @@ use std::collections::{HashMap, HashSet}; use std::convert::TryInto; +use std::sync::Arc; use std::time::Instant; use log::info; @@ -8,6 +9,7 @@ use crate::circuit_data::{ CircuitConfig, CircuitData, CommonCircuitData, ProverCircuitData, ProverOnlyCircuitData, VerifierCircuitData, VerifierOnlyCircuitData, }; +use crate::copy_constraint::CopyConstraint; use crate::field::cosets::get_unique_coset_shifts; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::Extendable; @@ -23,6 +25,7 @@ use crate::polynomial::commitment::ListPolynomialCommitment; use crate::polynomial::polynomial::PolynomialValues; use crate::proof::HashTarget; use crate::target::Target; +use crate::util::marking::{Markable, MarkedTargets}; use crate::util::partial_products::num_partial_products; use crate::util::{log2_ceil, log2_strict, transpose, transpose_poly_values}; use crate::wire::Wire; @@ -42,7 +45,11 @@ pub struct CircuitBuilder, const D: usize> { /// The next available index for a `VirtualTarget`. pub virtual_target_index: usize, - copy_constraints: Vec<(Target, Target)>, + copy_constraints: Vec, + + context: String, + + pub marked_targets: Vec, /// Generators used to generate the witness. pub generators: Vec>>, @@ -60,6 +67,8 @@ impl, const D: usize> CircuitBuilder { public_input_index: 0, virtual_target_index: 0, copy_constraints: Vec::new(), + context: String::new(), + marked_targets: Vec::new(), generators: Vec::new(), constants_to_targets: HashMap::new(), targets_to_constants: HashMap::new(), @@ -158,12 +167,29 @@ impl, const D: usize> CircuitBuilder { self.assert_equal(src, dst); } + /// Same as `route` with a named copy constraint. + pub fn named_route(&mut self, src: Target, dst: Target, name: String) { + self.generate_copy(src, dst); + self.named_assert_equal(src, dst, name); + } + pub fn route_extension(&mut self, src: ExtensionTarget, dst: ExtensionTarget) { for i in 0..D { self.route(src.0[i], dst.0[i]); } } + pub fn named_route_extension( + &mut self, + src: ExtensionTarget, + dst: ExtensionTarget, + name: String, + ) { + for i in 0..D { + self.named_route(src.0[i], dst.0[i], format!("{}: limb {}", name, i)); + } + } + /// Adds a generator which will copy `src` to `dst`. pub fn generate_copy(&mut self, src: Target, dst: Target) { self.add_generator(CopyGenerator { src, dst }); @@ -180,7 +206,24 @@ impl, const D: usize> CircuitBuilder { y.is_routable(&self.config), "Tried to route a wire that isn't routable" ); - self.copy_constraints.push((x, y)); + self.copy_constraints + .push(CopyConstraint::new((x, y), self.context.clone())); + } + + /// Same as `assert_equal` for a named copy constraint. + pub fn named_assert_equal(&mut self, x: Target, y: Target, name: String) { + assert!( + x.is_routable(&self.config), + "Tried to route a wire that isn't routable" + ); + assert!( + y.is_routable(&self.config), + "Tried to route a wire that isn't routable" + ); + self.copy_constraints.push(CopyConstraint::new( + (x, y), + format!("{}: {}", self.context.clone(), name), + )); } pub fn assert_zero(&mut self, x: Target) { @@ -194,6 +237,18 @@ impl, const D: usize> CircuitBuilder { } } + pub fn named_assert_equal_extension( + &mut self, + x: ExtensionTarget, + y: ExtensionTarget, + name: String, + ) { + for i in 0..D { + self.assert_equal(x.0[i], y.0[i]); + self.named_assert_equal(x.0[i], y.0[i], format!("{}: limb {}", name, i)); + } + } + pub fn add_generators(&mut self, generators: Vec>>) { self.generators.extend(generators); } @@ -249,6 +304,17 @@ impl, const D: usize> CircuitBuilder { self.targets_to_constants.get(&target).cloned() } + pub fn set_context(&mut self, new_context: &str) { + self.context = new_context.to_string(); + } + + pub fn add_marked(&mut self, targets: Arc, name: &str) { + self.marked_targets.push(MarkedTargets { + targets, + name: name.to_string(), + }) + } + /// The number of polynomial values that will be revealed per opening, both for the "regular" /// polynomials and for the Z polynomials. Because calculating these values involves a recursive /// dependence (the amount of blinding depends on the degree, which depends on the blinding), @@ -402,7 +468,7 @@ impl, const D: usize> CircuitBuilder { target_partition.add(Target::VirtualTarget { index }); } - for &(a, b) in &self.copy_constraints { + for &CopyConstraint { pair: (a, b), .. } in &self.copy_constraints { target_partition.merge(a, b); } @@ -457,6 +523,7 @@ impl, const D: usize> CircuitBuilder { subgroup, copy_constraints: self.copy_constraints, gate_instances: self.gate_instances, + marked_targets: self.marked_targets, }; // The HashSet of gates will have a non-deterministic order. When converting to a Vec, we diff --git a/src/circuit_data.rs b/src/circuit_data.rs index 91afdd23..e2df2755 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -2,6 +2,7 @@ use std::ops::{Range, RangeFrom}; use anyhow::Result; +use crate::copy_constraint::CopyConstraint; use crate::field::extension_field::Extendable; use crate::field::field::Field; use crate::fri::FriConfig; @@ -79,14 +80,7 @@ pub struct CircuitData, const D: usize> { impl, const D: usize> CircuitData { pub fn prove(&self, inputs: PartialWitness) -> Proof { - prove(&self.prover_only, &self.common, inputs, vec![]) - } - pub fn prove_marked( - &self, - inputs: PartialWitness, - marked: Vec, - ) -> Proof { - prove(&self.prover_only, &self.common, inputs, marked) + prove(&self.prover_only, &self.common, inputs) } pub fn verify(&self, proof: Proof) -> Result<()> { @@ -108,7 +102,7 @@ pub struct ProverCircuitData, const D: usize> { impl, const D: usize> ProverCircuitData { pub fn prove(&self, inputs: PartialWitness) -> Proof { - prove(&self.prover_only, &self.common, inputs, vec![]) + prove(&self.prover_only, &self.common, inputs) } } @@ -134,9 +128,10 @@ pub(crate) struct ProverOnlyCircuitData, const D: usize> { /// Subgroup of order `degree`. pub subgroup: Vec, /// The circuit's copy constraints. - pub copy_constraints: Vec<(Target, Target)>, + pub copy_constraints: Vec, /// The concrete placement of each gate in the circuit. pub gate_instances: Vec>, + pub marked_targets: Vec, } /// Circuit data required by the verifier, but not the prover. diff --git a/src/copy_constraint.rs b/src/copy_constraint.rs new file mode 100644 index 00000000..0798f2a8 --- /dev/null +++ b/src/copy_constraint.rs @@ -0,0 +1,21 @@ +use crate::target::Target; + +pub struct CopyConstraint { + pub pair: (Target, Target), + pub name: String, +} + +impl From<(Target, Target)> for CopyConstraint { + fn from(pair: (Target, Target)) -> Self { + Self { + pair, + name: String::default(), + } + } +} + +impl CopyConstraint { + pub fn new(pair: (Target, Target), name: String) -> Self { + Self { pair, name } + } +} diff --git a/src/fri/prover.rs b/src/fri/prover.rs index f937ee55..c0c37391 100644 --- a/src/fri/prover.rs +++ b/src/fri/prover.rs @@ -2,6 +2,7 @@ use crate::field::extension_field::{flatten, unflatten, Extendable}; use crate::field::field::Field; use crate::fri::FriConfig; use crate::hash::hash_n_to_1; +use crate::merkle_proofs::verify_merkle_proof; use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; use crate::plonk_common::reduce_with_powers; @@ -139,6 +140,9 @@ fn fri_prover_query_round, const D: usize>( .iter() .map(|t| (t.get(x_index).to_vec(), t.prove(x_index))) .collect::>(); + for ((v, p), t) in initial_proof.iter().zip(initial_merkle_trees.iter()) { + verify_merkle_proof(v.clone(), x_index, t.root, p, false).unwrap(); + } for (i, tree) in trees.iter().enumerate() { let arity_bits = config.reduction_arity_bits[i]; let arity = 1 << arity_bits; diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index f76a7506..bfe0275f 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use env_logger::builder; use itertools::izip; @@ -89,7 +91,7 @@ impl, const D: usize> CircuitBuilder { // Size of the LDE domain. let n = proof.final_poly.len() << total_arities; - // Recover the random betas used in the FRI reductions. + self.set_context("Recover the random betas used in the FRI reductions."); let betas = proof .commit_phase_merkle_roots .iter() @@ -100,7 +102,7 @@ impl, const D: usize> CircuitBuilder { .collect::>(); challenger.observe_extension_elements(&proof.final_poly.0); - // Check PoW. + self.set_context("Check PoW"); self.fri_verify_proof_of_work(proof, challenger, config); // Check that parameters are coherent. @@ -136,7 +138,19 @@ impl, const D: usize> CircuitBuilder { proof: &FriInitialTreeProofTarget, initial_merkle_roots: &[HashTarget], ) { - for ((evals, merkle_proof), &root) in proof.evals_proofs.iter().zip(initial_merkle_roots) { + for (i, ((evals, merkle_proof), &root)) in proof + .evals_proofs + .iter() + .zip(initial_merkle_roots) + .enumerate() + { + self.set_context(&format!("Verify {}-th initial Merkle proof.", i)); + if i == 0 { + self.add_marked(Arc::new(evals.clone()), "Evals"); + self.add_marked(Arc::new(merkle_proof.siblings.clone()), "merkle proof"); + self.add_marked(Arc::new(root.clone()), "root"); + self.add_marked(Arc::new(x_index.clone()), "x_index"); + } self.verify_merkle_proof(evals.clone(), x_index, root, merkle_proof); } } @@ -262,6 +276,7 @@ impl, const D: usize> CircuitBuilder { x_index = self.split_low_high(x_index, n_log, 64).0; let mut x_index_num_bits = n_log; let mut domain_size = n; + self.set_context("Check FRI initial proof."); self.fri_verify_initial_proof( x_index, &round_proof.initial_trees_proof, @@ -304,6 +319,7 @@ impl, const D: usize> CircuitBuilder { self.split_low_high(x_index, arity_bits, x_index_num_bits); evals = self.insert(low_x_index, e_x, evals); evaluations.push(evals); + self.set_context("Verify FRI round Merkle proof."); self.verify_merkle_proof( flatten_target(&evaluations[i]), high_x_index, diff --git a/src/lib.rs b/src/lib.rs index 50c0bb4e..8461d201 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ pub mod circuit_builder; pub mod circuit_data; +pub mod copy_constraint; pub mod field; pub mod fri; pub mod gadgets; diff --git a/src/merkle_proofs.rs b/src/merkle_proofs.rs index fee57d19..96d42a5f 100644 --- a/src/merkle_proofs.rs +++ b/src/merkle_proofs.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use anyhow::{ensure, Result}; use crate::circuit_builder::CircuitBuilder; @@ -57,81 +59,6 @@ pub(crate) fn verify_merkle_proof( } impl, const D: usize> CircuitBuilder { - pub(crate) fn verify_merkle_proof_marked( - &mut self, - leaf_data: Vec, - leaf_index: Target, - merkle_root: HashTarget, - proof: &MerkleProofTarget, - marked: &mut Vec, - ) { - let zero = self.zero(); - let height = proof.siblings.len(); - let purported_index_bits = self.split_le_virtual(leaf_index, height); - - let mut state: HashTarget = self.hash_or_noop(leaf_data); - let mut acc_leaf_index = zero; - - for (bit, &sibling) in purported_index_bits.into_iter().zip(&proof.siblings) { - let gate = self - .add_gate_no_constants(GMiMCGate::::with_automatic_constants()); - - let swap_wire = GMiMCGate::::WIRE_SWAP; - let swap_wire = Target::Wire(Wire { - gate, - input: swap_wire, - }); - self.generate_copy(bit, swap_wire); - - let old_acc_wire = GMiMCGate::::WIRE_INDEX_ACCUMULATOR_OLD; - let old_acc_wire = Target::Wire(Wire { - gate, - input: old_acc_wire, - }); - self.route(acc_leaf_index, old_acc_wire); - - let new_acc_wire = GMiMCGate::::WIRE_INDEX_ACCUMULATOR_NEW; - let new_acc_wire = Target::Wire(Wire { - gate, - input: new_acc_wire, - }); - acc_leaf_index = new_acc_wire; - - let input_wires = (0..12) - .map(|i| { - Target::Wire(Wire { - gate, - input: GMiMCGate::::wire_input(i), - }) - }) - .collect::>(); - - for i in 0..4 { - self.route(state.elements[i], input_wires[i]); - self.route(sibling.elements[i], input_wires[4 + i]); - self.route(zero, input_wires[8 + i]); - } - - state = HashTarget::from_vec( - (0..4) - .map(|i| { - Target::Wire(Wire { - gate, - input: GMiMCGate::::wire_output(i), - }) - }) - .collect(), - ) - } - - // self.assert_equal(acc_leaf_index, leaf_index); - marked.push(MarkedTargets { - targets: Box::new(acc_leaf_index), - name: "acc leaf".to_string(), - }); - - self.assert_hashes_equal(state, merkle_root) - } /// Verifies that the given leaf data is present at the given index in the Merkle tree with the /// given root. pub(crate) fn verify_merkle_proof( @@ -149,6 +76,7 @@ impl, const D: usize> CircuitBuilder { let mut acc_leaf_index = zero; for (bit, &sibling) in purported_index_bits.into_iter().zip(&proof.siblings) { + self.add_marked(Arc::new(state), "current digest"); let gate = self .add_gate_no_constants(GMiMCGate::::with_automatic_constants()); @@ -203,7 +131,7 @@ impl, const D: usize> CircuitBuilder { let leaf_index_rev = self.reverse_limbs::<2>(leaf_index, height); self.assert_equal(acc_leaf_index, leaf_index_rev); - self.assert_hashes_equal(state, merkle_root) + self.named_assert_hashes_equal(state, merkle_root, "Check Merkle root".into()) } pub(crate) fn assert_hashes_equal(&mut self, x: HashTarget, y: HashTarget) { @@ -211,6 +139,16 @@ impl, const D: usize> CircuitBuilder { self.assert_equal(x.elements[i], y.elements[i]); } } + + pub(crate) fn named_assert_hashes_equal(&mut self, x: HashTarget, y: HashTarget, name: String) { + for i in 0..4 { + self.named_assert_equal( + x.elements[i], + y.elements[i], + format!("{}: {}-th hash element", name, i), + ); + } + } } #[cfg(test)] @@ -239,7 +177,6 @@ mod tests { let config = CircuitConfig::large_config(); let mut builder = CircuitBuilder::::new(config); let mut pw = PartialWitness::new(); - let mut marked = Vec::new(); let log_n = 8; let n = 1 << log_n; @@ -265,19 +202,70 @@ mod tests { pw.set_target(data[j], tree.leaves[i][j]); } - marked.push(MarkedTargets { - targets: Box::new(i_c), - name: "i_c".to_string(), - }); - marked.push(MarkedTargets { - targets: Box::new(builder.reverse_limbs::<2>(i_c, log_n)), - name: "rev i_c".to_string(), - }); - builder.verify_merkle_proof_marked(data.clone(), i_c, root_t, &proof_t, &mut marked); builder.verify_merkle_proof(data, i_c, root_t, &proof_t); let data = builder.build(); - let proof = data.prove_marked(pw, marked); + let proof = data.prove(pw); + + verify(proof, &data.verifier_only, &data.common) + } + + #[test] + fn test_recursive_merkle_proof_yo() -> Result<()> { + type F = CrandallField; + type FF = QuarticCrandallField; + let config = CircuitConfig::large_config(); + let mut builder = CircuitBuilder::::new(config); + let mut pw = PartialWitness::new(); + + let eval = vec![ + 8165005271518921330, + 6083226207459673392, + 9958534500108693972, + 3430614617054831715, + 14276647488823198467, + 11751680815846448477, + 2771303161388554632, + 2371046485289351947, + 16743918419162514074, + 9932615810638040318, + 16314448410395528119, + 1511019414432045441, + 5645123553081661379, + 9778873694114674382, + 10629301051878288289, + 16655634835422730769, + 7474748727207643713, + 8501202586470516512, + 5612524789765317534, + 3026252715636633329, + 15131263578183166645, + 1869341605741303173, + 14645831398335944979, + 8356334351657818532, + 4888183615701827634, + 5994174007215505657, + 11524125964783895772, + 2202081323880269694, + 9827048951184368953, + 12675978139336549297, + 5868550852792001156, + ]; + let eval = eval + .into_iter() + .map(F::from_canonical_usize) + .collect::>(); + let data = builder.add_virtual_targets(eval.len()); + for j in 0..data.len() { + pw.set_target(data[j], eval[j]); + } + + dbg!(hash_or_noop(eval.clone())); + let hash = builder.hash_or_noop(data.clone()); + builder.add_marked(Arc::new(hash), "hash test"); + + let data = builder.build(); + let proof = data.prove(pw); verify(proof, &data.verifier_only, &data.common) } diff --git a/src/prover.rs b/src/prover.rs index aa0ecf30..4ff72608 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -27,7 +27,6 @@ pub(crate) fn prove, const D: usize>( prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, inputs: PartialWitness, - marked: Vec, ) -> Proof { let fri_config = &common_data.config.fri_config; let config = &common_data.config; @@ -45,6 +44,10 @@ pub(crate) fn prove, const D: usize>( "to generate witness" ); + for m in &prover_data.marked_targets { + m.display(&partial_witness); + } + timed!( partial_witness .check_copy_constraints(&prover_data.copy_constraints, &prover_data.gate_instances) @@ -57,10 +60,6 @@ pub(crate) fn prove, const D: usize>( "to compute full witness" ); - for m in marked { - m.display(&witness); - } - let wires_values: Vec> = timed!( witness .wire_values diff --git a/src/recursive_verifier.rs b/src/recursive_verifier.rs index 46a52cd3..2557402a 100644 --- a/src/recursive_verifier.rs +++ b/src/recursive_verifier.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use env_logger::builder; use crate::circuit_builder::CircuitBuilder; @@ -33,6 +35,7 @@ impl, const D: usize> CircuitBuilder { let mut challenger = RecursiveChallenger::new(self); + self.set_context("Challenger observes proof and generates challenges."); let digest = HashTarget::from_vec(self.constants(&inner_common_data.circuit_digest.elements)); challenger.observe_hash(&digest); @@ -59,7 +62,7 @@ impl, const D: usize> CircuitBuilder { let partial_products = &proof.openings.partial_products; let zeta_pow_deg = self.exp_u64_extension(zeta, inner_common_data.degree() as u64); - // Evaluate the vanishing polynomial at our challenge point, zeta. + self.set_context("Evaluate the vanishing polynomial at our challenge point, zeta."); let vanishing_polys_zeta = eval_vanishing_poly_recursively( self, inner_common_data, @@ -78,9 +81,10 @@ impl, const D: usize> CircuitBuilder { marked.push(MarkedTargets { name: "vanishing polys".into(), - targets: Box::new(vanishing_polys_zeta[0].clone()), + targets: Arc::new(vanishing_polys_zeta[0].clone()), }); + self.set_context("Check vanishing and quotient polynomials."); let quotient_polys_zeta = &proof.openings.quotient_polys; let zeta_pow_deg = self.exp_u64_extension(zeta, 1 << inner_common_data.degree_bits as u64); let z_h_zeta = self.sub_extension(zeta_pow_deg, one); @@ -91,7 +95,11 @@ impl, const D: usize> CircuitBuilder { let mut scale = ReducingFactorTarget::new(zeta_pow_deg); let mut rhs = scale.reduce(chunk, self); rhs = self.mul_extension(z_h_zeta, rhs); - self.route_extension(vanishing_polys_zeta[i], rhs); + self.named_route_extension( + vanishing_polys_zeta[i], + rhs, + format!("Vanishing polynomial == Z_H * quotient, challenge {}", i), + ); } let evaluations = proof.openings.clone(); @@ -355,8 +363,9 @@ mod tests { builder.add_recursive_verifier(pt, &config, &inner_data, &cd, &mut marked); dbg!(builder.num_gates()); + dbg!(builder.marked_targets.len()); let data = builder.build(); - let recursive_proof = data.prove_marked(pw, marked); + let recursive_proof = data.prove(pw); verify(recursive_proof, &data.verifier_only, &data.common).unwrap(); } diff --git a/src/util/marking.rs b/src/util/marking.rs index e4a13499..fd0a9e37 100644 --- a/src/util/marking.rs +++ b/src/util/marking.rs @@ -1,4 +1,5 @@ use std::convert::TryInto; +use std::sync::Arc; use crate::field::extension_field::target::ExtensionTarget; use crate::field::field::Field; @@ -6,7 +7,7 @@ use crate::proof::HashTarget; use crate::target::Target; use crate::witness::{PartialWitness, Witness}; -pub trait Markable { +pub trait Markable: 'static + Send + Sync { fn targets(&self) -> Vec; } @@ -34,20 +35,18 @@ impl Markable for Vec { } } +#[derive(Clone)] pub struct MarkedTargets { - pub targets: Box, + pub targets: Arc, pub name: String, } impl MarkedTargets { - pub fn display(&self, wit: &Witness) { + pub fn display(&self, pw: &PartialWitness) { let targets = self.targets.targets(); println!("Values for {}:", self.name); for &t in &targets { - match t { - Target::Wire(w) => println!("{}", wit.get_wire(w.gate, w.input)), - _ => println!("Not a wire."), - } + println!("{}", pw.get_target(t)); } println!("End of values for {}", self.name); } diff --git a/src/witness.rs b/src/witness.rs index 0f930971..8b46d43f 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -3,6 +3,7 @@ use std::convert::TryInto; use anyhow::{ensure, Result}; +use crate::copy_constraint::CopyConstraint; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field::Field; @@ -169,23 +170,23 @@ impl PartialWitness { /// Checks that the copy constraints are satisfied in the witness. pub fn check_copy_constraints( &self, - copy_constraints: &[(Target, Target)], + copy_constraints: &[CopyConstraint], gate_instances: &[GateInstance], ) -> Result<()> where F: Extendable, { - for &(a, b) in copy_constraints { + for CopyConstraint { pair: (a, b), name } in copy_constraints { // TODO: Take care of public inputs once they land. - let va = self.try_get_target(a).unwrap_or(F::ZERO); - let vb = self.try_get_target(b).unwrap_or(F::ZERO); - let desc = |t: Target| -> String { + let va = self.try_get_target(*a).unwrap_or(F::ZERO); + let vb = self.try_get_target(*b).unwrap_or(F::ZERO); + let desc = |t: &Target| -> String { match t { Target::Wire(Wire { gate, input }) => format!( "wire {} of gate #{} (`{}`)", input, gate, - gate_instances[gate].gate_type.0.id() + gate_instances[*gate].gate_type.0.id() ), Target::PublicInput { index } => format!("{}-th public input", index), Target::VirtualTarget { index } => format!("{}-th virtual target", index), @@ -193,8 +194,9 @@ impl PartialWitness { }; ensure!( va == vb, - "Copy constraint between {} and {} is not satisfied. \ + "Copy constraint '{}' between {} and {} is not satisfied. \ Got values of {} and {} respectively.", + name, desc(a), desc(b), va,