mirror of https://github.com/zcash/orchard.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,
|
meta,
|
||||||
advices[..5].try_into().unwrap(),
|
advices[..5].try_into().unwrap(),
|
||||||
advices[6],
|
advices[6],
|
||||||
|
lagrange_coeffs[0],
|
||||||
lookup,
|
lookup,
|
||||||
range_check.clone(),
|
range_check.clone(),
|
||||||
);
|
);
|
||||||
|
@ -273,6 +274,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|
||||||
meta,
|
meta,
|
||||||
advices[5..].try_into().unwrap(),
|
advices[5..].try_into().unwrap(),
|
||||||
advices[7],
|
advices[7],
|
||||||
|
lagrange_coeffs[1],
|
||||||
lookup,
|
lookup,
|
||||||
range_check,
|
range_check,
|
||||||
);
|
);
|
||||||
|
|
|
@ -484,6 +484,7 @@ mod tests {
|
||||||
meta,
|
meta,
|
||||||
advices[..5].try_into().unwrap(),
|
advices[..5].try_into().unwrap(),
|
||||||
advices[2],
|
advices[2],
|
||||||
|
lagrange_coeffs[0],
|
||||||
lookup,
|
lookup,
|
||||||
range_check.clone(),
|
range_check.clone(),
|
||||||
);
|
);
|
||||||
|
@ -491,6 +492,7 @@ mod tests {
|
||||||
meta,
|
meta,
|
||||||
advices[5..].try_into().unwrap(),
|
advices[5..].try_into().unwrap(),
|
||||||
advices[7],
|
advices[7],
|
||||||
|
lagrange_coeffs[1],
|
||||||
lookup,
|
lookup,
|
||||||
range_check,
|
range_check,
|
||||||
);
|
);
|
||||||
|
|
|
@ -30,13 +30,16 @@ mod hash_to_point;
|
||||||
/// Configuration for the Sinsemilla hash chip
|
/// Configuration for the Sinsemilla hash chip
|
||||||
#[derive(Eq, PartialEq, Clone, Debug)]
|
#[derive(Eq, PartialEq, Clone, Debug)]
|
||||||
pub struct SinsemillaConfig {
|
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,
|
q_sinsemilla1: Selector,
|
||||||
/// Fixed column used in Sinsemilla custom gates, to toggle behaviour at the ends of
|
/// Non-binary selector used in lookup argument and in the body of the Sinsemilla hash.
|
||||||
/// message pieces.
|
|
||||||
q_sinsemilla2: Column<Fixed>,
|
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$.
|
/// 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>,
|
fixed_y_q: Column<Fixed>,
|
||||||
/// Advice column used to store the x-coordinate of the accumulator at each
|
/// Advice column used to store the x-coordinate of the accumulator at each
|
||||||
/// iteration of the hash.
|
/// iteration of the hash.
|
||||||
|
@ -110,6 +113,7 @@ impl SinsemillaChip {
|
||||||
meta: &mut ConstraintSystem<pallas::Base>,
|
meta: &mut ConstraintSystem<pallas::Base>,
|
||||||
advices: [Column<Advice>; 5],
|
advices: [Column<Advice>; 5],
|
||||||
witness_pieces: Column<Advice>,
|
witness_pieces: Column<Advice>,
|
||||||
|
fixed_y_q: Column<Fixed>,
|
||||||
lookup: (Column<Fixed>, Column<Fixed>, Column<Fixed>),
|
lookup: (Column<Fixed>, Column<Fixed>, Column<Fixed>),
|
||||||
range_check: LookupRangeCheckConfig<pallas::Base, { sinsemilla::K }>,
|
range_check: LookupRangeCheckConfig<pallas::Base, { sinsemilla::K }>,
|
||||||
) -> <Self as Chip<pallas::Base>>::Config {
|
) -> <Self as Chip<pallas::Base>>::Config {
|
||||||
|
@ -121,7 +125,8 @@ impl SinsemillaChip {
|
||||||
let config = SinsemillaConfig {
|
let config = SinsemillaConfig {
|
||||||
q_sinsemilla1: meta.selector(),
|
q_sinsemilla1: meta.selector(),
|
||||||
q_sinsemilla2: meta.fixed_column(),
|
q_sinsemilla2: meta.fixed_column(),
|
||||||
fixed_y_q: meta.fixed_column(),
|
q_sinsemilla4: meta.selector(),
|
||||||
|
fixed_y_q,
|
||||||
x_a: advices[0],
|
x_a: advices[0],
|
||||||
x_p: advices[1],
|
x_p: advices[1],
|
||||||
bits: advices[2],
|
bits: advices[2],
|
||||||
|
@ -139,8 +144,7 @@ impl SinsemillaChip {
|
||||||
// Set up lookup argument
|
// Set up lookup argument
|
||||||
GeneratorTableConfig::configure(meta, config.clone());
|
GeneratorTableConfig::configure(meta, config.clone());
|
||||||
|
|
||||||
// Constant expressions
|
let two = pallas::Base::from_u64(2);
|
||||||
let two = Expression::Constant(pallas::Base::from_u64(2));
|
|
||||||
|
|
||||||
// Closures for expressions that are derived multiple times
|
// Closures for expressions that are derived multiple times
|
||||||
// x_r = lambda_1^2 - x_a - x_p
|
// 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))
|
(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| {
|
meta.create_gate("Sinsemilla gate", |meta| {
|
||||||
let q_s1 = meta.query_selector(config.q_sinsemilla1);
|
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 q_s3 = {
|
||||||
let one = Expression::Constant(pallas::Base::one());
|
let one = Expression::Constant(pallas::Base::one());
|
||||||
|
let q_s2 = meta.query_fixed(config.q_sinsemilla2, Rotation::cur());
|
||||||
q_s2.clone() * (q_s2 - one)
|
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_1_next = meta.query_advice(config.lambda_1, Rotation::next());
|
||||||
let lambda_2_cur = meta.query_advice(config.lambda_2, Rotation::cur());
|
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)
|
// Y_A = (lambda_1 + lambda_2) * (x_a - x_r)
|
||||||
let Y_A_next = Y_A(meta, Rotation::next());
|
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
|
// lambda2^2 - (x_a_next + x_r + x_a_cur) = 0
|
||||||
let secant_line =
|
let secant_line =
|
||||||
lambda_2_cur.clone().square() - (x_a_next.clone() + x_r + x_a_cur.clone());
|
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.
|
// y_a_final is assigned to the lambda1 column on the next offset.
|
||||||
let y_a_final = lambda_1_next;
|
let y_a_final = lambda_1_next;
|
||||||
|
|
||||||
two.clone() * Y_A_cur
|
Y_A_cur * two
|
||||||
+ (two.clone() - q_s3.clone()) * Y_A_next
|
+ (Expression::Constant(two) - q_s3.clone()) * Y_A_next
|
||||||
+ two * q_s3 * y_a_final
|
+ q_s3 * two * y_a_final
|
||||||
};
|
};
|
||||||
lhs - rhs
|
lhs - rhs
|
||||||
};
|
};
|
||||||
|
|
||||||
vec![
|
vec![
|
||||||
("Initial y_q", init_y_q_check),
|
|
||||||
("Secant line", q_s1.clone() * secant_line),
|
("Secant line", q_s1.clone() * secant_line),
|
||||||
("y check", q_s1 * y_check),
|
("y check", q_s1 * y_check),
|
||||||
]
|
]
|
||||||
|
|
|
@ -35,34 +35,25 @@ impl SinsemillaChip {
|
||||||
let x_q = *Q.coordinates().unwrap().x();
|
let x_q = *Q.coordinates().unwrap().x();
|
||||||
let y_q = *Q.coordinates().unwrap().y();
|
let y_q = *Q.coordinates().unwrap().y();
|
||||||
|
|
||||||
// Initialize the accumulator to `Q`.
|
// Constrain the initial x_a, lambda_1, lambda_2, x_p using the q_sinsemilla4
|
||||||
let (mut x_a, mut y_a): (X<pallas::Base>, Y<pallas::Base>) = {
|
// 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`.
|
// 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 x_a = {
|
||||||
let cell =
|
let cell =
|
||||||
region.assign_advice_from_constant(|| "fixed x_q", config.x_a, offset, x_q)?;
|
region.assign_advice_from_constant(|| "fixed x_q", config.x_a, offset, x_q)?;
|
||||||
CellValue::new(cell, Some(x_q))
|
CellValue::new(cell, Some(x_q))
|
||||||
};
|
};
|
||||||
|
|
||||||
// Constrain the initial x_a, lambda_1, lambda_2, x_p using the fixed y_q
|
x_a.into()
|
||||||
// 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())
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut zs_sum: Vec<Vec<CellValue<pallas::Base>>> = Vec::new();
|
let mut zs_sum: Vec<Vec<CellValue<pallas::Base>>> = Vec::new();
|
||||||
|
|
|
@ -695,6 +695,7 @@ mod tests {
|
||||||
meta,
|
meta,
|
||||||
advices[..5].try_into().unwrap(),
|
advices[..5].try_into().unwrap(),
|
||||||
advices[2],
|
advices[2],
|
||||||
|
lagrange_coeffs[0],
|
||||||
lookup,
|
lookup,
|
||||||
range_check.clone(),
|
range_check.clone(),
|
||||||
);
|
);
|
||||||
|
|
|
@ -191,6 +191,11 @@ pub mod tests {
|
||||||
let constants = meta.fixed_column();
|
let constants = meta.fixed_column();
|
||||||
meta.enable_constant(constants);
|
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
|
// Fixed columns for the Sinsemilla generator lookup table
|
||||||
let lookup = (
|
let lookup = (
|
||||||
meta.fixed_column(),
|
meta.fixed_column(),
|
||||||
|
@ -204,6 +209,7 @@ pub mod tests {
|
||||||
meta,
|
meta,
|
||||||
advices[5..].try_into().unwrap(),
|
advices[5..].try_into().unwrap(),
|
||||||
advices[7],
|
advices[7],
|
||||||
|
fixed_y_q_1,
|
||||||
lookup,
|
lookup,
|
||||||
range_check.clone(),
|
range_check.clone(),
|
||||||
);
|
);
|
||||||
|
@ -213,6 +219,7 @@ pub mod tests {
|
||||||
meta,
|
meta,
|
||||||
advices[..5].try_into().unwrap(),
|
advices[..5].try_into().unwrap(),
|
||||||
advices[2],
|
advices[2],
|
||||||
|
fixed_y_q_2,
|
||||||
lookup,
|
lookup,
|
||||||
range_check,
|
range_check,
|
||||||
);
|
);
|
||||||
|
|
|
@ -1365,6 +1365,7 @@ mod tests {
|
||||||
meta,
|
meta,
|
||||||
advices[..5].try_into().unwrap(),
|
advices[..5].try_into().unwrap(),
|
||||||
advices[2],
|
advices[2],
|
||||||
|
lagrange_coeffs[0],
|
||||||
lookup,
|
lookup,
|
||||||
range_check.clone(),
|
range_check.clone(),
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue