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:
therealyingtong 2021-07-23 20:25:02 +08:00
parent cba0d8672b
commit f6c951d975
7 changed files with 56 additions and 39 deletions

View File

@ -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,
); );

View File

@ -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,
); );

View File

@ -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),
] ]

View File

@ -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();

View File

@ -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(),
); );

View File

@ -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,
); );

View File

@ -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(),
); );