{"id":23317,"date":"2026-08-07T00:00:00","date_gmt":"2026-08-07T00:00:00","guid":{"rendered":"https:\/\/scannn.com\/a-quick-look-at-zero-knowledge-proofs\/"},"modified":"2026-08-07T00:00:00","modified_gmt":"2026-08-07T00:00:00","slug":"a-quick-look-at-zero-knowledge-proofs","status":"publish","type":"post","link":"https:\/\/scannn.com\/lv\/a-quick-look-at-zero-knowledge-proofs\/","title":{"rendered":"A quick look at zero-knowledge proofs"},"content":{"rendered":"\n<div wp_automatic_readability=\"262.15986641498\">\n<p><em>NB: This isn\u2019t about crypto. I don\u2019t care about crypto.<\/em><\/p>\n<p><a href=\"https:\/\/www.chrisgregory.me\/\">Chris<\/a> messaged me the other week asking<br \/>\nif I wanted to implement zero-knowledge proofs. I initially was not interested,<br \/>\nbut then he said:<\/p>\n<blockquote wp_automatic_readability=\"6\">\n<p>What if I told you there\u2019s a version of them that has nothing to do with<br \/>\ncryptocurrencies? What if I told you it involves graph theory? What if I told<br \/>\nyou there\u2019s a 30 line implementation?<\/p>\n<\/blockquote>\n<p>Now <em>that<\/em> was interesting.<\/p>\n<p>The idea of a zero-knowledge proof (ZKP) is that there are two parties: the<br \/>\nprover and the verifier. The prover asserts that it has a solution to a<br \/>\n(generally NP-complete) problem. The prover can convince the verifier of this<br \/>\nwithout sharing the actual solution to the problem.<\/p>\n<p>The canonical example is 3-coloring a graph. That is, the prover asserts that,<br \/>\nfor a given (shared) graph, it has a valid 3-coloring. It wants to convince the<br \/>\nverifier of this without revealing the actual color assignment.<\/p>\n<blockquote wp_automatic_readability=\"9\">\n<p>As a quick recap, graph coloring is the problem where given a graph,<br \/>\nwe find a way to assign each node a color such that no two adjacent nodes<br \/>\nhave the same color. 3-coloring is coloring with at most 3 colors.<\/p>\n<\/blockquote>\n<p><!--\ngraph {\n    0 -- 1;\n    1 -- 2;\n    2 -- 3;\n    3 -- 4;\n    4 -- 0;\n    0 -- 2;\n}\n--><\/p>\n<p><!--\ngraph {\n    0 [style=filled; color=navy; fontcolor=white];\n    1 [style=filled; color=darkgreen; fontcolor=white];\n    2 [style=filled; color=crimson; fontcolor=white];\n    3 [style=filled; color=navy; fontcolor=white];\n    4 [style=filled; color=darkgreen; fontcolor=white];\n    0 -- 1;\n    1 -- 2;\n    2 -- 3;\n    3 -- 4;\n    4 -- 0;\n    0 -- 2;\n}\n--><\/p>\n<p>How do you do this? Assorted blog posts and fancy-looking demonstrations were<br \/>\ninteresting but did not help us understand much.<\/p>\n<p>Chris and I went around in circles for a bit until we decided to take a look at<br \/>\n<a href=\"https:\/\/dl.acm.org\/doi\/pdf\/10.1145\/116825.116852\">one of the original papers<\/a><br \/>\n(PDF) by Goldreich, Micali, and Widgerson. We only really read page 23 (labeled<br \/>\npage 713 in the PDF) but that was enough to get things going.<\/p>\n<h2 id=\"the-papers-protocol\">The paper\u2019s protocol<\/h2>\n<p>Protocol 4 from the paper describes an interactive 3-color proof session<br \/>\nbetween the prover (P, with numbered steps) and the verifier (V, with numbered<br \/>\nsteps), reproduced here:<\/p>\n<blockquote wp_automatic_readability=\"40\">\n<p><em>common input<\/em> A graph <code class=\"language-plaintext highlighter-rouge\">G(V, E)<\/code> (<code class=\"language-plaintext highlighter-rouge\">n = |V|<\/code>, <code class=\"language-plaintext highlighter-rouge\">m = |E|<\/code>).<\/p>\n<p>The following four steps are executed <code class=\"language-plaintext highlighter-rouge\">m\u00b2<\/code> times, each time using independent<br \/>\ncoin tosses.<\/p>\n<p>(P1) The prover chooses at random an assignment of three colors to the<br \/>\nthree independent sets induced by <code class=\"language-plaintext highlighter-rouge\">\u03c6<\/code>, colors the graph using this 3-coloring,<br \/>\nand places these colors in <code class=\"language-plaintext highlighter-rouge\">n<\/code> locked boxes each bearing the number of the<br \/>\ncorresponding vertex. More specifically, the prover chooses a permutation<br \/>\n<code class=\"language-plaintext highlighter-rouge\">\u03c0 \u2208R S\u2083<\/code>, places <code class=\"language-plaintext highlighter-rouge\">\u03c0(\u03c6(i))<\/code> in a box marked <code class=\"language-plaintext highlighter-rouge\">i<\/code> (\u2200 <code class=\"language-plaintext highlighter-rouge\">i \u2208 V<\/code>), locks all boxes<br \/>\nand sends them (without the keys) to the verifier.<\/p>\n<p>(V1) The verifier chooses at random an edge <code class=\"language-plaintext highlighter-rouge\">e \u2208R E<\/code> and sends it to the<br \/>\nprover. (Intuitively, the verifier asks to examine the colors of the endpoints<br \/>\nof <code class=\"language-plaintext highlighter-rouge\">e \u2208 E<\/code>.)<\/p>\n<p>(P2) If <code class=\"language-plaintext highlighter-rouge\">e = (u, v) \u2208 E<\/code>, then the prover reveals the colors of <code class=\"language-plaintext highlighter-rouge\">u<\/code> and<br \/>\n<code class=\"language-plaintext highlighter-rouge\">v<\/code> by sending the verifier the keys to boxes <code class=\"language-plaintext highlighter-rouge\">u<\/code> and <code class=\"language-plaintext highlighter-rouge\">v<\/code>. Otherwise, the<br \/>\nprover does nothing.<\/p>\n<p>(V2) The verifier opens boxes <code class=\"language-plaintext highlighter-rouge\">u<\/code> and <code class=\"language-plaintext highlighter-rouge\">v<\/code> using the keys received and<br \/>\nchecks whether they contain two different elements of <code class=\"language-plaintext highlighter-rouge\">{1, 2, 3}<\/code>. If the keys<br \/>\ndo not match the boxes, or the contents violate the condition then the<br \/>\nverifier <em>rejects<\/em> and stops. Otherwise, the verifier continues to the next<br \/>\niteration.<\/p>\n<p>If the verifier has completed all <code class=\"language-plaintext highlighter-rouge\">m\u00b2<\/code> iterations then it <em>accepts<\/em>.<\/p>\n<\/blockquote>\n<p>We\u2019ll come back to the number of iterations. For now let\u2019s try to just do <em>one<\/em><br \/>\niteration. For each step, I\u2019ll annotate the code with \u201cOnly prover\u201d or \u201cOnly<br \/>\nverifier\u201d so that it\u2019s clear who can see what data.<\/p>\n<h2 id=\"one-iteration\">One iteration<\/h2>\n<p>We\u2019ll start by sketching out what it means to have a graph. For the example<br \/>\ngraphviz graph above, we have the following edge list data structure:<\/p>\n<div class=\"language-python highlighter-rouge\" wp_automatic_readability=\"12\">\n<div class=\"highlight\" wp_automatic_readability=\"19\">\n<pre class=\"highlight\"><code><span class=\"c1\"># Shared between prover, verifier\n<\/span><span class=\"n\">edges<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[(<\/span><span class=\"mi\">0<\/span><span class=\"p\">,<\/span> <span class=\"mi\">1<\/span><span class=\"p\">),<\/span> <span class=\"p\">(<\/span><span class=\"mi\">1<\/span><span class=\"p\">,<\/span> <span class=\"mi\">2<\/span><span class=\"p\">),<\/span> <span class=\"p\">(<\/span><span class=\"mi\">2<\/span><span class=\"p\">,<\/span> <span class=\"mi\">3<\/span><span class=\"p\">),<\/span> <span class=\"p\">(<\/span><span class=\"mi\">3<\/span><span class=\"p\">,<\/span> <span class=\"mi\">4<\/span><span class=\"p\">),<\/span> <span class=\"p\">(<\/span><span class=\"mi\">4<\/span><span class=\"p\">,<\/span> <span class=\"mi\">0<\/span><span class=\"p\">),<\/span> <span class=\"p\">(<\/span><span class=\"mi\">0<\/span><span class=\"p\">,<\/span> <span class=\"mi\">2<\/span><span class=\"p\">)]<\/span>\n<\/code><\/pre>\n<\/div>\n<\/div>\n<p>Each tuple in the list represents a connection between two numbered nodes.<br \/>\nFancy stuff. Because it\u2019s an undirected graph, <code class=\"language-plaintext highlighter-rouge\">(0, 1)<\/code> means the same as <code class=\"language-plaintext highlighter-rouge\">(1,<br \/>\n0)<\/code> so we don\u2019t have to include both. We can also color it:<\/p>\n<div class=\"language-python highlighter-rouge\" wp_automatic_readability=\"8\">\n<div class=\"highlight\" wp_automatic_readability=\"11\">\n<pre class=\"highlight\"><code><span class=\"c1\"># Only prover\n<\/span><span class=\"n\">coloring<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span><span class=\"mi\">0<\/span><span class=\"p\">:<\/span> <span class=\"s\">\"navy\"<\/span><span class=\"p\">,<\/span> <span class=\"mi\">1<\/span><span class=\"p\">:<\/span> <span class=\"s\">\"darkgreen\"<\/span><span class=\"p\">,<\/span> <span class=\"mi\">2<\/span><span class=\"p\">:<\/span> <span class=\"s\">\"crimson\"<\/span><span class=\"p\">,<\/span> <span class=\"mi\">3<\/span><span class=\"p\">:<\/span> <span class=\"s\">\"navy\"<\/span><span class=\"p\">,<\/span> <span class=\"mi\">4<\/span><span class=\"p\">:<\/span> <span class=\"s\">\"darkgreen\"<\/span><span class=\"p\">}<\/span>\n<\/code><\/pre>\n<\/div>\n<\/div>\n<p>Each key is a node number and each value is a color.<\/p>\n<p>Though finding a 3-coloring of a graph is slow, verifying one is fast\u2014linear<br \/>\nin the number of edges. Let\u2019s verify that we have a valid sample coloring:<\/p>\n<div class=\"language-python highlighter-rouge\" wp_automatic_readability=\"7.5\">\n<div class=\"highlight\" wp_automatic_readability=\"10\">\n<pre class=\"highlight\"><code><span class=\"c1\"># For the reader\n# Check each edge to make sure no edge has the same color on each node\n<\/span><span class=\"k\">assert<\/span> <span class=\"nb\">all<\/span><span class=\"p\">(<\/span><span class=\"n\">coloring<\/span><span class=\"p\">[<\/span><span class=\"n\">u<\/span><span class=\"p\">]<\/span> <span class=\"o\">!=<\/span> <span class=\"n\">coloring<\/span><span class=\"p\">[<\/span><span class=\"n\">v<\/span><span class=\"p\">]<\/span> <span class=\"k\">for<\/span> <span class=\"n\">u<\/span><span class=\"p\">,<\/span> <span class=\"n\">v<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">edges<\/span><span class=\"p\">)<\/span>\n<span class=\"c1\"># Check that the total number of colors used is 3\n<\/span><span class=\"k\">assert<\/span> <span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"nb\">set<\/span><span class=\"p\">(<\/span><span class=\"n\">coloring<\/span><span class=\"p\">.<\/span><span class=\"n\">values<\/span><span class=\"p\">()))<\/span> <span class=\"o\">&lt;=<\/span> <span class=\"mi\">3<\/span>\n<\/code><\/pre>\n<\/div>\n<\/div>\n<p>We\u2019ll now go through the paper\u2019s steps one by one, writing some code to<br \/>\naccompany each step.<\/p>\n<h3 id=\"tips-and-tricks\">Tips and tricks<\/h3>\n<p>If you are building alongside the blog post, I recommend using <code class=\"language-plaintext highlighter-rouge\">random.seed(0)<\/code><br \/>\nso your randomness doesn\u2019t change between runs of your program. I also<br \/>\nrecommend setting the environment variable <code class=\"language-plaintext highlighter-rouge\">PYTHONHASHSEED<\/code> to <code class=\"language-plaintext highlighter-rouge\">0<\/code> if you are<br \/>\nusing <code class=\"language-plaintext highlighter-rouge\">hash<\/code> for the same stability reasons.<\/p>\n<h3 id=\"step-p1\">Step P1<\/h3>\n<p>The first thing we need to do is permute the coloring we have. That is, we<br \/>\nshould swap around the color values while maintaining the 3-color property.<\/p>\n<p>Thankfully, this is easier than it might sound: the names of colors are<br \/>\nmeaningless to the 3-coloring; they need only be different along the edges. So<br \/>\nif we do a bijective (A maps to one B, and B came from one A) mapping from old<br \/>\nto new name, this will hold.<\/p>\n<p>I came up with this function that shuffles the colors, lines them up<br \/>\nside-by-side, makes a table, and then uses that to make a new coloring:<\/p>\n<div class=\"language-python highlighter-rouge\" wp_automatic_readability=\"12\">\n<div class=\"highlight\" wp_automatic_readability=\"19\">\n<pre class=\"highlight\"><code><span class=\"kn\">import<\/span> <span class=\"nn\">random<\/span>\n\n<span class=\"c1\"># Only prover\n<\/span><span class=\"k\">def<\/span> <span class=\"nf\">permute_three_coloring<\/span><span class=\"p\">(<\/span><span class=\"n\">coloring<\/span><span class=\"p\">):<\/span>\n    <span class=\"n\">all_colors<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">list<\/span><span class=\"p\">(<\/span><span class=\"nb\">set<\/span><span class=\"p\">(<\/span><span class=\"n\">coloring<\/span><span class=\"p\">.<\/span><span class=\"n\">values<\/span><span class=\"p\">()))<\/span>\n    <span class=\"n\">new_colors<\/span> <span class=\"o\">=<\/span> <span class=\"n\">random<\/span><span class=\"p\">.<\/span><span class=\"n\">sample<\/span><span class=\"p\">(<\/span><span class=\"n\">all_colors<\/span><span class=\"p\">,<\/span> <span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">all_colors<\/span><span class=\"p\">))<\/span>\n    <span class=\"n\">permutation<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span><span class=\"n\">old<\/span><span class=\"p\">:<\/span> <span class=\"n\">new<\/span> <span class=\"k\">for<\/span> <span class=\"n\">old<\/span><span class=\"p\">,<\/span> <span class=\"n\">new<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">zip<\/span><span class=\"p\">(<\/span><span class=\"n\">all_colors<\/span><span class=\"p\">,<\/span> <span class=\"n\">new_colors<\/span><span class=\"p\">)}<\/span>\n    <span class=\"k\">return<\/span> <span class=\"p\">{<\/span><span class=\"n\">node<\/span><span class=\"p\">:<\/span> <span class=\"n\">permutation<\/span><span class=\"p\">[<\/span><span class=\"n\">color<\/span><span class=\"p\">]<\/span> <span class=\"k\">for<\/span> <span class=\"n\">node<\/span><span class=\"p\">,<\/span> <span class=\"n\">color<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">coloring<\/span><span class=\"p\">.<\/span><span class=\"n\">items<\/span><span class=\"p\">()}<\/span>\n<span class=\"c1\"># For example,\n# {0: \"crimson\", 1: \"navy\", 2: \"darkgreen\", 3: \"crimson\", 4: \"navy\"}\n<\/span><\/code><\/pre>\n<\/div>\n<\/div>\n<p>Then we have to place the colors in \u201clocked boxes\u201d. One way to proverbially<br \/>\nlock a box is to apply a one-way function to it: for example, a hash function.<br \/>\nIf we hash each color and then pass only the hashes to the verifier, the<br \/>\nverifier cannot open them.<\/p>\n<p>This example uses the Python standard library hash function for brevity but it<br \/>\nmight be better to use a cryptographic hash function like <code class=\"language-plaintext highlighter-rouge\">hashlib.sha256<\/code>:<\/p>\n<div class=\"language-python highlighter-rouge\" wp_automatic_readability=\"9.5\">\n<div class=\"highlight\" wp_automatic_readability=\"14\">\n<pre class=\"highlight\"><code><span class=\"c1\"># Only prover. Wrong!\n<\/span><span class=\"k\">def<\/span> <span class=\"nf\">hash_coloring_wrong<\/span><span class=\"p\">(<\/span><span class=\"n\">coloring<\/span><span class=\"p\">):<\/span>\n    <span class=\"k\">return<\/span> <span class=\"p\">{<\/span><span class=\"n\">node<\/span><span class=\"p\">:<\/span> <span class=\"nb\">hash<\/span><span class=\"p\">(<\/span><span class=\"n\">color<\/span><span class=\"p\">)<\/span> <span class=\"k\">for<\/span> <span class=\"n\">node<\/span><span class=\"p\">,<\/span> <span class=\"n\">color<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">coloring<\/span><span class=\"p\">.<\/span><span class=\"n\">items<\/span><span class=\"p\">()}<\/span>\n<span class=\"c1\"># For example:\n# {0: -6789624683659967261, 1: 7846608853949633950, 2: 6009240650600289446,\n#  3: -6789624683659967261, 4: 7846608853949633950}\n<\/span><\/code><\/pre>\n<\/div>\n<\/div>\n<p>There\u2019s just one problem with handing these locked boxes to the verifier: two<br \/>\nboxes locked with the same colors will have the same hashes. The verifier would<br \/>\nknow the coloring. Even if the exact colors are now hidden, it\u2019s really the<br \/>\nstructure of the coloring for which we want to give up zero knowledge.<\/p>\n<p>To get around this, we can add what\u2019s called a <em>nonce<\/em> to each node and its<br \/>\ncoloring. That is, each node gets a little bit of random data packed into the<br \/>\nhash so that different nodes\u2019 <code class=\"language-plaintext highlighter-rouge\">\"darkgreen\"<\/code> hash values look different.<\/p>\n<div class=\"language-python highlighter-rouge\" wp_automatic_readability=\"17.5\">\n<div class=\"highlight\" wp_automatic_readability=\"30\">\n<pre class=\"highlight\"><code><span class=\"c1\"># Only prover\n<\/span><span class=\"k\">def<\/span> <span class=\"nf\">nonce<\/span><span class=\"p\">():<\/span>\n    <span class=\"k\">return<\/span> <span class=\"n\">random<\/span><span class=\"p\">.<\/span><span class=\"n\">randrange<\/span><span class=\"p\">(<\/span><span class=\"mi\">100<\/span><span class=\"p\">)<\/span>\n\n<span class=\"k\">def<\/span> <span class=\"nf\">box_coloring<\/span><span class=\"p\">(<\/span><span class=\"n\">coloring<\/span><span class=\"p\">):<\/span>\n    <span class=\"k\">return<\/span> <span class=\"p\">{<\/span><span class=\"n\">node<\/span><span class=\"p\">:<\/span> <span class=\"p\">(<\/span><span class=\"n\">color<\/span><span class=\"p\">,<\/span> <span class=\"n\">nonce<\/span><span class=\"p\">())<\/span> <span class=\"k\">for<\/span> <span class=\"p\">(<\/span><span class=\"n\">node<\/span><span class=\"p\">,<\/span> <span class=\"n\">color<\/span><span class=\"p\">)<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">coloring<\/span><span class=\"p\">.<\/span><span class=\"n\">items<\/span><span class=\"p\">()}<\/span>\n\n<span class=\"k\">def<\/span> <span class=\"nf\">hash_values<\/span><span class=\"p\">(<\/span><span class=\"n\">coloring<\/span><span class=\"p\">):<\/span>\n    <span class=\"k\">return<\/span> <span class=\"p\">{<\/span><span class=\"n\">k<\/span><span class=\"p\">:<\/span> <span class=\"nb\">hash<\/span><span class=\"p\">(<\/span><span class=\"n\">v<\/span><span class=\"p\">)<\/span> <span class=\"k\">for<\/span> <span class=\"p\">(<\/span><span class=\"n\">k<\/span><span class=\"p\">,<\/span> <span class=\"n\">v<\/span><span class=\"p\">)<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">coloring<\/span><span class=\"p\">.<\/span><span class=\"n\">items<\/span><span class=\"p\">()}<\/span>\n\n<span class=\"n\">permuted_coloring<\/span> <span class=\"o\">=<\/span> <span class=\"n\">permute_three_coloring<\/span><span class=\"p\">(<\/span><span class=\"n\">coloring<\/span><span class=\"p\">)<\/span>\n<span class=\"c1\"># For example:\n# {0: \"crimson\", 1: \"navy\", 2: \"darkgreen\", 3: \"crimson\", 4: \"navy\"}\n<\/span><span class=\"n\">boxed_coloring<\/span> <span class=\"o\">=<\/span> <span class=\"n\">box_coloring<\/span><span class=\"p\">(<\/span><span class=\"n\">permuted_coloring<\/span><span class=\"p\">)<\/span>\n<span class=\"c1\"># For example:\n# {0: (\"crimson\", 33), 1: (\"navy\", 65), 2: (\"darkgreen\", 62),\n#  3: (\"crimson\", 51), 4: (\"navy\", 38)}\n<\/span><span class=\"n\">hashed_coloring<\/span> <span class=\"o\">=<\/span> <span class=\"n\">hash_values<\/span><span class=\"p\">(<\/span><span class=\"n\">boxed_coloring<\/span><span class=\"p\">)<\/span>\n<span class=\"c1\"># For example:\n# {0: -2275004828450249492, 1: 2227921633151400991, 2: -5024343381376265886,\n#  3: -5381005702768533635, 4: 1164729608819214729}\n<\/span><\/code><\/pre>\n<\/div>\n<\/div>\n<p>Again, you probably don\u2019t want to use the standard library random number<br \/>\ngenerator for your nonces. You should consider something like<br \/>\n<code class=\"language-plaintext highlighter-rouge\">secrets.token_hex()<\/code> from the <code class=\"language-plaintext highlighter-rouge\">secrets<\/code> module (Python 3.6+). Maybe even<br \/>\nconsider using the <code class=\"language-plaintext highlighter-rouge\">hmac<\/code> module.<\/p>\n<p>Finally, we can send the <code class=\"language-plaintext highlighter-rouge\">hashed_coloring<\/code> to the verifier and begin step V1.<\/p>\n<h3 id=\"step-v1\">Step V1<\/h3>\n<p>Since the verifier knows the graph (but not its colors), it can pick an<br \/>\narbitrary edge to inspect. It wants to verify that the arbitrary edge it picked<br \/>\nsatisfies the 3-color conditions. It sends off a request for an arbitrary edge<br \/>\n<code class=\"language-plaintext highlighter-rouge\">e<\/code>:<\/p>\n<div class=\"language-python highlighter-rouge\" wp_automatic_readability=\"6\">\n<div class=\"highlight\" wp_automatic_readability=\"7\">\n<pre class=\"highlight\"><code><span class=\"c1\"># Only verifier\n<\/span><span class=\"n\">e<\/span> <span class=\"o\">=<\/span> <span class=\"n\">random<\/span><span class=\"p\">.<\/span><span class=\"n\">choice<\/span><span class=\"p\">(<\/span><span class=\"n\">edges<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">revealed<\/span> <span class=\"o\">=<\/span> <span class=\"n\">prover_please_reveal_colors<\/span><span class=\"p\">(<\/span><span class=\"n\">e<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre>\n<\/div>\n<\/div>\n<p>This is implicitly relying on some global state (the prover knowing what<br \/>\n\u201csession\u201d is active with the verifier). If you have multiple verifiers or<br \/>\nconcurrent sessions or something, you may need to thread through some context<br \/>\nidentifier in the communication.<\/p>\n<h3 id=\"step-p2\">Step P2<\/h3>\n<p>The prover, having received this request, sends over the colors and nonces for<br \/>\neach of the nodes in the edge.<\/p>\n<div class=\"language-python highlighter-rouge\" wp_automatic_readability=\"9\">\n<div class=\"highlight\" wp_automatic_readability=\"13\">\n<pre class=\"highlight\"><code><span class=\"c1\"># Only prover\n<\/span><span class=\"k\">def<\/span> <span class=\"nf\">prover_please_reveal_colors<\/span><span class=\"p\">(<\/span><span class=\"n\">edge<\/span><span class=\"p\">):<\/span>\n    <span class=\"n\">u<\/span><span class=\"p\">,<\/span> <span class=\"n\">v<\/span> <span class=\"o\">=<\/span> <span class=\"n\">edge<\/span>\n    <span class=\"k\">return<\/span> <span class=\"p\">{<\/span><span class=\"n\">u<\/span><span class=\"p\">:<\/span> <span class=\"n\">boxed_coloring<\/span><span class=\"p\">[<\/span><span class=\"n\">u<\/span><span class=\"p\">],<\/span> <span class=\"n\">v<\/span><span class=\"p\">:<\/span> <span class=\"n\">boxed_coloring<\/span><span class=\"p\">[<\/span><span class=\"n\">v<\/span><span class=\"p\">]}<\/span>\n<span class=\"c1\"># For example:\n# {3: ('crimson', 51), 4: ('navy', 38)}\n<\/span><\/code><\/pre>\n<\/div>\n<\/div>\n<p>You may be suspicious at this point because we\u2019re leaking some information<br \/>\nabout the coloring.<\/p>\n<p>Note that it\u2019s ok for the prover to reveal the color for one edge, because 1)<br \/>\nthe colors have been shuffled once per round and 2) we\u2019re going to apply our<br \/>\nbox locking protocol <em>each time we reveal an edge<\/em> (also once per round), so<br \/>\nthe verifier accumulates no information about our colors between iterations.<\/p>\n<h3 id=\"step-v2\">Step V2<\/h3>\n<p>The verifier can check that the color+nonce hashes to the hash value given for<br \/>\neach node in step P1. This ensures that the prover is not changing colors<br \/>\naround mid-round. This relies on the verifier and the prover using the same<br \/>\nhash function (and the same hash seed if using <code class=\"language-plaintext highlighter-rouge\">hash<\/code>).<\/p>\n<div class=\"language-python highlighter-rouge\" wp_automatic_readability=\"8.5\">\n<div class=\"highlight\" wp_automatic_readability=\"12\">\n<pre class=\"highlight\"><code><span class=\"c1\"># Only verifier\n<\/span><span class=\"k\">for<\/span> <span class=\"p\">(<\/span><span class=\"n\">node<\/span><span class=\"p\">,<\/span> <span class=\"p\">(<\/span><span class=\"n\">color<\/span><span class=\"p\">,<\/span> <span class=\"n\">nonce<\/span><span class=\"p\">))<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">revealed<\/span><span class=\"p\">.<\/span><span class=\"n\">items<\/span><span class=\"p\">():<\/span>\n    <span class=\"k\">assert<\/span> <span class=\"n\">hashed_coloring<\/span><span class=\"p\">[<\/span><span class=\"n\">node<\/span><span class=\"p\">]<\/span> <span class=\"o\">==<\/span> <span class=\"nb\">hash<\/span><span class=\"p\">((<\/span><span class=\"n\">color<\/span><span class=\"p\">,<\/span> <span class=\"n\">nonce<\/span><span class=\"p\">)),<\/span> <span class=\"sa\">f<\/span><span class=\"s\">\"Hash mismatch!\"<\/span>\n<\/code><\/pre>\n<\/div>\n<\/div>\n<p>The verifier can then inspect that the two color values are different. This<br \/>\ngives a small amount of credence (<code class=\"language-plaintext highlighter-rouge\">1\/|E|<\/code> because you know something about one<br \/>\nedge now) that the graph is 3-colored appropriately because the prover had no<br \/>\nway of knowing which edge the verifier would want to inspect.<\/p>\n<p>If either of these two conditions doesn\u2019t check out, the verifier <em>rejects<\/em>.<\/p>\n<h2 id=\"probabilities\">Probabilities<\/h2>\n<p>You have to do at least a couple of rounds of this for the verifier to believe<br \/>\nthe prover about the 3-coloring.<\/p>\n<p>The paper goes on to assert that \u201cthe probability that the verifier will<br \/>\naccept (i.e., complete all the <code class=\"language-plaintext highlighter-rouge\">m\u00b2<\/code> rounds without detecting that \u201csomething is<br \/>\nwrong\u201d) is bounded above by <code class=\"language-plaintext highlighter-rouge\">(1 - m\u207b\u00b9)^(m\u00b2)<\/code>\u201d (where <code class=\"language-plaintext highlighter-rouge\">m = |E|<\/code>). Which is<br \/>\npretty good. For a large graph (say, 1000 edges), this falls off reasonably<br \/>\nquickly:<\/p>\n<div class=\"language-python highlighter-rouge\" wp_automatic_readability=\"7\">\n<div class=\"highlight\" wp_automatic_readability=\"9\">\n<pre class=\"highlight\"><code><span class=\"n\">m<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">1000<\/span>\n<span class=\"k\">for<\/span> <span class=\"n\">i<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">range<\/span><span class=\"p\">(<\/span><span class=\"mi\">1<\/span><span class=\"p\">,<\/span> <span class=\"mi\">4600<\/span><span class=\"p\">):<\/span>\n    <span class=\"k\">print<\/span><span class=\"p\">(<\/span><span class=\"s\">\"(1 - m\u207b\u00b9)^round = \"<\/span><span class=\"p\">,<\/span> <span class=\"p\">(<\/span><span class=\"mi\">1<\/span> <span class=\"o\">-<\/span> <span class=\"n\">m<\/span><span class=\"o\">**-<\/span><span class=\"mi\">1<\/span><span class=\"p\">)<\/span><span class=\"o\">**<\/span><span class=\"n\">i<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre>\n<\/div>\n<\/div>\n<p>At 4600 rounds, you\u2019re at 1% possibility of \u201ccheating\u201d. At 10,000 rounds,<br \/>\nyou\u2019re at 0.0045% possibility of \u201ccheating\u201d. At <code class=\"language-plaintext highlighter-rouge\">m\u00b2 = 1,000,000<\/code> rounds, it\u2019s<br \/>\n<em>very low<\/em>.<\/p>\n<h2 id=\"a-networked-demo\">A networked demo<\/h2>\n<p>Writing Python code that runs in a single process with comments denoting<br \/>\n\u201cprover\u201d and \u201cverifier\u201d is not very satisfying. It does not preclude accidental<br \/>\ndata leaking in the slightest. It would be much more satisfying if there was<br \/>\nsome sort of barrier, like a process barrier or a network barrier, between the<br \/>\nprover and the verifier.<\/p>\n<p>For this reason, Chris and I have prepared a server (prover) and client<br \/>\n(verifier) demo. You can click \u201cRun Round\u201d to run a round (and show the<br \/>\npermuted colors). You can visit <a href=\"https:\/\/zkp-3color-demo.fly.dev\/docs\">the docs<br \/>\npage<\/a> to see the API docs and build a<br \/>\nclient yourself!<\/p>\n<div id=\"output\" wp_automatic_readability=\"10\">\n    <button id=\"round\">Run Round<\/button><br \/>\n    <noscript wp_automatic_readability=\"2\"><\/p>\n<p>JavaScript is required to run this demo.<\/p>\n<p><\/noscript><\/p>\n<p><i>(If you don&#8217;t see a graph right below this message, please wait a<br \/>\n    moment for the server to wake up.)<\/i><\/p>\n<ol reversed=\"\" style=\"max-height: 100px; overflow-y: auto;\"\/>\n<\/div>\n<h2 id=\"encoding-other-np-complete-problems\">Encoding other NP-complete problems<\/h2>\n<p>At this point, we\u2019ve shown how you can use a zero-knowledge interactive proof<br \/>\nto verify that someone has a valid 3-coloring of a graph without learning <em>any<\/em><br \/>\ninformation about the 3-coloring. So what? Is there anything else we can prove<br \/>\nwith zero knowledge? Is the interactive proof of 3-coloring just a contrived<br \/>\nparty trick without real applications?<\/p>\n<p>Well\u2026<\/p>\n<h3 id=\"sudoku\">Sudoku<\/h3>\n<p>Sudoku puzzles are another example of a problem that\u2019s hard to solve and easy<br \/>\nto verify. It is algorithmically hard to fill in 81 squares to satisfy the<br \/>\nconstraints of all rows, columns, and boxes containing the digits 1-9, but the<br \/>\nverifier is extremely quick.<\/p>\n<p>Let\u2019s say we wanted to prove we\u2019ve finished a Sudoku, but we don\u2019t want to give<br \/>\nup a morsel of information about the solution. We can execute an interactive<br \/>\nproof very analogous to 3-coloring! Instead of shuffling colors, we shuffle<br \/>\ndigits. Instead of revealing edges, we reveal rows, columns, and boxes.<\/p>\n<p>Next time your fellow bus rider leans over asking to see your Sudoku solution,<br \/>\njust ask them to go through 90,000 easy steps first!<\/p>\n<h3 id=\"reduction\">Reduction<\/h3>\n<p>Let\u2019s say we have a hard problem and we\u2019ve computed a solution it, but we don\u2019t<br \/>\nhave an obvious algorithm on hand to execute an interactive proof for it.<br \/>\nThanks to the authors of the paper above, we know that if the problem is<br \/>\nNP-complete there\u2019s an interactive proof for it!<\/p>\n<p>We use the power of a polynomial time reduction. We (somehow) convert our<br \/>\nsolution to a graph and its 3-coloring, then just follow the steps and code<br \/>\nabove! The \u201csomehow\u201d is the tricky part, but much research exists on converting<br \/>\nbetween different NP-complete problems.<\/p>\n<p>For example, you may want to create a zero-knowledge proof that you know the<br \/>\nprime factors for a very large composite number. Unfortunately, for only double<br \/>\ndigit numbers your graph is thousands of nodes, so reducing to 3-coloring has<br \/>\nits limits in practice and you\u2019re better off with more sophisticated proof<br \/>\ntechniques than a reduction to 3-coloring.<\/p>\n<h2 id=\"wrapping-up\">Wrapping up<\/h2>\n<p>After doing a bit of research, we decided that the most common real world use<br \/>\ncases of zero-knowledge proofs (age verification, crypto, etc) aren\u2019t<br \/>\nparticularly interesting to us. We enjoyed the graphs and theory of computation<br \/>\nand networked computing though. We hope you had fun playing around with<br \/>\ninteractive proofs too.<\/p>\n<\/p><\/div>\n<p><a href=\"https:\/\/bernsteinbear.com\/blog\/zkp\/?utm_source=tldrnewsletter\">Source link <\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>NB: This isn\u2019t about crypto. I don\u2019t care about crypto. Chris messaged me the other week asking if I wanted to implement zero-knowledge proofs. I initially was not interested, but then he said: What if I told you there\u2019s a version of them that has nothing to do with cryptocurrencies? What if I told you [&hellip;]<\/p>\n","protected":false},"author":16,"featured_media":23318,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[143],"tags":[],"class_list":["post-23317","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ai"],"_links":{"self":[{"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/posts\/23317","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/users\/16"}],"replies":[{"embeddable":true,"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/comments?post=23317"}],"version-history":[{"count":0,"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/posts\/23317\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/media\/23318"}],"wp:attachment":[{"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/media?parent=23317"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/categories?post=23317"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/scannn.com\/lv\/wp-json\/wp\/v2\/tags?post=23317"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}