2021-07-16 21:10:13 -07:00
|
|
|
//! Decomposes an $n$-bit field element $\alpha$ into $W$ windows, each window
|
|
|
|
//! being a $K$-bit word, using a running sum $z$.
|
|
|
|
//! We constrain $K \leq 3$ for this helper.
|
|
|
|
//! $$\alpha = k_0 + (2^K) k_1 + (2^{2K}) k_2 + ... + (2^{(W-1)K}) k_{W-1}$$
|
|
|
|
//!
|
|
|
|
//! $z_0$ is initialized as $\alpha$. Each successive $z_{i+1}$ is computed as
|
|
|
|
//! $$z_{i+1} = (z_{i} - k_i) / (2^K).$$
|
|
|
|
//! $z_W$ is constrained to be zero.
|
2021-07-09 05:32:35 -07:00
|
|
|
//! The difference between each interstitial running sum output is constrained
|
2021-07-16 21:10:13 -07:00
|
|
|
//! to be $K$ bits, i.e.
|
|
|
|
//! `range_check`($k_i$, $2^K$),
|
2021-07-19 03:48:49 -07:00
|
|
|
//! where
|
2021-07-19 04:14:32 -07:00
|
|
|
//! ```text
|
2021-07-19 03:48:49 -07:00
|
|
|
//! range_check(word, range)
|
|
|
|
//! = word * (1 - word) * (2 - word) * ... * ((range - 1) - word)
|
|
|
|
//! ```
|
2021-07-09 05:32:35 -07:00
|
|
|
//!
|
2021-07-16 21:10:13 -07:00
|
|
|
//! Given that the `range_check` constraint will be toggled by a selector, in
|
|
|
|
//! practice we will have a `selector * range_check(word, range)` expression
|
|
|
|
//! of degree `range + 1`.
|
2021-07-09 05:32:35 -07:00
|
|
|
//!
|
2021-07-16 21:10:13 -07:00
|
|
|
//! This means that $2^K$ has to be at most `degree_bound - 1` in order for
|
2021-07-09 05:32:35 -07:00
|
|
|
//! the range check constraint to stay within the degree bound.
|
|
|
|
|
|
|
|
use ff::PrimeFieldBits;
|
2022-01-27 15:28:02 -08:00
|
|
|
use halo2_proofs::{
|
2022-06-08 13:37:52 -07:00
|
|
|
circuit::{AssignedCell, Region, Value},
|
2022-04-24 15:13:38 -07:00
|
|
|
plonk::{Advice, Column, ConstraintSystem, Constraints, Error, Selector},
|
2021-07-09 05:32:35 -07:00
|
|
|
poly::Rotation,
|
|
|
|
};
|
|
|
|
|
2021-12-01 18:51:46 -08:00
|
|
|
use super::range_check;
|
2022-11-29 16:12:17 -08:00
|
|
|
|
2021-07-09 05:32:35 -07:00
|
|
|
use std::marker::PhantomData;
|
|
|
|
|
2021-07-24 08:54:54 -07:00
|
|
|
/// The running sum $[z_0, ..., z_W]$. If created in strict mode, $z_W = 0$.
|
2022-01-28 07:38:22 -08:00
|
|
|
#[derive(Debug)]
|
2022-11-29 16:12:17 -08:00
|
|
|
pub struct RunningSum<F: PrimeFieldBits>(Vec<AssignedCell<F, F>>);
|
|
|
|
impl<F: PrimeFieldBits> std::ops::Deref for RunningSum<F> {
|
2021-12-01 18:51:46 -08:00
|
|
|
type Target = Vec<AssignedCell<F, F>>;
|
2021-07-09 05:32:35 -07:00
|
|
|
|
2021-12-01 18:51:46 -08:00
|
|
|
fn deref(&self) -> &Vec<AssignedCell<F, F>> {
|
2021-07-09 05:32:35 -07:00
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
}
|
2021-07-24 08:54:54 -07:00
|
|
|
|
2022-01-28 07:38:22 -08:00
|
|
|
/// Configuration that provides methods for running sum decomposition.
|
2021-11-30 19:00:07 -08:00
|
|
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
2022-11-29 16:12:17 -08:00
|
|
|
pub struct RunningSumConfig<F: PrimeFieldBits, const WINDOW_NUM_BITS: usize> {
|
2022-01-28 07:38:22 -08:00
|
|
|
q_range_check: Selector,
|
|
|
|
z: Column<Advice>,
|
2021-07-09 05:32:35 -07:00
|
|
|
_marker: PhantomData<F>,
|
|
|
|
}
|
|
|
|
|
2022-11-29 16:12:17 -08:00
|
|
|
impl<F: PrimeFieldBits, const WINDOW_NUM_BITS: usize> RunningSumConfig<F, WINDOW_NUM_BITS> {
|
2022-01-28 07:38:22 -08:00
|
|
|
/// Returns the q_range_check selector of this [`RunningSumConfig`].
|
|
|
|
pub(crate) fn q_range_check(&self) -> Selector {
|
|
|
|
self.q_range_check
|
|
|
|
}
|
|
|
|
|
2021-07-09 05:32:35 -07:00
|
|
|
/// `perm` MUST include the advice column `z`.
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// Panics if WINDOW_NUM_BITS > 3.
|
2021-07-15 04:52:15 -07:00
|
|
|
///
|
|
|
|
/// # Side-effects
|
|
|
|
///
|
|
|
|
/// `z` will be equality-enabled.
|
2021-07-09 05:32:35 -07:00
|
|
|
pub fn configure(
|
|
|
|
meta: &mut ConstraintSystem<F>,
|
|
|
|
q_range_check: Selector,
|
|
|
|
z: Column<Advice>,
|
|
|
|
) -> Self {
|
|
|
|
assert!(WINDOW_NUM_BITS <= 3);
|
|
|
|
|
2022-01-04 21:28:16 -08:00
|
|
|
meta.enable_equality(z);
|
2021-07-15 04:52:15 -07:00
|
|
|
|
2021-07-09 05:32:35 -07:00
|
|
|
let config = Self {
|
|
|
|
q_range_check,
|
|
|
|
z,
|
|
|
|
_marker: PhantomData,
|
|
|
|
};
|
|
|
|
|
2022-05-08 20:27:03 -07:00
|
|
|
// https://p.z.cash/halo2-0.1:decompose-short-range
|
2021-07-09 05:32:35 -07:00
|
|
|
meta.create_gate("range check", |meta| {
|
|
|
|
let q_range_check = meta.query_selector(config.q_range_check);
|
|
|
|
let z_cur = meta.query_advice(config.z, Rotation::cur());
|
|
|
|
let z_next = meta.query_advice(config.z, Rotation::next());
|
|
|
|
// z_i = 2^{K}⋅z_{i + 1} + k_i
|
|
|
|
// => k_i = z_i - 2^{K}⋅z_{i + 1}
|
2021-12-07 09:47:03 -08:00
|
|
|
let word = z_cur - z_next * F::from(1 << WINDOW_NUM_BITS);
|
2021-07-09 05:32:35 -07:00
|
|
|
|
2022-04-24 15:13:38 -07:00
|
|
|
Constraints::with_selector(q_range_check, Some(range_check(word, 1 << WINDOW_NUM_BITS)))
|
2021-07-09 05:32:35 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
config
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Decompose a field element alpha that is witnessed in this helper.
|
|
|
|
///
|
|
|
|
/// `strict` = true constrains the final running sum to be zero, i.e.
|
2021-07-16 21:10:13 -07:00
|
|
|
/// constrains alpha to be within WINDOW_NUM_BITS * num_windows bits.
|
2021-07-09 05:32:35 -07:00
|
|
|
pub fn witness_decompose(
|
|
|
|
&self,
|
|
|
|
region: &mut Region<'_, F>,
|
|
|
|
offset: usize,
|
2022-06-08 13:37:52 -07:00
|
|
|
alpha: Value<F>,
|
2021-07-09 05:32:35 -07:00
|
|
|
strict: bool,
|
2021-07-16 21:10:13 -07:00
|
|
|
word_num_bits: usize,
|
|
|
|
num_windows: usize,
|
2021-07-24 08:54:54 -07:00
|
|
|
) -> Result<RunningSum<F>, Error> {
|
2022-06-08 13:37:52 -07:00
|
|
|
let z_0 = region.assign_advice(|| "z_0 = alpha", self.z, offset, || alpha)?;
|
2021-07-16 21:10:13 -07:00
|
|
|
self.decompose(region, offset, z_0, strict, word_num_bits, num_windows)
|
2021-07-09 05:32:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Decompose an existing variable alpha that is copied into this helper.
|
|
|
|
///
|
|
|
|
/// `strict` = true constrains the final running sum to be zero, i.e.
|
2021-07-16 21:10:13 -07:00
|
|
|
/// constrains alpha to be within WINDOW_NUM_BITS * num_windows bits.
|
2021-07-09 05:32:35 -07:00
|
|
|
pub fn copy_decompose(
|
|
|
|
&self,
|
|
|
|
region: &mut Region<'_, F>,
|
|
|
|
offset: usize,
|
2021-12-01 18:51:46 -08:00
|
|
|
alpha: AssignedCell<F, F>,
|
2021-07-09 05:32:35 -07:00
|
|
|
strict: bool,
|
2021-07-16 21:10:13 -07:00
|
|
|
word_num_bits: usize,
|
|
|
|
num_windows: usize,
|
2021-07-24 08:54:54 -07:00
|
|
|
) -> Result<RunningSum<F>, Error> {
|
2021-12-01 18:10:00 -08:00
|
|
|
let z_0 = alpha.copy_advice(|| "copy z_0 = alpha", region, self.z, offset)?;
|
2021-07-16 21:10:13 -07:00
|
|
|
self.decompose(region, offset, z_0, strict, word_num_bits, num_windows)
|
2021-07-09 05:32:35 -07:00
|
|
|
}
|
|
|
|
|
2021-07-16 21:10:13 -07:00
|
|
|
/// `z_0` must be the cell at `(self.z, offset)` in `region`.
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// Panics if there are too many windows for the given word size.
|
2021-07-09 05:32:35 -07:00
|
|
|
fn decompose(
|
|
|
|
&self,
|
|
|
|
region: &mut Region<'_, F>,
|
|
|
|
offset: usize,
|
2021-12-01 18:51:46 -08:00
|
|
|
z_0: AssignedCell<F, F>,
|
2021-07-09 05:32:35 -07:00
|
|
|
strict: bool,
|
2021-07-16 21:10:13 -07:00
|
|
|
word_num_bits: usize,
|
|
|
|
num_windows: usize,
|
2021-07-24 08:54:54 -07:00
|
|
|
) -> Result<RunningSum<F>, Error> {
|
2021-07-16 21:10:13 -07:00
|
|
|
// Make sure that we do not have more windows than required for the number
|
|
|
|
// of bits in the word. In other words, every window must contain at least
|
|
|
|
// one bit of the word (no empty windows).
|
|
|
|
//
|
|
|
|
// For example, let:
|
|
|
|
// - word_num_bits = 64
|
|
|
|
// - WINDOW_NUM_BITS = 3
|
|
|
|
// In this case, the maximum allowed num_windows is 22:
|
|
|
|
// 3 * 22 < 64 + 3
|
|
|
|
//
|
|
|
|
assert!(WINDOW_NUM_BITS * num_windows < word_num_bits + WINDOW_NUM_BITS);
|
|
|
|
|
2021-07-09 05:32:35 -07:00
|
|
|
// Enable selectors
|
2021-07-20 01:22:08 -07:00
|
|
|
for idx in 0..num_windows {
|
|
|
|
self.q_range_check.enable(region, offset + idx)?;
|
2021-07-09 05:32:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Decompose base field element into K-bit words.
|
2022-06-08 13:37:52 -07:00
|
|
|
let words = z_0
|
|
|
|
.value()
|
|
|
|
.map(|word| super::decompose_word::<F>(word, word_num_bits, WINDOW_NUM_BITS))
|
|
|
|
.transpose_vec(num_windows);
|
2021-07-09 05:32:35 -07:00
|
|
|
|
2021-07-24 08:54:54 -07:00
|
|
|
// Initialize empty vector to store running sum values [z_0, ..., z_W].
|
2021-12-01 18:51:46 -08:00
|
|
|
let mut zs: Vec<AssignedCell<F, F>> = vec![z_0.clone()];
|
2021-07-09 05:32:35 -07:00
|
|
|
let mut z = z_0;
|
|
|
|
|
2021-07-19 03:48:49 -07:00
|
|
|
// Assign running sum `z_{i+1}` = (z_i - k_i) / (2^K) for i = 0..=n-1.
|
2021-07-09 05:32:35 -07:00
|
|
|
// Outside of this helper, z_0 = alpha must have already been loaded into the
|
|
|
|
// `z` column at `offset`.
|
2022-06-08 13:37:52 -07:00
|
|
|
let two_pow_k_inv = Value::known(F::from(1 << WINDOW_NUM_BITS as u64).invert().unwrap());
|
2021-07-19 03:48:49 -07:00
|
|
|
for (i, word) in words.iter().enumerate() {
|
2021-07-09 05:32:35 -07:00
|
|
|
// z_next = (z_cur - word) / (2^K)
|
|
|
|
let z_next = {
|
2022-06-08 13:37:52 -07:00
|
|
|
let z_cur_val = z.value().copied();
|
2021-12-07 09:47:03 -08:00
|
|
|
let word = word.map(|word| F::from(word as u64));
|
2022-06-08 13:37:52 -07:00
|
|
|
let z_next_val = (z_cur_val - word) * two_pow_k_inv;
|
2021-12-01 17:10:00 -08:00
|
|
|
region.assign_advice(
|
2021-07-19 03:48:49 -07:00
|
|
|
|| format!("z_{:?}", i + 1),
|
2021-07-09 05:32:35 -07:00
|
|
|
self.z,
|
2021-07-19 03:48:49 -07:00
|
|
|
offset + i + 1,
|
2022-06-08 13:37:52 -07:00
|
|
|
|| z_next_val,
|
2021-12-01 17:10:00 -08:00
|
|
|
)?
|
2021-07-09 05:32:35 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
// Update `z`.
|
|
|
|
z = z_next;
|
2021-12-01 04:51:33 -08:00
|
|
|
zs.push(z.clone());
|
2021-07-09 05:32:35 -07:00
|
|
|
}
|
|
|
|
|
2021-07-20 01:22:08 -07:00
|
|
|
if strict {
|
|
|
|
// Constrain the final running sum output to be zero.
|
2023-08-09 08:22:20 -07:00
|
|
|
region.constrain_constant(zs[num_windows].cell(), F::ZERO)?;
|
2021-07-20 01:22:08 -07:00
|
|
|
}
|
|
|
|
|
2021-07-24 08:54:54 -07:00
|
|
|
Ok(RunningSum(zs))
|
2021-07-09 05:32:35 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2022-01-26 16:22:30 -08:00
|
|
|
use group::ff::{Field, PrimeField};
|
2022-01-27 15:28:02 -08:00
|
|
|
use halo2_proofs::{
|
2021-07-09 05:32:35 -07:00
|
|
|
circuit::{Layouter, SimpleFloorPlanner},
|
2022-03-29 18:39:50 -07:00
|
|
|
dev::{FailureLocation, MockProver, VerifyFailure},
|
2021-07-20 01:22:08 -07:00
|
|
|
plonk::{Any, Circuit, ConstraintSystem, Error},
|
2021-07-09 05:32:35 -07:00
|
|
|
};
|
2022-11-29 21:05:37 -08:00
|
|
|
use pasta_curves::pallas;
|
2022-01-18 06:30:55 -08:00
|
|
|
use rand::rngs::OsRng;
|
2021-07-09 05:32:35 -07:00
|
|
|
|
2022-01-27 13:53:10 -08:00
|
|
|
use crate::ecc::chip::{
|
2022-01-26 16:22:30 -08:00
|
|
|
FIXED_BASE_WINDOW_SIZE, L_SCALAR_SHORT as L_SHORT, NUM_WINDOWS, NUM_WINDOWS_SHORT,
|
|
|
|
};
|
|
|
|
|
|
|
|
const L_BASE: usize = pallas::Base::NUM_BITS as usize;
|
2021-08-19 21:21:46 -07:00
|
|
|
|
2021-07-09 05:32:35 -07:00
|
|
|
#[test]
|
|
|
|
fn test_running_sum() {
|
|
|
|
struct MyCircuit<
|
2022-11-29 16:12:17 -08:00
|
|
|
F: PrimeFieldBits,
|
2021-07-09 05:32:35 -07:00
|
|
|
const WORD_NUM_BITS: usize,
|
|
|
|
const WINDOW_NUM_BITS: usize,
|
|
|
|
const NUM_WINDOWS: usize,
|
|
|
|
> {
|
2022-06-08 13:37:52 -07:00
|
|
|
alpha: Value<F>,
|
2021-07-09 05:32:35 -07:00
|
|
|
strict: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<
|
2022-11-29 16:12:17 -08:00
|
|
|
F: PrimeFieldBits,
|
2021-07-09 05:32:35 -07:00
|
|
|
const WORD_NUM_BITS: usize,
|
|
|
|
const WINDOW_NUM_BITS: usize,
|
|
|
|
const NUM_WINDOWS: usize,
|
|
|
|
> Circuit<F> for MyCircuit<F, WORD_NUM_BITS, WINDOW_NUM_BITS, NUM_WINDOWS>
|
|
|
|
{
|
2021-07-16 21:10:13 -07:00
|
|
|
type Config = RunningSumConfig<F, WINDOW_NUM_BITS>;
|
2021-07-09 05:32:35 -07:00
|
|
|
type FloorPlanner = SimpleFloorPlanner;
|
|
|
|
|
|
|
|
fn without_witnesses(&self) -> Self {
|
|
|
|
Self {
|
2022-06-08 13:37:52 -07:00
|
|
|
alpha: Value::unknown(),
|
2021-07-10 18:49:23 -07:00
|
|
|
strict: self.strict,
|
2021-07-09 05:32:35 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
|
|
|
|
let z = meta.advice_column();
|
|
|
|
let q_range_check = meta.selector();
|
2021-07-20 01:22:08 -07:00
|
|
|
let constants = meta.fixed_column();
|
|
|
|
meta.enable_constant(constants);
|
2021-07-09 05:32:35 -07:00
|
|
|
|
2021-07-15 04:52:15 -07:00
|
|
|
RunningSumConfig::<F, WINDOW_NUM_BITS>::configure(meta, q_range_check, z)
|
2021-07-09 05:32:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn synthesize(
|
|
|
|
&self,
|
|
|
|
config: Self::Config,
|
|
|
|
mut layouter: impl Layouter<F>,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
layouter.assign_region(
|
|
|
|
|| "decompose",
|
|
|
|
|mut region| {
|
|
|
|
let offset = 0;
|
2021-07-24 08:54:54 -07:00
|
|
|
let zs = config.witness_decompose(
|
2021-07-09 05:32:35 -07:00
|
|
|
&mut region,
|
|
|
|
offset,
|
|
|
|
self.alpha,
|
|
|
|
self.strict,
|
2021-07-16 21:10:13 -07:00
|
|
|
WORD_NUM_BITS,
|
|
|
|
NUM_WINDOWS,
|
2021-07-09 05:32:35 -07:00
|
|
|
)?;
|
2021-12-01 04:51:33 -08:00
|
|
|
let alpha = zs[0].clone();
|
2021-07-09 05:32:35 -07:00
|
|
|
|
|
|
|
let offset = offset + NUM_WINDOWS + 1;
|
|
|
|
|
2021-07-16 21:10:13 -07:00
|
|
|
config.copy_decompose(
|
|
|
|
&mut region,
|
|
|
|
offset,
|
|
|
|
alpha,
|
|
|
|
self.strict,
|
|
|
|
WORD_NUM_BITS,
|
|
|
|
NUM_WINDOWS,
|
|
|
|
)?;
|
2021-07-09 05:32:35 -07:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Random base field element
|
|
|
|
{
|
2022-01-18 06:30:55 -08:00
|
|
|
let alpha = pallas::Base::random(OsRng);
|
2021-07-09 05:32:35 -07:00
|
|
|
|
|
|
|
// Strict full decomposition should pass.
|
2021-08-19 21:21:46 -07:00
|
|
|
let circuit: MyCircuit<pallas::Base, L_BASE, FIXED_BASE_WINDOW_SIZE, { NUM_WINDOWS }> =
|
|
|
|
MyCircuit {
|
2022-06-08 13:37:52 -07:00
|
|
|
alpha: Value::known(alpha),
|
2021-08-19 21:21:46 -07:00
|
|
|
strict: true,
|
|
|
|
};
|
2021-07-09 05:32:35 -07:00
|
|
|
let prover = MockProver::<pallas::Base>::run(8, &circuit, vec![]).unwrap();
|
|
|
|
assert_eq!(prover.verify(), Ok(()));
|
2021-07-16 09:44:56 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Random 64-bit word
|
|
|
|
{
|
2021-12-07 09:47:03 -08:00
|
|
|
let alpha = pallas::Base::from(rand::random::<u64>());
|
2021-07-16 09:44:56 -07:00
|
|
|
|
|
|
|
// Strict full decomposition should pass.
|
|
|
|
let circuit: MyCircuit<
|
|
|
|
pallas::Base,
|
2021-08-19 21:21:46 -07:00
|
|
|
L_SHORT,
|
2021-07-16 09:44:56 -07:00
|
|
|
FIXED_BASE_WINDOW_SIZE,
|
2021-08-19 21:21:46 -07:00
|
|
|
{ NUM_WINDOWS_SHORT },
|
2021-07-16 09:44:56 -07:00
|
|
|
> = MyCircuit {
|
2022-06-08 13:37:52 -07:00
|
|
|
alpha: Value::known(alpha),
|
2021-07-16 09:44:56 -07:00
|
|
|
strict: true,
|
|
|
|
};
|
|
|
|
let prover = MockProver::<pallas::Base>::run(8, &circuit, vec![]).unwrap();
|
|
|
|
assert_eq!(prover.verify(), Ok(()));
|
|
|
|
}
|
|
|
|
|
2021-07-16 21:10:13 -07:00
|
|
|
// 2^66
|
2021-07-16 09:44:56 -07:00
|
|
|
{
|
2021-07-16 21:10:13 -07:00
|
|
|
let alpha = pallas::Base::from_u128(1 << 66);
|
2021-07-09 05:32:35 -07:00
|
|
|
|
|
|
|
// Strict partial decomposition should fail.
|
|
|
|
let circuit: MyCircuit<
|
|
|
|
pallas::Base,
|
2021-08-19 21:21:46 -07:00
|
|
|
L_SHORT,
|
2021-07-09 05:32:35 -07:00
|
|
|
FIXED_BASE_WINDOW_SIZE,
|
2021-08-19 21:21:46 -07:00
|
|
|
{ NUM_WINDOWS_SHORT },
|
2021-07-09 05:32:35 -07:00
|
|
|
> = MyCircuit {
|
2022-06-08 13:37:52 -07:00
|
|
|
alpha: Value::known(alpha),
|
2021-07-09 05:32:35 -07:00
|
|
|
strict: true,
|
|
|
|
};
|
|
|
|
let prover = MockProver::<pallas::Base>::run(8, &circuit, vec![]).unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
prover.verify(),
|
|
|
|
Err(vec![
|
2021-07-20 01:22:08 -07:00
|
|
|
VerifyFailure::Permutation {
|
2021-07-26 05:54:27 -07:00
|
|
|
column: (Any::Fixed, 0).into(),
|
2022-03-29 18:39:50 -07:00
|
|
|
location: FailureLocation::OutsideRegion { row: 0 },
|
2021-07-20 01:22:08 -07:00
|
|
|
},
|
|
|
|
VerifyFailure::Permutation {
|
2021-07-26 05:54:27 -07:00
|
|
|
column: (Any::Fixed, 0).into(),
|
2022-03-29 18:39:50 -07:00
|
|
|
location: FailureLocation::OutsideRegion { row: 1 },
|
2021-07-20 01:22:08 -07:00
|
|
|
},
|
|
|
|
VerifyFailure::Permutation {
|
|
|
|
column: (Any::Advice, 0).into(),
|
2022-03-29 18:39:50 -07:00
|
|
|
location: FailureLocation::InRegion {
|
|
|
|
region: (0, "decompose").into(),
|
|
|
|
offset: 22,
|
|
|
|
},
|
2021-07-09 05:32:35 -07:00
|
|
|
},
|
2021-07-20 01:22:08 -07:00
|
|
|
VerifyFailure::Permutation {
|
|
|
|
column: (Any::Advice, 0).into(),
|
2022-03-29 18:39:50 -07:00
|
|
|
location: FailureLocation::InRegion {
|
|
|
|
region: (0, "decompose").into(),
|
|
|
|
offset: 45,
|
|
|
|
},
|
2021-07-20 01:22:08 -07:00
|
|
|
},
|
2021-07-09 05:32:35 -07:00
|
|
|
])
|
|
|
|
);
|
|
|
|
|
|
|
|
// Non-strict partial decomposition should pass.
|
|
|
|
let circuit: MyCircuit<
|
|
|
|
pallas::Base,
|
2021-08-19 21:21:46 -07:00
|
|
|
{ L_SHORT },
|
2021-07-09 05:32:35 -07:00
|
|
|
FIXED_BASE_WINDOW_SIZE,
|
2021-08-19 21:21:46 -07:00
|
|
|
{ NUM_WINDOWS_SHORT },
|
2021-07-09 05:32:35 -07:00
|
|
|
> = MyCircuit {
|
2022-06-08 13:37:52 -07:00
|
|
|
alpha: Value::known(alpha),
|
2021-07-09 05:32:35 -07:00
|
|
|
strict: false,
|
|
|
|
};
|
|
|
|
let prover = MockProver::<pallas::Base>::run(8, &circuit, vec![]).unwrap();
|
|
|
|
assert_eq!(prover.verify(), Ok(()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|