diff --git a/tutorial/Stark101-part5.ipynb b/tutorial/Stark101-part5.ipynb new file mode 100644 index 0000000..0c1ffe5 --- /dev/null +++ b/tutorial/Stark101-part5.ipynb @@ -0,0 +1,381 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Part 5: Verifier\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Load the Previous Session\n", + "Run the next cell to load the variables we'll use in this part. Since it repeats everything done in previous parts - it will take a while to run." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Generating the trace...\n", + "13.96262812614441s\n", + "Generating the composition polynomial and the FRI layers...\n", + "20.6886248588562s\n", + "Generating queries and decommitments...\n", + "0.7515149116516113s\n", + "Overall time: 35.40322494506836s\n", + "Uncompressed proof length in characters: 46153\n" + ] + } + ], + "source": [ + "import time\n", + "from tutorial_sessions import part1, part3 \n", + "\n", + "def decommit_on_fri_layers(idx, channel):\n", + " for layer, merkle in zip(fri_layers[:-1], fri_merkles[:-1]):\n", + " length = len(layer)\n", + " idx = idx % length\n", + " sib_idx = (idx + length // 2) % length \n", + " channel.send(str(layer[idx]))\n", + " channel.send(str(merkle.get_authentication_path(idx)))\n", + " channel.send(str(layer[sib_idx]))\n", + " channel.send(str(merkle.get_authentication_path(sib_idx)))\n", + " channel.send(str(fri_layers[-1][0]))\n", + "\n", + "def decommit_on_query(idx, channel): \n", + " assert idx + 16 < len(f_eval), f'query index: {idx} is out of range. Length of layer: {len(f_eval)}.'\n", + " channel.send(str(f_eval[idx])) # f(x).\n", + " channel.send(str(f_merkle.get_authentication_path(idx))) # auth path for f(x).\n", + " channel.send(str(f_eval[idx + 8])) # f(gx).\n", + " channel.send(str(f_merkle.get_authentication_path(idx + 8))) # auth path for f(gx).\n", + " channel.send(str(f_eval[idx + 16])) # f(g^2x).\n", + " channel.send(str(f_merkle.get_authentication_path(idx + 16))) # auth path for f(g^2x).\n", + " decommit_on_fri_layers(idx, channel) \n", + "\n", + "def decommit_fri(channel):\n", + " for query in range(3):\n", + " # Get a random index from the verifier and send the corresponding decommitment.\n", + " decommit_on_query(channel.receive_random_int(0, 8192-16-1), channel)\n", + "\n", + "start = time.time()\n", + "start_all = start\n", + "print(\"Generating the trace...\")\n", + "_, _, _, _, _, _, _, f_eval, f_merkle, _ = part1()\n", + "print(f'{time.time() - start}s')\n", + "start = time.time()\n", + "print(\"Generating the composition polynomial and the FRI layers...\")\n", + "fri_polys, fri_domains, fri_layers, fri_merkles, channel = part3()\n", + "print(f'{time.time() - start}s')\n", + "start = time.time()\n", + "print(\"Generating queries and decommitments...\")\n", + "decommit_fri(channel)\n", + "print(f'{time.time() - start}s')\n", + "start = time.time()\n", + "# print(channel.proof)\n", + "print(f'Overall time: {time.time() - start_all}s')\n", + "print(f'Uncompressed proof length in characters: {len(str(channel.proof))}')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Verify\n", + "verify the random numbers in the channel.\n", + "TODO: reduce proof size by removing the random numbers from the proof and recomputing them as in here" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success!\n" + ] + } + ], + "source": [ + "from channel import Channel\n", + "\n", + "verifier_channel = Channel()\n", + "for i, c in enumerate(channel.proof):\n", + " if c.startswith('send:'):\n", + " verifier_channel.send(c[len('send:'):])\n", + "# print(f'{i} send')\n", + " elif c.startswith('receive_random_field_element:'):\n", + " verifier_channel.receive_random_field_element()\n", + " assert verifier_channel.proof[-1] == c,f\"r={r} c={c}\"\n", + "# print(f'{i} receive_random_field_element')\n", + " elif c.startswith('receive_random_int:'):\n", + " verifier_channel.receive_random_int(0, 8192-16-1)\n", + " assert verifier_channel.proof[-1] == c,f\"r={r} c={c}\"\n", + "# print(f'{i} receive_random_int')\n", + " else:\n", + " raise f\"unknown channel prefix {c}\"\n", + " \n", + "print('Success!')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "utilites to read information from the channel's proof" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "channel_idx = 0\n", + "\n", + "def get_s(s='send:'):\n", + " global channel_idx\n", + " assert channel_idx < len(channel.proof)\n", + " assert channel.proof[channel_idx].startswith(s)\n", + " v = channel.proof[channel_idx][len(s):]\n", + " channel_idx += 1\n", + " return v\n", + "\n", + "def get_f():\n", + " v = get_s('receive_random_field_element:')\n", + " return FieldElement(int(v))\n", + "\n", + "def get_i():\n", + " v = get_s('receive_random_int:')\n", + " return int(v)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### channel information from part 1" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "mt_root = get_s() # of the 8K points on the trace poly" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### channel information from part 2" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "from field import FieldElement\n", + "\n", + "alpha0 = get_f()\n", + "alpha1 = get_f()\n", + "alpha2 = get_f()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "cp_mt_root = get_s() # of the 8K points on the composite poly" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### channel information from part 3" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Below the function `cp` returns the value of the composite polynomial at `x`. Note that this is the only place in the verifier where we enter the constrains (algorithm, parameters and result) of what we want to prove\n", + "* `x` is value on the evaluation domain of the polynomials (8K points)\n", + "* `f=f0` is the value of the interpolated polynimal `p` of the trace at the point `x`\n", + "* `f1` is the value of the interpolated polynimal at `g * x` this point has an index which is +8 the index of `x`\n", + "* `f2` is the value of the interpolated polynimal at `g * g * x` this point has an index which is +16 the index of `x`" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "g = FieldElement.generator() ** (3 * 2 ** 20)\n", + "\n", + "def p0(x,f):\n", + " return (f-1)/(x-1)\n", + "\n", + "def p1(x,f):\n", + " return (f-2338775057)/(x-g**1022)\n", + "\n", + "def p2(x,f0,f1,f2):\n", + " return (f2 - f1*f1 - f0*f0)/((1 - x**1024) / ((x - g**1021) * (x - g**1022) * (x - g**1023)))\n", + "\n", + "def cp(x,f0,f1,f2):\n", + " return alpha0*p0(x,f0) + alpha1*p1(x,f0) + alpha2*p2(x,f0,f1,f2)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "alphas = []\n", + "fri_merkles_root = [cp_mt_root]\n", + "while channel.proof[channel_idx].startswith('receive_random_field_element:') and channel.proof[channel_idx+1].startswith('send:'):\n", + " alphas.append(get_f())\n", + " fri_merkles_root.append(get_s())\n", + "fri_constant = FieldElement(int(get_s()))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### channel information from part 4\n", + "Repeat query 3 times. In each query:\n", + "* read and merkle validate value of intepolated polynomal at a random point `x` (idx) and `g*x` (idx+8) and `g*g*x` (idx+16)\n", + "* compute the value of the composite polynoimal at `x`\n", + "* read and merkle validate the value of the first fri layer at `x`.\n", + "* compare the fri value at `x` to the computed value. Note that we are not doing the same for `-x`\n", + "* compute next layer value\n", + "* repeat on next layer at same index which is `x**2`" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Success!\n" + ] + } + ], + "source": [ + "#channel_idx = 26 # reset for repeated debug runs\n", + "from merkle import verify_decommitment\n", + "\n", + "idxs = []\n", + "w = FieldElement.generator()\n", + "h = w ** ((2 ** 30 * 3) // 8192)\n", + "\n", + "def read_verify(idx,root):\n", + " v = get_s()\n", + " # TODO using eval on a string coming from an untrusted source is dangerous\n", + " assert verify_decommitment(idx,v,eval(get_s()),root)\n", + " return FieldElement(int(v))\n", + "\n", + "for query in range(3):\n", + "# print(query)\n", + " idx = get_i() # pick a random location in the domain\n", + " assert idx < 8192\n", + " \n", + " # sample the interpolated poly of the trace `f` at 3 locations that are one distance apart in the trace domain\n", + " # one step in the trace domain is 8 in the evaluation domain\n", + " f0 = read_verify(idx,mt_root) # f(x)\n", + " f1 = read_verify(idx+8,mt_root) # f(gx)\n", + " f2 = read_verify(idx+16,mt_root) # f(g^2x)\n", + " \n", + " x0 = w * (h ** idx)\n", + " next_cp0 = cp(x0,f0,f1,f2) # compute the value of the composite poly `cp` from `f`\n", + " length = 8192\n", + " # TODO we dont need to send fri_merkles_root[-1] in the channel\n", + " for i,(fri_merkle_root,alpha) in enumerate(zip(fri_merkles_root[:-1],alphas)):\n", + "# print(f'i={i}')\n", + " idx = idx % length\n", + " x0 = w * (h ** idx) # convert the index idx to a point in the evaluation domain. needed for cp_odd below\n", + " x0 = x0 ** (2**i)\n", + " cp0 = read_verify(idx,fri_merkle_root)\n", + " assert cp0 == next_cp0\n", + "\n", + " sib_idx = (idx + length // 2) % length \n", + "# x1 = w * (h ** sib_idx) # convert the index sib_idx to a point in the evaluation domain\n", + "# x1 = x1 ** (2**i)\n", + "# assert x0*x0 == x1*x1,'x0 != -x1'\n", + " cp1 = read_verify(sib_idx,fri_merkle_root)\n", + " # note we dont check cp1 because we dont have a computed value\n", + "\n", + " cp_even = (cp0 + cp1) / 2\n", + " cp_odd = (cp0 - cp1) / 2\n", + " cp_odd /= x0\n", + " next_cp0 = cp_even + alpha * cp_odd\n", + "\n", + " length = length // 2\n", + " \n", + " assert fri_constant == next_cp0\n", + " # TODO the proof implementation above gives us fri_constant again after each iteration. We dont need this\n", + " assert fri_constant == FieldElement(int(get_s()))\n", + " \n", + "assert channel_idx == len(channel.proof), 'we did not reach the end of the channel'\n", + "\n", + "print('Success!')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.0" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +}