Merge pull request #589 from zcash/protocol-rule-links

halo2_gadgets: Add protocol rule links for the chip constraints
This commit is contained in:
str4d 2022-05-27 16:46:56 +01:00 committed by GitHub
commit c0db68aa05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 71 additions and 10 deletions

25
book/src/IDENTIFIERS.json Normal file
View File

@ -0,0 +1,25 @@
{
"decompose-combined-lookup": "design/gadgets/decomposition.html#combined-lookup-expression",
"decompose-short-lookup": "design/gadgets/decomposition.html#short-range-check",
"decompose-short-range": "design/gadgets/decomposition.html#short-range-decomposition",
"ecc-complete-addition": "design/gadgets/ecc/addition.html#complete-addition-constraints",
"ecc-incomplete-addition": "design/gadgets/ecc/addition.html#incomplete-addition-constraints",
"ecc-fixed-mul-base-canonicity": "design/gadgets/ecc/fixed-base-scalar-mul.html#base-field-element",
"ecc-fixed-mul-coordinates": "design/gadgets/ecc/fixed-base-scalar-mul.html#constrain-coordinates",
"ecc-fixed-mul-full-word": "design/gadgets/ecc/fixed-base-scalar-mul.html#full-width-scalar",
"ecc-fixed-mul-load-base": "design/gadgets/ecc/fixed-base-scalar-mul.html#load-fixed-base",
"ecc-fixed-mul-short-msb": "design/gadgets/ecc/fixed-base-scalar-mul.html#constrain-short-signed-msb",
"ecc-fixed-mul-short-conditional-neg": "design/gadgets/ecc/fixed-base-scalar-mul.html#constrain-short-signed-conditional-neg",
"ecc-var-mul-complete-gate": "design/gadgets/ecc/var-base-scalar-mul.html#complete-gate",
"ecc-var-mul-incomplete-first-row": "design/gadgets/ecc/var-base-scalar-mul.html#incomplete-first-row-gate",
"ecc-var-mul-incomplete-last-row": "design/gadgets/ecc/var-base-scalar-mul.html#incomplete-last-row-gate",
"ecc-var-mul-incomplete-main-loop": "design/gadgets/ecc/var-base-scalar-mul.html#incomplete-main-loop-gate",
"ecc-var-mul-lsb-gate": "design/gadgets/ecc/var-base-scalar-mul.html#lsb-gate",
"ecc-var-mul-overflow": "design/gadgets/ecc/var-base-scalar-mul.html#overflow-check-constraints",
"ecc-var-mul-witness-scalar": "design/gadgets/ecc/var-base-scalar-mul.html#witness-scalar",
"ecc-witness-point": "design/gadgets/ecc/witnessing-points.html#points-including-the-identity",
"ecc-witness-non-identity-point": "design/gadgets/ecc/witnessing-points.html#non-identity-points",
"sinsemilla-constraints": "design/gadgets/sinsemilla.html#optimized-sinsemilla-gate",
"sinsemilla-merkle-crh-bit-lengths": "design/gadgets/sinsemilla/merkle-crh.html#bit-length-constraints",
"sinsemilla-merkle-crh-decomposition": "design/gadgets/sinsemilla/merkle-crh.html#decomposition-constraints"
}

View File

@ -41,7 +41,7 @@ So we get the constraints:
and so cannot be used with arbitrary inputs.
- $(y_r + y_q) \cdot (x_p - x_q) - (y_p - y_q) \cdot (x_q - x_r) = 0.$
### Constraints
### Constraints <a name="incomplete-addition-constraints">
$$
\begin{array}{|c|l|}
@ -99,7 +99,7 @@ $\hspace{1em}
\end{array}
$
### Constraints
### Constraints <a name="complete-addition-constraints">
$$
\begin{array}{|c|lcl|l|}

View File

@ -91,7 +91,7 @@ $\mathsf{v^{old}}$ and $\mathsf{v^{new}}$ are each already constrained to $64$ b
Decompose the magnitude $m$ into three-bit windows, and range-constrain each window, using the [short range decomposition](../decomposition.md#short-range-decomposition) gadget in strict mode, with $W = 22, K = 3.$
We have two additional constraints:
<a name="constrain-short-signed-msb"></a> We have two additional constraints:
$$
\begin{array}{|c|l|l|}
\hline
@ -147,7 +147,7 @@ Given a decomposed scalar $\alpha$ and a fixed base $B$, we compute $[\alpha]B$
> Note: complete addition is required in the final step to correctly map $[0]B$ to a representation of the point at infinity, $(0,0)$; and also to handle a corner case for which the last step is a doubling.
Constraints:
<a name="constrain-coordinates"></a> Constraints:
$$
\begin{array}{|c|l|}
\hline
@ -163,7 +163,7 @@ where $b = 5$ (from the Pallas curve equation).
### Signed short exponent
Recall that the signed short exponent is witnessed as a $64-$bit magnitude $m$, and a sign $s \in {1, -1}.$ Using the above algorithm, we compute $P = [m] \mathcal{B}$. Then, to get the final result $P',$ we conditionally negate $P$ using $(x, y) \mapsto (x, s \cdot y)$.
Constraints:
<a name="constrain-short-signed-conditional-neg"></a> Constraints:
$$
\begin{array}{|c|l|}
\hline

View File

@ -177,7 +177,7 @@ $$
For each $hi$ and $lo$ half, we have three sets of gates. Note that $i$ is going from $255..=3$; $i$ is NOT indexing the rows.
#### $q_1 = 1$
### $q_1 = 1$ <a name="incomplete-first-row-gate">
This gate is only used on the first row (before the for loop). We check that $\lambda_1, \lambda_2$ are initialized to values consistent with the initial $y_A.$
$$
\begin{array}{|c|l|}
@ -194,7 +194,7 @@ y_{A,n}^\text{witnessed} &\text{ is witnessed.}
\end{aligned}
$$
#### $q_2 = 1$
### $q_2 = 1$ <a name="incomplete-main-loop-gate">
This gate is used on all rows corresponding to the for loop except the last.
$$
@ -218,7 +218,7 @@ y_{A,i-1} &= \frac{(\lambda_{1,i-1} + \lambda_{2,i-1}) \cdot (x_{A,i-1} - (\lamb
\end{aligned}
$$
#### $q_3 = 1$
### $q_3 = 1$ <a name="incomplete-last-row-gate">
This gate is used on the final iteration of the for loop, handling the special case where we check that the output $y_A$ has been witnessed correctly.
$$
\begin{array}{|c|l|}

View File

@ -89,6 +89,7 @@ impl Config {
}
fn create_gate(&self, meta: &mut ConstraintSystem<pallas::Base>) {
// https://p.z.cash/halo2-0.1:ecc-complete-addition
meta.create_gate("complete addition", |meta| {
let q_add = meta.query_selector(self.q_add);
let x_p = meta.query_advice(self.x_p, Rotation::cur());

View File

@ -56,7 +56,8 @@ impl Config {
}
fn create_gate(&self, meta: &mut ConstraintSystem<pallas::Base>) {
meta.create_gate("incomplete addition gates", |meta| {
// https://p.z.cash/halo2-0.1:ecc-incomplete-addition
meta.create_gate("incomplete addition", |meta| {
let q_add_incomplete = meta.query_selector(self.q_add_incomplete);
let x_p = meta.query_advice(self.x_p, Rotation::cur());
let y_p = meta.query_advice(self.y_p, Rotation::cur());

View File

@ -130,6 +130,7 @@ impl Config {
fn create_gate(&self, meta: &mut ConstraintSystem<pallas::Base>) {
// If `lsb` is 0, (x, y) = (x_p, -y_p). If `lsb` is 1, (x, y) = (0,0).
// https://p.z.cash/halo2-0.1:ecc-var-mul-lsb-gate?partial
meta.create_gate("LSB check", |meta| {
let q_mul_lsb = meta.query_selector(self.q_mul_lsb);
@ -317,6 +318,8 @@ impl Config {
/// | x_p | y_p | acc_x | acc_y | complete addition | z_1 | q_mul_lsb = 1
/// |base_x|base_y| res_x | res_y | | | | | | z_0 |
/// ```
///
/// [Specification](https://p.z.cash/halo2-0.1:ecc-var-mul-lsb-gate?partial).
fn process_lsb(
&self,
region: &mut Region<'_, pallas::Base>,
@ -432,6 +435,7 @@ impl<F: FieldExt> Deref for Z<F> {
}
}
// https://p.z.cash/halo2-0.1:ecc-var-mul-witness-scalar?partial
fn decompose_for_scalar_mul(scalar: Option<&pallas::Base>) -> Vec<Option<bool>> {
construct_uint! {
struct U256(4);

View File

@ -49,6 +49,7 @@ impl Config {
// | y_p | z_{i + 1} |
// | | base_y |
// | | z_i |
// https://p.z.cash/halo2-0.1:ecc-var-mul-complete-gate
meta.create_gate(
"Decompose scalar for complete bits of variable-base mul",
|meta| {

View File

@ -116,6 +116,8 @@ impl<const NUM_BITS: usize> Config<NUM_BITS> {
};
// Constraints used for q_mul_{2, 3} == 1
// https://p.z.cash/halo2-0.1:ecc-var-mul-incomplete-main-loop?partial
// https://p.z.cash/halo2-0.1:ecc-var-mul-incomplete-last-row?partial
let for_loop = |meta: &mut VirtualCells<pallas::Base>,
y_a_next: Expression<pallas::Base>| {
let one = Expression::Constant(pallas::Base::one());
@ -167,6 +169,7 @@ impl<const NUM_BITS: usize> Config<NUM_BITS> {
};
// q_mul_1 == 1 checks
// https://p.z.cash/halo2-0.1:ecc-var-mul-incomplete-first-row
meta.create_gate("q_mul_1 == 1 checks", |meta| {
let q_mul_1 = meta.query_selector(self.q_mul_1);
@ -176,6 +179,7 @@ impl<const NUM_BITS: usize> Config<NUM_BITS> {
});
// q_mul_2 == 1 checks
// https://p.z.cash/halo2-0.1:ecc-var-mul-incomplete-main-loop?partial
meta.create_gate("q_mul_2 == 1 checks", |meta| {
let q_mul_2 = meta.query_selector(self.q_mul_2);
@ -205,6 +209,7 @@ impl<const NUM_BITS: usize> Config<NUM_BITS> {
});
// q_mul_3 == 1 checks
// https://p.z.cash/halo2-0.1:ecc-var-mul-incomplete-last-row?partial
meta.create_gate("q_mul_3 == 1 checks", |meta| {
let q_mul_3 = meta.query_selector(self.q_mul_3);
let y_a_final = meta.query_advice(self.double_and_add.lambda_1, Rotation::next());
@ -293,6 +298,7 @@ impl<const NUM_BITS: usize> Config<NUM_BITS> {
// Incomplete addition
for (row, k) in bits.iter().enumerate() {
// z_{i} = 2 * z_{i+1} + k_i
// https://p.z.cash/halo2-0.1:ecc-var-mul-witness-scalar?partial
let z_val = z
.value()
.zip(k.as_ref())

View File

@ -47,6 +47,7 @@ impl Config {
}
fn create_gate(&self, meta: &mut ConstraintSystem<pallas::Base>) {
// https://p.z.cash/halo2-0.1:ecc-var-mul-overflow
meta.create_gate("overflow checks", |meta| {
let q_mul_overflow = meta.query_selector(self.q_mul_overflow);

View File

@ -131,6 +131,7 @@ impl<FixedPoints: super::FixedPoints<pallas::Affine>> Config<FixedPoints> {
});
}
/// [Specification](https://p.z.cash/halo2-0.1:ecc-fixed-mul-coordinates).
#[allow(clippy::op_ref)]
fn coords_check(
&self,
@ -196,6 +197,7 @@ impl<FixedPoints: super::FixedPoints<pallas::Affine>> Config<FixedPoints> {
Ok((acc, mul_b))
}
/// [Specification](https://p.z.cash/halo2-0.1:ecc-fixed-mul-load-base).
fn assign_fixed_constants<F: FixedPoint<pallas::Affine>, const NUM_WINDOWS: usize>(
&self,
region: &mut Region<'_, pallas::Base>,

View File

@ -58,6 +58,7 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
fn create_gate(&self, meta: &mut ConstraintSystem<pallas::Base>) {
// Check that the base field element is canonical.
// https://p.z.cash/halo2-0.1:ecc-fixed-mul-base-canonicity
meta.create_gate("Canonicity checks", |meta| {
let q_mul_fixed_base_field = meta.query_selector(self.q_mul_fixed_base_field);

View File

@ -33,6 +33,7 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
fn create_gate(&self, meta: &mut ConstraintSystem<pallas::Base>) {
// Check that each window `k` is within 3 bits
// https://p.z.cash/halo2-0.1:ecc-fixed-mul-full-word
meta.create_gate("Full-width fixed-base scalar mul", |meta| {
let q_mul_fixed_full = meta.query_selector(self.q_mul_fixed_full);
let window = meta.query_advice(self.super_config.window, Rotation::cur());

View File

@ -33,6 +33,9 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
}
fn create_gate(&self, meta: &mut ConstraintSystem<pallas::Base>) {
// Gate contains the following constraints:
// - https://p.z.cash/halo2-0.1:ecc-fixed-mul-short-msb
// - https://p.z.cash/halo2-0.1:ecc-fixed-mul-short-conditional-neg
meta.create_gate("Short fixed-base mul gate", |meta| {
let q_mul_fixed_short = meta.query_selector(self.q_mul_fixed_short);
let y_p = meta.query_advice(self.super_config.add_config.y_p, Rotation::cur());

View File

@ -53,6 +53,7 @@ impl Config {
y.square() - (x.clone().square() * x) - Expression::Constant(pallas::Affine::b())
};
// https://p.z.cash/halo2-0.1:ecc-witness-point
meta.create_gate("witness point", |meta| {
// Check that the point being witnessed is either:
// - the identity, which is mapped to (0, 0) in affine coordinates; or
@ -72,6 +73,7 @@ impl Config {
]
});
// https://p.z.cash/halo2-0.1:ecc-witness-non-identity-point
meta.create_gate("witness non-identity point", |meta| {
// Check that the point being witnessed is a valid curve point y^2 = x^3 + b,
// where b = 5 in the Pallas equation

View File

@ -200,6 +200,7 @@ where
};
// Check that the initial x_A, x_P, lambda_1, lambda_2 are consistent with y_Q.
// https://p.z.cash/halo2-0.1:sinsemilla-constraints?partial
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());
@ -213,6 +214,7 @@ where
Constraints::with_selector(q_s4, Some(("init_y_q_check", init_y_q_check)))
});
// https://p.z.cash/halo2-0.1:sinsemilla-constraints?partial
meta.create_gate("Sinsemilla gate", |meta| {
let q_s1 = meta.query_selector(config.q_sinsemilla1);
let q_s3 = config.q_s3(meta);

View File

@ -36,6 +36,7 @@ impl GeneratorTableConfig {
config.generator_table.table_y,
);
// https://p.z.cash/halo2-0.1:sinsemilla-constraints?partial
meta.lookup(|meta| {
let q_s1 = meta.query_selector(config.q_sinsemilla1);
let q_s2 = meta.query_fixed(config.q_sinsemilla2, Rotation::cur());

View File

@ -25,6 +25,7 @@ where
Fixed: FixedPoints<pallas::Affine>,
Commit: CommitDomains<pallas::Affine, Fixed, Hash>,
{
/// [Specification](https://p.z.cash/halo2-0.1:sinsemilla-constraints?partial).
#[allow(non_snake_case)]
#[allow(clippy::type_complexity)]
pub(super) fn hash_message(

View File

@ -150,11 +150,13 @@ where
// b_2 has been constrained to be 5 bits outside this gate.
let b_2 = meta.query_advice(advices[3], Rotation::next());
// Constrain b_1 + 2^5 b_2 = z1_b
// https://p.z.cash/halo2-0.1:sinsemilla-merkle-crh-bit-lengths?partial
let b1_b2_check = z1_b.clone() - (b_1.clone() + b_2.clone() * two_pow_5);
// Derive b_0 (constrained by SinsemillaHash to be 10 bits)
let b_0 = b_whole - (z1_b * two_pow_10);
// Check that left = a_1 (240 bits) || b_0 (10 bits) || b_1 (5 bits)
// https://p.z.cash/halo2-0.1:sinsemilla-merkle-crh-decomposition?partial
let left_check = {
let reconstructed = {
let two_pow_240 = pallas::Base::from_u128(1 << 120).square();
@ -166,6 +168,7 @@ where
// Check that right = b_2 (5 bits) || c (250 bits)
// The Orchard specification allows this representation to be non-canonical.
// <https://zips.z.cash/protocol/protocol.pdf#merklepath>
// https://p.z.cash/halo2-0.1:sinsemilla-merkle-crh-decomposition?partial
let right_check = b_2 + c_whole * two_pow_5 - right_node;
Constraints::with_selector(
@ -213,7 +216,6 @@ where
) -> Result<Self::Var, Error> {
let config = self.config().clone();
// <https://zips.z.cash/protocol/protocol.pdf#orchardmerklecrh>
// We need to hash `l || left || right`, where `l` is a 10-bit value.
// We allow `left` and `right` to be non-canonical 255-bit encodings.
//
@ -224,6 +226,8 @@ where
//
// We start by witnessing all of the individual pieces, and range-constraining the
// short pieces b_1 and b_2.
//
// https://p.z.cash/halo2-0.1:sinsemilla-merkle-crh-bit-lengths?partial
// `a = a_0||a_1` = `l` || (bits 0..=239 of `left`)
let a = MessagePiece::from_subpieces(
@ -291,6 +295,8 @@ where
// this map is the same as why Sinsemilla uses incomplete addition: this situation
// yields a nontrivial discrete log relation, and by assumption it is hard to find
// these.
//
// https://p.z.cash/proto:merkle-crh-orchard
let (point, zs) = self.hash_to_point(
layouter.namespace(|| format!("hash at l = {}", l)),
Q,

View File

@ -84,6 +84,7 @@ impl<F: FieldExt + PrimeFieldBits, const WINDOW_NUM_BITS: usize>
_marker: PhantomData,
};
// https://p.z.cash/halo2-0.1:decompose-short-range
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());

View File

@ -96,6 +96,7 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
_marker: PhantomData,
};
// https://p.z.cash/halo2-0.1:decompose-combined-lookup
meta.lookup(|meta| {
let q_lookup = meta.query_selector(config.q_lookup);
let q_running = meta.query_selector(config.q_running);
@ -130,6 +131,7 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
});
// For short lookups, check that the word has been shifted by the correct number of bits.
// https://p.z.cash/halo2-0.1:decompose-short-lookup
meta.create_gate("Short lookup bitshift", |meta| {
let q_bitshift = meta.query_selector(config.q_bitshift);
let word = meta.query_advice(config.running_sum, Rotation::prev());