mirror of https://github.com/zcash/halo2.git
sinsemilla: Decompose fixed_y_q into binary selector and constant.
Previously, fixed_y_q was a non-binary selector that both loaded the y_Q value and toggled the y_Q gate. Now, the gate is toggled by a q_s4 simple selector, while the value is loaded into a separate fixed column.
This commit is contained in:
parent
cba0d8672b
commit
f6c951d975
|
@ -256,6 +256,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|
|||
meta,
|
||||
advices[..5].try_into().unwrap(),
|
||||
advices[6],
|
||||
lagrange_coeffs[0],
|
||||
lookup,
|
||||
range_check.clone(),
|
||||
);
|
||||
|
@ -273,6 +274,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|
|||
meta,
|
||||
advices[5..].try_into().unwrap(),
|
||||
advices[7],
|
||||
lagrange_coeffs[1],
|
||||
lookup,
|
||||
range_check,
|
||||
);
|
||||
|
|
|
@ -484,6 +484,7 @@ mod tests {
|
|||
meta,
|
||||
advices[..5].try_into().unwrap(),
|
||||
advices[2],
|
||||
lagrange_coeffs[0],
|
||||
lookup,
|
||||
range_check.clone(),
|
||||
);
|
||||
|
@ -491,6 +492,7 @@ mod tests {
|
|||
meta,
|
||||
advices[5..].try_into().unwrap(),
|
||||
advices[7],
|
||||
lagrange_coeffs[1],
|
||||
lookup,
|
||||
range_check,
|
||||
);
|
||||
|
|
|
@ -30,13 +30,16 @@ mod hash_to_point;
|
|||
/// Configuration for the Sinsemilla hash chip
|
||||
#[derive(Eq, PartialEq, Clone, Debug)]
|
||||
pub struct SinsemillaConfig {
|
||||
/// Selector used in the lookup argument as well as Sinsemilla custom gates.
|
||||
/// Binary selector used in lookup argument and in the body of the Sinsemilla hash.
|
||||
q_sinsemilla1: Selector,
|
||||
/// Fixed column used in Sinsemilla custom gates, to toggle behaviour at the ends of
|
||||
/// message pieces.
|
||||
/// Non-binary selector used in lookup argument and in the body of the Sinsemilla hash.
|
||||
q_sinsemilla2: Column<Fixed>,
|
||||
/// Fixed column used to constrain hash initialization to be consistent with
|
||||
/// q_sinsemilla2 is used to define a synthetic selector,
|
||||
/// q_sinsemilla3 = (q_sinsemilla2) ⋅ (q_sinsemilla2 - 1)
|
||||
/// Simple selector used to constrain hash initialization to be consistent with
|
||||
/// the y-coordinate of the domain $Q$.
|
||||
q_sinsemilla4: Selector,
|
||||
/// Fixed column used to load the y-coordinate of the domain $Q$.
|
||||
fixed_y_q: Column<Fixed>,
|
||||
/// Advice column used to store the x-coordinate of the accumulator at each
|
||||
/// iteration of the hash.
|
||||
|
@ -110,6 +113,7 @@ impl SinsemillaChip {
|
|||
meta: &mut ConstraintSystem<pallas::Base>,
|
||||
advices: [Column<Advice>; 5],
|
||||
witness_pieces: Column<Advice>,
|
||||
fixed_y_q: Column<Fixed>,
|
||||
lookup: (Column<Fixed>, Column<Fixed>, Column<Fixed>),
|
||||
range_check: LookupRangeCheckConfig<pallas::Base, { sinsemilla::K }>,
|
||||
) -> <Self as Chip<pallas::Base>>::Config {
|
||||
|
@ -121,7 +125,8 @@ impl SinsemillaChip {
|
|||
let config = SinsemillaConfig {
|
||||
q_sinsemilla1: meta.selector(),
|
||||
q_sinsemilla2: meta.fixed_column(),
|
||||
fixed_y_q: meta.fixed_column(),
|
||||
q_sinsemilla4: meta.selector(),
|
||||
fixed_y_q,
|
||||
x_a: advices[0],
|
||||
x_p: advices[1],
|
||||
bits: advices[2],
|
||||
|
@ -139,8 +144,7 @@ impl SinsemillaChip {
|
|||
// Set up lookup argument
|
||||
GeneratorTableConfig::configure(meta, config.clone());
|
||||
|
||||
// Constant expressions
|
||||
let two = Expression::Constant(pallas::Base::from_u64(2));
|
||||
let two = pallas::Base::from_u64(2);
|
||||
|
||||
// Closures for expressions that are derived multiple times
|
||||
// x_r = lambda_1^2 - x_a - x_p
|
||||
|
@ -159,14 +163,28 @@ impl SinsemillaChip {
|
|||
(lambda_1 + lambda_2) * (x_a - x_r(meta, rotation))
|
||||
};
|
||||
|
||||
// Check that the initial x_A, x_P, lambda_1, lambda_2 are consistent with y_Q.
|
||||
meta.create_gate("Initial y_Q", |meta| {
|
||||
let q_s4 = meta.query_selector(config.q_sinsemilla4);
|
||||
let y_q = meta.query_fixed(config.fixed_y_q, Rotation::cur());
|
||||
|
||||
// Y_A = (lambda_1 + lambda_2) * (x_a - x_r)
|
||||
let Y_A_cur = Y_A(meta, Rotation::cur());
|
||||
|
||||
// 2 * y_q - Y_{A,0} = 0
|
||||
let init_y_q_check = y_q * two - Y_A_cur;
|
||||
|
||||
vec![q_s4 * init_y_q_check]
|
||||
});
|
||||
|
||||
meta.create_gate("Sinsemilla gate", |meta| {
|
||||
let q_s1 = meta.query_selector(config.q_sinsemilla1);
|
||||
let q_s2 = meta.query_fixed(config.q_sinsemilla2, Rotation::cur());
|
||||
// q_s3 = (q_s2) * (q_s2 - 1)
|
||||
let q_s3 = {
|
||||
let one = Expression::Constant(pallas::Base::one());
|
||||
let q_s2 = meta.query_fixed(config.q_sinsemilla2, Rotation::cur());
|
||||
q_s2.clone() * (q_s2 - one)
|
||||
};
|
||||
let fixed_y_q = meta.query_fixed(config.fixed_y_q, Rotation::cur());
|
||||
|
||||
let lambda_1_next = meta.query_advice(config.lambda_1, Rotation::next());
|
||||
let lambda_2_cur = meta.query_advice(config.lambda_2, Rotation::cur());
|
||||
|
@ -182,10 +200,6 @@ impl SinsemillaChip {
|
|||
// Y_A = (lambda_1 + lambda_2) * (x_a - x_r)
|
||||
let Y_A_next = Y_A(meta, Rotation::next());
|
||||
|
||||
// Check that the initial x_A, x_P, lambda_1, lambda_2 are consistent with y_Q.
|
||||
// fixed_y_q * (2 * fixed_y_q - Y_{A,0}) = 0
|
||||
let init_y_q_check = fixed_y_q.clone() * (two.clone() * fixed_y_q - Y_A_cur.clone());
|
||||
|
||||
// lambda2^2 - (x_a_next + x_r + x_a_cur) = 0
|
||||
let secant_line =
|
||||
lambda_2_cur.clone().square() - (x_a_next.clone() + x_r + x_a_cur.clone());
|
||||
|
@ -202,15 +216,14 @@ impl SinsemillaChip {
|
|||
// y_a_final is assigned to the lambda1 column on the next offset.
|
||||
let y_a_final = lambda_1_next;
|
||||
|
||||
two.clone() * Y_A_cur
|
||||
+ (two.clone() - q_s3.clone()) * Y_A_next
|
||||
+ two * q_s3 * y_a_final
|
||||
Y_A_cur * two
|
||||
+ (Expression::Constant(two) - q_s3.clone()) * Y_A_next
|
||||
+ q_s3 * two * y_a_final
|
||||
};
|
||||
lhs - rhs
|
||||
};
|
||||
|
||||
vec![
|
||||
("Initial y_q", init_y_q_check),
|
||||
("Secant line", q_s1.clone() * secant_line),
|
||||
("y check", q_s1 * y_check),
|
||||
]
|
||||
|
|
|
@ -35,34 +35,25 @@ impl SinsemillaChip {
|
|||
let x_q = *Q.coordinates().unwrap().x();
|
||||
let y_q = *Q.coordinates().unwrap().y();
|
||||
|
||||
// Initialize the accumulator to `Q`.
|
||||
let (mut x_a, mut y_a): (X<pallas::Base>, Y<pallas::Base>) = {
|
||||
// Constrain the initial x_a, lambda_1, lambda_2, x_p using the q_sinsemilla4
|
||||
// selector.
|
||||
let mut y_a: Y<pallas::Base> = {
|
||||
// Enable `q_sinsemilla4` on the first row.
|
||||
config.q_sinsemilla4.enable(region, offset)?;
|
||||
region.assign_fixed(|| "fixed y_q", config.fixed_y_q, offset, || Ok(y_q))?;
|
||||
|
||||
(Some(y_q)).into()
|
||||
};
|
||||
|
||||
// Constrain the initial x_q to equal the x-coordinate of the domain's `Q`.
|
||||
let mut x_a: X<pallas::Base> = {
|
||||
let x_a = {
|
||||
let cell =
|
||||
region.assign_advice_from_constant(|| "fixed x_q", config.x_a, offset, x_q)?;
|
||||
CellValue::new(cell, Some(x_q))
|
||||
};
|
||||
|
||||
// Constrain the initial x_a, lambda_1, lambda_2, x_p using the fixed y_q
|
||||
// initializer. Assign `fixed_y_q` to be zero on every other row.
|
||||
{
|
||||
region.assign_fixed(|| "fixed y_q", config.fixed_y_q, offset, || Ok(y_q))?;
|
||||
|
||||
let total_num_words = message.iter().map(|piece| piece.num_words()).sum();
|
||||
for row in 1..total_num_words {
|
||||
region.assign_fixed(
|
||||
|| "fixed y_q",
|
||||
config.fixed_y_q,
|
||||
offset + row,
|
||||
|| Ok(pallas::Base::zero()),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
let y_a = Some(y_q);
|
||||
|
||||
(x_a.into(), y_a.into())
|
||||
x_a.into()
|
||||
};
|
||||
|
||||
let mut zs_sum: Vec<Vec<CellValue<pallas::Base>>> = Vec::new();
|
||||
|
|
|
@ -695,6 +695,7 @@ mod tests {
|
|||
meta,
|
||||
advices[..5].try_into().unwrap(),
|
||||
advices[2],
|
||||
lagrange_coeffs[0],
|
||||
lookup,
|
||||
range_check.clone(),
|
||||
);
|
||||
|
|
|
@ -191,6 +191,11 @@ pub mod tests {
|
|||
let constants = meta.fixed_column();
|
||||
meta.enable_constant(constants);
|
||||
|
||||
// NB: In the actual Action circuit, these fixed columns will be reused
|
||||
// by other chips. For this test, we are creating new fixed columns.
|
||||
let fixed_y_q_1 = meta.fixed_column();
|
||||
let fixed_y_q_2 = meta.fixed_column();
|
||||
|
||||
// Fixed columns for the Sinsemilla generator lookup table
|
||||
let lookup = (
|
||||
meta.fixed_column(),
|
||||
|
@ -204,6 +209,7 @@ pub mod tests {
|
|||
meta,
|
||||
advices[5..].try_into().unwrap(),
|
||||
advices[7],
|
||||
fixed_y_q_1,
|
||||
lookup,
|
||||
range_check.clone(),
|
||||
);
|
||||
|
@ -213,6 +219,7 @@ pub mod tests {
|
|||
meta,
|
||||
advices[..5].try_into().unwrap(),
|
||||
advices[2],
|
||||
fixed_y_q_2,
|
||||
lookup,
|
||||
range_check,
|
||||
);
|
||||
|
|
|
@ -1365,6 +1365,7 @@ mod tests {
|
|||
meta,
|
||||
advices[..5].try_into().unwrap(),
|
||||
advices[2],
|
||||
lagrange_coeffs[0],
|
||||
lookup,
|
||||
range_check.clone(),
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue