From c913679340d67d19ce3d24054810e9f6f7e7b982 Mon Sep 17 00:00:00 2001 From: iquerejeta Date: Fri, 24 Jan 2025 16:31:00 +0100 Subject: [PATCH 1/4] Update Cost model * Support only one polynomial commitments scheme. * Rename ModelCircuit -> CircuitModel --- examples/proof-size.rs | 9 ++--- src/dev/cost_model.rs | 78 +++++++++++++----------------------------- 2 files changed, 25 insertions(+), 62 deletions(-) diff --git a/examples/proof-size.rs b/examples/proof-size.rs index 9b92a3b4e..88308eb3b 100644 --- a/examples/proof-size.rs +++ b/examples/proof-size.rs @@ -5,7 +5,7 @@ use halo2_proofs::{ }; use halo2curves::pasta::Fp; -use halo2_proofs::dev::cost_model::{from_circuit_to_model_circuit, CommitmentScheme}; +use halo2_proofs::dev::cost_model::from_circuit_to_circuit_model; use halo2_proofs::plonk::{Expression, Selector, TableColumn}; use halo2_proofs::poly::Rotation; @@ -88,12 +88,7 @@ const K: u32 = 11; fn main() { let circuit = TestCircuit {}; - let model = from_circuit_to_model_circuit::<_, _, 32, 32>( - Some(K), - &circuit, - vec![], - CommitmentScheme::KZGGWC, - ); + let model = from_circuit_to_circuit_model::<_, _, 32, 32>(Some(K), &circuit, vec![]); println!( "Cost of circuit with 8 bit lookup table: \n{}", serde_json::to_string_pretty(&model).unwrap() diff --git a/src/dev/cost_model.rs b/src/dev/cost_model.rs index f4c086d2e..c1d251ad8 100644 --- a/src/dev/cost_model.rs +++ b/src/dev/cost_model.rs @@ -10,18 +10,7 @@ use ff::{Field, FromUniformBytes}; use serde::Deserialize; use serde_derive::Serialize; -use super::MockProver; - -/// Supported commitment schemes -#[derive(Debug, Eq, PartialEq)] -pub enum CommitmentScheme { - /// Inner Product Argument commitment scheme - IPA, - /// KZG with GWC19 mutli-open strategy - KZGGWC, - /// KZG with BDFG20 mutli-open strategy - KZGSHPLONK, -} +use super::{CellValue, InstanceValue, Region}; /// Options to build a circuit specification to measure the cost model of. #[derive(Debug)] @@ -130,7 +119,7 @@ impl Permutation { /// High-level specifications of an abstract circuit. #[derive(Debug, Deserialize, Serialize)] -pub struct ModelCircuit { +pub struct CircuitModel { /// Power-of-2 bound on the number of rows in the circuit. pub k: usize, /// Number of rows in the circuit (not including table rows). @@ -154,12 +143,9 @@ pub struct ModelCircuit { } impl CostOptions { - /// Convert [CostOptions] to [ModelCircuit]. The proof sizè is computed depending on the base + /// Convert [CostOptions] to [CircuitModel]. The proof sizè is computed depending on the base /// and scalar field size of the curve used, together with the [CommitmentScheme]. - pub fn into_model_circuit( - &self, - comm_scheme: CommitmentScheme, - ) -> ModelCircuit { + pub fn into_circuit_model(&self) -> CircuitModel { let mut queries: Vec<_> = iter::empty() .chain(self.advice.iter()) .chain(self.instance.iter()) @@ -198,41 +184,24 @@ impl CostOptions { // - SCALAR bytes (evals) per set of points in multiopen argument let multiopen = comp_bytes(1, point_sets); - let polycomm = match comm_scheme { - CommitmentScheme::IPA => { - // Polycommit IPA: - // - s_poly commitment (COMM bytes) - // - inner product argument (k rounds * 2 * COMM bytes) - // - a (SCALAR bytes) - // - xi (SCALAR bytes) - comp_bytes(1 + 2 * self.min_k, 2) - } - CommitmentScheme::KZGGWC => { - let mut nr_rotations = HashSet::new(); - for poly in self.advice.iter() { - nr_rotations.extend(poly.rotations.clone()); - } - for poly in self.fixed.iter() { - nr_rotations.extend(poly.rotations.clone()); - } - for poly in self.instance.iter() { - nr_rotations.extend(poly.rotations.clone()); - } + let mut nr_rotations = HashSet::new(); + for poly in self.advice.iter() { + nr_rotations.extend(poly.rotations.clone()); + } + for poly in self.fixed.iter() { + nr_rotations.extend(poly.rotations.clone()); + } + for poly in self.instance.iter() { + nr_rotations.extend(poly.rotations.clone()); + } - // Polycommit GWC: - // - number_rotations * COMM bytes - comp_bytes(nr_rotations.len(), 0) - } - CommitmentScheme::KZGSHPLONK => { - // Polycommit SHPLONK: - // - quotient polynomial commitment (COMM bytes) - comp_bytes(1, 0) - } - }; + // Polycommit GWC: + // - number_rotations * COMM bytes + let polycomm = comp_bytes(nr_rotations.len(), 0); let size = plonk + vanishing + multiopen + polycomm; - ModelCircuit { + CircuitModel { k: self.min_k, rows: self.rows_count, table_rows: self.table_rows_count, @@ -247,8 +216,8 @@ impl CostOptions { } } -/// Given a Plonk circuit, this function returns a [ModelCircuit] -pub fn from_circuit_to_model_circuit< +/// Given a Plonk circuit, this function returns a [CircuitModel] +pub fn from_circuit_to_circuit_model< F: Ord + Field + FromUniformBytes<64>, C: Circuit, const COMM: usize, @@ -257,10 +226,9 @@ pub fn from_circuit_to_model_circuit< k: Option, circuit: &C, instances: Vec>, - comm_scheme: CommitmentScheme, -) -> ModelCircuit { +) -> CircuitModel { let options = from_circuit_to_cost_model_options(k, circuit, instances); - options.into_model_circuit::(comm_scheme) + options.into_circuit_model::() } /// Given a circuit, this function returns [CostOptions]. If no upper bound for `k` is @@ -368,7 +336,7 @@ pub fn from_circuit_to_cost_model_options, max_degree: cs.degree(), lookup, permutation, - min_k, + min_k: (min_k - 1).next_power_of_two().ilog2() as usize, rows_count, table_rows_count, compressed_rows_count, From befda0e3833c775e674f3b8a74117f727f790a9a Mon Sep 17 00:00:00 2001 From: iquerejeta Date: Fri, 24 Jan 2025 16:32:37 +0100 Subject: [PATCH 2/4] Update Cost model Avoid running MockProver for cost model We copied the `Assignment` implementation of the MockProver, but in here we do not need to unwrap the witnesses (which was why the MockProver always needed `Value::known` instead of `Value::unknown`. Now we can run the cost model without assigned values. --- src/dev/cost_model.rs | 393 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 389 insertions(+), 4 deletions(-) diff --git a/src/dev/cost_model.rs b/src/dev/cost_model.rs index c1d251ad8..08d22173a 100644 --- a/src/dev/cost_model.rs +++ b/src/dev/cost_model.rs @@ -1,11 +1,20 @@ //! The cost estimator takes high-level parameters for a circuit design, and estimates the //! verification cost, as well as resulting proof size. -use std::collections::HashSet; +use blake2b_simd::blake2b; +use std::collections::{HashMap, HashSet}; +use std::ops::Range; use std::{iter, num::ParseIntError, str::FromStr}; +use crate::circuit; +use crate::circuit::Value; +use crate::plonk::sealed::SealedPhase; use crate::plonk::Any::Fixed; -use crate::plonk::{k_from_circuit, Circuit}; +use crate::plonk::{ + k_from_circuit, permutation, sealed, Advice, Any, Assignment, Challenge, Circuit, Column, + ConstraintSystem, Error, FirstPhase, FloorPlanner, Instance, Phase, Selector, +}; +use crate::utils::rational::Rational; use ff::{Field, FromUniformBytes}; use serde::Deserialize; use serde_derive::Serialize; @@ -240,10 +249,10 @@ pub fn from_circuit_to_cost_model_options, ) -> CostOptions { let instance_len = instances.iter().map(Vec::len).max().unwrap_or(0); let prover = if let Some(k) = k_upper_bound { - MockProver::run(k, circuit, instances).unwrap() + DevAssembly::run(k, circuit, instances).unwrap() } else { let k = k_from_circuit(circuit); - MockProver::run(k, circuit, instances).unwrap() + DevAssembly::run(k, circuit, instances).unwrap() }; let cs = prover.cs; @@ -342,3 +351,379 @@ pub fn from_circuit_to_cost_model_options, compressed_rows_count, } } + +struct DevAssembly { + k: u32, + cs: ConstraintSystem, + + /// The regions in the circuit. + regions: Vec, + /// The current region being assigned to. Will be `None` after the circuit has been + /// synthesized. + current_region: Option, + + // The fixed cells in the circuit, arranged as [column][row]. + fixed: Vec>>, + // The advice cells in the circuit, arranged as [column][row]. + _advice: Vec>>, + // The instance cells in the circuit, arranged as [column][row]. + instance: Vec>>, + + selectors: Vec>, + + _challenges: Vec, + + permutation: permutation::keygen::Assembly, + + // A range of available rows for assignment and copies. + usable_rows: Range, + + current_phase: sealed::Phase, +} + +impl + Ord> DevAssembly { + /// Runs a synthetic keygen-and-prove operation on the given circuit, collecting data + /// about the constraints and their assignments. + pub fn run>( + k: u32, + circuit: &ConcreteCircuit, + instance: Vec>, + ) -> Result { + let n = 1 << k; + + let mut cs = ConstraintSystem::default(); + #[cfg(feature = "circuit-params")] + let config = ConcreteCircuit::configure_with_params(&mut cs, circuit.params()); + #[cfg(not(feature = "circuit-params"))] + let config = ConcreteCircuit::configure(&mut cs); + let cs = cs; + + assert!( + n >= cs.minimum_rows(), + "n={}, minimum_rows={}, k={}", + n, + cs.minimum_rows(), + k, + ); + + assert_eq!(instance.len(), cs.num_instance_columns); + + let instance = instance + .into_iter() + .map(|instance| { + assert!( + instance.len() <= n - (cs.blinding_factors() + 1), + "instance.len={}, n={}, cs.blinding_factors={}", + instance.len(), + n, + cs.blinding_factors() + ); + + let mut instance_values = vec![InstanceValue::Padding; n]; + for (idx, value) in instance.into_iter().enumerate() { + instance_values[idx] = InstanceValue::Assigned(value); + } + + instance_values + }) + .collect::>(); + + // Fixed columns contain no blinding factors. + let fixed = vec![vec![CellValue::Unassigned; n]; cs.num_fixed_columns]; + let selectors = vec![vec![false; n]; cs.num_selectors]; + // Advice columns contain blinding factors. + let blinding_factors = cs.blinding_factors(); + let usable_rows = n - (blinding_factors + 1); + let _advice = vec![ + { + let mut column = vec![CellValue::Unassigned; n]; + // Poison unusable rows. + for (i, cell) in column.iter_mut().enumerate().skip(usable_rows) { + *cell = CellValue::Poison(i); + } + column + }; + cs.num_advice_columns + ]; + let permutation = permutation::keygen::Assembly::new(n, &cs.permutation); + let constants = cs.constants.clone(); + + // Use hash chain to derive deterministic challenges for testing + let _challenges = { + let mut hash: [u8; 64] = blake2b(b"CostModel").as_bytes().try_into().unwrap(); + iter::repeat_with(|| { + hash = blake2b(&hash).as_bytes().try_into().unwrap(); + F::from_uniform_bytes(&hash) + }) + .take(cs.num_challenges) + .collect() + }; + + let mut prover = DevAssembly { + k, + cs, + regions: vec![], + current_region: None, + fixed, + _advice, + instance, + selectors, + _challenges, + permutation, + usable_rows: 0..usable_rows, + current_phase: FirstPhase.to_sealed(), + }; + + for current_phase in prover.cs.phases() { + prover.current_phase = current_phase; + ConcreteCircuit::FloorPlanner::synthesize( + &mut prover, + circuit, + config.clone(), + constants.clone(), + )?; + } + + let (cs, selector_polys) = prover + .cs + .directly_convert_selectors_to_fixed(prover.selectors.clone()); + prover.cs = cs; + prover.fixed.extend(selector_polys.into_iter().map(|poly| { + let mut v = vec![CellValue::Unassigned; n]; + for (v, p) in v.iter_mut().zip(&poly[..]) { + *v = CellValue::Assigned(*p); + } + v + })); + + Ok(prover) + } +} + +impl DevAssembly { + fn in_phase(&self, phase: P) -> bool { + self.current_phase == phase.to_sealed() + } +} + +impl Assignment for DevAssembly { + fn enter_region(&mut self, name: N) + where + NR: Into, + N: FnOnce() -> NR, + { + if !self.in_phase(FirstPhase) { + return; + } + + assert!(self.current_region.is_none()); + self.current_region = Some(Region { + name: name().into(), + columns: HashSet::default(), + rows: None, + annotations: HashMap::default(), + enabled_selectors: HashMap::default(), + cells: HashMap::default(), + }); + } + + fn annotate_column(&mut self, _annotation: A, _column: Column) + where + A: FnOnce() -> AR, + AR: Into, + { + // Do nothing + } + + fn exit_region(&mut self) { + if !self.in_phase(FirstPhase) { + return; + } + + self.regions.push(self.current_region.take().unwrap()); + } + + fn enable_selector(&mut self, _: A, selector: &Selector, row: usize) -> Result<(), Error> + where + A: FnOnce() -> AR, + AR: Into, + { + if !self.usable_rows.contains(&row) { + return Err(Error::not_enough_rows_available(self.k)); + } + + // Track that this selector was enabled. We require that all selectors are enabled + // inside some region (i.e. no floating selectors). + self.current_region + .as_mut() + .unwrap() + .enabled_selectors + .entry(*selector) + .or_default() + .push(row); + + self.selectors[selector.0][row] = true; + + Ok(()) + } + + fn query_instance(&self, column: Column, row: usize) -> Result, Error> { + assert!( + self.usable_rows.contains(&row), + "row={}, usable_rows={:?}, k={}", + row, + self.usable_rows, + self.k, + ); + + Ok(self + .instance + .get(column.index()) + .and_then(|column| column.get(row)) + .map(|v| circuit::Value::known(v.value())) + .expect("bound failure")) + } + + fn assign_advice( + &mut self, + _: A, + column: Column, + row: usize, + _to: V, + ) -> Result<(), Error> + where + V: FnOnce() -> circuit::Value, + VR: Into>, + A: FnOnce() -> AR, + AR: Into, + { + if self.in_phase(FirstPhase) { + assert!( + self.usable_rows.contains(&row), + "row={}, usable_rows={:?}, k={}", + row, + self.usable_rows, + self.k, + ); + + if let Some(region) = self.current_region.as_mut() { + region.update_extent(column.into(), row); + region + .cells + .entry((column.into(), row)) + .and_modify(|count| *count += 1) + .or_default(); + } + } + + Ok(()) + } + + fn assign_fixed( + &mut self, + _: A, + column: Column, + row: usize, + to: V, + ) -> Result<(), Error> + where + V: FnOnce() -> circuit::Value, + VR: Into>, + A: FnOnce() -> AR, + AR: Into, + { + if !self.in_phase(FirstPhase) { + return Ok(()); + } + + assert!( + self.usable_rows.contains(&row), + "row={}, usable_rows={:?}, k={}", + row, + self.usable_rows, + self.k, + ); + + if let Some(region) = self.current_region.as_mut() { + region.update_extent(column.into(), row); + region + .cells + .entry((column.into(), row)) + .and_modify(|count| *count += 1) + .or_default(); + } + + *self + .fixed + .get_mut(column.index()) + .and_then(|v| v.get_mut(row)) + .expect("bounds failure") = CellValue::Assigned(to().into_field().evaluate().assign()?); + + Ok(()) + } + + fn copy( + &mut self, + left_column: Column, + left_row: usize, + right_column: Column, + right_row: usize, + ) -> Result<(), crate::plonk::Error> { + if !self.in_phase(FirstPhase) { + return Ok(()); + } + + assert!( + self.usable_rows.contains(&left_row) && self.usable_rows.contains(&right_row), + "left_row={}, right_row={}, usable_rows={:?}, k={}", + left_row, + right_row, + self.usable_rows, + self.k, + ); + + self.permutation + .copy(left_column, left_row, right_column, right_row) + } + + fn fill_from_row( + &mut self, + col: Column, + from_row: usize, + to: circuit::Value>, + ) -> Result<(), Error> { + if !self.in_phase(FirstPhase) { + return Ok(()); + } + + assert!( + self.usable_rows.contains(&from_row), + "row={}, usable_rows={:?}, k={}", + from_row, + self.usable_rows, + self.k, + ); + + for row in self.usable_rows.clone().skip(from_row) { + self.assign_fixed(|| "", col, row, || to)?; + } + + Ok(()) + } + + fn get_challenge(&self, _challenge: Challenge) -> circuit::Value { + Value::unknown() + } + + fn push_namespace(&mut self, _: N) + where + NR: Into, + N: FnOnce() -> NR, + { + // Do nothing; we don't care about namespaces in this context. + } + + fn pop_namespace(&mut self, _: Option) { + // Do nothing; we don't care about namespaces in this context. + } +} From b5053c0edf81a884048c6e118ac98e0ce5953354 Mon Sep 17 00:00:00 2001 From: iquerejeta Date: Fri, 24 Jan 2025 17:05:03 +0100 Subject: [PATCH 3/4] Run apt-get update before installing libfontconfig --- .github/workflows/ci.yml | 10 +++++----- .github/workflows/lints-beta.yml | 2 +- .github/workflows/lints-stable.yml | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 23e2bc22e..126101760 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,7 @@ jobs: override: false - name: Install libfontconfig1-dev if: runner.os == 'Linux' - run: sudo apt-get install -y libfontconfig1-dev + run: sudo apt-get update && sudo apt-get install -y libfontconfig1-dev - name: Run tests uses: actions-rs/cargo@v1 with: @@ -45,7 +45,7 @@ jobs: - wasm32-wasi steps: - - run: sudo apt-get install -y libfontconfig1-dev + - run: sudo apt-get update && sudo apt-get install -y libfontconfig1-dev - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: @@ -63,7 +63,7 @@ jobs: runs-on: ubuntu-latest steps: - - run: sudo apt-get install -y libfontconfig1-dev + - run: sudo apt-get update && sudo apt-get install -y libfontconfig1-dev - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: @@ -80,7 +80,7 @@ jobs: runs-on: ubuntu-latest steps: - - run: sudo apt-get install -y libfontconfig1-dev + - run: sudo apt-get update && sudo apt-get install -y libfontconfig1-dev - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: @@ -103,7 +103,7 @@ jobs: timeout-minutes: 30 runs-on: ubuntu-latest steps: - - run: sudo apt-get install -y libfontconfig1-dev + - run: sudo apt-get update && sudo apt-get install -y libfontconfig1-dev - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: diff --git a/.github/workflows/lints-beta.yml b/.github/workflows/lints-beta.yml index 407746981..bd80249e2 100644 --- a/.github/workflows/lints-beta.yml +++ b/.github/workflows/lints-beta.yml @@ -12,7 +12,7 @@ jobs: continue-on-error: true steps: - - run: sudo apt-get install -y libfontconfig1-dev + - run: sudo apt-get update && sudo apt-get install -y libfontconfig1-dev - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: diff --git a/.github/workflows/lints-stable.yml b/.github/workflows/lints-stable.yml index a7ff199bb..d8ad86089 100644 --- a/.github/workflows/lints-stable.yml +++ b/.github/workflows/lints-stable.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - - run: sudo apt-get install -y libfontconfig1-dev + - run: sudo apt-get update && sudo apt-get install -y libfontconfig1-dev - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: From 15fe1c68f58b1b041f3ea148040fc020df17ca1e Mon Sep 17 00:00:00 2001 From: iquerejeta Date: Mon, 3 Feb 2025 11:01:52 +0100 Subject: [PATCH 4/4] Remove instance from cost model (use only nb_instances) --- examples/proof-size.rs | 2 +- src/dev/cost_model.rs | 63 ++++++++++-------------------------------- 2 files changed, 15 insertions(+), 50 deletions(-) diff --git a/examples/proof-size.rs b/examples/proof-size.rs index 88308eb3b..ba1a0dd8a 100644 --- a/examples/proof-size.rs +++ b/examples/proof-size.rs @@ -88,7 +88,7 @@ const K: u32 = 11; fn main() { let circuit = TestCircuit {}; - let model = from_circuit_to_circuit_model::<_, _, 32, 32>(Some(K), &circuit, vec![]); + let model = from_circuit_to_circuit_model::<_, _, 32, 32>(Some(K), &circuit, 10); println!( "Cost of circuit with 8 bit lookup table: \n{}", serde_json::to_string_pretty(&model).unwrap() diff --git a/src/dev/cost_model.rs b/src/dev/cost_model.rs index 08d22173a..c6e2f0223 100644 --- a/src/dev/cost_model.rs +++ b/src/dev/cost_model.rs @@ -19,7 +19,7 @@ use ff::{Field, FromUniformBytes}; use serde::Deserialize; use serde_derive::Serialize; -use super::{CellValue, InstanceValue, Region}; +use super::{CellValue, Region}; /// Options to build a circuit specification to measure the cost model of. #[derive(Debug)] @@ -234,9 +234,9 @@ pub fn from_circuit_to_circuit_model< >( k: Option, circuit: &C, - instances: Vec>, + nb_instances: usize, ) -> CircuitModel { - let options = from_circuit_to_cost_model_options(k, circuit, instances); + let options = from_circuit_to_cost_model_options(k, circuit, nb_instances); options.into_circuit_model::() } @@ -245,14 +245,13 @@ pub fn from_circuit_to_circuit_model< pub fn from_circuit_to_cost_model_options, C: Circuit>( k_upper_bound: Option, circuit: &C, - instances: Vec>, + nb_instances: usize, ) -> CostOptions { - let instance_len = instances.iter().map(Vec::len).max().unwrap_or(0); let prover = if let Some(k) = k_upper_bound { - DevAssembly::run(k, circuit, instances).unwrap() + DevAssembly::run(k, circuit).unwrap() } else { let k = k_from_circuit(circuit); - DevAssembly::run(k, circuit, instances).unwrap() + DevAssembly::run(k, circuit).unwrap() }; let cs = prover.cs; @@ -328,12 +327,12 @@ pub fn from_circuit_to_cost_model_options, let min_k = [ rows_count + cs.blinding_factors(), table_rows_count + cs.blinding_factors(), - instance_len, + nb_instances, ] .into_iter() .max() .unwrap(); - if min_k == instance_len { + if min_k == nb_instances { println!("WARNING: The dominant factor in your circuit's size is the number of public inputs, which causes the verifier to perform linear work."); } @@ -366,8 +365,6 @@ struct DevAssembly { fixed: Vec>>, // The advice cells in the circuit, arranged as [column][row]. _advice: Vec>>, - // The instance cells in the circuit, arranged as [column][row]. - instance: Vec>>, selectors: Vec>, @@ -387,7 +384,6 @@ impl + Ord> DevAssembly { pub fn run>( k: u32, circuit: &ConcreteCircuit, - instance: Vec>, ) -> Result { let n = 1 << k; @@ -406,28 +402,6 @@ impl + Ord> DevAssembly { k, ); - assert_eq!(instance.len(), cs.num_instance_columns); - - let instance = instance - .into_iter() - .map(|instance| { - assert!( - instance.len() <= n - (cs.blinding_factors() + 1), - "instance.len={}, n={}, cs.blinding_factors={}", - instance.len(), - n, - cs.blinding_factors() - ); - - let mut instance_values = vec![InstanceValue::Padding; n]; - for (idx, value) in instance.into_iter().enumerate() { - instance_values[idx] = InstanceValue::Assigned(value); - } - - instance_values - }) - .collect::>(); - // Fixed columns contain no blinding factors. let fixed = vec![vec![CellValue::Unassigned; n]; cs.num_fixed_columns]; let selectors = vec![vec![false; n]; cs.num_selectors]; @@ -466,7 +440,6 @@ impl + Ord> DevAssembly { current_region: None, fixed, _advice, - instance, selectors, _challenges, permutation, @@ -567,21 +540,13 @@ impl Assignment for DevAssembly { Ok(()) } - fn query_instance(&self, column: Column, row: usize) -> Result, Error> { - assert!( - self.usable_rows.contains(&row), - "row={}, usable_rows={:?}, k={}", - row, - self.usable_rows, - self.k, - ); + fn query_instance(&self, _column: Column, row: usize) -> Result, Error> { + if !self.usable_rows.contains(&row) { + return Err(Error::not_enough_rows_available(self.k)); + } - Ok(self - .instance - .get(column.index()) - .and_then(|column| column.get(row)) - .map(|v| circuit::Value::known(v.value())) - .expect("bound failure")) + // There is no instance in this context. + Ok(Value::unknown()) } fn assign_advice(