├── .github └── workflows │ ├── book.yml │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Cargo.toml ├── FILE_TEMPLATE ├── LICENSE ├── Makefile ├── README.md ├── benches └── plonk.rs ├── examples ├── example_poly.rs └── simple_circuit.rs ├── plonk-book ├── .gitignore ├── book.toml ├── macros.txt └── src │ ├── SUMMARY.md │ ├── chapter_1.md │ ├── chapter_2.md │ ├── chapter_3.md │ └── images │ └── images │ ├── EC.png │ ├── ECP.png │ ├── circuit.png │ ├── gate.png │ ├── gate1.png │ └── logicgate.png ├── plonk-core ├── Cargo.toml ├── README.md └── src │ ├── circuit.rs │ ├── commitment.rs │ ├── constraint_system │ ├── arithmetic.rs │ ├── boolean.rs │ ├── composer.rs │ ├── ecc │ │ ├── curve_addition │ │ │ ├── fixed_base_gate.rs │ │ │ ├── mod.rs │ │ │ └── variable_base_gate.rs │ │ ├── mod.rs │ │ └── scalar_mul │ │ │ ├── fixed_base.rs │ │ │ ├── mod.rs │ │ │ └── variable_base.rs │ ├── hash.rs │ ├── helper.rs │ ├── logic.rs │ ├── lookup.rs │ ├── mod.rs │ ├── range.rs │ └── variable.rs │ ├── error.rs │ ├── lib.rs │ ├── lookup │ ├── lookup_table.rs │ ├── mod.rs │ ├── multiset.rs │ ├── preprocess.rs │ └── witness_table.rs │ ├── permutation │ ├── constants.rs │ └── mod.rs │ ├── prelude.rs │ ├── proof_system │ ├── linearisation_poly.rs │ ├── mod.rs │ ├── permutation.rs │ ├── pi.rs │ ├── preprocess.rs │ ├── proof.rs │ ├── prover.rs │ ├── quotient_poly.rs │ ├── verifier.rs │ └── widget │ │ ├── arithmetic.rs │ │ ├── ecc │ │ ├── curve_addition.rs │ │ ├── fixed_base_scalar_mul.rs │ │ └── mod.rs │ │ ├── logic.rs │ │ ├── lookup.rs │ │ ├── mod.rs │ │ └── range.rs │ ├── test.rs │ ├── transcript.rs │ └── util.rs ├── plonk-hashing ├── Cargo.toml ├── README.md └── src │ ├── lib.rs │ └── poseidon │ ├── constants.rs │ ├── matrix.rs │ ├── mds.rs │ ├── mod.rs │ ├── poseidon_ref.rs │ ├── preprocessing.rs │ ├── round_constant.rs │ ├── round_numbers.rs │ └── zprize_constraints.rs ├── rust-toolchain ├── rustfmt.toml └── src └── lib.rs /.github/workflows/book.yml: -------------------------------------------------------------------------------- 1 | name: plonk book 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | deploy: 10 | runs-on: macos-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: actions-rs/toolchain@v1 14 | with: 15 | toolchain: nightly 16 | override: true 17 | 18 | - name: Setup mdBook 19 | uses: peaceiris/actions-mdbook@v1 20 | with: 21 | mdbook-version: 'latest' 22 | 23 | - name: Install mdbook-katex 24 | uses: actions-rs/cargo@v1 25 | with: 26 | command: install 27 | args: mdbook-katex 28 | 29 | - name: Build plonk book 30 | run: mdbook build plonk-book/ 31 | 32 | - name: Move book to docs 33 | run: | 34 | mkdir ./docs && mv ./plonk-book/book/* ./docs 35 | 36 | - name: Deploy to GitHub Pages 37 | uses: peaceiris/actions-gh-pages@v3 38 | with: 39 | github_token: ${{ secrets.GITHUB_TOKEN }} 40 | publish_dir: ./docs 41 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI checks 2 | 3 | on: 4 | pull_request: 5 | types: [synchronize, opened, reopened, ready_for_review] 6 | push: 7 | branches: 8 | - master 9 | 10 | env: 11 | RUSTFLAGS: "-D warnings" 12 | 13 | ## `actions-rs/toolchain@v1` overwrite set to false so that 14 | ## `rust-toolchain` is always used and the only source of truth. 15 | 16 | jobs: 17 | test: 18 | if: github.event.pull_request.draft == false 19 | 20 | name: Test on ${{ matrix.os }} 21 | runs-on: ${{ matrix.os }} 22 | strategy: 23 | matrix: 24 | # We don't need to test across multiple platforms yet 25 | # os: [ubuntu-latest, windows-latest, macOS-latest] 26 | os: [ubuntu-latest] 27 | 28 | steps: 29 | - uses: actions/checkout@v2 30 | - uses: actions-rs/toolchain@v1 31 | with: 32 | override: false 33 | - name: Run tests 34 | uses: actions-rs/cargo@v1 35 | with: 36 | command: test 37 | args: --verbose --release --workspace --all-features 38 | - name: Check Benchmarks 39 | uses: actions-rs/cargo@v1 40 | with: 41 | command: bench 42 | args: --verbose --workspace --all-features --no-run 43 | - name: Check Examples 44 | uses: actions-rs/cargo@v1 45 | with: 46 | command: run 47 | args: --example simple_circuit --verbose --release --all-features 48 | 49 | build: 50 | if: github.event.pull_request.draft == false 51 | 52 | name: Build target ${{ matrix.target }} 53 | runs-on: ubuntu-latest 54 | strategy: 55 | matrix: 56 | target: 57 | - wasm32-unknown-unknown 58 | - wasm32-wasi 59 | 60 | steps: 61 | - uses: actions/checkout@v2 62 | - uses: actions-rs/toolchain@v1 63 | with: 64 | override: false 65 | - name: Add target 66 | run: rustup target add ${{ matrix.target }} 67 | - name: cargo build 68 | uses: actions-rs/cargo@v1 69 | with: 70 | command: build 71 | args: --all-features 72 | 73 | doc-links: 74 | if: github.event.pull_request.draft == false 75 | 76 | name: Intra-doc links 77 | runs-on: ubuntu-latest 78 | 79 | steps: 80 | - uses: actions/checkout@v2 81 | - uses: actions-rs/toolchain@v1 82 | with: 83 | override: false 84 | - name: cargo fetch 85 | uses: actions-rs/cargo@v1 86 | with: 87 | command: fetch 88 | 89 | # Ensure intra-documentation links all resolve correctly 90 | # Requires #![deny(intra_doc_link_resolution_failure)] in crates. 91 | - name: Check intra-doc links 92 | uses: actions-rs/cargo@v1 93 | with: 94 | command: doc 95 | args: --workspace --all-features 96 | 97 | fmt: 98 | if: github.event.pull_request.draft == false 99 | 100 | name: Rustfmt 101 | timeout-minutes: 30 102 | runs-on: ubuntu-latest 103 | steps: 104 | - uses: actions/checkout@v2 105 | - uses: actions-rs/toolchain@v1 106 | with: 107 | override: false 108 | - run: rustup component add rustfmt 109 | - uses: actions-rs/cargo@v1 110 | with: 111 | command: fmt 112 | args: --all -- --check 113 | 114 | clippy: 115 | if: github.event.pull_request.draft == false 116 | 117 | name: Clippy lint checks 118 | runs-on: ubuntu-latest 119 | 120 | steps: 121 | - uses: actions/checkout@v2 122 | - uses: actions-rs/toolchain@v1 123 | with: 124 | override: false 125 | components: clippy 126 | - name: Run clippy 127 | uses: actions-rs/cargo@v1 128 | with: 129 | command: clippy 130 | args: --verbose --release --workspace --examples --tests --all-features 131 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | notes.md 5 | scrap.rs 6 | kzg10.rs 7 | design.md 8 | refactor_notes 9 | .DS_Store -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Have a look at https://keepachangelog.com/en/1.0.0/ 2 | 3 | The following are just a few examples, we will start keeping a change log for real after the 0.1 release. 4 | 5 | # Unreleased 6 | 7 | - Added blinder polynomials 8 | - Added benchmarks for proving & verification processes 9 | - Added big_arith_gate and conditional point negate 10 | - Added Serde support using ark-serialize and derive feature -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to plonk 2 | 3 | All of the code under this repository is licensed under the 4 | Mozilla Public License Version 2.0. 5 | 6 | If you have questions or comments, please feel free to email the 7 | authors listed [here](https://github.com/rust-zkp/ark-plonk/blob/master/Cargo.toml). 8 | 9 | For feature requests, suggestions, and bug reports, please open an issue on 10 | [our Github](https://github.com/rust-zkp/ark-plonk). (Or, send us 11 | an email if you're opposed to using Github for whatever reason.) 12 | 13 | Patches are welcomed as pull requests on 14 | [our Github](https://github.com/rust-zkp/ark-plonk), as well as by 15 | email (preferably sent to all of the authors listed in `Cargo.toml`). 16 | 17 | If you want to work on an issue, please let us know in the comments of it and 18 | we will assign it to you. 19 | 20 | If you're new to this repository and you want to contribute, you can 21 | look for the issues tagged the with `good first issue` label. We try to add this tag 22 | to the issues we belive are easy for newcomers. 23 | 24 | If you need any help regarding an issue feel free to ask the authors in the `Draft pull request` 25 | or the issue (or by email if you prefer it by any reason.) 26 | 27 | # Pull Requests 28 | 29 | - Keep your pull requests as `Draft` until they are ready to be reviewed. 30 | 31 | - Be descriptive in the titles of your PRs and also provide good descriptions about 32 | which things are being added, changed, fixed or removed also addressing the corresponding 33 | issue that it is addressing to. 34 | 35 | - Any pull request that it's not passing our CI tests & builds will not be merged. 36 | This implies incorrect formatting errors, compilation errors, clippy lints, or any 37 | other kind of inconsistency with your code. 38 | 39 | - Do not open PRs that are not linked or related to a previously opened issue. 40 | 41 | - Update the `Unreleased` section of the [CHANGELOG](https://github.com/rust-zkp/ark-plonk/blob/master/CHANGELOG.md) 42 | if your PR includes anything that it's worth to be noticed in there. Avoid adding things 43 | like doc-nitpicks and similar changes which do not affect directly any added, 44 | fixed, removed or changed feature. 45 | 46 | # Code of Conduct 47 | 48 | We follow the [Rust Code of Conduct](http://www.rust-lang.org/conduct.html): 49 | 50 | * We are committed to providing a friendly, safe and welcoming environment for all, regardless 51 | of level of experience, gender identity and expression, sexual orientation, disability, personal 52 | appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic. 53 | 54 | * Please avoid using overtly sexual aliases or other nicknames that might detract from a friendly, 55 | safe and welcoming environment for all. 56 | 57 | * Please be kind and courteous. There’s no need to be mean or rude. 58 | 59 | * Respect that people have differences of opinion and that every design or implementation choice carries 60 | a trade-off and numerous costs. There is seldom a right answer. 61 | 62 | * Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works. 63 | 64 | * We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We interpret the term “harassment” as including the definition in the Citizen Code of Conduct; if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don’t tolerate behavior that excludes people in socially marginalized groups. 65 | 66 | * Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the channel ops or any of the Rust moderation team immediately. Whether you’re a regular contributor or a newcomer, we care about making this community a safe place for you and we’ve got your back. 67 | 68 | * Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome. 69 | 70 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "plonk" 3 | version = "0.8.2" 4 | authors = [ 5 | "Kevaundray Wedderburn ", 6 | "Luke Pearson ", 7 | "Jules De Smit ", 8 | "Joshua Fitzgerald ", 9 | "Carlos Perez ", 10 | "David Nevado ", 11 | "Zhenfei Zhang " 12 | ] 13 | readme = "README.md" 14 | repository = "https://github.com/zk-garage/plonk" 15 | keywords = ["cryptography", "plonk", "zk-snarks", "zero-knowledge", "crypto"] 16 | categories =["algorithms", "cryptography", "science"] 17 | description = "A pure-Rust implementation of the PLONK ZK-Proof algorithm." 18 | license = "MPL" 19 | edition = "2021" 20 | include = ["src", "LICENSE-*", "README.md"] 21 | 22 | [package.metadata.docs.rs] 23 | # To build locally: 24 | # RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --open 25 | all-features = true 26 | rustdoc-args = ["--cfg", "doc_cfg"] 27 | 28 | [workspace] 29 | resolver = "2" 30 | members = [ 31 | "plonk-core", 32 | "plonk-hashing", 33 | ] 34 | 35 | [dependencies] 36 | plonk-core = { path = "plonk-core" } 37 | plonk-hashing = { path = "plonk-hashing" } 38 | 39 | [dev-dependencies] 40 | ark-ff = "0.3" 41 | ark-sponge = "0.3" 42 | ark-std = "0.3" 43 | ark-bls12-377 = "0.3" 44 | ark-bls12-381 = "0.3" 45 | ark-ec = "0.3" 46 | ark-ed-on-bls12-377 = "0.3" 47 | ark-ed-on-bls12-381 = "0.3" 48 | ark-poly = "0.3" 49 | ark-poly-commit = "0.3" 50 | blake2 = "0.9" 51 | criterion = "0.3" 52 | derivative = "2.2.0" 53 | paste = "1.0.6" 54 | rand_core = {version = "0.6", default-features=false, features = ["getrandom"] } 55 | tempdir = "0.3" 56 | ark-vesta = "0.3" 57 | 58 | 59 | 60 | [[bench]] 61 | name = "plonk" 62 | harness = false 63 | 64 | [profile.bench] 65 | codegen-units = 1 66 | debug = false 67 | debug-assertions = false 68 | incremental = false 69 | lto = "thin" 70 | opt-level = 3 71 | overflow-checks = false 72 | rpath = false 73 | -------------------------------------------------------------------------------- /FILE_TEMPLATE: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) ZK-GARAGE for some files. All rights reserved. 6 | // Copyright (c) DUSK NETWORK for some files. All rights reserved. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | help: ## Display this help screen 2 | @grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-15s\033[0m %s\n", $$1, $$2}' 3 | 4 | doc: ## Generate documentation 5 | @cargo rustdoc --lib -- -D warnings 6 | 7 | doc-internal: ## Generate documentation with private items 8 | @cargo rustdoc --lib -- --document-private-items -D warnings 9 | 10 | book: ## Generate book 11 | @mdbook build plonk-book 12 | 13 | 14 | .PHONY: help doc doc-internal book 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PLONK 2 | [![CI checks](https://github.com/ZK-Garage/plonk/actions/workflows/ci.yml/badge.svg)](https://github.com/ZK-Garage/plonk/actions/workflows/ci.yml) 3 | [![Repository](https://img.shields.io/badge/github-plonk-blueviolet?logo=github)](https://github.com/ZK-Garage/plonk) 4 | [![Documentation](https://img.shields.io/badge/docs-plonk-blue?logo=rust)](https://docs.rs/plonk/) 5 | 6 | _This is a pure rust implementation of various zkp components by the [ZK-Garage](https://github.com/ZK-Garage) team_ 7 | 8 | ## About 9 | This a library currently contains several modules 10 | 11 | The `plonk-core` module is an implemention of the PLONK proving system, that leverages custom gates and lookups to significantly enhance performance and lower constraint count to optimise the generation of zero knowledge proofs. The backend of the `plonk-core` module is designed to be compatible with the [arkworks](https://github.com/arkworks-rs) suite. By leveraging the operations in arkworks we have optimised algebra and generic trait abstractions for curve types, allowing users to define their SNARK over different curves and, if applicabale, utilise embedded or pairing curves. The polynomial commitment scheme is also generic, which allows users to implement differing PCSs dependent upon particular needs. 12 | 13 | The `plonk-hashing` module is set to contain several hashing algorithms, commencing with an optimised implementation of the Poseidon hashing algorithm generic for both plonk-style arithmetic representation and R1CS. Which will be extended but not limited to Reinforced Concrete and Blake2s. 14 | 15 | The `plonk-book` is a module which contains a detailed overview of the working parts within a EC based ZKP system, with explanation of some of the characteristics particular to PLONK, e.g. Lagrange bases. There is a also a chapter on the construction of the PLONK algorithms, as well as an explanation of the implementations features and details specific to this repository. 16 | ### Compile the Plonk book 17 | First, you need to install mdbook command line tool used to create books with Markdown. 18 | 19 | ```cargo install mdbook``` 20 | 21 | 22 | 23 | You should also install Katex preprocessor which renders Latex equations into HTML at build time 24 | 25 | ```cargo install --git "https://github.com/lzanini/mdbook-katex"``` 26 | 27 | 28 | Then, you build the book as follows: 29 | 30 | ```mdbook build``` 31 | 32 | Last but not least, you can read the book by doing this command 33 | 34 | ```mdbook serve --open``` 35 | 36 | This will display the book in your default web browser after building it. 37 | 38 | 39 | 40 | ### Join the effort 41 | 42 | Please, if you're interested in collaborating, contributing or just discussing, you can join our Discord here: 43 | 44 | ## Features 45 | 46 | This crate includes a variety of features which will briefly be explained below: 47 | 48 | - `parallel`: Enables `rayon` and other parallelisation primitives to be used and speed up some of the algorithms used by the crate and it's dependencies. 49 | 50 | - `asm`: Enables inline-assembly implementations for some of the internal algorithms and primitives used by the `arkworks` dependencies of the crate. 51 | 52 | - `trace`: Enables the Circuit debugger tooling. This is essentially the capability of using the `StandardComposer::check_circuit_satisfied` function. The function will output information about each circuit gate until one of the gates does not satisfy the equation, or there are no more gates. If there is an unsatisfied gate equation, the function will panic and return the gate number. 53 | 54 | - `trace-print`: Goes a step further than `trace` and prints each `gate` component data, giving a clear overview of all the values which make up the circuit that we're constructing. __The recommended method is to derive the std output, and the std error, and then place them in text file which can be used to efficiently analyse the gates.__ 55 | 56 | ## Documentation 57 | 58 | There are two main types of documentation in this repository: 59 | 60 | - **Crate documentation**. This provides info about all of the functions that the library provides, as well 61 | as the documentation regarding the data structures that it exports. To check this, please feel free to go to 62 | the [documentation page](https://docs.rs/ark-plonk/) or run `make doc` or `make doc-internal`. 63 | 64 | - **Notes**. This is a specific subset of documentation which explains the key mathematical concepts 65 | of PLONK and how they work with mathematical demonstrations. To check it, run `make doc` and open the resulting docs, 66 | which will be located under `/target/doc/plonk/index.html` with your browser. 67 | 68 | **Examples**. Examples can be found in the `examples` folder. Run them, e.g., via `cargo run --example simple_circuit`. 69 | 70 | ## Performance 71 | 72 | Benches taken running: `RUSTFLAGS='-C target-cpu=native' cargo bench` with an `AMD Ryzen 7 3700X` 73 | These benches use the `Bls12-381` curve. 74 | 75 | Using `KZG10` commitments: 76 | ``` 77 | Compile: 78 | 2^5 [ 17.632 ms 17.669 ms 17.696 ms] 79 | 2^6 [ 22.666 ms 22.702 ms 22.747 ms] 80 | 2^7 [ 29.618 ms 29.653 ms 29.719 ms] 81 | 2^8 [ 47.467 ms 47.556 ms 47.609 ms] 82 | 2^9 [ 65.458 ms 65.786 ms 66.174 ms] 83 | 2^10 [ 97.172 ms 97.514 ms 97.897 ms] 84 | 2^11 [ 167.89 ms 168.17 ms 168.41 ms] 85 | 2^12 [ 314.51 ms 314.65 ms 314.78 ms] 86 | 2^13 [ 526.59 ms 527.63 ms 529.18 ms] 87 | 2^14 [ 1.0238 s 1.0253 s 1.0272 s] 88 | 2^15 [ 2.0029 s 2.0088 s 2.0143 s] 89 | 2^16 [ 3.7727 s 3.7846 s 3.7955 s] 90 | 2^17 [ 6.7340 s 6.7523 s 6.7700 s] 91 | 2^18 [13.584 s 13.613 s 13.640 s] 92 | 93 | Prove: 94 | 2^5 [ 16.172 ms 16.208 ms 16.264 ms] 95 | 2^6 [ 21.676 ms 21.712 ms 21.748 ms] 96 | 2^7 [ 29.493 ms 29.545 ms 29.613 ms] 97 | 2^8 [ 48.970 ms 49.039 ms 49.104 ms] 98 | 2^9 [ 72.251 ms 72.533 ms 72.703 ms] 99 | 2^10 [ 128.89 ms 130.71 ms 132.10 ms] 100 | 2^11 [ 242.91 ms 247.74 ms 252.29 ms] 101 | 2^12 [ 455.79 ms 459.45 ms 462.85 ms] 102 | 2^13 [ 776.94 ms 781.89 ms 787.94 ms] 103 | 2^14 [ 1.4752 s 1.4824 s 1.4893 s] 104 | 2^15 [ 2.8589 s 2.8682 s 2.8787 s] 105 | 2^16 [ 5.4610 s 5.4766 s 5.4927 s] 106 | 2^17 [10.078 s 10.118 s 10.159 s] 107 | 2^18 [20.151 s 20.184 s 20.216 s] 108 | 109 | Verify: 110 | 2^5 [ 5.5250 ms 5.5560 ms 5.5983 ms] 111 | 2^6 [ 5.4933 ms 5.5461 ms 5.5910 ms] 112 | 2^7 [ 5.5678 ms 5.6002 ms 5.6247 ms] 113 | 2^8 [ 5.5391 ms 5.5756 ms 5.6027 ms] 114 | 2^9 [ 5.5421 ms 5.5648 ms 5.5929 ms] 115 | 2^10 [ 5.5423 ms 5.5825 ms 5.6240 ms] 116 | 2^11 [ 5.5269 ms 5.5576 ms 5.6027 ms] 117 | 2^12 [ 5.5624 ms 5.6081 ms 5.6623 ms] 118 | 2^13 [ 5.6288 ms 5.6656 ms 5.6914 ms] 119 | 2^14 [ 5.6068 ms 5.6186 ms 5.6292 ms] 120 | 2^15 [ 5.5930 ms 5.6241 ms 5.6543 ms] 121 | 2^16 [ 6.0845 ms 6.1324 ms 6.1745 ms] 122 | 2^17 [ 6.5760 ms 6.5896 ms 6.6030 ms] 123 | 2^18 [ 8.1152 ms 8.1481 ms 8.1710 ms] 124 | ``` 125 | Using `IPA` commitments: 126 | ``` 127 | Compile: 128 | 2^5 [ 16.768 ms 16.818 ms 16.857 ms] 129 | 2^6 [ 21.958 ms 21.977 ms 21.993 ms] 130 | 2^7 [ 28.847 ms 28.869 ms 28.903 ms] 131 | 2^8 [ 47.626 ms 47.660 ms 47.693 ms] 132 | 2^9 [ 67.319 ms 67.485 ms 67.674 ms] 133 | 2^10 [ 98.526 ms 98.891 ms 99.072 ms] 134 | 2^11 [ 171.84 ms 172.06 ms 172.25 ms] 135 | 2^12 [ 322.42 ms 322.55 ms 322.69 ms] 136 | 2^13 [ 533.50 ms 533.95 ms 534.53 ms] 137 | 2^14 [ 1.0333 s 1.0342 s 1.0351 s] 138 | 2^15 [ 2.0156 s 2.0240 s 2.0308 s] 139 | 2^16 [ 3.8668 s 3.8769 s 3.8871 s] 140 | 2^17 [ 6.8066 s 6.8259 s 6.8506 s] 141 | 2^18 [13.757 s 13.773 s 13.788 s] 142 | 143 | Prove: 144 | 2^5 [ 32.205 ms 32.802 ms 33.418 ms] 145 | 2^6 [ 39.419 ms 39.479 ms 39.550 ms] 146 | 2^7 [ 53.665 ms 53.767 ms 53.876 ms] 147 | 2^8 [ 83.829 ms 84.005 ms 84.171 ms] 148 | 2^9 [ 127.58 ms 127.85 ms 128.11 ms] 149 | 2^10 [ 207.01 ms 208.50 ms 210.09 ms] 150 | 2^11 [ 397.91 ms 400.53 ms 403.63 ms] 151 | 2^12 [ 719.49 ms 725.85 ms 732.68 ms] 152 | 2^13 [ 1.2864 s 1.2912 s 1.2953 s] 153 | 2^14 [ 2.4494 s 2.4552 s 2.4620 s] 154 | 2^15 [ 4.7411 s 4.7617 s 4.7826 s] 155 | 2^16 [ 9.1925 s 9.2148 s 9.2360 s] 156 | 2^17 [17.499 s 17.584 s 17.660 s] 157 | 2^18 [35.019 s 35.084 s 35.138 s] 158 | 159 | Verify: 160 | 2^5 [ 7.9861 ms 8.0159 ms 8.0433 ms] 161 | 2^6 [ 8.9787 ms 9.0031 ms 9.0272 ms] 162 | 2^7 [ 10.648 ms 10.675 ms 10.714 ms] 163 | 2^8 [ 13.466 ms 13.526 ms 13.596 ms] 164 | 2^9 [ 17.140 ms 17.188 ms 17.267 ms] 165 | 2^10 [ 25.379 ms 25.574 ms 25.785 ms] 166 | 2^11 [ 34.424 ms 37.413 ms 38.720 ms] 167 | 2^12 [ 39.254 ms 39.429 ms 39.595 ms] 168 | 2^13 [ 69.872 ms 70.392 ms 70.790 ms] 169 | 2^14 [ 130.16 ms 130.93 ms 131.90 ms] 170 | 2^15 [ 243.71 ms 246.59 ms 249.40 ms] 171 | 2^16 [ 409.56 ms 415.00 ms 419.81 ms] 172 | 2^17 [ 777.07 ms 789.39 ms 801.28 ms] 173 | 2^18 [1.4931 s 1.4999 s 1.5065 s] 174 | ``` 175 | 176 | ## Acknowledgements 177 | 178 | - Reference [implementation](https://github.com/AztecProtocol/barretenberg) by Aztec Protocol 179 | - Initial [implementation](https://github.com/kobigurk/plonk/tree/kobigurk/port_to_zexe) of PLONK with arkworks backend was done years before this lib existed by Kobi Gurkan 180 | - Initial rust [implementation](https://github.com/dusk-network/plonk) of PLONK by Dusk Network, originally forked from dusk-network/plonk 181 | 182 | ## Licensing 183 | 184 | This software is distributed under the terms of Mozilla Public License Version 2.0 (MPL-2.0). Please see [LICENSE](https://github.com/ZK-Garage/plonk/blob/master/LICENSE) for further info. 185 | 186 | ## Contributing 187 | - If you want to contribute to this repository/project please, check [CONTRIBUTING.md](./CONTRIBUTING.md) 188 | - If you want to report a bug or request a new feature addition, please open an issue on this repository. 189 | -------------------------------------------------------------------------------- /benches/plonk.rs: -------------------------------------------------------------------------------- 1 | // Licensed under the Apache License, Version 2.0 or the MIT license 3 | // , at your 4 | // option. This file may not be copied, modified, or distributed 5 | // except according to those terms. 6 | // 7 | // Copyright (c) ZK-GARAGE. All rights reserved. 8 | 9 | //! PLONK Benchmarks 10 | 11 | use ark_bls12_377::Bls12_377; 12 | use ark_bls12_381::Bls12_381; 13 | use ark_ec::{PairingEngine, TEModelParameters}; 14 | use ark_ed_on_bls12_381::EdwardsParameters; 15 | use ark_ff::{FftField, PrimeField}; 16 | use blake2; 17 | use core::marker::PhantomData; 18 | use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; 19 | use plonk::commitment::{HomomorphicCommitment, IPA, KZG10}; 20 | use plonk::prelude::*; 21 | use rand_core::OsRng; 22 | 23 | /// Benchmark Circuit 24 | #[derive(derivative::Derivative)] 25 | #[derivative(Debug, Default)] 26 | pub struct BenchCircuit { 27 | /// Circuit Size 28 | size: usize, 29 | 30 | /// Field and parameters 31 | _phantom: PhantomData<(F, P)>, 32 | } 33 | 34 | impl BenchCircuit { 35 | /// Builds a new circuit with a constraint count of `2^degree`. 36 | #[inline] 37 | pub fn new(degree: usize) -> Self { 38 | Self { 39 | size: 1 << degree, 40 | _phantom: PhantomData::<(F, P)>, 41 | } 42 | } 43 | } 44 | 45 | impl Circuit for BenchCircuit 46 | where 47 | F: FftField + PrimeField, 48 | P: TEModelParameters, 49 | { 50 | const CIRCUIT_ID: [u8; 32] = [0xff; 32]; 51 | 52 | #[inline] 53 | fn gadget( 54 | &mut self, 55 | composer: &mut StandardComposer, 56 | ) -> Result<(), Error> { 57 | composer.add_dummy_lookup_table(); 58 | while composer.circuit_bound() < self.size - 1 { 59 | composer.add_dummy_constraints(); 60 | } 61 | Ok(()) 62 | } 63 | 64 | #[inline] 65 | fn padded_circuit_size(&self) -> usize { 66 | self.size 67 | } 68 | } 69 | 70 | fn kzg10_benchmarks(c: &mut Criterion) { 71 | constraint_system_benchmark::< 72 | ::Fr, 73 | EdwardsParameters, 74 | KZG10, 75 | >("KZG10", c); 76 | } 77 | 78 | fn ipa_benchmarks(c: &mut Criterion) { 79 | constraint_system_benchmark::< 80 | ::Fr, 81 | ark_ed_on_bls12_377::EdwardsParameters, 82 | IPA<::G1Affine, blake2::Blake2b>, 83 | >("IPA", c); 84 | } 85 | 86 | /// Generates full benchmark suite for compiling, proving, and verifying. 87 | fn constraint_system_benchmark(name: &str, c: &mut Criterion) 88 | where 89 | F: PrimeField, 90 | P: TEModelParameters, 91 | HC: HomomorphicCommitment, 92 | { 93 | let label = b"ark".as_slice(); 94 | 95 | const MINIMUM_DEGREE: usize = 5; 96 | const MAXIMUM_DEGREE: usize = 19; 97 | 98 | let pp = HC::setup(1 << MAXIMUM_DEGREE, None, &mut OsRng) 99 | .expect("Unable to sample public parameters."); 100 | 101 | let mut compiling_benchmarks = 102 | c.benchmark_group(format!("{0}/compile", name)); 103 | for degree in MINIMUM_DEGREE..MAXIMUM_DEGREE { 104 | let mut circuit = BenchCircuit::::new(degree); 105 | compiling_benchmarks.bench_with_input( 106 | BenchmarkId::from_parameter(degree), 107 | °ree, 108 | |b, _| { 109 | b.iter(|| { 110 | circuit 111 | .compile::(&pp) 112 | .expect("Unable to compile circuit.") 113 | }) 114 | }, 115 | ); 116 | } 117 | compiling_benchmarks.finish(); 118 | 119 | let mut proving_benchmarks = c.benchmark_group("prove"); 120 | for degree in MINIMUM_DEGREE..MAXIMUM_DEGREE { 121 | let mut circuit = BenchCircuit::::new(degree); 122 | let (pk_p, _) = circuit 123 | .compile::(&pp) 124 | .expect("Unable to compile circuit."); 125 | proving_benchmarks.bench_with_input( 126 | BenchmarkId::from_parameter(degree), 127 | °ree, 128 | |b, _| { 129 | b.iter(|| { 130 | circuit.gen_proof::(&pp, pk_p.clone(), &label).unwrap() 131 | }) 132 | }, 133 | ); 134 | } 135 | proving_benchmarks.finish(); 136 | 137 | let mut verifying_benchmarks = c.benchmark_group("verify"); 138 | for degree in MINIMUM_DEGREE..MAXIMUM_DEGREE { 139 | let mut circuit = BenchCircuit::::new(degree); 140 | let (pk_p, (vk, _pi_pos)) = 141 | circuit.compile(&pp).expect("Unable to compile circuit."); 142 | let (proof, pi) = 143 | circuit.gen_proof::(&pp, pk_p.clone(), &label).unwrap(); 144 | verifying_benchmarks.bench_with_input( 145 | BenchmarkId::from_parameter(degree), 146 | °ree, 147 | |b, _| { 148 | b.iter(|| { 149 | plonk::circuit::verify_proof::( 150 | &pp, 151 | vk.clone(), 152 | &proof, 153 | &pi, 154 | &label, 155 | ) 156 | .expect("Unable to verify benchmark circuit."); 157 | }) 158 | }, 159 | ); 160 | } 161 | verifying_benchmarks.finish(); 162 | } 163 | 164 | criterion_group! { 165 | name = plonk; 166 | config = Criterion::default().sample_size(10); 167 | targets = kzg10_benchmarks, ipa_benchmarks 168 | } 169 | criterion_main!(plonk); 170 | -------------------------------------------------------------------------------- /examples/example_poly.rs: -------------------------------------------------------------------------------- 1 | // Licensed under the Apache License, Version 2.0 or the MIT license 3 | // , at your 4 | // option. This file may not be copied, modified, or distributed 5 | // except according to those terms. 6 | // 7 | // Copyright (c) ZK-GARAGE. All rights reserved. 8 | 9 | //! PLONK Example 10 | 11 | use ark_bls12_381::{Bls12_381, Fr as BlsScalar}; 12 | use ark_ec::TEModelParameters; 13 | use ark_ed_on_bls12_381::{ 14 | EdwardsParameters as JubJubParameters, Fr as JubJubScalar, 15 | }; 16 | use ark_ff::PrimeField; 17 | use ark_poly::polynomial::univariate::DensePolynomial; 18 | use ark_poly_commit::{sonic_pc::SonicKZG10, PolynomialCommitment}; 19 | use plonk::error::to_pc_error; 20 | use plonk_core::circuit::{verify_proof, Circuit}; 21 | use plonk_core::constraint_system::StandardComposer; 22 | use plonk_core::error::Error; 23 | use plonk_core::prelude::*; 24 | use rand_core::OsRng; 25 | 26 | fn main() -> Result<(), Error> { 27 | // Implements a circuit that defines the following function: 28 | // ------------------- 29 | // def f(x, y, z): 30 | // if x == 1: 31 | // return y*z 32 | // return 2y - z 33 | // ------------------- 34 | // where x,y,z are inputs and r is the output 35 | 36 | #[derive(derivative::Derivative)] 37 | #[derivative(Debug(bound = ""), Default(bound = ""))] 38 | pub struct TestCircuit 39 | where 40 | F: PrimeField, 41 | P: TEModelParameters, 42 | { 43 | x: F, 44 | y: F, 45 | z: F, 46 | r: F, 47 | dummy: P::ScalarField, 48 | } 49 | 50 | impl Circuit for TestCircuit 51 | where 52 | F: PrimeField, 53 | P: TEModelParameters, 54 | { 55 | const CIRCUIT_ID: [u8; 32] = [0xff; 32]; 56 | 57 | fn gadget( 58 | &mut self, 59 | composer: &mut StandardComposer, 60 | ) -> Result<(), Error> { 61 | let x = composer.add_input(self.x); 62 | let y = composer.add_input(self.y); 63 | let z = composer.add_input(self.z); 64 | let r = composer.add_input(self.r); 65 | 66 | let one = composer.add_input(F::one()); 67 | let zero = composer.zero_var(); 68 | 69 | // Define 2y-z 70 | let two_y_minus_z = composer.arithmetic_gate(|gate| { 71 | gate.witness(y, z, None).add(F::from(2u64), -F::one()) 72 | }); 73 | 74 | // Define y*z 75 | let y_times_z = composer 76 | .arithmetic_gate(|gate| gate.witness(y, z, None).mul(F::one())); 77 | 78 | // Define x-1 79 | let x_minus_1 = composer.arithmetic_gate(|gate| { 80 | gate.witness(x, zero, None) 81 | .add(F::one(), F::zero()) 82 | .constant(-F::one()) 83 | }); 84 | 85 | // Define x-1==0 86 | let x_bool = composer.is_zero_with_output(x_minus_1); 87 | 88 | // Define x bool negate 89 | // x_bool_negate also be implemented with an add gate as 90 | // x_bool_negate = 1 - x_bool 91 | let x_bool_negate = composer.xor_gate(x_bool, one, 10); 92 | 93 | // Define functon part 1: I(x==1)yz 94 | let function_part_1 = composer.arithmetic_gate(|gate| { 95 | gate.witness(x_bool, y_times_z, None).mul(F::one()) 96 | }); 97 | 98 | // Define functon part 2: I(x!=1)(2y-z) 99 | let function_part_2 = composer.arithmetic_gate(|gate| { 100 | gate.witness(x_bool_negate, two_y_minus_z, None) 101 | .mul(F::one()) 102 | }); 103 | 104 | // Define the full function as 105 | // r = I(x==1)yz + I(x!=1)(2y-z) 106 | let full_function = composer.arithmetic_gate(|gate| { 107 | gate.witness(function_part_1, function_part_2, None) 108 | .add(F::one(), F::one()) 109 | }); 110 | 111 | composer.assert_equal(full_function, r); 112 | 113 | Ok(()) 114 | } 115 | 116 | fn padded_circuit_size(&self) -> usize { 117 | 1 << 9 118 | } 119 | } 120 | 121 | // Generate CRS 122 | type PC = SonicKZG10>; 123 | let pp = PC::setup(1 << 10, None, &mut OsRng) 124 | .map_err(to_pc_error::)?; 125 | 126 | let mut circuit = TestCircuit::::default(); 127 | // Compile the circuit 128 | let (pk_p, (vk, _pi_pos)) = circuit.compile::(&pp)?; 129 | 130 | // Prover POV 131 | let x = 1u64; 132 | let y = 2u64; 133 | let z = 5u64; 134 | let r = 10u64; 135 | println!("x:{}, y:{}, z:{}, r:{}", x, y, z, r); 136 | let (proof, pi) = { 137 | let mut circuit: TestCircuit = 138 | TestCircuit { 139 | x: BlsScalar::from(x), 140 | y: BlsScalar::from(y), 141 | z: BlsScalar::from(z), 142 | r: BlsScalar::from(r), 143 | dummy: JubJubScalar::from(2u64), 144 | }; 145 | circuit.gen_proof::(&pp, pk_p, b"Test") 146 | }?; 147 | 148 | // Verifier POV 149 | let verifier_data = VerifierData::new(vk, pi); 150 | verify_proof::( 151 | &pp, 152 | verifier_data.key, 153 | &proof, 154 | &verifier_data.pi, 155 | b"Test", 156 | ) 157 | } 158 | -------------------------------------------------------------------------------- /examples/simple_circuit.rs: -------------------------------------------------------------------------------- 1 | // Licensed under the Apache License, Version 2.0 or the MIT license 3 | // , at your 4 | // option. This file may not be copied, modified, or distributed 5 | // except according to those terms. 6 | // 7 | // Copyright (c) ZK-GARAGE. All rights reserved. 8 | 9 | //! PLONK Example 10 | 11 | use ark_bls12_381::{Bls12_381, Fr as BlsScalar}; 12 | use ark_ec::models::twisted_edwards_extended::GroupAffine; 13 | use ark_ec::{AffineCurve, ProjectiveCurve, TEModelParameters}; 14 | use ark_ed_on_bls12_381::{ 15 | EdwardsParameters as JubJubParameters, Fr as JubJubScalar, 16 | }; 17 | use ark_ff::PrimeField; 18 | use ark_poly::polynomial::univariate::DensePolynomial; 19 | use ark_poly_commit::{sonic_pc::SonicKZG10, PolynomialCommitment}; 20 | use plonk::error::to_pc_error; 21 | use plonk_core::circuit::{verify_proof, Circuit}; 22 | use plonk_core::constraint_system::StandardComposer; 23 | use plonk_core::error::Error; 24 | use plonk_core::prelude::*; 25 | use rand_core::OsRng; 26 | 27 | fn main() -> Result<(), Error> { 28 | // Implements a circuit that checks: 29 | // 1) a + b = c where C is a PI 30 | // 2) a <= 2^6 31 | // 3) b <= 2^4 32 | // 4) a * b = d where D is a PI 33 | // 5) JubJub::GENERATOR * e(JubJubScalar) = f where F is a PI 34 | #[derive(derivative::Derivative)] 35 | #[derivative(Debug(bound = ""), Default(bound = ""))] 36 | pub struct TestCircuit 37 | where 38 | F: PrimeField, 39 | P: TEModelParameters, 40 | { 41 | a: F, 42 | b: F, 43 | c: F, 44 | d: F, 45 | e: P::ScalarField, 46 | f: GroupAffine

, 47 | } 48 | 49 | impl Circuit for TestCircuit 50 | where 51 | F: PrimeField, 52 | P: TEModelParameters, 53 | { 54 | const CIRCUIT_ID: [u8; 32] = [0xff; 32]; 55 | 56 | fn gadget( 57 | &mut self, 58 | composer: &mut StandardComposer, 59 | ) -> Result<(), Error> { 60 | let a = composer.add_input(self.a); 61 | let b = composer.add_input(self.b); 62 | let zero = composer.zero_var(); 63 | 64 | // Make first constraint a + b = c (as public input) 65 | composer.arithmetic_gate(|gate| { 66 | gate.witness(a, b, Some(zero)) 67 | .add(F::one(), F::one()) 68 | .pi(-self.c) 69 | }); 70 | 71 | // Check that a and b are in range 72 | composer.range_gate(a, 6); 73 | composer.range_gate(b, 4); 74 | // Make second constraint a * b = d 75 | composer.arithmetic_gate(|gate| { 76 | gate.witness(a, b, Some(zero)).mul(F::one()).pi(-self.d) 77 | }); 78 | let e = 79 | composer.add_input(from_embedded_curve_scalar::(self.e)); 80 | let (x, y) = P::AFFINE_GENERATOR_COEFFS; 81 | let generator = GroupAffine::new(x, y); 82 | let scalar_mul_result = 83 | composer.fixed_base_scalar_mul(e, generator); 84 | // Apply the constrain 85 | composer.assert_equal_public_point(scalar_mul_result, self.f); 86 | Ok(()) 87 | } 88 | 89 | fn padded_circuit_size(&self) -> usize { 90 | 1 << 9 91 | } 92 | } 93 | 94 | // Generate CRS 95 | type PC = SonicKZG10>; 96 | let pp = PC::setup(1 << 10, None, &mut OsRng) 97 | .map_err(to_pc_error::)?; 98 | 99 | let mut circuit = TestCircuit::::default(); 100 | // Compile the circuit 101 | let (pk_p, (vk, _pi_pos)) = circuit.compile::(&pp)?; 102 | 103 | let (x, y) = JubJubParameters::AFFINE_GENERATOR_COEFFS; 104 | let generator: GroupAffine = GroupAffine::new(x, y); 105 | let point_f_pi: GroupAffine = 106 | AffineCurve::mul(&generator, JubJubScalar::from(2u64).into_repr()) 107 | .into_affine(); 108 | // Prover POV 109 | let (proof, pi) = { 110 | let mut circuit: TestCircuit = 111 | TestCircuit { 112 | a: BlsScalar::from(20u64), 113 | b: BlsScalar::from(5u64), 114 | c: BlsScalar::from(25u64), 115 | d: BlsScalar::from(100u64), 116 | e: JubJubScalar::from(2u64), 117 | f: point_f_pi, 118 | }; 119 | circuit.gen_proof::(&pp, pk_p, b"Test") 120 | }?; 121 | 122 | // Verifier POV 123 | let verifier_data = VerifierData::new(vk, pi); 124 | verify_proof::( 125 | &pp, 126 | verifier_data.key, 127 | &proof, 128 | &verifier_data.pi, 129 | b"Test", 130 | ) 131 | } 132 | -------------------------------------------------------------------------------- /plonk-book/.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | -------------------------------------------------------------------------------- /plonk-book/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["mathcrypto"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "PLONK Book" 7 | 8 | [output.html] 9 | [output.katex] 10 | 11 | [preprocessor.katex] 12 | macros = "macros.txt" 13 | 14 | -------------------------------------------------------------------------------- /plonk-book/macros.txt: -------------------------------------------------------------------------------- 1 | \grad:{\nabla} 2 | \R:{\mathbb{R}^{#1 \times #2}} -------------------------------------------------------------------------------- /plonk-book/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Chapter 1: Background Material](./chapter_1.md) 4 | - [Chapter 2: PLONK](./chapter_2.md) 5 | - [Chapter 3: PLONK library](./chapter_3.md) 6 | -------------------------------------------------------------------------------- /plonk-book/src/images/images/EC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZK-Garage/plonk/4fc762ac433a942939ad43a3b29050a26ce52533/plonk-book/src/images/images/EC.png -------------------------------------------------------------------------------- /plonk-book/src/images/images/ECP.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZK-Garage/plonk/4fc762ac433a942939ad43a3b29050a26ce52533/plonk-book/src/images/images/ECP.png -------------------------------------------------------------------------------- /plonk-book/src/images/images/circuit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZK-Garage/plonk/4fc762ac433a942939ad43a3b29050a26ce52533/plonk-book/src/images/images/circuit.png -------------------------------------------------------------------------------- /plonk-book/src/images/images/gate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZK-Garage/plonk/4fc762ac433a942939ad43a3b29050a26ce52533/plonk-book/src/images/images/gate.png -------------------------------------------------------------------------------- /plonk-book/src/images/images/gate1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZK-Garage/plonk/4fc762ac433a942939ad43a3b29050a26ce52533/plonk-book/src/images/images/gate1.png -------------------------------------------------------------------------------- /plonk-book/src/images/images/logicgate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZK-Garage/plonk/4fc762ac433a942939ad43a3b29050a26ce52533/plonk-book/src/images/images/logicgate.png -------------------------------------------------------------------------------- /plonk-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "plonk-core" 3 | version = "0.1.0" 4 | authors = ["ZK-GARAGE Authors"] 5 | readme = "README.md" 6 | repository = "https://github.com/zk-garage/plonk" 7 | keywords = ["cryptography", "plonk", "zk-snarks", "zero-knowledge", "crypto"] 8 | categories = ["algorithms", "cryptography", "science"] 9 | description = "A pure-Rust implementation of the PLONK ZK-Proof algorithm." 10 | license = "MIT OR Apache-2.0" 11 | edition = "2021" 12 | 13 | [package.metadata.docs.rs] 14 | # To build locally: 15 | # RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --open 16 | all-features = true 17 | rustdoc-args = ["--cfg", "doc_cfg"] 18 | 19 | [features] 20 | # Default Features 21 | default = [ 22 | # "trace-print", 23 | "trace", 24 | "asm", 25 | "itertools/default", 26 | "parallel", 27 | ] 28 | 29 | # Raw Assembly 30 | asm = ["ark-ff/asm"] 31 | 32 | # Parallelism Features 33 | parallel = [ 34 | "ark-ec/parallel", 35 | "ark-ff/parallel", 36 | "ark-poly-commit/parallel", 37 | "ark-poly/parallel", 38 | ] 39 | 40 | # Enable Standard Library 41 | std = [ 42 | "ark-ec/std", 43 | "ark-ff/std", 44 | ] 45 | 46 | # Minimal Tracing Features 47 | trace = [] 48 | 49 | # Tracing Printing Features 50 | trace-print = ["trace", "std"] 51 | 52 | [dependencies] 53 | ark-bls12-381 = "0.3" 54 | ark-std = { version = "0.3", features = ["std"] } 55 | blake2 = "0.9" 56 | ark-ec = { version = "0.3", default-features = false } 57 | ark-ff = { version = "0.3", default-features = false } 58 | ark-poly = { version = "0.3" } 59 | ark-poly-commit = { version = "0.3" } 60 | ark-serialize = { version = "0.3", features = ["derive"] } 61 | derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } 62 | hashbrown = { version = "0.11.2", default-features = false, features = ["ahash"] } 63 | indexmap = { version = "1.8.1", default-features = false } 64 | itertools = { version = "0.10.1", default-features = false } 65 | merlin = { version = "3.0", default-features = false } 66 | num-traits = { version = "0.2.14" } 67 | rand_core = {version = "0.6", default-features=false, features = ["getrandom"] } 68 | 69 | [dev-dependencies] 70 | ark-bls12-377 = "0.3" 71 | ark-bls12-381 = "0.3" 72 | ark-ed-on-bls12-377 = "0.3" 73 | ark-ed-on-bls12-381 = "0.3" 74 | criterion = "0.3" 75 | paste = "1.0.6" 76 | tempdir = "0.3" 77 | cfg-if = "0.1" 78 | -------------------------------------------------------------------------------- /plonk-core/README.md: -------------------------------------------------------------------------------- 1 | # PLONK Core Library -------------------------------------------------------------------------------- /plonk-core/src/commitment.rs: -------------------------------------------------------------------------------- 1 | //! Useful commitment stuff 2 | use ark_ec::{msm::VariableBaseMSM, AffineCurve, PairingEngine}; 3 | use ark_ff::{Field, PrimeField}; 4 | use ark_poly::univariate::DensePolynomial; 5 | use ark_poly_commit::{sonic_pc::SonicKZG10, PolynomialCommitment}; 6 | 7 | /// A homomorphic polynomial commitment 8 | pub trait HomomorphicCommitment: 9 | PolynomialCommitment> 10 | where 11 | F: PrimeField, 12 | Self::VerifierKey: core::fmt::Debug, 13 | { 14 | /// Combine a linear combination of homomorphic commitments 15 | fn multi_scalar_mul( 16 | commitments: &[Self::Commitment], 17 | scalars: &[F], 18 | ) -> Self::Commitment; 19 | } 20 | 21 | /// The Default KZG-style commitment scheme 22 | pub type KZG10 = SonicKZG10::Fr>>; 23 | /// A single KZG10 commitment 24 | pub type KZG10Commitment = as PolynomialCommitment< 25 | ::Fr, 26 | DensePolynomial<::Fr>, 27 | >>::Commitment; 28 | 29 | impl HomomorphicCommitment for KZG10 30 | where 31 | E: PairingEngine, 32 | { 33 | fn multi_scalar_mul( 34 | commitments: &[KZG10Commitment], 35 | scalars: &[E::Fr], 36 | ) -> KZG10Commitment { 37 | let scalars_repr = scalars 38 | .iter() 39 | .map(::into_repr) 40 | .collect::>(); 41 | 42 | let points_repr = commitments.iter().map(|c| c.0).collect::>(); 43 | 44 | ark_poly_commit::kzg10::Commitment::( 45 | VariableBaseMSM::multi_scalar_mul(&points_repr, &scalars_repr) 46 | .into(), 47 | ) 48 | } 49 | } 50 | 51 | /// Shortened type for Inner Product Argument polynomial commitment schemes 52 | pub type IPA = ark_poly_commit::ipa_pc::InnerProductArgPC< 53 | G, 54 | D, 55 | DensePolynomial<::ScalarField>, 56 | >; 57 | /// Shortened type for an Inner Product Argument polynomial commitment 58 | pub type IPACommitment = as PolynomialCommitment< 59 | ::ScalarField, 60 | DensePolynomial<::ScalarField>, 61 | >>::Commitment; 62 | 63 | use blake2::digest::Digest; 64 | impl HomomorphicCommitment<::ScalarField> 65 | for IPA 66 | where 67 | G: AffineCurve, 68 | D: Digest, 69 | { 70 | fn multi_scalar_mul( 71 | commitments: &[IPACommitment], 72 | scalars: &[::ScalarField], 73 | ) -> IPACommitment { 74 | let scalars_repr = scalars 75 | .iter() 76 | .map(::ScalarField::into_repr) 77 | .collect::>(); 78 | 79 | let points_repr = 80 | commitments.iter().map(|c| c.comm).collect::>(); 81 | 82 | IPACommitment:: { 83 | comm: VariableBaseMSM::multi_scalar_mul( 84 | &points_repr, 85 | &scalars_repr, 86 | ) 87 | .into(), 88 | shifted_comm: None, // TODO: support degree bounds? 89 | } 90 | } 91 | } 92 | 93 | /// Computes a linear combination of the polynomial evaluations and polynomial 94 | /// commitments provided a challenge. 95 | // TODO: complete doc & use util::lc for eval combination 96 | pub fn linear_combination( 97 | evals: &[F], 98 | commitments: &[H::Commitment], 99 | challenge: F, 100 | ) -> (H::Commitment, F) 101 | where 102 | F: PrimeField, 103 | H: HomomorphicCommitment, 104 | { 105 | assert_eq!(evals.len(), commitments.len()); 106 | let powers = crate::util::powers_of(challenge) 107 | .take(evals.len()) 108 | .collect::>(); 109 | let combined_eval = evals 110 | .iter() 111 | .zip(powers.iter()) 112 | .map(|(&eval, power)| eval * power) 113 | .sum(); 114 | let combined_commitment = H::multi_scalar_mul(commitments, &powers); 115 | (combined_commitment, combined_eval) 116 | } 117 | 118 | /// Aggregate polynomials 119 | pub fn aggregate_polynomials( 120 | polynomials: &[DensePolynomial], 121 | challenge: F, 122 | ) -> DensePolynomial { 123 | use core::ops::Add; 124 | use num_traits::Zero; 125 | crate::util::powers_of(challenge) 126 | .zip(polynomials) 127 | .map(|(challenge, poly)| poly * challenge) 128 | .fold(Zero::zero(), Add::add) 129 | } 130 | -------------------------------------------------------------------------------- /plonk-core/src/constraint_system/boolean.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) DUSK NETWORK. All rights reserved. 6 | 7 | //! Boolean Gates 8 | 9 | use crate::constraint_system::{StandardComposer, Variable}; 10 | use ark_ec::ModelParameters; 11 | use ark_ff::PrimeField; 12 | 13 | impl StandardComposer 14 | where 15 | F: PrimeField, 16 | P: ModelParameters, 17 | { 18 | /// Adds a boolean constraint (also known as binary constraint) where 19 | /// the gate eq. will enforce that the [`Variable`] received is either `0` 20 | /// or `1` by adding a constraint in the circuit. 21 | /// 22 | /// Note that using this constraint with whatever [`Variable`] that is not 23 | /// representing a value equalling 0 or 1, will always force the equation to 24 | /// fail. 25 | pub fn boolean_gate(&mut self, a: Variable) -> Variable { 26 | self.w_l.push(a); 27 | self.w_r.push(a); 28 | self.w_o.push(a); 29 | self.w_4.push(self.zero_var); 30 | 31 | self.q_m.push(F::one()); 32 | self.q_l.push(F::zero()); 33 | self.q_r.push(F::zero()); 34 | self.q_o.push(-F::one()); 35 | self.q_c.push(F::zero()); 36 | self.q_4.push(F::zero()); 37 | self.q_arith.push(F::one()); 38 | 39 | // add high degree selectors 40 | self.q_hl.push(F::zero()); 41 | self.q_hr.push(F::zero()); 42 | self.q_h4.push(F::zero()); 43 | 44 | self.q_range.push(F::zero()); 45 | self.q_logic.push(F::zero()); 46 | self.q_fixed_group_add.push(F::zero()); 47 | self.q_variable_group_add.push(F::zero()); 48 | self.q_lookup.push(F::zero()); 49 | 50 | self.perm 51 | .add_variables_to_map(a, a, a, self.zero_var, self.n); 52 | 53 | self.n += 1; 54 | 55 | a 56 | } 57 | } 58 | 59 | #[cfg(test)] 60 | mod test { 61 | use super::*; 62 | use crate::{ 63 | batch_test, commitment::HomomorphicCommitment, 64 | constraint_system::helper::*, 65 | }; 66 | use ark_bls12_377::Bls12_377; 67 | use ark_bls12_381::Bls12_381; 68 | use ark_ec::TEModelParameters; 69 | 70 | fn test_correct_bool_gate() 71 | where 72 | F: PrimeField, 73 | P: TEModelParameters, 74 | PC: HomomorphicCommitment, 75 | { 76 | let res = gadget_tester::( 77 | |composer: &mut StandardComposer| { 78 | let zero = composer.zero_var(); 79 | let one = composer.add_input(F::one()); 80 | composer.boolean_gate(zero); 81 | composer.boolean_gate(one); 82 | }, 83 | 32, 84 | ); 85 | assert!(res.is_ok()) 86 | } 87 | 88 | fn test_incorrect_bool_gate() 89 | where 90 | F: PrimeField, 91 | P: TEModelParameters, 92 | PC: HomomorphicCommitment, 93 | { 94 | let res = gadget_tester::( 95 | |composer: &mut StandardComposer| { 96 | let zero = composer.add_input(F::from(5u64)); 97 | let one = composer.add_input(F::one()); 98 | composer.boolean_gate(zero); 99 | composer.boolean_gate(one); 100 | }, 101 | 32, 102 | ); 103 | assert!(res.is_err()) 104 | } 105 | 106 | // Test for Bls12_381 107 | batch_test!( 108 | [ 109 | test_correct_bool_gate, 110 | test_incorrect_bool_gate 111 | ], 112 | [] => ( 113 | Bls12_381, ark_ed_on_bls12_381::EdwardsParameters 114 | ) 115 | ); 116 | 117 | // Test for Bls12_377 118 | batch_test!( 119 | [ 120 | test_correct_bool_gate, 121 | test_incorrect_bool_gate 122 | ], 123 | [] => ( 124 | Bls12_377, ark_ed_on_bls12_377::EdwardsParameters ) 125 | ); 126 | } 127 | -------------------------------------------------------------------------------- /plonk-core/src/constraint_system/ecc/curve_addition/fixed_base_gate.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) DUSK NETWORK. All rights reserved. 6 | 7 | //! Fixed-Base Curve Addition Gate 8 | 9 | use crate::constraint_system::{StandardComposer, Variable}; 10 | use ark_ec::models::TEModelParameters; 11 | use ark_ff::PrimeField; 12 | 13 | /// Contains all of the components needed to verify that a bit scalar 14 | /// multiplication was computed correctly. 15 | #[derive(derivative::Derivative)] 16 | #[derivative(Clone, Copy, Debug)] 17 | pub struct WnafRound

18 | where 19 | P: TEModelParameters, 20 | { 21 | /// This is the accumulated x coordinate point that we wish to add (so 22 | /// far, it depends on where you are in the scalar mul) it is linked to 23 | /// the wnaf entry, so must not be revealed 24 | pub acc_x: Variable, 25 | 26 | /// This is the accumulated y coordinate 27 | pub acc_y: Variable, 28 | 29 | /// This is the wnaf accumulated entry 30 | /// For all intents and purposes, you can think of this as the secret bit 31 | pub accumulated_bit: Variable, 32 | 33 | /// This is the multiplication of x_\alpha * y_\alpha 34 | /// we need this as a distinct wire, so that the degree of the polynomial 35 | /// does not go over 4 36 | pub xy_alpha: Variable, 37 | 38 | /// This is the possible x co-ordinate of the wnaf point we are going to 39 | /// add Actual x-co-ordinate = b_i * x_\beta 40 | pub x_beta: P::BaseField, 41 | 42 | /// This is the possible y co-ordinate of the wnaf point we are going to 43 | /// add Actual y coordinate = (b_i)^2 [y_\beta -1] + 1 44 | pub y_beta: P::BaseField, 45 | 46 | /// This is the multiplication of x_\beta * y_\beta 47 | pub xy_beta: P::BaseField, 48 | } 49 | 50 | impl StandardComposer 51 | where 52 | F: PrimeField, 53 | P: TEModelParameters, 54 | { 55 | /// Generates a new structure for preparing a [`WnafRound`] ROUND. 56 | pub(crate) fn new_wnaf( 57 | acc_x: Variable, 58 | acc_y: Variable, 59 | accumulated_bit: Variable, 60 | xy_alpha: Variable, 61 | x_beta: F, 62 | y_beta: F, 63 | xy_beta: F, 64 | ) -> WnafRound

{ 65 | WnafRound { 66 | acc_x, 67 | acc_y, 68 | accumulated_bit, 69 | xy_alpha, 70 | x_beta, 71 | y_beta, 72 | xy_beta, 73 | } 74 | } 75 | 76 | /// Fixed group addition of a point. 77 | pub(crate) fn fixed_group_add(&mut self, wnaf_round: WnafRound

) { 78 | self.w_l.push(wnaf_round.acc_x); 79 | self.w_r.push(wnaf_round.acc_y); 80 | self.w_o.push(wnaf_round.xy_alpha); 81 | self.w_4.push(wnaf_round.accumulated_bit); 82 | 83 | self.q_l.push(wnaf_round.x_beta); 84 | self.q_r.push(wnaf_round.y_beta); 85 | 86 | self.q_c.push(wnaf_round.xy_beta); 87 | self.q_o.push(F::zero()); 88 | self.q_fixed_group_add.push(F::one()); 89 | self.q_variable_group_add.push(F::zero()); 90 | 91 | self.q_m.push(F::zero()); 92 | self.q_4.push(F::zero()); 93 | self.q_arith.push(F::zero()); 94 | self.q_range.push(F::zero()); 95 | self.q_logic.push(F::zero()); 96 | self.q_lookup.push(F::zero()); 97 | 98 | // add high degree selectors 99 | self.q_hl.push(F::zero()); 100 | self.q_hr.push(F::zero()); 101 | self.q_h4.push(F::zero()); 102 | 103 | self.perm.add_variables_to_map( 104 | wnaf_round.acc_x, 105 | wnaf_round.acc_y, 106 | wnaf_round.xy_alpha, 107 | wnaf_round.accumulated_bit, 108 | self.n, 109 | ); 110 | 111 | self.n += 1; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /plonk-core/src/constraint_system/ecc/curve_addition/mod.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) DUSK NETWORK. All rights reserved. 6 | 7 | //! Curve Addition Gates 8 | 9 | mod fixed_base_gate; 10 | mod variable_base_gate; 11 | -------------------------------------------------------------------------------- /plonk-core/src/constraint_system/ecc/curve_addition/variable_base_gate.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) DUSK NETWORK. All rights reserved. 6 | 7 | //! Variable-base Curve Addition Gate 8 | 9 | use crate::constraint_system::{ecc::Point, StandardComposer}; 10 | use ark_ec::models::{ 11 | twisted_edwards_extended::GroupAffine as TEGroupAffine, TEModelParameters, 12 | }; 13 | use ark_ff::PrimeField; 14 | 15 | impl StandardComposer 16 | where 17 | F: PrimeField, 18 | P: TEModelParameters, 19 | { 20 | /// Adds two curve points together using a curve addition gate 21 | /// Note that since the points are not fixed the generator is not a part of 22 | /// the circuit description, however it is less efficient for a program 23 | /// width of 4. 24 | pub fn point_addition_gate( 25 | &mut self, 26 | point_a: Point

, 27 | point_b: Point

, 28 | ) -> Point

{ 29 | // In order to verify that two points were correctly added 30 | // without going over a degree 4 polynomial, we will need 31 | // x_1, y_1, x_2, y_2 32 | // x_3, y_3, x_1 * y_2 33 | 34 | let x_1 = point_a.x; 35 | let y_1 = point_a.y; 36 | let x_2 = point_b.x; 37 | let y_2 = point_b.y; 38 | 39 | // Compute the resulting point 40 | let x_1_scalar = self.variables.get(&x_1).unwrap(); 41 | let y_1_scalar = self.variables.get(&y_1).unwrap(); 42 | let x_2_scalar = self.variables.get(&x_2).unwrap(); 43 | let y_2_scalar = self.variables.get(&y_2).unwrap(); 44 | 45 | let p1 = TEGroupAffine::

::new(*x_1_scalar, *y_1_scalar); 46 | let p2 = TEGroupAffine::

::new(*x_2_scalar, *y_2_scalar); 47 | 48 | let point = p1 + p2; 49 | let x_3_scalar = point.x; 50 | let y_3_scalar = point.y; 51 | 52 | let x1_scalar_y2_scalar = *x_1_scalar * y_2_scalar; 53 | 54 | // Add the rest of the prepared points into the composer 55 | let x_1_y_2 = self.add_input(x1_scalar_y2_scalar); 56 | let x_3 = self.add_input(x_3_scalar); 57 | let y_3 = self.add_input(y_3_scalar); 58 | 59 | self.w_l.extend(&[x_1, x_3]); 60 | self.w_r.extend(&[y_1, y_3]); 61 | self.w_o.extend(&[x_2, self.zero_var]); 62 | self.w_4.extend(&[y_2, x_1_y_2]); 63 | let zeros = [F::zero(), F::zero()]; 64 | 65 | self.q_l.extend(&zeros); 66 | self.q_r.extend(&zeros); 67 | self.q_c.extend(&zeros); 68 | self.q_o.extend(&zeros); 69 | self.q_m.extend(&zeros); 70 | self.q_4.extend(&zeros); 71 | self.q_arith.extend(&zeros); 72 | self.q_range.extend(&zeros); 73 | self.q_logic.extend(&zeros); 74 | self.q_fixed_group_add.extend(&zeros); 75 | self.q_lookup.extend(&zeros); 76 | 77 | // add high degree selectors 78 | self.q_hl.extend(zeros.iter()); 79 | self.q_hr.extend(zeros.iter()); 80 | self.q_h4.extend(zeros.iter()); 81 | 82 | self.q_variable_group_add.push(F::one()); 83 | self.q_variable_group_add.push(F::zero()); 84 | 85 | self.perm.add_variables_to_map(x_1, y_1, x_2, y_2, self.n); 86 | self.n += 1; 87 | 88 | self.perm.add_variables_to_map( 89 | x_3, 90 | y_3, 91 | self.zero_var, 92 | x_1_y_2, 93 | self.n, 94 | ); 95 | self.n += 1; 96 | 97 | Point::

::new(x_3, y_3) 98 | } 99 | } 100 | 101 | #[cfg(test)] 102 | mod test { 103 | use super::*; 104 | use crate::{batch_test, constraint_system::helper::*}; 105 | use ark_bls12_377::Bls12_377; 106 | use ark_bls12_381::Bls12_381; 107 | 108 | use crate::commitment::HomomorphicCommitment; 109 | 110 | /// Adds two curve points together using the classical point addition 111 | /// algorithm. This method is slower than WNAF and is just meant to be the 112 | /// source of truth to test the WNAF method. 113 | pub fn classical_point_addition( 114 | composer: &mut StandardComposer, 115 | point_a: Point

, 116 | point_b: Point

, 117 | ) -> Point

118 | where 119 | F: PrimeField, 120 | P: TEModelParameters, 121 | { 122 | let zero = composer.zero_var; 123 | let x1 = point_a.x; 124 | let y1 = point_a.y; 125 | 126 | let x2 = point_b.x; 127 | let y2 = point_b.y; 128 | 129 | // x1 * y2 130 | let x1_y2 = composer 131 | .arithmetic_gate(|gate| gate.mul(F::one()).witness(x1, y2, None)); 132 | // y1 * x2 133 | let y1_x2 = composer 134 | .arithmetic_gate(|gate| gate.mul(F::one()).witness(y1, x2, None)); 135 | // y1 * y2 136 | let y1_y2 = composer 137 | .arithmetic_gate(|gate| gate.mul(F::one()).witness(y1, y2, None)); 138 | // x1 * x2 139 | let x1_x2 = composer 140 | .arithmetic_gate(|gate| gate.mul(F::one()).witness(x1, x2, None)); 141 | // d x1x2 * y1y2 142 | let d_x1_x2_y1_y2 = composer.arithmetic_gate(|gate| { 143 | gate.mul(P::COEFF_D).witness(x1_x2, y1_y2, None) 144 | }); 145 | 146 | // x1y2 + y1x2 147 | let x_numerator = composer.arithmetic_gate(|gate| { 148 | gate.witness(x1_y2, y1_x2, None).add(F::one(), F::one()) 149 | }); 150 | 151 | // y1y2 - a * x1x2 152 | let y_numerator = composer.arithmetic_gate(|gate| { 153 | gate.witness(y1_y2, x1_x2, None).add(F::one(), -P::COEFF_A) 154 | }); 155 | 156 | // 1 + dx1x2y1y2 157 | let x_denominator = composer.arithmetic_gate(|gate| { 158 | gate.witness(d_x1_x2_y1_y2, zero, None) 159 | .add(F::one(), F::zero()) 160 | .constant(F::one()) 161 | }); 162 | 163 | // Compute the inverse 164 | let inv_x_denom = composer 165 | .variables 166 | .get(&x_denominator) 167 | .unwrap() 168 | .inverse() 169 | .unwrap(); 170 | let inv_x_denom = composer.add_input(inv_x_denom); 171 | 172 | // Assert that we actually have the inverse 173 | // inv_x * x = 1 174 | composer.arithmetic_gate(|gate| { 175 | gate.witness(x_denominator, inv_x_denom, Some(zero)) 176 | .mul(F::one()) 177 | .constant(-F::one()) 178 | }); 179 | 180 | // 1 - dx1x2y1y2 181 | let y_denominator = composer.arithmetic_gate(|gate| { 182 | gate.witness(d_x1_x2_y1_y2, zero, None) 183 | .add(-F::one(), F::zero()) 184 | .constant(F::one()) 185 | }); 186 | 187 | let inv_y_denom = composer 188 | .variables 189 | .get(&y_denominator) 190 | .unwrap() 191 | .inverse() 192 | .unwrap(); 193 | 194 | let inv_y_denom = composer.add_input(inv_y_denom); 195 | // Assert that we actually have the inverse 196 | // inv_y * y = 1 197 | composer.arithmetic_gate(|gate| { 198 | gate.mul(F::one()) 199 | .witness(y_denominator, inv_y_denom, Some(zero)) 200 | .constant(-F::one()) 201 | }); 202 | 203 | // We can now use the inverses 204 | 205 | let x_3 = composer.arithmetic_gate(|gate| { 206 | gate.mul(F::one()).witness(inv_x_denom, x_numerator, None) 207 | }); 208 | 209 | let y_3 = composer.arithmetic_gate(|gate| { 210 | gate.mul(F::one()).witness(inv_y_denom, y_numerator, None) 211 | }); 212 | 213 | Point::new(x_3, y_3) 214 | } 215 | 216 | fn test_curve_addition() 217 | where 218 | F: PrimeField, 219 | P: TEModelParameters, 220 | PC: HomomorphicCommitment, 221 | { 222 | let res = gadget_tester::( 223 | |composer: &mut StandardComposer| { 224 | let (x, y) = P::AFFINE_GENERATOR_COEFFS; 225 | let generator = TEGroupAffine::

::new(x, y); 226 | let x_var = composer.add_input(x); 227 | let y_var = composer.add_input(y); 228 | let expected_point = generator + generator; 229 | let point_a = Point::new(x_var, y_var); 230 | let point_b = Point::new(x_var, y_var); 231 | 232 | let point = composer.point_addition_gate(point_a, point_b); 233 | let point2 = 234 | classical_point_addition(composer, point_a, point_b); 235 | 236 | composer.assert_equal_point(point, point2); 237 | 238 | composer.assert_equal_public_point(point, expected_point); 239 | }, 240 | 2000, 241 | ); 242 | assert!(res.is_ok()); 243 | } 244 | 245 | batch_test!( 246 | [test_curve_addition], 247 | [] 248 | => ( 249 | Bls12_381, 250 | ark_ed_on_bls12_381::EdwardsParameters 251 | ) 252 | ); 253 | 254 | batch_test!( 255 | [test_curve_addition], 256 | [] => ( 257 | Bls12_377, 258 | ark_ed_on_bls12_377::EdwardsParameters 259 | ) 260 | ); 261 | } 262 | -------------------------------------------------------------------------------- /plonk-core/src/constraint_system/ecc/mod.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) DUSK NETWORK. All rights reserved. 6 | 7 | //! Elliptic Curve Gates 8 | 9 | pub mod curve_addition; 10 | pub mod scalar_mul; 11 | 12 | use crate::constraint_system::{variable::Variable, StandardComposer}; 13 | use ark_ec::{ 14 | twisted_edwards_extended::GroupAffine as TEGroupAffine, ModelParameters, 15 | TEModelParameters, 16 | }; 17 | use ark_ff::PrimeField; 18 | use core::marker::PhantomData; 19 | 20 | /// Represents a point of the embeded curve in the circuit 21 | #[derive(derivative::Derivative)] 22 | #[derivative(Clone, Copy, Debug)] 23 | pub struct Point

24 | where 25 | P: ModelParameters, 26 | { 27 | /// `X`-coordinate 28 | x: Variable, 29 | 30 | /// `Y`-coordinate 31 | y: Variable, 32 | 33 | /// Type Parameter Marker 34 | __: PhantomData

, 35 | } 36 | 37 | impl Point

38 | where 39 | F: PrimeField, 40 | P: TEModelParameters, 41 | { 42 | /// Builds a new [`Point`] from `X` and `Y` coordinates. 43 | /// 44 | /// # Safety 45 | /// 46 | /// This method should only be called when we have a guarantee in some 47 | /// [`StandardComposer`] that these two are valid coordinates for an 48 | /// elliptic curve point in affine form. 49 | pub fn new(x: Variable, y: Variable) -> Self { 50 | Self { 51 | x, 52 | y, 53 | __: PhantomData, 54 | } 55 | } 56 | 57 | /// Returns an identity point. 58 | pub fn identity(composer: &mut StandardComposer) -> Self { 59 | let one = 60 | composer.add_witness_to_circuit_description(P::BaseField::one()); 61 | Self::new(composer.zero_var, one) 62 | } 63 | 64 | /// Returns the `X`-coordinate of `self`. 65 | pub fn x(&self) -> &Variable { 66 | &self.x 67 | } 68 | 69 | /// Returns the `Y`-coordinate of `self`. 70 | pub fn y(&self) -> &Variable { 71 | &self.y 72 | } 73 | } 74 | 75 | impl StandardComposer 76 | where 77 | F: PrimeField, 78 | P: TEModelParameters, 79 | { 80 | /// Converts an embeded curve point into a constraint system Point 81 | /// without constraining the values 82 | pub fn add_affine(&mut self, affine: TEGroupAffine

) -> Point

{ 83 | Point::new(self.add_input(affine.x), self.add_input(affine.y)) 84 | } 85 | 86 | /// Converts an embeded curve point into a constraint system Point 87 | /// without constraining the values 88 | pub fn add_public_affine(&mut self, affine: TEGroupAffine

) -> Point

{ 89 | let point = self.add_affine(affine); 90 | self.constrain_to_constant(point.x, F::zero(), Some(-affine.x)); 91 | self.constrain_to_constant(point.y, F::zero(), Some(-affine.y)); 92 | point 93 | } 94 | 95 | /// Add the provided affine point as a circuit description and return its 96 | /// constrained witness value 97 | pub fn add_affine_to_circuit_description( 98 | &mut self, 99 | affine: TEGroupAffine

, 100 | ) -> Point

{ 101 | // NOTE: Not using individual gates because one of these may be zero. 102 | Point::new( 103 | self.add_witness_to_circuit_description(affine.x), 104 | self.add_witness_to_circuit_description(affine.y), 105 | ) 106 | } 107 | 108 | /// Asserts that a [`Point`] in the circuit is equal to a known public 109 | /// point. 110 | pub fn assert_equal_public_point( 111 | &mut self, 112 | point: Point

, 113 | public_point: TEGroupAffine

, 114 | ) { 115 | self.constrain_to_constant(point.x, F::zero(), Some(-public_point.x)); 116 | self.constrain_to_constant(point.y, F::zero(), Some(-public_point.y)); 117 | } 118 | } 119 | 120 | impl StandardComposer 121 | where 122 | F: PrimeField, 123 | P: TEModelParameters, 124 | { 125 | /// Asserts that a point in the circuit is equal to another point in the 126 | /// circuit. 127 | pub fn assert_equal_point(&mut self, lhs: Point

, rhs: Point

) { 128 | self.assert_equal(lhs.x, rhs.x); 129 | self.assert_equal(lhs.y, rhs.y); 130 | } 131 | 132 | /// Adds to the circuit description the conditional selection of the 133 | /// a point between two of them: 134 | /// 135 | /// ```text 136 | /// bit == 1 => point_1, 137 | /// bit == 0 => point_0, 138 | /// ``` 139 | /// 140 | /// # Note 141 | /// 142 | /// The `bit` used as input which is a [`Variable`] should have previously 143 | /// been constrained to be either `1` or `0` using a boolean constraint. 144 | /// See: [`StandardComposer::boolean_gate`]. 145 | pub fn conditional_point_select( 146 | &mut self, 147 | point_1: Point

, 148 | point_0: Point

, 149 | bit: Variable, 150 | ) -> Point

{ 151 | Point::new( 152 | self.conditional_select(bit, point_1.x, point_0.x), 153 | self.conditional_select(bit, point_1.y, point_0.y), 154 | ) 155 | } 156 | 157 | /// Adds to the circuit description the conditional negation of a point: 158 | /// bit == 1 => -value, 159 | /// bit == 0 => value, 160 | /// 161 | /// # Note 162 | /// The `bit` used as input which is a [`Variable`] should had previously 163 | /// been constrained to be either 1 or 0 using a bool constrain. See: 164 | /// [`StandardComposer::boolean_gate`]. 165 | pub fn conditional_point_neg( 166 | &mut self, 167 | bit: Variable, 168 | point_b: Point

, 169 | ) -> Point

{ 170 | let zero = self.zero_var; 171 | let x = point_b.x; 172 | let y = point_b.y; 173 | 174 | // negation of point (x, y) is (-x, y) 175 | let x_neg = self.arithmetic_gate(|gate| { 176 | gate.witness(x, zero, None).add(-F::one(), F::zero()) 177 | }); 178 | 179 | let x_updated = self.conditional_select(bit, x_neg, x); 180 | 181 | Point::new(x_updated, y) 182 | } 183 | 184 | /// Adds to the circuit description the conditional selection of the 185 | /// identity point: 186 | /// 187 | /// ```text 188 | /// bit == 1 => point, 189 | /// bit == 0 => 1, 190 | /// ``` 191 | /// 192 | /// # Note 193 | /// 194 | /// The `bit` used as input which is a [`Variable`] should have previously 195 | /// been constrained to be either `1` or `0` using a boolean constraint. 196 | /// See: [`StandardComposer::boolean_gate`]. 197 | fn conditional_select_identity( 198 | &mut self, 199 | bit: Variable, 200 | point: Point

, 201 | ) -> Point

{ 202 | Point::new( 203 | self.conditional_select_zero(bit, point.x), 204 | self.conditional_select_one(bit, point.y), 205 | ) 206 | } 207 | } 208 | 209 | #[cfg(test)] 210 | mod test { 211 | use super::*; 212 | use crate::{ 213 | batch_test, commitment::HomomorphicCommitment, 214 | constraint_system::helper::*, 215 | }; 216 | use ark_bls12_377::Bls12_377; 217 | use ark_bls12_381::Bls12_381; 218 | 219 | fn test_conditional_select_point() 220 | where 221 | F: PrimeField, 222 | P: TEModelParameters, 223 | PC: HomomorphicCommitment, 224 | { 225 | let res = gadget_tester::( 226 | |composer: &mut StandardComposer| { 227 | let bit_1 = composer.add_input(F::one()); 228 | let bit_0 = composer.zero_var(); 229 | 230 | let point_a = Point::

::identity(composer); 231 | let point_b = Point::new( 232 | composer.add_input(F::from(10u64)), 233 | composer.add_input(F::from(20u64)), 234 | ); 235 | 236 | let choice = 237 | composer.conditional_point_select(point_a, point_b, bit_1); 238 | 239 | composer.assert_equal_point(point_a, choice); 240 | 241 | let choice = 242 | composer.conditional_point_select(point_a, point_b, bit_0); 243 | composer.assert_equal_point(point_b, choice); 244 | }, 245 | 32, 246 | ); 247 | assert!(res.is_ok()); 248 | } 249 | 250 | fn test_conditional_point_neg() 251 | where 252 | F: PrimeField, 253 | P: TEModelParameters, 254 | PC: HomomorphicCommitment, 255 | { 256 | gadget_tester::( 257 | |composer: &mut StandardComposer| { 258 | let bit_1 = composer.add_input(F::one()); 259 | let bit_0 = composer.zero_var(); 260 | 261 | let point = 262 | TEGroupAffine::

::new(F::from(10u64), F::from(20u64)); 263 | let point_var = Point::new( 264 | composer.add_input(point.x), 265 | composer.add_input(point.y), 266 | ); 267 | 268 | let neg_point = 269 | composer.conditional_point_neg(bit_1, point_var); 270 | composer.assert_equal_public_point(neg_point, -point); 271 | 272 | let non_neg_point = 273 | composer.conditional_point_neg(bit_0, point_var); 274 | composer.assert_equal_public_point(non_neg_point, point); 275 | }, 276 | 32, 277 | ) 278 | .expect("test failed"); 279 | } 280 | 281 | // Bls12-381 tests 282 | batch_test!( 283 | [ 284 | test_conditional_select_point, 285 | test_conditional_point_neg 286 | ], 287 | [] => ( 288 | Bls12_381, 289 | ark_ed_on_bls12_381::EdwardsParameters 290 | ) 291 | ); 292 | 293 | // Bls12-377 tests 294 | batch_test!( 295 | [ 296 | test_conditional_select_point, 297 | test_conditional_point_neg 298 | ], 299 | [] => ( 300 | Bls12_377, 301 | ark_ed_on_bls12_377::EdwardsParameters 302 | ) 303 | ); 304 | } 305 | -------------------------------------------------------------------------------- /plonk-core/src/constraint_system/ecc/scalar_mul/mod.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) DUSK NETWORK. All rights reserved. 6 | 7 | //! Scalar Multiplication Gates 8 | 9 | mod fixed_base; 10 | mod variable_base; 11 | -------------------------------------------------------------------------------- /plonk-core/src/constraint_system/ecc/scalar_mul/variable_base.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) DUSK NETWORK. All rights reserved. 6 | 7 | //! Variable-base Scalar Multiplication Gate 8 | 9 | use crate::constraint_system::{ 10 | ecc::Point, variable::Variable, StandardComposer, 11 | }; 12 | use ark_ec::TEModelParameters; 13 | use ark_ff::{BigInteger, FpParameters, PrimeField}; 14 | 15 | impl StandardComposer 16 | where 17 | F: PrimeField, 18 | P: TEModelParameters, 19 | { 20 | /// Adds a variable-base scalar multiplication to the circuit description. 21 | /// 22 | /// # Note 23 | /// 24 | /// If you're planning to multiply always by the generator of the scalar 25 | /// field, you should use [`StandardComposer::fixed_base_scalar_mul`] 26 | /// which is optimized for fixed_base ops. 27 | pub fn variable_base_scalar_mul( 28 | &mut self, 29 | curve_var: Variable, 30 | point: Point

, 31 | ) -> Point

{ 32 | // Turn scalar into bits 33 | let raw_scalar = *self 34 | .variables 35 | .get(&curve_var) 36 | // We can unwrap safely here since it should be impossible to obtain 37 | // a `Variable` without first linking it inside of the 38 | // HashMap from which we are calling the `get()` now. Therefore, if 39 | // the `get()` fn fails now, somethig is going really 40 | // bad. 41 | .expect("Variable in existance without referenced scalar"); 42 | let scalar_bits_var = self.scalar_decomposition(curve_var, raw_scalar); 43 | 44 | let identity = Point::identity(self); 45 | let mut result = identity; 46 | 47 | for bit in scalar_bits_var.into_iter().rev() { 48 | result = self.point_addition_gate(result, result); 49 | let point_to_add = self.conditional_select_identity(bit, point); 50 | result = self.point_addition_gate(result, point_to_add); 51 | } 52 | 53 | result 54 | } 55 | 56 | fn scalar_decomposition( 57 | &mut self, 58 | witness_var: Variable, 59 | witness_scalar: F, 60 | ) -> Vec { 61 | // Decompose the bits 62 | let scalar_bits_iter = witness_scalar.into_repr().to_bits_le(); 63 | 64 | // Add all the bits into the composer 65 | let scalar_bits_var: Vec = scalar_bits_iter 66 | .iter() 67 | .map(|bit| self.add_input(F::from(*bit as u64))) 68 | .collect(); 69 | 70 | // Take the first 252 bits 71 | let scalar_bits_var = scalar_bits_var 72 | [..::Params::MODULUS_BITS as usize] 73 | .to_vec(); 74 | 75 | // Now ensure that the bits correctly accumulate to the witness given 76 | let mut accumulator_var = self.zero_var; 77 | let mut accumulator_scalar = F::zero(); 78 | 79 | for (power, bit) in scalar_bits_var.iter().enumerate() { 80 | self.boolean_gate(*bit); 81 | 82 | let two_pow = F::from(2u64).pow([power as u64, 0, 0, 0]); 83 | 84 | accumulator_var = self.arithmetic_gate(|gate| { 85 | gate.witness(*bit, accumulator_var, None) 86 | .add(two_pow, F::one()) 87 | }); 88 | 89 | accumulator_scalar += 90 | two_pow * F::from(scalar_bits_iter[power] as u64); 91 | } 92 | self.assert_equal(accumulator_var, witness_var); 93 | 94 | scalar_bits_var 95 | } 96 | } 97 | 98 | #[cfg(test)] 99 | mod test { 100 | use super::*; 101 | use crate::{ 102 | batch_test, commitment::HomomorphicCommitment, 103 | constraint_system::helper::*, util, 104 | }; 105 | use ark_bls12_377::Bls12_377; 106 | use ark_bls12_381::Bls12_381; 107 | use ark_ec::{ 108 | twisted_edwards_extended::GroupAffine as TEGroupAffine, AffineCurve, 109 | TEModelParameters, 110 | }; 111 | 112 | fn test_var_base_scalar_mul() 113 | where 114 | F: PrimeField, 115 | P: TEModelParameters, 116 | PC: HomomorphicCommitment, 117 | { 118 | let res = gadget_tester::( 119 | |composer: &mut StandardComposer| { 120 | let scalar = F::from_le_bytes_mod_order(&[ 121 | 182, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 122 | 147, 32, 104, 166, 0, 59, 52, 1, 1, 59, 103, 6, 169, 175, 123 | 51, 101, 234, 180, 125, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 124 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 125 | 0, 126 | ]); 127 | let secret_scalar = composer.add_input(scalar); 128 | 129 | let (x, y) = P::AFFINE_GENERATOR_COEFFS; 130 | let generator = TEGroupAffine::new(x, y); 131 | 132 | let expected_point: TEGroupAffine

= AffineCurve::mul( 133 | &generator, 134 | util::to_embedded_curve_scalar::(scalar), 135 | ) 136 | .into(); 137 | 138 | let point = composer.add_affine(generator); 139 | 140 | let point_scalar = 141 | composer.variable_base_scalar_mul(secret_scalar, point); 142 | 143 | composer 144 | .assert_equal_public_point(point_scalar, expected_point); 145 | }, 146 | 4096, 147 | ); 148 | assert!(res.is_ok()); 149 | } 150 | 151 | // Tests for Bls12_381 152 | batch_test!( 153 | [test_var_base_scalar_mul], 154 | [] => ( 155 | Bls12_381, 156 | ark_ed_on_bls12_381::EdwardsParameters 157 | ) 158 | ); 159 | 160 | // Tests for Bls12_377 161 | batch_test!( 162 | [test_var_base_scalar_mul], 163 | [] => ( 164 | Bls12_377, 165 | ark_ed_on_bls12_377::EdwardsParameters 166 | ) 167 | ); 168 | } 169 | -------------------------------------------------------------------------------- /plonk-core/src/constraint_system/hash.rs: -------------------------------------------------------------------------------- 1 | use ark_ec::TEModelParameters; 2 | use ark_ff::PrimeField; 3 | 4 | use super::{StandardComposer, Variable}; 5 | 6 | pub(crate) const SBOX_ALPHA: u64 = 5; 7 | 8 | impl StandardComposer 9 | where 10 | F: PrimeField, 11 | P: TEModelParameters, 12 | { 13 | /// Input 3 inputs \[w1, w2, w3\], 14 | /// four selectors \[q1, q2, q3, qc, qo\], 15 | /// constraint the following statement: 16 | /// ``` ignore 17 | /// q1 w^5 + q2 w2^5 + q3 w3^5 + q4 + q5 w4 = 0 18 | /// ``` 19 | /// Return w4 20 | pub fn full_affine_transform_gate( 21 | &mut self, 22 | vars: &[Variable; 3], 23 | selectors: &[F; 5], 24 | ) -> Variable { 25 | let w4_val = (selectors[0] 26 | * self.value_of_var(vars[0]).pow([SBOX_ALPHA]) 27 | + selectors[1] * self.value_of_var(vars[1]).pow([SBOX_ALPHA]) 28 | + selectors[2] * self.value_of_var(vars[2]).pow([SBOX_ALPHA]) 29 | + selectors[3]) 30 | / -selectors[4]; 31 | let w4_var = self.add_input(w4_val); 32 | 33 | // add wires 34 | self.w_l.push(vars[0]); 35 | self.w_r.push(vars[1]); 36 | self.w_o.push(w4_var); 37 | self.w_4.push(vars[2]); 38 | 39 | // add high degree selectors 40 | self.q_hl.push(selectors[0]); 41 | self.q_hr.push(selectors[1]); 42 | self.q_h4.push(selectors[2]); 43 | self.q_c.push(selectors[3]); 44 | self.q_o.push(selectors[4]); 45 | 46 | // add dummy selector vectors 47 | self.q_l.push(F::zero()); 48 | self.q_r.push(F::zero()); 49 | self.q_m.push(F::zero()); 50 | self.q_4.push(F::zero()); 51 | self.q_arith.push(F::one()); 52 | 53 | self.q_range.push(F::zero()); 54 | self.q_logic.push(F::zero()); 55 | self.q_fixed_group_add.push(F::zero()); 56 | self.q_variable_group_add.push(F::zero()); 57 | self.q_lookup.push(F::zero()); 58 | 59 | self.perm 60 | .add_variables_to_map(vars[0], vars[1], w4_var, vars[2], self.n); 61 | self.n += 1; 62 | 63 | w4_var 64 | } 65 | 66 | /// Input 3 inputs \[w1, w2, w3\], 67 | /// four selectors \[q1, q2, q3, qc, qo\], 68 | /// constraint the following statement: 69 | /// ``` ignore 70 | /// q1 w^5 + q2 w2^5 + q3 w3^5 + q4 + q5 w4 = 0 71 | /// ``` 72 | /// Return w4 73 | pub fn partial_affine_transform_gate( 74 | &mut self, 75 | vars: &[Variable; 3], 76 | selectors: &[F; 5], 77 | ) -> Variable { 78 | let w4_val = (selectors[0] 79 | * self.value_of_var(vars[0]).pow([SBOX_ALPHA]) 80 | + selectors[1] * self.value_of_var(vars[1]) 81 | + selectors[2] * self.value_of_var(vars[2]) 82 | + selectors[3]) 83 | / -selectors[4]; 84 | let w4_var = self.add_input(w4_val); 85 | 86 | // add wires 87 | self.w_l.push(vars[0]); 88 | self.w_r.push(vars[1]); 89 | self.w_o.push(w4_var); 90 | self.w_4.push(vars[2]); 91 | 92 | // add high degree selectors 93 | self.q_hl.push(selectors[0]); 94 | self.q_hr.push(F::zero()); 95 | self.q_h4.push(F::zero()); 96 | self.q_c.push(selectors[3]); 97 | self.q_o.push(selectors[4]); 98 | 99 | // add dummy selector vectors 100 | self.q_l.push(F::zero()); 101 | self.q_r.push(selectors[1]); 102 | self.q_m.push(F::zero()); 103 | self.q_4.push(selectors[2]); 104 | self.q_arith.push(F::one()); 105 | 106 | self.q_range.push(F::zero()); 107 | self.q_logic.push(F::zero()); 108 | self.q_fixed_group_add.push(F::zero()); 109 | self.q_variable_group_add.push(F::zero()); 110 | self.q_lookup.push(F::zero()); 111 | 112 | self.perm 113 | .add_variables_to_map(vars[0], vars[1], w4_var, vars[2], self.n); 114 | self.n += 1; 115 | 116 | w4_var 117 | } 118 | } 119 | 120 | #[cfg(test)] 121 | mod test { 122 | use super::*; 123 | use crate::{ 124 | batch_test, commitment::HomomorphicCommitment, 125 | constraint_system::helper::gadget_tester, 126 | }; 127 | use ark_bls12_377::Bls12_377; 128 | use ark_bls12_381::Bls12_381; 129 | 130 | fn test_degree_5_gates() 131 | where 132 | F: PrimeField, 133 | P: TEModelParameters, 134 | PC: HomomorphicCommitment, 135 | { 136 | let res = gadget_tester::( 137 | |composer: &mut StandardComposer| { 138 | let a = composer.add_input(F::from(2u64)); 139 | let b = composer.add_input(F::from(3u64)); 140 | let c = composer.add_input(F::from(5u64)); 141 | 142 | let q1 = F::from(7u64); 143 | let q2 = F::from(11u64); 144 | let q3 = F::from(13u64); 145 | let q4 = F::from(17u64); 146 | let q5 = -F::from(1u64); 147 | 148 | // 7*2^5+ 11*3^5 + 13*5^5 + 17 149 | let d = composer.add_input(F::from(43539u64)); 150 | // 7*2^5+ 11*3 + 13*5 + 17 151 | let e = composer.add_input(F::from(339u64)); 152 | 153 | let d_rec = composer.full_affine_transform_gate( 154 | &[a, b, c], 155 | &[q1, q2, q3, q4, q5], 156 | ); 157 | composer.assert_equal(d, d_rec); 158 | 159 | let e_rec = composer.partial_affine_transform_gate( 160 | &[a, b, c], 161 | &[q1, q2, q3, q4, q5], 162 | ); 163 | composer.assert_equal(e, e_rec); 164 | 165 | composer.check_circuit_satisfied(); 166 | }, 167 | 32, 168 | ); 169 | assert!(res.is_ok(), "{:?}", res.err().unwrap()); 170 | } 171 | 172 | // Tests for Bls12_381 173 | batch_test!( 174 | [ 175 | test_degree_5_gates 176 | ], 177 | [] => ( 178 | Bls12_381, 179 | ark_ed_on_bls12_381::EdwardsParameters 180 | ) 181 | ); 182 | 183 | // Tests for Bls12_377 184 | batch_test!( 185 | [ 186 | test_degree_5_gates 187 | ], 188 | [] => ( 189 | Bls12_377, 190 | ark_ed_on_bls12_377::EdwardsParameters 191 | ) 192 | ); 193 | } 194 | -------------------------------------------------------------------------------- /plonk-core/src/constraint_system/helper.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) DUSK NETWORK. All rights reserved. 6 | 7 | use super::StandardComposer; 8 | use crate::{ 9 | commitment::HomomorphicCommitment, 10 | error::{to_pc_error, Error}, 11 | proof_system::{Prover, Verifier}, 12 | }; 13 | use ark_ec::TEModelParameters; 14 | use ark_ff::PrimeField; 15 | use rand_core::OsRng; 16 | 17 | /// Adds dummy constraints using arithmetic gates. 18 | #[allow(dead_code)] 19 | pub(crate) fn dummy_gadget( 20 | n: usize, 21 | composer: &mut StandardComposer, 22 | ) where 23 | F: PrimeField, 24 | P: TEModelParameters, 25 | { 26 | let one = F::one(); 27 | let var_one = composer.add_input(one); 28 | for _ in 0..n { 29 | composer.arithmetic_gate(|gate| { 30 | gate.witness(var_one, var_one, None).add(F::one(), F::one()) 31 | }); 32 | } 33 | } 34 | 35 | /// Takes a generic gadget function with no auxillary input and tests whether it 36 | /// passes an end-to-end test. 37 | #[allow(dead_code)] 38 | pub(crate) fn gadget_tester( 39 | gadget: fn(&mut StandardComposer), 40 | n: usize, 41 | ) -> Result, Error> 42 | where 43 | F: PrimeField, 44 | P: TEModelParameters, 45 | PC: HomomorphicCommitment, 46 | { 47 | // Common View 48 | let universal_params = 49 | PC::setup(2 * n, None, &mut OsRng).map_err(to_pc_error::)?; 50 | 51 | // Provers View 52 | let (proof, public_inputs) = { 53 | // Create a prover struct 54 | let mut prover = Prover::::new(b"demo"); 55 | 56 | // Additionally key the transcript 57 | prover.key_transcript(b"key", b"additional seed information"); 58 | 59 | // Add gadgets 60 | gadget(prover.mut_cs()); 61 | 62 | // Commit Key 63 | let (ck, _) = 64 | PC::trim(&universal_params, prover.circuit_bound(), 0, None) 65 | .map_err(to_pc_error::)?; 66 | 67 | // Preprocess circuit 68 | prover.preprocess(&ck)?; 69 | 70 | // Once the prove method is called, the public inputs are cleared 71 | // So pre-fetch these before calling Prove 72 | let public_inputs = prover.cs.get_pi().clone(); 73 | 74 | // Compute Proof 75 | (prover.prove(&ck)?, public_inputs) 76 | }; 77 | // Verifiers view 78 | // 79 | // Create a Verifier object 80 | let mut verifier = Verifier::new(b"demo"); 81 | 82 | // Additionally key the transcript 83 | verifier.key_transcript(b"key", b"additional seed information"); 84 | 85 | // Add gadgets 86 | gadget(verifier.mut_cs()); 87 | 88 | // Compute Commit and Verifier Key 89 | let (ck, vk) = 90 | PC::trim(&universal_params, verifier.circuit_bound(), 0, None) 91 | .map_err(to_pc_error::)?; 92 | 93 | // Preprocess circuit 94 | verifier.preprocess(&ck)?; 95 | 96 | // Verify proof 97 | verifier.verify(&proof, &vk, &public_inputs)?; 98 | Ok(proof) 99 | } 100 | -------------------------------------------------------------------------------- /plonk-core/src/constraint_system/lookup.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) ZK-Garage. All rights reserved. 6 | 7 | use crate::constraint_system::{StandardComposer, Variable}; 8 | use ark_ec::TEModelParameters; 9 | use ark_ff::PrimeField; 10 | 11 | impl StandardComposer 12 | where 13 | F: PrimeField, 14 | P: TEModelParameters, 15 | { 16 | /// Adds a plookup gate to the circuit with its corresponding 17 | /// constraints. 18 | pub fn lookup_gate( 19 | &mut self, 20 | a: Variable, 21 | b: Variable, 22 | c: Variable, 23 | d: Option, 24 | pi: Option, 25 | ) -> Variable { 26 | // Check if advice wire has a value 27 | let d = match d { 28 | Some(var) => var, 29 | None => self.zero_var, 30 | }; 31 | 32 | self.w_l.push(a); 33 | self.w_r.push(b); 34 | self.w_o.push(c); 35 | self.w_4.push(d); 36 | 37 | // Add selector vectors 38 | self.q_m.push(F::zero()); 39 | self.q_l.push(F::zero()); 40 | self.q_r.push(F::zero()); 41 | self.q_o.push(F::zero()); 42 | self.q_c.push(F::zero()); 43 | self.q_4.push(F::zero()); 44 | self.q_arith.push(F::zero()); 45 | self.q_range.push(F::zero()); 46 | self.q_logic.push(F::zero()); 47 | self.q_fixed_group_add.push(F::zero()); 48 | self.q_variable_group_add.push(F::zero()); 49 | 50 | // add high degree selectors 51 | self.q_hl.push(F::zero()); 52 | self.q_hr.push(F::zero()); 53 | self.q_h4.push(F::zero()); 54 | 55 | // For a lookup gate, only one selector poly is 56 | // turned on as the output is inputted directly 57 | self.q_lookup.push(F::one()); 58 | 59 | if let Some(pi) = pi { 60 | self.add_pi(self.n, &pi).unwrap_or_else(|_| { 61 | panic!("Could not insert PI {:?} at {}", pi, self.n) 62 | }); 63 | }; 64 | 65 | self.perm.add_variables_to_map(a, b, c, d, self.n); 66 | 67 | self.n += 1; 68 | 69 | c 70 | } 71 | } 72 | 73 | #[cfg(test)] 74 | mod test { 75 | use super::*; 76 | use crate::{ 77 | batch_test, commitment::HomomorphicCommitment, 78 | constraint_system::helper::*, lookup::LookupTable, 79 | }; 80 | use ark_bls12_377::Bls12_377; 81 | use ark_bls12_381::Bls12_381; 82 | use rand_core::{OsRng, RngCore}; 83 | 84 | fn test_plookup_xor() 85 | where 86 | F: PrimeField, 87 | P: TEModelParameters, 88 | PC: HomomorphicCommitment, 89 | { 90 | let res = gadget_tester::( 91 | |composer: &mut StandardComposer| { 92 | let rng = &mut OsRng; 93 | 94 | composer.lookup_table = LookupTable::::xor_table(0, 4); 95 | 96 | let negative_one = composer.add_input(-F::one()); 97 | 98 | let rand1 = rng.next_u32() % 16; 99 | let rand2 = rng.next_u32() % 16; 100 | let rand3 = rng.next_u32() % 16; 101 | 102 | let rand1_var = composer.add_input(F::from(rand1)); 103 | let rand2_var = composer.add_input(F::from(rand2)); 104 | let rand3_var = composer.add_input(F::from(rand3)); 105 | 106 | let xor12 = rand1 ^ rand2; 107 | let xor13 = rand1 ^ rand3; 108 | let xor23 = rand2 ^ rand3; 109 | 110 | let xor12_var = composer.add_input(F::from(xor12)); 111 | let xor13_var = composer.add_input(F::from(xor13)); 112 | let xor23_var = composer.add_input(F::from(xor23)); 113 | 114 | composer.lookup_gate( 115 | rand1_var, 116 | rand2_var, 117 | xor12_var, 118 | Some(negative_one), 119 | None, 120 | ); 121 | 122 | composer.lookup_gate( 123 | rand1_var, 124 | rand3_var, 125 | xor13_var, 126 | Some(negative_one), 127 | None, 128 | ); 129 | 130 | composer.lookup_gate( 131 | rand2_var, 132 | rand3_var, 133 | xor23_var, 134 | Some(negative_one), 135 | None, 136 | ); 137 | 138 | composer.arithmetic_gate(|gate| { 139 | gate.add(F::one(), F::one()) 140 | .witness(rand1_var, rand2_var, None) 141 | }); 142 | composer.arithmetic_gate(|gate| { 143 | gate.mul(F::one()).witness(rand2_var, rand3_var, None) 144 | }); 145 | }, 146 | 256, 147 | ); 148 | assert!(res.is_ok(), "{:?}", res.err().unwrap()); 149 | } 150 | 151 | // Bls12-381 tests 152 | batch_test!( 153 | [ 154 | test_plookup_xor 155 | ], 156 | [] => ( 157 | Bls12_381, ark_ed_on_bls12_381::EdwardsParameters 158 | ) 159 | ); 160 | 161 | // Bls12-377 tests 162 | batch_test!( 163 | [ 164 | test_plookup_xor 165 | ], 166 | [] => ( 167 | Bls12_377, ark_ed_on_bls12_377::EdwardsParameters 168 | ) 169 | ); 170 | } 171 | -------------------------------------------------------------------------------- /plonk-core/src/constraint_system/mod.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) DUSK NETWORK. All rights reserved. 6 | 7 | //! The constraint system module stores the implementation of the PLONK 8 | //! [`StandardComposer`], as well as the circuit tools and abstractions, used by 9 | //! the Composer to generate, build, preprocess circuits. 10 | 11 | mod arithmetic; 12 | mod boolean; 13 | mod hash; 14 | mod logic; 15 | mod lookup; 16 | mod range; 17 | 18 | pub(crate) mod composer; 19 | pub(crate) mod helper; 20 | pub(crate) mod variable; 21 | 22 | pub mod ecc; 23 | 24 | pub(crate) use hash::SBOX_ALPHA; 25 | pub(crate) use variable::WireData; 26 | 27 | pub use composer::StandardComposer; 28 | pub use variable::Variable; 29 | -------------------------------------------------------------------------------- /plonk-core/src/constraint_system/variable.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) DUSK NETWORK. All rights reserved. 6 | 7 | //! This module holds the components needed in the Constraint System. 8 | //! 9 | //! The two components used are Variables and Wires. 10 | use std::fmt::Display; 11 | 12 | /// The value is a reference to the actual value that was added to the 13 | /// constraint system 14 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 15 | pub struct Variable(pub(crate) usize); 16 | 17 | impl Display for Variable { 18 | // This trait requires `fmt` with this exact signature. 19 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 20 | write!(f, "{}", self.0) 21 | } 22 | } 23 | 24 | /// Stores the data for a specific wire in an arithmetic circuit 25 | /// This data is the gate index and the type of wire 26 | /// Left(1) signifies that this wire belongs to the first gate and is the left 27 | /// wire 28 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 29 | pub enum WireData { 30 | /// Left Wire of n'th gate 31 | Left(usize), 32 | /// Right Wire of n'th gate 33 | Right(usize), 34 | /// Output Wire of n'th gate 35 | Output(usize), 36 | /// Fourth Wire of n'th gate 37 | Fourth(usize), 38 | } 39 | -------------------------------------------------------------------------------- /plonk-core/src/error.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) DUSK NETWORK. All rights reserved. 6 | 7 | //! A collection of all possible errors encountered in PLONK. 8 | 9 | /// Defines all possible errors that can be encountered in PLONK. 10 | #[derive(Debug)] 11 | pub enum Error { 12 | // FFT errors 13 | /// This error occurs when an error triggers on any of the fft module 14 | /// functions. 15 | InvalidEvalDomainSize { 16 | /// Log size of the group 17 | log_size_of_group: u32, 18 | /// Two adicity generated 19 | adicity: u32, 20 | }, 21 | 22 | // Prover/Verifier errors 23 | /// This error occurs when a proof verification fails. 24 | ProofVerificationError, 25 | /// This error occurs when the circuit is not provided with all of the 26 | /// required inputs. 27 | CircuitInputsNotFound, 28 | /// This error occurs when we want to verify a Proof but the pi_constructor 29 | /// attribute is uninitialized. 30 | UninitializedPIGenerator, 31 | /// PublicInput serialization error 32 | InvalidPublicInputBytes, 33 | /// PublicInput value conversion error 34 | InvalidPublicInputValue, 35 | /// This error occurs when the Prover structure already contains a 36 | /// preprocessed circuit inside, but you call preprocess again. 37 | CircuitAlreadyPreprocessed, 38 | 39 | // Preprocessing errors 40 | /// This error occurs when an error triggers during the preprocessing 41 | /// stage. 42 | MismatchedPolyLen, 43 | 44 | /// Polynomial Commitment errors 45 | PCError { 46 | /// Polynomial Commitment errors 47 | error: String, 48 | }, 49 | 50 | // KZG10 errors 51 | // XXX: Are these errors still used? 52 | /// This error occurs when the user tries to create PublicParameters 53 | /// and supplies the max degree as zero. 54 | DegreeIsZero, 55 | /// This error occurs when the user tries to trim PublicParameters 56 | /// to a degree that is larger than the maximum degree. 57 | TruncatedDegreeTooLarge, 58 | /// This error occurs when the user tries to trim PublicParameters 59 | /// down to a degree that is zero. 60 | TruncatedDegreeIsZero, 61 | /// This error occurs when the user tries to commit to a polynomial whose 62 | /// degree is larger than the supported degree for that proving key. 63 | PolynomialDegreeTooLarge, 64 | /// This error occurs when the user tries to commit to a polynomial whose 65 | /// degree is zero. 66 | PolynomialDegreeIsZero, 67 | /// This error occurs when the pairing check fails at being equal to the 68 | /// Identity point. 69 | PairingCheckFailure, 70 | 71 | /// This error occurs when there are not enough bytes to read out of a 72 | /// slice during deserialization. 73 | NotEnoughBytes, 74 | /// This error occurs when a malformed point is decoded from a byte array. 75 | PointMalformed, 76 | /// This error occurs when a malformed scalar is decoded from a byte 77 | /// array. 78 | ScalarMalformed, 79 | 80 | // Plonkup errors 81 | /// Query element not found in lookup table 82 | ElementNotIndexed, 83 | /// Cannot commit to table column polynomial 84 | TablePreProcessingError, 85 | } 86 | 87 | impl From for Error { 88 | fn from(error: ark_poly_commit::error::Error) -> Self { 89 | Self::PCError { 90 | error: format!("Polynomial Commitment Error: {:?}", error), 91 | } 92 | } 93 | } 94 | 95 | /// Convert an ark_poly_commit error 96 | pub fn to_pc_error(error: PC::Error) -> Error 97 | where 98 | F: ark_ff::Field, 99 | PC: ark_poly_commit::PolynomialCommitment< 100 | F, 101 | ark_poly::univariate::DensePolynomial, 102 | >, 103 | { 104 | Error::PCError { 105 | error: format!("Polynomial Commitment Error: {:?}", error), 106 | } 107 | } 108 | 109 | impl std::fmt::Display for Error { 110 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 111 | match self { 112 | Self::InvalidEvalDomainSize { 113 | log_size_of_group, 114 | adicity, 115 | } => write!( 116 | f, 117 | "Log-size of the EvaluationDomain group > TWO_ADICITY\ 118 | Size: {:?} > TWO_ADICITY = {:?}", 119 | log_size_of_group, adicity 120 | ), 121 | Self::ProofVerificationError => { 122 | write!(f, "proof verification failed") 123 | } 124 | Self::CircuitInputsNotFound => { 125 | write!(f, "circuit inputs not found") 126 | } 127 | Self::UninitializedPIGenerator => { 128 | write!(f, "PI generator uninitialized") 129 | } 130 | Self::InvalidPublicInputBytes => { 131 | write!(f, "invalid public input bytes") 132 | } 133 | Self::InvalidPublicInputValue => { 134 | write!(f, "public input value conversion error") 135 | } 136 | Self::MismatchedPolyLen => { 137 | write!(f, "the length of the wires is not the same") 138 | } 139 | Self::PCError { error } => { 140 | write!(f, "{:?}", error) 141 | } 142 | Self::CircuitAlreadyPreprocessed => { 143 | write!(f, "circuit has already been preprocessed") 144 | } 145 | Self::DegreeIsZero => { 146 | write!(f, "cannot create PublicParameters with max degree 0") 147 | } 148 | Self::TruncatedDegreeTooLarge => { 149 | write!(f, "cannot trim more than the maximum degree") 150 | } 151 | Self::TruncatedDegreeIsZero => write!( 152 | f, 153 | "cannot trim PublicParameters to a maximum size of zero" 154 | ), 155 | Self::PolynomialDegreeTooLarge => write!( 156 | f, 157 | "proving key is not large enough to commit to said polynomial" 158 | ), 159 | Self::PolynomialDegreeIsZero => { 160 | write!(f, "cannot commit to polynomial of zero degree") 161 | } 162 | Self::PairingCheckFailure => write!(f, "pairing check failed"), 163 | Self::NotEnoughBytes => write!(f, "not enough bytes left to read"), 164 | Self::PointMalformed => write!(f, "point bytes malformed"), 165 | Self::ScalarMalformed => write!(f, "scalar bytes malformed"), 166 | Self::ElementNotIndexed => { 167 | write!(f, "element not found in lookup table") 168 | } 169 | Self::TablePreProcessingError => { 170 | write!(f, "lookup table not preprocessed correctly") 171 | } 172 | } 173 | } 174 | } 175 | 176 | impl std::error::Error for Error {} 177 | -------------------------------------------------------------------------------- /plonk-core/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Licensed under the Apache License, Version 2.0 or the MIT license 3 | // , at your 4 | // option. This file may not be copied, modified, or distributed 5 | // except according to those terms. 6 | // 7 | // Copyright (c) DUSK NETWORK. All rights reserved. 8 | 9 | //! Permutations over Lagrange-bases for Oecumenical Noninteractive 10 | //! arguments of Knowledge (PLONK) is a zero knowledge proof system. 11 | //! 12 | //! This protocol was created by: 13 | //! - Ariel Gabizon (Protocol Labs), 14 | //! - Zachary J. Williamson (Aztec Protocol) 15 | //! - Oana Ciobotaru 16 | //! 17 | //! This crate contains a pure Rust implementation of this algorithm using 18 | //! code done by the creators of the protocol as a reference implementation: 19 | //! 20 | //! 21 | 22 | // Bitshift/Bitwise ops are allowed to gain performance. 23 | #![allow(clippy::suspicious_arithmetic_impl)] 24 | // Some structs do not have AddAssign or MulAssign impl. 25 | #![allow(clippy::suspicious_op_assign_impl)] 26 | // Variables have always the same names in respect to wires. 27 | #![allow(clippy::many_single_char_names)] 28 | // Bool expr are usually easier to read with match statements. 29 | #![allow(clippy::match_bool)] 30 | // We have quite some functions that require quite some args by it's nature. 31 | // It can be refactored but for now, we avoid these warns. 32 | #![allow(clippy::too_many_arguments)] 33 | #![deny(rustdoc::broken_intra_doc_links)] 34 | #![deny(missing_docs)] 35 | 36 | extern crate alloc; 37 | 38 | mod permutation; 39 | mod transcript; 40 | mod util; 41 | 42 | pub mod circuit; 43 | pub mod commitment; 44 | pub mod constraint_system; 45 | pub mod error; 46 | pub mod lookup; 47 | pub mod prelude; 48 | pub mod proof_system; 49 | 50 | #[cfg(test)] 51 | mod test; 52 | -------------------------------------------------------------------------------- /plonk-core/src/lookup/mod.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) DUSK NETWORK. All rights reserved. 6 | 7 | //! Module containing the lookups. 8 | 9 | pub(crate) mod lookup_table; 10 | pub(crate) mod multiset; 11 | pub(crate) mod preprocess; 12 | pub(crate) mod witness_table; 13 | 14 | pub use lookup_table::LookupTable; 15 | pub use multiset::MultiSet; 16 | pub use preprocess::PreprocessedLookupTable; 17 | pub use witness_table::WitnessTable; 18 | -------------------------------------------------------------------------------- /plonk-core/src/lookup/preprocess.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) DUSK NETWORK. All rights reserved. 6 | 7 | use crate::commitment::HomomorphicCommitment; 8 | use crate::error::{to_pc_error, Error}; 9 | use crate::lookup::{LookupTable, MultiSet}; 10 | use ark_ff::PrimeField; 11 | use ark_poly::domain::EvaluationDomain; 12 | use ark_poly::polynomial::univariate::DensePolynomial; 13 | 14 | /// This table will be the preprocessed version of the precomputed table, 15 | /// T, with arity 4. This structure is passed to the proof alongside the 16 | /// table of witness values. 17 | #[derive(Clone, Debug, Eq, PartialEq)] 18 | pub struct PreprocessedLookupTable 19 | where 20 | F: PrimeField, 21 | PC: HomomorphicCommitment, 22 | { 23 | /// This is the circuit size 24 | pub n: u32, 25 | 26 | /// Vector of columns in the preprocessed table containing a 27 | /// `MultiSet`, `Commitment` and `DensePolynomial`. 28 | pub(crate) t: Vec<(MultiSet, PC::Commitment, DensePolynomial)>, 29 | } 30 | 31 | impl PreprocessedLookupTable 32 | where 33 | F: PrimeField, 34 | PC: HomomorphicCommitment, 35 | { 36 | /// This function takes in a precomputed look up table and 37 | /// pads it to the length of the circuit entries, as a power 38 | /// of 2. The function then interpolates a polynomial from the 39 | /// padded table and makes a commitment to the poly. The 40 | /// outputted struct will be used in the proof alongside our 41 | /// circuit witness table. 42 | pub fn preprocess( 43 | table: &LookupTable, 44 | commit_key: &PC::CommitterKey, 45 | n: u32, 46 | ) -> Result { 47 | assert!(n.is_power_of_two()); 48 | let domain = EvaluationDomain::new(n as usize).unwrap(); 49 | let result = table 50 | .vec_to_multiset() 51 | .into_iter() 52 | .enumerate() 53 | .map(|(index, mut column)| { 54 | column.pad(n); 55 | let poly = column.to_polynomial(&domain); 56 | let poly_name = format!("t_{}_poly", index + 1); 57 | let labeled_poly = ark_poly_commit::LabeledPolynomial::new( 58 | poly_name, 59 | poly.to_owned(), 60 | None, 61 | None, 62 | ); 63 | let commitment = PC::commit(commit_key, &[labeled_poly], None) 64 | .map_err(to_pc_error::)?; 65 | Ok((column, commitment.0[0].commitment().clone(), poly)) 66 | }) 67 | .collect::>()?; 68 | Ok(Self { n, t: result }) 69 | } 70 | } 71 | 72 | #[cfg(test)] 73 | mod test { 74 | use super::*; 75 | use crate::batch_test; 76 | use crate::commitment::HomomorphicCommitment; 77 | use crate::lookup::{LookupTable, PreprocessedLookupTable}; 78 | use ark_bls12_377::Bls12_377; 79 | use ark_bls12_381::Bls12_381; 80 | use ark_ec::TEModelParameters; 81 | use rand_core::OsRng; 82 | 83 | /// This function creates a table and preprocesses it. Then it checks that 84 | /// all table columns are the same length. 85 | #[allow(clippy::extra_unused_type_parameters)] 86 | fn test_table_preprocessing() 87 | where 88 | F: PrimeField, 89 | P: TEModelParameters, 90 | PC: HomomorphicCommitment, 91 | { 92 | let pp = PC::setup(32, None, &mut OsRng) 93 | .map_err(to_pc_error::) 94 | .unwrap(); 95 | let (_committer_key, _) = PC::trim(&pp, 32, 0, None) 96 | .map_err(to_pc_error::) 97 | .unwrap(); 98 | 99 | // Commit Key 100 | let (ck, _) = PC::trim(&pp, 32, 0, None) 101 | .map_err(to_pc_error::) 102 | .unwrap(); 103 | 104 | let mut table: LookupTable = LookupTable::new(); 105 | 106 | (0..11).for_each(|_a| { 107 | table.insert_xor_row(19u64, 6u64, 64u64); 108 | table.insert_xor_row(4u64, 2u64, 64u64); 109 | }); 110 | 111 | let preprocessed_table = 112 | PreprocessedLookupTable::::preprocess(&table, &ck, 32) 113 | .unwrap(); 114 | 115 | preprocessed_table.t.iter().for_each(|column| { 116 | assert!(preprocessed_table.n as usize == column.0.len()); 117 | }); 118 | } 119 | 120 | // Bls12-381 tests 121 | batch_test!( 122 | [ 123 | test_table_preprocessing 124 | ], 125 | [] => ( 126 | Bls12_381, 127 | ark_ed_on_bls12_381::EdwardsParameters 128 | ) 129 | ); 130 | 131 | // Bls12-377 tests 132 | batch_test!( 133 | [ 134 | test_table_preprocessing 135 | ], 136 | [] => ( 137 | Bls12_377, 138 | ark_ed_on_bls12_377::EdwardsParameters 139 | ) 140 | ); 141 | } 142 | -------------------------------------------------------------------------------- /plonk-core/src/lookup/witness_table.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) DUSK NETWORK. All rights reserved. 6 | 7 | use crate::error::Error; 8 | use crate::lookup::{LookupTable, MultiSet}; 9 | use ark_ff::Field; 10 | 11 | /// This witness table contains queries to a lookup table for lookup gates. 12 | /// This table can have any arity. (But for the time other parts of the codebase 13 | /// force 4-arity) 14 | #[derive(Clone, Debug, Default, Eq, PartialEq)] 15 | pub struct WitnessTable 16 | where 17 | F: Field, 18 | { 19 | /// Vector containing one `MultiSet` for each wire 20 | pub f: Vec>, 21 | } 22 | 23 | impl WitnessTable 24 | where 25 | F: Field, 26 | { 27 | /// Initializes empty witness table of arity 4 28 | pub fn new() -> Self { 29 | Self { 30 | f: vec![MultiSet::new(); 4], 31 | } 32 | } 33 | 34 | /// This allows the witness table to be filled directly without 35 | /// taking any values, or the results, from the lookup table. 36 | /// If the values do no exist in the lookup table, then the proof 37 | /// will fail when witness and preprocessed tables are concatenated. 38 | pub fn from_wire_values(&mut self, wires: Vec) { 39 | assert_eq!(wires.len(), self.f.len()); 40 | wires 41 | .into_iter() 42 | .zip(self.f.iter_mut()) 43 | .for_each(|(val, column)| column.push(val)); 44 | } 45 | 46 | /// Attempts to look up a value from a lookup table. If successful, all four 47 | /// elements are pushed to their respective multisets. 48 | pub fn value_from_table( 49 | &mut self, 50 | lookup_table: &LookupTable, 51 | left_wire_val: F, 52 | right_wire_val: F, 53 | fourth_wire_val: F, 54 | ) -> Result<(), Error> { 55 | // This function is specific for 4-arity 56 | assert_eq!(self.f.len(), 4); 57 | let output_wire_val = lookup_table.lookup( 58 | left_wire_val, 59 | right_wire_val, 60 | fourth_wire_val, 61 | )?; 62 | self.f[0].push(left_wire_val); 63 | self.f[1].push(right_wire_val); 64 | self.f[2].push(output_wire_val); 65 | self.f[3].push(fourth_wire_val); 66 | Ok(()) 67 | } 68 | } 69 | #[cfg(test)] 70 | mod test { 71 | use super::*; 72 | use crate::batch_field_test; 73 | use crate::lookup::LookupTable; 74 | use ark_bls12_377::Fr as bls12_377_scalar_field; 75 | use ark_bls12_381::Fr as bls12_381_scalar_field; 76 | 77 | fn test_lookup_fuctionality_1() 78 | where 79 | F: Field, 80 | { 81 | // Build lookup table 82 | let lookup_table = LookupTable::::xor_table(0, 3); 83 | 84 | // Instantiate empty multisets of wire values in witness table 85 | let mut f = WitnessTable::::new(); 86 | // Read values from lookup table and insert into witness table 87 | assert!(f 88 | .value_from_table( 89 | &lookup_table, 90 | F::from(2u64), 91 | F::from(5u64), 92 | -F::one() 93 | ) 94 | .is_ok()); 95 | // Check that non existent elements cause a failure 96 | assert!(f 97 | .value_from_table( 98 | &lookup_table, 99 | F::from(25u64), 100 | F::from(5u64), 101 | -F::one() 102 | ) 103 | .is_err()); 104 | } 105 | 106 | fn test_lookup_fuctionality_2() 107 | where 108 | F: Field, 109 | { 110 | // Build empty lookup tables 111 | let mut lookup_table = LookupTable::::new(); 112 | 113 | // Add a consecutive set of tables, with 114 | // XOR operationd and addition operations 115 | lookup_table.insert_multi_xor(0, 4); 116 | lookup_table.insert_multi_add(2, 3); 117 | 118 | // Build empty witness table 119 | let mut f = WitnessTable::::new(); 120 | 121 | // Check for output of wires within lookup table and 122 | // if they exist input them to the witness table 123 | assert!(f 124 | .value_from_table( 125 | &lookup_table, 126 | F::from(2u32), 127 | F::from(3u32), 128 | -F::one() 129 | ) 130 | .is_ok()); 131 | assert!(f 132 | .value_from_table( 133 | &lookup_table, 134 | F::from(4u32), 135 | F::from(6u32), 136 | F::zero() 137 | ) 138 | .is_ok()); 139 | 140 | // Check that values not contained in the lookup table 141 | // do not get added to the witness table 142 | assert!(f 143 | .value_from_table( 144 | &lookup_table, 145 | F::from(22u32), 146 | F::one(), 147 | -F::one() 148 | ) 149 | .is_err()); 150 | assert!(f 151 | .value_from_table(&lookup_table, F::zero(), F::one(), F::zero()) 152 | .is_err()); 153 | } 154 | 155 | // Bls12-381 tests 156 | batch_field_test!( 157 | [ 158 | test_lookup_fuctionality_1, 159 | test_lookup_fuctionality_2 160 | ], 161 | [] => bls12_381_scalar_field 162 | ); 163 | 164 | // Bls12-377 tests 165 | batch_field_test!( 166 | [ 167 | test_lookup_fuctionality_1, 168 | test_lookup_fuctionality_2 169 | ], 170 | [] => bls12_377_scalar_field 171 | ); 172 | } 173 | -------------------------------------------------------------------------------- /plonk-core/src/permutation/constants.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) ZK-GARAGE. All rights reserved. 6 | 7 | //! Constants are used in the permutation argument to generate H cosets. 8 | #![allow(non_snake_case)] 9 | 10 | use ark_ff::FftField; 11 | 12 | pub(crate) fn K1() -> F { 13 | F::from(7_u64) 14 | } 15 | 16 | pub(crate) fn K2() -> F { 17 | F::from(13_u64) 18 | } 19 | 20 | pub(crate) fn K3() -> F { 21 | F::from(17_u64) 22 | } 23 | 24 | #[cfg(test)] 25 | mod test { 26 | use ark_bls12_377::Bls12_377; 27 | use ark_bls12_381::Bls12_381; 28 | use ark_poly::{EvaluationDomain, GeneralEvaluationDomain}; 29 | 30 | use crate::batch_test_field; 31 | 32 | use super::*; 33 | 34 | /// Check if `cts` generate valid cosets of the roots of 35 | /// unity subgroup (of `domain_size`) of the field F. 36 | /// https://hackmd.io/CfFCbA0TTJ6X08vHg0-9_g 37 | fn check_constants(cts: &[F], domain_size: u64) -> bool 38 | where 39 | F: FftField, 40 | { 41 | if cts.is_empty() { 42 | return true; 43 | }; 44 | 45 | let domain: GeneralEvaluationDomain = 46 | EvaluationDomain::new(domain_size as usize).unwrap(); 47 | 48 | // First check 49 | if domain.evaluate_vanishing_polynomial(cts[0]).is_zero() { 50 | return false; 51 | } 52 | 53 | let mut prev_cts = Vec::with_capacity(cts.len()); 54 | prev_cts.push(cts[0]); 55 | 56 | // Rest of the constants 57 | for k_last in cts.iter().skip(1) { 58 | let k_last_inv = k_last.inverse().unwrap(); 59 | 60 | // Check that the constant k_last is not in each of the previously 61 | // generated cosets k_prev * H. 62 | // <=> (k_prev / k_last )^domain_size -1 != 0. 63 | if prev_cts.iter().any(|&k_prev| { 64 | domain 65 | .evaluate_vanishing_polynomial(k_prev * k_last_inv) 66 | .is_zero() 67 | }) { 68 | return false; 69 | } 70 | prev_cts.push(*k_last); 71 | } 72 | 73 | true 74 | } 75 | 76 | fn test_constants() 77 | where 78 | F: FftField, 79 | { 80 | // The constants are checked for subgropus H up to 2^MAX_DEGREE size. 81 | let MAX_DEGREE = 32; 82 | let constants: [F; 3] = [super::K1(), super::K2(), super::K3()]; 83 | assert!(check_constants(&constants, 2u64.pow(MAX_DEGREE))); 84 | } 85 | 86 | batch_test_field!( 87 | [test_constants], 88 | [] => ( 89 | Bls12_381 90 | ) 91 | ); 92 | batch_test_field!( 93 | [test_constants], 94 | [] => ( 95 | Bls12_377 96 | ) 97 | ); 98 | } 99 | -------------------------------------------------------------------------------- /plonk-core/src/prelude.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) DUSK NETWORK. All rights reserved. 6 | 7 | //! Collection of functions needed to build and use PLONK circuits. 8 | //! 9 | //! Use this as the only import that you need to interact with the principal 10 | //! data structures of the plonk library. 11 | 12 | pub use crate::{ 13 | circuit::{self, verify_proof, Circuit, VerifierData}, 14 | constraint_system::{ecc::Point, StandardComposer, Variable}, 15 | error::Error, 16 | proof_system::{Proof, ProverKey, VerifierKey}, 17 | util::from_embedded_curve_scalar, 18 | }; 19 | -------------------------------------------------------------------------------- /plonk-core/src/proof_system/mod.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) DUSK NETWORK. All rights reserved. 6 | 7 | //! PLONK Proving System 8 | 9 | mod linearisation_poly; 10 | mod permutation; 11 | mod preprocess; 12 | mod quotient_poly; 13 | mod widget; 14 | 15 | pub mod pi; 16 | pub mod proof; 17 | pub mod prover; 18 | pub mod verifier; 19 | 20 | pub use proof::*; 21 | pub use prover::Prover; 22 | pub use verifier::Verifier; 23 | pub use widget::*; 24 | -------------------------------------------------------------------------------- /plonk-core/src/proof_system/pi.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) ZK-Garage. All rights reserved. 6 | 7 | //! Public Inputs of the circuit. This values are available for the 8 | //! [`super::Prover`] and [`super::Verifier`]. 9 | //! 10 | //! This module contains the implementation of the [`PublicInputs`] struct and 11 | //! all the basic manipulations such as inserting new values and getting the 12 | //! public inputs in evaluation or coefficient form. 13 | 14 | use alloc::collections::BTreeMap; 15 | use ark_ff::{FftField, ToConstraintField}; 16 | use ark_poly::{ 17 | univariate::DensePolynomial, EvaluationDomain, GeneralEvaluationDomain, 18 | UVPolynomial, 19 | }; 20 | use ark_serialize::{ 21 | CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write, 22 | }; 23 | use itertools::Itertools; 24 | 25 | use crate::prelude::Error; 26 | 27 | /// Public Inputs 28 | #[derive(CanonicalDeserialize, CanonicalSerialize, derivative::Derivative)] 29 | #[derivative(Clone, Debug, Default, Eq, Hash, PartialEq)] 30 | pub struct PublicInputs 31 | where 32 | F: FftField, 33 | { 34 | // non-zero values of the public input 35 | values: BTreeMap, 36 | } 37 | 38 | impl PublicInputs 39 | where 40 | F: FftField, 41 | { 42 | /// Creates a new struct for [`PublicInputs`]. 43 | pub fn new() -> Self { 44 | Self { 45 | values: BTreeMap::new(), 46 | } 47 | } 48 | 49 | /// Inserts a new public input value at a given position. 50 | /// 51 | /// If the value provided is zero no insertion will be made as zeros are the 52 | /// implicit value of empty positions. 53 | /// The function will panic if an insertion is atempted in an already 54 | /// occupied position. 55 | fn insert(&mut self, pos: usize, val: F) { 56 | if self.values.contains_key(&pos) { 57 | panic!("Insertion in public inputs conflicts with previous value at position {}", pos); 58 | } 59 | if val != F::zero() { 60 | self.values.insert(pos, val); 61 | } 62 | } 63 | 64 | /// Inserts public input data that can be converted to one or more field 65 | /// elements starting at a given position. 66 | /// Returns the number of field elements occupied by the input or 67 | /// [`Error::InvalidPublicInputValue`] if the input could not be converted. 68 | pub fn add_input(&mut self, pos: usize, item: &T) -> Result 69 | where 70 | T: ToConstraintField, 71 | { 72 | self.extend(pos, [item]) 73 | } 74 | 75 | /// Inserts all the elements of `iter` converted into constraint field 76 | /// elements in consecutive positions. 77 | /// Returns the number of field elements occupied by `iter` 78 | /// [`Error::InvalidPublicInputValue`] if the input could not be converted. 79 | fn extend<'t, T, I>( 80 | &mut self, 81 | init_pos: usize, 82 | iter: I, 83 | ) -> Result 84 | where 85 | T: 't + ToConstraintField, 86 | I: IntoIterator, 87 | { 88 | let mut count = 0; 89 | for item in iter { 90 | for elem in item 91 | .to_field_elements() 92 | .ok_or(Error::InvalidPublicInputValue)? 93 | { 94 | self.insert(init_pos + count, elem); 95 | count += 1; 96 | } 97 | } 98 | Ok(count) 99 | } 100 | 101 | /// Returns the public inputs as a vector of `n` evaluations. 102 | /// The provided `n` must be a power of 2. 103 | pub fn as_evals(&self, n: usize) -> Vec { 104 | assert!(n.is_power_of_two()); 105 | let mut pi = vec![F::zero(); n]; 106 | self.values.iter().for_each(|(pos, eval)| pi[*pos] = *eval); 107 | pi 108 | } 109 | 110 | /// Returns the public inputs as a vector of `n` evaluations. 111 | /// The provided `n` must be a power of 2. 112 | pub fn into_dense_poly(&self, n: usize) -> DensePolynomial { 113 | let domain = GeneralEvaluationDomain::::new(n).unwrap(); 114 | let evals = self.as_evals(n); 115 | DensePolynomial::from_coefficients_vec(domain.ifft(&evals)) 116 | } 117 | 118 | /// Constructs [`PublicInputs`] from a positions and a values. 119 | /// 120 | /// Panics if the positions and values have different lenghts or if 121 | /// several values try to be inserted in the same position. 122 | pub fn from_val_pos(pos: &[usize], vals: &[T]) -> Result 123 | where 124 | T: ToConstraintField, 125 | { 126 | let mut pi = Self::new(); 127 | pos.iter().zip_eq(vals).try_for_each( 128 | |(p, v)| -> Result<(), Error> { 129 | pi.add_input(*p, v)?; 130 | Ok(()) 131 | }, 132 | )?; 133 | Ok(pi) 134 | } 135 | 136 | /// Returns the position of non-zero PI values. 137 | pub fn get_pos(&self) -> impl Iterator { 138 | self.values.keys() 139 | } 140 | 141 | /// Returns the non-zero PI values. 142 | pub fn get_vals(&self) -> impl Iterator { 143 | self.values.values() 144 | } 145 | } 146 | 147 | #[cfg(test)] 148 | mod test { 149 | use super::*; 150 | use crate::batch_field_test; 151 | use ark_bls12_377::Fr as Bls12_377_scalar_field; 152 | use ark_bls12_381::Fr as Bls12_381_scalar_field; 153 | 154 | // Checks PublicInputs representation is not affected by insertion order 155 | // or extra zeros. 156 | fn test_pi_unique_repr() 157 | where 158 | F: FftField, 159 | { 160 | let mut pi_1 = PublicInputs::new(); 161 | 162 | pi_1.insert(2, F::from(2u64)); 163 | pi_1.insert(5, F::from(5u64)); 164 | pi_1.insert(6, F::from(6u64)); 165 | 166 | let mut pi_2 = PublicInputs::new(); 167 | 168 | pi_2.insert(6, F::from(6u64)); 169 | pi_2.insert(5, F::from(5u64)); 170 | pi_2.insert(2, F::from(2u64)); 171 | 172 | pi_2.insert(0, F::zero()); 173 | pi_2.insert(1, F::zero()); 174 | assert_eq!(pi_1, pi_2); 175 | } 176 | 177 | // Checks PublicInputs does not allow to override already inserted values. 178 | fn test_pi_dup_insertion() 179 | where 180 | F: FftField, 181 | { 182 | let mut pi_1 = PublicInputs::new(); 183 | 184 | pi_1.insert(2, F::from(2u64)); 185 | pi_1.insert(5, F::from(5u64)); 186 | pi_1.insert(5, F::from(2u64)); 187 | } 188 | 189 | // Bls12-381 tests 190 | batch_field_test!( 191 | [ 192 | test_pi_unique_repr 193 | ], 194 | [ 195 | test_pi_dup_insertion 196 | ] => Bls12_381_scalar_field 197 | ); 198 | 199 | // Bls12-377 tests 200 | batch_field_test!( 201 | [ 202 | test_pi_unique_repr 203 | ], 204 | [ 205 | test_pi_dup_insertion 206 | ] => Bls12_377_scalar_field 207 | ); 208 | } 209 | -------------------------------------------------------------------------------- /plonk-core/src/proof_system/verifier.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) DUSK NETWORK. All rights reserved. 6 | 7 | //! Verifier-side of the PLONK Proving System 8 | 9 | //use crate::circuit::EmbeddedCurve; 10 | use crate::{ 11 | commitment::HomomorphicCommitment, 12 | constraint_system::StandardComposer, 13 | error::Error, 14 | proof_system::{widget::VerifierKey as PlonkVerifierKey, Proof}, 15 | }; 16 | use ark_ec::TEModelParameters; 17 | use ark_ff::PrimeField; 18 | use core::marker::PhantomData; 19 | use merlin::Transcript; 20 | 21 | use super::pi::PublicInputs; 22 | 23 | /// Abstraction structure designed verify [`Proof`]s. 24 | pub struct Verifier 25 | where 26 | F: PrimeField, 27 | P: TEModelParameters, 28 | PC: HomomorphicCommitment, 29 | { 30 | /// VerificationKey which is used to verify a specific PLONK circuit 31 | pub verifier_key: Option>, 32 | 33 | /// Circuit Description 34 | pub(crate) cs: StandardComposer, 35 | 36 | /// Store the messages exchanged during the preprocessing stage. 37 | /// 38 | /// This is copied each time, we make a proof, so that we can use the same 39 | /// verifier to verify multiple proofs from the same circuit. If this is 40 | /// not copied, then the verification procedure will modify the transcript, 41 | /// making it unusable for future proofs. 42 | pub preprocessed_transcript: Transcript, 43 | } 44 | 45 | impl Verifier 46 | where 47 | F: PrimeField, 48 | P: TEModelParameters, 49 | PC: HomomorphicCommitment, 50 | { 51 | /// Creates a new `Verifier` instance. 52 | pub fn new(label: &'static [u8]) -> Self { 53 | Self { 54 | verifier_key: None, 55 | cs: StandardComposer::new(), 56 | preprocessed_transcript: Transcript::new(label), 57 | } 58 | } 59 | 60 | /// Creates a new `Verifier` instance with some expected size. 61 | pub fn with_expected_size(label: &'static [u8], size: usize) -> Self { 62 | Self { 63 | verifier_key: None, 64 | cs: StandardComposer::with_expected_size(size), 65 | preprocessed_transcript: Transcript::new(label), 66 | } 67 | } 68 | 69 | /// Returns the smallest power of two needed for the curcuit 70 | pub fn circuit_bound(&self) -> usize { 71 | self.cs.circuit_bound() 72 | } 73 | 74 | /// Returns a mutable copy of the underlying composer. 75 | pub fn mut_cs(&mut self) -> &mut StandardComposer { 76 | &mut self.cs 77 | } 78 | 79 | /// Preprocess a circuit to obtain a [`PlonkVerifierKey`] and a 80 | /// circuit descriptor so that the `Verifier` instance can verify 81 | /// [`Proof`]s for this circuit descriptor instance. 82 | pub fn preprocess( 83 | &mut self, 84 | commit_key: &PC::CommitterKey, 85 | ) -> Result<(), Error> { 86 | let vk = self.cs.preprocess_verifier( 87 | commit_key, 88 | &mut self.preprocessed_transcript, 89 | PhantomData::, 90 | )?; 91 | 92 | self.verifier_key = Some(vk); 93 | Ok(()) 94 | } 95 | 96 | /// Keys the [`Transcript`] with additional seed information 97 | /// Wrapper around [`Transcript::append_message`]. 98 | /// 99 | /// [`Transcript`]: merlin::Transcript 100 | /// [`Transcript::append_message`]: merlin::Transcript::append_message 101 | pub fn key_transcript(&mut self, label: &'static [u8], message: &[u8]) { 102 | self.preprocessed_transcript.append_message(label, message); 103 | } 104 | 105 | /// Verifies a [`Proof`] using `pc_verifier_key` and `public_inputs`. 106 | pub fn verify( 107 | &self, 108 | proof: &Proof, 109 | pc_verifier_key: &PC::VerifierKey, 110 | public_inputs: &PublicInputs, 111 | ) -> Result<(), Error> { 112 | proof.verify::

( 113 | self.verifier_key.as_ref().unwrap(), 114 | &mut self.preprocessed_transcript.clone(), 115 | pc_verifier_key, 116 | public_inputs, 117 | ) 118 | } 119 | } 120 | 121 | impl Default for Verifier 122 | where 123 | F: PrimeField, 124 | P: TEModelParameters, 125 | PC: HomomorphicCommitment, 126 | { 127 | #[inline] 128 | fn default() -> Verifier { 129 | Verifier::new(b"plonk") 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /plonk-core/src/proof_system/widget/arithmetic.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) DUSK NETWORK. All rights reserved. 6 | 7 | //! Arithmetic Gates 8 | 9 | use crate::proof_system::WitnessValues; 10 | use crate::{ 11 | constraint_system::SBOX_ALPHA, 12 | proof_system::linearisation_poly::ProofEvaluations, 13 | }; 14 | use ark_ff::{FftField, PrimeField}; 15 | use ark_poly::{polynomial::univariate::DensePolynomial, Evaluations}; 16 | use ark_poly_commit::PolynomialCommitment; 17 | use ark_serialize::*; 18 | 19 | /// Arithmetic Gates Prover Key 20 | #[derive(CanonicalDeserialize, CanonicalSerialize, derivative::Derivative)] 21 | #[derivative(Clone, Debug, Eq, PartialEq)] 22 | pub struct ProverKey 23 | where 24 | F: FftField, 25 | { 26 | /// Multiplication Selector 27 | pub q_m: (DensePolynomial, Evaluations), 28 | 29 | /// Left Wire Selector 30 | pub q_l: (DensePolynomial, Evaluations), 31 | 32 | /// Right Wire Selector 33 | pub q_r: (DensePolynomial, Evaluations), 34 | 35 | /// Output Wire Selector 36 | pub q_o: (DensePolynomial, Evaluations), 37 | 38 | /// Fourth Wire Selector 39 | pub q_4: (DensePolynomial, Evaluations), 40 | 41 | /// Constant Selector 42 | pub q_c: (DensePolynomial, Evaluations), 43 | 44 | /// High degree selector 45 | pub q_hl: (DensePolynomial, Evaluations), 46 | 47 | /// High degree selector 48 | pub q_hr: (DensePolynomial, Evaluations), 49 | 50 | /// High degree selector 51 | pub q_h4: (DensePolynomial, Evaluations), 52 | 53 | /// Arithmetic Selector 54 | pub q_arith: (DensePolynomial, Evaluations), 55 | } 56 | 57 | impl ProverKey 58 | where 59 | F: PrimeField, 60 | { 61 | /// Computes the arithmetic gate contribution to the quotient polynomial at 62 | /// the element of the domain at the given `index`. 63 | pub fn compute_quotient_i( 64 | &self, 65 | index: usize, 66 | wit_vals: WitnessValues, 67 | ) -> F { 68 | ((wit_vals.a_val * wit_vals.b_val * self.q_m.1[index]) 69 | + (wit_vals.a_val * self.q_l.1[index]) 70 | + (wit_vals.b_val * self.q_r.1[index]) 71 | + (wit_vals.c_val * self.q_o.1[index]) 72 | + (wit_vals.d_val * self.q_4.1[index]) 73 | + (wit_vals.a_val.pow([SBOX_ALPHA]) * self.q_hl.1[index]) 74 | + (wit_vals.b_val.pow([SBOX_ALPHA]) * self.q_hr.1[index]) 75 | + (wit_vals.d_val.pow([SBOX_ALPHA]) * self.q_h4.1[index]) 76 | + self.q_c.1[index]) 77 | * self.q_arith.1[index] 78 | } 79 | 80 | /// Computes the arithmetic gate contribution to the linearisation 81 | /// polynomial at the given evaluation points. 82 | pub fn compute_linearisation( 83 | &self, 84 | a_eval: F, 85 | b_eval: F, 86 | c_eval: F, 87 | d_eval: F, 88 | q_arith_eval: F, 89 | ) -> DensePolynomial { 90 | &(&((&self.q_m.0 * (a_eval * b_eval)) 91 | + (&self.q_l.0 * a_eval) 92 | + (&self.q_r.0 * b_eval) 93 | + (&self.q_o.0 * c_eval) 94 | + (&self.q_4.0 * d_eval) 95 | + (&self.q_hl.0 * a_eval.pow([SBOX_ALPHA])) 96 | + (&self.q_hr.0 * b_eval.pow([SBOX_ALPHA])) 97 | + (&self.q_h4.0 * d_eval.pow([SBOX_ALPHA]))) 98 | + &self.q_c.0) 99 | * q_arith_eval 100 | } 101 | } 102 | 103 | /// Arithmetic Gates Verifier Key 104 | #[derive(CanonicalDeserialize, CanonicalSerialize, derivative::Derivative)] 105 | #[derivative( 106 | Clone, 107 | Copy(bound = "PC::Commitment: Copy"), 108 | Debug(bound = "PC::Commitment: std::fmt::Debug"), 109 | Eq(bound = "PC::Commitment: Eq"), 110 | PartialEq(bound = "PC::Commitment: PartialEq") 111 | )] 112 | pub struct VerifierKey 113 | where 114 | F: PrimeField, 115 | PC: PolynomialCommitment>, 116 | { 117 | /// Multiplication Selector Commitment 118 | pub q_m: PC::Commitment, 119 | 120 | /// Left Selector Commitment 121 | pub q_l: PC::Commitment, 122 | 123 | /// Right Selector Commitment 124 | pub q_r: PC::Commitment, 125 | 126 | /// Output Selector Commitment 127 | pub q_o: PC::Commitment, 128 | 129 | /// Fourth Selector Commitment 130 | pub q_4: PC::Commitment, 131 | 132 | /// Constant Selector Commitment 133 | pub q_c: PC::Commitment, 134 | 135 | /// High degree left Selector Commitment 136 | pub q_hl: PC::Commitment, 137 | 138 | /// High degree right Selector Commitment 139 | pub q_hr: PC::Commitment, 140 | 141 | /// High degree 4-th Selector Commitment 142 | pub q_h4: PC::Commitment, 143 | 144 | /// Arithmetic Selector Commitment 145 | pub q_arith: PC::Commitment, 146 | } 147 | 148 | impl VerifierKey 149 | where 150 | F: PrimeField, 151 | PC: PolynomialCommitment>, 152 | { 153 | /// Computes arithmetic gate contribution to the linearisation polynomial 154 | /// commitment. 155 | pub fn compute_linearisation_commitment( 156 | &self, 157 | scalars: &mut Vec, 158 | points: &mut Vec, 159 | evaluations: &ProofEvaluations, 160 | ) { 161 | let q_arith_eval = evaluations.custom_evals.get("q_arith_eval"); 162 | 163 | scalars.push( 164 | evaluations.wire_evals.a_eval 165 | * evaluations.wire_evals.b_eval 166 | * q_arith_eval, 167 | ); 168 | points.push(self.q_m.clone()); 169 | 170 | scalars.push(evaluations.wire_evals.a_eval * q_arith_eval); 171 | points.push(self.q_l.clone()); 172 | 173 | scalars.push(evaluations.wire_evals.b_eval * q_arith_eval); 174 | points.push(self.q_r.clone()); 175 | 176 | scalars.push(evaluations.wire_evals.d_eval * q_arith_eval); 177 | points.push(self.q_4.clone()); 178 | 179 | scalars.push(evaluations.wire_evals.c_eval * q_arith_eval); 180 | points.push(self.q_o.clone()); 181 | 182 | scalars.push( 183 | evaluations.wire_evals.a_eval.pow([SBOX_ALPHA]) * q_arith_eval, 184 | ); 185 | points.push(self.q_hl.clone()); 186 | 187 | scalars.push( 188 | evaluations.wire_evals.b_eval.pow([SBOX_ALPHA]) * q_arith_eval, 189 | ); 190 | points.push(self.q_hr.clone()); 191 | 192 | scalars.push( 193 | evaluations.wire_evals.d_eval.pow([SBOX_ALPHA]) * q_arith_eval, 194 | ); 195 | points.push(self.q_h4.clone()); 196 | 197 | scalars.push(q_arith_eval); 198 | points.push(self.q_c.clone()); 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /plonk-core/src/proof_system/widget/ecc/curve_addition.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) DUSK NETWORK. All rights reserved. 6 | 7 | //! Elliptic Curve Point Addition Gate 8 | 9 | use crate::proof_system::{ 10 | linearisation_poly::CustomEvaluations, 11 | widget::{GateConstraint, WitnessValues}, 12 | CustomValues, 13 | }; 14 | use ark_ec::{ModelParameters, TEModelParameters}; 15 | use ark_ff::PrimeField; 16 | use core::marker::PhantomData; 17 | 18 | /// Values needed for the computation of the Curve Addition gate constraint. 19 | pub struct CAVals 20 | where 21 | F: PrimeField, 22 | { 23 | /// Left wire value in the next position 24 | pub a_next_val: F, 25 | /// Right wire value in the next position 26 | pub b_next_val: F, 27 | /// Fourth wire value in the next position 28 | pub d_next_val: F, 29 | } 30 | 31 | impl CustomValues for CAVals 32 | where 33 | F: PrimeField, 34 | { 35 | fn from_evaluations(custom_evals: &CustomEvaluations) -> Self { 36 | let a_next_val = custom_evals.get("a_next_eval"); 37 | let b_next_val = custom_evals.get("b_next_eval"); 38 | let d_next_val = custom_evals.get("d_next_eval"); 39 | CAVals { 40 | a_next_val, 41 | b_next_val, 42 | d_next_val, 43 | } 44 | } 45 | } 46 | 47 | /// Curve Addition Gate 48 | #[derive(derivative::Derivative)] 49 | #[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] 50 | pub struct CurveAddition(PhantomData<(F, P)>) 51 | where 52 | F: PrimeField, 53 | P: ModelParameters; 54 | 55 | impl GateConstraint for CurveAddition 56 | where 57 | F: PrimeField, 58 | P: TEModelParameters, 59 | { 60 | type CustomVals = CAVals; 61 | #[inline] 62 | fn constraints( 63 | separation_challenge: F, 64 | wit_vals: WitnessValues, 65 | custom_vals: Self::CustomVals, 66 | ) -> F { 67 | let x_1 = wit_vals.a_val; 68 | let x_3 = custom_vals.a_next_val; 69 | let y_1 = wit_vals.b_val; 70 | let y_3 = custom_vals.b_next_val; 71 | let x_2 = wit_vals.c_val; 72 | let y_2 = wit_vals.d_val; 73 | let x1_y2 = custom_vals.d_next_val; 74 | 75 | let kappa = separation_challenge.square(); 76 | 77 | // Check that `x1 * y2` is correct 78 | let xy_consistency = x_1 * y_2 - x1_y2; 79 | 80 | let y1_x2 = y_1 * x_2; 81 | let y1_y2 = y_1 * y_2; 82 | let x1_x2 = x_1 * x_2; 83 | 84 | // Check that `x_3` is correct 85 | let x3_lhs = x1_y2 + y1_x2; 86 | let x3_rhs = x_3 + (x_3 * P::COEFF_D * x1_y2 * y1_x2); 87 | let x3_consistency = (x3_lhs - x3_rhs) * kappa; 88 | 89 | // Check that `y_3` is correct 90 | let y3_lhs = y1_y2 - P::COEFF_A * x1_x2; 91 | let y3_rhs = y_3 - y_3 * P::COEFF_D * x1_y2 * y1_x2; 92 | let y3_consistency = (y3_lhs - y3_rhs) * kappa.square(); 93 | 94 | (xy_consistency + x3_consistency + y3_consistency) 95 | * separation_challenge 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /plonk-core/src/proof_system/widget/ecc/fixed_base_scalar_mul.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) DUSK NETWORK. All rights reserved. 6 | 7 | //! Elliptic Curve Fixed-Base Scalar Multiplication Gate 8 | //! 9 | //! NOTE: The ECC gadget does not check that the initial point is on the 10 | //! curve for two reasons: 11 | //! - We constrain the accumulator to start from the identity point, which the 12 | //! verifier knows is on the curve 13 | //! - We are adding multiples of the generator to the accumulator which the 14 | //! verifier also knows is on the curve and is prime order 15 | //! - We do allow arbitrary BlsScalar multiplication, and possibly XXX: may add 16 | //! constraints to ensure the generator is correct (prime order) 17 | //! 18 | //! Bits are accumulated in base2. So we use d(Xw) - 2d(X) to extract the 19 | //! base2 bit. 20 | 21 | use crate::proof_system::{ 22 | linearisation_poly::CustomEvaluations, 23 | widget::{GateConstraint, WitnessValues}, 24 | CustomValues, 25 | }; 26 | use ark_ec::{ModelParameters, TEModelParameters}; 27 | use ark_ff::PrimeField; 28 | use core::marker::PhantomData; 29 | 30 | /// Values needed for the computation of the Fixed Base Multiplication gate 31 | /// constraint. 32 | pub struct FBSMVals 33 | where 34 | F: PrimeField, 35 | { 36 | /// Left wire value in the next position 37 | pub a_next_val: F, 38 | /// Right wire value in the next position 39 | pub b_next_val: F, 40 | /// Fourth wire value in the next position 41 | pub d_next_val: F, 42 | /// Left selector value 43 | pub q_l_val: F, 44 | /// Right selector value 45 | pub q_r_val: F, 46 | /// Constant selector value 47 | pub q_c_val: F, 48 | } 49 | 50 | impl CustomValues for FBSMVals 51 | where 52 | F: PrimeField, 53 | { 54 | fn from_evaluations(custom_evals: &CustomEvaluations) -> Self { 55 | let a_next_val = custom_evals.get("a_next_eval"); 56 | let b_next_val = custom_evals.get("b_next_eval"); 57 | let d_next_val = custom_evals.get("d_next_eval"); 58 | let q_l_val = custom_evals.get("q_l_eval"); 59 | let q_r_val = custom_evals.get("q_r_eval"); 60 | let q_c_val = custom_evals.get("q_c_eval"); 61 | FBSMVals { 62 | a_next_val, 63 | b_next_val, 64 | d_next_val, 65 | q_l_val, 66 | q_r_val, 67 | q_c_val, 68 | } 69 | } 70 | } 71 | 72 | /// Fixed-Base Scalar Multiplication Gate 73 | #[derive(derivative::Derivative)] 74 | #[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] 75 | pub struct FixedBaseScalarMul(PhantomData<(F, P)>) 76 | where 77 | F: PrimeField, 78 | P: ModelParameters; 79 | 80 | impl GateConstraint for FixedBaseScalarMul 81 | where 82 | F: PrimeField, 83 | P: TEModelParameters, 84 | { 85 | type CustomVals = FBSMVals; 86 | 87 | #[inline] 88 | fn constraints( 89 | separation_challenge: F, 90 | wit_vals: WitnessValues, 91 | custom_vals: Self::CustomVals, 92 | ) -> F { 93 | let kappa = separation_challenge.square(); 94 | let kappa_sq = kappa.square(); 95 | let kappa_cu = kappa_sq * kappa; 96 | 97 | let x_beta_eval = custom_vals.q_l_val; 98 | let y_beta_eval = custom_vals.q_r_val; 99 | 100 | let acc_x = wit_vals.a_val; 101 | let acc_x_next = custom_vals.a_next_val; 102 | let acc_y = wit_vals.b_val; 103 | let acc_y_next = custom_vals.b_next_val; 104 | 105 | let xy_alpha = wit_vals.c_val; 106 | 107 | let accumulated_bit = wit_vals.d_val; 108 | let accumulated_bit_next = custom_vals.d_next_val; 109 | let bit = extract_bit(accumulated_bit, accumulated_bit_next); 110 | 111 | // Check bit consistency 112 | let bit_consistency = check_bit_consistency(bit); 113 | 114 | let y_alpha = bit.square() * (y_beta_eval - F::one()) + F::one(); 115 | let x_alpha = x_beta_eval * bit; 116 | 117 | // xy_alpha consistency check 118 | let xy_consistency = ((bit * custom_vals.q_c_val) - xy_alpha) * kappa; 119 | 120 | // x accumulator consistency check 121 | let x_3 = acc_x_next; 122 | let lhs = x_3 + (x_3 * xy_alpha * acc_x * acc_y * P::COEFF_D); 123 | let rhs = (x_alpha * acc_y) + (y_alpha * acc_x); 124 | let x_acc_consistency = (lhs - rhs) * kappa_sq; 125 | 126 | // y accumulator consistency check 127 | let y_3 = acc_y_next; 128 | let lhs = y_3 - (y_3 * xy_alpha * acc_x * acc_y * P::COEFF_D); 129 | let rhs = y_alpha * acc_y - P::COEFF_A * x_alpha * acc_x; 130 | let y_acc_consistency = (lhs - rhs) * kappa_cu; 131 | 132 | let checks = bit_consistency 133 | + x_acc_consistency 134 | + y_acc_consistency 135 | + xy_consistency; 136 | 137 | checks * separation_challenge 138 | } 139 | } 140 | 141 | /// Extracts the bit value from the accumulated bit. 142 | pub(crate) fn extract_bit(curr_acc: F, next_acc: F) -> F 143 | where 144 | F: PrimeField, 145 | { 146 | next_acc - curr_acc - curr_acc 147 | } 148 | 149 | /// Ensures that the bit is either `+1`, `-1`, or `0`. 150 | pub(crate) fn check_bit_consistency(bit: F) -> F 151 | where 152 | F: PrimeField, 153 | { 154 | let one = F::one(); 155 | bit * (bit - one) * (bit + one) 156 | } 157 | -------------------------------------------------------------------------------- /plonk-core/src/proof_system/widget/ecc/mod.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) DUSK NETWORK. All rights reserved. 6 | 7 | //! Elliptic Curve Cryptography Gates 8 | 9 | mod curve_addition; 10 | mod fixed_base_scalar_mul; 11 | 12 | pub use curve_addition::*; 13 | pub use fixed_base_scalar_mul::*; 14 | -------------------------------------------------------------------------------- /plonk-core/src/proof_system/widget/logic.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) DUSK NETWORK. All rights reserved.. 6 | 7 | //! Logic Gates 8 | 9 | use crate::proof_system::{ 10 | linearisation_poly::CustomEvaluations, 11 | widget::{GateConstraint, WitnessValues}, 12 | CustomValues, 13 | }; 14 | use ark_ff::PrimeField; 15 | use core::marker::PhantomData; 16 | 17 | /// Values needed for the computation of the logic gate constraint. 18 | pub struct LogicVals 19 | where 20 | F: PrimeField, 21 | { 22 | /// Left wire value in the next position 23 | pub a_next_val: F, 24 | /// Right wire value in the next position 25 | pub b_next_val: F, 26 | /// Fourth wire value in the next position 27 | pub d_next_val: F, 28 | /// Constant selector value 29 | pub q_c_val: F, 30 | } 31 | 32 | impl CustomValues for LogicVals 33 | where 34 | F: PrimeField, 35 | { 36 | fn from_evaluations(custom_evals: &CustomEvaluations) -> Self { 37 | // TODO: Subsitute labels 38 | let a_next_val = custom_evals.get("a_next_eval"); 39 | let b_next_val = custom_evals.get("b_next_eval"); 40 | let d_next_val = custom_evals.get("d_next_eval"); 41 | let q_c_val = custom_evals.get("q_c_eval"); 42 | LogicVals { 43 | a_next_val, 44 | b_next_val, 45 | d_next_val, 46 | q_c_val, 47 | } 48 | } 49 | } 50 | 51 | /// Logic Gate 52 | #[derive(derivative::Derivative)] 53 | #[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] 54 | pub struct Logic(PhantomData) 55 | where 56 | F: PrimeField; 57 | 58 | impl GateConstraint for Logic 59 | where 60 | F: PrimeField, 61 | { 62 | type CustomVals = LogicVals; 63 | 64 | #[inline] 65 | fn constraints( 66 | separation_challenge: F, 67 | wit_vals: WitnessValues, 68 | custom_vals: Self::CustomVals, 69 | ) -> F { 70 | let four = F::from(4_u64); 71 | let kappa = separation_challenge.square(); 72 | let kappa_sq = kappa.square(); 73 | let kappa_cu = kappa_sq * kappa; 74 | let kappa_qu = kappa_cu * kappa; 75 | 76 | let a = custom_vals.a_next_val - four * wit_vals.a_val; 77 | let c_0 = delta(a); 78 | 79 | let b = custom_vals.b_next_val - four * wit_vals.b_val; 80 | let c_1 = delta(b) * kappa; 81 | 82 | let d = custom_vals.d_next_val - four * wit_vals.d_val; 83 | let c_2 = delta(d) * kappa_sq; 84 | 85 | let w = wit_vals.c_val; 86 | let c_3 = (w - a * b) * kappa_cu; 87 | 88 | let c_4 = delta_xor_and(a, b, w, d, custom_vals.q_c_val) * kappa_qu; 89 | 90 | (c_0 + c_1 + c_2 + c_3 + c_4) * separation_challenge 91 | } 92 | } 93 | 94 | /// Computes `f(f-1)(f-2)(f-3)` 95 | pub(crate) fn delta(f: F) -> F 96 | where 97 | F: PrimeField, 98 | { 99 | let f_1 = f - F::one(); 100 | let f_2 = f - F::from(2_u64); 101 | let f_3 = f - F::from(3_u64); 102 | f * f_1 * f_2 * f_3 103 | } 104 | 105 | /// The identity we want to check is `q_logic * A = 0` where: 106 | /// 107 | /// ```text 108 | /// A = B + E 109 | /// B = q_c * [9c - 3(a+b)] 110 | /// E = 3(a+b+c) - 2F 111 | /// F = w[w(4w - 18(a+b) + 81) + 18(a^2 + b^2) - 81(a+b) + 83] 112 | /// ``` 113 | #[allow(non_snake_case)] 114 | pub(crate) fn delta_xor_and(a: F, b: F, w: F, c: F, q_c: F) -> F 115 | where 116 | F: PrimeField, 117 | { 118 | let nine = F::from(9_u64); 119 | let two = F::from(2_u64); 120 | let three = F::from(3_u64); 121 | let four = F::from(4_u64); 122 | let eighteen = F::from(18_u64); 123 | let eighty_one = F::from(81_u64); 124 | let eighty_three = F::from(83_u64); 125 | let F = w 126 | * (w * (four * w - eighteen * (a + b) + eighty_one) 127 | + eighteen * (a.square() + b.square()) 128 | - eighty_one * (a + b) 129 | + eighty_three); 130 | let E = three * (a + b + c) - (two * F); 131 | let B = q_c * ((nine * c) - three * (a + b)); 132 | B + E 133 | } 134 | -------------------------------------------------------------------------------- /plonk-core/src/proof_system/widget/lookup.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) ZK-Garage. All rights reserved. 6 | //! Lookup gates 7 | 8 | use crate::error::Error; 9 | use crate::lookup::multiset::MultiSet; 10 | use crate::proof_system::linearisation_poly::ProofEvaluations; 11 | use crate::util::lc; 12 | use ark_ff::{FftField, PrimeField}; 13 | use ark_poly::polynomial::univariate::DensePolynomial; 14 | use ark_poly::{EvaluationDomain, Evaluations, GeneralEvaluationDomain}; 15 | use ark_poly_commit::PolynomialCommitment; 16 | use ark_serialize::*; 17 | 18 | /// Lookup Gates Prover Key 19 | #[derive(CanonicalDeserialize, CanonicalSerialize, derivative::Derivative)] 20 | #[derivative(Clone, Debug, Eq, PartialEq)] 21 | pub struct ProverKey 22 | where 23 | F: PrimeField, 24 | { 25 | /// Lookup selector 26 | pub q_lookup: (DensePolynomial, Evaluations), 27 | /// Column 1 of lookup table 28 | pub table_1: MultiSet, 29 | /// Column 2 of lookup table 30 | pub table_2: MultiSet, 31 | /// Column 3 of lookup table 32 | pub table_3: MultiSet, 33 | /// Column 4 of lookup table 34 | pub table_4: MultiSet, 35 | } 36 | 37 | impl ProverKey 38 | where 39 | F: PrimeField, 40 | { 41 | /// Compute lookup portion of quotient polynomial 42 | pub fn compute_lookup_quotient_term( 43 | &self, 44 | domain: &GeneralEvaluationDomain, 45 | wl_eval_8n: &[F], 46 | wr_eval_8n: &[F], 47 | wo_eval_8n: &[F], 48 | w4_eval_8n: &[F], 49 | f_eval_8n: &[F], 50 | table_eval_8n: &[F], 51 | h1_eval_8n: &[F], 52 | h2_eval_8n: &[F], 53 | z2_eval_8n: &[F], 54 | l1_eval_8n: &[F], 55 | delta: F, 56 | epsilon: F, 57 | zeta: F, 58 | lookup_sep: F, 59 | ) -> Result, Error> 60 | where 61 | F: PrimeField, 62 | { 63 | let domain_8n = GeneralEvaluationDomain::::new(8 * domain.size()) 64 | .ok_or(Error::InvalidEvalDomainSize { 65 | log_size_of_group: (8 * domain.size()).trailing_zeros(), 66 | adicity: 67 | <::FftParams as ark_ff::FftParameters>::TWO_ADICITY, 68 | })?; 69 | 70 | Ok((0..domain_8n.size()) 71 | .map(|i| { 72 | self.compute_quotient_i( 73 | i, 74 | wl_eval_8n[i], 75 | wr_eval_8n[i], 76 | wo_eval_8n[i], 77 | w4_eval_8n[i], 78 | f_eval_8n[i], 79 | table_eval_8n[i], 80 | table_eval_8n[i + 8], 81 | h1_eval_8n[i], 82 | h1_eval_8n[i + 8], 83 | h2_eval_8n[i], 84 | z2_eval_8n[i], 85 | z2_eval_8n[i + 8], 86 | l1_eval_8n[i], 87 | delta, 88 | epsilon, 89 | zeta, 90 | lookup_sep, 91 | ) 92 | }) 93 | .collect()) 94 | } 95 | 96 | /// Compute evals of lookup portion of quotient polynomial 97 | pub fn compute_quotient_i( 98 | &self, 99 | index: usize, 100 | w_l_i: F, 101 | w_r_i: F, 102 | w_o_i: F, 103 | w_4_i: F, 104 | f_i: F, 105 | table_i: F, 106 | table_i_next: F, 107 | h1_i: F, 108 | h1_i_next: F, 109 | h2_i: F, 110 | z2_i: F, 111 | z2_i_next: F, 112 | l1_i: F, 113 | delta: F, 114 | epsilon: F, 115 | zeta: F, 116 | lookup_sep: F, 117 | ) -> F { 118 | // q_lookup(X) * (a(X) + zeta * b(X) + (zeta^2 * c(X)) + (zeta^3 * d(X) 119 | // - f(X))) * α_1 120 | let lookup_sep_sq = lookup_sep.square(); 121 | let lookup_sep_cu = lookup_sep_sq * lookup_sep; 122 | let one_plus_delta = delta + F::one(); 123 | let epsilon_one_plus_delta = epsilon * one_plus_delta; 124 | 125 | let a = { 126 | let q_lookup_i = self.q_lookup.1[index]; 127 | let compressed_tuple = lc(&[w_l_i, w_r_i, w_o_i, w_4_i], &zeta); 128 | q_lookup_i * (compressed_tuple - f_i) * lookup_sep 129 | }; 130 | 131 | // z2(X) * (1+δ) * (ε+f(X)) * (ε*(1+δ) + t(X) + δt(Xω)) * lookup_sep^2 132 | let b = { 133 | let b_0 = epsilon + f_i; 134 | let b_1 = epsilon_one_plus_delta + table_i + delta * table_i_next; 135 | 136 | z2_i * one_plus_delta * b_0 * b_1 * lookup_sep_sq 137 | }; 138 | 139 | // − z2(Xω) * (ε*(1+δ) + h1(X) + δ*h2(X)) * (ε*(1+δ) + h2(X) + δ*h1(Xω)) 140 | // * lookup_sep^2 141 | let c = { 142 | let c_0 = epsilon_one_plus_delta + h1_i + delta * h2_i; 143 | let c_1 = epsilon_one_plus_delta + h2_i + delta * h1_i_next; 144 | 145 | -z2_i_next * c_0 * c_1 * lookup_sep_sq 146 | }; 147 | 148 | let d = { (z2_i - F::one()) * l1_i * lookup_sep_cu }; 149 | 150 | a + b + c + d 151 | } 152 | 153 | /// Compute linearization for lookup gates 154 | pub(crate) fn compute_linearisation( 155 | &self, 156 | l1_eval: F, 157 | a_eval: F, 158 | b_eval: F, 159 | c_eval: F, 160 | d_eval: F, 161 | f_eval: F, 162 | table_eval: F, 163 | table_next_eval: F, 164 | h1_next_eval: F, 165 | h2_eval: F, 166 | z2_next_eval: F, 167 | delta: F, 168 | epsilon: F, 169 | zeta: F, 170 | z2_poly: &DensePolynomial, 171 | h1_poly: &DensePolynomial, 172 | lookup_sep: F, 173 | ) -> DensePolynomial { 174 | let lookup_sep_sq = lookup_sep.square(); 175 | let lookup_sep_cu = lookup_sep * lookup_sep_sq; 176 | let one_plus_delta = delta + F::one(); 177 | let epsilon_one_plus_delta = epsilon * one_plus_delta; 178 | 179 | let a = { 180 | let compressed_tuple = lc(&[a_eval, b_eval, c_eval, d_eval], &zeta); 181 | &self.q_lookup.0 * ((compressed_tuple - f_eval) * lookup_sep) 182 | }; 183 | 184 | // z2(X) * (1 + δ) * (ε + f_bar) * (ε(1+δ) + t_bar + δ*tω_bar) * 185 | // lookup_sep^2 186 | let b = { 187 | let b_0 = epsilon + f_eval; 188 | let b_1 = 189 | epsilon_one_plus_delta + table_eval + delta * table_next_eval; 190 | let b_2 = l1_eval * lookup_sep_cu; 191 | 192 | z2_poly * (one_plus_delta * b_0 * b_1 * lookup_sep_sq + b_2) 193 | }; 194 | 195 | // h1(X) * (−z2ω_bar) * (ε(1+δ) + h2_bar + δh1ω_bar) * lookup_sep^2 196 | let c = { 197 | let c_0 = -z2_next_eval * lookup_sep_sq; 198 | let c_1 = epsilon_one_plus_delta + h2_eval + delta * h1_next_eval; 199 | 200 | h1_poly * (c_0 * c_1) 201 | }; 202 | a + b + c 203 | } 204 | } 205 | 206 | /// LookUp Verifier Key 207 | #[derive(CanonicalDeserialize, CanonicalSerialize, derivative::Derivative)] 208 | #[derivative( 209 | Clone, 210 | Copy(bound = "PC::Commitment: Copy"), 211 | Debug(bound = "PC::Commitment: core::fmt::Debug"), 212 | Eq(bound = "PC::Commitment: Eq"), 213 | PartialEq(bound = "PC::Commitment: PartialEq") 214 | )] 215 | pub struct VerifierKey 216 | where 217 | F: PrimeField, 218 | PC: PolynomialCommitment>, 219 | { 220 | /// Lookup Selector Commitment 221 | pub q_lookup: PC::Commitment, 222 | /// Commitment to first table column 223 | pub table_1: PC::Commitment, 224 | /// Commitment to second table column 225 | pub table_2: PC::Commitment, 226 | /// Commitment to third table column 227 | pub table_3: PC::Commitment, 228 | /// Commitment to fourth table column 229 | pub table_4: PC::Commitment, 230 | } 231 | 232 | impl VerifierKey 233 | where 234 | F: PrimeField, 235 | PC: PolynomialCommitment>, 236 | { 237 | /// Computes the linearisation commitments. 238 | pub fn compute_linearisation_commitment( 239 | &self, 240 | scalars: &mut Vec, 241 | points: &mut Vec, 242 | evaluations: &ProofEvaluations, 243 | (delta, epsilon, zeta): (F, F, F), 244 | lookup_sep: F, 245 | l1_eval: F, 246 | z2_comm: PC::Commitment, 247 | h1_comm: PC::Commitment, 248 | ) { 249 | let one_plus_delta = F::one() + delta; 250 | let epsilon_one_plus_delta = epsilon * one_plus_delta; 251 | let lookup_sep_sq = lookup_sep.square(); 252 | let lookup_sep_cu = lookup_sep_sq * lookup_sep; 253 | 254 | let a = { 255 | let compressed_eval = lc( 256 | &[ 257 | evaluations.wire_evals.a_eval, 258 | evaluations.wire_evals.b_eval, 259 | evaluations.wire_evals.c_eval, 260 | evaluations.wire_evals.d_eval, 261 | ], 262 | &zeta, 263 | ); 264 | 265 | let a_0 = compressed_eval - evaluations.lookup_evals.f_eval; 266 | a_0 * lookup_sep 267 | }; 268 | 269 | scalars.push(a); 270 | points.push(self.q_lookup.clone()); 271 | 272 | // (1 + δ) * (ε + f_bar) * (ε(1+δ) + t_bar + δ*tω_bar) * lookup_sep^2 273 | let b = { 274 | let b_0 = epsilon + evaluations.lookup_evals.f_eval; 275 | let b_1 = epsilon_one_plus_delta 276 | + evaluations.lookup_evals.table_eval 277 | + delta * evaluations.lookup_evals.table_next_eval; 278 | let b_2 = l1_eval * lookup_sep_cu; 279 | one_plus_delta * b_0 * b_1 * lookup_sep_sq + b_2 280 | }; 281 | 282 | scalars.push(b); 283 | points.push(z2_comm); 284 | 285 | let c = { 286 | let c_0 = -evaluations.lookup_evals.z2_next_eval * lookup_sep_sq; 287 | let c_1 = epsilon_one_plus_delta 288 | + evaluations.lookup_evals.h2_eval 289 | + delta * evaluations.lookup_evals.h1_next_eval; 290 | c_0 * c_1 291 | }; 292 | scalars.push(c); 293 | points.push(h1_comm); 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /plonk-core/src/proof_system/widget/range.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) DUSK NETWORK. All rights reserved. 6 | 7 | //! Range Gate 8 | 9 | use crate::proof_system::{ 10 | linearisation_poly::CustomEvaluations, CustomValues, GateConstraint, 11 | WitnessValues, 12 | }; 13 | use ark_ff::PrimeField; 14 | use core::marker::PhantomData; 15 | 16 | /// Values needed for the computation of the range gate constraint. 17 | pub struct RangeVals 18 | where 19 | F: PrimeField, 20 | { 21 | /// Fourth wire value in the next position 22 | pub d_next_val: F, 23 | } 24 | 25 | impl CustomValues for RangeVals 26 | where 27 | F: PrimeField, 28 | { 29 | fn from_evaluations(custom_vals: &CustomEvaluations) -> Self { 30 | let d_next_val = custom_vals.get("d_next_eval"); 31 | RangeVals { d_next_val } 32 | } 33 | } 34 | /// Range Gate 35 | #[derive(derivative::Derivative)] 36 | #[derivative(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] 37 | pub struct Range(PhantomData) 38 | where 39 | F: PrimeField; 40 | 41 | impl GateConstraint for Range 42 | where 43 | F: PrimeField, 44 | { 45 | type CustomVals = RangeVals; 46 | #[inline] 47 | fn constraints( 48 | separation_challenge: F, 49 | wit_vals: WitnessValues, 50 | custom_vals: Self::CustomVals, 51 | ) -> F { 52 | let four = F::from(4u64); 53 | let kappa = separation_challenge.square(); 54 | let kappa_sq = kappa.square(); 55 | let kappa_cu = kappa_sq * kappa; 56 | let b_1 = delta(wit_vals.c_val - four * wit_vals.d_val); 57 | let b_2 = delta(wit_vals.b_val - four * wit_vals.c_val) * kappa; 58 | let b_3 = delta(wit_vals.a_val - four * wit_vals.b_val) * kappa_sq; 59 | let b_4 = 60 | delta(custom_vals.d_next_val - four * wit_vals.a_val) * kappa_cu; 61 | (b_1 + b_2 + b_3 + b_4) * separation_challenge 62 | } 63 | } 64 | 65 | /// Computes `f(f-1)(f-2)(f-3)`. 66 | fn delta(f: F) -> F 67 | where 68 | F: PrimeField, 69 | { 70 | let f_1 = f - F::one(); 71 | let f_2 = f - F::from(2_u64); 72 | let f_3 = f - F::from(3_u64); 73 | f * f_1 * f_2 * f_3 74 | } 75 | -------------------------------------------------------------------------------- /plonk-core/src/test.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) ZK-GARAGE. All rights reserved. 6 | 7 | //! Test Suite 8 | 9 | /// Defines a set of tests on a pairing engine / curve combination. 10 | /// 11 | /// The set of tests is split in two. The first set between `[]` is for regular 12 | /// tests that should not panic. The second set is for tests that should panic. 13 | 14 | #[macro_export] 15 | macro_rules! batch_test_field { 16 | ( [$($test_set:ident),*], [$($test_panic_set:ident),*] => ($engine:ty) ) => { 17 | paste::item! { 18 | $( 19 | #[test] 20 | #[allow(non_snake_case)] 21 | fn [< $test_set _on_ $engine>]() { 22 | $test_set::<<$engine as ark_ec::PairingEngine>::Fr>() 23 | } 24 | )* 25 | $( 26 | #[test] 27 | #[should_panic] 28 | #[allow(non_snake_case)] 29 | fn [< $test_panic_set _on_ $engine>]() { 30 | $test_panic_set::<<$engine as ark_ec::PairingEngine>::Fr>() 31 | } 32 | )* 33 | } 34 | } 35 | } 36 | 37 | #[macro_export] 38 | macro_rules! batch_test_field_params { 39 | ( [$($test_set:ident),*], [$($test_panic_set:ident),*] => ($engine:ty, $params:ty) ) => { 40 | paste::item! { 41 | $( 42 | #[test] 43 | #[allow(non_snake_case)] 44 | fn [< $test_set _on_ $engine>]() { 45 | $test_set::<<$engine as ark_ec::PairingEngine>::Fr, $params>() 46 | } 47 | )* 48 | $( 49 | #[test] 50 | #[should_panic] 51 | #[allow(non_snake_case)] 52 | fn [< $test_panic_set _on_ $engine>]() { 53 | $test_panic_set::<<$engine as ark_ec::PairingEngine>::Fr, $params>() 54 | } 55 | )* 56 | } 57 | } 58 | } 59 | 60 | #[macro_export] 61 | macro_rules! batch_test_kzg { 62 | ( [$($test_set:ident),*], [$($test_panic_set:ident),*] => ($engine:ty, $params:ty) ) => { 63 | paste::item! { 64 | $( 65 | #[test] 66 | #[allow(non_snake_case)] 67 | fn [< $test_set _on_ $engine>]() { 68 | $test_set::<<$engine as ark_ec::PairingEngine>::Fr, $params, $crate::commitment::KZG10<$engine>>() 69 | } 70 | )* 71 | $( 72 | #[test] 73 | #[should_panic] 74 | #[allow(non_snake_case)] 75 | fn [< $test_panic_set _on_ $engine>]() { 76 | $test_panic_set::<<$engine as ark_ec::PairingEngine>::Fr, $params, $crate::commitment::KZG10<$engine>>() 77 | } 78 | )* 79 | } 80 | } 81 | } 82 | 83 | #[macro_export] 84 | macro_rules! batch_test { 85 | ( [$($test_set:ident),*], [$($test_panic_set:ident),*] => ($engine:ty, $params:ty) ) => { 86 | paste::item! { 87 | $( 88 | #[test] 89 | #[allow(non_snake_case)] 90 | fn [< $test_set _on_ $engine _kzg>]() { 91 | $test_set::<<$engine as ark_ec::PairingEngine>::Fr, $params, $crate::commitment::KZG10<$engine>>() 92 | } 93 | #[test] 94 | #[allow(non_snake_case)] 95 | fn [< $test_set _on_ $engine _ipa>]() { 96 | $test_set::<<$engine as ark_ec::PairingEngine>::Fr, $params, ark_poly_commit::ipa_pc::InnerProductArgPC<<$engine as ark_ec::PairingEngine>::G1Affine, blake2::Blake2s, ark_poly::univariate::DensePolynomial<<$engine as ark_ec::PairingEngine>::Fr>>>() 97 | } 98 | )* 99 | $( 100 | #[test] 101 | #[should_panic] 102 | #[allow(non_snake_case)] 103 | fn [< $test_panic_set _on_ $engine _kzg>]() { 104 | $test_panic_set::<<$engine as ark_ec::PairingEngine>::Fr, $params, $crate::commitment::KZG10<$engine>>() 105 | } 106 | #[test] 107 | #[should_panic] 108 | #[allow(non_snake_case)] 109 | fn [< $test_panic_set _on_ $engine _ipa>]() { 110 | $test_panic_set::<<$engine as ark_ec::PairingEngine>::Fr, $params, ark_poly_commit::ipa_pc::InnerProductArgPC<<$engine as ark_ec::PairingEngine>::G1Affine, blake2::Blake2s, ark_poly::univariate::DensePolynomial<<$engine as ark_ec::PairingEngine>::Fr>>>() 111 | } 112 | )* 113 | } 114 | } 115 | } 116 | 117 | #[macro_export] 118 | macro_rules! batch_field_test { 119 | ( [$($test_set:ident),*], [$($test_panic_set:ident),*] => $field:ty ) => { 120 | paste::item! { 121 | $( 122 | #[test] 123 | #[allow(non_snake_case)] 124 | fn [< $test_set _on_ $field>]() { 125 | $test_set::<$field>() 126 | } 127 | )* 128 | $( 129 | #[test] 130 | #[should_panic] 131 | #[allow(non_snake_case)] 132 | fn [< $test_panic_set _on_ $field>]() { 133 | $test_panic_set::<$field>() 134 | } 135 | )* 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /plonk-core/src/transcript.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) DUSK NETWORK. All rights reserved. 6 | 7 | //! This is an extension over the [Merlin Transcript](Transcript) which adds a 8 | //! few extra functionalities. 9 | 10 | use ark_ff::PrimeField; 11 | use ark_serialize::CanonicalSerialize; 12 | use merlin::Transcript; 13 | 14 | /// Transcript adds an abstraction over the Merlin transcript 15 | /// For convenience 16 | pub(crate) trait TranscriptProtocol { 17 | /// Append an `item` with the given `label`. 18 | fn append(&mut self, label: &'static [u8], item: &impl CanonicalSerialize); 19 | 20 | /// Compute a `label`ed challenge variable. 21 | fn challenge_scalar(&mut self, label: &'static [u8]) -> F; 22 | 23 | /// Append domain separator for the circuit size. 24 | fn circuit_domain_sep(&mut self, n: u64); 25 | } 26 | 27 | impl TranscriptProtocol for Transcript { 28 | fn append(&mut self, label: &'static [u8], item: &impl CanonicalSerialize) { 29 | let mut bytes = Vec::new(); 30 | item.serialize(&mut bytes).unwrap(); 31 | self.append_message(label, &bytes) 32 | } 33 | 34 | fn challenge_scalar(&mut self, label: &'static [u8]) -> F 35 | where 36 | F: PrimeField, 37 | { 38 | // XXX: review this: assure from_random_bytes returnes a valid Field 39 | // element 40 | let size = F::size_in_bits() / 8; 41 | let mut buf = vec![0u8; size]; 42 | self.challenge_bytes(label, &mut buf); 43 | F::from_random_bytes(&buf).unwrap() 44 | } 45 | 46 | fn circuit_domain_sep(&mut self, n: u64) { 47 | self.append_message(b"dom-sep", b"circuit_size"); 48 | self.append_u64(b"n", n); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /plonk-core/src/util.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) DUSK NETWORK. All rights reserved. 6 | 7 | use ark_ec::{ModelParameters, TEModelParameters}; 8 | use ark_ff::{BigInteger, FftField, Field, FpParameters, PrimeField}; 9 | use ark_poly::{EvaluationDomain, GeneralEvaluationDomain}; 10 | use core::ops::Mul; 11 | use std::ops::Add; 12 | 13 | /// Returns an iterator over increasing powers of the given `scalar` starting 14 | /// at `0`. 15 | #[inline] 16 | pub fn powers_of(scalar: F) -> impl Iterator 17 | where 18 | F: Field, 19 | { 20 | core::iter::successors(Some(F::one()), move |p| Some(*p * scalar)) 21 | } 22 | 23 | /// Evaluation Domain Extension Trait 24 | pub trait EvaluationDomainExt: EvaluationDomain 25 | where 26 | F: FftField, 27 | { 28 | /// Returns the value of `log_2(self.size)`. 29 | fn log_size_of_group(&self) -> u32; 30 | 31 | /// Returns the inverse of the size in the field. 32 | fn size_inv(&self) -> F; 33 | 34 | /// Returns a fixed generator of the subgroup. 35 | fn group_gen(&self) -> F; 36 | 37 | /// Returns the inverse of the fixed generator of the subgroup. 38 | fn group_gen_inv(&self) -> F; 39 | 40 | /// Returns a fixed multiplicative generator of the finite field. 41 | fn generator_inv(&self) -> F; 42 | } 43 | 44 | impl EvaluationDomainExt for GeneralEvaluationDomain 45 | where 46 | F: FftField, 47 | { 48 | #[inline] 49 | fn log_size_of_group(&self) -> u32 { 50 | match self { 51 | GeneralEvaluationDomain::Radix2(domain) => domain.log_size_of_group, 52 | GeneralEvaluationDomain::MixedRadix(domain) => { 53 | domain.log_size_of_group 54 | } 55 | } 56 | } 57 | 58 | #[inline] 59 | fn size_inv(&self) -> F { 60 | match self { 61 | GeneralEvaluationDomain::Radix2(domain) => domain.size_inv, 62 | GeneralEvaluationDomain::MixedRadix(domain) => domain.size_inv, 63 | } 64 | } 65 | 66 | #[inline] 67 | fn group_gen(&self) -> F { 68 | match self { 69 | GeneralEvaluationDomain::Radix2(domain) => domain.group_gen, 70 | GeneralEvaluationDomain::MixedRadix(domain) => domain.group_gen, 71 | } 72 | } 73 | 74 | #[inline] 75 | fn group_gen_inv(&self) -> F { 76 | match self { 77 | GeneralEvaluationDomain::Radix2(domain) => domain.group_gen_inv, 78 | GeneralEvaluationDomain::MixedRadix(domain) => domain.group_gen_inv, 79 | } 80 | } 81 | 82 | #[inline] 83 | fn generator_inv(&self) -> F { 84 | match self { 85 | GeneralEvaluationDomain::Radix2(domain) => domain.generator_inv, 86 | GeneralEvaluationDomain::MixedRadix(domain) => domain.generator_inv, 87 | } 88 | } 89 | } 90 | 91 | /// Get a pairing friendly curve scalar `E::Fr` from a scalar of the embedded 92 | /// curve. Panics if the embedded scalar is greater than the modulus of the 93 | /// pairing firendly curve scalar field 94 | #[allow(dead_code)] 95 | pub fn from_embedded_curve_scalar( 96 | embedded_scalar:

::ScalarField, 97 | ) -> F 98 | where 99 | F: PrimeField, 100 | P: TEModelParameters, 101 | { 102 | let scalar_repr = embedded_scalar.into_repr(); 103 | let modulus = <::Params as FpParameters>::MODULUS; 104 | if modulus.num_bits() >= scalar_repr.num_bits() { 105 | let s = <::BigInt as BigInteger>::from_bits_le( 106 | &scalar_repr.to_bits_le(), 107 | ); 108 | assert!(s < modulus, 109 | "The embedded scalar exceeds the capacity representation of the outter curve scalar"); 110 | } else { 111 | let m = <::BigInt as BigInteger>::from_bits_le( 112 | &modulus.to_bits_le(), 113 | ); 114 | assert!(scalar_repr < m, 115 | "The embedded scalar exceeds the capacity representation of the outter curve scalar"); 116 | } 117 | F::from_le_bytes_mod_order(&scalar_repr.to_bytes_le()) 118 | } 119 | 120 | /// Get a embedded curve scalar `P::ScalarField` from a scalar of the pariring 121 | /// friendly curve. Panics if the pairing frindly curve scalar is greater than 122 | /// the modulus of the embedded curve scalar field 123 | #[allow(dead_code)] 124 | pub(crate) fn to_embedded_curve_scalar(pfc_scalar: F) -> P::ScalarField 125 | where 126 | F: PrimeField, 127 | P: TEModelParameters, 128 | { 129 | let scalar_repr = pfc_scalar.into_repr(); 130 | let modulus = 131 | <::Params as FpParameters>::MODULUS; 132 | if modulus.num_bits() >= scalar_repr.num_bits() { 133 | let s = <::BigInt as BigInteger>::from_bits_le( 134 | &scalar_repr.to_bits_le(), 135 | ); 136 | assert!(s < modulus, 137 | "The embedded scalar exceeds the capacity representation of the outter curve scalar"); 138 | } else { 139 | let m = <::BigInt as BigInteger>::from_bits_le( 140 | &modulus.to_bits_le(), 141 | ); 142 | assert!(scalar_repr < m, 143 | "The embedded scalar exceeds the capacity representation of the outter curve scalar"); 144 | } 145 | P::ScalarField::from_le_bytes_mod_order(&scalar_repr.to_bytes_le()) 146 | } 147 | 148 | /// Linear combination of a series of values 149 | /// 150 | /// For values [v_0, v_1,... v_k] returns: 151 | /// v_0 + challenge * v_1 + ... + challenge^k * v_k 152 | pub fn lc(values: &[T], challenge: &F) -> T 153 | where 154 | T: Mul + Add + Clone, 155 | F: Field, 156 | { 157 | // Ensure valid challenge 158 | assert_ne!(*challenge, F::zero()); 159 | assert_ne!(*challenge, F::one()); 160 | 161 | let kth_val = match values.last() { 162 | Some(val) => val.clone(), 163 | _ => panic!("At least one value must be provided to compute a linear combination") 164 | }; 165 | 166 | values 167 | .iter() 168 | .rev() 169 | .skip(1) 170 | .fold(kth_val, |acc, val| acc * *challenge + val.clone()) 171 | } 172 | 173 | /// Macro to quickly label polynomials 174 | #[macro_export] 175 | macro_rules! label_polynomial { 176 | ($poly:expr) => { 177 | ark_poly_commit::LabeledPolynomial::new( 178 | stringify!($poly).to_owned(), 179 | $poly.clone(), 180 | None, 181 | None, 182 | ) 183 | }; 184 | } 185 | 186 | /// Macro to quickly label polynomial commitments 187 | #[macro_export] 188 | macro_rules! label_commitment { 189 | ($comm:expr) => { 190 | ark_poly_commit::LabeledCommitment::new( 191 | stringify!($comm).to_owned(), 192 | $comm.clone(), 193 | None, 194 | ) 195 | }; 196 | } 197 | 198 | /// Macro to quickly label evaluations 199 | #[macro_export] 200 | macro_rules! label_eval { 201 | ($eval:expr) => { 202 | (stringify!($eval).to_owned(), $eval) 203 | }; 204 | } 205 | 206 | /// Macro to get appropirate label 207 | #[macro_export] 208 | macro_rules! get_label { 209 | ($eval:expr) => { 210 | stringify!($comm).to_owned() 211 | }; 212 | } 213 | 214 | #[cfg(test)] 215 | mod test { 216 | use crate::batch_field_test; 217 | 218 | use super::*; 219 | use ark_bls12_377::Fr as Bls12_377_scalar_field; 220 | use ark_bls12_381::Fr as Bls12_381_scalar_field; 221 | use ark_ff::Field; 222 | use rand_core::OsRng; 223 | 224 | fn test_correct_lc() { 225 | let n_iter = 10; 226 | for _ in 0..n_iter { 227 | let a = F::rand(&mut OsRng); 228 | let b = F::rand(&mut OsRng); 229 | let c = F::rand(&mut OsRng); 230 | let d = F::rand(&mut OsRng); 231 | let e = F::rand(&mut OsRng); 232 | let challenge = F::rand(&mut OsRng); 233 | let expected = a 234 | + b * challenge 235 | + c * challenge * challenge 236 | + d * challenge * challenge * challenge 237 | + e * challenge * challenge * challenge * challenge; 238 | 239 | let result = lc(&[a, b, c, d, e], &challenge); 240 | assert_eq!(result, expected) 241 | } 242 | } 243 | 244 | fn test_incorrect_lc() { 245 | let n_iter = 10; 246 | for _ in 0..n_iter { 247 | let a = F::rand(&mut OsRng); 248 | let b = F::rand(&mut OsRng); 249 | let c = F::rand(&mut OsRng); 250 | let d = F::rand(&mut OsRng); 251 | let e = F::rand(&mut OsRng); 252 | let challenge = F::rand(&mut OsRng); 253 | let expected = F::one() 254 | + a 255 | + b * challenge 256 | + c * challenge * challenge 257 | + d * challenge * challenge * challenge 258 | + e * challenge * challenge * challenge * challenge; 259 | 260 | let result = lc(&[a, b, c, d, e], &challenge); 261 | assert_eq!(result, expected) 262 | } 263 | } 264 | batch_field_test!( 265 | [ 266 | test_correct_lc 267 | ], 268 | [ 269 | test_incorrect_lc 270 | ] => Bls12_381_scalar_field 271 | ); 272 | batch_field_test!( 273 | [ 274 | test_correct_lc 275 | ], 276 | [ 277 | test_incorrect_lc 278 | ] => Bls12_377_scalar_field 279 | ); 280 | } 281 | -------------------------------------------------------------------------------- /plonk-hashing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "plonk-hashing" 3 | version = "0.8.2" 4 | authors = ["ZK-GARAGE Authors"] 5 | readme = "README.md" 6 | repository = "https://github.com/zk-garage/plonk" 7 | keywords = ["cryptography", "plonk", "zk-snarks", "zero-knowledge", "crypto"] 8 | categories = ["algorithms", "cryptography", "science"] 9 | description = "A pure-Rust implementation of the PLONK ZK-Proof algorithm." 10 | license = "MIT OR Apache-2.0" 11 | edition = "2021" 12 | 13 | [package.metadata.docs.rs] 14 | # To build locally: 15 | # RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --open 16 | all-features = true 17 | rustdoc-args = ["--cfg", "doc_cfg"] 18 | 19 | [features] 20 | # Enable Standard Library 21 | std = [] 22 | 23 | [dependencies] 24 | plonk-core = { path = "../plonk-core", features = [ "trace" ] } 25 | ark-ec = { version = "0.3", features = ["std"] } 26 | ark-ff = { version = "0.3", features = ["std"] } 27 | ark-serialize = { version = "0.3", features = ["derive"] } 28 | ark-poly = "0.3" 29 | ark-poly-commit = "0.3" 30 | ark-crypto-primitives = { version = "^0.3.0", features = ["r1cs"], default-features = false } 31 | ark-std = { version = "^0.3.0", default-features = false } 32 | itertools = { version = "0.10.1", default-features = false } 33 | num-traits = "0.2.14" 34 | derivative = { version = "2.2.0", default-features = false, features = ["use_core"] } 35 | hashbrown = { version = "0.11.2", default-features = false, features = ["ahash"] } 36 | ark-relations = "0.3.0" 37 | ark-r1cs-std = "0.3.1" 38 | thiserror = "1.0.30" 39 | 40 | [dev-dependencies] 41 | ark-bls12-381 = { version = "0.3", features = ["curve"] } 42 | ark-ed-on-bls12-381 = { version = "0.3" } 43 | neptune = { version = "7.2.0" } -------------------------------------------------------------------------------- /plonk-hashing/README.md: -------------------------------------------------------------------------------- 1 | # PLONK Hashing Library -------------------------------------------------------------------------------- /plonk-hashing/src/lib.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) ZK-GARAGE. All rights reserved. 6 | 7 | //! PLONK Hashing Library 8 | 9 | // #![cfg_attr(not(any(feature = "std", test)), no_std)] 10 | #![cfg_attr(doc_cfg, feature(doc_cfg))] 11 | #![forbid(rustdoc::broken_intra_doc_links)] 12 | // #![forbid(missing_docs)] 13 | 14 | pub extern crate alloc; 15 | 16 | pub mod poseidon; 17 | -------------------------------------------------------------------------------- /plonk-hashing/src/poseidon/constants.rs: -------------------------------------------------------------------------------- 1 | use crate::poseidon::{ 2 | matrix::Matrix, 3 | mds::{factor_to_sparse_matrixes, MdsMatrices, SparseMatrix}, 4 | preprocessing::compress_round_constants, 5 | round_constant::generate_constants, 6 | round_numbers::calc_round_numbers, 7 | }; 8 | use ark_ff::PrimeField; 9 | 10 | #[derive(Clone, Debug, PartialEq)] 11 | pub struct PoseidonConstants { 12 | pub mds_matrices: MdsMatrices, 13 | pub round_constants: Vec, 14 | pub compressed_round_constants: Vec, 15 | pub pre_sparse_matrix: Matrix, 16 | pub sparse_matrixes: Vec>, 17 | pub domain_tag: F, 18 | pub full_rounds: usize, 19 | pub half_full_rounds: usize, 20 | pub partial_rounds: usize, 21 | } 22 | 23 | impl PoseidonConstants { 24 | /// Generate all constants needed for poseidon hash of specified 25 | /// width. Note that WIDTH = ARITY + 1 26 | pub fn generate() -> Self { 27 | let arity = WIDTH - 1; 28 | let mds_matrices = MdsMatrices::new(WIDTH); 29 | let (num_full_rounds, num_partial_rounds) = 30 | calc_round_numbers(WIDTH, true); 31 | 32 | debug_assert_eq!(num_full_rounds % 2, 0); 33 | let num_half_full_rounds = num_full_rounds / 2; 34 | let round_constants = generate_constants( 35 | 1, // prime field 36 | 1, // sbox 37 | F::size_in_bits() as u16, 38 | WIDTH.try_into().expect("WIDTH is too large"), 39 | num_full_rounds 40 | .try_into() 41 | .expect("num_full_rounds is too large"), 42 | num_partial_rounds 43 | .try_into() 44 | .expect("num_partial_rounds is too large"), 45 | ); 46 | let domain_tag = F::from(((1 << arity) - 1) as u64); 47 | 48 | let compressed_round_constants = compress_round_constants( 49 | WIDTH, 50 | num_full_rounds, 51 | num_partial_rounds, 52 | &round_constants, 53 | &mds_matrices, 54 | ); 55 | 56 | let (pre_sparse_matrix, sparse_matrixes) = factor_to_sparse_matrixes( 57 | mds_matrices.m.clone(), 58 | num_partial_rounds, 59 | ); 60 | 61 | assert!( 62 | WIDTH * (num_full_rounds + num_partial_rounds) 63 | <= round_constants.len(), 64 | "Not enough round constants" 65 | ); 66 | 67 | PoseidonConstants { 68 | mds_matrices, 69 | round_constants, 70 | domain_tag, 71 | full_rounds: num_full_rounds, 72 | half_full_rounds: num_half_full_rounds, 73 | partial_rounds: num_partial_rounds, 74 | compressed_round_constants, 75 | pre_sparse_matrix, 76 | sparse_matrixes, 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /plonk-hashing/src/poseidon/mds.rs: -------------------------------------------------------------------------------- 1 | // adapted from https://github.com/filecoin-project/neptune/blob/master/src/mds.rs 2 | use crate::poseidon::matrix::Matrix; 3 | use ark_ff::vec::*; 4 | use ark_ff::PrimeField; 5 | 6 | #[derive(Clone, Debug, PartialEq)] 7 | pub struct MdsMatrices { 8 | pub m: Matrix, 9 | pub m_inv: Matrix, 10 | pub m_hat: Matrix, 11 | pub m_hat_inv: Matrix, 12 | pub m_prime: Matrix, 13 | pub m_double_prime: Matrix, 14 | } 15 | 16 | impl MdsMatrices { 17 | /// Derive MDS matrix of size `dim*dim` and relevant things 18 | pub fn new(dim: usize) -> Self { 19 | let m = Self::generate_mds(dim); 20 | Self::derive_mds_matrices(m) 21 | } 22 | 23 | /// Given an MDS matrix `m`, compute all its associated matrices. 24 | pub(crate) fn derive_mds_matrices(m: Matrix) -> Self { 25 | let m_inv = m.invert().expect("Derived MDS matrix is not invertible"); 26 | let m_hat = m.minor(0, 0); 27 | let m_hat_inv = 28 | m_hat.invert().expect("Derived MDS matrix is not correct"); 29 | let m_prime = Self::make_prime(&m); 30 | let m_double_prime = Self::make_double_prime(&m, &m_hat_inv); 31 | MdsMatrices { 32 | m, 33 | m_inv, 34 | m_hat, 35 | m_hat_inv, 36 | m_prime, 37 | m_double_prime, 38 | } 39 | } 40 | 41 | fn generate_mds(t: usize) -> Matrix { 42 | let xs: Vec = (0..t as u64).map(F::from).collect(); 43 | let ys: Vec = (t as u64..2 * t as u64).map(F::from).collect(); 44 | 45 | let matrix = xs 46 | .iter() 47 | .map(|xs_item| { 48 | ys.iter() 49 | .map(|ys_item| { 50 | // Generate the entry at (i,j) 51 | let mut tmp = *xs_item; 52 | tmp.add_assign(ys_item); 53 | tmp.inverse().unwrap() 54 | }) 55 | .collect() 56 | }) 57 | .collect::>(); 58 | 59 | debug_assert!(matrix.is_invertible()); 60 | debug_assert_eq!(matrix, matrix.transpose()); 61 | matrix 62 | } 63 | 64 | /// Returns a matrix associated to `m` in the optimization of 65 | /// MDS matrices. 66 | fn make_prime(m: &Matrix) -> Matrix { 67 | m.iter_rows() 68 | .enumerate() 69 | .map(|(i, row)| match i { 70 | 0 => { 71 | let mut new_row = vec![F::zero(); row.len()]; 72 | new_row[0] = F::one(); 73 | new_row 74 | } 75 | _ => { 76 | let mut new_row = vec![F::zero(); row.len()]; 77 | new_row[1..].copy_from_slice(&row[1..]); 78 | new_row 79 | } 80 | }) 81 | .collect() 82 | } 83 | 84 | /// Returns a matrix associated to `m` in the optimization of 85 | /// MDS matrices. 86 | fn make_double_prime(m: &Matrix, m_hat_inv: &Matrix) -> Matrix { 87 | let (v, w) = Self::make_v_w(m); 88 | let w_hat = m_hat_inv.right_apply(&w); 89 | 90 | m.iter_rows() 91 | .enumerate() 92 | .map(|(i, row)| match i { 93 | 0 => { 94 | let mut new_row = Vec::with_capacity(row.len()); 95 | new_row.push(row[0]); 96 | new_row.extend(&v); 97 | new_row 98 | } 99 | _ => { 100 | let mut new_row = vec![F::zero(); row.len()]; 101 | new_row[0] = w_hat[i - 1]; 102 | new_row[i] = F::one(); 103 | new_row 104 | } 105 | }) 106 | .collect() 107 | } 108 | 109 | /// Returns two vectors associated to `m` in the optimization of 110 | /// MDS matrices. 111 | fn make_v_w(m: &Matrix) -> (Vec, Vec) { 112 | let v = m[0][1..].to_vec(); 113 | let w = m.iter_rows().skip(1).map(|column| column[0]).collect(); 114 | (v, w) 115 | } 116 | } 117 | 118 | /// A `SparseMatrix` is specifically one of the form of M''. 119 | /// This means its first row and column are each dense, and the interior matrix 120 | /// (minor to the element in both the row and column) is the identity. 121 | /// We will pluralize this compact structure `sparse_matrixes` to distinguish 122 | /// from `sparse_matrices` from which they are created. 123 | #[derive(Debug, Clone, PartialEq)] 124 | pub struct SparseMatrix { 125 | /// `w_hat` is the first column of the M'' matrix. It will be directly 126 | /// multiplied (scalar product) with a row of state elements. 127 | pub w_hat: Vec, 128 | /// `v_rest` contains all but the first (already included in `w_hat`). 129 | pub v_rest: Vec, 130 | } 131 | 132 | impl SparseMatrix { 133 | pub fn new(m_double_prime: &Matrix) -> Self { 134 | assert!(m_double_prime.is_sparse()); 135 | 136 | let w_hat = m_double_prime.iter_rows().map(|r| r[0]).collect(); 137 | let v_rest = m_double_prime[0][1..].to_vec(); 138 | Self { w_hat, v_rest } 139 | } 140 | 141 | pub fn size(&self) -> usize { 142 | self.w_hat.len() 143 | } 144 | 145 | pub fn to_matrix(&self) -> Matrix { 146 | let mut m = Matrix::identity(self.size()); 147 | for (j, elt) in self.w_hat.iter().enumerate() { 148 | m[j][0] = *elt; 149 | } 150 | for (i, elt) in self.v_rest.iter().enumerate() { 151 | m[0][i + 1] = *elt; 152 | } 153 | m 154 | } 155 | } 156 | 157 | // TODO: naming is from https://github.com/filecoin-project/neptune/blob/master/src/mds.rs 158 | // TODO: which is little difficult to follow... We need to change it 159 | 160 | pub fn factor_to_sparse_matrixes( 161 | base_matrix: Matrix, 162 | n: usize, 163 | ) -> (Matrix, Vec>) { 164 | let (pre_sparse, mut sparse_matrices) = 165 | (0..n).fold((base_matrix.clone(), Vec::new()), |(curr, mut acc), _| { 166 | let derived = MdsMatrices::derive_mds_matrices(curr); 167 | acc.push(derived.m_double_prime); 168 | let new = base_matrix.matmul(&derived.m_prime).unwrap(); 169 | (new, acc) 170 | }); 171 | sparse_matrices.reverse(); 172 | let sparse_matrixes = sparse_matrices 173 | .iter() 174 | .map(|m| SparseMatrix::::new(m)) 175 | .collect::>(); 176 | 177 | (pre_sparse, sparse_matrixes) 178 | } 179 | 180 | #[cfg(test)] 181 | mod tests { 182 | use crate::poseidon::mds::MdsMatrices; 183 | use ark_bls12_381::Fr; 184 | use ark_std::{test_rng, UniformRand}; 185 | 186 | #[test] 187 | fn test_mds_matrices_creation() { 188 | for i in 2..5 { 189 | test_mds_matrices_creation_aux(i); 190 | } 191 | } 192 | 193 | fn test_mds_matrices_creation_aux(width: usize) { 194 | let MdsMatrices { 195 | m, 196 | m_inv, 197 | m_hat, 198 | m_hat_inv: _, 199 | m_prime, 200 | m_double_prime, 201 | } = MdsMatrices::::new(width); 202 | 203 | for i in 0..m_hat.num_rows() { 204 | for j in 0..m_hat.num_columns() { 205 | assert_eq!( 206 | m[i + 1][j + 1], 207 | m_hat[i][j], 208 | "MDS minor has wrong value." 209 | ); 210 | } 211 | } 212 | 213 | // M^-1 x M = I 214 | assert!(m_inv.matmul(&m).unwrap().is_identity()); 215 | 216 | // M' x M'' = M 217 | assert_eq!(m, m_prime.matmul(&m_double_prime).unwrap()); 218 | } 219 | 220 | #[test] 221 | fn test_swapping() { 222 | test_swapping_aux(3) 223 | } 224 | 225 | fn test_swapping_aux(width: usize) { 226 | let mut rng = test_rng(); 227 | let mds = MdsMatrices::::new(width); 228 | 229 | let base = (0..width).map(|_| Fr::rand(&mut rng)).collect::>(); 230 | let x = { 231 | let mut x = base.clone(); 232 | x[0] = Fr::rand(&mut rng); 233 | x 234 | }; 235 | let y = { 236 | let mut y = base; 237 | y[0] = Fr::rand(&mut rng); 238 | y 239 | }; 240 | 241 | let qx = mds.m_prime.right_apply(&x); 242 | let qy = mds.m_prime.right_apply(&y); 243 | assert_eq!(qx[0], x[0]); 244 | assert_eq!(qy[0], y[0]); 245 | assert_eq!(qx[1..], qy[1..]); 246 | 247 | let mx = mds.m.left_apply(&x); 248 | let m1_m2_x = 249 | mds.m_prime.left_apply(&mds.m_double_prime.left_apply(&x)); 250 | assert_eq!(mx, m1_m2_x); 251 | 252 | let xm = mds.m.right_apply(&x); 253 | let x_m1_m2 = 254 | mds.m_double_prime.right_apply(&mds.m_prime.right_apply(&x)); 255 | assert_eq!(xm, x_m1_m2); 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /plonk-hashing/src/poseidon/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod constants; 2 | pub mod matrix; 3 | pub mod mds; 4 | pub mod poseidon_ref; 5 | pub mod preprocessing; 6 | pub mod round_constant; 7 | pub mod round_numbers; 8 | pub mod zprize_constraints; 9 | 10 | use thiserror::Error; 11 | 12 | #[derive(Error, Debug)] 13 | pub enum PoseidonError { 14 | #[error("Buffer is full")] 15 | FullBuffer, 16 | } 17 | -------------------------------------------------------------------------------- /plonk-hashing/src/poseidon/preprocessing.rs: -------------------------------------------------------------------------------- 1 | //! acknowledgement: adapted from FileCoin Project: https://github.com/filecoin-project/neptune/blob/master/src/preprocessing.rs 2 | 3 | use super::{matrix::vec_add, mds::MdsMatrices}; 4 | use ark_ff::vec::Vec; 5 | use ark_ff::PrimeField; 6 | 7 | // - Compress constants by pushing them back through linear layers and through 8 | // the identity components of partial layers. 9 | // - As a result, constants need only be added after each S-box. 10 | pub(crate) fn compress_round_constants( 11 | width: usize, 12 | full_rounds: usize, 13 | partial_rounds: usize, 14 | round_constants: &[F], 15 | mds_matrices: &MdsMatrices, 16 | ) -> Vec { 17 | let inverse_matrix = &mds_matrices.m_inv; 18 | 19 | let mut res: Vec = Vec::new(); 20 | 21 | let round_keys = |r: usize| &round_constants[r * width..(r + 1) * width]; 22 | 23 | // This is half full-rounds. 24 | let half_full_rounds = full_rounds / 2; 25 | 26 | // First round constants are unchanged. 27 | res.extend(round_keys(0)); 28 | 29 | // Post S-box adds for the first set of full rounds should be 'inverted' 30 | // from next round. The final round is skipped when fully preprocessing 31 | // because that value must be obtained from the result of preprocessing 32 | // the partial rounds. 33 | let end = half_full_rounds - 1; 34 | for i in 0..end { 35 | let next_round = round_keys(i + 1); 36 | let inverted = inverse_matrix.right_apply(next_round); 37 | res.extend(inverted); 38 | } 39 | 40 | // The plan: 41 | // - Work backwards from last row in this group 42 | // - Invert the row. 43 | // - Save first constant (corresponding to the one S-box performed). 44 | // - Add inverted result to previous row. 45 | // - Repeat until all partial round key rows have been consumed. 46 | // - Extend the preprocessed result by the final resultant row. 47 | // - Move the accumulated list of single round keys to the preprocesed 48 | // result. 49 | // - (Last produced should be first applied, so either pop until empty, or 50 | // reverse and extend, etc.) 51 | 52 | // 'partial_keys' will accumulated the single post-S-box constant for each 53 | // partial-round, in reverse order. 54 | let mut partial_keys: Vec = Vec::new(); 55 | 56 | let final_round = half_full_rounds + partial_rounds; 57 | let final_round_key = round_keys(final_round).to_vec(); 58 | 59 | // 'round_acc' holds the accumulated result of inverting and adding 60 | // subsequent round constants (in reverse). 61 | let round_acc = (0..partial_rounds) 62 | .map(|i| round_keys(final_round - i - 1)) 63 | .fold(final_round_key, |acc, previous_round_keys| { 64 | let mut inverted = inverse_matrix.right_apply(&acc); 65 | 66 | partial_keys.push(inverted[0]); 67 | inverted[0] = F::zero(); 68 | 69 | vec_add(previous_round_keys, &inverted) 70 | }); 71 | 72 | res.extend(inverse_matrix.right_apply(&round_acc)); 73 | 74 | while let Some(x) = partial_keys.pop() { 75 | res.push(x) 76 | } 77 | 78 | // Post S-box adds for the first set of full rounds should be 'inverted' 79 | // from next round. 80 | for i in 1..(half_full_rounds) { 81 | let start = half_full_rounds + partial_rounds; 82 | let next_round = round_keys(i + start); 83 | let inverted = inverse_matrix.right_apply(next_round); 84 | res.extend(inverted); 85 | } 86 | 87 | res 88 | } 89 | -------------------------------------------------------------------------------- /plonk-hashing/src/poseidon/round_constant.rs: -------------------------------------------------------------------------------- 1 | use alloc::collections::vec_deque::VecDeque; 2 | use ark_ff::{BigInteger, PrimeField}; 3 | /// From the paper 4 | /// THe parameter describes the initial state of constant generation (80-bits) 5 | /// * `field`: description of field. b0, b1 6 | /// * `sbox`: description of s-box. b2..=b5 7 | /// * `field_size`: binary representation of field size. b6..=b17 8 | /// * `t`: binary representation of t. b18..=b29 9 | /// * `rf`: binary representation of rf. b30..=b39 10 | /// * `rp`: binary representation of rp. b40..=b49 11 | /// * `ones`: set to 1. b50..=b79 12 | pub fn generate_constants( 13 | field: u8, 14 | sbox: u8, 15 | field_size: u16, 16 | t: u16, 17 | r_f: u16, 18 | r_p: u16, 19 | ) -> Vec { 20 | let n_bytes = (F::size_in_bits() + 8 - 1) / 8; 21 | if n_bytes != 32 { 22 | unimplemented!("neptune currently supports 32-byte fields exclusively"); 23 | }; 24 | assert_eq!((field_size as f32 / 8.0).ceil() as usize, n_bytes); 25 | 26 | // r_f here is 2* number of *half* full rounds. 27 | let num_constants = (r_f + r_p) * t; 28 | let mut init_sequence: VecDeque = VecDeque::new(); 29 | append_bits(&mut init_sequence, 2, field); // Bits 0-1 30 | append_bits(&mut init_sequence, 4, sbox); // Bits 2-5 31 | append_bits(&mut init_sequence, 12, field_size); // Bits 6-17 32 | append_bits(&mut init_sequence, 12, t); // Bits 18-29 33 | append_bits(&mut init_sequence, 10, r_f); // Bits 30-39 34 | append_bits(&mut init_sequence, 10, r_p); // Bits 40-49 35 | append_bits(&mut init_sequence, 30, 0b111111111111111111111111111111u128); // Bits 50-79 36 | 37 | let mut grain = GrainLFSR::new(init_sequence, field_size); 38 | let mut round_constants: Vec = Vec::new(); 39 | 40 | match field { 41 | 1 => { 42 | for _ in 0..num_constants { 43 | while { 44 | // TODO: Please review this part. May be different from 45 | // neptune. 46 | 47 | // Generate 32 bytes and interpret them as a big-endian 48 | // integer. Bytes are big-endian to 49 | // agree with the integers generated by grain_random_bits in 50 | // the reference implementation: 51 | // 52 | // def grain_random_bits(num_bits): 53 | // random_bits = [grain_gen.next() for i in range(0, 54 | // num_bits)] random_int = 55 | // int("".join(str(i) for i in random_bits), 2) 56 | // return random_int 57 | let mut repr = F::default().into_repr().to_bytes_be(); 58 | grain.get_next_bytes(repr.as_mut()); 59 | repr.reverse(); 60 | if let Some(f) = F::from_random_bytes(&repr) { 61 | round_constants.push(f); 62 | false 63 | } else { 64 | true 65 | } 66 | } {} 67 | } 68 | } 69 | _ => { 70 | panic!("Only prime fields are supported."); 71 | } 72 | } 73 | round_constants 74 | } 75 | 76 | fn append_bits>(vec: &mut VecDeque, n: usize, from: T) { 77 | let val = from.into(); 78 | for i in (0..n).rev() { 79 | vec.push_back((val >> i) & 1 != 0); 80 | } 81 | } 82 | 83 | // adapted from: https://github.com/filecoin-project/neptune/blob/master/src/round_constants.rs 84 | struct GrainLFSR { 85 | state: VecDeque, 86 | field_size: u16, 87 | } 88 | 89 | impl GrainLFSR { 90 | pub fn new(initial_sequence: VecDeque, field_size: u16) -> Self { 91 | assert_eq!( 92 | initial_sequence.len(), 93 | 80, 94 | "Initial Sequence must be 80 bits" 95 | ); 96 | let mut g = GrainLFSR { 97 | state: initial_sequence, 98 | field_size, 99 | }; 100 | (0..160).for_each(|_| { 101 | g.generate_new_bit(); 102 | }); 103 | assert_eq!(80, g.state.len()); 104 | g 105 | } 106 | 107 | fn generate_new_bit(&mut self) -> bool { 108 | let new_bit = self.bit(62) 109 | ^ self.bit(51) 110 | ^ self.bit(38) 111 | ^ self.bit(23) 112 | ^ self.bit(13) 113 | ^ self.bit(0); 114 | self.state.pop_front(); 115 | self.state.push_back(new_bit); 116 | new_bit 117 | } 118 | 119 | fn bit(&self, index: usize) -> bool { 120 | self.state[index] 121 | } 122 | 123 | fn next_byte(&mut self, bit_count: usize) -> u8 { 124 | // Accumulate bits from most to least significant, so the most 125 | // significant bit is the one generated first by the bit stream 126 | let mut acc: u8 = 0; 127 | self.take(bit_count).for_each(|bit| { 128 | acc <<= 1; 129 | if bit { 130 | acc += 1; 131 | } 132 | }); 133 | 134 | acc 135 | } 136 | 137 | fn get_next_bytes(&mut self, result: &mut [u8]) { 138 | let remainder_bits = self.field_size as usize % 8; 139 | // Prime fields will always have remainder bits, 140 | // but other field types could be supported in the future. 141 | if remainder_bits > 0 { 142 | // If there is an unfull byte, it should be the first. 143 | // Subsequent bytes are packed into result in the order generated. 144 | result[0] = self.next_byte(remainder_bits); 145 | } else { 146 | result[0] = self.next_byte(8); 147 | } 148 | 149 | // First byte is already set 150 | for item in result.iter_mut().skip(1) { 151 | *item = self.next_byte(8) 152 | } 153 | } 154 | } 155 | 156 | impl Iterator for GrainLFSR { 157 | type Item = bool; 158 | 159 | // TO BE checked 160 | fn next(&mut self) -> Option { 161 | let mut new_bit = self.generate_new_bit(); 162 | while !new_bit { 163 | let _new_bit = self.generate_new_bit(); 164 | new_bit = self.generate_new_bit(); 165 | } 166 | new_bit = self.generate_new_bit(); 167 | Some(new_bit) 168 | } 169 | } 170 | 171 | // TODO: TO BE TESTED! 172 | -------------------------------------------------------------------------------- /plonk-hashing/src/poseidon/round_numbers.rs: -------------------------------------------------------------------------------- 1 | // Adapted from https://github.com/filecoin-project/neptune/blob/master/src/round_numbers.rs 2 | 3 | // The number of bits of the Poseidon prime field modulus. Denoted `n` in the 4 | // Poseidon paper (where `n = ceil(log2(p))`). Note that BLS12-381's scalar 5 | // field modulus is 255 bits, however we use 256 bits for simplicity when 6 | // operating on bytes as the single bit difference does not affect 7 | // the round number security properties. 8 | const PRIME_BITLEN: usize = 256; 9 | 10 | // Security level (in bits), denoted `M` in the Poseidon paper. 11 | const M: usize = 128; 12 | 13 | /// The number of S-boxes (also called the "cost") given by equation (14) in the 14 | /// Poseidon paper: `cost = t * R_F + R_P`. 15 | fn n_sboxes(t: usize, rf: usize, rp: usize) -> usize { 16 | t * rf + rp 17 | } 18 | 19 | /// Returns the round numbers for a given arity `(R_F, R_P)`. 20 | #[allow(dead_code)] 21 | pub(crate) fn round_numbers_base(arity: usize) -> (usize, usize) { 22 | let t = arity + 1; 23 | calc_round_numbers(t, true) 24 | } 25 | 26 | /// In case of newly-discovered attacks, we may need stronger security. 27 | /// This option exists so we can preemptively create circuits in order to switch 28 | /// to them quickly if needed. 29 | /// 30 | /// "A realistic alternative is to increase the number of partial rounds by 25%. 31 | /// Then it is unlikely that a new attack breaks through this number, 32 | /// but even if this happens then the complexity is almost surely above 2^64, 33 | /// and you will be safe." 34 | /// - D Khovratovich 35 | #[allow(dead_code)] 36 | pub(crate) fn round_numbers_strengthened(arity: usize) -> (usize, usize) { 37 | let (full_round, partial_rounds) = round_numbers_base(arity); 38 | 39 | // Increase by 25%, rounding up. 40 | let strengthened_partial_rounds = 41 | f64::ceil(partial_rounds as f64 * 1.25) as usize; 42 | 43 | (full_round, strengthened_partial_rounds) 44 | } 45 | 46 | /// Returns the round numbers for a given width `t`. Here, the `security_margin` 47 | /// parameter does not indicate that we are calculating `R_F` and `R_P` for the 48 | /// "strengthened" round numbers, done in the function 49 | /// `round_numbers_strengthened()`. 50 | pub(crate) fn calc_round_numbers( 51 | t: usize, 52 | security_margin: bool, 53 | ) -> (usize, usize) { 54 | let mut rf = 0; 55 | let mut rp = 0; 56 | let mut n_sboxes_min = usize::MAX; 57 | 58 | for mut rf_test in (2..=1000).step_by(2) { 59 | for mut rp_test in 4..200 { 60 | if round_numbers_are_secure(t, rf_test, rp_test) { 61 | if security_margin { 62 | rf_test += 2; 63 | rp_test = (1.075 * rp_test as f32).ceil() as usize; 64 | } 65 | let n_sboxes = n_sboxes(t, rf_test, rp_test); 66 | if n_sboxes < n_sboxes_min 67 | || (n_sboxes == n_sboxes_min && rf_test < rf) 68 | { 69 | rf = rf_test; 70 | rp = rp_test; 71 | n_sboxes_min = n_sboxes; 72 | } 73 | } 74 | } 75 | } 76 | 77 | (rf, rp) 78 | } 79 | 80 | /// Returns `true` if the provided round numbers satisfy the security 81 | /// inequalities specified in the Poseidon paper. 82 | fn round_numbers_are_secure(t: usize, rf: usize, rp: usize) -> bool { 83 | let (rp, t, n, m) = (rp as f32, t as f32, PRIME_BITLEN as f32, M as f32); 84 | let rf_stat = if m <= (n - 3.0) * (t + 1.0) { 85 | 6.0 86 | } else { 87 | 10.0 88 | }; 89 | let rf_interp = 0.43 * m + t.log2() - rp; 90 | let rf_grob_1 = 0.21 * n - rp; 91 | let rf_grob_2 = (0.14 * n - 1.0 - rp) / (t - 1.0); 92 | let rf_max = [rf_stat, rf_interp, rf_grob_1, rf_grob_2] 93 | .iter() 94 | .map(|rf| rf.ceil() as usize) 95 | .max() 96 | .unwrap(); 97 | rf >= rf_max 98 | } 99 | 100 | #[cfg(test)] 101 | mod tests { 102 | use super::*; 103 | 104 | use std::fs; 105 | 106 | #[test] 107 | fn test_round_numbers_against_known_values() { 108 | // Each case contains a `t` (where `t = arity + 1`) and the `R_P` 109 | // expected for that `t`. 110 | let cases = [ 111 | (2usize, 55usize), 112 | (3, 55), 113 | (4, 56), 114 | (5, 56), 115 | (6, 56), 116 | (7, 56), 117 | (8, 57), 118 | (9, 57), 119 | (10, 57), 120 | (11, 57), 121 | (12, 57), 122 | (13, 57), 123 | (14, 57), 124 | (15, 57), 125 | (16, 59), 126 | (17, 59), 127 | (25, 59), 128 | (37, 60), 129 | (65, 61), 130 | ]; 131 | for (t, rp_expected) in cases.iter() { 132 | let (rf, rp) = calc_round_numbers(*t, true); 133 | assert_eq!(rf, 8); 134 | assert_eq!(rp, *rp_expected); 135 | } 136 | } 137 | 138 | #[ignore] 139 | #[test] 140 | fn test_round_numbers_against_python_script() { 141 | // A parsed line from `parameters/round_numbers.txt`. 142 | struct Line { 143 | t: usize, 144 | rf: usize, 145 | rp: usize, 146 | sbox_cost: usize, 147 | size_cost: usize, 148 | } 149 | 150 | let lines: Vec = fs::read_to_string( 151 | "parameters/round_numbers.txt", 152 | ) 153 | .expect( 154 | "failed to read round numbers file: `parameters/round_numbers.txt`", 155 | ) 156 | .lines() 157 | .skip_while(|line| line.starts_with('#')) 158 | .map(|line| { 159 | let nums: Vec = line 160 | .split(' ') 161 | .map(|s| { 162 | s.parse().unwrap_or_else(|_| { 163 | panic!("failed to parse line as `usize`s: {}", line) 164 | }) 165 | }) 166 | .collect(); 167 | assert_eq!( 168 | nums.len(), 169 | 5, 170 | "line in does not contain 5 values: {}", 171 | line 172 | ); 173 | Line { 174 | t: nums[0], 175 | rf: nums[1], 176 | rp: nums[2], 177 | sbox_cost: nums[3], 178 | size_cost: nums[4], 179 | } 180 | }) 181 | .collect(); 182 | 183 | assert!( 184 | !lines.is_empty(), 185 | "no lines were parsed from `round_numbers.txt`", 186 | ); 187 | 188 | for line in lines { 189 | let (rf, rp) = calc_round_numbers(line.t, true); 190 | let sbox_cost = n_sboxes(line.t, rf, rp); 191 | let size_cost = sbox_cost * PRIME_BITLEN; 192 | 193 | assert_eq!(rf, line.rf, "full rounds differ from script"); 194 | assert_eq!(rp, line.rp, "partial rounds differ from script"); 195 | assert_eq!(sbox_cost, line.sbox_cost, "cost differs from script"); 196 | assert_eq!( 197 | size_cost, line.size_cost, 198 | "size-cost differs from script" 199 | ); 200 | } 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly-2023-06-15 2 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 80 2 | wrap_comments = true 3 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) DUSK NETWORK. All rights reserved. 6 | 7 | //! A crate for a generic Rust PLONK implementation using arkworks as a backend. 8 | 9 | #![cfg_attr(not(any(feature = "std", test)), no_std)] 10 | #![cfg_attr(doc_cfg, feature(doc_cfg))] 11 | #![forbid(rustdoc::broken_intra_doc_links)] 12 | #![forbid(missing_docs)] 13 | 14 | #[doc(inline)] 15 | pub use plonk_core::*; 16 | 17 | #[doc(inline)] 18 | pub use plonk_hashing as hashing; 19 | --------------------------------------------------------------------------------