<![CDATA[Rachit's Blog]]>https://blog.rachitasrivastava.comRSS for NodeWed, 09 Oct 2024 06:20:24 GMT60<![CDATA[Verkle Tries Explained: A Simple Guide for Beginners]]>https://blog.rachitasrivastava.com/verkle-tries-explained-a-simple-guide-for-beginnershttps://blog.rachitasrivastava.com/verkle-tries-explained-a-simple-guide-for-beginnersSat, 10 Aug 2024 18:29:37 GMT<![CDATA[<p>Welcome to a deep dive into Verkle trees and their impact on Ethereum's state management. As Ethereum aims to improve scalability and efficiency, Verkle trees are becoming a key part of the solution.</p><p>In this blog, we'll break down what Verkle trees are and how they compare to the traditional hexary Patricia trees. We'll discuss the upcoming changes that will introduce Verkle trees alongside the current hexary Patricia trees. After these changes, all state edits and copies of accessed state will be stored in the Verkle tree, while the hexary Patricia tree will be locked from further modifications. This marks the first step in moving Ethereum towards exclusively using Verkle trees for state storage. Let's explore the details and understand why this change is crucial for Ethereum's future.</p><p>Ethereum requires statelessness moving forward. Stateless clients are ones that do not have to store the entire state database in order to validate incoming blocks. Instead of using their own local copy of Ethereum's state to verify blocks, stateless clients use a "witness" to the state data that arrives with the block.</p><p>A witness is a collection of individual pieces of the state data that are required to execute a particular set of transactions, and a cryptographic proof that the witness is really part of the full data.</p><p>Currently, in a hexary Merkle tree, the witness sizes are huge.<br />State access proposed gas cost = 1900, and current gas limit = 30M. This implies a total of 15789 state accesses.</p><ul><li><p>Using hexary merkle tree: 3kB per witness => ~47 MB!!!</p></li><li><p>Using binary Merkle tree: 0.5 kB/witness = 8 MB</p></li><li><p>Using width 256 verkle tree, 32 byte group: 96 byte/witness = 1.5 MB</p></li></ul><h2 id="heading-understanding-merkle-trees">Understanding Merkle Trees</h2><p>A <strong>Merkle tree</strong> is a type of data structure used in computer science and cryptography to efficiently and securely verify large sets of data. It's particularly important in blockchain technologies, such as Bitcoin and Ethereum. Heres how it works:</p><h3 id="heading-structure-of-a-merkle-tree">Structure of a Merkle Tree</h3><ol><li><p><strong>Leaf Nodes</strong>: The bottom-most nodes in the tree are called leaf nodes. Each leaf node contains the hash of a piece of data (for example, a transaction in a blockchain).</p></li><li><p><strong>Non-Leaf Nodes</strong>: Each non-leaf node is the hash of its two child nodes. This means that every node in the tree (except the leaves) is a hash of the data below it.</p></li><li><p><strong>Root Hash</strong>: The top node of the tree is known as the root hash or Merkle root. This single hash represents the entire data set.</p></li></ol><h3 id="heading-how-it-works">How it Works</h3><ul><li><p><strong>Hashing</strong>: Hashing is the process of converting data into a fixed-size string of characters, which is typically a unique representation of the data. In a Merkle tree, the data in each leaf node is hashed.</p></li><li><p><strong>Combining Hashes</strong>: Each pair of leaf node hashes is combined and then hashed again to form a parent node. This process is repeated up the tree until you reach the root hash.</p></li></ul><h3 id="heading-example">Example</h3><p>Imagine you have four transactions, A, B, C, and D. Heres how you would create a Merkle tree:</p><ol><li><p>Hash each transaction to create four leaf nodes: <code>Hash(A)</code>, <code>Hash(B)</code>, <code>Hash(C)</code>, <code>Hash(D)</code>.</p></li><li><p>Pair the leaf nodes and hash them to create the next level: <code>Hash(Hash(A) + Hash(B))</code>, <code>Hash(Hash(C) + Hash(D))</code>.</p></li><li><p>Finally, hash these results together to create the root hash: <code>Hash(Hash(Hash(A) + Hash(B)) + Hash(Hash(C) + Hash(D)))</code>.</p></li></ol><p><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/95/Hash_Tree.svg/1920px-Hash_Tree.svg.png" alt class="image--center mx-auto" /></p><h3 id="heading-what-if-we-increase-the-width-of-the-merkle-tree">What if we increase the width of the merkle tree ?</h3><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1722802342023/07c3c352-e6a3-4761-bd86-a37a4fecc40b.png" alt class="image--center mx-auto" /></p><ul><li><p>In this case, to check a particular leaf, all siblings need to be passed to verify its inclusion inside <code>inner 01</code> i.e. if there are <code>n</code> number of leafs, to calculate the immediate hash, we need to provide <code>n-1</code> number of leaf hashes.</p></li><li><p>The proofs become very inefficient</p></li></ul><h3 id="heading-where-does-verkle-trees-come-into-play">Where does Verkle trees come into play ?</h3><p>The goal is to have a scheme where we can have less height of the tree and still are able to efficiently proof all siblings. We do this by introducing "Verkle Commitments" in the structure.</p><h2 id="heading-verkle-trie">Verkle Trie</h2><ul><li><p>Verkle = Vector commitment + Merkle. Basically a merkle tree but with vector commitments instead of hashes.</p></li><li><p>Trie = tree + retrieval i.e. tree where each node represents a prefix of keys.</p></li></ul><h3 id="heading-understanding-vector-commitment-schemes">Understanding Vector Commitment Schemes</h3><p>We will use KZG or Kate commitment scheme since its fairly more popular construction. In KZG we essentially create a polynomial, and then evaluate the polynomial at a secrete point. This is our vector commitment. The key is that using this commitment, we can do "openings" on the commitment and check the inclusion of a given set of points. You can read more about how this scheme works <a target="_blank" href="https://blog.rachitasrivastava.com/demystifying-kzg-poly-commit-scheme">here</a>.</p><p>Similarly another property of KZG is the ability to do multi proofs check. Essentially we can verify the inclusion of a set of point inside a KZG commited polynomial with a single pairing check. More info <a target="_blank" href="https://dankradfeist.de/ethereum/2020/06/16/kate-polynomial-commitments.html">here</a>.</p><p>From the above, it's easy to understand the advantages of a vector commitment over hash based commitment:</p><p>We can essentially "open" a vector commitment to verify the inclusion of a piece of data, contrary to hashed where we have to regenerate the hash itself, using all the involved data points. This reduced the size of our witness significantly.</p><p>Now, imagine the same tree before, but now nodes are a vector commitment rather than hash based commitments. This implies that to confirm the inclusion of a leaf, I need to do an opening check on the root, and similarly on the inner node, without having to supply all the siblings. Essentially reducing my witness size dramatically.</p><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1722803083003/372b0573-ea87-4cab-a053-535ebe5807f9.png" alt="( credits: Dankred Feist )" class="image--center mx-auto" /></p><p>How to navigate:</p><p>Verkle proof for a single leaf: <code>0101 0111 1010 1111 -> 1213</code> is a key value pair.</p><p><img src="https://dankradfeist.de/assets/verkle_trie.svg" alt class="image--center mx-auto" /></p><ul><li><p>We start from the root, the proof of Inner root A us <code>0101</code>`.</p></li><li><p>Proof that the root of Inner node B is the evaluation of the commitment of Inner node A at <code>0111</code>.</p></li><li><p>Proof that the root of the node <code>0101 0111 1010 111 -> 1213</code> is the evaluation of the commitment of Inner node B at index 1010</p></li></ul><h3 id="heading-pedersen-commitment-scheme">Pedersen Commitment Scheme</h3><p>The scheme involves:</p><ul><li><p>Elliptic group G with generators \(g\_1, g\_2, ..., g\_n\\\).</p><ul><li><p>The only important thing is that their relative discrete logarithm (\(g\_1=qg\_2\\\) is not known</p></li><li><p>This does not need a trusted setup</p></li></ul></li><li><p>To commit to vector \(x\_1, x\_2, , x\_n\\\)</p><ul><li>Compute \(C = x\_1 * g\_1 + x\_2 * g\_2 + ... + x\_n * g\_n\\\)Ethereum state after migration</li></ul></li></ul><p><strong>Why Pederson commitment</strong></p><ul><li><p>Smaller commitments+proofs as no pairing-friendly group (32 bytes)</p></li><li><p>No trusted setup</p></li><li><p>Everything can be done inside a SNARK as well</p></li></ul><p><img src="https://storage.googleapis.com/ethereum-hackmd/upload_2eea7f262d0e6a38fa048c6194df24ea.png" alt class="image--center mx-auto" /></p><p><strong>Key Structure</strong></p><p>\(key = Pederson(Contract Address, Storage Location[:-1]\\\) + suffix</p><p>where suffix = \(offset % 256\\\) ( offset in the group of 256 leaves )</p><ul><li>The key is called the <code>stem</code> , which gives the pass through the stem tree and extension, and then we use suffix to get the element in the suffix tree.</li></ul><h3 id="heading-state-expiry">State Expiry</h3><p>Currently, once you pay fees on ethereum, the day stays forever. This causes state bloat after a while.</p><p>Now, after a while data will expire</p><ul><li><p>Each year, a new period starts with a fresh tree</p></li><li><p>Data for the current and previous period is kept</p></li><li><p>Data of previous years is discarded ( except the root )</p></li><li><p>After two periods, data needs to be resurrected with a proof.</p></li></ul><p><strong>Address Space Extension</strong></p><p>State expiry comes with a thing called Address Space Extension.</p><ul><li><p>Currently in ethereum, address are 20 bytes long, which are the first 20 bytes of the hash of your public key.</p></li><li><p>Now the pub key will be 32 bytes. We take 26 bytes of the hash, the remaining 6 bytes are used for versioning and future use, and to denote the epoch / period.</p></li></ul><div class="hn-table"><table><thead><tr><td>Period</td><td>Address</td><td>Balance</td></tr></thead><tbody><tr><td>0</td><td>010000<strong>000000</strong>000000...00000000</td><td>1234</td></tr><tr><td>0</td><td>010000<strong>000000</strong>000000...00000001</td><td>4567</td></tr><tr><td>1</td><td>010000<strong>000001</strong>0000000...0000000</td><td>7890</td></tr><tr><td>2</td><td>010000<strong>000002</strong>0000000...0000000</td><td>1121</td></tr><tr><td>2</td><td>010000<strong>000000</strong>0000000...0000000</td><td>1234</td></tr></tbody></table></div><ul><li><p>Period 0: Lets say Alice controls address 0x000...000. Bob has address 0x000.0001. Both have period as 0 since they are scene for the first time.</p></li><li><p>Period 1: Alice receives some funds from Bob, Bob doesn't actually resurrect the address from period 0 actually, but sends the funds to Alice to an address containing the current period ( 1 ). So it's seen for the first time in period 1. Alice still controls the private key of this hash, so in a way she controls two addresses with 1 private key.</p></li><li><p>Period 2: Alice wants the value from period one, she passes a proof to resurrect the address. The value from Period 1 that bob send, is not overwritten.</p></li></ul><p>Points around Address State Extension:</p><ul><li><p>Simplify data storage problem.</p></li><li><p>Risk of breaking existing contracts.</p></li></ul><h3 id="heading-state-network">State Network</h3><ul><li><p>A network alongside the block propagation network.</p></li><li><p>A node only stores a subset of the entire data.</p></li><li><p>Data is requested over the network as needed</p></li><li><p>A proof is provided to ensure that the provided data is correct.</p></li></ul><p>And that covers the whole design around Verkle trees.</p><p>Connect with on Twitter <a target="_blank" href="https://x.com/privacy_prophet">@privacy_prophet</a> where I post deep insights on a wide variety of topics in the Blockchain ecosystem.</p><hr /><p><strong>Resources</strong></p><ul><li><p><a target="_blank" href="https://notes.ethereum.org/@vbuterin/verkle_tree_eip">Vitalik Blog</a></p></li><li><p><a target="_blank" href="https://www.youtube.com/watch?v=RGJOQHzg3UQ&t=475s">Dankshard Feist session on Verkle trees</a></p></li><li><p><a target="_blank" href="https://ethereum-magicians.org/t/proposed-verkle-tree-scheme-for-ethereum-state/5805">Proposed Verkle Tree Scheme</a></p></li><li><p><a target="_blank" href="https://blog.ethereum.org/2021/12/02/verkle-tree-structure">Ethereum.org Blog</a></p></li><li><p><a target="_blank" href="https://www.youtube.com/watch?v=f7bEtX3Z57o&t=1317s">Statelessness and Verkle Trees by Guillaume Ballet</a></p></li></ul>]]><![CDATA[<p>Welcome to a deep dive into Verkle trees and their impact on Ethereum's state management. As Ethereum aims to improve scalability and efficiency, Verkle trees are becoming a key part of the solution.</p><p>In this blog, we'll break down what Verkle trees are and how they compare to the traditional hexary Patricia trees. We'll discuss the upcoming changes that will introduce Verkle trees alongside the current hexary Patricia trees. After these changes, all state edits and copies of accessed state will be stored in the Verkle tree, while the hexary Patricia tree will be locked from further modifications. This marks the first step in moving Ethereum towards exclusively using Verkle trees for state storage. Let's explore the details and understand why this change is crucial for Ethereum's future.</p><p>Ethereum requires statelessness moving forward. Stateless clients are ones that do not have to store the entire state database in order to validate incoming blocks. Instead of using their own local copy of Ethereum's state to verify blocks, stateless clients use a "witness" to the state data that arrives with the block.</p><p>A witness is a collection of individual pieces of the state data that are required to execute a particular set of transactions, and a cryptographic proof that the witness is really part of the full data.</p><p>Currently, in a hexary Merkle tree, the witness sizes are huge.<br />State access proposed gas cost = 1900, and current gas limit = 30M. This implies a total of 15789 state accesses.</p><ul><li><p>Using hexary merkle tree: 3kB per witness => ~47 MB!!!</p></li><li><p>Using binary Merkle tree: 0.5 kB/witness = 8 MB</p></li><li><p>Using width 256 verkle tree, 32 byte group: 96 byte/witness = 1.5 MB</p></li></ul><h2 id="heading-understanding-merkle-trees">Understanding Merkle Trees</h2><p>A <strong>Merkle tree</strong> is a type of data structure used in computer science and cryptography to efficiently and securely verify large sets of data. It's particularly important in blockchain technologies, such as Bitcoin and Ethereum. Heres how it works:</p><h3 id="heading-structure-of-a-merkle-tree">Structure of a Merkle Tree</h3><ol><li><p><strong>Leaf Nodes</strong>: The bottom-most nodes in the tree are called leaf nodes. Each leaf node contains the hash of a piece of data (for example, a transaction in a blockchain).</p></li><li><p><strong>Non-Leaf Nodes</strong>: Each non-leaf node is the hash of its two child nodes. This means that every node in the tree (except the leaves) is a hash of the data below it.</p></li><li><p><strong>Root Hash</strong>: The top node of the tree is known as the root hash or Merkle root. This single hash represents the entire data set.</p></li></ol><h3 id="heading-how-it-works">How it Works</h3><ul><li><p><strong>Hashing</strong>: Hashing is the process of converting data into a fixed-size string of characters, which is typically a unique representation of the data. In a Merkle tree, the data in each leaf node is hashed.</p></li><li><p><strong>Combining Hashes</strong>: Each pair of leaf node hashes is combined and then hashed again to form a parent node. This process is repeated up the tree until you reach the root hash.</p></li></ul><h3 id="heading-example">Example</h3><p>Imagine you have four transactions, A, B, C, and D. Heres how you would create a Merkle tree:</p><ol><li><p>Hash each transaction to create four leaf nodes: <code>Hash(A)</code>, <code>Hash(B)</code>, <code>Hash(C)</code>, <code>Hash(D)</code>.</p></li><li><p>Pair the leaf nodes and hash them to create the next level: <code>Hash(Hash(A) + Hash(B))</code>, <code>Hash(Hash(C) + Hash(D))</code>.</p></li><li><p>Finally, hash these results together to create the root hash: <code>Hash(Hash(Hash(A) + Hash(B)) + Hash(Hash(C) + Hash(D)))</code>.</p></li></ol><p><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/95/Hash_Tree.svg/1920px-Hash_Tree.svg.png" alt class="image--center mx-auto" /></p><h3 id="heading-what-if-we-increase-the-width-of-the-merkle-tree">What if we increase the width of the merkle tree ?</h3><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1722802342023/07c3c352-e6a3-4761-bd86-a37a4fecc40b.png" alt class="image--center mx-auto" /></p><ul><li><p>In this case, to check a particular leaf, all siblings need to be passed to verify its inclusion inside <code>inner 01</code> i.e. if there are <code>n</code> number of leafs, to calculate the immediate hash, we need to provide <code>n-1</code> number of leaf hashes.</p></li><li><p>The proofs become very inefficient</p></li></ul><h3 id="heading-where-does-verkle-trees-come-into-play">Where does Verkle trees come into play ?</h3><p>The goal is to have a scheme where we can have less height of the tree and still are able to efficiently proof all siblings. We do this by introducing "Verkle Commitments" in the structure.</p><h2 id="heading-verkle-trie">Verkle Trie</h2><ul><li><p>Verkle = Vector commitment + Merkle. Basically a merkle tree but with vector commitments instead of hashes.</p></li><li><p>Trie = tree + retrieval i.e. tree where each node represents a prefix of keys.</p></li></ul><h3 id="heading-understanding-vector-commitment-schemes">Understanding Vector Commitment Schemes</h3><p>We will use KZG or Kate commitment scheme since its fairly more popular construction. In KZG we essentially create a polynomial, and then evaluate the polynomial at a secrete point. This is our vector commitment. The key is that using this commitment, we can do "openings" on the commitment and check the inclusion of a given set of points. You can read more about how this scheme works <a target="_blank" href="https://blog.rachitasrivastava.com/demystifying-kzg-poly-commit-scheme">here</a>.</p><p>Similarly another property of KZG is the ability to do multi proofs check. Essentially we can verify the inclusion of a set of point inside a KZG commited polynomial with a single pairing check. More info <a target="_blank" href="https://dankradfeist.de/ethereum/2020/06/16/kate-polynomial-commitments.html">here</a>.</p><p>From the above, it's easy to understand the advantages of a vector commitment over hash based commitment:</p><p>We can essentially "open" a vector commitment to verify the inclusion of a piece of data, contrary to hashed where we have to regenerate the hash itself, using all the involved data points. This reduced the size of our witness significantly.</p><p>Now, imagine the same tree before, but now nodes are a vector commitment rather than hash based commitments. This implies that to confirm the inclusion of a leaf, I need to do an opening check on the root, and similarly on the inner node, without having to supply all the siblings. Essentially reducing my witness size dramatically.</p><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1722803083003/372b0573-ea87-4cab-a053-535ebe5807f9.png" alt="( credits: Dankred Feist )" class="image--center mx-auto" /></p><p>How to navigate:</p><p>Verkle proof for a single leaf: <code>0101 0111 1010 1111 -> 1213</code> is a key value pair.</p><p><img src="https://dankradfeist.de/assets/verkle_trie.svg" alt class="image--center mx-auto" /></p><ul><li><p>We start from the root, the proof of Inner root A us <code>0101</code>`.</p></li><li><p>Proof that the root of Inner node B is the evaluation of the commitment of Inner node A at <code>0111</code>.</p></li><li><p>Proof that the root of the node <code>0101 0111 1010 111 -> 1213</code> is the evaluation of the commitment of Inner node B at index 1010</p></li></ul><h3 id="heading-pedersen-commitment-scheme">Pedersen Commitment Scheme</h3><p>The scheme involves:</p><ul><li><p>Elliptic group G with generators \(g\_1, g\_2, ..., g\_n\\\).</p><ul><li><p>The only important thing is that their relative discrete logarithm (\(g\_1=qg\_2\\\) is not known</p></li><li><p>This does not need a trusted setup</p></li></ul></li><li><p>To commit to vector \(x\_1, x\_2, , x\_n\\\)</p><ul><li>Compute \(C = x\_1 * g\_1 + x\_2 * g\_2 + ... + x\_n * g\_n\\\)Ethereum state after migration</li></ul></li></ul><p><strong>Why Pederson commitment</strong></p><ul><li><p>Smaller commitments+proofs as no pairing-friendly group (32 bytes)</p></li><li><p>No trusted setup</p></li><li><p>Everything can be done inside a SNARK as well</p></li></ul><p><img src="https://storage.googleapis.com/ethereum-hackmd/upload_2eea7f262d0e6a38fa048c6194df24ea.png" alt class="image--center mx-auto" /></p><p><strong>Key Structure</strong></p><p>\(key = Pederson(Contract Address, Storage Location[:-1]\\\) + suffix</p><p>where suffix = \(offset % 256\\\) ( offset in the group of 256 leaves )</p><ul><li>The key is called the <code>stem</code> , which gives the pass through the stem tree and extension, and then we use suffix to get the element in the suffix tree.</li></ul><h3 id="heading-state-expiry">State Expiry</h3><p>Currently, once you pay fees on ethereum, the day stays forever. This causes state bloat after a while.</p><p>Now, after a while data will expire</p><ul><li><p>Each year, a new period starts with a fresh tree</p></li><li><p>Data for the current and previous period is kept</p></li><li><p>Data of previous years is discarded ( except the root )</p></li><li><p>After two periods, data needs to be resurrected with a proof.</p></li></ul><p><strong>Address Space Extension</strong></p><p>State expiry comes with a thing called Address Space Extension.</p><ul><li><p>Currently in ethereum, address are 20 bytes long, which are the first 20 bytes of the hash of your public key.</p></li><li><p>Now the pub key will be 32 bytes. We take 26 bytes of the hash, the remaining 6 bytes are used for versioning and future use, and to denote the epoch / period.</p></li></ul><div class="hn-table"><table><thead><tr><td>Period</td><td>Address</td><td>Balance</td></tr></thead><tbody><tr><td>0</td><td>010000<strong>000000</strong>000000...00000000</td><td>1234</td></tr><tr><td>0</td><td>010000<strong>000000</strong>000000...00000001</td><td>4567</td></tr><tr><td>1</td><td>010000<strong>000001</strong>0000000...0000000</td><td>7890</td></tr><tr><td>2</td><td>010000<strong>000002</strong>0000000...0000000</td><td>1121</td></tr><tr><td>2</td><td>010000<strong>000000</strong>0000000...0000000</td><td>1234</td></tr></tbody></table></div><ul><li><p>Period 0: Lets say Alice controls address 0x000...000. Bob has address 0x000.0001. Both have period as 0 since they are scene for the first time.</p></li><li><p>Period 1: Alice receives some funds from Bob, Bob doesn't actually resurrect the address from period 0 actually, but sends the funds to Alice to an address containing the current period ( 1 ). So it's seen for the first time in period 1. Alice still controls the private key of this hash, so in a way she controls two addresses with 1 private key.</p></li><li><p>Period 2: Alice wants the value from period one, she passes a proof to resurrect the address. The value from Period 1 that bob send, is not overwritten.</p></li></ul><p>Points around Address State Extension:</p><ul><li><p>Simplify data storage problem.</p></li><li><p>Risk of breaking existing contracts.</p></li></ul><h3 id="heading-state-network">State Network</h3><ul><li><p>A network alongside the block propagation network.</p></li><li><p>A node only stores a subset of the entire data.</p></li><li><p>Data is requested over the network as needed</p></li><li><p>A proof is provided to ensure that the provided data is correct.</p></li></ul><p>And that covers the whole design around Verkle trees.</p><p>Connect with on Twitter <a target="_blank" href="https://x.com/privacy_prophet">@privacy_prophet</a> where I post deep insights on a wide variety of topics in the Blockchain ecosystem.</p><hr /><p><strong>Resources</strong></p><ul><li><p><a target="_blank" href="https://notes.ethereum.org/@vbuterin/verkle_tree_eip">Vitalik Blog</a></p></li><li><p><a target="_blank" href="https://www.youtube.com/watch?v=RGJOQHzg3UQ&t=475s">Dankshard Feist session on Verkle trees</a></p></li><li><p><a target="_blank" href="https://ethereum-magicians.org/t/proposed-verkle-tree-scheme-for-ethereum-state/5805">Proposed Verkle Tree Scheme</a></p></li><li><p><a target="_blank" href="https://blog.ethereum.org/2021/12/02/verkle-tree-structure">Ethereum.org Blog</a></p></li><li><p><a target="_blank" href="https://www.youtube.com/watch?v=f7bEtX3Z57o&t=1317s">Statelessness and Verkle Trees by Guillaume Ballet</a></p></li></ul>]]><![CDATA[Groth 16: A linear PCP based Snark]]>https://blog.rachitasrivastava.com/groth-16-a-linear-pcp-based-snarkhttps://blog.rachitasrivastava.com/groth-16-a-linear-pcp-based-snarkFri, 05 Jan 2024 09:30:29 GMT<![CDATA[<p>Prerequisites:</p><ul><li><p>Understanding of what QAP is and how to create one from R1CS.</p></li><li><p>Group theory understandings</p></li><li><p><a target="_blank" href="https://twitter.com/RacSri25/status/1738503612094148718">Bilinear Pairings</a></p></li></ul><hr /><h3 id="heading-probabilistic-checkable-proofs-pcp">Probabilistic Checkable Proofs ( PCP )</h3><p>What is a PCP:</p><p>Prover generates a PCP oracle, which can be thought of as a big message of polynomial size. Verifier queries PCP oracle at random points, and using these queries verifier can verify the computation of a polynomial.</p><p>In Linear PCP, the PCP generated by the Prover is a linear function. The Oracle responds to linear queries by doing an inner product.</p><p>Recall:</p><p>\(p(x) = (\sum_{i=1}^m c_i \times l_i(x)) \times (\sum_{i=1}^m c_i \times r_i(x)) \times (\sum_{i=1}^m c_i \times o_i(x)) = V(x).q(x)\)</p><p>To show the above can be done using a linear PCP model.</p><ul><li><p>The verifier can submit only linear queries to the PCP:</p><p> \( \times - = V(\gamma)\)</p></li></ul><hr /><p><strong>Some termonologies before we continue further:</strong></p><ul><li><p><strong>Knowledge of exponentiation:</strong></p><ul><li><p>Sample random \(\alpha\), compute \(g^{\alpha .l_i(\tau)}\) for i = 1...m.</p></li><li><p>\(\pi_1 = g^{\sum_{i=1}^m c_i \times l_i (\tau)}\) and \(\pi_1^{'} = g^{\sum_{i=1}^m c_i \times l_i (\tau)}\)</p></li><li><p>\(e(\pi_1, g^\alpha) = e(\pi^{'}, g)\)</p></li></ul></li><li><p><strong>Generic Group Model</strong></p><ul><li><p>The adversary is only given an oracle to compute the group operation.</p></li><li><p>eg. given \(g^{\alpha.l_i(\tau)}\)for i= 1,...,m. The adversary can only compute their linear combinations.</p></li></ul></li></ul><hr /><h3 id="heading-linear-pcp"><strong>Linear PCP</strong></h3><ul><li><p><strong>Preprocessing ( Key Generation )</strong>:</p><ul><li><p>Proving key: \(p, G, g, G_T, e\)</p></li><li><p>Compute Public Keys: \(g^{l_i(\tau)},g^{r_i(\tau)},g^{o_i(\tau)}\) for \(i=1, \ldots,m\) --- (1)</p></li><li><p>Evaluate: \(g^\tau , g^{\tau^2}, \ldots, g^{\tau^m}\) ( Public ). --- (2)</p></li><li><p>Also: \(g ^ {\beta(l_i(\tau) + r_i(\tau) + o_i(\tau))})\) for \(i \in [m]\) --- (3)</p></li><li><p>Verification Key: \(g^V(\tau) \) - this is known to the verifier</p></li><li><p>delete \(\tau\)</p></li></ul></li><li><p>Prove Generation</p><ul><li><p>\(\pi _1 = g^{\sum_{i=1}^m c_i \times l_i(\tau)}\) , where the term in power is the public key calculated in (1).</p></li><li><p>Similarly, calculate \(\pi _2\) and \(\pi _3\) using (1) above.</p></li><li><p>\(\pi _4 = g^{q(\tau)}\) calculated using (2) above.</p></li><li><p>\(\pi_5 = \prod _{i=1}^m (g ^ {\beta(l_i(\tau) + r_i(\tau) + o_i(\tau))})^{c_i}\)</p></li></ul></li><li><p>Verification</p><ul><li><p>The idea is to check the value of p(x) equation equality in the exponent.</p></li><li><p>Verify using: \(\frac{e(\pi_1, \pi_2)}{e(\pi_3, g)} = e(g^{V(\tau)}, \pi_4)\) bilinear relation.</p></li><li><p>\(e(\pi_1\pi_2\pi_3, g^\beta) = e(\pi_5, g)\): This check makes sure that the prover isn't cheating and not using the same vector \(c\) in the generation of \(\pi_1, \pi_2, \pi_3\).</p></li></ul></li></ul><p>The above protocol is not the real implementation. If you notice, while input in the above circuit is considered as a witness. But in practical application, a circuit has both, public and private inputs.</p><p>So to convert the above into a real protocol, notice the proof property that is generated above:</p><p>$$\pi_1 = g^{\sum_{i \in I_{mid}}c_i \times l_i(\tau)} * g^{\sum_{i \in I_{io}}c_i \times l_i(\tau)}$$</p><p>where \(I_{mid}\) is the witness, and \(I_{io}\) is the public Input/output. So now the proof becomes:</p><p>$$\pi_1 = g^{\sum_{i \in I_{mid}}c_i \times l_i(\tau)}$$</p><p>and the verifier can simply do:</p><p>$$\pi_1^* = \pi_1 . g^{\sum_{i \in I_{io}}c_i \times l_i(\tau)}$$</p><p>$$\frac{e(\pi_1*,\pi_2*)}{e(\pi_3^*, g)} = e(g^{V(\tau), \pi_4})$$</p><p>And that's Groth 16 protocol under the hood!!</p><hr /><h3 id="heading-variant-of-groth16">Variant of Groth16</h3><p>$$p(x) = (\sum_{i=1}^m c_i \times l_i(x)) \times (\sum_{i=1}^m c_i \times r_i(x)) \times (\sum_{i=1}^m c_i \times o_i(x)) = V(x).q(x)$$</p><ol><li><p>Proof:</p><ul><li><p>\(\pi_1 = g^{\alpha + \sum_{i=1}^m c_i \times l_i (\tau)}\)</p></li><li><p>\(\pi_2 = g^{\beta + \sum_{i=1}^m c_i \times r_i (\tau)}\)</p></li><li><p>\(\pi_3 = \prod _{i=1}^m (g ^ {\sum c_i \times \beta(l_i(\tau) + r_i(\tau) + o_i(\tau)) + V(\tau)q(\tau)})\)</p></li></ul></li><li><p>Verify:</p><ul><li>\(e(\pi_1, \pi_2) = e(\pi_3, g).e(g^\alpha, g^\beta)\)</li></ul></li></ol><blockquote><p>To make the above systems Zero Knowlege, a randomizer is simply added to the proofs generated.</p><p>\(\pi _1 = g^{\sum_{i=1}^m c_i \times l_i(\tau) + \delta_1V(\tau)} \) and similarly for \(\pi_2\) and \(\pi_3\)</p></blockquote><hr /><h3 id="heading-properties-of-groth16">Properties of Groth16</h3><ol><li><p><strong>Computation Cost</strong>: The cost to generate a proof in Groth16 is relatively high compared to the verification cost. This is because the prover must perform a series of complex mathematical operations, which include polynomial evaluations and multi-exponentiations in elliptic curve groups.</p></li><li><p><strong>Input Length Dependence</strong>: The proving time is dependent on the length of the witness (the information that proves the statement). For more complex statements or larger datasets, the time required to generate a proof increases.</p></li><li><p><strong>Memory Usage</strong>: Groth16 proving can be memory-intensive. The prover needs to handle large polynomials and multiple group elements, which requires substantial computational resources, especially for complex statements.</p></li><li><p><strong>Parallelizability</strong>: The proving process in Groth16 can be parallelized to some extent. This means that with sufficient computational resources, the time taken to generate a proof can be reduced.</p></li><li><p><strong>Efficient Verification</strong>: Groth16 stands out for its extremely efficient verification process. Verifying a proof typically involves a few pairings and elliptic curve operations, which are computationally cheaper than the operations required for proof generation.</p></li><li><p><strong>Constant Time Verification</strong>: Regardless of the complexity of the original computation, the verification time remains constant. This is a significant advantage, especially in systems where quick verification is essential, like in blockchain transactions.</p></li><li><p><strong>Low Resource Requirement</strong>: The verifier does not need extensive computational resources. This asymmetry between proving and verification complexity makes Groth16 particularly suitable for systems where the verifier's resources are limited, such as in smart contracts on a blockchain.</p></li><li><p><strong>Asymmetry Advantage</strong>: The asymmetry in computational requirements between the prover and verifier in Groth16 is by design. While it demands more from the prover, it benefits systems where verification needs to be quick and inexpensive, a common scenario in decentralized systems like blockchains.</p></li><li><p><strong>Scalability Impact</strong>: This asymmetry affects scalability. The efficiency in verification means that more transactions (or other verifiable computations) can be processed in a shorter time, which is crucial for the scalability of blockchains.</p></li><li><p><strong>Security and Trust</strong>: The proving complexity also contributes to the security of the system, as it makes it computationally prohibitive to generate false proofs.</p></li><li><p>In Groth16 variants:</p><ol><li><p>Proof size: 3 group elements - 144 bytes</p></li><li><p>Verifier time: 1 pairing equation</p></li></ol></li></ol><hr /><p>Follow me on social media: <a target="_blank" href="https://twitter.com/privacy_prophet">Twitter</a>, and <a target="_blank" href="https://www.linkedin.com/in/rachit-anand-srivastava/">LinkedIn</a> for new updates in the cryptography space and any discussion related to computer science.</p>]]><![CDATA[<p>Prerequisites:</p><ul><li><p>Understanding of what QAP is and how to create one from R1CS.</p></li><li><p>Group theory understandings</p></li><li><p><a target="_blank" href="https://twitter.com/RacSri25/status/1738503612094148718">Bilinear Pairings</a></p></li></ul><hr /><h3 id="heading-probabilistic-checkable-proofs-pcp">Probabilistic Checkable Proofs ( PCP )</h3><p>What is a PCP:</p><p>Prover generates a PCP oracle, which can be thought of as a big message of polynomial size. Verifier queries PCP oracle at random points, and using these queries verifier can verify the computation of a polynomial.</p><p>In Linear PCP, the PCP generated by the Prover is a linear function. The Oracle responds to linear queries by doing an inner product.</p><p>Recall:</p><p>\(p(x) = (\sum_{i=1}^m c_i \times l_i(x)) \times (\sum_{i=1}^m c_i \times r_i(x)) \times (\sum_{i=1}^m c_i \times o_i(x)) = V(x).q(x)\)</p><p>To show the above can be done using a linear PCP model.</p><ul><li><p>The verifier can submit only linear queries to the PCP:</p><p> \( \times - = V(\gamma)\)</p></li></ul><hr /><p><strong>Some termonologies before we continue further:</strong></p><ul><li><p><strong>Knowledge of exponentiation:</strong></p><ul><li><p>Sample random \(\alpha\), compute \(g^{\alpha .l_i(\tau)}\) for i = 1...m.</p></li><li><p>\(\pi_1 = g^{\sum_{i=1}^m c_i \times l_i (\tau)}\) and \(\pi_1^{'} = g^{\sum_{i=1}^m c_i \times l_i (\tau)}\)</p></li><li><p>\(e(\pi_1, g^\alpha) = e(\pi^{'}, g)\)</p></li></ul></li><li><p><strong>Generic Group Model</strong></p><ul><li><p>The adversary is only given an oracle to compute the group operation.</p></li><li><p>eg. given \(g^{\alpha.l_i(\tau)}\)for i= 1,...,m. The adversary can only compute their linear combinations.</p></li></ul></li></ul><hr /><h3 id="heading-linear-pcp"><strong>Linear PCP</strong></h3><ul><li><p><strong>Preprocessing ( Key Generation )</strong>:</p><ul><li><p>Proving key: \(p, G, g, G_T, e\)</p></li><li><p>Compute Public Keys: \(g^{l_i(\tau)},g^{r_i(\tau)},g^{o_i(\tau)}\) for \(i=1, \ldots,m\) --- (1)</p></li><li><p>Evaluate: \(g^\tau , g^{\tau^2}, \ldots, g^{\tau^m}\) ( Public ). --- (2)</p></li><li><p>Also: \(g ^ {\beta(l_i(\tau) + r_i(\tau) + o_i(\tau))})\) for \(i \in [m]\) --- (3)</p></li><li><p>Verification Key: \(g^V(\tau) \) - this is known to the verifier</p></li><li><p>delete \(\tau\)</p></li></ul></li><li><p>Prove Generation</p><ul><li><p>\(\pi _1 = g^{\sum_{i=1}^m c_i \times l_i(\tau)}\) , where the term in power is the public key calculated in (1).</p></li><li><p>Similarly, calculate \(\pi _2\) and \(\pi _3\) using (1) above.</p></li><li><p>\(\pi _4 = g^{q(\tau)}\) calculated using (2) above.</p></li><li><p>\(\pi_5 = \prod _{i=1}^m (g ^ {\beta(l_i(\tau) + r_i(\tau) + o_i(\tau))})^{c_i}\)</p></li></ul></li><li><p>Verification</p><ul><li><p>The idea is to check the value of p(x) equation equality in the exponent.</p></li><li><p>Verify using: \(\frac{e(\pi_1, \pi_2)}{e(\pi_3, g)} = e(g^{V(\tau)}, \pi_4)\) bilinear relation.</p></li><li><p>\(e(\pi_1\pi_2\pi_3, g^\beta) = e(\pi_5, g)\): This check makes sure that the prover isn't cheating and not using the same vector \(c\) in the generation of \(\pi_1, \pi_2, \pi_3\).</p></li></ul></li></ul><p>The above protocol is not the real implementation. If you notice, while input in the above circuit is considered as a witness. But in practical application, a circuit has both, public and private inputs.</p><p>So to convert the above into a real protocol, notice the proof property that is generated above:</p><p>$$\pi_1 = g^{\sum_{i \in I_{mid}}c_i \times l_i(\tau)} * g^{\sum_{i \in I_{io}}c_i \times l_i(\tau)}$$</p><p>where \(I_{mid}\) is the witness, and \(I_{io}\) is the public Input/output. So now the proof becomes:</p><p>$$\pi_1 = g^{\sum_{i \in I_{mid}}c_i \times l_i(\tau)}$$</p><p>and the verifier can simply do:</p><p>$$\pi_1^* = \pi_1 . g^{\sum_{i \in I_{io}}c_i \times l_i(\tau)}$$</p><p>$$\frac{e(\pi_1*,\pi_2*)}{e(\pi_3^*, g)} = e(g^{V(\tau), \pi_4})$$</p><p>And that's Groth 16 protocol under the hood!!</p><hr /><h3 id="heading-variant-of-groth16">Variant of Groth16</h3><p>$$p(x) = (\sum_{i=1}^m c_i \times l_i(x)) \times (\sum_{i=1}^m c_i \times r_i(x)) \times (\sum_{i=1}^m c_i \times o_i(x)) = V(x).q(x)$$</p><ol><li><p>Proof:</p><ul><li><p>\(\pi_1 = g^{\alpha + \sum_{i=1}^m c_i \times l_i (\tau)}\)</p></li><li><p>\(\pi_2 = g^{\beta + \sum_{i=1}^m c_i \times r_i (\tau)}\)</p></li><li><p>\(\pi_3 = \prod _{i=1}^m (g ^ {\sum c_i \times \beta(l_i(\tau) + r_i(\tau) + o_i(\tau)) + V(\tau)q(\tau)})\)</p></li></ul></li><li><p>Verify:</p><ul><li>\(e(\pi_1, \pi_2) = e(\pi_3, g).e(g^\alpha, g^\beta)\)</li></ul></li></ol><blockquote><p>To make the above systems Zero Knowlege, a randomizer is simply added to the proofs generated.</p><p>\(\pi _1 = g^{\sum_{i=1}^m c_i \times l_i(\tau) + \delta_1V(\tau)} \) and similarly for \(\pi_2\) and \(\pi_3\)</p></blockquote><hr /><h3 id="heading-properties-of-groth16">Properties of Groth16</h3><ol><li><p><strong>Computation Cost</strong>: The cost to generate a proof in Groth16 is relatively high compared to the verification cost. This is because the prover must perform a series of complex mathematical operations, which include polynomial evaluations and multi-exponentiations in elliptic curve groups.</p></li><li><p><strong>Input Length Dependence</strong>: The proving time is dependent on the length of the witness (the information that proves the statement). For more complex statements or larger datasets, the time required to generate a proof increases.</p></li><li><p><strong>Memory Usage</strong>: Groth16 proving can be memory-intensive. The prover needs to handle large polynomials and multiple group elements, which requires substantial computational resources, especially for complex statements.</p></li><li><p><strong>Parallelizability</strong>: The proving process in Groth16 can be parallelized to some extent. This means that with sufficient computational resources, the time taken to generate a proof can be reduced.</p></li><li><p><strong>Efficient Verification</strong>: Groth16 stands out for its extremely efficient verification process. Verifying a proof typically involves a few pairings and elliptic curve operations, which are computationally cheaper than the operations required for proof generation.</p></li><li><p><strong>Constant Time Verification</strong>: Regardless of the complexity of the original computation, the verification time remains constant. This is a significant advantage, especially in systems where quick verification is essential, like in blockchain transactions.</p></li><li><p><strong>Low Resource Requirement</strong>: The verifier does not need extensive computational resources. This asymmetry between proving and verification complexity makes Groth16 particularly suitable for systems where the verifier's resources are limited, such as in smart contracts on a blockchain.</p></li><li><p><strong>Asymmetry Advantage</strong>: The asymmetry in computational requirements between the prover and verifier in Groth16 is by design. While it demands more from the prover, it benefits systems where verification needs to be quick and inexpensive, a common scenario in decentralized systems like blockchains.</p></li><li><p><strong>Scalability Impact</strong>: This asymmetry affects scalability. The efficiency in verification means that more transactions (or other verifiable computations) can be processed in a shorter time, which is crucial for the scalability of blockchains.</p></li><li><p><strong>Security and Trust</strong>: The proving complexity also contributes to the security of the system, as it makes it computationally prohibitive to generate false proofs.</p></li><li><p>In Groth16 variants:</p><ol><li><p>Proof size: 3 group elements - 144 bytes</p></li><li><p>Verifier time: 1 pairing equation</p></li></ol></li></ol><hr /><p>Follow me on social media: <a target="_blank" href="https://twitter.com/privacy_prophet">Twitter</a>, and <a target="_blank" href="https://www.linkedin.com/in/rachit-anand-srivastava/">LinkedIn</a> for new updates in the cryptography space and any discussion related to computer science.</p>]]>https://cdn.hashnode.com/res/hashnode/image/upload/v1704194856204/1f63f4ed-0578-4f64-90a5-d025e65ecf12.jpeg<![CDATA[Circuit Satisfiability to Quadratic Arithmetic Program]]>https://blog.rachitasrivastava.com/circuit-satisfiability-to-quadratic-arithmetic-programhttps://blog.rachitasrivastava.com/circuit-satisfiability-to-quadratic-arithmetic-programThu, 04 Jan 2024 10:30:13 GMT<![CDATA[<p>In this article, we will explore how the following topics:</p><ul><li><p>R1CS</p></li><li><p>Transcript</p></li><li><p>Selector Polynomial</p></li><li><p>Vanishing Polynomial</p></li><li><p>Quadratic Arithmetic Program</p></li></ul><hr /><h3 id="heading-r1cs">R1CS</h3><p>The term "R1CS" stands for "Rank-1 Constraint System." It's a method used in computer science, particularly in the field of cryptography and zero-knowledge proofs. The Rank-1 Constraint System is a way to express a set of arithmetic constraints. It's commonly used in the construction of zero-knowledge proofs, particularly in systems like zk-SNARKs (Zero-Knowledge Succinct Non-Interactive Argument of Knowledge).</p><p>An R1CS typically consists of three matrices: A, B, and C. Each row in these matrices represents a single constraint, and the system is satisfied if there exists a vector of variables \(\vec{x}\) such that for every row \( \vec{i}\), the following equation holds:</p><p>$$\vec{a}_i \cdot \vec{x} \times \vec{b}_i \cdot \vec{x} = \vec{c}_i \cdot \vec{x}$$</p><h3 id="heading-transcript">Transcript</h3><p>Trace just means Prover is just going to evaluate this entire circuit and take the values on all the wires. In Interactive proofs, the value of every gate is traced, in Plonk, the trace is defined as the left input, right input, and output of every gate.</p><p>QAP: input + output of every <strong>multiplication gate.</strong></p><p>e.g.:</p><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1704175742715/75437c93-13e0-4b25-8e79-9d2572d53df7.png" alt class="image--center mx-auto" /></p><p>( image credits to Yupeng Zhang )</p><h3 id="heading-selector-polynomial">Selector Polynomial</h3><p>Let's assume a polynomial \(l_i(x)\) which means is \(c_i\) the left input of gate j, for j = 1,2,3, where \(c_i\) is the value in the i'th position in the transcript ( as in the above image ).</p><p>e.g.</p><ol><li><p>\(l_1(x) : (1,0,0)\).<br /> The polynomial interpolation at a known set \(\Omega\) : \(l_1(w) = 1, l_1(w^2) = 0, l_1(w^3) = 0\).</p></li><li><p>\(l_x(x) : (0, 0 ,1)\)</p><p> Polynomial interpolation: \(l_3(w) = 0, l_3(w^2) = 0, l_w(w^3) = 0\) since value 1 is the input of the addition gate which in turn is the left input of the multiplication gate ( remember we are taking trace over multiplication gate in case of QAP )</p></li></ol><p><strong>Properties</strong>:</p><ul><li><p>\(L(x) = \sum_{i=1}^9 c_i * l_i(x)\). If you write the trace for the entire transcript in the above example: \(L(w) = c_1 = 3\), \(L(w^3) = c_3 + c_4\). The values are the 1s that are present at the respective input column in the entire transpose.</p></li><li><p>Similarly, we calculate \(r(x)\) and create a transcript then using that a selector polynomial \(R(x)\).</p></li><li><p>Similarly, we have \(O(x)\) which represents the gate outputs.</p></li></ul><p>Now we have:</p><p>$$p(x) = L(x)R(x) - O(x)$$</p><ul><li>Claim is \(p(w^j) = 0 , for \ j = 1,2,3..\)</li></ul><h3 id="heading-vanishing-polynomial">Vanishing Polynomial</h3><ul><li>We define \(p(x) = V(x)q(x)\) , where \(V(x) = (x - w )(x-w^2)(x-w^3)\) is the vanishing polynomial of the set \(\Omega = \{ w, w^2, w^3 \}\)</li></ul><hr /><p>Finally:</p><p>P claims to know a w such that \(C(x,w) = y\) \(<==>\)P claims to know that a vector c such that \(p(x) = V(x)q(x)\).</p><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1704177568238/adcb6845-3242-4456-a19c-811f595338a6.png" alt class="image--center mx-auto" /></p><p>Where the table is a piece of public information, and generated during preprocessing phase.</p><p>Now, instead of checking the constraints in the R1CS individually, we can now check <em>all of the constraints at the same time</em> by doing the dot product check <em>on the polynomials</em>.</p><hr /><p>Lastly, follow me on social media: <a target="_blank" href="https://twitter.com/privacy_prophet">Twitter</a>, and <a target="_blank" href="https://www.linkedin.com/in/rachit-anand-srivastava/">LinkedIn</a> for new updates in the cryptography space.</p><hr /><p>References:</p><ul><li><p><a target="_blank" href="https://risencrypto.github.io/R1CSQAP/#:~:text=The%20next%20step%20is%20taking,vector%20%26%20the%20number%20of%20gates">https://risencrypto.github.io/R1CSQAP/#:~:text=The%20next%20step%20is%20taking,vector%20%26%20the%20number%20of%20gates</a>.</p></li><li><p><a target="_blank" href="https://medium.com/@VitalikButerin/quadratic-arithmetic-programs-from-zero-to-hero-f6d558cea649">https://medium.com/@VitalikButerin/quadratic-arithmetic-programs-from-zero-to-hero-f6d558cea649</a></p></li><li><p>SNARK lecture on linear PCP by Yupeng Zhang <a target="_blank" href="https://www.youtube.com/watch?v=I7TXIHXamwM&list=PLS01nW3Rtgor_yJmQsGBZAg5XM4TSGpPs&index=9&ab_channel=Blockchain-Web3MOOCs">Link</a></p></li></ul>]]><![CDATA[<p>In this article, we will explore how the following topics:</p><ul><li><p>R1CS</p></li><li><p>Transcript</p></li><li><p>Selector Polynomial</p></li><li><p>Vanishing Polynomial</p></li><li><p>Quadratic Arithmetic Program</p></li></ul><hr /><h3 id="heading-r1cs">R1CS</h3><p>The term "R1CS" stands for "Rank-1 Constraint System." It's a method used in computer science, particularly in the field of cryptography and zero-knowledge proofs. The Rank-1 Constraint System is a way to express a set of arithmetic constraints. It's commonly used in the construction of zero-knowledge proofs, particularly in systems like zk-SNARKs (Zero-Knowledge Succinct Non-Interactive Argument of Knowledge).</p><p>An R1CS typically consists of three matrices: A, B, and C. Each row in these matrices represents a single constraint, and the system is satisfied if there exists a vector of variables \(\vec{x}\) such that for every row \( \vec{i}\), the following equation holds:</p><p>$$\vec{a}_i \cdot \vec{x} \times \vec{b}_i \cdot \vec{x} = \vec{c}_i \cdot \vec{x}$$</p><h3 id="heading-transcript">Transcript</h3><p>Trace just means Prover is just going to evaluate this entire circuit and take the values on all the wires. In Interactive proofs, the value of every gate is traced, in Plonk, the trace is defined as the left input, right input, and output of every gate.</p><p>QAP: input + output of every <strong>multiplication gate.</strong></p><p>e.g.:</p><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1704175742715/75437c93-13e0-4b25-8e79-9d2572d53df7.png" alt class="image--center mx-auto" /></p><p>( image credits to Yupeng Zhang )</p><h3 id="heading-selector-polynomial">Selector Polynomial</h3><p>Let's assume a polynomial \(l_i(x)\) which means is \(c_i\) the left input of gate j, for j = 1,2,3, where \(c_i\) is the value in the i'th position in the transcript ( as in the above image ).</p><p>e.g.</p><ol><li><p>\(l_1(x) : (1,0,0)\).<br /> The polynomial interpolation at a known set \(\Omega\) : \(l_1(w) = 1, l_1(w^2) = 0, l_1(w^3) = 0\).</p></li><li><p>\(l_x(x) : (0, 0 ,1)\)</p><p> Polynomial interpolation: \(l_3(w) = 0, l_3(w^2) = 0, l_w(w^3) = 0\) since value 1 is the input of the addition gate which in turn is the left input of the multiplication gate ( remember we are taking trace over multiplication gate in case of QAP )</p></li></ol><p><strong>Properties</strong>:</p><ul><li><p>\(L(x) = \sum_{i=1}^9 c_i * l_i(x)\). If you write the trace for the entire transcript in the above example: \(L(w) = c_1 = 3\), \(L(w^3) = c_3 + c_4\). The values are the 1s that are present at the respective input column in the entire transpose.</p></li><li><p>Similarly, we calculate \(r(x)\) and create a transcript then using that a selector polynomial \(R(x)\).</p></li><li><p>Similarly, we have \(O(x)\) which represents the gate outputs.</p></li></ul><p>Now we have:</p><p>$$p(x) = L(x)R(x) - O(x)$$</p><ul><li>Claim is \(p(w^j) = 0 , for \ j = 1,2,3..\)</li></ul><h3 id="heading-vanishing-polynomial">Vanishing Polynomial</h3><ul><li>We define \(p(x) = V(x)q(x)\) , where \(V(x) = (x - w )(x-w^2)(x-w^3)\) is the vanishing polynomial of the set \(\Omega = \{ w, w^2, w^3 \}\)</li></ul><hr /><p>Finally:</p><p>P claims to know a w such that \(C(x,w) = y\) \(<==>\)P claims to know that a vector c such that \(p(x) = V(x)q(x)\).</p><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1704177568238/adcb6845-3242-4456-a19c-811f595338a6.png" alt class="image--center mx-auto" /></p><p>Where the table is a piece of public information, and generated during preprocessing phase.</p><p>Now, instead of checking the constraints in the R1CS individually, we can now check <em>all of the constraints at the same time</em> by doing the dot product check <em>on the polynomials</em>.</p><hr /><p>Lastly, follow me on social media: <a target="_blank" href="https://twitter.com/privacy_prophet">Twitter</a>, and <a target="_blank" href="https://www.linkedin.com/in/rachit-anand-srivastava/">LinkedIn</a> for new updates in the cryptography space.</p><hr /><p>References:</p><ul><li><p><a target="_blank" href="https://risencrypto.github.io/R1CSQAP/#:~:text=The%20next%20step%20is%20taking,vector%20%26%20the%20number%20of%20gates">https://risencrypto.github.io/R1CSQAP/#:~:text=The%20next%20step%20is%20taking,vector%20%26%20the%20number%20of%20gates</a>.</p></li><li><p><a target="_blank" href="https://medium.com/@VitalikButerin/quadratic-arithmetic-programs-from-zero-to-hero-f6d558cea649">https://medium.com/@VitalikButerin/quadratic-arithmetic-programs-from-zero-to-hero-f6d558cea649</a></p></li><li><p>SNARK lecture on linear PCP by Yupeng Zhang <a target="_blank" href="https://www.youtube.com/watch?v=I7TXIHXamwM&list=PLS01nW3Rtgor_yJmQsGBZAg5XM4TSGpPs&index=9&ab_channel=Blockchain-Web3MOOCs">Link</a></p></li></ul>]]>https://cdn.hashnode.com/res/hashnode/image/upload/v1704174695792/8e7a561b-ed3d-4d8b-8c88-16c793019494.jpeg<![CDATA[FRI Polynomial Commitment Scheme]]>https://blog.rachitasrivastava.com/fri-polynomial-commitment-schemehttps://blog.rachitasrivastava.com/fri-polynomial-commitment-schemeTue, 02 Jan 2024 10:30:10 GMT<![CDATA[<p>The FRI (Fast Reed-Solomon Interactive Oracle) commitment scheme is a cryptographic protocol that plays a crucial role in various zero-knowledge proof systems. It's designed to efficiently prove the correctness of computations without revealing any underlying data. The FRI scheme is particularly important in the context of zk-STARKs (Zero-Knowledge Scalable Transparent Arguments of Knowledge), which are a type of non-interactive cryptographic proof that's scalable and doesn't require a trusted setup.</p><h3 id="heading-problem-addressed-using-fri">Problem addressed using FRI:</h3><ol><li><p><strong>Want P time linear in degree, not field size</strong>:</p><ul><li><p>Let's say we use a Merkle tree for commitment to values in a field f. But this is very inefficient since if field f is big, then the number of leaves, and consequently the proof length grows significantly. In FRI, rather than P merkle-commiting to all (p-1) evaluations of q, P merkle-commits to evaluations of q(x) for \(x \in \Omega\) of \(F_p\).</p></li><li><p>\(\Omega\) has size \(\rho ^ {-1} k\) where \(\rho < 1/2\) constant and k is the degree of q.</p></li><li><p>Proof length will be about \(\lambda / log(\rho ^ {-1}). log^2(k)\) hash values where \(\lambda\) is the security parameter, aka. " \(\lambda\) bits of security ".</p></li><li><p>Let \(\omega \in F_p\), n is the smallest integer such that \(\omega ^ n = 1\), then \(\Omega = \{ 1, \omega, \omega ^2, \ldots, \omega ^ {n-1} \}\)</p></li><li><p>From group theory, \(\Omega\) has size n if and only if n divides p-1.</p><ul><li>Hence, FRI based SNARKs work over fields like \(F_p\) with \(p = 2^{64} - 2^{32} + 1\) , p-1 is divisible by \(2^{32}\). Running FRI over the field can support any power of 2 value of n up to \(2^{32}\).</li></ul></li><li><p>For eg. FRI commitment to univariate polynomial \(q(x)\) in \(F_{41}[X]\) when \(8 = \rho^{-1}.k\), where roots of unity = \(\{ 1, -1, 9, -9, 3, -3, 14, -14 \}\) becomes the root of the committed merkle tree.</p></li></ul></li></ol><blockquote><p>To visualize roots of unity try this tool -> <a target="_blank" href="https://www.geogebra.org/m/sZFwAZfs">https://www.geogebra.org/m/sZFwAZfs</a></p></blockquote><ol><li><p><strong>V needs to know the committed vector at all evaluations over domain \(\Omega\) of some (k-1) degree polynomial.</strong></p><ul><li><p>V "inspects" a few entries of the vector to "get a sense" of whether its low-degree. This will add a Merkle authentication path ( log(n) hash values ) to the proof.</p></li><li><p>This is impractical. FRI's test will be interactive. We use a "folding phase" followed by a "query phase". The folding phase is log(k) rounds. The query phase is one round.</p></li></ul></li></ol><hr /><h3 id="heading-folding-phase-interactive">Folding Phase ( Interactive )</h3><h4 id="heading-step-1-starting-with-a-high-degree-polynomial"><strong>Step 1: Starting with a High-Degree Polynomial</strong></h4><ul><li>The process begins with the prover having a high-degree polynomial. This polynomial is a representation of the computation or data they intend to prove something about.</li></ul><h4 id="heading-step-2-interpolation-and-evaluation"><strong>Step 2: Interpolation and Evaluation</strong></h4><ul><li>The polynomial is evaluated at various points. These points typically lie in a domain related to a specific subgroup of a finite field.</li></ul><h4 id="heading-step-3-the-folding-process"><strong>Step 3: The Folding Process</strong></h4><ul><li><p>The prover reduces the degree of the polynomial. They select a subset of the evaluation points and merge the values of the polynomial at these points using a linear or affine transformation.</p></li><li><p>V picks up a random field element r, and r is used to "randomly combine" every two paired-up entries.</p></li><li><p>q(x) is split into "even and odd components" -> \(q(x) = q_e(X^2) + Xq_o(X^2)\)</p></li><li><p>The prescribed "folding" q is \(q_{fold}(Z) = q_e(Z) + rq_o(Z)\) where degree of \(q_{fold}\) is half of the degree of q.</p></li></ul><h4 id="heading-step-4-recursive-application"><strong>Step 4: Recursive Application</strong></h4><ul><li><p><strong>What Happens</strong>: After folding, the polynomial's degree is lowered. The prover then commits to this new, reduced-degree polynomial. Folding is repeated until the degree falls to 0.</p></li><li><p><strong>Why It Matters</strong>: This recursive process is essential for gradually simplifying the polynomial.</p></li></ul><p><strong>Step 5: End</strong></p><ul><li>The length of the folded vector after the degree has fallen to 0 is still \(\rho ^ {-1} >= 2\). Since the degree should be 0, P can specify the folder vector with a single field element.</li></ul><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1704046170064/d04decfe-23c9-4354-8056-73d631ca8f88.png" alt class="image--center mx-auto" /></p><p>( image credit: Justin Thaler lecture ( link in references ).</p><blockquote><ul><li><p>The calculation in the picture uses the fact that if x and -x are n'th roots of unity and z = \(x^2\). Then \(q_{fold}(z) = \frac{(r+x)}{2x} .q(x) + \frac{(r-x)}{-2x} .q(-x) \) ( derivation not covered in this post ).</p></li><li><p>FRI heavily uses the fact that the map \(x -> x^2\) is 2 to 1 on \(\Omega\), ensuring that the relevant domain halves in size with each fold.</p></li></ul></blockquote><hr /><h3 id="heading-query-phase-interactive">Query Phase ( Interactive )</h3><ul><li><p>P may have "lied":</p><ul><li><p>sending a vector that is not the prescribed folding of the previous vector</p></li><li><p>To "artificially" reduce the degree of the claimed folded vector.</p></li></ul></li><li><p>The Query phase is where the verifier tries to detect inconsistencies.</p></li><li><p>V picks about \(\lambda / log ( \rho ^ {-1}) \) entries of each folded vector and confirming each is the prescribed linear combination of the relevant two entries of the previous vector.</p></li><li><p>Proof length ( and V time ): roughly \((\lambda / log(\rho ^ {-1})_. log(k)^2\) hash evaluations. Each of the folded vectors ( log(k) ) is queried at \(\lambda / log ( \rho ^ {-1})\)</p></li></ul><hr /><blockquote><ul><li><p>For security analysis, refer the links in references. This article focuses on the working only.</p></li><li><p>There is a known attack where prover folders a polynomial s rather than q, where s agrees on a set T = \(\rho n\) elements of \(\Omega\).</p></li></ul></blockquote><hr /><h3 id="heading-polynomial-commitment-using-fri">Polynomial Commitment using FRI</h3><ul><li><p>Problems with FRI</p><ul><li><p>P has only the Merkle-committed to evaluations of q over domain \(\Omega\), not the whole field.</p></li><li><p>V only knows that q is "not too far" from low-degree, not exactly low-degree.</p></li></ul></li><li><p>Solution</p><ul><li><p>We know \(q(X) - v = w(X)(X-r)\) is equivalent to \(q(r) = v\) ( refer <a target="_blank" href="https://hashnode.com/edit/clqrsz77o000308js2rtx6hv9">here</a>). w is a polynomial of degree at most d.</p></li><li><p>So to confirm \(q(r) = v\) V applies FRI's fold + query procedure to the functions \((q(X) - v)(X-r)^{-1}\) using degree bound d-1. Whenever the FRI verifier queries this function at \(\Omega\), evaluation can be obtained with one query to q at the same point.</p></li><li><p>V doesn't know that q is exactly a low degree, but to pass V's check, \(v\) has to equal \(h(r)\) where h is the degree d polynomial that is closest to q.</p></li></ul></li></ul><blockquote><p>Each FRI verifies query brings <1 bit of security to the commitment scheme. FRI today is used as a weaker primitive tha a polynomial commitment ( list polynomial commitment scheme ), which suffices for SNARK security. P is bound to a "small set" of low-degree polynomials rather than to a single one.</p></blockquote><hr /><p><strong>Conclusion:</strong> The FRI commitment scheme represents a significant advancement in the field of cryptographic proofs, enabling more secure and efficient verification of computations in various applications, including blockchain technology and privacy-preserving computations.</p><p>Lastly, follow me on social media: <a target="_blank" href="https://twitter.com/privacy_prophet">Twitter</a>, and <a target="_blank" href="https://www.linkedin.com/in/rachit-anand-srivastava/">LinkedIn</a> for new updates in the cryptography space.</p><hr /><p><strong>References</strong>:</p><ol><li><p><a target="_blank" href="https://blog.lambdaclass.com/how-to-code-fri-from-scratch/">https://blog.lambdaclass.com/how-to-code-fri-from-scratch/</a></p></li><li><p>Lecture by Justin Thaler <a target="_blank" href="https://www.youtube.com/watch?v=A3edAQDPnDY&list=PLS01nW3Rtgor_yJmQsGBZAg5XM4TSGpPs&index=8&ab_channel=Blockchain-Web3MOOCs">Link</a></p></li><li><p><a target="_blank" href="https://chat.openai.com/">https://chat.openai.com/</a></p></li><li><p><a target="_blank" href="https://eprint.iacr.org/2019/1020.pdf">https://eprint.iacr.org/2019/1020.pdf</a></p></li><li><p><a target="_blank" href="https://aszepieniec.github.io/stark-anatomy/fri.html">https://aszepieniec.github.io/stark-anatomy/fri.html</a></p></li></ol>]]><![CDATA[<p>The FRI (Fast Reed-Solomon Interactive Oracle) commitment scheme is a cryptographic protocol that plays a crucial role in various zero-knowledge proof systems. It's designed to efficiently prove the correctness of computations without revealing any underlying data. The FRI scheme is particularly important in the context of zk-STARKs (Zero-Knowledge Scalable Transparent Arguments of Knowledge), which are a type of non-interactive cryptographic proof that's scalable and doesn't require a trusted setup.</p><h3 id="heading-problem-addressed-using-fri">Problem addressed using FRI:</h3><ol><li><p><strong>Want P time linear in degree, not field size</strong>:</p><ul><li><p>Let's say we use a Merkle tree for commitment to values in a field f. But this is very inefficient since if field f is big, then the number of leaves, and consequently the proof length grows significantly. In FRI, rather than P merkle-commiting to all (p-1) evaluations of q, P merkle-commits to evaluations of q(x) for \(x \in \Omega\) of \(F_p\).</p></li><li><p>\(\Omega\) has size \(\rho ^ {-1} k\) where \(\rho < 1/2\) constant and k is the degree of q.</p></li><li><p>Proof length will be about \(\lambda / log(\rho ^ {-1}). log^2(k)\) hash values where \(\lambda\) is the security parameter, aka. " \(\lambda\) bits of security ".</p></li><li><p>Let \(\omega \in F_p\), n is the smallest integer such that \(\omega ^ n = 1\), then \(\Omega = \{ 1, \omega, \omega ^2, \ldots, \omega ^ {n-1} \}\)</p></li><li><p>From group theory, \(\Omega\) has size n if and only if n divides p-1.</p><ul><li>Hence, FRI based SNARKs work over fields like \(F_p\) with \(p = 2^{64} - 2^{32} + 1\) , p-1 is divisible by \(2^{32}\). Running FRI over the field can support any power of 2 value of n up to \(2^{32}\).</li></ul></li><li><p>For eg. FRI commitment to univariate polynomial \(q(x)\) in \(F_{41}[X]\) when \(8 = \rho^{-1}.k\), where roots of unity = \(\{ 1, -1, 9, -9, 3, -3, 14, -14 \}\) becomes the root of the committed merkle tree.</p></li></ul></li></ol><blockquote><p>To visualize roots of unity try this tool -> <a target="_blank" href="https://www.geogebra.org/m/sZFwAZfs">https://www.geogebra.org/m/sZFwAZfs</a></p></blockquote><ol><li><p><strong>V needs to know the committed vector at all evaluations over domain \(\Omega\) of some (k-1) degree polynomial.</strong></p><ul><li><p>V "inspects" a few entries of the vector to "get a sense" of whether its low-degree. This will add a Merkle authentication path ( log(n) hash values ) to the proof.</p></li><li><p>This is impractical. FRI's test will be interactive. We use a "folding phase" followed by a "query phase". The folding phase is log(k) rounds. The query phase is one round.</p></li></ul></li></ol><hr /><h3 id="heading-folding-phase-interactive">Folding Phase ( Interactive )</h3><h4 id="heading-step-1-starting-with-a-high-degree-polynomial"><strong>Step 1: Starting with a High-Degree Polynomial</strong></h4><ul><li>The process begins with the prover having a high-degree polynomial. This polynomial is a representation of the computation or data they intend to prove something about.</li></ul><h4 id="heading-step-2-interpolation-and-evaluation"><strong>Step 2: Interpolation and Evaluation</strong></h4><ul><li>The polynomial is evaluated at various points. These points typically lie in a domain related to a specific subgroup of a finite field.</li></ul><h4 id="heading-step-3-the-folding-process"><strong>Step 3: The Folding Process</strong></h4><ul><li><p>The prover reduces the degree of the polynomial. They select a subset of the evaluation points and merge the values of the polynomial at these points using a linear or affine transformation.</p></li><li><p>V picks up a random field element r, and r is used to "randomly combine" every two paired-up entries.</p></li><li><p>q(x) is split into "even and odd components" -> \(q(x) = q_e(X^2) + Xq_o(X^2)\)</p></li><li><p>The prescribed "folding" q is \(q_{fold}(Z) = q_e(Z) + rq_o(Z)\) where degree of \(q_{fold}\) is half of the degree of q.</p></li></ul><h4 id="heading-step-4-recursive-application"><strong>Step 4: Recursive Application</strong></h4><ul><li><p><strong>What Happens</strong>: After folding, the polynomial's degree is lowered. The prover then commits to this new, reduced-degree polynomial. Folding is repeated until the degree falls to 0.</p></li><li><p><strong>Why It Matters</strong>: This recursive process is essential for gradually simplifying the polynomial.</p></li></ul><p><strong>Step 5: End</strong></p><ul><li>The length of the folded vector after the degree has fallen to 0 is still \(\rho ^ {-1} >= 2\). Since the degree should be 0, P can specify the folder vector with a single field element.</li></ul><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1704046170064/d04decfe-23c9-4354-8056-73d631ca8f88.png" alt class="image--center mx-auto" /></p><p>( image credit: Justin Thaler lecture ( link in references ).</p><blockquote><ul><li><p>The calculation in the picture uses the fact that if x and -x are n'th roots of unity and z = \(x^2\). Then \(q_{fold}(z) = \frac{(r+x)}{2x} .q(x) + \frac{(r-x)}{-2x} .q(-x) \) ( derivation not covered in this post ).</p></li><li><p>FRI heavily uses the fact that the map \(x -> x^2\) is 2 to 1 on \(\Omega\), ensuring that the relevant domain halves in size with each fold.</p></li></ul></blockquote><hr /><h3 id="heading-query-phase-interactive">Query Phase ( Interactive )</h3><ul><li><p>P may have "lied":</p><ul><li><p>sending a vector that is not the prescribed folding of the previous vector</p></li><li><p>To "artificially" reduce the degree of the claimed folded vector.</p></li></ul></li><li><p>The Query phase is where the verifier tries to detect inconsistencies.</p></li><li><p>V picks about \(\lambda / log ( \rho ^ {-1}) \) entries of each folded vector and confirming each is the prescribed linear combination of the relevant two entries of the previous vector.</p></li><li><p>Proof length ( and V time ): roughly \((\lambda / log(\rho ^ {-1})_. log(k)^2\) hash evaluations. Each of the folded vectors ( log(k) ) is queried at \(\lambda / log ( \rho ^ {-1})\)</p></li></ul><hr /><blockquote><ul><li><p>For security analysis, refer the links in references. This article focuses on the working only.</p></li><li><p>There is a known attack where prover folders a polynomial s rather than q, where s agrees on a set T = \(\rho n\) elements of \(\Omega\).</p></li></ul></blockquote><hr /><h3 id="heading-polynomial-commitment-using-fri">Polynomial Commitment using FRI</h3><ul><li><p>Problems with FRI</p><ul><li><p>P has only the Merkle-committed to evaluations of q over domain \(\Omega\), not the whole field.</p></li><li><p>V only knows that q is "not too far" from low-degree, not exactly low-degree.</p></li></ul></li><li><p>Solution</p><ul><li><p>We know \(q(X) - v = w(X)(X-r)\) is equivalent to \(q(r) = v\) ( refer <a target="_blank" href="https://hashnode.com/edit/clqrsz77o000308js2rtx6hv9">here</a>). w is a polynomial of degree at most d.</p></li><li><p>So to confirm \(q(r) = v\) V applies FRI's fold + query procedure to the functions \((q(X) - v)(X-r)^{-1}\) using degree bound d-1. Whenever the FRI verifier queries this function at \(\Omega\), evaluation can be obtained with one query to q at the same point.</p></li><li><p>V doesn't know that q is exactly a low degree, but to pass V's check, \(v\) has to equal \(h(r)\) where h is the degree d polynomial that is closest to q.</p></li></ul></li></ul><blockquote><p>Each FRI verifies query brings <1 bit of security to the commitment scheme. FRI today is used as a weaker primitive tha a polynomial commitment ( list polynomial commitment scheme ), which suffices for SNARK security. P is bound to a "small set" of low-degree polynomials rather than to a single one.</p></blockquote><hr /><p><strong>Conclusion:</strong> The FRI commitment scheme represents a significant advancement in the field of cryptographic proofs, enabling more secure and efficient verification of computations in various applications, including blockchain technology and privacy-preserving computations.</p><p>Lastly, follow me on social media: <a target="_blank" href="https://twitter.com/privacy_prophet">Twitter</a>, and <a target="_blank" href="https://www.linkedin.com/in/rachit-anand-srivastava/">LinkedIn</a> for new updates in the cryptography space.</p><hr /><p><strong>References</strong>:</p><ol><li><p><a target="_blank" href="https://blog.lambdaclass.com/how-to-code-fri-from-scratch/">https://blog.lambdaclass.com/how-to-code-fri-from-scratch/</a></p></li><li><p>Lecture by Justin Thaler <a target="_blank" href="https://www.youtube.com/watch?v=A3edAQDPnDY&list=PLS01nW3Rtgor_yJmQsGBZAg5XM4TSGpPs&index=8&ab_channel=Blockchain-Web3MOOCs">Link</a></p></li><li><p><a target="_blank" href="https://chat.openai.com/">https://chat.openai.com/</a></p></li><li><p><a target="_blank" href="https://eprint.iacr.org/2019/1020.pdf">https://eprint.iacr.org/2019/1020.pdf</a></p></li><li><p><a target="_blank" href="https://aszepieniec.github.io/stark-anatomy/fri.html">https://aszepieniec.github.io/stark-anatomy/fri.html</a></p></li></ol>]]>https://cdn.hashnode.com/res/hashnode/image/upload/v1704043643535/cd86fe3e-2fcb-4c60-8bb6-a6bc57a579e9.jpeg<![CDATA[Bulletproof Commitment Schemes: Integrating Cryptography and Mathematics]]>https://blog.rachitasrivastava.com/bulletproof-commitment-schemes-integrating-cryptography-and-mathematicshttps://blog.rachitasrivastava.com/bulletproof-commitment-schemes-integrating-cryptography-and-mathematicsSun, 31 Dec 2023 09:30:10 GMT<![CDATA[<h4 id="heading-introduction">Introduction</h4><p>The integration of mathematics in cryptography has led to innovative solutions like bulletproof commitment schemes, which are essential in enhancing digital security and privacy. Bulletproofs are a form of non-interactive zero-knowledge proof system, that relies heavily on mathematical concepts to provide efficient and secure commitment schemes. In this post, we delve into the mathematical underpinnings of bulletproofs and how they revolutionize commitment schemes.</p><h4 id="heading-what-is-a-bulletproof-commitment-scheme">What is a Bulletproof Commitment Scheme?</h4><p>A bulletproof commitment scheme is a cryptographic protocol that allows a party to commit to a value, maintaining its confidentiality, and ensuring the integrity of the commitment. These schemes are known for their efficiency, non-reliance on a trusted setup, and their ability to produce short proofs.</p><h4 id="heading-mathematical-foundations-of-bulletproofs">Mathematical Foundations of Bulletproofs</h4><ol><li><p><strong>Pedersen Commitments</strong>: The core of bulletproofs lies in Pedersen commitments, a cryptographic algorithm based on discrete logarithm problems. A Pedersen commitment to a value <code>v</code> is of the form <code>C = vG + rH</code>, where <code>G</code> and <code>H</code> are known generator points on an elliptic curve, and <code>r</code> is a secret blinding factor.</p></li><li><p><strong>Range Proofs</strong>: Bulletproofs provide a way to prove that a committed number lies within a certain range without revealing the number itself. This is done through complex polynomial commitments and inner product arguments, leveraging mathematical constructs like Hadamard products and vector operations.</p></li><li><p><strong>Inner Product Argument</strong>: At the heart of a bulletproof range proof is an efficient protocol for proving the inner product between two vectors, which allows the verifier to check that the commitment represents a number in the desired range without learning anything more about the number.</p></li></ol><h4 id="heading-how-bulletproof-commitment-schemes-work">How Bulletproof Commitment Schemes Work</h4><p><strong>Transparent Setup:</strong></p><ul><li><p>Sample random \(gp = (g_0, g_1, g_2, \ldots, g_d) \) in G.</p></li><li><p>Commit: \(f(x) = f_0 + f_1x + f_2x^2 + \ldots | f_dx^d\)<br /> \(com_f = g_0^{f_0} + g_1^{f_1} + \ldots + g_d^{f_d}\)</p></li></ul><p><strong>Steps in working of Poly-commitment based on BulletProofs:</strong></p><ol><li><p><strong>Evaluate</strong></p><ul><li><p>\(v = f_0 + f_1u + f_2u^2 + f_3u^3\) and send to the verifier.</p></li><li><p>Compute \(L, R, v_L, v_R\)</p><p> where \(L = g_2^{f_0}.g_3^{f_1}\), \(R = g_0^{f_2}.g_1^{f_3}\) , \(v_L = f_0 + f_1u\), \(v_r = f_2 + f_3u\) and sent to verifier.</p></li><li><p>Receive \(r\) from the verifier, reduce \(f\) to \(f^{'}\) of degree \(d/2\) where new polynomials change from \(f_0, f_1, f_2, f_3\) to \(rf_0 + f2, rf_1 + f_3\)</p></li><li><p>Update the bases \(gp'\)</p></li></ul></li><li><p><strong>Verify</strong></p><ul><li><p>Check \(v = v_L + v_R.u^{d/2}\)</p></li><li><p>Generate \(r\) randomly</p></li><li><p>Update:</p><ul><li><p>\(com^{'} = L^r.com_f.R^{r^{-1}}\)</p></li><li><p>\(gp^{'}= (g_0^{r^{-1}}.g_2, g_1^{r^{-1}}.g_3)\)</p></li><li><p>\(v^{'} =r.v_L + v_R\)</p></li></ul></li></ul></li></ol><p>Recurse log d times.</p><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1703934099569/37f8eba8-d505-4cc4-b4fd-0641d19a5752.png" alt="Poly-commitment based on Bulletproofs" class="image--center mx-auto" /></p><p><em>(image credit: Yupeng Zhang lecture on ZKP MOOC)</em></p><h4 id="heading-applications-and-importance">Applications and Importance</h4><ul><li><p><strong>Blockchain and Cryptocurrency</strong>: In blockchain, bulletproofs enable confidential transactions by allowing the amounts to be hidden yet verifiable.</p></li><li><p><strong>Voting Systems</strong>: Secure voting mechanisms can utilize bulletproofs to count votes accurately while maintaining voter anonymity.</p></li><li><p><strong>Privacy-Enhancing Technologies</strong>: They are crucial in constructing zero-knowledge proofs, pivotal for privacy in various digital applications.</p></li></ul><h4 id="heading-conclusion">Conclusion</h4><p>The mathematical complexity and cryptographic robustness of bulletproof commitment schemes mark a significant advancement in secure digital communications. By blending advanced mathematics with cryptographic techniques, bulletproofs offer a powerful tool for ensuring privacy and integrity in various digital applications, especially in the rapidly evolving field of blockchain technology. Understanding these schemes not only provides insight into modern cryptographic practices but also underscores the importance of mathematical principles in developing secure digital systems.</p><p>Lastly, follow me on social media: <a target="_blank" href="https://twitter.com/privacy_prophet">Twitter</a>, and <a target="_blank" href="https://www.linkedin.com/in/rachit-anand-srivastava/">LinkedIn</a> for new updates in the cryptography space.</p>]]><![CDATA[<h4 id="heading-introduction">Introduction</h4><p>The integration of mathematics in cryptography has led to innovative solutions like bulletproof commitment schemes, which are essential in enhancing digital security and privacy. Bulletproofs are a form of non-interactive zero-knowledge proof system, that relies heavily on mathematical concepts to provide efficient and secure commitment schemes. In this post, we delve into the mathematical underpinnings of bulletproofs and how they revolutionize commitment schemes.</p><h4 id="heading-what-is-a-bulletproof-commitment-scheme">What is a Bulletproof Commitment Scheme?</h4><p>A bulletproof commitment scheme is a cryptographic protocol that allows a party to commit to a value, maintaining its confidentiality, and ensuring the integrity of the commitment. These schemes are known for their efficiency, non-reliance on a trusted setup, and their ability to produce short proofs.</p><h4 id="heading-mathematical-foundations-of-bulletproofs">Mathematical Foundations of Bulletproofs</h4><ol><li><p><strong>Pedersen Commitments</strong>: The core of bulletproofs lies in Pedersen commitments, a cryptographic algorithm based on discrete logarithm problems. A Pedersen commitment to a value <code>v</code> is of the form <code>C = vG + rH</code>, where <code>G</code> and <code>H</code> are known generator points on an elliptic curve, and <code>r</code> is a secret blinding factor.</p></li><li><p><strong>Range Proofs</strong>: Bulletproofs provide a way to prove that a committed number lies within a certain range without revealing the number itself. This is done through complex polynomial commitments and inner product arguments, leveraging mathematical constructs like Hadamard products and vector operations.</p></li><li><p><strong>Inner Product Argument</strong>: At the heart of a bulletproof range proof is an efficient protocol for proving the inner product between two vectors, which allows the verifier to check that the commitment represents a number in the desired range without learning anything more about the number.</p></li></ol><h4 id="heading-how-bulletproof-commitment-schemes-work">How Bulletproof Commitment Schemes Work</h4><p><strong>Transparent Setup:</strong></p><ul><li><p>Sample random \(gp = (g_0, g_1, g_2, \ldots, g_d) \) in G.</p></li><li><p>Commit: \(f(x) = f_0 + f_1x + f_2x^2 + \ldots | f_dx^d\)<br /> \(com_f = g_0^{f_0} + g_1^{f_1} + \ldots + g_d^{f_d}\)</p></li></ul><p><strong>Steps in working of Poly-commitment based on BulletProofs:</strong></p><ol><li><p><strong>Evaluate</strong></p><ul><li><p>\(v = f_0 + f_1u + f_2u^2 + f_3u^3\) and send to the verifier.</p></li><li><p>Compute \(L, R, v_L, v_R\)</p><p> where \(L = g_2^{f_0}.g_3^{f_1}\), \(R = g_0^{f_2}.g_1^{f_3}\) , \(v_L = f_0 + f_1u\), \(v_r = f_2 + f_3u\) and sent to verifier.</p></li><li><p>Receive \(r\) from the verifier, reduce \(f\) to \(f^{'}\) of degree \(d/2\) where new polynomials change from \(f_0, f_1, f_2, f_3\) to \(rf_0 + f2, rf_1 + f_3\)</p></li><li><p>Update the bases \(gp'\)</p></li></ul></li><li><p><strong>Verify</strong></p><ul><li><p>Check \(v = v_L + v_R.u^{d/2}\)</p></li><li><p>Generate \(r\) randomly</p></li><li><p>Update:</p><ul><li><p>\(com^{'} = L^r.com_f.R^{r^{-1}}\)</p></li><li><p>\(gp^{'}= (g_0^{r^{-1}}.g_2, g_1^{r^{-1}}.g_3)\)</p></li><li><p>\(v^{'} =r.v_L + v_R\)</p></li></ul></li></ul></li></ol><p>Recurse log d times.</p><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1703934099569/37f8eba8-d505-4cc4-b4fd-0641d19a5752.png" alt="Poly-commitment based on Bulletproofs" class="image--center mx-auto" /></p><p><em>(image credit: Yupeng Zhang lecture on ZKP MOOC)</em></p><h4 id="heading-applications-and-importance">Applications and Importance</h4><ul><li><p><strong>Blockchain and Cryptocurrency</strong>: In blockchain, bulletproofs enable confidential transactions by allowing the amounts to be hidden yet verifiable.</p></li><li><p><strong>Voting Systems</strong>: Secure voting mechanisms can utilize bulletproofs to count votes accurately while maintaining voter anonymity.</p></li><li><p><strong>Privacy-Enhancing Technologies</strong>: They are crucial in constructing zero-knowledge proofs, pivotal for privacy in various digital applications.</p></li></ul><h4 id="heading-conclusion">Conclusion</h4><p>The mathematical complexity and cryptographic robustness of bulletproof commitment schemes mark a significant advancement in secure digital communications. By blending advanced mathematics with cryptographic techniques, bulletproofs offer a powerful tool for ensuring privacy and integrity in various digital applications, especially in the rapidly evolving field of blockchain technology. Understanding these schemes not only provides insight into modern cryptographic practices but also underscores the importance of mathematical principles in developing secure digital systems.</p><p>Lastly, follow me on social media: <a target="_blank" href="https://twitter.com/privacy_prophet">Twitter</a>, and <a target="_blank" href="https://www.linkedin.com/in/rachit-anand-srivastava/">LinkedIn</a> for new updates in the cryptography space.</p>]]>https://cdn.hashnode.com/res/hashnode/image/upload/v1703934438908/02afdb7c-9b95-42cb-af76-d9a67af52634.jpeg<![CDATA[Demystifying KZG Poly-commit Scheme]]>https://blog.rachitasrivastava.com/demystifying-kzg-poly-commit-schemehttps://blog.rachitasrivastava.com/demystifying-kzg-poly-commit-schemeSat, 30 Dec 2023 08:30:45 GMT<![CDATA[<hr /><p>A KZG polynomial commitment scheme allows a prover to commit to a polynomial so that they can later reveal and prove properties about the polynomial (like its value at certain points) without revealing the entire polynomial. This is particularly useful in scenarios where the polynomial represents some private data or computation.</p><p>Prequistics:</p><ul><li><p>How c<a target="_blank" href="https://www.di.ens.fr/~fouque/CoursBCS3.pdf">yclic groups</a> work in Numbers Theory</p></li><li><p>Discrete Log + <a target="_blank" href="https://www.nku.edu/~christensen/092mat483%20DH%20key%20exchange.pdf">Diffie-Hellman</a></p></li><li><p>Understanding of <a target="_blank" href="https://twitter.com/RacSri25/status/1738503612094148718">Bilinear Pairing</a></p></li></ul><hr /><h3 id="heading-kzg-poly-commit-scheme"><strong>KZG Poly-commit scheme</strong></h3><p>Before we start, lets see the elements we have:</p><ul><li><p>Bilinear Group p, G, g, G, e.</p></li><li><p>Univariate polynomials: \(F = F_p^{<=d}[X]\)</p></li></ul><p><strong>Step 1: Keygen</strong></p><ul><li><p>Sample random Fp</p></li><li><p>\(gp = (g, g^{\tau}, g^{\tau^2}, \ldots, g^{\tau^d})\)</p></li><li><p>delete \(\tau\) ( trusted setup )</p></li></ul><p><strong>Step 2: Commit</strong></p><ul><li><p>commit (gp, f) -> \(com_f\)</p></li><li><p>\(f(x) = f_0 + f_1x + f_2x ^2 + \ldots + f_dx^d\)</p></li><li><p>\(com_f = g^{f(\tau)}\)<br /> \(= g ^ {f_0 + f_1\tau + f_2\tau^2+ \ldots + f_d^{\tau ^ d}}\)</p><p> \(= (g)^{f_0}. (g^\tau)^{f_1}. (g^{\tau^2})^{f_2}. \ldots (g^{\tau^d})^{f_d}\)</p></li></ul><p><strong>Step 3: Proof</strong></p><ul><li><p>\(eval(gp, f, u) -> v, \pi\): where \(u\) is a random point the verifier chooses.</p><ul><li><p>\(f(x) - f(u) = (x - u)q(x)\) as u is a root of \(f(x) - f(u)\)</p></li><li><p>Compute \(q(x) \) and \(\pi = g^{q(\tau)}\) computed using gp elements.</p></li></ul></li></ul><p><strong>Step 4: Verify</strong></p><ul><li><p>\(verify(gp, com_f, u, v, \pi )\):</p><ul><li><p>where \(v = f(u)\)</p></li><li><p>The idea is to check the equation at point \(\tau\): \(g ^ {f(\tau) - f(u)} = g ^ {(\tau - u)q(\tau)}\)</p></li><li><p>Challenge: only know \(g ^ {\tau - u}\) and \(g ^ {q(\tau)}\)</p></li><li><p>Solution: pairings - \(e(com_f / g^v, g) = e(g^{\tau - u}, \pi)\)<br /> \(=e(g,g)^{f(\tau) - f(u)} = e(g,g)^{({\tau - u}). q(\tau)}\)</p></li></ul></li></ul><hr /><p>The key features of KZG polynomial commitments include:</p><ol><li><p><strong>Commitment</strong>: The prover commits to a polynomial by computing a commitment, a concise representation of the polynomial. This commitment is computed using a bilinear pairing over elliptic curves, which forms the cryptographic backbone of the scheme.</p></li><li><p><strong>Evaluation Proofs</strong>: The prover can provide proofs that a committed polynomial evaluates to a certain value at a specific point. These proofs are small in size and can be verified quickly.</p></li><li><p><strong>Hiding Property</strong>: The commitment does not reveal the polynomial itself, preserving the privacy of the data.</p></li><li><p><strong>Efficiency</strong>: KZG commitments are highly efficient in terms of both computation and communication overhead, making them suitable for use in blockchain networks where resources are limited.</p></li><li><p><strong>Applications</strong>: They are widely used in the construction of zk-SNARKs (zero-knowledge Succinct Non-interactive ARguments of Knowledge) and other zero-knowledge proof systems. In blockchain, they enable scalable and private transactions.</p></li></ol><hr /><h3 id="heading-achieving-zero-knowledge">Achieving Zero-Knowledge</h3><p>The plain KZG is not ZK. Eg. \(com_f = g ^{f(\tau)}\) is deterministic.</p><p>To achieve Zero Knowledge, we mask the commitment with randomizers.</p><ul><li><p>Commit: \(com_f = g^{f(\tau) + r\eta}\)</p></li><li><p>Evaluate: ( where \(r\) and \(r^{'}\) are both randomizers )</p><p> $$f(x) + ry - f(u) = (x - u)(q(x) + r^{'}y) + y(r - r^{'}(x - u))$$</p></li></ul><p>$$\pi = g ^ {q(\tau) + r^{'}\eta} , g^{r-r^{'} (\tau - u)}$$</p><hr /><p><strong>Other Variants Of KZG</strong></p><ol><li><strong>Batch opening: Single Polynomial:</strong></li></ol><ul><li><ul><li><p>Prover wants to prove \(f\) at \(u_1,\ldots, u_m \) for m<d.</p><ul><li><p>Steps:</p><ul><li><p>Extrapolate \(f(u_1),\ldots,f(u_m)\) to get \(h(x)\)</p></li><li><p>\(f(x) - h(x) = \prod_{i=1}^m(x-u_i)q(x)\). Why? Check the roots of polynomials on LHS.</p></li><li><p>\(\pi = g ^ {q(\tau)}\)</p></li><li><p>\(e ( com_f / g^ {h(\tau)}, g) = e(g^{\prod_{i=1}^m(\tau - u_i)}, \pi)\) where \(g^{h(r)}\) verifier can calculate using the global params.</p></li></ul></li></ul></li></ul></li></ul><ol><li><p><strong>Batch opening: Multiple Polynomials</strong></p><ul><li><p>Prover wants to prove \(f_i(u_{i,j}) = v_{i,j}\)</p></li><li><p>Steps:</p><ul><li><p>Extrapolate \(f_i(u_1),\ldots,f(u_m)\) to get \(h_i(x)\) for \(i \in [n]\)</p></li><li><p>\(f_i(x) - h_i(x) = \prod_{i=1}^m(x - u_m)q_i(m)\)</p></li><li><p>Combine all \(q_i(x)\) via a random linear combination.</p></li></ul></li></ul></li></ol><hr /><p>And that's it !!!</p><p>Lastly, follow me on social media: <a target="_blank" href="https://twitter.com/privacy_prophet">Twitter</a>, and <a target="_blank" href="https://www.linkedin.com/in/rachit-anand-srivastava/">LinkedIn</a> for new updates in the cryptography space.</p>]]><![CDATA[<hr /><p>A KZG polynomial commitment scheme allows a prover to commit to a polynomial so that they can later reveal and prove properties about the polynomial (like its value at certain points) without revealing the entire polynomial. This is particularly useful in scenarios where the polynomial represents some private data or computation.</p><p>Prequistics:</p><ul><li><p>How c<a target="_blank" href="https://www.di.ens.fr/~fouque/CoursBCS3.pdf">yclic groups</a> work in Numbers Theory</p></li><li><p>Discrete Log + <a target="_blank" href="https://www.nku.edu/~christensen/092mat483%20DH%20key%20exchange.pdf">Diffie-Hellman</a></p></li><li><p>Understanding of <a target="_blank" href="https://twitter.com/RacSri25/status/1738503612094148718">Bilinear Pairing</a></p></li></ul><hr /><h3 id="heading-kzg-poly-commit-scheme"><strong>KZG Poly-commit scheme</strong></h3><p>Before we start, lets see the elements we have:</p><ul><li><p>Bilinear Group p, G, g, G, e.</p></li><li><p>Univariate polynomials: \(F = F_p^{<=d}[X]\)</p></li></ul><p><strong>Step 1: Keygen</strong></p><ul><li><p>Sample random Fp</p></li><li><p>\(gp = (g, g^{\tau}, g^{\tau^2}, \ldots, g^{\tau^d})\)</p></li><li><p>delete \(\tau\) ( trusted setup )</p></li></ul><p><strong>Step 2: Commit</strong></p><ul><li><p>commit (gp, f) -> \(com_f\)</p></li><li><p>\(f(x) = f_0 + f_1x + f_2x ^2 + \ldots + f_dx^d\)</p></li><li><p>\(com_f = g^{f(\tau)}\)<br /> \(= g ^ {f_0 + f_1\tau + f_2\tau^2+ \ldots + f_d^{\tau ^ d}}\)</p><p> \(= (g)^{f_0}. (g^\tau)^{f_1}. (g^{\tau^2})^{f_2}. \ldots (g^{\tau^d})^{f_d}\)</p></li></ul><p><strong>Step 3: Proof</strong></p><ul><li><p>\(eval(gp, f, u) -> v, \pi\): where \(u\) is a random point the verifier chooses.</p><ul><li><p>\(f(x) - f(u) = (x - u)q(x)\) as u is a root of \(f(x) - f(u)\)</p></li><li><p>Compute \(q(x) \) and \(\pi = g^{q(\tau)}\) computed using gp elements.</p></li></ul></li></ul><p><strong>Step 4: Verify</strong></p><ul><li><p>\(verify(gp, com_f, u, v, \pi )\):</p><ul><li><p>where \(v = f(u)\)</p></li><li><p>The idea is to check the equation at point \(\tau\): \(g ^ {f(\tau) - f(u)} = g ^ {(\tau - u)q(\tau)}\)</p></li><li><p>Challenge: only know \(g ^ {\tau - u}\) and \(g ^ {q(\tau)}\)</p></li><li><p>Solution: pairings - \(e(com_f / g^v, g) = e(g^{\tau - u}, \pi)\)<br /> \(=e(g,g)^{f(\tau) - f(u)} = e(g,g)^{({\tau - u}). q(\tau)}\)</p></li></ul></li></ul><hr /><p>The key features of KZG polynomial commitments include:</p><ol><li><p><strong>Commitment</strong>: The prover commits to a polynomial by computing a commitment, a concise representation of the polynomial. This commitment is computed using a bilinear pairing over elliptic curves, which forms the cryptographic backbone of the scheme.</p></li><li><p><strong>Evaluation Proofs</strong>: The prover can provide proofs that a committed polynomial evaluates to a certain value at a specific point. These proofs are small in size and can be verified quickly.</p></li><li><p><strong>Hiding Property</strong>: The commitment does not reveal the polynomial itself, preserving the privacy of the data.</p></li><li><p><strong>Efficiency</strong>: KZG commitments are highly efficient in terms of both computation and communication overhead, making them suitable for use in blockchain networks where resources are limited.</p></li><li><p><strong>Applications</strong>: They are widely used in the construction of zk-SNARKs (zero-knowledge Succinct Non-interactive ARguments of Knowledge) and other zero-knowledge proof systems. In blockchain, they enable scalable and private transactions.</p></li></ol><hr /><h3 id="heading-achieving-zero-knowledge">Achieving Zero-Knowledge</h3><p>The plain KZG is not ZK. Eg. \(com_f = g ^{f(\tau)}\) is deterministic.</p><p>To achieve Zero Knowledge, we mask the commitment with randomizers.</p><ul><li><p>Commit: \(com_f = g^{f(\tau) + r\eta}\)</p></li><li><p>Evaluate: ( where \(r\) and \(r^{'}\) are both randomizers )</p><p> $$f(x) + ry - f(u) = (x - u)(q(x) + r^{'}y) + y(r - r^{'}(x - u))$$</p></li></ul><p>$$\pi = g ^ {q(\tau) + r^{'}\eta} , g^{r-r^{'} (\tau - u)}$$</p><hr /><p><strong>Other Variants Of KZG</strong></p><ol><li><strong>Batch opening: Single Polynomial:</strong></li></ol><ul><li><ul><li><p>Prover wants to prove \(f\) at \(u_1,\ldots, u_m \) for m<d.</p><ul><li><p>Steps:</p><ul><li><p>Extrapolate \(f(u_1),\ldots,f(u_m)\) to get \(h(x)\)</p></li><li><p>\(f(x) - h(x) = \prod_{i=1}^m(x-u_i)q(x)\). Why? Check the roots of polynomials on LHS.</p></li><li><p>\(\pi = g ^ {q(\tau)}\)</p></li><li><p>\(e ( com_f / g^ {h(\tau)}, g) = e(g^{\prod_{i=1}^m(\tau - u_i)}, \pi)\) where \(g^{h(r)}\) verifier can calculate using the global params.</p></li></ul></li></ul></li></ul></li></ul><ol><li><p><strong>Batch opening: Multiple Polynomials</strong></p><ul><li><p>Prover wants to prove \(f_i(u_{i,j}) = v_{i,j}\)</p></li><li><p>Steps:</p><ul><li><p>Extrapolate \(f_i(u_1),\ldots,f(u_m)\) to get \(h_i(x)\) for \(i \in [n]\)</p></li><li><p>\(f_i(x) - h_i(x) = \prod_{i=1}^m(x - u_m)q_i(m)\)</p></li><li><p>Combine all \(q_i(x)\) via a random linear combination.</p></li></ul></li></ul></li></ol><hr /><p>And that's it !!!</p><p>Lastly, follow me on social media: <a target="_blank" href="https://twitter.com/privacy_prophet">Twitter</a>, and <a target="_blank" href="https://www.linkedin.com/in/rachit-anand-srivastava/">LinkedIn</a> for new updates in the cryptography space.</p>]]>https://cdn.hashnode.com/res/hashnode/image/upload/v1703919453646/e91190d2-eb2b-4bb6-b33b-a732b2639bbf.jpeg