diff --git a/book/src/IDENTIFIERS.json b/book/src/IDENTIFIERS.json new file mode 100644 index 00000000..8080c0ec --- /dev/null +++ b/book/src/IDENTIFIERS.json @@ -0,0 +1,19 @@ +{ + "commit-ivk-bit-lengths": "design/circuit/commit-ivk.html#bit-length-constraints", + "commit-ivk-canonicity-ak": "design/circuit/commit-ivk.html#canonicity-ak", + "commit-ivk-canonicity-nk": "design/circuit/commit-ivk.html#canonicity-nk", + "commit-ivk-decompositions": "design/circuit/commit-ivk.html#constrain-bit-lengths", + "commit-ivk-region-layout": "design/circuit/commit-ivk.html#region-layout", + "note-commit-canonicity-g_d": "design/circuit/note-commit.html#canonicity-g_d", + "note-commit-canonicity-pk_d": "design/circuit/note-commit.html#canonicity-pk_d", + "note-commit-canonicity-psi": "design/circuit/note-commit.html#canonicity-psi", + "note-commit-canonicity-rho": "design/circuit/note-commit.html#canonicity-rho", + "note-commit-canonicity-v": "design/circuit/note-commit.html#canonicity-v", + "note-commit-canonicity-y": "design/circuit/note-commit.html#canonicity-y", + "note-commit-decomposition-b": "design/circuit/note-commit.html#decomposition-b", + "note-commit-decomposition-d": "design/circuit/note-commit.html#decomposition-d", + "note-commit-decomposition-e": "design/circuit/note-commit.html#decomposition-e", + "note-commit-decomposition-g": "design/circuit/note-commit.html#decomposition-g", + "note-commit-decomposition-h": "design/circuit/note-commit.html#decomposition-h", + "note-commit-decomposition-y": "design/circuit/note-commit.html#decomposition-y" +} \ No newline at end of file diff --git a/book/src/design/circuit/commit-ivk.md b/book/src/design/circuit/commit-ivk.md index 26c33cfa..dd8c588d 100644 --- a/book/src/design/circuit/commit-ivk.md +++ b/book/src/design/circuit/commit-ivk.md @@ -146,7 +146,7 @@ $\NullifierKey$). > - Let $x' = x + t' - t$. > - Enforce $0 \leq x' < t'$. -### $\AuthSignPublic$ with $b_1 = 1 \implies \AuthSignPublic \geq 2^{254}$ +### $\AuthSignPublic$ with $b_1 = 1 \implies \AuthSignPublic \geq 2^{254}$ In these cases, we check that $\textsf{ak}_{0..=253} < t_\mathbb{P}$: @@ -181,7 +181,7 @@ $$ \end{array} $$ -### $\NullifierKey$ with $d_1 = 1 \implies \NullifierKey \geq 2^{254}$ +### $\NullifierKey$ with $d_1 = 1 \implies \NullifierKey \geq 2^{254}$ In these cases, we check that $\textsf{nk}_{0..=253} < t_\mathbb{P}$: diff --git a/book/src/design/circuit/note-commit.md b/book/src/design/circuit/note-commit.md index e3e04fc6..5e8b3c9f 100644 --- a/book/src/design/circuit/note-commit.md +++ b/book/src/design/circuit/note-commit.md @@ -104,7 +104,7 @@ The following helper gates are defined: - $\ShortLookupRangeCheck{}$ is a [short lookup range check](../decomposition.md#short-range-check). -### $b = b_0 \bconcat b_1 \bconcat b_2 \bconcat b_3$ +### $b = b_0 \bconcat b_1 \bconcat b_2 \bconcat b_3$ $b$ has been constrained to be $10$ bits by the Sinsemilla hash. #### Region layout @@ -132,7 +132,7 @@ Outside this gate, we have constrained: - $\ShortLookupRangeCheck{b_0, 4}$ - $\ShortLookupRangeCheck{b_3, 4}$ -### $d = d_0 \bconcat d_1 \bconcat d_2 \bconcat d_3$ +### $d = d_0 \bconcat d_1 \bconcat d_2 \bconcat d_3$ $d$ has been constrained to be $60$ bits by the $\SinsemillaHash$. #### Region layout @@ -161,7 +161,7 @@ Outside this gate, we have constrained: - $d_3$ is equality-constrained to $z_{d,1}$, where the latter is the index-1 running sum output of $\SinsemillaHash(d),$ constrained by the hash to be $50$ bits. -### $e = e_0 \bconcat e_1$ +### $e = e_0 \bconcat e_1$ $e$ has been constrained to be $10$ bits by the $\SinsemillaHash$. #### Region layout @@ -186,7 +186,7 @@ Outside this gate, we have constrained: - $\ShortLookupRangeCheck{e_0, 6}$ - $\ShortLookupRangeCheck{e_1, 4}$ -### $g = g_0 \bconcat g_1 \bconcat g_2$ +### $g = g_0 \bconcat g_1 \bconcat g_2$ $g$ has been constrained to be $250$ bits by the $\SinsemillaHash$. #### Region layout @@ -214,7 +214,7 @@ Outside this gate, we have constrained: - $g_2$ is equality-constrained to $z_{g,1}$, where the latter is the index-1 running sum output of $\SinsemillaHash(g),$ constrained by the hash to be 240 bits. -### $h = h_0 \bconcat h_1 \bconcat h_2$ +### $h = h_0 \bconcat h_1 \bconcat h_2$ $h$ has been constrained to be $10$ bits by the $\SinsemillaHash$. #### Region layout @@ -280,7 +280,7 @@ below are enforced if and only if the corresponding top bit is set to 1. > - Let $x' = x + t' - t$. > - Enforce $0 \leq x' < t'$. -### $x(\mathsf{g_d})$ with $b_1 = 1 \implies x(\mathsf{g_d}) \geq 2^{254}$ +### $x(\mathsf{g_d})$ with $b_1 = 1 \implies x(\mathsf{g_d}) \geq 2^{254}$ Recall that $x(\mathsf{g_d}) = a + 2^{250} \cdot b_0 + 2^{254} \cdot b_1$. When the top bit $b_1$ is set, we check that $x(\mathsf{g_d})_{0..=253} < t_\mathbb{P}$: @@ -327,7 +327,7 @@ $$ \end{array} $$ -### $x(\mathsf{pk_d})$ with $d_0 = 1 \implies x(\mathsf{pk_d}) \geq 2^{254}$ +### $x(\mathsf{pk_d})$ with $d_0 = 1 \implies x(\mathsf{pk_d}) \geq 2^{254}$ Recall that $x(\mathsf{pk_d}) = b_3 + 2^4 \cdot c + 2^{254} \cdot d_0$. When the top bit $d_0$ is set, we check that $x(\mathsf{pk_d})_{0..=253} < t_\mathbb{P}$: @@ -368,7 +368,7 @@ $$ \end{array} $$ -### $\mathsf{v} = d_2 + 2^8 \cdot d_3 + 2^{58} \cdot e_0$ +### $\mathsf{v} = d_2 + 2^8 \cdot d_3 + 2^{58} \cdot e_0$ #### Region layout $$ @@ -388,7 +388,7 @@ $$ \end{array} $$ -### $\rho$ with $g_0 = 1 \implies \rho \geq 2^{254}$ +### $\rho$ with $g_0 = 1 \implies \rho \geq 2^{254}$ Recall that $\rho = e_1 + 2^4 \cdot f + 2^{254} \cdot g_0$. When the top bit $g_0$ is set, we check that $\rho_{0..=253} < t_\mathbb{P}$: @@ -429,7 +429,7 @@ $$ \end{array} $$ -### $\psi$ with $h_1 = 1 \implies \psi \geq 2^{254}$ +### $\psi$ with $h_1 = 1 \implies \psi \geq 2^{254}$ Recall that $\psi = g_1 + 2^9 \cdot g_2 + 2^{249} \cdot h_0 + 2^{254} \cdot h_1$. When the top bit $h_1$ is set, we check that $\psi_{0..=253} < t_\mathbb{P}$: @@ -476,7 +476,7 @@ $$ \end{array} $$ -### $y$-coordinate checks +### $y$-coordinate checks Note that only the $ỹ$ LSB of the $y$-coordinates $y(\mathsf{g_d}), y(\mathsf{pk_d})$ was input to the hash, while the other bits of the $y$-coordinate were unused. However, we @@ -523,7 +523,7 @@ $$ \end{array} $$ -### $y(\mathsf{g_d})$ with $k_3 = 1 \implies y(\mathsf{g_d}) \geq 2^{254}$ +### $y(\mathsf{g_d})$ with $k_3 = 1 \implies y(\mathsf{g_d}) \geq 2^{254}$ In these cases, we check that $y(\mathsf{g_d})_{0..=253} < t_\mathbb{P}$: diff --git a/src/circuit.rs b/src/circuit.rs index a64188fa..f1742a8d 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -143,10 +143,10 @@ impl plonk::Circuit for Circuit { meta.advice_column(), ]; - // Constrain v_old - v_new = magnitude * sign - // Either v_old = 0, or calculated root = anchor - // Constrain v_old = 0 or enable_spends = 1. - // Constrain v_new = 0 or enable_outputs = 1. + // Constrain v_old - v_new = magnitude * sign (https://p.z.cash/ZKS:action-cv-net-integrity?partial). + // Either v_old = 0, or calculated root = anchor (https://p.z.cash/ZKS:action-merkle-path-validity?partial). + // Constrain v_old = 0 or enable_spends = 1 (https://p.z.cash/ZKS:action-enable-spend). + // Constrain v_new = 0 or enable_outputs = 1 (https://p.z.cash/ZKS:action-enable-output). let q_orchard = meta.selector(); meta.create_gate("Orchard circuit checks", |meta| { let q_orchard = meta.query_selector(q_orchard); @@ -389,7 +389,7 @@ impl plonk::Circuit for Circuit { (psi_old, rho_old, cm_old, g_d_old, ak_P, nk, v_old, v_new) }; - // Merkle path validity check. + // Merkle path validity check (https://p.z.cash/ZKS:action-merkle-path-validity?partial). let root = { let path = self .path @@ -404,7 +404,7 @@ impl plonk::Circuit for Circuit { merkle_inputs.calculate_root(layouter.namespace(|| "Merkle path"), leaf)? }; - // Value commitment integrity. + // Value commitment integrity (https://p.z.cash/ZKS:action-cv-net-integrity?partial). let v_net_magnitude_sign = { // Witness the magnitude and sign of v_net = v_old - v_new let v_net_magnitude_sign = { @@ -462,7 +462,7 @@ impl plonk::Circuit for Circuit { v_net_magnitude_sign }; - // Nullifier integrity + // Nullifier integrity (https://p.z.cash/ZKS:action-nullifier-integrity). let nf_old = { let nf_old = gadget::derive_nullifier( layouter.namespace(|| "nf_old = DeriveNullifier_nk(rho_old, psi_old, cm_old)"), @@ -481,7 +481,7 @@ impl plonk::Circuit for Circuit { nf_old }; - // Spend authority + // Spend authority (https://p.z.cash/ZKS:action-spend-authority) { let alpha = ScalarFixed::new(ecc_chip.clone(), layouter.namespace(|| "alpha"), self.alpha)?; @@ -501,7 +501,7 @@ impl plonk::Circuit for Circuit { layouter.constrain_instance(rk.inner().y().cell(), config.primary, RK_Y)?; } - // Diversified address integrity. + // Diversified address integrity (https://p.z.cash/ZKS:action-addr-integrity?partial). let pk_d_old = { let ivk = { let ak = ak_P.extract_p().inner().clone(); @@ -547,7 +547,7 @@ impl plonk::Circuit for Circuit { pk_d_old }; - // Old note commitment integrity. + // Old note commitment integrity (https://p.z.cash/ZKS:action-cm-old-integrity?partial). { let rcm_old = ScalarFixed::new( ecc_chip.clone(), @@ -575,7 +575,7 @@ impl plonk::Circuit for Circuit { derived_cm_old.constrain_equal(layouter.namespace(|| "cm_old equality"), &cm_old)?; } - // New note commitment integrity. + // New note commitment integrity (https://p.z.cash/ZKS:action-cmx-new-integrity?partial). { // Witness g_d_new let g_d_new = { diff --git a/src/circuit/commit_ivk.rs b/src/circuit/commit_ivk.rs index 2bee762f..1460c9d8 100644 --- a/src/circuit/commit_ivk.rs +++ b/src/circuit/commit_ivk.rs @@ -52,6 +52,8 @@ impl CommitIvkChip { // - c: 240 bits, // - d: 10 bits // + // https://p.z.cash/orchard-0.1:commit-ivk-decompositions + // https://p.z.cash/orchard-0.1:commit-ivk-region-layout?partial /* The pieces are laid out in this configuration: @@ -106,6 +108,7 @@ impl CommitIvkChip { let d_decomposition_check = d_whole - (d_0.clone() + d_1.clone() * two_pow_9); // Check `b_1` and `d_1` are each a single-bit value. + // https://p.z.cash/orchard-0.1:commit-ivk-bit-lengths?partial let b1_bool_check = bool_check(b_1.clone()); let d1_bool_check = bool_check(d_1.clone()); @@ -126,6 +129,7 @@ impl CommitIvkChip { // ak = a (250 bits) || b_0 (4 bits) || b_1 (1 bit) // The `ak` canonicity checks are enforced if and only if `b_1` = 1. + // https://p.z.cash/orchard-0.1:commit-ivk-canonicity-ak?partial let ak_canonicity_checks = { // b_1 = 1 => b_0 = 0 let b0_canon_check = b_1.clone() * b_0; @@ -163,6 +167,7 @@ impl CommitIvkChip { // nk = b_2 (5 bits) || c (240 bits) || d_0 (9 bits) || d_1 (1 bit) // The `nk` canonicity checks are enforced if and only if `d_1` = 1. + // https://p.z.cash/orchard-0.1:commit-ivk-canonicity-nk?partial let nk_canonicity_checks = { // d_1 = 1 => d_0 = 0 let c0_canon_check = d_1.clone() * d_0; @@ -257,6 +262,8 @@ pub(in crate::circuit) mod gadgets { // // We start by witnessing all of the individual pieces, and range-constraining // the short pieces b_0, b_2, and d_0. + // + // https://p.z.cash/orchard-0.1:commit-ivk-bit-lengths?partial // `a` = bits 0..=249 of `ak` let a = MessagePiece::from_subpieces( @@ -327,6 +334,8 @@ pub(in crate::circuit) mod gadgets { // `ivk = ⊥` is handled internally to `CommitDomain::short_commit`: incomplete // addition constraints allows ⊥ to occur, and then during synthesis it detects // these edge cases and raises an error (aborting proof creation). + // + // https://p.z.cash/ZKS:action-addr-integrity?partial let (ivk, zs) = { let message = Message::from_pieces( sinsemilla_chip.clone(), @@ -385,6 +394,8 @@ pub(in crate::circuit) mod gadgets { } /// Witnesses and decomposes the `a'` value we need to check the canonicity of `ak`. + /// + /// [Specification](https://p.z.cash/orchard-0.1:commit-ivk-canonicity-ak?partial). #[allow(clippy::type_complexity)] fn ak_canonicity( lookup_config: &LookupRangeCheckConfig, @@ -424,6 +435,8 @@ pub(in crate::circuit) mod gadgets { } /// Witnesses and decomposes the `b2c'` value we need to check the canonicity of `nk`. + /// + /// [Specification](https://p.z.cash/orchard-0.1:commit-ivk-canonicity-nk?partial). #[allow(clippy::type_complexity)] fn nk_canonicity( lookup_config: &LookupRangeCheckConfig, @@ -468,7 +481,9 @@ pub(in crate::circuit) mod gadgets { } impl CommitIvkConfig { - /// Assign cells for the canonicity gate. + /// Assign cells for the [canonicity gate]. + /// + /// [canonicity gate]: https://p.z.cash/orchard-0.1:commit-ivk-region-layout?partial /* The pieces are laid out in this configuration: diff --git a/src/circuit/note_commit.rs b/src/circuit/note_commit.rs index d2a27adf..b7163285 100644 --- a/src/circuit/note_commit.rs +++ b/src/circuit/note_commit.rs @@ -59,6 +59,8 @@ type CanonicityBounds = ( /// ------------------------------------ /// | b | b_0 | b_1 | 1 | /// | | b_2 | b_3 | 0 | +/// +/// https://p.z.cash/orchard-0.1:note-commit-decomposition-b?partial #[derive(Clone, Debug)] struct DecomposeB { q_notecommit_b: Selector, @@ -207,6 +209,8 @@ impl DecomposeB { /// ------------------------------------ /// | d | d_0 | d_1 | 1 | /// | | d_2 | d_3 | 0 | +/// +/// https://p.z.cash/orchard-0.1:note-commit-decomposition-d?partial #[derive(Clone, Debug)] struct DecomposeD { q_notecommit_d: Selector, @@ -346,6 +350,8 @@ impl DecomposeD { /// | A_6 | A_7 | A_8 | q_notecommit_e | /// ------------------------------------ /// | e | e_0 | e_1 | 1 | +/// +/// https://p.z.cash/orchard-0.1:note-commit-decomposition-e?partial #[derive(Clone, Debug)] struct DecomposeE { q_notecommit_e: Selector, @@ -463,6 +469,8 @@ impl DecomposeE { /// ------------------------------ /// | g | g_0 | 1 | /// | g_1 | g_2 | 0 | +/// +/// https://p.z.cash/orchard-0.1:note-commit-decomposition-g?partial #[derive(Clone, Debug)] struct DecomposeG { q_notecommit_g: Selector, @@ -588,6 +596,8 @@ impl DecomposeG { /// | A_6 | A_7 | A_8 | q_notecommit_h | /// ------------------------------------ /// | h | h_0 | h_1 | 1 | +/// +/// https://p.z.cash/orchard-0.1:note-commit-decomposition-h?partial #[derive(Clone, Debug)] struct DecomposeH { q_notecommit_h: Selector, @@ -708,6 +718,8 @@ impl DecomposeH { /// ----------------------------------------------------------- /// | x(g_d) | b_0 | a | z13_a | 1 | /// | | b_1 | a_prime | z13_a_prime | 0 | +/// +/// https://p.z.cash/orchard-0.1:note-commit-canonicity-g_d?partial #[derive(Clone, Debug)] struct GdCanonicity { q_notecommit_g_d: Selector, @@ -823,6 +835,8 @@ impl GdCanonicity { /// ------------------------------------------------------------------- /// | x(pk_d) | b_3 | c | z13_c | 1 | /// | | d_0 | b3_c_prime | z14_b3_c_prime | 0 | +/// +/// https://p.z.cash/orchard-0.1:note-commit-canonicity-pk_d?partial #[derive(Clone, Debug)] struct PkdCanonicity { q_notecommit_pk_d: Selector, @@ -937,6 +951,8 @@ impl PkdCanonicity { /// | A_6 | A_7 | A_8 | A_9 | q_notecommit_value | /// ------------------------------------------------ /// | value | d_2 | d_3 | e_0 | 1 | +/// +/// https://p.z.cash/orchard-0.1:note-commit-canonicity-v?partial #[derive(Clone, Debug)] struct ValueCanonicity { q_notecommit_value: Selector, @@ -1013,6 +1029,8 @@ impl ValueCanonicity { /// -------------------------------------------------------------- /// | rho | e_1 | f | z13_f | 1 | /// | | g_0 | e1_f_prime | z14_e1_f_prime | 0 | +/// +/// https://p.z.cash/orchard-0.1:note-commit-canonicity-rho?partial #[derive(Clone, Debug)] struct RhoCanonicity { q_notecommit_rho: Selector, @@ -1126,6 +1144,8 @@ impl RhoCanonicity { /// ---------------------------------------------------------------- /// | psi | g_1 | g_2 | z13_g | 1 | /// | h_0 | h_1 | g1_g2_prime | z13_g1_g2_prime | 0 | +/// +/// https://p.z.cash/orchard-0.1:note-commit-canonicity-psi?partial #[derive(Clone, Debug)] struct PsiCanonicity { q_notecommit_psi: Selector, @@ -1296,6 +1316,7 @@ impl YCanonicity { let z13_j_prime = meta.query_advice(advices[9], Rotation::next()); // Decomposition checks + // https://p.z.cash/orchard-0.1:note-commit-decomposition-y?partial let decomposition_checks = { // Check that k_3 is boolean let k3_check = bool_check(k_3.clone()); @@ -1316,6 +1337,7 @@ impl YCanonicity { }; // Canonicity checks. These are enforced if and only if k_3 = 1. + // https://p.z.cash/orchard-0.1:note-commit-canonicity-y?partial let canonicity_checks = { iter::empty() .chain(Some(("k_3 = 1 => k_2 = 0", k_2))) @@ -1652,6 +1674,9 @@ pub(in crate::circuit) mod gadgets { // `cm = ⊥` is handled internally to `CommitDomain::commit`: incomplete addition // constraints allows ⊥ to occur, and then during synthesis it detects these edge // cases and raises an error (aborting proof creation). + // + // https://p.z.cash/ZKS:action-cm-old-integrity?partial + // https://p.z.cash/ZKS:action-cmx-new-integrity?partial let (cm, zs) = { let message = Message::from_pieces( chip.clone(), @@ -1774,6 +1799,10 @@ pub(in crate::circuit) mod gadgets { } /// A canonicity check helper used in checking x(g_d), y(g_d), and y(pk_d). + /// + /// Specifications: + /// - [`g_d` canonicity](https://p.z.cash/orchard-0.1:note-commit-canonicity-g_d?partial) + /// - [`y` canonicity](https://p.z.cash/orchard-0.1:note-commit-canonicity-y?partial) fn canon_bitshift_130( lookup_config: &LookupRangeCheckConfig, mut layouter: impl Layouter, @@ -1806,6 +1835,8 @@ pub(in crate::circuit) mod gadgets { } /// Check canonicity of `x(pk_d)` encoding. + /// + /// [Specification](https://p.z.cash/orchard-0.1:note-commit-canonicity-pk_d?partial). fn pkd_x_canonicity( lookup_config: &LookupRangeCheckConfig, mut layouter: impl Layouter, @@ -1845,6 +1876,8 @@ pub(in crate::circuit) mod gadgets { } /// Check canonicity of `rho` encoding. + /// + /// [Specification](https://p.z.cash/orchard-0.1:note-commit-canonicity-rho?partial). fn rho_canonicity( lookup_config: &LookupRangeCheckConfig, mut layouter: impl Layouter, @@ -1884,6 +1917,8 @@ pub(in crate::circuit) mod gadgets { } /// Check canonicity of `psi` encoding. + /// + /// [Specification](https://p.z.cash/orchard-0.1:note-commit-canonicity-psi?partial). fn psi_canonicity( lookup_config: &LookupRangeCheckConfig, mut layouter: impl Layouter, @@ -1922,6 +1957,10 @@ pub(in crate::circuit) mod gadgets { /// Check canonicity of y-coordinate given its LSB as a value. /// Also, witness the LSB and return the witnessed cell. + /// + /// Specifications: + /// - [`y` decomposition](https://p.z.cash/orchard-0.1:note-commit-decomposition-y?partial) + /// - [`y` canonicity](https://p.z.cash/orchard-0.1:note-commit-canonicity-y?partial) fn y_canonicity( lookup_config: &LookupRangeCheckConfig, y_canon: &YCanonicity,