<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[sergerad.xyz]]></title><description><![CDATA[Cryptography and Engineering]]></description><link>https://www.sergerad.xyz</link><image><url>https://substackcdn.com/image/fetch/$s_!UdxN!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd59a6205-d313-41e3-b0c3-8e9785de1395_700x700.png</url><title>sergerad.xyz</title><link>https://www.sergerad.xyz</link></image><generator>Substack</generator><lastBuildDate>Mon, 20 Apr 2026 01:26:23 GMT</lastBuildDate><atom:link href="https://www.sergerad.xyz/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Serge]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[sergerad@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[sergerad@substack.com]]></itunes:email><itunes:name><![CDATA[Serge]]></itunes:name></itunes:owner><itunes:author><![CDATA[Serge]]></itunes:author><googleplay:owner><![CDATA[sergerad@substack.com]]></googleplay:owner><googleplay:email><![CDATA[sergerad@substack.com]]></googleplay:email><googleplay:author><![CDATA[Serge]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[The Sum-Check Protocol]]></title><description><![CDATA[Articles based on Justin Thaler's Proofs, Arguments, and Zero-Knowledge]]></description><link>https://www.sergerad.xyz/p/the-sum-check-protocol</link><guid isPermaLink="false">https://www.sergerad.xyz/p/the-sum-check-protocol</guid><dc:creator><![CDATA[Serge]]></dc:creator><pubDate>Tue, 08 Oct 2024 19:29:16 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/90475a31-6288-4e22-80e3-aaa04bd9e270_1027x1027.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The sum-check protocol is an interactive proof which can be leveraged in the design of SNARKs. The protocol is said to be information-theoretically secure<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a> because it does not involve cryptographic operations - only field additions and multiplications. However, when applied to SNARK design, the sum-check protocol is combined with cryptographic primitives in order to yield an argument.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a></p><p>The ultimate goal of the sum-check protocol in SNARK design is to reduce a claim made by a prover to a set of operations that can be executed efficiently by a corresponding verifier. In this article, we will learn the inner workings of the sum-check protocol while also touching on its practical applications.</p><h2>Hypothetical Context</h2><p>Before we drill into the mathematical definition of the sum-check protocol, lets try to understand the protocol at a high level in the context of a hypothetical arithmetic circuit, a prover <em><strong>P</strong></em> and a verifier <em><strong>V</strong></em>.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-3" href="#footnote-3" target="_self">3</a></p><p><em><strong>P</strong></em> wants to convince <em><strong>V</strong></em> that some circuit was executed correctly with a specific witness and that they know a multivariate polynomial <em><strong>f</strong></em> which represents this execution. The polynomial <em><strong>f</strong></em> was derived from an algebraic representation of the circuit which contains <em><strong>M</strong></em> constraints and <em><strong>N </strong></em>variables. Somehow, <em><strong>V</strong></em> must be convinced that <em><strong>f</strong></em> is the reduction of <em><strong>M</strong></em> many constraint polynomials that evaluate to <em><strong>0</strong></em> when paired up with their corresponding variables from the execution trace.</p><p>Because we want our interactive proof to be succinct,<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-4" href="#footnote-4" target="_self">4</a> we need <em><strong>V</strong></em>&#8217;s runtime to be much more efficient than the runtime exhibited by <em><strong>P</strong></em>&#8217;s construction of the proof for <em><strong>f</strong></em>. This efficiency can be achieved through the sum-check protocol, which allows <em><strong>P</strong></em>&#8217;s claim about <em><strong>M</strong></em> constraints to be reduced to a single claim about a <em>random</em> linear combination of those constraints. The randomness in this linear combination is provided by <em><strong>V</strong></em> and it allows the protocol to leverage the probabilities involved in the Schwartz-Zippel lemma which we have covered extensively in previous articles.</p><p>If the number of constraints <em><strong>M</strong></em> is equal to <em><strong>2&#7504;</strong></em>, then the sum-check protocol involves <em><strong>m</strong></em> many rounds of interaction between <em><strong>P </strong></em>and <em><strong>V</strong></em>.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-5" href="#footnote-5" target="_self">5</a> <em><strong>V</strong></em> must also compute an evaluation of <em><strong>f</strong></em> at the end of the protocol, meaning that <em><strong>V</strong></em>&#8217;s runtime is <em><strong>O(m+z)</strong></em> where <em><strong>z</strong></em> is the cost to evaluate <em><strong>f</strong></em> at a single input in the relevant field. This is a drastic improvement from <em><strong>O(2&#7504;)</strong></em> which is <em><strong>P</strong></em>&#8217;s runtime when computing the polynomial <em><strong>f</strong></em>.</p><h2>The Protocol</h2><p>Now that we have some high level context for the sum-check protocol in SNARKs, lets define the protocol precisely as an interactive proof.</p><p>As above, the prover <em><strong>P</strong></em> has a <em><strong>v</strong></em>-variate polynomial <em><strong>f</strong></em> defined over a finite field <strong>F</strong><em>p.</em></p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;f(x_1,x_2,...,x_v)&quot;,&quot;id&quot;:&quot;SXPWVXFFFD&quot;}" data-component-name="LatexBlockToDOM"></div><p><em><strong>P</strong></em> claims to know the sum of the evaluations of all Boolean inputs (the Boolean hypercube) over <em><strong>f</strong></em>.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-6" href="#footnote-6" target="_self">6</a> </p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;S = \\sum_{(x_1,...,x_v) \\in\\{0,1\\}^{v}}f(x_1,...,x_v)&quot;,&quot;id&quot;:&quot;OQPSNVSHKM&quot;}" data-component-name="LatexBlockToDOM"></div><p><em><strong>P</strong></em> sends this sum <em><strong>S</strong></em> to <em><strong>V</strong></em>.</p><p>The remainder of the protocol is designed to reach a stage where <em><strong>V</strong></em> can confirm the correctness of <em><strong>S</strong></em>, to a sufficiently high probability of success, through a single evaluation of <em><strong>f</strong></em>. To reach this final stage however, <em><strong>V</strong></em> and <em><strong>P</strong></em> must execute <em><strong>v</strong></em> rounds of communication, each of which involve a single low degree univariate polynomial derived from <em><strong>f</strong></em>.</p><p>The <em><strong>v</strong></em> number of rounds of the protocol proceed as follows. </p><h4>Round 1</h4><p>Firstly, <em><strong>P</strong></em> sends the univariate polynomial <em><strong>g&#8321;(X&#8321;)</strong></em> which is claimed to be the sum of <em><strong>f</strong></em> over a subset of the entire set of variables, namely <em><strong>(x&#8322;,&#8230;,x&#7525;)</strong></em>:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;g_1(X_1) := \\sum_{(x_2,...,x_v) \\in\\{0,1\\}^{v-1}}f(X_1,x_2,...,x_v)&quot;,&quot;id&quot;:&quot;JOESPTPTBL&quot;}" data-component-name="LatexBlockToDOM"></div><p>Lets compare this equation to the one for <em><strong>S</strong></em>. The difference is that the first variable <em><strong>x&#8321;</strong></em> of our <em><strong>v</strong></em>-variate polynomial has been replaced with a fixed variable <em><strong>X&#8321;</strong></em>. The value of <em><strong>X&#8321; </strong></em>in our domain is either <em><strong>0</strong></em> or <em><strong>1</strong></em>, so <em><strong>V</strong></em> is able to verify the following:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;S=g_1(0) + g_1(1)&quot;,&quot;id&quot;:&quot;MZSEHPZILP&quot;}" data-component-name="LatexBlockToDOM"></div><p>At this point, <em><strong>V</strong></em> has been able to compute the claimed sum of <em><strong>f</strong></em> over the Boolean hypercube much more efficiently than <em><strong>P</strong></em> did. However, <em><strong>V</strong></em> is not yet convinced that the claimed sum is in fact the correct sum. The remainder of the protocol will allow <em><strong>V</strong></em> to be convinced of the correctness of <em><strong>S</strong></em>.</p><p><em><strong>V</strong></em> must also check that <em><strong>g&#8321; </strong></em>is a univariate polynomial of degree less than or equal to the degree of <em><strong>x&#8321;</strong></em> in <em><strong>f</strong></em>.</p><p><em><strong>V</strong></em> then selects a random element <em><strong>r&#8321;</strong></em> from <strong>F</strong><em>p </em>and sends it to <em><strong>P</strong></em><strong>.</strong></p><h4>Remaining Rounds</h4><p>The process from round 1 is repeated for all remaining variables <em><strong>(x&#8322;,&#8230;,x&#7525;)</strong></em>.</p><p>For each of the <em><strong>i</strong></em>th rounds, <em><strong>P</strong></em> sends a univariate polynomial <em><strong>g&#7522;(X&#7522;)</strong></em> claimed to equal:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;g_i(X_i) := \\sum_{(x_{i+1},...,x_v) \\in\\{0,1\\}^{v-i}}f(r_1,...,r_{i-1},X_i, x_{i+1},...,x_v)&quot;,&quot;id&quot;:&quot;RPTQHZPDTU&quot;}" data-component-name="LatexBlockToDOM"></div><p><em><strong>V</strong></em> checks that <em><strong>g&#7522; </strong></em>is a univariate polynomial of degree less than or equal to the degree of <em><strong>x&#7522;</strong></em> in <em><strong>f</strong></em>.</p><p><em><strong>V </strong></em>checks that the evaluation of the previous univariate polynomial and corresponding random element is equal to the sum of evaluations of <em><strong>g&#7522; </strong></em>over Boolean inputs for <em><strong>X&#7522;</strong></em>:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;g_{i-1}(r_{i-1}) = g_i(0) + g_i(1)&quot;,&quot;id&quot;:&quot;OFNUFQIPQR&quot;}" data-component-name="LatexBlockToDOM"></div><p><em><strong>V </strong></em>selects another random element <em><strong>r&#7522; </strong></em>from <strong>F</strong><em>p </em>and sends it to <em><strong>P</strong></em><strong>.</strong></p><h4>Final Round</h4><p>During the final round, <em><strong>V </strong></em>will gain access to <em><strong>g&#7525; </strong></em>which is confirmed to be a univariate polynomial of sufficiently low degree equal to:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;g_v(X_v)=f(r_1,...,r_{v-1},X_v)&quot;,&quot;id&quot;:&quot;TGQRYFRDFR&quot;}" data-component-name="LatexBlockToDOM"></div><p><em><strong>V</strong></em> then selects a final random element <em><strong>r&#7525;</strong></em> and confirms the following equality:<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-7" href="#footnote-7" target="_self">7</a></p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;g_v(r_v) = f(r_1,...,r_v)&quot;,&quot;id&quot;:&quot;PDFVPYQQOO&quot;}" data-component-name="LatexBlockToDOM"></div><p>If this check succeeds, then <em><strong>V</strong></em> is convinced as to the correctness of <em><strong>P</strong></em>&#8217;s original statement that the evaluation of <em><strong>f</strong></em> over all Boolean inputs is equal to <em><strong>S</strong></em>.</p><h2>Minimal Worked Example</h2><p>Lets walk through a worked example of the sum-check algorithm to improve our understanding of the protocol.</p><p>We define the multivariate polynomial <em><strong>f </strong></em>as follows:<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-8" href="#footnote-8" target="_self">8</a></p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;f(a, b, c) = a + 2b^2 + 3ac^3&quot;,&quot;id&quot;:&quot;POLSGQHMJJ&quot;}" data-component-name="LatexBlockToDOM"></div><p>The sum of the evaluations of <em><strong>f</strong></em> over the Boolean hypercube is <em><strong>S=18</strong></em>:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\begin{align}\nf(0,0,0)=0\\\\\nf(0,0,1)=0 \\\\\nf(0,1,0)=2 \\\\\nf(0,1,1)=2 \\\\\nf(1,0,0)=1 \\\\\nf(1,0,1)=4 \\\\\nf(1,1,0)=3 \\\\\nf(1,1,1)= 6 \\\\\n\\end{align}&quot;,&quot;id&quot;:&quot;JITJMRXCNL&quot;}" data-component-name="LatexBlockToDOM"></div><p>The first univariate polynomial <em><strong>g&#8321; </strong></em>provided by the prover is as follows:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\n\\begin{align}\ng_1(a) = f(a,0,0) + f(a,0,1) + f(a,1,0) + f(a,1,1)\\\\\n = (a) + (a+3a)+ (a+2) + (a+2+3a) \\\\\n= 4+10a\n\\end{align}&quot;,&quot;id&quot;:&quot;DOWLFZIDMT&quot;}" data-component-name="LatexBlockToDOM"></div><p>The verifier checks that <em><strong>g&#8321;</strong></em> is a univariate polynomial of degree at most 1 and that the sum of its evaluations over Boolean inputs is equal to <em><strong>S=18</strong></em>.</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;S=g_1(0)+g_1(1)= 4 + 10 + 4 = 18&quot;,&quot;id&quot;:&quot;WSRHPDOFQH&quot;}" data-component-name="LatexBlockToDOM"></div><p>The verifier selects a random <em><strong>r&#8321;=3</strong></em>. The prover responds with a second univariate polynomial <em><strong>g&#8322;</strong>:</em></p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\begin{align}\ng_2(b)=f(3,b,0)+f(3,b,1) \\\\\n= (3+2b^2) + (3+2b^2+9) \\\\\n= 15 + 4b^2\n\\end{align}&quot;,&quot;id&quot;:&quot;JVWEOXKDFX&quot;}" data-component-name="LatexBlockToDOM"></div><p>The verifier checks that <em><strong>g&#8322; </strong></em>is a univariate polynomial of degree at most 2 and that the sum of its evaluations over binary inputs is equal to <em><strong>g&#8321;(r&#8321;)</strong></em>:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\begin{align}\ng_1(r_1)=4+30=34 \\\\\ng_2(0)+g_2(1)= 15+15+4 =34 \\\\\n\\end{align} &quot;,&quot;id&quot;:&quot;FQYGTYBXCT&quot;}" data-component-name="LatexBlockToDOM"></div><p>The verifier selects a random <em><strong>r&#8322;=2</strong></em>. The prover responds with a third univariate polynomial <em><strong>g&#8323;</strong>:</em></p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\begin{align}\ng_3(c)=f(3,2,c) \\\\\n= (3+8+9c^3) \\\\\n= 11+9c^3\n\\end{align}&quot;,&quot;id&quot;:&quot;MCRJBCTRFN&quot;}" data-component-name="LatexBlockToDOM"></div><p>The verifier checks that <em><strong>g&#8323; </strong></em>is a univariate polynomial of degree at most 3 and that the sum of its evaluations over binary inputs is equal to <em><strong>g&#8322;(r&#8322;)</strong></em>:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\begin{align}\ng_2(r_2)=15+16=31 \\\\\ng_3(0)+g_3(1)= 11+11+9=31 \\\\\n\\end{align} &quot;,&quot;id&quot;:&quot;PTHTDZCUGO&quot;}" data-component-name="LatexBlockToDOM"></div><p>The verifier selects a random <em><strong>r&#8323;=1</strong></em> and confirms that <em><strong>f(r&#8321;,r&#8322;,r&#8323;)=g&#8323;(r&#8323;)</strong></em>:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\begin{align}\nf(3,2,1)=3+2\\times2^2+3\\times3\\times1^3 = 20\\\\\ng_3(1)= 11+9\\times1^3 =20\\\\\n\\end{align} &quot;,&quot;id&quot;:&quot;ZSNVCHKECJ&quot;}" data-component-name="LatexBlockToDOM"></div><p>After this final check, the verifier is convinced that the original claim by the prover that <em><strong>S=18</strong></em> is in fact correct.</p><h4>Example in Rust</h4><p>The above worked example can be executed from the Rust crate found <a href="https://github.com/sergerad/rustbutverify/blob/main/sumcheck/src/main.rs">here</a>:</p><pre><code>$ cargo run -p sumcheck

Defined:        f   = a + 2b^2 + 3ac^3
Round 1:        g_1 = 4 + 10x   r_1 = 3 
Round 2:        g_2 = 15 + 4x^2 r_2 = 2 
Round 3:        g_3 = 11 + 9x^3 r_3 = 1 
Finalized:      g_3(r_3) = 20</code></pre><p>The crate is factored to be an approachable learning resource. For example, instead of random values, the code uses the <em><strong>r&#8321;</strong></em>, <em><strong>r&#8322;</strong></em>, and <em><strong>r&#8323; </strong></em>from the worked example above. The protocol round functions are also factored to reflect the iterative breakdown covered in this article. So if you are interested in getting a better understanding of the protocol, a look into the code and its explanatory comments should be very helpful.</p><h2>Sum-Check in Snark Design</h2><p>Justin Thaler refers to the sum-check protocol as a &#8220;hammer in the design of efficient interactive proofs&#8221;.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-9" href="#footnote-9" target="_self">9</a> In Chapter 4 of the book, Thaler shows how the sum-check protocol can be used to implement application-specific interactive proofs. This includes interactive proofs for triangle counting, matrix multiplication, and Boolean circuit satisfiability.</p><p>The sum-check protocol is leveraged by modern SNARKs both as a fundamental component to GKR and as a stand-alone building block in its own right.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.sergerad.xyz/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">If you would like to follow along with this series, feel free to subscribe!</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>Information-theoretic secure systems are secure against adversaries with infinite compute and time. This is in contrast to systems whose security relies on the computational cost of cryptanalysis. </p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>Cryptographic primitives such as checksums involved in the Fiat-Shamir transformation and witness commitments.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-3" href="#footnote-anchor-3" class="footnote-number" contenteditable="false" target="_self">3</a><div class="footnote-content"><p>Note that the sum-check protocol is just one component of the proof system in this hypothetical scenario. But we can ignore the other components for now.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-4" href="#footnote-anchor-4" class="footnote-number" contenteditable="false" target="_self">4</a><div class="footnote-content"><p>Remember that SNARKs require verifier time complexity to be logarithmic to the circuit size.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-5" href="#footnote-anchor-5" class="footnote-number" contenteditable="false" target="_self">5</a><div class="footnote-content"><p>A circuit can easily have a billion constraints.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-6" href="#footnote-anchor-6" class="footnote-number" contenteditable="false" target="_self">6</a><div class="footnote-content"><p>In the previous article about multilinear extensions we introduced the concept of the Boolean hypercube.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-7" href="#footnote-anchor-7" class="footnote-number" contenteditable="false" target="_self">7</a><div class="footnote-content"><p>In the formal definition of the sum-check protocol, the verifier requires an oracle query to <em><strong>f</strong></em> for this step. In practice, the verifier would either be able to efficiently evaluate <em><strong>f(r&#8321;,&#8230;,r&#7525;) </strong></em>or would have to ask the prover to perform the evaluation and provide a proof for the claimed evaluation.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-8" href="#footnote-anchor-8" class="footnote-number" contenteditable="false" target="_self">8</a><div class="footnote-content"><p>Note that we have replaced the <em><strong>X&#8321;</strong></em>,<em><strong> X&#8322;</strong></em>, and<em><strong> X&#8323; </strong></em>notation with <em><strong>a</strong></em>, <em><strong>b</strong></em>, and <em><strong>c</strong></em> here simply for readability.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-9" href="#footnote-anchor-9" class="footnote-number" contenteditable="false" target="_self">9</a><div class="footnote-content"><p>Proofs, Arguments, and Zero-Knowledge, Chapter 4, Page 51.</p><div><hr></div><p>Artwork by fullvector from <a href="https://www.freepik.com/author/fullvector">Freepik</a>.</p></div></div>]]></content:encoded></item><item><title><![CDATA[Multilinear Extension]]></title><description><![CDATA[Articles based on Justin Thaler's Proofs, Arguments, and Zero-Knowledge]]></description><link>https://www.sergerad.xyz/p/multilinear-extension</link><guid isPermaLink="false">https://www.sergerad.xyz/p/multilinear-extension</guid><dc:creator><![CDATA[Serge]]></dc:creator><pubDate>Fri, 13 Sep 2024 21:59:04 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/bf25bf59-d515-433b-aeb1-46d823fdce02_4074x4074.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the previous article we took a deep dive into low-degree extension with univariate Lagrange interpolation. We also touched on how low-degree extension compares to multilinear extension (MLE) and pointed out that MLE can produce polynomials whose total degree is logarithmic to the size of the relevant domain.</p><p>In this article, we will unpack how MLE works and how it can be implemented, while also touching on the significance of MLE in the context of  interactive proofs.</p><h2>Multivariate Functions</h2><p>We are now concerned with <em>multivariate</em> functions defined over the <em>v</em>-variate domain <em><strong>{0,1}&#7515; </strong></em>. This domain contains <em><strong>2&#7515;</strong></em> unique Boolean tuples.</p><p>For example,  <em><strong>f ( x<sub>1, </sub>x<sub>2 </sub>) </strong></em>is a bivariate function where <em><strong>v=2</strong></em> and the domain is <em><strong>{0,1}&#178;:</strong></em></p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\{(0,0),(0,1),(1,0),(1,1)\\}&quot;,&quot;id&quot;:&quot;DEFODKKSPA&quot;}" data-component-name="LatexBlockToDOM"></div><p>And<em><strong> f ( x<sub>1, </sub>x<sub>2, &#8230;,  </sub>x<sub>v </sub>) </strong>is a v</em>-variate function whose domain contains <em><strong>2&#7515; </strong></em>unique tuples.</p><p>These functions map Boolean tuples to elements in a finite field:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;f : \\{0,1\\}^v \\rightarrow \\mathbb F&quot;,&quot;id&quot;:&quot;OXXNORCFQR&quot;}" data-component-name="LatexBlockToDOM"></div><p>Importantly, every such function has an extension polynomial that is <em>multilinear</em>.</p><h2>Multilinear Polynomials</h2><p>In a multilinear polynomial, every variable is of degree at most 1. The <em>total degree</em> of a polynomial is the sum of the degrees of the variables in the highest-degree term.</p><p>So, for example, the following MLE of <em><strong>f ( x<sub>1, </sub>x<sub>2 </sub>) </strong></em>over <em><strong>{0,1}&#178;</strong></em> has a total degree of <em><strong>2</strong></em> due to the final term being composed of both <em><strong>x<sub>1 </sub></strong></em>and <em><strong>x<sub>2</sub></strong></em>:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;f(x_1,x_2) = a_0 + a_1x_1 + a_2x_2 + a_3x_1x_2&quot;,&quot;id&quot;:&quot;FFABRRZTKX&quot;}" data-component-name="LatexBlockToDOM"></div><p>Since the highest-degree term of a multilinear polynomial can only involve <em><strong>v</strong></em> Boolean variables, its total degree cannot be greater than <em><strong>v</strong></em>. This means that the total degree of a MLE of a multivariate function is logarithmic to the domain size, <em><strong>2&#7515;</strong></em>.</p><p>Remember that univariate low-degree extension required polynomials of degree <em><strong>n-1</strong></em> which was linear to the domain size, <em><strong>n</strong></em>. As such, MLEs can be leveraged for the purposes of SNARK design in different ways to their univariate counterparts.</p><h2>The Boolean Hypercube</h2><p>Another way of describing our multivariate function is in relation to a Boolean hypercube. Specifically, our function maps a <em>v</em>-dimensional Boolean hypercube to a finite field element.</p><p>A Boolean hypercube is a geometric representation of the set of all Boolean tuples of a given length. The hypercube represents the domain <em><strong>{0,1}&#7515;</strong></em> which consists of all possible Boolean tuples of length <em><strong>v</strong></em>. We can visualize the cube as a graph in which each vertex corresponds to a Boolean tuple and is connected to another tuple that differs by a single bit. The distance between two vertices is the number of bit positions that the two tuples differ at (the Hamming distance).</p><p>The 3-dimensional Boolean hypercube can be visualized as follows:</p><pre><code>             000---------001
            / |          / |
           /  |         /  |    3-Dimensional Boolean Hypercube:
          010--------011   |    {000,001,010,011,100,101,110,111}
          |   |        |   |    
          |   100------|-101
          |  /         |  /
          | /          | /
          110--------111</code></pre><p>Notice that in the domain of <em><strong>{0,1}&#179;</strong></em>, each vertex in the Boolean hypercube has <em><strong>3</strong></em> edges and the maximum Hamming distance is also <em><strong>3</strong></em>.</p><p>When we compute the MLE of some multivariate function, we compute it over the Boolean hypercube that represents the input domain.</p><h2>Extension of the Multivariate Function</h2><p>As mentioned above, every multivariate function, <em><strong>f(x)</strong></em>, that maps the <em>v</em>-dimensional Boolean hypercube to a finite field has a unique corresponding multilinear extension polynomial. The extension polynomial evaluates identically to <em><strong>f(x)</strong></em> for every Boolean tuple from the hypercube. However, the extension polynomial&#8217;s domain is F<em><strong>&#7515; </strong></em>rather than <em><strong>2&#7515; </strong></em>because it has evaluations for every tuple in <em><strong>{0,1,2,&#8230;,</strong></em><strong>|F|</strong><em><strong>-1}&#7515;</strong></em>.As such, <em><strong>g(x)</strong></em> is a distance-amplifying encoding of <em><strong>f(x)</strong></em>.</p><p>As always, distance-amplification allows us to exploit the probabilities involved in the Schwartz-Zippel lemma for the purposes of interactive proofs. With respect to multivariate polynomials over finite field <strong>F</strong>, the Schwartz-Zippel lemma states that any two distinct polynomials of total degree at most <em><strong>d</strong></em> can agree on at most <em><strong>d/</strong></em><strong>|F|</strong> inputs. So if two multivariate functions over the domain <em><strong>{0,1}&#7515;</strong></em> disagree at even a single input, then any extensions of those functions of total degree at most <em><strong>d</strong></em> must differ virtually everywhere, given that <em><strong>d</strong></em> is much smaller than <strong>|F|</strong>.</p><h2>Lagrange Interpolation of Multilinear Polynomials</h2><p>Just as we did for univariate low-degree extension in the previous article, we can use Lagrange interpolation to perform MLE of some multivariate function.</p><p>Recall that basis polynomials are designed to evaluate to 1 for one particular input and 0 for all other possible input. Just as with the univariate approach, the algorithm involves a linear combination of basis polynomials. But this time, we create a basis polynomial for every (Boolean) variable in the multivariate function.</p><p>The MLE of <em><strong>f </strong></em>is the sum of all multilinear Lagrange basis polynomials with interpolating set <em><strong>{0,1}&#7515;</strong></em>, weighted by the evaluation the respective element from the interpolating set:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\tilde{f}(x_1,...,x_v)=\\sum_{w\\in\\{0,1\\}^v} f(w)\\cdot \\chi_w(x_1,...,x_v)&quot;,&quot;id&quot;:&quot;DHYNTTXHFE&quot;}" data-component-name="LatexBlockToDOM"></div><p>For every Boolean tuple <em><strong>w</strong></em> in <em><strong>{0,1}&#7515;</strong></em>, we multiply its evaluation under <em><strong>f</strong></em> with the set of multilinear Lagrange basis polynomials interpolated by the Booelan variables in <em><strong>w</strong></em>.</p><p>To evaluate the set of interpolated basis polynomials, we pair each Boolean tuple <em><strong>w</strong></em> with a tuple <em><strong>x</strong></em>. Because we intend to evaluate <em><strong>x</strong></em> over the MLE of <em><strong>f</strong></em>, the tuple <em><strong>x </strong></em>has a domain of <em><strong>{0,1,&#8230;|F|-1}&#7515;</strong></em>. For each pair, we evaluate an equation that will yield <em><strong>1</strong></em> when the <em><strong>i</strong></em>&#8217;th variables of <em><strong>w</strong></em> and <em><strong>x</strong></em> are equal and <em><strong>0 </strong></em>if they are unequal while <em><strong>x</strong></em> is inside the domain of <em><strong>{0,1}&#7515;</strong></em>. This equation is repeated for every variable in <em><strong>w </strong></em>and <em><strong>x</strong></em> and the results are multiplied together:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\chi_w(x_1,...,x_v) := \\prod^v_{i=1} (x_iw_i + (1-x_i)(1-w_i))&quot;,&quot;id&quot;:&quot;LURYYTYFJD&quot;}" data-component-name="LatexBlockToDOM"></div><p>As you can see, the above algorithm allows us to evaluate some field element under the MLE of <em><strong>f</strong></em> through the use of multilinear Lagrange interpolation. Now lets apply the algorithm to a simple example.</p><p>We define a bivariate function over the domain <em><strong>{0,1}&#178;</strong></em>:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;f(0,0)=a, \\hspace{2mm} f(0,1)=b, \\hspace{2mm} f(1,0)=c,  \\hspace{2mm} (1,1)=d&quot;,&quot;id&quot;:&quot;USGJCNHTVV&quot;}" data-component-name="LatexBlockToDOM"></div><p>The MLE of the bivariate function is then:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\tilde{f}(x_1,x_2) = a\\cdot(1-x_1)(1-x_2) + b\\cdot(1-x_1)x_2 + c\\cdot x_1(1-x_2) + d\\cdot x_1x_2&quot;,&quot;id&quot;:&quot;TZVHVDZZAA&quot;}" data-component-name="LatexBlockToDOM"></div><p>Notice that each of the four terms on the right hand side of the equation ensures that the MLE evaluates equally to our predefined values for the bivariate function:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\begin{align}\nf(0,0) = \\tilde{f}(0,0) = a\\cdot1 + b\\cdot0 + c\\cdot 0 + d\\cdot 0 = a \\\\\nf(0,1) = \\tilde{f}(0,1) = a\\cdot0+ b\\cdot1 + c\\cdot 0 + d\\cdot 0 = b \\\\\nf(1,0) = \\tilde{f}(1,0) = a\\cdot0 + b\\cdot0 + c\\cdot 1 + d\\cdot 0 = c \\\\\nf(1,1) = \\tilde{f}(1,1) = a\\cdot0 + b\\cdot0 + c\\cdot 0 + d\\cdot 1 = d\n\\end{align}\n&quot;,&quot;id&quot;:&quot;EAUPOZBDXP&quot;}" data-component-name="LatexBlockToDOM"></div><p>Now that we have observed the patterns at play, lets replace the variables <em><strong>a</strong></em>,<em><strong> b</strong></em>,<em><strong> c</strong></em>, and <em><strong>d</strong></em> with actual evaluations<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a> of <em><strong>f</strong></em> and see what the corresponding MLE looks like over a finite field <strong>F</strong><em><strong>p</strong></em> where <em><strong>p=5</strong></em>:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;f(0,0)=1, \\hspace{2mm} f(0,1)=2, \\hspace{2mm} f(1,0)=3,  \\hspace{2mm} (1,1)=4&quot;,&quot;id&quot;:&quot;LCHNILFHZB&quot;}" data-component-name="LatexBlockToDOM"></div><p>Given the above evaluations, our MLE equation is:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\tilde{f}(x_1,x_2) = (1-x_1)(1-x_2) + 2(1-x_1)x_2 + 3x_1(1-x_2) + 4 x_1x_2&quot;,&quot;id&quot;:&quot;UETKPHNSKQ&quot;}" data-component-name="LatexBlockToDOM"></div><p>Which we can simplify to:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\tilde{f}(x_1,x_2) = 1 + 2x_1 + x_2&quot;,&quot;id&quot;:&quot;GAPLLTQSJF&quot;}" data-component-name="LatexBlockToDOM"></div><p>So our MLE over <strong>F</strong><em><strong>p</strong></em>, where <em><strong>p=5</strong></em>, looks like this:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;f(x_1,x_2) = \\begin{matrix}\n &amp; \\textit{0}  &amp; \\textit{1}\\\\\n\\textit{0} &amp; 1 &amp; 2\\\\\n\\textit{1} &amp; 3 &amp; 4\n\\end{matrix}\n\\hspace{10mm}\n\\tilde{f}(x_1,x_2) =\n\\begin{matrix}\n  &amp; \\textit{0} &amp; \\textit{1} &amp; \\textit{2} &amp; \\textit{3} &amp; \\textit{4} \\\\\n\\textit{0} &amp; 1 &amp; 2 &amp; 3 &amp; 4 &amp; 0 \\\\\n\\textit{1} &amp; 3 &amp; 4 &amp; 0 &amp; 1 &amp; 2  \\\\\n\\textit{2} &amp; 0 &amp; 1 &amp; 2 &amp; 3 &amp; 4  \\\\\n\\textit{3} &amp; 2 &amp; 3 &amp; 4 &amp; 0 &amp; 1  \\\\\n\\textit{4} &amp; 4 &amp; 0 &amp; 1 &amp; 2 &amp; 3\n\\end{matrix}&quot;,&quot;id&quot;:&quot;BCVUFWORYS&quot;}" data-component-name="LatexBlockToDOM"></div><p>With the above example, we can observe the distance-amplification of MLEs and understand how it was achieved. To further improve our understanding, we can look at how this algorithm can be implemented in Rust. A simple implementation of the algorithm for educational purposes is available <a href="https://github.com/sergerad/rustbutverify/blob/main/mle/src/main.rs">here</a>. Running the MLE package will execute the working example we covered above: </p><pre><code>$ cargo run -p mle

Computed f(w) over the hypercube {0,1}^2:
f[0, 0] -&gt; 1
f[0, 1] -&gt; 2
f[1, 0] -&gt; 3
f[1, 1] -&gt; 4

Computed MLE(x) for every x in Fp:
1 2 3 4 0 
3 4 0 1 2 
0 1 2 3 4 
2 3 4 0 1 
4 0 1 2 3 </code></pre><h2>Multilinear Polynomials in SNARK Design</h2><p>Today, the most common approaches to SNARK design tend to involve some combination of a polynomial interactive oracle proof (IOP), a polynomial commitment scheme, and the Fiat-Shamir transformation.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a> The polynomial commitment scheme transforms the IOP into succinct interactive arguments while the Fiat-Shamir transformation renders the arguments non-interactive and publicly verifiable.</p><p>Some polynomial IOP approaches leverage multivariate polynomials while others rely on univariate ones. Univariate polynomial commitment schemes can be transformed into multivariate ones at the expense of some computation.</p><p>The sum-check protocol is an interactive proof protocol which provides a means of designing efficient polynomial IOPs using multivariate polynomials. In the sum-check protocol, the verifier forces the prover to sum up all the evaluations of a multivariate polynomial over a Boolean hypercube. If you have read this article to completion, this should sound like a very familiar procedure to you. </p><p>In the next article, we will explore the sum-check protocol for interactive proofs.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.sergerad.xyz/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">If you would like to follow along with this series, feel free to subscribe!</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>The evaluations are arbitrary. We could choose any combination of elements from the respective finite field.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>See section 19.1 of Proofs, Arguments, and Zero-Knowledge for a breakdown of the SNARK landscape.</p><div><hr></div><p>Artwork by fullvector from <a href="https://www.freepik.com/author/fullvector">Freepik</a>.</p></div></div>]]></content:encoded></item><item><title><![CDATA[Low-Degree Extension]]></title><description><![CDATA[Articles based on Justin Thaler's Proofs, Arguments, and Zero-Knowledge]]></description><link>https://www.sergerad.xyz/p/low-degree-extension</link><guid isPermaLink="false">https://www.sergerad.xyz/p/low-degree-extension</guid><dc:creator><![CDATA[Serge]]></dc:creator><pubDate>Mon, 26 Aug 2024 07:43:21 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/8e695c6a-97cf-4f5f-aa90-c05148298df3_4279x4279.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the previous articles we performed Reed-Solomon encoding on various input vectors. The resulting encoded vectors were of length <em><strong>p</strong></em> which was a prime number much greater than the length of the original input, <em><strong>n</strong></em>. In this sense, we had created distance-amplified encodings which we used to construct some probabilistic procedures. These probabilistic procedures were more efficient than their deterministic counterparts.</p><p>Previously, we treated the values of the input vectors as <em>coefficients</em> to a univariate polynomial of degree <em><strong>n-1</strong></em>. It turns out that, in the context of interactive proofs and arguments of knowledge, it can actually be more useful to treat those values as <em>evaluations</em> of some polynomial rather than its coefficients.</p><h2>From Coefficients to Evaluations</h2><p>Lets illustrate the differences between treating our input elements as evaluations as opposed to coefficients of a polynomial with a simple worked example. Say we have an input vector <em><strong>a </strong></em>where <em><strong>n=4</strong></em>:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;a=(0,1,2,0)&quot;,&quot;id&quot;:&quot;QCEKQNHBNN&quot;}" data-component-name="LatexBlockToDOM"></div><p>The Reed-Solomon encoding interprets <em><strong>a</strong></em> as the following polynomial<em><strong>:</strong></em></p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;p_a(x)=x+2x^2&quot;,&quot;id&quot;:&quot;LEUQIQBUVZ&quot;}" data-component-name="LatexBlockToDOM"></div><p>Then we can achieve a distance amplified encoding of the original vector by evaluating its corresponding polynomial over a field of prime order <em><strong>p</strong></em>. In the past we mentioned that <em><strong>p</strong></em> should be a prime bigger than <em><strong>n&#178;</strong></em>, so lets say <em><strong>p=67</strong></em>. Then the Reed-Solomon encoding of <em><strong>a</strong></em> in <strong>F</strong><em>p </em>is:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;(0, 3, 10, 21, 36, 55, 11, ..., 1)&quot;,&quot;id&quot;:&quot;THTGKXCJBO&quot;}" data-component-name="LatexBlockToDOM"></div><p>Now, if we instead treat <em><strong>a</strong></em> as evaluations of a univariate polynomial over some canonical input set, our encoded vector must look something like this:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;(0, 1, 2, 0, y_4, y_5,y_6,...,y_{66})\n&quot;,&quot;id&quot;:&quot;PNQGAQJTMC&quot;}" data-component-name="LatexBlockToDOM"></div><p>Notice that the encoded vector is a superset of the input. As such, the encoding can be thought of as an extension of the input vector. Soon we will see that it is specifically a <em>low-degree extension</em> of <em><strong>a</strong></em>, or <em><strong>LDE(a)</strong></em>,<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a> and that low-degree extensions are actually more useful in the context of interactive proofs than our previous Reed-Solomon encodings. But how do we find this polynomial that corresponds to the evaluations found in <em><strong>LDE(a)</strong></em>? We do this through Lagrange interpolation.</p><p>Before we explain Lagrange interpolation, lets restate the context a little more formally. Paraphrasing lemma 2.3 from section 2.4 of Proofs, Arguments, and Zero-Knowledge: let <em><strong>p</strong></em> be a prime larger than <em><strong>n</strong></em> and <strong>F</strong><em>p</em><strong> </strong>be the field of integers modulo <em><strong>p</strong></em>. For any vector <em><strong>a </strong></em>that is a subset of integers less than or equal to <em><strong>n</strong></em> in <strong>F</strong><em>p</em>, there is a unique univariate polynomial of degree at most <em><strong>n-1</strong></em> such that:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;q_a(i) = a_{i+1}\\hspace{2mm} for \\hspace{2mm} i = 0,...,n-1&quot;,&quot;id&quot;:&quot;GOGILLMDVX&quot;}" data-component-name="LatexBlockToDOM"></div><p>This formula relates to the following definition of the input vector <em><strong>a</strong></em>:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;(a_1,...,a_n) \\in \\mathbb F^n&quot;,&quot;id&quot;:&quot;SNZHNOOWKI&quot;}" data-component-name="LatexBlockToDOM"></div><p>All that we are saying here is that there exists a unique univariate polynomial which, given some list of canonical inputs, evaluates to the values found in <em><strong>LDE(a)</strong></em>.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a> We will show that this polynomial can be constructed as a linear combination of Lagrange basis polynomials. But first, we need to understand what a Lagrange basis polynomial is.</p><h2>Univariate Lagrange Interpolation</h2><p>In order to find the polynomial that evaluates the canonical input, <em><strong>(0,1,&#8230;,n-1)</strong></em>, to <em><strong>LDE(a)</strong></em>, we need to perform a linear combination of a set of Lagrange basis polynomials. We need a basis polynomial for every element in the canonical input.</p><p>Conceptually, a Lagrange basis polynomial is designed to map a single element of the canonical input to <em><strong>1</strong></em> and all others to <em><strong>0</strong></em>. Specifically, the <em><strong>i</strong></em>&#8217;th Lagrange basis polynomial maps <em><strong>i</strong></em> to <em><strong>1</strong></em> and all other elements to <em><strong>0</strong></em>. With that understanding, we can try digesting the following formula for Lagrange basis polynomials:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\delta_i(x)=\\prod_{k=0,1,...,n-1\\hspace{1mm}:\\hspace{1mm}k\\neq i} \\frac{x-k}{i-k} &quot;,&quot;id&quot;:&quot;NEGLGAQIRL&quot;}" data-component-name="LatexBlockToDOM"></div><p>At face value it is probably unclear as to how this formula achieves the mapping we described above. For now, just trust that it does; when we go on to do the worked example, we will elaborate on this.</p><p>Next, we define our interpolated polynomial as the linear combination of the basis polynomials:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;q_a(x)=\\sum_{j=0}^{n-1} a_{j+1}\\cdot\\delta_j(x)&quot;,&quot;id&quot;:&quot;ECIDRJWODZ&quot;}" data-component-name="LatexBlockToDOM"></div><p>This simply means that the polynomial which evaluates each canonical input element to an element in <em><strong>LDE(a)</strong></em> is equivalent to the sum of all of our Lagrange basis polynomials weighted by their respective evaluations.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-3" href="#footnote-3" target="_self">3</a></p><p>Now lets do a simple worked example and build an intuition around why this makes sense. If <em><strong>n=4</strong></em>, then our basis polynomials behave as follows.</p><p>The first basis polynomial ensures that the evaluation is <em><strong>1</strong></em> when <em><strong>x=0</strong>. </em>And that the evaluation is <em><strong>0</strong></em> when <em><strong>x</strong></em> is any of the remaining elements of the canonical input domain (<em><strong>1</strong></em>,<em><strong> 2</strong></em>,<em><strong> </strong></em>and<em><strong> 3</strong></em>).</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\delta_0(x) = \\frac{(x-1)\\cdot(x-2)\\cdot(x-3)}{(0-1)\\cdot(0-2)\\cdot(0-3)}&quot;,&quot;id&quot;:&quot;XRFAMAPQNH&quot;}" data-component-name="LatexBlockToDOM"></div><p>The second basis polynomial does the same for <em><strong>i=1</strong></em>.</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\delta_1(x) = \\frac{(x-0)\\cdot(x-2)\\cdot(x-3)}{(1-0)\\cdot(1-2)\\cdot(1-3)}&quot;,&quot;id&quot;:&quot;JKBJXZUBZS&quot;}" data-component-name="LatexBlockToDOM"></div><p>And the third and fourth basis polynomials do the same for <em><strong>i=2</strong></em> and <em><strong>i=3</strong></em>, respectively.</p><p>Lets evaluate the fourth basis polynomial to make sure we understand whats going on here. When <em><strong>x=3</strong></em>:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\delta_3(3) = \\frac{(3-0)\\cdot(3-1)\\cdot(3-2)}{(3-0)\\cdot(3-1)\\cdot(3-2)}&quot;,&quot;id&quot;:&quot;FMDJTZLOMN&quot;}" data-component-name="LatexBlockToDOM"></div><p>Obviously, when the numerator and denominator are equal and non-zero, the result is <em><strong>1</strong></em>. This is the case whenever <em><strong>x</strong></em>=<em><strong>i</strong></em> , due to this part of the basis polynomial formula:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;if\\hspace{2mm}\\frac{x-k}{i-k} =1, \\hspace{2mm} then  \\hspace{2mm}x=i&quot;,&quot;id&quot;:&quot;HIPFNOEPFD&quot;}" data-component-name="LatexBlockToDOM"></div><p>Another way to put this is that the denominator normalizes the linear factor <em><strong>x-k</strong></em>. This normalization ensures that when all these terms are multiplied together, the resulting polynomial will evaluate to 1 when <em><strong>x=i</strong></em>.</p><p>When <em><strong>x</strong></em> is any element from the canonical input domain other than <em><strong>3</strong></em>, say <em><strong>1</strong></em>:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\delta_3(1) = \\frac{(1-0)\\cdot(1-1)\\cdot(1-2)}{(1-0)\\cdot(1-1)\\cdot(1-2)} = \\frac{1\\cdot0\\cdot-1}{1\\cdot0\\cdot-1}&quot;,&quot;id&quot;:&quot;FZRACYJRFY&quot;}" data-component-name="LatexBlockToDOM"></div><p>Then the result must clearly be <em><strong>0</strong></em>. This is because one of the <em><strong>n-1</strong></em> factors in the equation will always evaluate to <em><strong>0</strong></em> when <em><strong>x</strong></em> is not the <em><strong>i</strong></em>&#8217;th element.</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;if\\hspace{2mm}\\frac{x-k}{i-k} =0, \\hspace{2mm} then  \\hspace{2mm}x-k=0= i - k&quot;,&quot;id&quot;:&quot;ABONTBAEEQ&quot;}" data-component-name="LatexBlockToDOM"></div><p>So now we should have an intuition as to why basis polynomials evaluate to <em><strong>1</strong></em> and <em><strong>0 </strong></em>when they need to. Here is a plot of the four basis polynomials:<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-4" href="#footnote-4" target="_self">4</a></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!f4P7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77c63464-51b1-4155-9c9e-21c55e31d250_2233x954.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!f4P7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77c63464-51b1-4155-9c9e-21c55e31d250_2233x954.png 424w, https://substackcdn.com/image/fetch/$s_!f4P7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77c63464-51b1-4155-9c9e-21c55e31d250_2233x954.png 848w, https://substackcdn.com/image/fetch/$s_!f4P7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77c63464-51b1-4155-9c9e-21c55e31d250_2233x954.png 1272w, https://substackcdn.com/image/fetch/$s_!f4P7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77c63464-51b1-4155-9c9e-21c55e31d250_2233x954.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!f4P7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77c63464-51b1-4155-9c9e-21c55e31d250_2233x954.png" width="728" height="311" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/77c63464-51b1-4155-9c9e-21c55e31d250_2233x954.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:622,&quot;width&quot;:1456,&quot;resizeWidth&quot;:728,&quot;bytes&quot;:192284,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!f4P7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77c63464-51b1-4155-9c9e-21c55e31d250_2233x954.png 424w, https://substackcdn.com/image/fetch/$s_!f4P7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77c63464-51b1-4155-9c9e-21c55e31d250_2233x954.png 848w, https://substackcdn.com/image/fetch/$s_!f4P7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77c63464-51b1-4155-9c9e-21c55e31d250_2233x954.png 1272w, https://substackcdn.com/image/fetch/$s_!f4P7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77c63464-51b1-4155-9c9e-21c55e31d250_2233x954.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Notice that each <em><strong>x</strong></em> value that corresponds to an element in our canonical input relates to either a <em><strong>0</strong></em> or <em><strong>1</strong></em> <strong>y</strong> value. The next question you might ask is why a linear combination of the Lagrange basis polynomials results in the polynomial <em><strong>LDE(a)</strong></em>.</p><h2>Linear Combination of Basis Polynomials</h2><p>Recall that the linear combination of the Lagrange basis polynomials is the sum of all the basis polynomials weighted by their respective evaluations. Lets break this out in our worked example from above, which involved the vector <em><strong>a=(0,1,2,0)</strong></em>:<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-5" href="#footnote-5" target="_self">5</a></p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;q_a(x)=0\\cdot \\delta_0(x) + 1\\cdot \\delta_1(x) + 2\\cdot \\delta_2(x) + 0\\cdot\\delta_3(x)&quot;,&quot;id&quot;:&quot;RAKBPSRWNP&quot;}" data-component-name="LatexBlockToDOM"></div><p>This reduces to the following equation:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;2^{-1}\\cdot \\:\\:x\\left(x-2\\right)\\left(x-3\\right)\\:-\\:x\\left(x-1\\right)\\left(x-3\\right)&quot;,&quot;id&quot;:&quot;XVROIDSTXT&quot;}" data-component-name="LatexBlockToDOM"></div><p>And if we plot this equation, it should look like an interpolation of our four basis polynomials:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_QtV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95a7a23e-b643-4bc5-9f03-a127ccaac77f_2233x954.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_QtV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95a7a23e-b643-4bc5-9f03-a127ccaac77f_2233x954.png 424w, https://substackcdn.com/image/fetch/$s_!_QtV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95a7a23e-b643-4bc5-9f03-a127ccaac77f_2233x954.png 848w, https://substackcdn.com/image/fetch/$s_!_QtV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95a7a23e-b643-4bc5-9f03-a127ccaac77f_2233x954.png 1272w, https://substackcdn.com/image/fetch/$s_!_QtV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95a7a23e-b643-4bc5-9f03-a127ccaac77f_2233x954.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_QtV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95a7a23e-b643-4bc5-9f03-a127ccaac77f_2233x954.png" width="1456" height="622" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/95a7a23e-b643-4bc5-9f03-a127ccaac77f_2233x954.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:622,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:114776,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!_QtV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95a7a23e-b643-4bc5-9f03-a127ccaac77f_2233x954.png 424w, https://substackcdn.com/image/fetch/$s_!_QtV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95a7a23e-b643-4bc5-9f03-a127ccaac77f_2233x954.png 848w, https://substackcdn.com/image/fetch/$s_!_QtV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95a7a23e-b643-4bc5-9f03-a127ccaac77f_2233x954.png 1272w, https://substackcdn.com/image/fetch/$s_!_QtV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F95a7a23e-b643-4bc5-9f03-a127ccaac77f_2233x954.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Take a moment to verify that this polynomial satisfies our <em><strong>LDE(a)</strong></em> which looks like this:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;(0, 1, 2, 0, y_4, y_5,y_6,...,y_{66})&quot;,&quot;id&quot;:&quot;HURUXFSVKQ&quot;}" data-component-name="LatexBlockToDOM"></div><p>While we can only verify the first four evaluations this way, we know that the interpolated polynomial is unique. Any two distinct polynomials of degree at most <em><strong>n-1</strong></em> can agree on at most <em><strong>n-1</strong></em> inputs. Since we created our polynomial from <em><strong>n</strong></em> inputs, there cannot be two distinct polynomials of degree at most <em><strong>n-1</strong></em> that satisfy the equation.</p><p>If you are curious as to the full evaluation of <em><strong>LDE(a) </strong></em>or you want to walk through an educational implementation of the algorithm in Rust, you can use <a href="https://github.com/sergerad/rustbutverify/blob/main/lagrange/src/main.rs#L10">this</a>. Running the relevant package will execute the worked example from above:</p><pre><code>$ cargo run -p lagrange
    a = [0, 1, 2, 0]
LDE(a)= [0, 1, 2, 0, 59, 42, 13, 36, 41, ..., 2]</code></pre><p>To conclude, we have used Lagrange interpolation to construct a low-degree extension of our <em><strong>n</strong></em>-sized vector <em><strong>a</strong></em>. The low-degree extension allows us to leverage those same probabilities we used in the previous articles with Reed-Solomon encoding due to the Schwartz-Zippel lemma. However, all of the techniques so far have resulted in polynomials of a maximum degree of <em><strong>n-1</strong></em>. This linear relationship between input size and polynomial degree is something that determines the utility of such techniques in the context of interactive proofs.</p><h2>Multilinear Extension</h2><p>In contrast to univariate low-degree extension which produces polynomials whose degree scales with input size, multilinear extension can produce polynomials whose total degree is logarithmic to the <em>domain</em> size. So, for example, if the domain of our elements is <em><strong>2&#179;&#178;</strong></em>, then the total degree of the multilinear extension is <em><strong>32</strong></em>. Because their degree does not scale with input size, multivariate polynomials of ultra-low degree can be more practical than their univariate counterparts for various use cases.</p><p>We will explore multilinear extension and other techniques involved in interactive proofs in future articles.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.sergerad.xyz/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">If you would like to follow along with this series, feel free to subscribe!</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>In this article we will use <em><strong>LDE(a)</strong></em> to refer to both the interpolated polynomial as well as the vector containing all possible evaluations of that polynomial over the finite field.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>The set of canonical inputs here is <em><strong>(0,1,&#8230;,n-1)</strong></em>.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-3" href="#footnote-anchor-3" class="footnote-number" contenteditable="false" target="_self">3</a><div class="footnote-content"><p>Remember that the evaluations are the elements in vector <em><strong>a</strong></em>.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-4" href="#footnote-anchor-4" class="footnote-number" contenteditable="false" target="_self">4</a><div class="footnote-content"><p>Note that the graph is over the set of real numbers whereas the domain we are interested in is <strong>F</strong><em>p</em><strong>.</strong></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-5" href="#footnote-anchor-5" class="footnote-number" contenteditable="false" target="_self">5</a><div class="footnote-content"><p>Remember that the vector <em><strong>a</strong></em> is also a subset of <em><strong>LDE(a)</strong></em>. Namely, the first <em><strong>n</strong></em> elements.</p><div><hr></div><p>Artwork by fullvector from <a href="https://www.freepik.com/author/fullvector">Freepik</a>.</p></div></div>]]></content:encoded></item><item><title><![CDATA[Freivald's Algorithm]]></title><description><![CDATA[Articles based on Justin Thaler's Proofs, Arguments, and Zero-Knowledge]]></description><link>https://www.sergerad.xyz/p/understanding-zkps-freivalds-algorithm</link><guid isPermaLink="false">https://www.sergerad.xyz/p/understanding-zkps-freivalds-algorithm</guid><dc:creator><![CDATA[Serge]]></dc:creator><pubDate>Fri, 02 Aug 2024 21:36:04 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/e5323c3a-440e-4bc6-9379-7b190e2ea64e_3359x3359.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the <a href="https://open.substack.com/pub/sergerad/p/understanding-zkps-reed-solomon-fingerprinting?r=271n0i&amp;utm_campaign=post&amp;utm_medium=web">previous article</a> we looked at how we can use Reed-Solomon encoding to create a probabilistic protocol that reduces the communication cost of verifying data equality between two independent parties. We did this by treating the subject data as a vector of coefficients to a univariate polynomial.</p><p>It turns out that we can also use Reed-Solomon encoding to reduce the <em>computational</em> cost of certain algorithms. Freivald&#8217;s algorithm is one such algorithm which can be used to verify that the result of a matrix multiplication is correct in less time than the fastest known deterministic algorithm.</p><h2>Freivald&#8217;s Algorithm</h2><p>We have three <em><strong>n &#215; n</strong></em> matrices - <em><strong>A</strong></em>, <em><strong>B</strong></em>, and <em><strong>C</strong></em>. The matrices are over a finite field <strong>F</strong><em>p</em> where <em><strong>p &gt; n&#178;</strong></em>. We want to verify that <em><strong>C</strong></em> = <em><strong>A</strong></em>&#8901;<em><strong>B</strong></em> in a more efficient manner than the fastest known deterministic algorithm which runs in worse than <em><strong>O(n&#178;)</strong></em> time.</p><p>The algorithm starts by selecting a random element, <em><strong>r</strong></em>, from the finite field, <strong>F</strong><em>p</em>. We then produce a vector of field elements, <em><strong>x</strong></em>, which will allow us to treat the rows of the matrices as univariate polynomials<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a>:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;x=(1, r, r^2, ..., r^{n-1})&quot;,&quot;id&quot;:&quot;GENQPKASAV&quot;}" data-component-name="LatexBlockToDOM"></div><p>We then want to evaluate the following which will allow us to conclude that <em><strong>C</strong></em> is (probably) equal to <em><strong>A</strong></em>&#8901;<em><strong>B</strong></em>:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;Cx = A \\cdot Bx&quot;,&quot;id&quot;:&quot;PGGOBKSGED&quot;}" data-component-name="LatexBlockToDOM"></div><p>Why does this perform better than just evaluating <em><strong>A</strong></em>&#8901;<em><strong>B - C = 0</strong></em>? Because multiplying a matrix by a vector yields a vector:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\begin{pmatrix}\n1 &amp; 0 &amp; 0 &amp; 0 \\\\\n0 &amp; 2 &amp; 0 &amp; 0 \\\\\n0 &amp; 0 &amp; 3 &amp; 0 \\\\\n0 &amp; 0 &amp; 0 &amp; 4 \n\\end{pmatrix}\n\\cdot\n\\begin{pmatrix}\n1 \\\\\nr \\\\\nr^2 \\\\\nr^3 \n\\end{pmatrix}\n=\n\\begin{pmatrix}\n1 \\\\\n2r \\\\\n3r^2 \\\\\n4r^3\n\\end{pmatrix}&quot;,&quot;id&quot;:&quot;WIZVQPCRNZ&quot;}" data-component-name="LatexBlockToDOM"></div><p>Observe that the resulting vector looks like a univariate polynomial. Again, if you prefer to think in code, you can use <a href="https://github.com/sergerad/rustbutverify/blob/main/matrix/src/main.rs">this</a> as a playground:<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a></p><pre><code>use ndarray::{arr2, Array1};

fn main() {
    // A and B
    let a = arr2(&amp;[[4, 0, 0], [0, 3, 0], [0, 0, 2]]);
    let b = arr2(&amp;[[1, 0, 0], [0, 2, 0], [0, 0, 3]]);
    // C = A . B
    let c = arr2(&amp;[[4, 0, 0], [0, 6, 0], [0, 0, 6]]);

    // Select a random r and generate x = (r^0, r^1, r^2)
    let n = 3u32;
    let r = 2u32;
    let x: Array1&lt;_&gt; = (0..n).map(|i| r.pow(i)).collect();

    // Check that Cx = A . B x
    let cx = c.dot(&amp;x);
    let bx = b.dot(&amp;x);
    let abx = a.dot(&amp;bx);
    assert_eq!(cx, abx);
}</code></pre><p>The matrix and vector multiplication operation can be thought of as performing Reed-Solomon encoding of every row of the matrix. Whereas in the previous article we reduced a vector into a single field element, in Freivald&#8217;s algorithm, we are reducing some number of matrices into vectors of field elements. Both techniques can be thought of as reducing the problem of comparing large data sets to a comparison of Reed-Solomon fingerprints because both are subject to the same probabilities. This is to say that the Schwartz-Zippel Lemma for the special case of univariate polynomials applies here:</p><blockquote><p>Two <em>unequal</em> polynomials of degree at most <em><strong>n,</strong></em> with coefficients in some finite field <strong>F</strong><em>p</em>, can evaluate to the same field element from the same input for at most <em><strong>n</strong></em> values in <strong>F</strong><em>p</em>.</p></blockquote><p>Applying this lemma to our problem means that if <em><strong>C</strong></em> is not in fact equal to <em><strong>A</strong></em>&#8901;<em><strong>B</strong></em>, then the chance that the polynomials represented by <em><strong>Cx</strong></em> and <em><strong>A</strong></em>&#8901;<em><strong>Bx </strong></em>evaluate to the same field element is <em><strong>(n-1)/p</strong></em>.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-3" href="#footnote-3" target="_self">3</a> So if <em><strong>Cx</strong></em> and <em><strong>A</strong></em>&#8901;<em><strong>Bx </strong></em>end up being equal, we have a <em><strong>1-(n-1)/p</strong></em> probability of correctly concluding that <em><strong>C = A</strong></em>&#8901;<em><strong>B</strong></em>. By ensuring that <em><strong>p </strong></em>is sufficiently large, we can increase our chance of correctness to a sufficient degree.</p><p>As such, we can view Freivald&#8217;s algorithm as a probabilistic proof system where the proof is the matrix <em><strong>C</strong></em> and the verification procedure is the evaluation of <em><strong>Cx = A</strong></em>&#8901;<em><strong>Bx </strong></em>which has <em><strong>O(n&#178;) </strong></em>time complexity.</p><p>To conclude, we have now observed two probabilistic techniques that reduce runtime and communication costs of algorithms by transforming input data into <em>distance-amplified</em> encodings. This distance amplification moves the problem space into a domain of sufficiently high order such that the probabilities involved in the Schwartz-Zippel lemma allow us to efficiently verify equality of two arbitrarily sized values from the input domain.</p><p>So far, we have treated elements from our input vector as coefficients to a polynomial over a finite field:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;(3,1,2) = 3 +x+2x^2\n&quot;,&quot;id&quot;:&quot;YRYRQGCRUO&quot;}" data-component-name="LatexBlockToDOM"></div><p>Which can be Reed-Solomon encoded over a finite field for the purpose of distance amplification:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;(3, 6, 12, 24, 39, ..., 2 + p + 2(p-1)^2)&quot;,&quot;id&quot;:&quot;GCANVPATGQ&quot;}" data-component-name="LatexBlockToDOM"></div><p>In the next article, we will look at an alternate approach which instead treats these elements as evaluations of a univariate polynomial over some canonical set of inputs. We will then explore how Lagrange interpolation can be used to derive this univariate polynomial for the purposes of our proof systems.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.sergerad.xyz/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">If you would like to follow along with this series, feel free to subscribe!</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>The vector <em><strong>x</strong></em> should remind you of the hash function from our Reed-Solomon fingerprint algorithm in the previous article.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>This example involves only small matrices that are not over a finite field for the sake of illustrating the matrix arithmetic involved.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-3" href="#footnote-anchor-3" class="footnote-number" contenteditable="false" target="_self">3</a><div class="footnote-content"><p>Assuming that <em><strong>x</strong></em> was constructed from a random field element <em><strong>r</strong></em>.</p><div><hr></div><p>Artwork by fullvector from <a href="https://www.freepik.com/author/fullvector">Freepik</a>.</p></div></div>]]></content:encoded></item><item><title><![CDATA[Reed-Solomon Fingerprinting]]></title><description><![CDATA[Articles based on Justin Thaler's Proofs, Arguments, and Zero-Knowledge]]></description><link>https://www.sergerad.xyz/p/understanding-zkps-reed-solomon-fingerprinting</link><guid isPermaLink="false">https://www.sergerad.xyz/p/understanding-zkps-reed-solomon-fingerprinting</guid><dc:creator><![CDATA[Serge]]></dc:creator><pubDate>Sun, 28 Jul 2024 17:23:05 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/82119b0e-eff8-43a9-b901-9f9522367255_3600x3600.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Justin Thaler&#8217;s book Proofs, Arguments, and Zero-Knowledge is an excellent monograph on verifiable computing. This article is part of a series that will paraphrase and distill key sections of the book from the perspective of a protocol engineer.</p><p>We begin with chapter 2 as this is where the book itself begins technical analysis of proving systems.</p><h2>Reed-Solomon Fingerprinting</h2><p>Chapter 2 of Thaler&#8217;s book is entitled The Power of Randomness. It explores how a probabilistic procedure can be used to maximize the efficiency of communication required to verify equality of some data held by two independent parties. These parties are, naturally, called Alice and Bob.</p><p>Alice and Bob want to verify that they possess the same ASCII encoded file. The file could be very large. Its size in characters is denoted by <em><strong>n</strong></em>.</p><p>No <em>deterministic</em> procedure can be used to communicate less information than the trivial solution of sending Alice&#8217;s file to Bob. However, a <em>randomized</em> procedure can be used to reduce communication cost exponentially; so long as this procedure has negligible probability of producing an incorrect result.</p><p>One such such procedure involves creating a short &#8220;fingerprint&#8221; of Alice&#8217;s file. This fingerprint approximates a unique identifier to a sufficiently high degree. In order to produce the fingerprint, we use a hash function.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a></p><p>For the purposes of the hash function, we select a prime number, <em><strong>p</strong></em>, greater than <em><strong>n&#178;</strong></em>.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a> This is because we want all arithmetic to be done in a finite field, <strong>F</strong><em>p</em>, which can represent at least as many integers as there are elements in the input&#8217;s domain. Our input is Alice&#8217;s ASCII encoded file, so the input domain has 128 elements:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;(0, 1, 2, ..., 127)&quot;,&quot;id&quot;:&quot;IIDELBMMCL&quot;}" data-component-name="LatexBlockToDOM"></div><p>Whereas the finite field has at least 128&#178; elements:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;(0, 1, 2, ..., 127, ..., p-1)&quot;,&quot;id&quot;:&quot;DZYGAYSOIY&quot;}" data-component-name="LatexBlockToDOM"></div><p>Alice selects a random field element, <em><strong>x</strong></em>, from this finite field:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;x \\in \\mathbb F_p&quot;,&quot;id&quot;:&quot;FIBSCSIPSE&quot;}" data-component-name="LatexBlockToDOM"></div><p>The hash function interprets its input as the coefficients of a <em>univariate</em> polynomial of degree <em><strong>n-1</strong></em>, and outputs the polynomial evaluated at <em><strong>x</strong></em>.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-3" href="#footnote-3" target="_self">3</a> The polynomial represents a Reed-Solomon encoding of the input vector. Ultimately we want to evaluate this polynomial so that we can send the result to Bob:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;h_x(a_1,...,a_n) = \\sum_{i=1}^n a_i \\cdot x^{i-1}&quot;,&quot;id&quot;:&quot;HBQKTBRZOL&quot;}" data-component-name="LatexBlockToDOM"></div><p>If you prefer to think in code, you can take a look at my rough implementation of this <a href="https://github.com/sergerad/rustbutverify/blob/main/polynomial/src/main.rs">here</a>. It involves a fair bit of boilerplate for the <code>FieldElement</code> type and its arithmetic operations so here is a snippet of the most relevant parts:</p><pre><code><code>fn hash(a: &amp;[FieldElement], x: FieldElement) -&gt; FieldElement {
    a.iter()
        .enumerate()
        .fold(FieldElement::new(0, x.modulus), |y, (i, &amp;a)| {
            y + a * x.pow(i as u64)
        })
}

fn main() {
    let n = 128u64;
    let p = n.pow(2);
    let a: Vec&lt;FieldElement&gt; = (1..=n).map(|a| {
        FieldElement::new(a, p)
    }).collect();
    let x = FieldElement::new(511, p);
    let y = hash(&amp;a, x);
    println!("h(a, x={x}, p={p}) = {y}");
}</code></code></pre><p>The communication protocol is that Alice picks a random element <em><strong>x</strong></em> from the finite field, executes the above-defined hash function on the series of ASCII characters and sends Bob the output, <em><strong>y</strong></em>, along with <em><strong>x. </strong></em>Bob executes the same hash function with <em><strong>x</strong></em> and his file and compares his output to that of Alice&#8217;s.</p><p>If Bob&#8217;s <em><strong>y</strong></em> matches Alice&#8217;s, Bob resolves that the results would be equal for every possible choice of <em><strong>x</strong></em>. This is a deterministic corollary based on the nature of the hash function&#8217;s equation we mentioned above - if all coefficients match, it doesn&#8217;t matter which field element you specify for <em><strong>x</strong></em>. However, to understand why Bob can conclude that the files are not equal on the basis of unequal <em><strong>y </strong></em>values, we need to understand the probabilities at play.</p><p>Two <em>unequal</em> polynomials of degree at most <em><strong>n,</strong></em> with coefficients in some finite field <strong>F</strong><em>p</em>, can evaluate to the same field element from the same input for at most <em><strong>n</strong></em> values in <strong>F</strong><em>p</em>.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-4" href="#footnote-4" target="_self">4</a> So the probability that Alice picks an <em><strong>x</strong></em> which evaluates to the same <em><strong>y</strong></em> for two unequal polynomials is <em><strong>(n-1)/p</strong></em>. If <em><strong>p </strong></em>is sufficiently large, then the chance of this happening is negligible.</p><p>We can visualize this low probability with a simple example. Lets take a look at a representation of two small vectors as polynomials over real numbers.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-5" href="#footnote-5" target="_self">5</a> Each vector contains only four elements so their corresponding polynomials are of degree 3 at most.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xx3F!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fb2527a-b566-4389-a39d-ac69253d4919_2381x1679.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xx3F!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fb2527a-b566-4389-a39d-ac69253d4919_2381x1679.png 424w, https://substackcdn.com/image/fetch/$s_!xx3F!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fb2527a-b566-4389-a39d-ac69253d4919_2381x1679.png 848w, https://substackcdn.com/image/fetch/$s_!xx3F!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fb2527a-b566-4389-a39d-ac69253d4919_2381x1679.png 1272w, https://substackcdn.com/image/fetch/$s_!xx3F!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fb2527a-b566-4389-a39d-ac69253d4919_2381x1679.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xx3F!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fb2527a-b566-4389-a39d-ac69253d4919_2381x1679.png" width="1456" height="1027" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9fb2527a-b566-4389-a39d-ac69253d4919_2381x1679.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1027,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:549718,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!xx3F!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fb2527a-b566-4389-a39d-ac69253d4919_2381x1679.png 424w, https://substackcdn.com/image/fetch/$s_!xx3F!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fb2527a-b566-4389-a39d-ac69253d4919_2381x1679.png 848w, https://substackcdn.com/image/fetch/$s_!xx3F!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fb2527a-b566-4389-a39d-ac69253d4919_2381x1679.png 1272w, https://substackcdn.com/image/fetch/$s_!xx3F!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fb2527a-b566-4389-a39d-ac69253d4919_2381x1679.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Observe that the polynomials only share one equal evaluation for <em><strong>x</strong></em>. But there are many more unequal evaluations. The same is true for our protocol where we use polynomials over a finite field. The number of unequal valuations depends on the finite field because it determines the size of our domain. The larger the <em><strong>p </strong></em>compared to the degree of the polynomials, the greater the disparity between equal and unequal evaluations grows. This is why we chose <em><strong>p</strong></em> to be at least <em><strong>n&#178;</strong></em> earlier.</p><p>So when Bob observes a discrepancy between his output and that of Alice&#8217;s, he knows that Alice could not have selected an <em><strong>x</strong></em> which evaluates to the same <em><strong>y</strong></em> across two unequal polynomials. The chance that Bob&#8217;s conclusion is correct is <em><strong>1-(n-1)/p</strong></em>. And because we specified that <em><strong>p&gt;=n&#178; </strong></em>we know that this is <em>at least</em> <em><strong>1-1/n</strong></em>.</p><p>Because the protocol only involves communication of two field elements (<em><strong>x </strong></em>and<em><strong> y</strong></em>), we are able to achieve an exponential improvement in communcation over the deterministic approach.</p><p>To conclude, we saw that there is a relationship between the finite field&#8217;s order, <em><strong>p</strong></em>, and the length of the input, <em><strong>n</strong></em> which affects the probabilities relied on by our protocol. We also saw that the input domain (128 ASCII characters) is treated as a subset of the finite field; meaning the procedure involves an extension of the input domain.</p><p>Now we can start to understand how we can use polynomials over finite fields to provide probabilistic solutions to problems that would otherwise require impractical, deterministic approaches.</p><p>In the next article we will explore the remainder of chapter 2 which constructs more sophisticated probabilistic procedures through Freivald&#8217;s algorithm and Lagrange interpolation.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.sergerad.xyz/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">If you would like to follow along with this series, feel free to subscribe!</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>A cryptographic hash function is not used in this section for the purposes of the communication protocol.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>This assumes that the square of the length of the file is greater than the number of unique characters in ASCII (128).</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-3" href="#footnote-anchor-3" class="footnote-number" contenteditable="false" target="_self">3</a><div class="footnote-content"><p><em><strong>x</strong></em> is the monomial in the univariate polynomial.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-4" href="#footnote-anchor-4" class="footnote-number" contenteditable="false" target="_self">4</a><div class="footnote-content"><p>This is Fact 2.1 from the book. Later, it is revealed that fact 2.1 is a special (univariate) case of the Schwartz-Zippel Lemma.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-5" href="#footnote-anchor-5" class="footnote-number" contenteditable="false" target="_self">5</a><div class="footnote-content"><p>Remember that the protocol uses polynomials over a finite field, so this just for demonstration purposes.</p><div><hr></div><p>Artwork by fullvector from <a href="https://www.freepik.com/author/fullvector">Freepik</a>.</p></div></div>]]></content:encoded></item><item><title><![CDATA[Panic and Recover in the Go Runtime]]></title><description><![CDATA[Digging into the Go Runtime]]></description><link>https://www.sergerad.xyz/p/panic-and-recover-in-the-go-runtime</link><guid isPermaLink="false">https://www.sergerad.xyz/p/panic-and-recover-in-the-go-runtime</guid><dc:creator><![CDATA[Serge]]></dc:creator><pubDate>Wed, 03 Apr 2024 20:57:42 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Bb3F!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e88a979-dc48-4b85-8ca2-da7aa2515ff1_2986x2952.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Bb3F!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e88a979-dc48-4b85-8ca2-da7aa2515ff1_2986x2952.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Bb3F!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e88a979-dc48-4b85-8ca2-da7aa2515ff1_2986x2952.png 424w, https://substackcdn.com/image/fetch/$s_!Bb3F!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e88a979-dc48-4b85-8ca2-da7aa2515ff1_2986x2952.png 848w, https://substackcdn.com/image/fetch/$s_!Bb3F!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e88a979-dc48-4b85-8ca2-da7aa2515ff1_2986x2952.png 1272w, https://substackcdn.com/image/fetch/$s_!Bb3F!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e88a979-dc48-4b85-8ca2-da7aa2515ff1_2986x2952.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Bb3F!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e88a979-dc48-4b85-8ca2-da7aa2515ff1_2986x2952.png" width="414" height="409.1662087912088" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2e88a979-dc48-4b85-8ca2-da7aa2515ff1_2986x2952.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1439,&quot;width&quot;:1456,&quot;resizeWidth&quot;:414,&quot;bytes&quot;:847125,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Bb3F!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e88a979-dc48-4b85-8ca2-da7aa2515ff1_2986x2952.png 424w, https://substackcdn.com/image/fetch/$s_!Bb3F!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e88a979-dc48-4b85-8ca2-da7aa2515ff1_2986x2952.png 848w, https://substackcdn.com/image/fetch/$s_!Bb3F!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e88a979-dc48-4b85-8ca2-da7aa2515ff1_2986x2952.png 1272w, https://substackcdn.com/image/fetch/$s_!Bb3F!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e88a979-dc48-4b85-8ca2-da7aa2515ff1_2986x2952.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Art by <a href="https://github.com/ashleymcnamara/gophers?tab=readme-ov-file">Ashley McNamara</a></figcaption></figure></div><p>Go developers are well aware of how the built-in panic and recover funcs behave. But most of us haven&#8217;t dug into the Go source to understand why they behave the way they do. Why does recover need to be a defer call? Why is a panic only recoverable within the goroutine it occurred? To answer these questions, we need to understand the Go runtime.</p><h3>The Go Runtime</h3><p>The Go runtime is composed of code to be executed (goroutines), OS threads, and runtime resources such as scheduler and memory allocator state. The Go source <a href="https://github.com/golang/go/blob/master/src/runtime/HACKING.md#gs-ms-ps">refers to these</a> in the runtime pkg as <code>G</code>, <code>M</code>, and <code>P</code>, respectively. Each <code>M</code> has a system stack associated with it (referred to as <code>g0</code> in the source). The Go runtime executes on the user stack unless making explicit calls to <a href="https://github.com/golang/go/blob/63deaf00ea6058d1422f0b435e475666cba5743e/src/runtime/stubs.go#L38">mcall</a> or <a href="https://github.com/golang/go/blob/63deaf00ea6058d1422f0b435e475666cba5743e/src/runtime/stubs.go#L58">systemstack</a> funcs. The behaviour of <a href="https://github.com/golang/go/blob/master/src/runtime/HACKING.md#getg-and-getgmcurg">getg</a> can also tell us when the runtime is executing on user or system stack.</p><p>Each <a href="https://github.com/golang/go/blob/63deaf00ea6058d1422f0b435e475666cba5743e/src/runtime/runtime2.go#L422">goroutine</a> has a user stack associated with it and, importantly, all values of the <a href="https://github.com/golang/go/blob/2e1003e2f7e42efc5771812b9ee6ed264803796c/src/runtime/runtime2.go#L1013">panic struct</a> reside on this stack.</p><h3>Panic and Recover in the Go Runtime</h3><p>Panics are initiated via the <a href="https://github.com/golang/go/blob/63deaf00ea6058d1422f0b435e475666cba5743e/src/runtime/panic.go#L709">gopanic</a> func. Panic structs are simply initialized with the argument supplied to the <code>panic()</code> call:</p><pre><code>var p _panic
p.arg = e</code></pre><p>If the panic occurred on a user stack, the panic procedure begins <a href="https://github.com/golang/go/blob/63deaf00ea6058d1422f0b435e475666cba5743e/src/runtime/panic.go#L753">here</a> with reference to the caller's program counter and stack pointer:</p><pre><code><code>p.start(getcallerpc(), unsafe.Pointer(getcallersp()))</code></code></pre><p>The goroutine is assigned a panic reference from the panic struct&#8217;s <code>start()</code> method <a href="https://github.com/golang/go/blob/63deaf00ea6058d1422f0b435e475666cba5743e/src/runtime/panic.go#L813">here</a>:</p><pre><code>gp._panic = (*_panic)(noescape(unsafe.Pointer(p)))</code></pre><p>After this, the user stack is unwound while looking for deferred function calls. If a stack frame with deferred func calls is found, the panic struct is assigned the relevant pointers for the continued unwinding and recovery process. The stack unwinding executes on the system stack <a href="https://github.com/golang/go/blob/63deaf00ea6058d1422f0b435e475666cba5743e/src/runtime/panic.go#L939">here</a>:</p><pre><code><code>func (p *_panic) nextFrame() (ok bool) {
...
gp := getg()
...
&#9;systemstack(func() {
...
&#9;&#9;var u unwinder
&#9;&#9;u.initAt(p.lr, uintptr(p.fp), 0, gp, 0)
...
&#9;&#9;p.lr = u.frame.lr
&#9;&#9;p.sp = unsafe.Pointer(u.frame.sp)
&#9;&#9;p.fp = unsafe.Pointer(u.frame.fp)</code></code></pre><p>The panic process then calls all the deferred funcs from the relevant stack frame <a href="https://github.com/golang/go/blob/63deaf00ea6058d1422f0b435e475666cba5743e/src/runtime/panic.go#L755">here</a>.</p><pre><code><code>for {
&#9;fn, ok := p.nextDefer()
&#9;if !ok {
&#9;&#9;break
&#9;}
&#9;fn()
}</code></code></pre><p>Eventually, the <a href="https://github.com/golang/go/blob/63deaf00ea6058d1422f0b435e475666cba5743e/src/runtime/panic.go#L830">nextDefer</a> func will return the builtin <code>recover()</code> func which is implemented <a href="https://github.com/golang/go/blob/63deaf00ea6058d1422f0b435e475666cba5743e/src/runtime/panic.go#L982">here</a>, as <code>gorecover()</code>. The first thing that <code>gorecover()</code> does is get a reference to the relevant panic:</p><pre><code><code>func gorecover(argp uintptr) any {
...
&#9;gp := getg()
&#9;p := gp._panic</code></code></pre><p>The panic struct contains an argument pointer of the topmost deferred function call (<code>p.argp</code>, assigned in <code>nextDefer()</code> <a href="https://github.com/golang/go/blob/d49a14c6092ba30d0afbdaa06a24016ff1fe2a82/src/runtime/panic.go#L846">here</a>). The <code>gorecover()</code> func checks that its caller has matched this pointer and executes the recover by returning the argument supplied to the panic call (<code>p.arg</code>, which was assigned to the panic in <code>gopanic()</code> <a href="https://github.com/golang/go/blob/63deaf00ea6058d1422f0b435e475666cba5743e/src/runtime/panic.go#L749">here</a>).</p><pre><code><code>if p != nil &amp;&amp; !p.goexit &amp;&amp; !p.recovered &amp;&amp; argp == uintptr(p.argp) {
&#9;p.recovered = true
&#9;return p.arg
}</code></code></pre><p>Once the panic is in a recovered state (<code>p.recovered</code>), it will <a href="https://github.com/golang/go/blob/63deaf00ea6058d1422f0b435e475666cba5743e/src/runtime/panic.go#L839">begin</a> a <a href="https://github.com/golang/go/blob/63deaf00ea6058d1422f0b435e475666cba5743e/src/runtime/panic.go#L1061">recovery</a> process on the system stack whereby the Go runtime ensures that the caller of the recovered func receives results as if the panic had never happened.</p><h3>Summarising the Source</h3><p>The source we covered above is overwhelming on first pass. The key takeaways are the following:</p><ul><li><p>The Go runtime executes goroutines (<code>G</code>) on OS threads (<code>M</code>);</p></li><li><p>Goroutines have their own user stack space;</p></li><li><p>The Go runtime manages panic instances with reference to values on the panicking goroutines&#8217; stack space;</p></li><li><p>The Go runtime uses the system stack to unwind the user stack frames;</p></li><li><p>The Go runtime executes deferred funcs during the stack unwinding process; and</p></li><li><p>The Go runtime initiates a &#8220;recovery&#8221; process if one of the deferred funcs was the built-in <code>recover()</code>.</p></li></ul><p>Now that we have an understanding of what happens under the hood, lets see it in action!</p><h3>Using the Assembler</h3><p>We can look at assembly of Go programs using godbolt.org. Here is a simple program that panics:</p><pre><code><code>package main

func proc() {
    defer func() {
        if r := recover(); r != nil {
            println(r)
        }
    }()
    panic(1)
}

func main() {
    proc()
}</code></code></pre><p>If we look at the <a href="https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(filename:'1',fontScale:14,fontUsePx:'0',j:1,lang:go,selection:(endColumn:19,endLineNumber:6,positionColumn:19,positionLineNumber:6,selectionStartColumn:19,selectionStartLineNumber:6,startColumn:19,startLineNumber:6),source:'//+Type+your+code+here,+or+load+an+example.%0A//+Your+function+name+should+start+with+a+capital+letter.%0Apackage+main%0A%0Afunc+proc()+%7B%0A++++defer+func()+%7B%0A++++++++if+r+:%3D+recover()%3B+r+!!%3D+nil+%7B%0A++++++++++++println(r)%0A++++++++%7D%0A++++%7D()%0A++++panic(1)%0A%7D%0A%0Afunc+main()+%7B%0A++++proc()%0A%7D'),l:'5',n:'0',o:'Go+source+%231',t:'0')),k:50,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:gl1170,filters:(b:'0',binary:'1',binaryObject:'1',commentOnly:'0',debugCalls:'1',demangle:'0',directives:'0',execute:'1',intel:'0',libraryCode:'0',trim:'1'),flagsViewOpen:'1',fontScale:14,fontUsePx:'0',j:1,lang:go,libs:!(),options:'',overrides:!(),selection:(endColumn:31,endLineNumber:102,positionColumn:31,positionLineNumber:102,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:'5',n:'0',o:'+x86-64+gc+1.17+(Editor+%231)',t:'0')),k:50,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4">assembly</a> of this program, we can see the expected runtime calls we covered above.</p><pre><code><code>proc_pc0:
...
        CALL    runtime.gopanic(SB)
...
        CALL    runtime.deferreturn(SB)

proc_func1_pc0:
...
        CALL    runtime.gorecover(SB)</code></code></pre><p>The compiler inserts a call to <a href="https://github.com/golang/go/blob/63deaf00ea6058d1422f0b435e475666cba5743e/src/runtime/panic.go#L581">deferreturn</a> at the end of all funcs make defer calls. This func uses the panic struct for its purposes, but it has a different flow to the one we  covered above.</p><p>The <code>SB</code> (static-base pointer) symbol is a virtual register that points to the beginning of the address space of the program. This symbol can be used to jump to function addresses, which is what the <code>CALL</code> instructions above are doing. If you are interested in understanding the Go assembler in greater detail, go.dev provides <a href="https://go.dev/doc/asm">this</a> primer.</p><h2>Thanks!</h2><p>Thanks for taking your time to read this article. This article was initially intended as an addendum to the <a href="https://sergerad.substack.com/p/panic-recover-and-relax-in-go?r=271n0i">Panic, Recover, and Relax (in Go)</a> article published beforehand. However, while digging into the implementation inside the Go runtime, it became clear that there was a lot to explain!</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.sergerad.xyz/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Serge&#8217;s Substack! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Panic, Recover, and Relax (in Go)]]></title><description><![CDATA[github.com/sergerad/relax]]></description><link>https://www.sergerad.xyz/p/panic-recover-and-relax-in-go</link><guid isPermaLink="false">https://www.sergerad.xyz/p/panic-recover-and-relax-in-go</guid><dc:creator><![CDATA[Serge]]></dc:creator><pubDate>Wed, 03 Apr 2024 20:54:51 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!-lTi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6697406b-641f-493f-8f68-4a67ab47d1df_3000x3000.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-lTi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6697406b-641f-493f-8f68-4a67ab47d1df_3000x3000.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-lTi!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6697406b-641f-493f-8f68-4a67ab47d1df_3000x3000.png 424w, https://substackcdn.com/image/fetch/$s_!-lTi!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6697406b-641f-493f-8f68-4a67ab47d1df_3000x3000.png 848w, https://substackcdn.com/image/fetch/$s_!-lTi!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6697406b-641f-493f-8f68-4a67ab47d1df_3000x3000.png 1272w, https://substackcdn.com/image/fetch/$s_!-lTi!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6697406b-641f-493f-8f68-4a67ab47d1df_3000x3000.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-lTi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6697406b-641f-493f-8f68-4a67ab47d1df_3000x3000.png" width="458" height="458" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6697406b-641f-493f-8f68-4a67ab47d1df_3000x3000.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:1456,&quot;width&quot;:1456,&quot;resizeWidth&quot;:458,&quot;bytes&quot;:1487405,&quot;alt&quot;:&quot;https://github.com/ashleymcnamara/gophers&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="https://github.com/ashleymcnamara/gophers" title="https://github.com/ashleymcnamara/gophers" srcset="https://substackcdn.com/image/fetch/$s_!-lTi!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6697406b-641f-493f-8f68-4a67ab47d1df_3000x3000.png 424w, https://substackcdn.com/image/fetch/$s_!-lTi!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6697406b-641f-493f-8f68-4a67ab47d1df_3000x3000.png 848w, https://substackcdn.com/image/fetch/$s_!-lTi!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6697406b-641f-493f-8f68-4a67ab47d1df_3000x3000.png 1272w, https://substackcdn.com/image/fetch/$s_!-lTi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6697406b-641f-493f-8f68-4a67ab47d1df_3000x3000.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Art by <a href="https://github.com/ashleymcnamara/gophers?tab=readme-ov-file">Ashley McNamara</a></figcaption></figure></div><h2>Panic at the Disc-Go</h2><p>We have all encountered panics when writing Go programs:</p><blockquote><p><strong>Panic</strong> is a built-in function that stops the ordinary flow of control and begins <em>panicking</em>. When the function F calls panic, execution of F stops, any deferred functions in F are executed normally, and then F returns to its caller. To the caller, F then behaves like a call to panic. The process continues up the stack until all functions in the current goroutine have returned, at which point the program crashes. Panics can be initiated by invoking panic directly. They can also be caused by runtime errors, such as out-of-bounds array accesses.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a></p></blockquote><p>Panics are required when there is simply no alternate behaviour for the relevant stack to exhibit. However, crashing our entire application abruptly may be undesirable for our system and our users. For example, web server frameworks will recover from panics caused by user-defined handler stacks so that a concurrent panic only affects a single request. See Gin's default <a href="https://github.com/gin-gonic/gin/blob/b4f66e965ba9d60257e0de4c25d4ad4bd6115927/recovery.go#L32">recovery middleware</a> for example.</p><p>Panics can only be recovered from within the goroutine that the panic occurred. This is because the Go runtime causes the entire process to exit when the panicking goroutine's stack has been completely unrolled.</p><p>So if we import a third party module that runs its own goroutines, there is simply no way we can stop our application from crashing if those goroutines panic. However, most third party modules probably don&#8217;t control concurrency themselves. Arguably they shouldn&#8217;t, and don&#8217;t need to, control concurrency when being reused by other projects; there is always a level of abstraction that can allow for other users to import your code and control concurrency for themselves. Anywho, thats not important right now. But that stance on concurrency is perhaps enough for us to justify further exploration of how we can build Go applications that guarantee panic recovery. So please indulge me on this.</p><p><em>By the way, if you are interested in digging into how panic and recover are implemented in the Go runtime, check out the companion article <a href="https://sergerad.substack.com/p/panic-and-recover-in-the-go-runtime?r=271n0i">here</a>.</em></p><h2>Convincing Go to Relax</h2><p>A single panic doesn&#8217;t need to end an entire application abruptly. We can (if desired) recover and shutdown gracefully, instead. But ensuring that panics are handled in complex projects is no simple feat; we can make Go relax, it just takes some convincing.</p><p>Because panic recovery is limited to goroutine scope, each recoverable goroutine needs to defer a call to <code>recover()</code>.</p><blockquote><p><strong>Recover</strong> is a built-in function that regains control of a panicking goroutine. Recover is only useful inside deferred functions. During normal execution, a call to recover will return nil and have no other effect. If the current goroutine is panicking, a call to recover will capture the value given to panic and resume normal execution.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a></p></blockquote><p>Every time we wanted to turn a panic into an error, we would have to repeat the following snippet for each corresponding goroutine we launch:</p><pre><code><code>defer func() {
    if r := recover(); r != nil {
        // Closure assigns panic to error
        err = fmt.Errorf("recovered from: %v": r)
    }
}()</code></code></pre><p>We need to use an anonymous func here because we require a <strong>closure</strong> if we are to assign the recovered content to an error declared in the surrounding scope. You can imagine that most developers would not bother with this boilerplate. But even if you do bother, there are some pitfalls to be aware of. If the func within which the defer call is made is to actually return the error assigned inside the deferred closure, that func must define <strong>named return variables</strong>:</p><blockquote><p>&#8230; if the deferred function is a <a href="https://go.dev/ref/spec#Function_literals">function literal</a> and the surrounding function has <a href="https://go.dev/ref/spec#Function_types">named result parameters</a> that are in scope within the literal, the deferred function may access and modify the result parameters before they are returned.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-3" href="#footnote-3" target="_self">3</a></p></blockquote><p>These complexities add up, making panic recovery a burden that most developers often wouldn&#8217;t bother with. But we can simplify things using the <a href="https://github.com/sergerad/relax">relax</a> module, which abstracts away these complexities and makes it easy for us to write relaxed Go applications which always shutdown gracefully.</p><p>Instead of using the native <code>go</code> keyword, you can use <code>relax.Go()</code> which handles all the complexities of panic recovery for us:</p><pre><code><code>routine := relax.Go(func() error {
        []int{}[0] = 1 // Panic for example
})</code></code></pre><p>Once you have the <code>relax.Routine</code> which is returned from <code>relax.Go()</code>, you can either wait for it to return an error or release it with a callback that handles the returned error concurrently:</p><pre><code><code>// Block on routine completion
if err := routine.Wait(); err != nil {
&#9;// Handle the error...
}

// Handle error concurrently
routine.Release(func(err error) {
&#9;// Handle the error...
})</code></code></pre><p>The <code>relax</code> module also provides a wrapper around <a href="https://golang.org/x/sync/errgroup">errgroup</a>. Just like <code>relax.Routine</code>, <code>relax.RoutineGroup</code> will let you run goroutines without worrying about panic recovery:</p><pre><code><code>// Instantiate the routine group
group, ctx := relax.NewGroup(relax.Context())

// Launch some goroutines via group.Go()
...

// Wait for routine group to return an error
if err := group.Wait(); err != nil {
&#9;// Handle the error...
}</code></code></pre><p>You may have noticed the <code>relax.Context()</code> above. The <code>relax</code> module also provides a way of shutting down your application gracefully when <code>SIGINT</code> and <code>SIGTERM</code> signals are received. You just have to derive all your application contexts from the one returned by <code>relax.Context()</code>.</p><h2>Recover, Relax, and Shutdown Gracefully</h2><p>Being able to recover from panics doesn&#8217;t necessarily mean it is appropriate for your runtime to panic, recover, and then continue.</p><p>For example, if you have a runtime that executes some logic in an infinite loop, and that logic relies on side effects (such as state recorded in the relevant struct type), introducing a recover-and-continue pattern may just swap one error state for another, due to the effect of a panic on relevant side effects. In this case, you could try factoring your loop to reset state on recovery, but it is probably much simpler to shutdown gracefully after recovering from the panic.</p><p>Not all applications care about shutting down gracefully; it is perfectly fine to crash on panic if there are no negative consequences to doing so.</p><p>So what kind of applications care about shutting down gracefully? Here are a few concrete examples:</p><ul><li><p>API servers that may leave dangling resources in their corresponding databases or abruptly terminate a large number of parallel client connections due to a panic;</p></li><li><p>CLIs whose stdout format is regularly relied upon by, for example, unix command pipelines;</p></li><li><p>Applications that write state to a filesystem which may produce irrecoverable state if a series of dependent file writes is interrupted by a panic; and</p></li><li><p>Tests, such as integration tests using Go's <a href="https://go.dev/doc/build-cover#panicprof">coverage capabilities</a>, where a panic can cause you to lose your prized test results.</p></li></ul><p>When implementing your next application, have a think about the consequences of an abrupt, panic-induced crash. Will it cause UX problems? Will it cause problems for some system upstream? Will it leave dangling resources in your data store? If so, consider taking measures to keep your application relaxed in the case of panics.</p><p>But remember not to overdo it - there are plenty of situations where its simpler and safer to let the panic unroll the stack and cause the application to exit.</p><h2>Thanks!</h2><p>Thank you for taking the time to read this article. It has been an interesting rabbit hole to explore and I hope you enjoyed the read.</p><p>If the relax module gets any contributions and usage, we can work towards a 1.0 release. Please feel free to submit contributions or create issues and discussions.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.sergerad.xyz/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Serge&#8217;s Substack! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p><a href="https://go.dev/blog/defer-panic-and-recover">https://go.dev/blog/defer-panic-and-recover</a></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>Ibid.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-3" href="#footnote-anchor-3" class="footnote-number" contenteditable="false" target="_self">3</a><div class="footnote-content"><p><a href="https://golang.org/ref/spec#Defer_statements">https://golang.org/ref/spec#Defer_statements</a></p><p></p></div></div>]]></content:encoded></item></channel></rss>