living in the intersection of math & magic

Pinned Tweet
There are tons of free ZK resources out there. The hard part is knowing what to learn, and in what order. So I built a curated ZK learning roadmap: Math → Cryptography → SNARKs/STARKs → Prover Engineering → zkVMs github.com/SrishtiCode/ZKRoa…
3
7
41
3,591
Srishti retweeted
Got my first achievement badge from ZK Roadmaps! 🥹 Really happy to see something I built for the ZK community being recognized.
There are tons of free ZK resources out there. The hard part is knowing what to learn, and in what order. So I built a curated ZK learning roadmap: Math → Cryptography → SNARKs/STARKs → Prover Engineering → zkVMs github.com/SrishtiCode/ZKRoa…
1
2
13
231
Q. Why isn't KZG's basic commitment automatically "hiding," and what's the concrete fix? A basic KZG commitment, C = g^(p(s)), is binding but not automatically hiding. Binding means that a prover cannot easily change the committed polynomial, while hiding means that the commitment does not reveal information about the polynomial itself. Although the commitment does not directly expose the coefficients, additional information, such as enough evaluation proofs at different points, could allow someone to reconstruct the polynomial through interpolation. The concrete fix is to add random blinding. In polynomial-commitment schemes, this can involve adding a random multiple of a vanishing polynomial, such as p'(x) = p(x) + r(x)Z(x). Because Z(x) is zero on the intended evaluation domain, this modification does not change the polynomial's values at those points, but it introduces randomness that helps hide the original polynomial. The exact blinding method depends on the KZG variant and the security property required.
3
106
Q. Why is KZG's commitment C = g^(p(s)) computationally binding under the discrete log assumption? KZG commitments are binding because, under the usual discrete logarithm and knowledge-of-exponent assumptions, it is computationally infeasible to find two different polynomials that produce the same commitment. The commitment is C = g^(p(s)), where s is a secret value chosen during setup. If a prover creates two different polynomials, p and p', with the same commitment, then their difference d(x) = p(x) - p'(x) must satisfy d(s) = 0. In other words, the secret s would have to be a root of a nonzero polynomial deliberately constructed by the attacker. Since s is hidden and randomly chosen, the probability of this happening is very small, roughly bounded by the degree of the polynomial divided by the field size. However, one important correction is that this does not directly reduce to solving discrete logarithms. KZG binding is normally justified using a stronger or more specialized assumption called the knowledge of exponent assumption, together with the security of the trusted setup.
3
5
81
Q. Why does PLONK's universal setup still need to be updatable, beyond simply being "universal"? Universal and updatable describe two different properties of a PLONK setup. Universal means that the same setup can support many different circuits up to a certain size, so a new ceremony is not needed for every application. However, if the original ceremony’s secret randomness called toxic waste were compromised, an attacker could potentially create invalid proofs for all circuits using that setup. Updatability reduces this long-term trust risk: new participants can contribute fresh randomness later, re-randomizing the setup without invalidating existing circuits. As long as at least one participant in the entire contribution history honestly generates and destroys their secret, the setup remains secure. Therefore, universality provides reusability, while updatability provides ongoing trust improvement and recovery from reliance on the original ceremony.
5
96
Q. What specific inefficiency does a lookup argument (PLOOKUP-style) fix that arithmetic constraints alone cannot solve cheaply? A lookup argument makes it much cheaper to prove that a value belongs to a specific set of allowed values. Imagine you are building a zkVM and need to prove that a particular value is a valid byte. A byte must be between 0 and 255. If you use only ordinary arithmetic constraints, you have to build extra logic to prove that the value falls within this range. You might break the number into bits and then prove that each bit is either 0 or 1. This requires additional constraints and increases the size of your circuit. Now imagine you have a predefined table containing all 256 possible byte values. With a lookup argument, you can simply say, "The value I am using must appear in this table." The lookup protocol then uses a polynomial-based technique to verify that your claimed values belong to the allowed table, without requiring you to build a large collection of arithmetic constraints for every possible value. The same idea works for operations such as XOR: instead of constructing a complicated arithmetic circuit to calculate XOR, you can create a table of valid input-output combinations and check that your result matches one of the entries. In simple terms, arithmetic constraints prove that a mathematical equation is correct, while lookup arguments efficiently prove that a value is present in a predefined list of valid values. This is especially useful in zkVMs, where operations such as byte manipulation, range checks, and bitwise logic occur frequently.
1
8
214
My followers list is small, but it has some of the coolest people I know. Really proud of that, and genuinely grateful for each of you. ✨️
9
164
I love prime numbers, the green-tao theorem is filled with prime numbers. You should probably go look it up ;)
110
16
188
Q. How does PLONK's "custom gate" mechanism differ structurally from R1CS's fixed A*B=C gate shape? The key difference is that R1CS has a fixed constraint shape, while PLONK allows more flexibility in what each row of the circuit can calculate. In R1CS, each constraint must follow the form (A · z) × (B · z) = (C · z), which means you can multiply two linear expressions and set the result equal to another linear expression. If you want to perform a more complicated operation, you often need extra intermediate wires and additional constraints. In PLONK, each row has three wire values, usually called a, b, and c, and a set of selector values that tell the system how to combine them. For example, the equation q_L·a + q_R·b + q_M·a·b + q_O·c + q_C = 0 can express addition, multiplication, or a combination of these operations, depending on the selector values. Custom gates go further by allowing the circuit designer to define additional algebraic relationships tailored to a specific operation, such as a step in a hash function. This can reduce the number of rows and intermediate wires needed for certain computations. However, custom gates do not make arbitrary computations fit into one row; the operation must still be expressible through the gate's allowed algebraic structure, and additional rows may be needed. In simple terms, R1CS uses a standard building block for multiplication, while PLONK lets you design more specialized building blocks for particular calculations.
1
8
293
Srishti retweeted
We don't have enough empathy for unemployed young people
92
3,777
37,341
421,145
Srishti retweeted
I see all this discussion around Formal Verification focusing so much on bugs, but what a lot of people doesn't seem to realize is that bugs only makes sense with respect to some specification. If you don't have a specification of your software, then it can't even have bugs to begin with!
8
5
29
1,637
Srishti retweeted
Replying to @srishticode
@zksecurityXYZ have great tutorial for plank plonk.zksecurity.xyz/
1
1
29
Srishti retweeted
What a time to be alive!
5
10
1,101
Q. Explain PLONK's permutation argument: what's actually inside the "grand product" polynomial Z(x)? In PLONK, the permutation argument is a way to prove that different wires in a circuit contain the same values whenever they are supposed to be connected. Imagine a circuit where the output of one gate must be connected to the input of another gate. Instead of checking every connection separately, PLONK uses a clever trick called the grand product polynomial. First, PLONK assigns a position to every wire and creates a permutation that tells us which wire positions should contain equal values. Then, it chooses two random numbers, β and γ, and constructs a running product. At each step, it multiplies the current product by a fraction: the numerator uses the wire's value plus β times its position plus γ, while the denominator uses the same value plus β times the position of the wire it is supposed to match. The polynomial Z(x) stores this running product across the circuit's evaluation domain. If the wiring is correct, the products of the numerators and denominators contain the same terms in a different order, so they cancel out and the final product returns to 1. If the wiring is incorrect, the products generally do not match, and the final value is unlikely to be 1 because β and γ were chosen randomly. PLONK can therefore check many wire connections through a single polynomial identity instead of checking every connection individually. In simple terms, Z(x) is like a running scorekeeper: it multiplies fractions that track whether the values assigned to connected wires are consistent, and if everything is correct, the score returns to 1 at the end.
1
2
6
206
Q. Why does Groth16 need two independent random blinding factors, not one, to achieve zero-knowledge? Groth16 uses two separate random values, usually called r and s, to hide information about the secret witness in two different proof elements, A and B. Think of A and B as two separate envelopes containing information about the same secret calculation. If the prover used only one random value to hide both envelopes, the relationship between their contents might reveal information about the witness. Using two independently chosen random values makes it harder for the verifier to learn anything from the individual elements or their relationship. However, simply using two random numbers is not enough on its own; the blinding must be designed according to the Groth16 protocol. The prover combines these random values with the witness-related terms in A, B, and C in a carefully calculated way. The verifier then uses a pairing equation to check the proof, and the extra blinding terms cancel out mathematically, so the proof remains valid. In simple terms, two random values help hide the secret information in two different parts of the proof, while C is constructed to ensure that this extra hiding does not break verification.
1
5
193
Q. What do the three group elements A, B, C in a Groth16 proof actually encode? In Groth16, a proof consists of three group elements called A, B, and C. Together, they provide evidence that the prover knows a valid witness satisfying the circuit's constraints, without revealing that witness to the verifier. Think of these three elements as cryptographic summaries of the computation. A contains information about the witness-weighted polynomials from the QAP, combined with secret setup values and a random blinding factor. B contains similar information, but it is represented in a different elliptic-curve group, allowing the verifier to use a pairing to check the proof. C contains information related to the QAP quotient polynomial, which represents how the computation's polynomial identity is satisfied, along with additional witness-related and blinding terms. The values α and β come from the trusted setup, while the random values used for blinding are freshly chosen for each proof to help preserve zero-knowledge. The verifier then uses a special mathematical operation called a pairing to check a relationship between A, B, C, and the public inputs. If the relationship holds, the verifier accepts the proof. In simple terms, A, B, and C are three cryptographic pieces of evidence that encode information about the hidden computation. The verifier combines them through pairings to check that the computation is valid, without directly seeing the secret witness.
2
92
Q. How does AIR's transition constraint formalism generalize R1CS's flat list for a repetitive computation? Imagine you are building a program that counts from 1 to 100. At every step, the next number must equal the current number plus 1. In R1CS, you would represent this computation using separate constraints for each step. For example, you would write t₁ + 1 = t₂, t₂ + 1 = t₃, and continue until you reach 100. Each step requires its own constraint, even though the same rule is being repeated. AIR (Algebraic Intermediate Representation) handles this differently. Instead of writing the same rule 99 times, you write one transition constraint: next_value = current_value + 1. AIR applies this rule to every pair of consecutive rows in the execution trace. The trace is like a table recording the program's state at each step, and T(x) represents the current row while T(g·x) represents the next row. The constraint polynomial checks whether the relationship between these two rows is correct. If the constraint evaluates to zero for every relevant row, the entire repeated computation satisfies that rule. In simple terms, R1CS writes down each multiplication or relationship separately, while AIR writes a general rule once and applies it across an entire execution trace. This makes AIR particularly suitable for proving repetitive computations, such as loops and large program executions.
3
145
Q. Why does the QAP product A(x)·B(x) have degree roughly 2m (not m), and what does that concretely cost the trusted setup? The reason A(x) × B(x) has a degree of roughly 2m instead of m is that multiplying polynomials adds their degrees. Imagine that your R1CS circuit has 100 constraints. When we use Lagrange interpolation, we create two polynomials, A(x) and B(x), each with a degree less than 100. When we multiply them together, the resulting polynomial can have a degree of up to 198, because the degrees add up. This is similar to multiplying two ordinary expressions: if you multiply x² by x³, you get x⁵, so the degrees are added. In QAP, this multiplication creates a polynomial that can be almost twice as large in degree as the original polynomials. Now, what does this cost in the trusted setup? In Groth16, the setup needs to generate cryptographic powers such as g, gˢ, gˢ², gˢ³, and so on, up to the maximum polynomial degree required by the proving system. If your circuit has 100 constraints and the required degree is approximately 200, the setup needs enough powers to support that degree. For a larger circuit with 10,000 constraints, the required degree may be close to 20,000. This means that the number of powers in the setup grows roughly in proportion to twice the number of constraints, increasing the amount of data that must be generated and processed. In simple terms, QAP turns your circuit into polynomials, but when those polynomials are multiplied, their degree grows, and the trusted setup must be large enough to handle that growth.
1
3
198
Q. Explain how QAP's Lagrange interpolation converts R1CS's matrix columns into polynomials. Imagine you have an R1CS system with several constraints and several variables. Each matrix, A, B, and C, contains numbers that describe how each variable participates in each constraint. QAP uses Lagrange interpolation to turn these matrix columns into polynomials. Think of each column as a list of numbers, one number for each constraint. For example, if a column contains the values [2, 4, 6], we can create a polynomial that passes through three corresponding points, such as (1, 2), (ω, 4), and (ω², 6). Lagrange interpolation gives us the unique polynomial of degree less than the number of constraints that passes through all those points. We do this for every column, creating one polynomial for each variable. Then, we multiply each of these polynomials by the corresponding value in the witness vector z and add them together. This produces one combined polynomial, A(x), representing the entire A matrix evaluated using the witness. We do the same for B(x) and C(x). Now, instead of checking every R1CS constraint separately, we calculate A(x) × B(x) − C(x). If the witness is valid, this polynomial will evaluate to zero at every point in our chosen evaluation domain, meaning every original R1CS constraint is satisfied. A polynomial that is zero at all these points must be divisible by a special polynomial called the vanishing polynomial, Z(x), which is constructed to be zero exactly at those points. In simple terms, QAP transforms many individual R1CS equations into one polynomial equation, making it possible to use polynomial-based cryptographic techniques to create a proof.
1
2
127