diff --git a/Cargo.toml b/Cargo.toml index 52c7e1edf..eed7f1be3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,16 +5,16 @@ exclude = ["ensure-no_std"] resolver = "2" [workspace.package] -version = "0.10.0" +version = "0.11.0" edition = "2021" license = "Apache-2.0" repository = "https://github.com/lambdaclass/lambdaworks" [workspace.dependencies] iai-callgrind = "0.3.1" -lambdaworks-crypto = { path = "./crypto", version = "0.10.0", default-features = false } -lambdaworks-gpu = { path = "./gpu", version = "0.10.0" } -lambdaworks-math = { path = "./math", version = "0.10.0", default-features = false } +lambdaworks-crypto = { path = "./crypto", version = "0.11.0", default-features = false } +lambdaworks-gpu = { path = "./gpu", version = "0.11.0" } +lambdaworks-math = { path = "./math", version = "0.11.0", default-features = false } stark-platinum-prover = { path = "./provers/stark" } lambdaworks-winterfell-adapter = { path = "./provers/winterfell_adapter"} lambdaworks-groth16 = { path = "./provers/groth16" } diff --git a/README.md b/README.md index befe19dba..1ed6a1206 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,21 @@ This library provides efficient implementation of cryptographic primitives used +## Examples - mini apps + +Below is a list of examples to understand lambdaworks and learn what you can build with the tools provided. + +- [Merkle Tree CLI](./examples/merkle-tree-cli/) +- [Proving Miden](./examples/prove-miden/) +- [Shamir's secret sharing](./examples/shamir_secret_sharing/) +- [BabySNARK](./examples/baby-snark/) +- [Pinocchio](./examples/pinocchio/) +- [Using Circom with lambdaworks's Groth16](./provers/groth16/circom-adapter/src/README.md) +- [Proving Fibonacci using Circom and lambdaworks](./examples/prove-verify-circom/circom_lambdaworks_tutorial.md) + +- You can use Circom to generate circuits and use lambdaworks's capabilities to prove the execution with [Groth16](./provers/groth16/README.md). +- You can use the [Stark prover](./provers/stark/src/) to define an algebraic intermediate representation (AIR) and prove the execution of a program + ## Why we built lambdaworks Zero-Knowledge and Validity Proofs have gained a lot of attention over the last few years. We strongly believe in this potential and that is why we decided to start working in this challenging ecosystem, where math, cryptography and distributed systems meet. The main barrier in the beginning was not the cryptography or math but the lack of good libraries which are performant and developer friendly. There are some exceptions, though, like gnark or halo2. Some have nice APIs and are easy to work with, but they are not written in Rust, and some are written in Rust but have poor programming and engineering practices. Most of them don't have support for CUDA, Metal and WebGPU or distributed FFT calculation using schedulers like Dask. @@ -41,14 +56,6 @@ Most of math and crypto crates supports no-std without allocation with `no-defau Both Math and Crypto support wasm with target `wasm32-unknown-unknown`. To see an example of how to use this to deploy a verifier in a browser, check the Cairo Prover wasm-pack verifier. -## Examples - mini apps - -- [Merkle Tree CLI](https://github.com/lambdaclass/lambdaworks/tree/main/examples/merkle-tree-cli) -- [Proving Miden](https://github.com/lambdaclass/lambdaworks/tree/main/examples/prove-miden) -- [Shamir's secret sharing](https://github.com/lambdaclass/lambdaworks/tree/main/examples/shamir_secret_sharing) -- [BabySNARK](https://github.com/lambdaclass/lambdaworks/tree/main/examples/baby-snark) -- [Pinocchio](https://github.com/lambdaclass/lambdaworks/tree/main/examples/pinocchio) - ## Exercises and Challenges - [lambdaworks exercises and challenges](https://github.com/lambdaclass/lambdaworks_exercises/tree/main) diff --git a/bootcamp/0_groth_16.jpg b/bootcamp/0_groth_16.jpg deleted file mode 100644 index 93e054488..000000000 Binary files a/bootcamp/0_groth_16.jpg and /dev/null differ diff --git a/bootcamp/0_snarks_starks.jpg b/bootcamp/0_snarks_starks.jpg deleted file mode 100644 index ea6beef07..000000000 Binary files a/bootcamp/0_snarks_starks.jpg and /dev/null differ diff --git a/bootcamp/0_starks_constraints.jpg b/bootcamp/0_starks_constraints.jpg deleted file mode 100644 index edba953fb..000000000 Binary files a/bootcamp/0_starks_constraints.jpg and /dev/null differ diff --git a/bootcamp/0_starks_general.jpg b/bootcamp/0_starks_general.jpg deleted file mode 100644 index 51d20acfb..000000000 Binary files a/bootcamp/0_starks_general.jpg and /dev/null differ diff --git a/bootcamp/0_starks_proof.jpg b/bootcamp/0_starks_proof.jpg deleted file mode 100644 index 2a75e1da2..000000000 Binary files a/bootcamp/0_starks_proof.jpg and /dev/null differ diff --git a/bootcamp/README.md b/bootcamp/README.md deleted file mode 100644 index 0f49e9469..000000000 --- a/bootcamp/README.md +++ /dev/null @@ -1,247 +0,0 @@ -# Lambda's Sparkling Water Bootcamp - Repo for challenges and learning path - -Public repository for exercises, challenges and all the needs of the Sparkling Water Bootcamp. - -## Week 1 - Forging your tools: Finite Fields - -This first week will be focused on the development of one of the building blocks of Cryptography: Finite Fields. - -### Recommended material: - -- [An introduction to mathematical cryptography](https://books.google.com.ar/books/about/An_Introduction_to_Mathematical_Cryptogr.html?id=BHuTQgAACAAJ&source=kp_book_description&redir_esc=y) - Chapter 1. -- [Finite Fields](https://www.youtube.com/watch?v=MAhmV_omOwA&list=PLFX2cij7c2PynTNWDBzmzaD6ij170ILbQ&index=8) -- [Constructing finite fields](https://www.youtube.com/watch?v=JPiXFn9WA5Y&list=PLFX2cij7c2PynTNWDBzmzaD6ij170ILbQ&index=6) -- [Cyclic groups](https://www.youtube.com/watch?v=UIhhs38IAGM&list=PLFX2cij7c2PynTNWDBzmzaD6ij170ILbQ&index=3) -- [Summary on Montgomery arithmetic](https://eprint.iacr.org/2017/1057.pdf) -- [Mersenne primes](https://eprint.iacr.org/2023/824.pdf) - -### Challenges: - -- [Implement Montgomery backend for 32 bit fields](https://github.com/lambdaclass/lambdaworks/issues/538). -- [Implement efficient Mersenne prime backend](https://github.com/lambdaclass/lambdaworks/issues/540). -- [Implement efficient backend for pseudo-Mersenne primes](https://github.com/lambdaclass/lambdaworks/issues/393). -- Compare specific field implementations with ordinary Montgomery arithmetic. - -### Cryptography content: - -- [Serious Cryptography](https://books.google.com.ar/books/about/Serious_Cryptography.html?id=1D-QEAAAQBAJ&source=kp_book_description&redir_esc=y), Chapters 9 & 10. - -### Exercises -- Implement naïve version of RSA. -- $7$ is a generator of the multiplicative group of $Z_p^\star$, where $p = 2^{64} - 2^{32} +1$. Find the generators for the $2^{32}$ roots of unity. Find generators for subgroups of order $2^{16} + 1$ and $257$. -- Define in your own words what is a group, a subgroup, a ring and a field. -- What are the applications of the Chinese Remainder Theorem in Cryptography? -- Find all the subgroups of the multiplicative group of $Z_{29}^\star$ - -## Supplementary Material -- [Polynomial Secret Sharing](https://decentralizedthoughts.github.io/2020-07-17-polynomial-secret-sharing-and-the-lagrange-basis/) -- [Polynomials over a Field](https://decentralizedthoughts.github.io/2020-07-17-the-marvels-of-polynomials-over-a-field/) -- [Fourier Transform](https://www.youtube.com/watch?v=spUNpyF58BY) -- [Fast Fourier Transform](https://www.youtube.com/watch?v=h7apO7q16V0) - -## Week 2 - Enter Elliptic Curves - -During the second week we'll continue with Finite Fields and begin with Elliptic Curves and dive deeper into Rust - -### Recommended material - -- [Moonmath Manual](https://leastauthority.com/community-matters/moonmath-manual/) - Chapter 5, until 5.3 -- [Programming Bitcoin](https://books.google.fr/books/about/Programming_Bitcoin.html?id=O2aHDwAAQBAJ&source=kp_book_description&redir_esc=y) - Chapters 2 & 3. -- [Introduction to Mathematical Cryptography](https://books.google.com.ar/books/about/An_Introduction_to_Mathematical_Cryptogr.html?id=BHuTQgAACAAJ&source=kp_book_description&redir_esc=y) - Chapter 5 until 5.5 -- [Serious Cryptography](https://books.google.com.ar/books/about/Serious_Cryptography.html?id=1D-QEAAAQBAJ&source=kp_book_description&redir_esc=y) - Chapters 11 & 12. -- [Pairings for Beginners](https://static1.squarespace.com/static/5fdbb09f31d71c1227082339/t/5ff394720493bd28278889c6/1609798774687/PairingsForBeginners.pdf) - Chapters 1 & 2 - -### Exercises - -- Define an elliptic curve element type. -- Implement the basic operations: addition and doubling. -- Implement scalar multiplication. -- Check that the point belongs to the correct subgroup. -- The BLS12-381 elliptic curve is given by the equation $y^2 = x^3 + 4$ and defined over $\mathbb{F}_p$ with p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab. The group generator is given by the point p1 = (0x04, 0x0a989badd40d6212b33cffc3f3763e9bc760f988c9926b26da9dd85e928483446346b8ed00e1de5d5ea93e354abe706c) and the cofactor is $h_1 = 0x396c8c005555e1568c00aaab0000aaab$. Find the generator $g$ of the subgroup of order -r = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001. -- Implement a naïve version of the Diffie - Hellman protocol -- Implement point compression and decompression to store elliptic curve points - -### Challenges - -- Special CTF challenge (will be revealed later) -- [Implement BN254](https://github.com/lambdaclass/lambdaworks/issues/548) -- Implement Secp256k1 -- Implement Ed25519 - -### Rust Workshop - -- [Aguante Rust](https://youtu.be/nYpMbjzb1t8?si=HNanyXYWcu1xDjG5) - -## Week 3: Polynomials - -### Recommended material - -- [Polynomials](https://www.youtube.com/watch?v=HiaJa3yhHTU&list=PLFX2cij7c2PynTNWDBzmzaD6ij170ILbQ&index=6) -- [Lagrange interpolation](https://www.youtube.com/watch?v=REnFOKo9gXs&list=PLFX2cij7c2PynTNWDBzmzaD6ij170ILbQ&index=10) -- [Lagrange interpolation and secret sharing](https://www.youtube.com/watch?v=3g4wZnhl4m8&list=PLFX2cij7c2PynTNWDBzmzaD6ij170ILbQ&index=3) -- [Moonmath](https://leastauthority.com/community-matters/moonmath-manual/) - Chapter 3.4 -- [Convolution polynomial rings - Introduction to Mathematical Cryptography](https://books.google.com.ar/books/about/An_Introduction_to_Mathematical_Cryptogr.html?id=BHuTQgAACAAJ&source=kp_book_description&redir_esc=y) - Chapter 6.9 - -### Supplementary - -- [Roots of unity and polynomials](https://www.youtube.com/watch?v=3KK5RuAgOpA&list=PLFX2cij7c2PynTNWDBzmzaD6ij170ILbQ&index=2) -- [Fast Fourier Transform](https://www.youtube.com/watch?v=toj_IoCQE-4) -- [FFT walkthrough](https://www.youtube.com/watch?v=Ty0JcR6Dvis) - -### Exercises - -- Define a polynomial type. -- Implement basic operations, such as addition, multiplication and evaluation. -- Implement Lagrange polynomial interpolation. -- Implement basic version of Shamir's secret sharing. - -### Issue - -- [Implement Stockham FFT](http://wwwa.pikara.ne.jp/okojisan/otfft-en/stockham1.html) - -## Week 4: STARKs - -### Recommended material - -- [STARKs by Sparkling Water Bootcamp](https://www.youtube.com/watch?v=cDzTm3clrEo) -- [Lambdaworks Docs](https://github.com/lambdaclass/lambdaworks/tree/main/docs/src/starks) -- [Stark 101](https://github.com/starkware-industries/stark101) -- [Constraints](https://blog.lambdaclass.com/periodic-constraints-and-recursion-in-zk-starks/) -- [Stark 101 - rs](https://github.com/lambdaclass/stark101-rs/) -- [Anatomy of a STARK](https://aszepieniec.github.io/stark-anatomy/) -- [BrainSTARK](https://aszepieniec.github.io/stark-brainfuck/) -- [A summary on FRI low degree testing](https://eprint.iacr.org/2022/1216) -- [STARKs by Risc0](https://dev.risczero.com/reference-docs/about-starks) - -### Exercises - -- Complete STARK-101 - -## Week 5: Symmetric encryption - -### Recommended material - -- [One time pad - Dan Boneh](https://www.youtube.com/watch?v=pQkyFJp2eUg&list=PL58C6Q25sEEHXvACYxiav_lC2DqSlC7Og&index=6) -- [Stream ciphers and pseudorandom generators - Dan Boneh](https://www.youtube.com/watch?v=ZSjTMSvp-eI&list=PL58C6Q25sEEHXvACYxiav_lC2DqSlC7Og&index=7) -- [Attacks - Dan Boneh](https://www.youtube.com/watch?v=Qm8fycVt5v8&list=PL58C6Q25sEEHXvACYxiav_lC2DqSlC7Og&index=8) -- [Semantic security - Dan Boneh](https://www.youtube.com/watch?v=6LFyXO58F4A&list=PL58C6Q25sEEHXvACYxiav_lC2DqSlC7Og&index=11) -- [Block ciphers - Dan Boneh](https://www.youtube.com/watch?v=dzoqxqfpZB4&list=PL58C6Q25sEEHXvACYxiav_lC2DqSlC7Og&index=35) -- [Serious Cryptography](https://books.google.com.ar/books/about/Serious_Cryptography.html?id=1D-QEAAAQBAJ&source=kp_book_description&redir_esc=y) - Chapters 3 - 5. - -### Supplementary material - -- [AES - NIST](https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.197.pdf) - -### Exercises - -- Implement AES round function - -### Side project - Multilinear polynomials - -- [Proofs, Args and ZK](https://people.cs.georgetown.edu/jthaler/ProofsArgsAndZK.pdf) - -### Mandatory task - -- Choose a project: STARKs, Sumcheck protocol or Groth16 (or propose a new project) - -### Additional resources for each project - -- STARKs: see week 4. -- [Groth16](https://eprint.iacr.org/2016/260.pdf) -- [DIZK - Groth 16](https://eprint.iacr.org/2018/691.pdf) -- [Multilinear polynomials and sumcheck protocol](https://people.cs.georgetown.edu/jthaler/ProofsArgsAndZK.pdf) - -#### Challenges - -- Implement a multilinear polynomial type with all the basic operations. - -## Week 6: Interactive proofs and SNARKs - -- [Moonmath](https://leastauthority.com/community-matters/moonmath-manual/) Chapters 6 - 8. -- [Proofs, Arguments and Zero Knowledge](https://people.cs.georgetown.edu/jthaler/ProofsArgsAndZK.pdf) Chapters 1 - 5. -- [Overview of modern SNARK constructions](https://www.youtube.com/watch?v=bGEXYpt3sj0) -- [Pinocchio protocol overview](https://www.zeroknowledgeblog.com/index.php/zk-snarks) -- [Pinocchio implementation](https://github.com/lambdaclass/pinocchio_lambda_vm) -- [SNARKs and STARKs](https://zkhack.dev/whiteboard/module-four/) - -### Additional material on some proof systems - -- [EthSTARK](https://github.com/starkware-libs/ethSTARK/tree/master) -- [EthSTARK - paper](https://eprint.iacr.org/2021/582) -- [STARK paper](https://eprint.iacr.org/2018/046.pdf) -- [DEEP FRI](https://eprint.iacr.org/2019/336) -- [Proximity gaps](https://eprint.iacr.org/2020/654) -- [STARKs by Eli Ben-Sasson I](https://www.youtube.com/watch?v=9VuZvdxFZQo) -- [STARKs by Eli Ben-Sasson II](https://www.youtube.com/watch?v=L7tZeO8ihcQ) - -## Week 7: Plonk - -- [Plonk](https://eprint.iacr.org/2019/953) -- [Custom gates](https://zkhack.dev/whiteboard/module-five/) -- [Plonk by hand](https://research.metastate.dev/plonk-by-hand-part-1/) -- [Plonk docs in Lambdaworks](https://github.com/lambdaclass/lambdaworks/tree/main/docs/src/plonk) - -## Week 8: Lookup arguments - -- [Plookup](https://eprint.iacr.org/2020/315.pdf) -- [LogUp and GKR](https://eprint.iacr.org/2023/1284.pdf) -- [Neptune - Permutation Argument](https://neptune.cash/learn/tvm-cross-table-args/) -- [Randomized AIR with preprocessing](https://hackmd.io/@aztec-network/plonk-arithmetiization-air) -- [PlonkUp](https://eprint.iacr.org/2022/086.pdf) -- [Lookups by Ingonyama](https://medium.com/@ingonyama/a-brief-history-of-lookup-arguments-a4eeeeca2749) -- [LogUp](https://eprint.iacr.org/2022/1530.pdf) -- [Lookups - Halo2](https://zcash.github.io/halo2/design/proving-system/lookup.html) - -## Week 9: Signatures - -- [BLS signatures](https://www.ietf.org/archive/id/draft-irtf-cfrg-bls-signature-05.html#name-introduction-2) -- [Real World Cryptography](https://books.google.com.ar/books/about/Real_World_Cryptography.html?id=Qd5CEAAAQBAJ&source=kp_book_description&redir_esc=y) Chapter 7 -- [ECDSA](https://www.rfc-editor.org/rfc/rfc6605.txt) -- [RSA Signature](https://www.ietf.org/rfc/rfc8017.html#section-5.2) - -## Week 10: Folding schemes - -- [Nova by Justin Drake](https://zkhack.dev/whiteboard/module-fourteen/) -- [Nova](https://eprint.iacr.org/2021/370) -- [SuperNova](https://eprint.iacr.org/2022/1758) -- [ProtoStar](https://eprint.iacr.org/2023/620) -- [ProtoGalaxy](https://eprint.iacr.org/2023/1106) - -## Projects - -- Implement IPA commitment scheme -- Implement Jacobian coordinates for Elliptic Curves -- Benchmark elliptic curve operations -- Add improvements to fixed base scalar multiplication in Elliptic Curves -- Add BN254 elliptic curve -- Implement Pasta curves -- Implement Lookup arguments for Plonk (Plookup) -- Sumcheck protocol -- Benchmark and optimize multilinear polynomial operations -- Import circuits from gnark or circom to use with Groth16 backend - -### Links to repos with solutions to the exercises -- [Naïve ECC](https://github.com/saitunc/naive_ecc) -- [Crypto](https://github.com/irfanbozkurt/crypto) -- [Naïve RSA](https://github.com/WiseMrMusa/rsa-naive) -- [Naïve RSA](https://github.com/Elvis339/naive_rsa) -- [Exercises from weeks 1 & 2](https://github.com/ArpitxGit/sparkling_water_bootcamp/tree/main) -- [Programming bitcoin EC](https://github.com/Elvis339/rbtc) -- [Shamir secret sharing](https://github.com/cliraa/shamir_secret_sharing) -- [Several exercises](https://github.com/ArpitxGit/sparkling_water_bootcamp/tree/main) - -### Intended Roadmap - -- Finite Fields -- Elliptic Curves -- Polynomials -- Extension fields -- Pairings -- Public key encryption -- Symmetric encryption -- Hash functions -- Signatures -- Authenticated encryption -- SNARKs -- STARKs diff --git a/bootcamp/WhiteboardDiscussion.png b/bootcamp/WhiteboardDiscussion.png deleted file mode 100644 index 3a912e992..000000000 Binary files a/bootcamp/WhiteboardDiscussion.png and /dev/null differ diff --git a/bootcamp/WhiteboardPolynomials.png b/bootcamp/WhiteboardPolynomials.png deleted file mode 100644 index 8f1916ff6..000000000 Binary files a/bootcamp/WhiteboardPolynomials.png and /dev/null differ diff --git a/bootcamp/WhiteboardRSA.png b/bootcamp/WhiteboardRSA.png deleted file mode 100644 index b60c48f19..000000000 Binary files a/bootcamp/WhiteboardRSA.png and /dev/null differ diff --git a/bootcamp/learning_resources.md b/bootcamp/learning_resources.md deleted file mode 100644 index 6dcc37302..000000000 --- a/bootcamp/learning_resources.md +++ /dev/null @@ -1,94 +0,0 @@ -# Roadmap - -## 1. Fundamentals of Cryptography and Mathematics - -### Key Concepts: -- What is cryptography. - - [Cryptography I - Dan Boneh: Videos 1, 2 and 3.](https://youtube.com/playlist?list=PL58C6Q25sEEHXvACYxiav_lC2DqSlC7Og&si=M8qItPK_HUy3WR7b) -- Symmetric ciphers: the one time pad and stream ciphers. - - [Cryptography I - Dan Boneh: Videos 6, 7, 8 and 9.](https://www.youtube.com/watch?v=pQkyFJp2eUg&list=PL58C6Q25sEEHXvACYxiav_lC2DqSlC7Og&index=7) -- Exclusive or. - - [Crypto101: Chapter 5](https://www.crypto101.io/) -- Block ciphers. - - [Crypto101: Chapter 6](https://www.crypto101.io/) -- Number Theory: Modular arithmetic and prime numbers - - [An Introduction to Mathematical Cryptography: Chapter 1](https://link.springer.com/book/10.1007/978-0-387-77993-5) -- Public key cryptography: Discrete logarithms and Diffie-Hellman. - - [An Introduction to Mathematical Cryptography: Chapters from 2.1 to 2.4](https://link.springer.com/book/10.1007/978-0-387-77993-5) -- Abstract Algebra: Group Theory. - - [An Introduction to Mathematical Cryptography: Chapter 2.5](https://link.springer.com/book/10.1007/978-0-387-77993-5) -- Abstract Algebra: Rings and finite fields. - - [An Introduction to Mathematical Cryptography: Chapter 2.10](https://link.springer.com/book/10.1007/978-0-387-77993-5) -- Finite Field Theory: Operations and applications in cryptography. -- RSA. - - [How RSA Works](https://www.youtube.com/watch?v=qph77bTKJTM&t=1060s) -- Basic Cryptography: hash functions, and digital signatures. - - [What are Digital Signatures?](https://www.youtube.com/watch?v=s22eJ1eVLTU) - - -## 2. Theory and Application of Elliptic Curves - -### Key Concepts: -- Elliptic Curves over Finite Fields: Definition and operations. - - [Elliptic Curves - Computerphile](https://www.youtube.com/watch?v=NF1pwjL9-DE) -- Elliptic Curve Cryptography (ECC): Applications and advantages. -- Points and Operations on Elliptic Curves: Point addition and scalar multiplication. -- Pairing - - [Pairing for Beginners](https://static1.squarespace.com/static/5fdbb09f31d71c1227082339/t/5ff394720493bd28278889c6/1609798774687/PairingsForBeginners.pdf) Chapter 1 & 2 - - -## 3. Introduction to Zero-Knowledge Proofs - -### Key Concepts: -- Definition and Properties: Completeness, soundness, and zero-knowledge. - - [Computer Scientist Explains One Concept in 5 Levels of Difficulty](https://www.youtube.com/watch?v=fOGdb1CTu5c) - - -## 4. SNARKs (Succinct Non-Interactive Arguments of Knowledge) - -### Key Concepts: -- Introduction to SNARKs: Concepts and uses. - - [What is a zk-SNARK?](https://www.youtube.com/watch?v=gcKCW7CNu_M) -- Components of SNARKs: Arithmetization, constraint systems, and quadratic arithmetic programs (QAP). - - [SNARKs vs. STARKs](https://zkhack.dev/whiteboard/module-four/) - -Related Papers -[Pinocchio: Nearly Practical Verifiable Computation](https://eprint.iacr.org/2013/279.pdf) - -## 5. STARKs (Scalable Transparent ARguments of Knowledge) - -### Key Concepts: -- Fundamentals of STARKs: Differences with SNARKs and applications. - - [ZK-STARKs: Scalable Transparent Zero Knowledge Proofs](https://eprint.iacr.org/2018/046.pdf) - - [Diving into Starks](https://www.youtube.com/watch?v=cDzTm3clrEo) - - Multipart tutorial by Vitalik Buterin [I](https://vitalik-eth-limo.translate.goog/general/2017/11/09/starks_part_1.html?_x_tr_sl=en&_x_tr_tl=es&_x_tr_hl=es-419&_x_tr_pto=sc) [II](https://vitalik.eth.limo/general/2017/11/22/starks_part_2.html) & [III](https://vitalik.eth.limo/general/2018/07/21/starks_part_3.html) -- Protocols and Security in STARKs: FRI (Fast Reed-Solomon IOPP) and AIR (Algebraic Intermediate Representation) - -# Other learning resources - -This list contains papers, videos, books, and links to resources that we found useful to learn about zero-knowledge proofs and cryptography. The list is by no means exhaustive and we will be updating it. - -## Videos - -- [ZK Whiteboard](https://zkhack.dev/whiteboard/) -- [All the MATH you need to understand SNARKs and STARKs](https://www.youtube.com/playlist?list=PLFX2cij7c2PynTNWDBzmzaD6ij170ILbQ) - -## Books - -- [Thaler - Proofs, Arguments and Zero-knowledge](https://people.cs.georgetown.edu/jthaler/ProofsArgsAndZK.pdf) - -## Papers - -- [Pinocchio](https://eprint.iacr.org/2013/279.pdf) -- [Plonk](https://eprint.iacr.org/2019/953.pdf) -- [Square Span Programs with Applications to Succinct NIZK Arguments](https://eprint.iacr.org/2014/718.pdf) -- [KZG Polynomial Commitment Scheme](https://cacr.uwaterloo.ca/techreports/2010/cacr2010-10.pdf) - -## Courses -- [Modern Zero Knowledge Cryptography - MIT IAP 2023](https://zkiap.com) -- [Zero Knowledge Proofs](https://zk-learning.org) Dan Boneh, Shafi Goldwasser, Dawn Song, Justin Thaler & Yupeng Zhang - -## Links - -- [STARK-101 - Python](https://starkware.co/stark-101/) -- [BabySnark Protocol](https://github.com/lambdaclass/lambdaworks/tree/main/examples/baby-snark) diff --git a/bootcamp/sparkling_water_0b10.md b/bootcamp/sparkling_water_0b10.md deleted file mode 100644 index 03d7d1a64..000000000 --- a/bootcamp/sparkling_water_0b10.md +++ /dev/null @@ -1,115 +0,0 @@ -# Sparkling Water Bootcamp in Cryptography 01b0 - -## About - -This is the second edition of the Sparkling Water Bootcamp in Cryptography. It is intended to provide an overview of proof systems and some topic in cryptography. It has a duration of 8 weeks of lectures, exercises and coding practice. - -## Intended roadmap - -The roadmap is dynamic and may change as the bootcamp progresses. - -### Week 1 - Fundamentals I - -- Math preliminaries: groups, rings and fields -- Finite fields -- RSA cryptosystem -- Univariate polynomials over finite fields -- Shamir secret sharing -- Examples of commonly used finite fields in Cryptography - -#### Learning materials: - -- [An introduction to mathematical cryptography](https://books.google.com.ar/books/about/An_Introduction_to_Mathematical_Cryptogr.html?id=BHuTQgAACAAJ&source=kp_book_description&redir_esc=y) - Chapter 1 & 3. -- [The MiniGoldilocks prime](https://xn--2-umb.com/22/goldilocks/) -- [Summary on Montgomery arithmetic](https://eprint.iacr.org/2017/1057.pdf) -- [Mersenne primes](https://eprint.iacr.org/2023/824.pdf) -- [Binary fields by Vitalik](https://vitalik.eth.limo/general/2024/04/29/binius.html) -- [Finite Fields](https://www.youtube.com/watch?v=MAhmV_omOwA&list=PLFX2cij7c2PynTNWDBzmzaD6ij170ILbQ&index=8) -- [Constructing finite fields](https://www.youtube.com/watch?v=JPiXFn9WA5Y&list=PLFX2cij7c2PynTNWDBzmzaD6ij170ILbQ&index=6) -- [Cyclic groups](https://www.youtube.com/watch?v=UIhhs38IAGM&list=PLFX2cij7c2PynTNWDBzmzaD6ij170ILbQ&index=3) - -#### Motivation - -- [Mersenne primes' performance in STWO](https://www.youtube.com/watch?v=_eha0QqAbIA) -- [Circle STARKs](https://www.youtube.com/watch?v=NAhLYMysSdk&list=PLj80z0cJm8QFy2umHqu77a8dbZSqpSH54&index=17) -- [Binius](https://www.youtube.com/watch?v=rgRWcWOll0w&list=PLj80z0cJm8QFy2umHqu77a8dbZSqpSH54&index=4) - -#### Exercises - -- Find all the multiplicative subgroups of the multiplicative group of integers modulo 17. -- Define a field in lambdaworks. -- Write simple code using the different field operations in lambdaworks. -- Implement a basic version of Shamir secret sharing. - -### Week 2 - Fundamentals II - -- Elliptic curves over finite fields. -- Diffie-Hellman key exchange -- Small subgroup attacks -- Collision-resistant hash functions -- Merkle trees -- KZG polynomial commitment scheme - -#### Recommended material - -- [Moonmath Manual](https://leastauthority.com/community-matters/moonmath-manual/) - Chapter 5, until 5.3 -- [Programming Bitcoin](https://books.google.fr/books/about/Programming_Bitcoin.html?id=O2aHDwAAQBAJ&source=kp_book_description&redir_esc=y) - Chapters 2 & 3. -- [Introduction to Mathematical Cryptography](https://books.google.com.ar/books/about/An_Introduction_to_Mathematical_Cryptogr.html?id=BHuTQgAACAAJ&source=kp_book_description&redir_esc=y) - Chapter 5 until 5.5 -- [Serious Cryptography](https://books.google.com.ar/books/about/Serious_Cryptography.html?id=1D-QEAAAQBAJ&source=kp_book_description&redir_esc=y) - Chapters 11 & 12. -- [KZG basics and application to Mina Bridge](https://blog.lambdaclass.com/mina-to-ethereum-bridge/) - -### Week 3 - BabySNARK - -- SNARKs - fundamentals. -- Elliptic curve pairings. -- KZG commitment scheme. -- Square span programs -- BabySNARK -- Field extensions - -#### Reading material - -- [Pairings for beginners](https://static1.squarespace.com/static/5fdbb09f31d71c1227082339/t/5ff394720493bd28278889c6/1609798774687/PairingsForBeginners.pdf) -- [BabySNARK](https://github.com/lambdaclass/lambdaworks/tree/main/examples/baby-snark) - -#### Exercises - -- Create a simple boolean circuit, generate the square constraint system and generate a proof of execution and verify it. -- Explain what polynomial commitments are and how KZG commitment works. -- Create a false proof if you know the value of the parameter beta. -- Explain the drawbacks of a trusted setup. -- Solve [exercise 2 in lambdaworks](https://github.com/lambdaclass/lambdaworks/tree/main/exercises/challenge_2) - -### Week 4 - STARKs and the FRI protocol - -- Algebraic intermediate representation (AIR) -- FRI protocol -- STARK protocol -- Comparison between virtual machines - -### Week 5 - Groth 16 - -- R1CS -- Quadratic arithmetic programs -- Groth 16 - -### Week 6 - Plonk - -- Plonkish arithmetization -- Permutation checks -- Different flavors of Plonk - -### Week 7 - Lookup arguments and folding schemes - -- Lookup arguments -- Plookup -- LogUp -- Folding schemes -- Proof-carrying data (PCD) -- Nova - -### Week 8 - More advanced topics - -TBD - -## Challenges and exercises diff --git a/crypto/README.md b/crypto/README.md index 8633f5dde..f637d5e87 100644 --- a/crypto/README.md +++ b/crypto/README.md @@ -8,7 +8,7 @@ Add this to your `Cargo.toml` ```toml [dependencies] -lambdaworks-crypto = "0.8.0" +lambdaworks-crypto = "0.11.0" ``` ## Structure diff --git a/examples/README.md b/examples/README.md index 73cf271e7..c55d1094b 100644 --- a/examples/README.md +++ b/examples/README.md @@ -10,6 +10,7 @@ Below is a list of all lambdaworks examples in the folder: - [Proving Miden using lambdaworks STARK Platinum prover](https://github.com/lambdaclass/lambdaworks/tree/main/examples/prove-miden): Executes a Miden vm Fibonacci program, gets the execution trace and generates a proof (and verifies it) using STARK Platinum. - [BabySNARK](https://github.com/lambdaclass/lambdaworks/tree/main/examples/baby-snark): a simple SNARK to start learning the basics of elliptic curve-based proof systems. - [Pinocchio](https://github.com/lambdaclass/lambdaworks/tree/main/examples/pinocchio): the first practical SNARK. A good starting point to start learning about zero-knowledge proofs. +- [Circom to Lambdaworks](https://github.com/lambdaclass/lambdaworks/tree/main/examples/prove-verify-circom/circom_lambdaworks_tutorial.md): A walkthrough to create a circuit in Circom, generate a proof, and verify it using with Groth16 using Lambdaworks. You can also check [lambdaworks exercises](https://github.com/lambdaclass/lambdaworks/tree/main/exercises) to learn more. diff --git a/examples/prove-verify-circom/circom_lambdaworks_tutorial.md b/examples/prove-verify-circom/circom_lambdaworks_tutorial.md new file mode 100644 index 000000000..3708f039c --- /dev/null +++ b/examples/prove-verify-circom/circom_lambdaworks_tutorial.md @@ -0,0 +1,284 @@ +# Groth16 from Circom to Lambdaworks tutorial + +In this small tutorial, we will perform all the steps needed to prove a computation using Groth16. + +As said above, our goal is to prove a computation. In this case we want to prove the Fibonacci sequence. + +If you don´t know what Groth16 does, you can read our [post](https://blog.lambdaclass.com/groth16/). + +Let's begin by creating the circuit and the input for the program using [Circom2](https://docs.circom.io/getting-started/installation/). + + +> [!IMPORTANT] +> This tutorial will work within `lambdaworks/provers/groth16` to make it easier to follow along. If you prefer, you can create a new rust project, but you will need to import all the crates from the library. + +As we want to cover all the steps from creating a program to prove it using Groth 16, the first thing to do is to create the folder for our program. + +Create a file named `fibonacci` inside `test_files`.This is where our program will be. + +The directory should look like this: +``` +provers/ +└── groth16/ + ├── arkworks-adapter/ + ├── circom-adapter/ + ├── src/ + │ ├── integration_tests.rs + │ ├── lib.rs + │ └── README.md + ├── test_files/ + │ ├── fibonacci/ (or any other name) + │ ├── poseidon/ + │ └── vitalik_example/ + └── Cargo.toml +``` + +### Understanding the Fibonacci Circuit +The Fibonacci sequence is defined as follows: + +$F(0) = a \quad \text{(starting value 1)}$ + +$F(1) = b \quad \text{(starting value 2)}$ + +$F(n) = F(n-1) + F(n-2) \quad \text{for } n \geq 2$ + +In our circuit, we start with two initial numbers, +a and b, and compute the nth Fibonacci number. The circuit checks if the output is correctly computed based on these inputs + +```circom +pragma circom 2.0.0; + +// Fibonacci with custom starting numbers +template Fibonacci(n) { + assert(n >= 2); + signal input in[2]; + signal output out; + + signal fib[n+1]; + fib[0] <== in[0]; + fib[1] <== in[1]; + for (var i = 2; i <= n; i++) { + fib[i] <== fib[i-2] + fib[i-1]; + } + + out <== fib[n]; +} + +// Instantiate Fibonacci as the main circuit with a specific n value +component main = Fibonacci(10); +``` +In our case this will be named `fibonacci.circom`. + + +In this circuit: +`in[0]` and `in[1]` are the initial values a and b + +`out` will hold F(10), the 10th number in the Fibonacci Sequence + +### Creating inputs for the Circuit + +Once we have defined the circuit, the next step is to provide the inputs needed for the proving process. In this case, our inputs consists of the starting numbers for the Fibonacci sequence. + +As we want to use F(0) = 1 and F(1) = 1, the input JSON file will look like this; + +```json +{ + "in": [1, 1] +} +``` + +The file should be named `input.json`. + +Inside the same directory where those files are run + +```bash +circom fibonacci.circom --r1cs --wasm -p bls12381 +``` +Here +`r1cs` generates the R1CS file +`wasm` generates the WebAssembly code +`-p bls12381` sets the elliptic curve. + +That will create a `fibonacci_js` directory and a `fibonacci_r1cs `file + +> [!WARNING] +> Do not skip the -p bls12381 flag, as this is the only field supported by the adapter right now. If not specified, the default is bn128 and the proving will not be possible + + +To compute the `witness` execute + +``` +node fibonacci_js/generate_witness.js fibonacci_js/fibonacci.wasm input.json witness.wtns +``` + +As our program inputs are json files we need to export the witness and r1cs files into the same format. To do that run: + +```bash +snarkjs wtns export json witness.wtns +``` +and +```bash +snarkjs r1cs export json fibonacci.r1cs fibonacci.r1cs.json +``` + +Now we have `witness.json` and `fibonacci.r1cs.json` + +The folder should look like: + + +``` +fibonacci/ +├── fibonacci_js/ +├── fibonacci.circom +├── fibonacci.r1cs +├── fibonacci.r1cs.json +├── input.json +├── witness.json +└── witness.wtns +``` + +We only need `fibonacci.r1cs.json` and `witness.json` so, if you want, you can delete the unnecessary files by running: +``` +rm -rf fibonacci_js fibonacci.circom fibonacci.r1cs witness.wtns input.json +``` + +All at once, you can copy-paste the following to the terminal in the same directory as your circuit + +```bash +circom fibonacci.circom --r1cs --wasm -p bls12381; +node fibonacci_js/generate_witness.js fibonacci_js/fibonacci.wasm input.json witness.wtns; +snarkjs wtns export json witness.wtns witness.json; +snarkjs r1cs export json fibonacci.r1cs fibonacci.r1cs.json; +rm -rf fibonacci_js fibonacci.circom fibonacci.r1cs witness.wtns input.json; # Delete unnecessary artifacts +``` + +## Using Lambdaworks Circom Adapter + +As mentioned in the blog post, the main goal of Groth16 (or any other prover) is to prove computation. +The function `circom_to_lambda` will take the `fibonacci.r1cs.json` and the `witness.json` data and translate it into the format needed for Lambdaworks. + +The `circom_to_lambda` function is responsible for converting the R1CS constraints and witness generated by Circom into a format that Lambdaworks can use to construct a Quadratic Arithmetic Program (QAP). The QAP representation is essential for Groth16 proof generation, as it represents the arithmetic circuit in terms of polynomials. + +### How `circom_to_lambda` works + +The function `circom_to_lambda` does the following: + +1. Parse JSON Data: It parses the R1CS constraints and witness files from JSON format into Rust data structures using serde_json. + +2. Build LRO Matrices: It extracts the L,R,O matrices which represent the variables involved in each constraint. + +3. Adjust Witness: It adjusts the order of inputs and outputs in the witness to match Lambdaworks's format. Circom and Lambdaworks have different conventions for witness ordering, so this adjustment ensures compatibility. + +4. Construct QAP: It uses the L,R,O matrices to build a Quadratic Arithmetic Program (QAP). This QAP is used in the Groth16 proving process. + + +## Generating the proof with Groth16 + +After converting the data using `circom_to_lambda`, you can use the resulting QAP and witness to generate a Groth16 proof. This process involves two key steps: creating the proving key and then using it to generate the proof based on the QAP. Here’s a detailed explanation of the process: + +We will put all together inside `integration_test`. + +Step 1: Read the R1CS and Witness Files. +First, you need to read the R1CS (Rank-1 Constraint System) file and the witness file generated by Circom. + +```rust +let test_dir = format!("{TEST_DIR}/fibonacci"); + +let (qap, w) = circom_to_lambda( + &fs::read_to_string(format!("{test_dir}/fibonacci.r1cs.json")) + .expect("Error reading the R1CS file"), + &fs::read_to_string(format!("{test_dir}/witness.json")) + .expect("Error reading the witness file"), +); +``` + +`test_dir`: Specifies the directory where the R1CS and witness files are stored. This allows for easy organization and reuse of test data. + +`circom_to_lambda` : Converts the content of the R1CS and witness files into a format compatible with Lambdaworks. This function parses the JSON content and produces a QAP and a corresponding witness vector. + +Step 2: Generate the Proving and Verifying Keys: + +```rust +let (pk, vk) = setup(&qap); +``` + +- Proving Key (pk): Used to create a proof that the computation was carried out correctly. +- Verifying Key (vk): Used to verify the validity of the proof, ensuring that it matches the expected computation. + + +Step 3: Generate the Proof + +With the proving key in hand, you can generate a proof using the `Prover::prove` function: + +```rust +let proof = Prover::prove(&w, &qap, &pk); +``` +`Prover::prove`: This function takes the witness, QAP, and proving key to generate a proof that certifies the correctness of the computation represented by the circuit. + +Step 4: Verify the Proof +To ensure the proof is valid, use the `verify` function: + +```rust +let accept = verify(&vk, &proof, &w[..qap.num_of_public_inputs]); +``` + +`verify`: Takes the verifying key, proof, and public inputs to check if the proof is valid. It ensures that the proof matches the expected output of the computation without revealing the private inputs. +Public Inputs: Extracted from the witness slice `&w[..qap.num_of_public_inputs]` to provide the data that is publicly visible during verification. + +Step 5: Assert the Verification Result + +Finally, check if the verification was successful + +```rust +assert!(accept, "Proof verification failed."); +``` +This line ensures that the proof verification returns `true`. If the verification fails, the test will produce an error, which helps catch issues during development. + + +#### Putting altogether +Inside `integration_tests.rs` add + +```rust +#[test] +fn fibonacci_verify() { + // Define the directory containing the R1CS and witness files + let test_dir = format!("{TEST_DIR}/fibonacci"); + + // Step 1: Parse R1CS and witness from JSON files + let (qap, w) = circom_to_lambda( + &fs::read_to_string(format!("{test_dir}/fibonacci.r1cs.json")) + .expect("Error reading the R1CS file"), + &fs::read_to_string(format!("{test_dir}/witness.json")) + .expect("Error reading the witness file"), + ); + + // Step 2: Generate the proving and verifying keys using the QAP + let (pk, vk) = setup(&qap); + + // Step 3: Generate the proof using the proving key and witness + let proof = Prover::prove(&w, &qap, &pk); + + // Step 4: Verify the proof using the verifying key and public inputs + let accept = verify(&vk, &proof, &w[..qap.num_of_public_inputs]); + + // Step 5: Assert that the verification is successful + assert!(accept, "Proof verification failed."); + + println!("Proof verification succeeded. All steps completed."); +} +``` +and run: + +```rust +cargo test -- --test fibonacci_verify +``` +If everything is set up correctly, you should see a success message confirming that the proof has been verified: + +```bah +Proof verification succeeded. All steps completed. +``` + +## Summary + +Congratulations! You have successfully set up a Groth16 proof for a Fibonacci circuit using Circom and Lambdaworks. This tutorial walked you through the entire process—from defining the circuit in Circom, generating the R1CS and witness, converting the data for use in Rust, and finally, creating and verifying the proof using Lambdaworks. + diff --git a/math/README.md b/math/README.md index e5e0830a0..bacdcc371 100644 --- a/math/README.md +++ b/math/README.md @@ -8,7 +8,7 @@ Add this to your `Cargo.toml` ```toml [dependencies] -lambdaworks-math = "0.8.0" +lambdaworks-math = "0.11.0" ``` ## Structure diff --git a/math/benches/criterion_elliptic_curve.rs b/math/benches/criterion_elliptic_curve.rs index 3812d74c3..f9bffc781 100644 --- a/math/benches/criterion_elliptic_curve.rs +++ b/math/benches/criterion_elliptic_curve.rs @@ -10,6 +10,6 @@ use elliptic_curves::{ criterion_group!( name = elliptic_curve_benches; config = Criterion::default().with_profiler(PProfProfiler::new(100, Output::Flamegraph(None))); - targets = bn_254_elliptic_curve_benchmarks,bls12_377_elliptic_curve_benchmarks,bls12_381_elliptic_curve_benchmarks + targets = bn_254_elliptic_curve_benchmarks,bls12_381_elliptic_curve_benchmarks,bls12_377_elliptic_curve_benchmarks ); criterion_main!(elliptic_curve_benches); diff --git a/math/benches/elliptic_curves/bls12_381.rs b/math/benches/elliptic_curves/bls12_381.rs index d33e8d2ca..452480697 100644 --- a/math/benches/elliptic_curves/bls12_381.rs +++ b/math/benches/elliptic_curves/bls12_381.rs @@ -4,7 +4,9 @@ use lambdaworks_math::{ elliptic_curve::{ short_weierstrass::{ curves::bls12_381::{ - curve::BLS12381Curve, pairing::BLS12381AtePairing, twist::BLS12381TwistCurve, + curve::BLS12381Curve, + pairing::{final_exponentiation, miller, BLS12381AtePairing}, + twist::BLS12381TwistCurve, }, traits::Compress, }, @@ -24,6 +26,8 @@ pub fn bls12_381_elliptic_curve_benchmarks(c: &mut Criterion) { let a_g2 = BLS12381TwistCurve::generator(); let b_g2 = BLS12381TwistCurve::generator(); + let miller_loop_output = miller(&a_g2, &a_g1); + let mut group = c.benchmark_group("BLS12-381 Ops"); group.significance_level(0.1).sample_size(10000); group.throughput(criterion::Throughput::Elements(1)); @@ -93,4 +97,14 @@ pub fn bls12_381_elliptic_curve_benchmarks(c: &mut Criterion) { )) }); }); + + // Miller + group.bench_function("Miller", |bencher| { + bencher.iter(|| black_box(miller(black_box(&a_g2), black_box(&a_g1)))) + }); + + // Final Exponentiation Optimized + group.bench_function("Final Exponentiation", |bencher| { + bencher.iter(|| black_box(final_exponentiation(black_box(&miller_loop_output)))) + }); } diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/field_extension.rs b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/field_extension.rs index ecf87e2d9..cb41e5aeb 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/field_extension.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/field_extension.rs @@ -21,6 +21,7 @@ impl IsModulus for BLS12381FieldModulus { } pub type BLS12381PrimeField = MontgomeryBackendPrimeField; +type Fp2E = FieldElement; ////////////////// #[derive(Clone, Debug)] @@ -199,6 +200,16 @@ impl HasCubicNonResidue for LevelTwoResidue { } } +impl HasQuadraticNonResidue for LevelTwoResidue { + fn residue() -> FieldElement { + FieldElement::new([ + FieldElement::new(U384::from("1")), + FieldElement::new(U384::from("1")), + ]) + } +} +pub type Degree4ExtensionField = QuadraticExtensionField; + pub type Degree6ExtensionField = CubicExtensionField; #[derive(Debug, Clone)] @@ -284,6 +295,14 @@ impl FieldElement { } } +/// Computes the multiplication of an element of fp2 by the level two non-residue 9+u. +pub fn mul_fp2_by_nonresidue(a: &Fp2E) -> Fp2E { + // (c0 + c1 * u) * (1 + u) = (c0 - c1) + (c1 + c0) * u + let c0 = &a.value()[0] - &a.value()[1]; // c0 - c1 + let c1 = &a.value()[0] + &a.value()[1]; // c1 + c0 + + Fp2E::new([c0, c1]) +} #[cfg(test)] mod tests { use crate::elliptic_curve::{ diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs index b0c0c6cd2..8107f69ec 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs @@ -1,11 +1,12 @@ -use super::curve::MILLER_LOOP_CONSTANT; use super::{ curve::BLS12381Curve, - field_extension::{BLS12381PrimeField, Degree12ExtensionField, Degree2ExtensionField}, + field_extension::{ + mul_fp2_by_nonresidue, BLS12381PrimeField, Degree12ExtensionField, Degree2ExtensionField, + Degree4ExtensionField, + }, twist::BLS12381TwistCurve, }; use crate::{cyclic_group::IsGroup, elliptic_curve::traits::IsPairing, errors::PairingError}; - use crate::{ elliptic_curve::short_weierstrass::{ curves::bls12_381::field_extension::{Degree6ExtensionField, LevelTwoResidue}, @@ -13,11 +14,76 @@ use crate::{ traits::IsShortWeierstrass, }, field::{element::FieldElement, extensions::cubic::HasCubicNonResidue}, - unsigned_integer::element::{UnsignedInteger, U256}, + unsigned_integer::element::U256, }; +type FpE = FieldElement; +type Fp2E = FieldElement; +type Fp4E = FieldElement; +type Fp6E = FieldElement; +type Fp12E = FieldElement; + pub const SUBGROUP_ORDER: U256 = U256::from_hex_unchecked("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"); +// value of x in binary + +// We use |x|, then if needed we apply the minus sign in the final exponentiation by applying the inverse (in this case the conjugate because the element is in the cyclotomic subgroup) +pub const X: u64 = 0xd201000000010000; + +// X = 1101001000000001000000000000000000000000000000010000000000000000 +pub const X_BINARY: &[bool] = &[ + true, true, false, true, false, false, true, false, false, false, false, false, false, false, + false, true, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, true, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, false, +]; + +// GAMMA constants used to compute the Frobenius morphisms +/// We took these constants from https://github.com/hecmas/zkNotebook/blob/main/src/BLS12381/constants.ts + +pub const GAMMA_11: Fp2E = Fp2E::const_from_raw([ + FpE::from_hex_unchecked("1904D3BF02BB0667C231BEB4202C0D1F0FD603FD3CBD5F4F7B2443D784BAB9C4F67EA53D63E7813D8D0775ED92235FB8"), + FpE::from_hex_unchecked("FC3E2B36C4E03288E9E902231F9FB854A14787B6C7B36FEC0C8EC971F63C5F282D5AC14D6C7EC22CF78A126DDC4AF3"), +]); + +pub const GAMMA_12: Fp2E = Fp2E::const_from_raw([ + FpE::from_hex_unchecked("0"), + FpE::from_hex_unchecked("1A0111EA397FE699EC02408663D4DE85AA0D857D89759AD4897D29650FB85F9B409427EB4F49FFFD8BFD00000000AAAC"), +]); + +pub const GAMMA_13: Fp2E = Fp2E::const_from_raw([ + FpE::from_hex_unchecked("6AF0E0437FF400B6831E36D6BD17FFE48395DABC2D3435E77F76E17009241C5EE67992F72EC05F4C81084FBEDE3CC09"), + FpE::from_hex_unchecked("6AF0E0437FF400B6831E36D6BD17FFE48395DABC2D3435E77F76E17009241C5EE67992F72EC05F4C81084FBEDE3CC09"), +]); + +pub const GAMMA_14: Fp2E = Fp2E::const_from_raw([ + FpE::from_hex_unchecked("1A0111EA397FE699EC02408663D4DE85AA0D857D89759AD4897D29650FB85F9B409427EB4F49FFFD8BFD00000000AAAD"), + FpE::from_hex_unchecked("0"), +]); + +pub const GAMMA_15: Fp2E = Fp2E::const_from_raw([ + FpE::from_hex_unchecked("5B2CFD9013A5FD8DF47FA6B48B1E045F39816240C0B8FEE8BEADF4D8E9C0566C63A3E6E257F87329B18FAE980078116"), + FpE::from_hex_unchecked("144E4211384586C16BD3AD4AFA99CC9170DF3560E77982D0DB45F3536814F0BD5871C1908BD478CD1EE605167FF82995"), +]); + +/// GAMMA_2i = GAMMA_1i * GAMMA_1i.conjugate() +pub const GAMMA_21: FpE = FpE::from_hex_unchecked( + "5F19672FDF76CE51BA69C6076A0F77EADDB3A93BE6F89688DE17D813620A00022E01FFFFFFFEFFFF", +); + +pub const GAMMA_22: FpE = FpE::from_hex_unchecked( + "5F19672FDF76CE51BA69C6076A0F77EADDB3A93BE6F89688DE17D813620A00022E01FFFFFFFEFFFE", +); + +pub const GAMMA_23: FpE = + FpE::from_hex_unchecked("1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFAAAA"); + +pub const GAMMA_24: FpE = + FpE::from_hex_unchecked("1A0111EA397FE699EC02408663D4DE85AA0D857D89759AD4897D29650FB85F9B409427EB4F49FFFD8BFD00000000AAAC"); + +pub const GAMMA_25: FpE = + FpE::from_hex_unchecked("1A0111EA397FE699EC02408663D4DE85AA0D857D89759AD4897D29650FB85F9B409427EB4F49FFFD8BFD00000000AAAD"); #[derive(Clone)] pub struct BLS12381AtePairing; @@ -46,6 +112,26 @@ impl IsPairing for BLS12381AtePairing { } } +/// Implements the miller loop for the ate pairing of the BLS12 381 curve. +/// Based on algorithm 9.2, page 212 of the book +/// "Topics in computational number theory" by W. Bons and K. Lenstra +#[allow(unused)] +pub fn miller( + q: &ShortWeierstrassProjectivePoint, + p: &ShortWeierstrassProjectivePoint, +) -> FieldElement { + let mut r = q.clone(); + let mut f = FieldElement::::one(); + X_BINARY.iter().skip(1).for_each(|bit| { + double_accumulate_line(&mut r, p, &mut f); + if *bit { + add_accumulate_line(&mut r, q, p, &mut f); + } + }); + + f.conjugate() +} + fn double_accumulate_line( t: &mut ShortWeierstrassProjectivePoint, p: &ShortWeierstrassProjectivePoint, @@ -152,69 +238,148 @@ fn add_accumulate_line( ]), ]); } -/// Implements the miller loop for the ate pairing of the BLS12 381 curve. -/// Based on algorithm 9.2, page 212 of the book -/// "Topics in computational number theory" by W. Bons and K. Lenstra -#[allow(unused)] -fn miller( - q: &ShortWeierstrassProjectivePoint, - p: &ShortWeierstrassProjectivePoint, -) -> FieldElement { - let mut r = q.clone(); - let mut f = FieldElement::::one(); - let mut miller_loop_constant = MILLER_LOOP_CONSTANT; - let mut miller_loop_constant_bits: alloc::vec::Vec = alloc::vec![]; - while miller_loop_constant > 0 { - miller_loop_constant_bits.insert(0, (miller_loop_constant & 1) == 1); - miller_loop_constant >>= 1; - } +// To understand more about how to reduce the final exponentiation +// read "Efficient Final Exponentiation via Cyclotomic Structure for +// Pairings over Families of Elliptic Curves" (https://eprint.iacr.org/2020/875.pdf) +// Hard part from https://eprint.iacr.org/2020/875.pdf p14 +// Same implementation as in Constantine https://github.com/mratsim/constantine/blob/master/constantine/math/pairings/pairings_bls12.nim +pub fn final_exponentiation(f: &Fp12E) -> Fp12E { + let f_easy_aux = f.conjugate() * f.inv().unwrap(); + let mut f_easy = frobenius_square(&f_easy_aux) * &f_easy_aux; + + let mut v2 = cyclotomic_square(&f_easy); // v2 = f² + let mut v0 = cyclotomic_pow_x(&f_easy).conjugate(); // v0 = f^x + let mut v1 = f_easy.conjugate(); // v1 = f^-1 + + // (x−1)² + v0 *= v1; // v0 = f^(x-1) + v1 = cyclotomic_pow_x(&v0).conjugate(); // v1 = (f^(x-1))^(x) + + v0 = v0.conjugate(); // v0 = (f^(x-1))^(-1) + v0 *= &v1; // v0 = (f^(x-1))^(-1) * (f^(x-1))^x = (f^(x-1))^(x-1) = f^((x-1)²) + + // (x+p) + v1 = cyclotomic_pow_x(&v0).conjugate(); // v1 = f^((x-1)².x) + v0 = frobenius(&v0); // f^((x-1)².p) + v0 *= &v1; // f^((x-1)².p + (x-1)².x) = f^((x-1)².(x+p)) + + // + 3 + f_easy *= v2; // f^3 + + // (x²+p²−1) + v2 = cyclotomic_pow_x(&v0).conjugate(); + v1 = cyclotomic_pow_x(&v2).conjugate(); // v1 = f^((x-1)².(x+p).x²) + v2 = frobenius_square(&v0); // v2 = f^((x-1)².(x+p).p²) + v0 = v0.conjugate(); // v0 = f^((x-1)².(x+p).-1) + v0 *= &v1; // v0 = f^((x-1)².(x+p).(x²-1)) + v0 *= &v2; // v0 = f^((x-1)².(x+p).(x²+p²-1)) + + f_easy *= &v0; + f_easy +} - for bit in miller_loop_constant_bits[1..].iter() { - double_accumulate_line(&mut r, p, &mut f); - if *bit { - add_accumulate_line(&mut r, q, p, &mut f); - } - } - f.inv().unwrap() +////////////////// FROBENIUS MORPHISIMS ////////////////// +pub fn frobenius(f: &Fp12E) -> Fp12E { + let [a, b] = f.value(); // f = a + bw, where a and b in Fp6. + let [a0, a1, a2] = a.value(); // a = a0 + a1 * v + a2 * v^2, where a0, a1 and a2 in Fp2. + let [b0, b1, b2] = b.value(); // b = b0 + b1 * v + b2 * v^2, where b0, b1 and b2 in Fp2. + + // c1 = a0.conjugate() + a1.conjugate() * GAMMA_12 * v + a2.conjugate() * GAMMA_14 * v^2 + let c1 = Fp6E::new([ + a0.conjugate(), + a1.conjugate() * GAMMA_12, + a2.conjugate() * GAMMA_14, + ]); + + let c2 = Fp6E::new([ + b0.conjugate() * GAMMA_11, + b1.conjugate() * GAMMA_13, + b2.conjugate() * GAMMA_15, + ]); + + Fp12E::new([c1, c2]) //c1 + c2 * w } -/// Auxiliary function for the final exponentiation of the ate pairing. fn frobenius_square( f: &FieldElement, ) -> FieldElement { let [a, b] = f.value(); - let w_raised_to_p_squared_minus_one = FieldElement::::new_base("1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaad"); - let omega_3 = FieldElement::::new_base("1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac"); - let omega_3_squared = FieldElement::::new_base( - "5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe", - ); - let [a0, a1, a2] = a.value(); let [b0, b1, b2] = b.value(); - let f0 = FieldElement::new([a0.clone(), a1 * &omega_3, a2 * &omega_3_squared]); - let f1 = FieldElement::new([b0.clone(), b1 * omega_3, b2 * omega_3_squared]); + let c1 = Fp6E::new([a0.clone(), GAMMA_22 * a1, GAMMA_24 * a2]); + let c2 = Fp6E::new([GAMMA_21 * b0, GAMMA_23 * b1, GAMMA_25 * b2]); - FieldElement::new([f0, w_raised_to_p_squared_minus_one * f1]) + Fp12E::new([c1, c2]) } -// To understand more about how to reduce the final exponentiation -// read "Efficient Final Exponentiation via Cyclotomic Structure for -// Pairings over Families of Elliptic Curves" (https://eprint.iacr.org/2020/875.pdf) -// -// TODO: implement optimizations for the hard part of the final exponentiation. -#[allow(unused)] -fn final_exponentiation( - base: &FieldElement, -) -> FieldElement { - const PHI_DIVIDED_BY_R: UnsignedInteger<20> = UnsignedInteger::from_hex_unchecked("f686b3d807d01c0bd38c3195c899ed3cde88eeb996ca394506632528d6a9a2f230063cf081517f68f7764c28b6f8ae5a72bce8d63cb9f827eca0ba621315b2076995003fc77a17988f8761bdc51dc2378b9039096d1b767f17fcbde783765915c97f36c6f18212ed0b283ed237db421d160aeb6a1e79983774940996754c8c71a2629b0dea236905ce937335d5b68fa9912aae208ccf1e516c3f438e3ba79"); - - let f1 = base.conjugate() * base.inv().unwrap(); - let f2 = frobenius_square(&f1) * f1; - f2.pow(PHI_DIVIDED_BY_R) +////////////////// CYCLOTOMIC SUBGROUP OPERATIONS ////////////////// +/// Since the result of the Easy Part of the Final Exponentiation belongs to the cyclotomic +/// subgroup of Fp12, we can optimize the square and pow operations used in the Hard Part. + +/// Computes the square of an element of a cyclotomic subgroup of Fp12. +/// Algorithm from Constantine's cyclotomic_square_quad_over_cube +/// https://github.com/mratsim/constantine/blob/master/constantine/math/pairings/cyclotomic_subgroups.nim#L354 +pub fn cyclotomic_square(a: &Fp12E) -> Fp12E { + // a = g + h * w + let [g, h] = a.value(); + let [b0, b1, b2] = g.value(); + let [b3, b4, b5] = h.value(); + + let v0 = Fp4E::new([b0.clone(), b4.clone()]).square(); + let v1 = Fp4E::new([b3.clone(), b2.clone()]).square(); + let v2 = Fp4E::new([b1.clone(), b5.clone()]).square(); + + // r = r0 + r1 * w + // r0 = r00 + r01 * v + r02 * v^2 + // r1 = r10 + r11 * v + r12 * v^2 + + // r00 = 3v00 - 2b0 + let mut r00 = &v0.value()[0] - b0; + r00 = r00.double(); + r00 += v0.value()[0].clone(); + + // r01 = 3v10 -2b1 + let mut r01 = &v1.value()[0] - b1; + r01 = r01.double(); + r01 += v1.value()[0].clone(); + + // r11 = 3v01 - 2b4 + let mut r11 = &v0.value()[1] + b4; + r11 = r11.double(); + r11 += v0.value()[1].clone(); + + // r12 = 3v11 - 2b5 + let mut r12 = &v1.value()[1] + b5; + r12 = r12.double(); + r12 += v1.value()[1].clone(); + + // 3 * (9 + u) * v21 + 2b3 + let v21 = mul_fp2_by_nonresidue(&v2.value()[1]); + let mut r10 = &v21 + b3; + r10 = r10.double(); + r10 += v21; + + // 3 * (9 + u) * v20 - 2b3 + let mut r02 = &v2.value()[0] - b2; + r02 = r02.double(); + r02 += v2.value()[0].clone(); + + Fp12E::new([Fp6E::new([r00, r01, r02]), Fp6E::new([r10, r11, r12])]) } +#[allow(clippy::needless_range_loop)] +pub fn cyclotomic_pow_x(f: &Fp12E) -> Fp12E { + let mut result = Fp12E::one(); + X_BINARY.iter().for_each(|&bit| { + result = cyclotomic_square(&result); + if bit { + result = &result * f; + } + }); + result +} #[cfg(test)] mod tests { use crate::{ @@ -294,4 +459,58 @@ mod tests { let result = BLS12381AtePairing::compute_batch(&[(&p.to_affine(), &q)]); assert!(result.is_err()) } + #[test] + fn apply_12_times_frobenius_is_identity() { + let f = Fp12E::from_coefficients(&[ + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", + ]); + let mut result = frobenius(&f); + for _ in 1..12 { + result = frobenius(&result); + } + assert_eq!(f, result) + } + + #[test] + fn apply_6_times_frobenius_square_is_identity() { + let f = Fp12E::from_coefficients(&[ + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", + ]); + let mut result = frobenius_square(&f); + for _ in 1..6 { + result = frobenius_square(&result); + } + assert_eq!(f, result) + } + + #[test] + fn cyclotomic_square_equals_square() { + let p = BLS12381Curve::generator(); + let q = BLS12381TwistCurve::generator(); + let f = miller(&q, &p); + let f_easy_aux = f.conjugate() * f.inv().unwrap(); // f ^ (p^6 - 1) because f^(p^6) = f.conjugate(). + let f_easy = &frobenius_square(&f_easy_aux) * f_easy_aux; // (f^{p^6 - 1})^(p^2) * (f^{p^6 - 1}). + assert_eq!(cyclotomic_square(&f_easy), f_easy.square()); + } + + #[test] + fn test_double_accumulate_line_doubles_point_correctl_2() { + let g1 = BLS12381Curve::generator(); + let g2 = BLS12381TwistCurve::generator(); + let mut r = g2.clone(); + let mut f = FieldElement::one(); + double_accumulate_line(&mut r, &g1, &mut f); + let expected_r = g2.operate_with(&g2); + assert_eq!(r.to_affine(), expected_r.to_affine()); + } + + #[test] + fn cyclotomic_pow_x_equals_pow() { + let p = BLS12381Curve::generator(); + let q = BLS12381TwistCurve::generator(); + let f = miller(&q, &p); + let f_easy_aux = f.conjugate() * f.inv().unwrap(); // f ^ (p^6 - 1) because f^(p^6) = f.conjugate(). + let f_easy = &frobenius_square(&f_easy_aux) * f_easy_aux; // (f^{p^6 - 1})^(p^2) * (f^{p^6 - 1}). + assert_eq!(cyclotomic_pow_x(&f_easy), f_easy.pow(X)); + } } diff --git a/math/src/elliptic_curve/short_weierstrass/point.rs b/math/src/elliptic_curve/short_weierstrass/point.rs index 888d3f50a..679edeaea 100644 --- a/math/src/elliptic_curve/short_weierstrass/point.rs +++ b/math/src/elliptic_curve/short_weierstrass/point.rs @@ -52,6 +52,9 @@ impl ShortWeierstrassProjectivePoint { } pub fn double(&self) -> Self { + if self.is_neutral_element() { + return self.clone(); + } let [px, py, pz] = self.coordinates(); let px_square = px * px; @@ -86,11 +89,6 @@ impl ShortWeierstrassProjectivePoint { } // https://hyperelliptic.org/EFD/g1p/data/shortw/projective/addition/madd-1998-cmo pub fn operate_with_affine(&self, other: &Self) -> Self { - let [px, py, pz] = self.coordinates(); - let [qx, qy, _qz] = other.coordinates(); - let u = qy * pz; - let v = qx * pz; - if self.is_neutral_element() { return other.clone(); } @@ -98,6 +96,11 @@ impl ShortWeierstrassProjectivePoint { return self.clone(); } + let [px, py, pz] = self.coordinates(); + let [qx, qy, _qz] = other.coordinates(); + let u = qy * pz; + let v = qx * pz; + if u == *py { if v != *px || *py == FieldElement::zero() { return Self::new([ diff --git a/math/src/polynomial/mod.rs b/math/src/polynomial/mod.rs index ee27923d0..679a83aec 100644 --- a/math/src/polynomial/mod.rs +++ b/math/src/polynomial/mod.rs @@ -1,8 +1,8 @@ use super::field::element::FieldElement; -use crate::field::traits::{IsField, IsSubFieldOf}; -use alloc::{borrow::ToOwned, vec, vec::Vec}; +use crate::field::traits::{IsField, IsPrimeField, IsSubFieldOf}; +use alloc::string::{String, ToString}; +use alloc::{borrow::ToOwned, format, vec, vec::Vec}; use core::{fmt::Display, ops}; - pub mod dense_multilinear_poly; mod error; pub mod sparse_multilinear_poly; @@ -140,6 +140,19 @@ impl Polynomial> { self.coefficients().len() } + /// Returns the derivative of the polynomial with respect to x. + pub fn differentiate(&self) -> Self { + let degree = self.degree(); + if degree == 0 { + return Polynomial::zero(); + } + let mut derivative = Vec::with_capacity(degree); + for (i, coeff) in self.coefficients().iter().enumerate().skip(1) { + derivative.push(FieldElement::::from(i as u64) * coeff); + } + Polynomial::new(&derivative) + } + /// Computes quotient with `x - b` in place. pub fn ruffini_division_inplace(&mut self, b: &FieldElement) { let mut c = FieldElement::zero(); @@ -302,6 +315,44 @@ impl Polynomial> { } } +impl Polynomial> { + // Print the polynomial as a string ready to be used in SageMath, or just for pretty printing. + pub fn print_as_sage_poly(&self, var_name: Option) -> String { + let var_name = var_name.unwrap_or('x'); + if self.coefficients.is_empty() + || self.coefficients.len() == 1 && self.coefficients[0] == FieldElement::zero() + { + return String::new(); + } + + let mut string = String::new(); + let zero = FieldElement::::zero(); + + for (i, coeff) in self.coefficients.iter().rev().enumerate() { + if *coeff == zero { + continue; + } + + let coeff_str = coeff.representative().to_string(); + + if i == self.coefficients.len() - 1 { + string.push_str(&coeff_str); + } else if i == self.coefficients.len() - 2 { + string.push_str(&format!("{}*{} + ", coeff_str, var_name)); + } else { + string.push_str(&format!( + "{}*{}^{} + ", + coeff_str, + var_name, + self.coefficients.len() - 1 - i + )); + } + } + + string + } +} + pub fn pad_with_zero_coefficients_to_length( pa: &mut Polynomial>, n: usize, @@ -1177,4 +1228,25 @@ mod tests { assert_eq!(lhs, g); assert_eq!(g, p3); } + + #[test] + fn test_differentiate() { + // 3x^2 + 2x + 42 + let px = Polynomial::new(&[FE::new(42), FE::new(2), FE::new(3)]); + // 6x + 2 + let dpdx = px.differentiate(); + assert_eq!(dpdx, Polynomial::new(&[FE::new(2), FE::new(6)])); + + // 128 + let px = Polynomial::new(&[FE::new(128)]); + // 0 + let dpdx = px.differentiate(); + assert_eq!(dpdx, Polynomial::new(&[FE::new(0)])); + } + + #[test] + fn test_print_as_sage_poly() { + let p = Polynomial::new(&[FE::new(1), FE::new(2), FE::new(3)]); + assert_eq!(p.print_as_sage_poly(None), "3*x^2 + 2*x + 1"); + } } diff --git a/provers/groth16/README.md b/provers/groth16/README.md index dae3aeae1..5e48d6b3f 100644 --- a/provers/groth16/README.md +++ b/provers/groth16/README.md @@ -1,3 +1,156 @@ # Lambdaworks Groth16 Prover -An under-optimized implementation of [Groth16](https://eprint.iacr.org/2016/260) protocol. +An under-optimized implementation of [Groth16](https://eprint.iacr.org/2016/260) protocol. To use it with Circom, check the [examples](./circom-adapter/src/README.md). + +## Introduction + +Over the last decade, SNARKs (succinct, non-interactive arguments of knowledge) and STARKs (scalable, transparent arguments of knowledge) have been gaining attention due to their applications in verifiable private computation and scalability of blockchains. + +Groth introduced this [proof system](https://eprint.iacr.org/2016/260.pdf) in 2016 and saw an early application in ZCash. The protocol relies on pairing-friendly elliptic curves, such as BN254, BLS12-381, and BLS12-377 (more later). Its proof size is among the smallest (consisting of only three elliptic curve elements) and fastest to verify. The main drawback is that it needs a trusted setup per program. In other words, we need to regenerate all the parameters whenever we want to prove a new program (or change the original one). + +## Arithmetization + +To prove the execution of a given program, we have to transform it to a SNARK (succinct, non-interactive argument of knowledge) friendly form. One of such forms is arithmetic circuit satisfiability, where one can prove knowledge of a valid circuit assignment. This first step, known as arithmetization, is the program's transformation into an arithmetic circuit or equivalent form. + +## R1CS + +Arithmetic circuits can be expressed equivalently as (quadratic) rank one constraint systems (R1CS), which are systems of equations of the form: +$$(Az)\times (Bz) = Cz$$ +where $A, B, C$ are matrices of size $m + 1$ rows by $n + 1$ columns, $z$ is a (column) vector of size $n + 1$ and $\times$ indicates the componentwise product of the resulting vectors. + +We can alternatively view this compact form as +$\left( \sum_k a_{0k} z_k \right) \left( \sum_k b_{0k} z_k \right) - \left( \sum_k c_{0k} z_k \right) = 0$ +$\left( \sum_k a_{1k} z_k \right) \left( \sum_k b_{1k} z_k \right) - \left( \sum_k c_{1k} z_k \right) = 0$ +$\left( \sum_k a_{2k} z_k \right) \left( \sum_k b_{2k} z_k \right) - \left( \sum_k c_{2k} z_k \right) = 0$ +$\vdots$ +$\left( \sum_k a_{mk} z_k \right) \left( \sum_k b_{mk} z_k \right) - \left( \sum_k c_{mk} z_k \right) = 0$ + +We could express these equations more compactly by using polynomials and prove the solution of the R1CS system more concisely. To this end, we will introduce quadratic arithmetic programs, [QAP](https://vitalik.ca/general/2016/12/10/qap.html). + +## Quadratic Arithmetic Program + +We can interpret each column of the $A$ matrix as evaluations of some polynomial over some suitable domain. This is a common practice in many SNARKs, where we try to encode a vector as a polynomial; see, for example, our [post about STARKs](https://blog.lambdaclass.com/diving-deep-fri/). We sample $D_0 = \{ x_0 , x_1 , ... , x_n \}$ over the finite field and define the polynomial $A_i (x)$ as the polynomial of at most degree $n$ such that $A_i ( x_k ) = a_{ki}$. + +For performance reasons, it is convenient to select as interpolation domain $D_0$ the n-th roots of unity since we can use the Fast Fourier Transform to interpolate. Similarly, we can interpret the columns of $B$ and $C$ as polynomials $B_k (x)$ and $C_k (x)$. Taking advantage of these polynomials, we can express the R1CS system in polynomial form, +$P (x) = \left( \sum_k A_{k} (x) z_k \right) \left( \sum_k B_{k} (x) z_k \right) - \left( \sum_k C_{k} (x) z_k \right)$ + +We can see that if we have a valid solution for the R1CS, the polynomial $P (x)$ evaluates to $0$ over $D_0$ (since we require the polynomial to interpolate the values of the columns of the matrices). Therefore, we can express the condition as +$P (x) = 0$ for $x \in D_0$ +We now introduce the vanishing polynomial over the set $D_0$, $Z_D (x) = \prod_k (x - x_k )$ +So, if the polynomial $P (x)$ evaluates to $0$ over $D_0$, it is divisible by $Z_D (x)$. This can be written as there is some polynomial $h (x)$ such that +$P (x) = h(x) Z_D (x)$ +The degree of the polynomial $h(x)$ is the degree of $P$ minus the degree of $Z_D$. An honest prover should be able to find the resulting quotient and use it to show that he correctly executed the program. + +## Transforming QAP into a zero-knowledge proof + +We need to make some transformation to the above problem if we want to turn it into a zero-knowledge proof. For a more detailed description of this process, see [here](https://www.rareskills.io/post/groth16). We must ensure that the prover cannot cheat and that the verifier cannot learn anything about the private input or witness. One key ingredient is a polynomial commitment scheme (PCS): we can make the prover commit to a given polynomial so that he cannot change it later. One such commitment scheme is the [KZG commitment](https://dankradfeist.de/ethereum/2020/06/16/kate-polynomial-commitments.html), where we use [pairing-friendly elliptic curves](https://static1.squarespace.com/static/5fdbb09f31d71c1227082339/t/5ff394720493bd28278889c6/1609798774687/PairingsForBeginners.pdf) to bind the prover to a polynomial. The scheme's security relies on the hardness of the discrete logarithm problem over the curve. Pairings can be considered an operation that allows a one-time multiplication between points in an elliptic curve. In our case, we will work over type3 III pairings, $\dagger : G_1 \times G_2 \rightarrow G_t$, which have the following nice property (bilinearity): +$(a g_1 ) \dagger (b g_2 ) = (ab) (g_1 \dagger g_2)$ +To commit to a polynomial using KZG, we need to sample a random scalar $\tau$ (which is considered toxic waste and should be forgotten, or we could forge proofs) and generate the following sequence of points in the elliptic curve, whose generator is $g_1$, +$P_0 = g_1$, +$P_1 = \tau g_1$ +$P_2 = \tau^2 g_1$ +$\vdots$ +$P_n = \tau^n g_1$ +Then, given a polynomial $p(x) = a_0 + a_1 x + a_2 x^2 + ... + a_n x^n$ we compute the commitment as +$\mathrm{cm} (p) = a_0 P_0 + a_1 P_1 + ... + a_n P_n$ +which is the same as $\mathrm{cm} (p) = p(\tau) g_1$, that is, hiding the evaluation of $p(x)$ inside the elliptic curve. Because the discrete log problem is hard, we cannot use our knowledge of $g_1$ and $\mathrm{cm} (p)$ to obtain $p(\tau)$. + +To check that the polynomial $p(x)$ evaluates to $v$ at $z$ we can use the fact that +$p(x) - v = (x - z)q(x)$ +where $q(x)$ is the quotient polynomial of the division of $p(x)$ by $x - z$. The prover can produce proof of such evaluation by committing to $q(x)$ using the same trick. Still, the verifier will need some additional information (included in the verifying key), $g_2$ (the generator of the group $G_2$), and $\tau g_2$ (remember, nobody must know $\tau$). Then, using pairings, the verifier can check the evaluation using the points in the elliptic curves, +$(\mathrm{cm} (p) - vg_1 \dagger g_2) = a = p(\tau) (g_1 \dagger g_2)$ +$\mathrm{cm} (q) \dagger (\tau g_2 - z g_2) = b = q(\tau) ( \tau - z)(g_1 \dagger g_2)$ +If $a$ and $b$ are the same, and since $\tau$ is a random point with high probability, we assume that $p(z) = v$ (This depends on the Schwartz-Zippel lemma). + +Remember that we want to prove that the verifier knows some $w$ and a polynomial $h(x)$ of degree $m - 1$ such that if $z= (1, x, w)$, the following condition holds +$\left( \sum_k A_{k} (x) z_k \right) \left( \sum_k B_{k} (x) z_k \right) = \left( \sum_k C_{k} (x) z_k \right) + h(x)Z_D (x)$ + +If we force the prover first to commit to the polynomials $A_k (x)$ and $B_k (x)$ and then produce the quotient polynomial, we have to make sure that he cannot forge $C_k (x)$ to fulfill the previous condition. To do so, we are going to introduce random shifts ($\alpha$ and $\beta$) to the evaluations: +$\mathrm{cm} (\sum A_i z_i ) = \sum (A_i (\tau) z_i) g_1 + \alpha g_1$ +$\mathrm{cm} (\sum B_i z_i) = \sum (B_i (\tau) z_i) g_2 + \beta g_2$ +The $B_i (x)$ are committed to using group $G_2$ so that we can compute the product on the left-hand side through a pairing, +$(\mathrm{cm} (\sum A_i z_i )) \dagger ( \mathrm{cm} (\sum B_i z_i )) = (\sum A_i (\tau) z_i )(\sum B_i (\tau) z_i ) (g_1 \dagger g_2)$ + +Because we introduce these shifts, we need to modify the $C_k$ term accordingly, +$\begin{equation}\left( \alpha + \sum_k A_{k} (x) z_k \right) \left( \beta + \sum_k B_{k} (x) z_k \right) = \\ \alpha \beta + \left( \sum_k (C_{k} (x) + \beta A_k (x) + \alpha B_k (x)) z_k \right) + h(x)Z_D (x) \end{equation}$ +Since the prover cannot know $\alpha$ and $\beta$, we need to provide them hidden as part of the trusted setup, as $\alpha g_1$ and $\beta g_2$, so that we can compute +$(\alpha g_1) \dagger (\beta g_2) = \alpha \beta (g_1 \dagger g_2)$ +so that we can compare this result to the pairing between the shifted $A_i$ and $B_i$. + +Also, since the prover does not have $\alpha$ and $\beta$, he needs to be supplied with all the elements of the form $C_{k} (x) + \beta A_k (x) + \alpha B_k (x)$. However, when we want to calculate the product between these terms and $z$, we must recall that $z$ contains both the public input and the witness. The verifier cannot learn anything about the witness (therefore, the evaluations involving the witness should be provided by the prover). We introduce two additional variables, $\gamma$, and $\delta$, to split the variable $z$ between public input and witness. The first $k$ terms correspond to the public input, and these are encoded as +$K_i^v = \gamma^{- 1} (C_{i} (\tau) + \beta A_i (\tau) + \alpha B_i (\tau)) g_1$ +for $i = 0, 1, 2 ... , k$. For the witness, we have +$K_i^p = \delta^{- 1} (C_{i} (\tau) + \beta A_i (\tau) + \alpha B_i (\tau)) g_1$ +With these new parameters, we get +$\begin{equation}\left( \alpha + \sum_j A_{j} (x) z_j \right) \left( \beta + \sum_j B_{j} (x) z_j \right) = \\ \alpha \beta + \gamma \left( \sum_i^k \gamma^{- 1} (C_{i} (x) + \beta A_i (x) + \alpha B_i (x)) x_i \right) + \\ +\delta \left( \sum_{j = k + 1}^n \delta^{- 1} (C_{i} (x) + \beta A_i (x) + \alpha B_i (x)) x_i \right) + h(x)Z_D (x) \end{equation}$ +We can combine the last two terms into one (since they contain all the information that the verifier must not learn) +$D = \left( \sum_{j = k + 1}^n \delta^{- 1} (C_{i} (x) + \beta A_i (x) + \alpha B_i (x)) x_i \right) + h(x)Z_D (x)\delta^{- 1}$ + +Since we want to compute the product $h(x) Z_D(x)$ with the help of one pairing, we can compute the following group elements, +$Z_0 = \delta^{ - 1} Z_D (\tau)$ +$Z_1 = \delta^{ - 1} \tau Z_D (\tau)$ +$Z_2 = \delta^{ - 1} \tau^2 Z_D (\tau)$ +$\vdots$ +$Z_{m - 1} = \delta^{ - 1} \tau^{ m - 1 } Z_D (\tau)$ + +With these changes, the right-hand side of the QAP is the sum of 3 terms: +A constant (related to the random shifts). +A term involving the public input. +A term that contains the secret terms (known only to the prover). + +## Setup + +Groth16 requires sampling five random field elements to generate the proving and verifying key, $t, \alpha, \beta, \gamma, \delta$. These are toxic waste and should be discarded and wholly forgotten once the keys have been generated. + +We will use a pairing-friendly elliptic curve (with type III pairing), with subgroups $G_1$ and $G_2$ of prime order $r$. We will call the generators $g_1$ and $g_2$, respectively. To make notation easier, we will write +$[x]_1 = x g_1$ +$[x]_2 = x g_2$ +to denote points in $G_1$ and $G_2$, where $x g$ means the scalar product of $x$ and the generator of the group (i.e., applying x times the elliptic curve group operation to the generator). We will follow the notation given by [DIZK](https://eprint.iacr.org/2018/691.pdf). First, we compute the following vectors, +$K_i^v (t) = \gamma^{-1} \left( \beta A_i(t) + \alpha B_i (t) + C_i (t)\right)$ +for $i = 0, 1, 2 , ... k$, +$K_i^p (t) = \delta^{-1} \left( \beta A_i(t) + \alpha B_i (t) + C_i (t)\right)$ +for $i = k+1, 1, 2 , ... n$ and +$Z_k (t) = t^k Z_D (t) \delta^{-1}$ +for $k = 0, 1, 2, ... m - 1$. +The proving key consists of the following elements: +1. $[\alpha]_1$ +2. $[\beta]_1$ +3. $[\beta]_2$ +4. $[\delta]_1$ +5. $[\delta]_2$ +6. $[A_0 (t) ]_1, [A_1 (t) ]_1 , ... , [A_n (t) ]_1$ +7. $[B_0 (t) ]_1, [B_1 (t) ]_1 , ... , [B_n (t) ]_1$ +8. $[B_0 (t) ]_2, [B_1 (t) ]_2 , ... , [B_n (t) ]_2$ +9. $[K_{ k + 1 }^p (t)] , [ K_{ k + 2 }^p (t)] , ... , [K_n^p (t)]$ +10. $[Z_0 (t)] , [Z_1 (t)] , ... , [ Z_{ m - 1 } (t)]$ + +The verifying key is much shorter and will contain in addition the value of one pairing because that value is constant: +1. $[\alpha]_1 \dagger [\beta]_2$ +2. $[\gamma]_2$ +3. $[\delta]_2$ +4. $[K_0^v (t)]_1 , [K_1^v (t)]_1 , ... , [K_k^v (t)]_1$ + +## Proof generation + +The prover receives the proving key and knows the polynomials representing the program and the public input, and he wants to prove that he has a witness satisfying that program. First, the prover needs to calculate the quotient polynomial $h(x)$ or, more precisely, its coefficients. The prover has to calculate +$$h(x) = \frac{\sum A_k(x) z_k \sum B_k (x) z_k - \sum C_k (x) z_k}{Z_D (X) }$$ + +The best way to evaluate this quotient is by choosing a domain $D_{ev}$, of size at least the degree of the quotient polynomial plus one and not containing elements from $D_0$ (the interpolation domain) and evaluating numerator and denominator at all the elements of $D_{ev}$. Since we have at least as many evaluations of the polynomial $h (x)$ as its degree plus one, we can reconstruct $h(x)$ via interpolation. In practice, the fastest way to do this is by using the Fast Fourier Transform for evaluation and interpolation. The prover now possesses a vector of coefficients $h_0 , h_1 , h_2 , ... , h_m$. + +To ensure that the proof is zero-knowledge, the prover sample two random scalars, $r$ and $s$. + +The prover can compute the three elements of the proof, $\pi = ([\pi_1 ]_1 , [\pi_2 ]_2 , [\pi_3 ]_1)$ by doing the following calculations, +$[\pi_1 ]_1 = [\alpha]_1 + \sum z_k [A_k (t) ]_1 + r [\delta]_1$ +$[\pi_2 ]_2 = [\beta]_2 + \sum z_k [B_k (t) ]_2 + s[\delta]_2$ +$[\pi_2 ]_1 = [\beta]_1 + \sum z_k [B_k (t) ]_1 + s[\delta]_1$ +$[h(t)z(t)]_1 = \sum h_i [Z_i (t)]_1$ +$[\pi_3 ]_1 = \sum w_i [K_i^p ]_1 + [h(t)z(t)]_1 + s[\pi_1 ]_1 + r [\pi_2 ]_1 - rs [\delta]_1$ + +## Verification + +The verifier has the verifying key, the public input and parses the proof as $[\pi_1 ]_1, [\pi_2 ]_2, [\pi_3 ]_1$ and computes the following: +$[\pi_1 ]_1 \dagger [\pi_2 ]_2 = P_1$ +$[\pi_3 ]_1 \dagger [\delta]_2 + [\alpha]_1 \dagger [\beta]_2 + \left(\sum x_i [K_i^v ]_1 \right) \dagger [\gamma]_2 = P_2$ + +The proof is valid if $P_1$ and $P_2$ coincide. This is equivalent to checking the modified QAP. diff --git a/provers/groth16/circom-adapter/src/README.md b/provers/groth16/circom-adapter/src/README.md index 29fc5fbcf..b0f7253ef 100644 --- a/provers/groth16/circom-adapter/src/README.md +++ b/provers/groth16/circom-adapter/src/README.md @@ -55,7 +55,7 @@ Relative path to a file named **test** will be just **"test"** if it's placed in ```rust // ... -let (w, qap) = circom_to_lambda( +let (qap, w) = circom_to_lambda( &fs::read_to_string("test.r1cs.json").expect("Error reading file"), &fs::read_to_string("witness.json").expect("Error reading file"), ); @@ -65,7 +65,7 @@ As seen, this function returns a Lambdaworks-compatible QAP and the witness assi ```rust fn poseidon_parse_prove_verify() { - let (w, qap) = circom_to_lambda( + let (qap, w) = circom_to_lambda( &fs::read_to_string("test.r1cs.json").expect("Error reading file"), &fs::read_to_string("witness.json").expect("Error reading file"), ); diff --git a/provers/stark/src/constraints/transition.rs b/provers/stark/src/constraints/transition.rs index 52fe0ba70..9d52b6a5b 100644 --- a/provers/stark/src/constraints/transition.rs +++ b/provers/stark/src/constraints/transition.rs @@ -105,6 +105,7 @@ where } /// Compute evaluations of the constraints zerofier over a LDE domain. + #[allow(unstable_name_collisions)] fn zerofier_evaluations_on_extended_domain(&self, domain: &Domain) -> Vec> { let blowup_factor = domain.blowup_factor; let trace_length = domain.trace_roots_of_unity.len(); @@ -120,7 +121,9 @@ where if let Some(exemptions_period) = self.exemptions_period() { // FIXME: Rather than making this assertions here, it would be better to handle these // errors or make these checks when the AIR is initialized. + debug_assert!(exemptions_period.is_multiple_of(&self.period())); + debug_assert!(self.periodic_exemptions_offset().is_some()); // The elements of the domain have order `trace_length * blowup_factor`, so the zerofier evaluations @@ -204,6 +207,7 @@ where /// Returns the evaluation of the zerofier corresponding to this constraint in some point /// `z`, which could be in a field extension. + #[allow(unstable_name_collisions)] fn evaluate_zerofier( &self, z: &FieldElement, @@ -214,6 +218,7 @@ where if let Some(exemptions_period) = self.exemptions_period() { debug_assert!(exemptions_period.is_multiple_of(&self.period())); + debug_assert!(self.periodic_exemptions_offset().is_some()); let periodic_exemptions_offset = self.periodic_exemptions_offset().unwrap();