├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Cargo.toml ├── LICENSE.txt ├── Makefile ├── README.md ├── Testfile ├── benches ├── generators.rs ├── linear_proof.rs ├── r1cs.rs └── range_proof.rs ├── docs ├── aggregation-api.md ├── assets │ ├── aggregated-range-proof.sketch │ ├── bulletproofs-diagram.png │ └── rustdoc-include-katex-header.html ├── cs-proof.md ├── inner-product-protocol.md ├── notes-intro.md ├── notes-ipp.md ├── notes-r1cs.md ├── notes-rp.md ├── r1cs-docs-example.md └── range-proof-protocol.md ├── rust-toolchain ├── src ├── errors.rs ├── generators.rs ├── inner_product_proof.rs ├── lib.rs ├── linear_proof.rs ├── r1cs │ ├── constraint_system.rs │ ├── linear_combination.rs │ ├── mod.rs │ ├── proof.rs │ ├── prover.rs │ └── verifier.rs ├── range_proof │ ├── dealer.rs │ ├── messages.rs │ ├── mod.rs │ └── party.rs ├── transcript.rs └── util.rs └── tests ├── r1cs.rs └── range_proof.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | cache: cargo 3 | 4 | env: 5 | - TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='' 6 | # The yoloproofs feature is disabled on the main branch. 7 | #- TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='yoloproofs' 8 | # run cargo bench with a filter that matches no benchmarks. 9 | # this ensures the benchmarks build but doesn't run them on the CI server. 10 | - TEST_COMMAND=bench EXTRA_FLAGS='"DONTRUNBENCHMARKS"' FEATURES='' 11 | 12 | matrix: 13 | fast_finish: true 14 | include: 15 | before_script: 16 | - rustup component add rustfmt-preview 17 | script: 18 | - cargo fmt --all -- --check 19 | 20 | script: 21 | - cargo $TEST_COMMAND --features="$FEATURES" $EXTRA_FLAGS 22 | 23 | notifications: 24 | slack: 25 | rooms: 26 | - dalek-cryptography:Xxv9WotKYWdSoKlgKNqXiHoD#dalek-bots 27 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | Entries are listed in reverse chronological order. 4 | 5 | ## 2.0.0 6 | 7 | * Switch from `failure` to `std`-compatible errors via `thiserror`. 8 | * Update `rand`, `curve25519-dalek`, `merlin` versions. 9 | * Adds `no_std` support by @xoloki. 10 | 11 | ## 1.0.4 12 | 13 | * Change doc-include paths to allow compilation on the latest Rust nightly 14 | (which changed the path root). 15 | * Various changes to the (unreleased, unstable) R1CS implementation, which is 16 | disabled in the released version of the code. 17 | 18 | ## 1.0.3 19 | 20 | * Mistakes were made. Yanked and replaced by 1.0.4 above. 21 | 22 | ## 1.0.2 23 | 24 | * Updates the library to use the renamed functions in Merlin 1.1. 25 | * Adds additional validation checks to prevent identity points being used as 26 | part of a proof. This does not appear to have security content, but is 27 | intended as a defense-in-depth mechanism. 28 | See [this comment][identity_comment] for more motivation. 29 | * Documentation tweaks. 30 | 31 | ## 1.0.1 32 | 33 | * Tweaks to crate metadata. 34 | * Minor documentation changes. 35 | * Adds a regression test for deserialize-and-verify for proofs created using 36 | v1.0.0, to ensure they continue to verify in future versions. 37 | 38 | ## 1.0.0 39 | 40 | * Minor tweaks to the prerelease version. 41 | * Preliminary support for R1CS proofs, but this feature is hard-disabled in the 42 | published crate. 43 | 44 | ## 1.0.0-pre.0 45 | 46 | Initial prerelease version, supporting single and aggregated range proofs, and 47 | multiparty proof aggregation. 48 | 49 | [identity_comment]: https://github.com/dalek-cryptography/bulletproofs/pull/248#discussion_r251916724 50 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We welcome code and documentation PRs. It's probably best to start by 4 | opening or commenting on an existing issue, since the code is in a state 5 | of flux. 6 | 7 | # Workflow 8 | 9 | ## Branches 10 | 11 | Currently, the work on the `bulletproofs` crate is in two branches: 12 | 13 | * `main` holds the latest released version; 14 | 15 | * `develop` holds ongoing development work. 16 | 17 | Pull requests should be made against `develop`, **not** `main`. 18 | 19 | It's best to start a PR for every in-progress branch so that it's possible 20 | to track all ongoing development work. Adding the `PTAL` (please take a 21 | look) label indicates that the branch is ready for code review. 22 | 23 | ## Labels 24 | 25 | Labels starting with `T-` are for labeling topics (`T-api`, `T-r1cs`, etc). 26 | 27 | The `T-research` label indicates unsolved open problems. 28 | 29 | Labels starting with `P-` are for priority levels. 30 | 31 | Labels starting with `E-` are for effort estimates. 32 | 33 | ## CI 34 | 35 | We enforce style in CI using `cargo fmt`. 36 | 37 | The `bulletproofs` repo currently pins a Rust nightly. This means that 38 | all `cargo` invocations made inside of the `bulletproofs` repo use the 39 | pinned nightly version. This means that running `cargo fmt` in the 40 | `bulletproofs` repo requires that `rustfmt` is installed *for the pinned 41 | nightly toolchain*. To do this, run 42 | ``` 43 | rustup component add rustfmt-preview 44 | ``` 45 | while in the `bulletproofs` repo. 46 | 47 | To run `rustfmt`, use 48 | ``` 49 | cargo fmt 50 | ``` 51 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bulletproofs" 3 | # Before doing a release: 4 | # - update version field 5 | # - update html_root_url 6 | # - ensure yoloproofs was disabled in an atomic (revertable) commit 7 | # - update CHANGELOG 8 | version = "2.0.0" 9 | authors = ["Cathie Yun ", 10 | "Henry de Valence ", 11 | "Oleg Andreev "] 12 | readme = "README.md" 13 | license = "MIT" 14 | repository = "https://github.com/dalek-cryptography/bulletproofs" 15 | categories = ["cryptography"] 16 | keywords = ["cryptography", "crypto", "ristretto", "zero-knowledge", "bulletproofs"] 17 | description = "A pure-Rust implementation of Bulletproofs using Ristretto" 18 | edition = "2018" 19 | 20 | [dependencies] 21 | curve25519-dalek = { version = "2", default-features = false, features = ["u64_backend", "nightly", "serde", "alloc"] } 22 | subtle = { version = "2", default-features = false } 23 | sha3 = { version = "0.8", default-features = false } 24 | digest = { version = "0.8", default-features = false } 25 | rand_core = { version = "0.5", default-features = false, features = ["alloc"] } 26 | rand = { version = "0.7", default-features = false, optional = true } 27 | byteorder = { version = "1", default-features = false } 28 | serde = { version = "1", default-features = false, features = ["alloc"] } 29 | serde_derive = { version = "1", default-features = false } 30 | thiserror = { version = "1", optional = true } 31 | merlin = { version = "2", default-features = false } 32 | clear_on_drop = { version = "0.2", default-features = false, features = ["nightly"] } 33 | 34 | [dev-dependencies] 35 | hex = "0.3" 36 | criterion = "0.3" 37 | bincode = "1" 38 | rand_chacha = "0.2" 39 | 40 | [features] 41 | default = ["std", "avx2_backend"] 42 | avx2_backend = ["curve25519-dalek/avx2_backend"] 43 | # yoloproofs = [] 44 | std = ["rand", "rand/std", "thiserror"] 45 | 46 | [[test]] 47 | name = "range_proof" 48 | 49 | [[test]] 50 | name = "r1cs" 51 | required-features = ["yoloproofs"] 52 | 53 | [[bench]] 54 | name = "range_proof" 55 | harness = false 56 | 57 | [[bench]] 58 | name = "generators" 59 | harness = false 60 | 61 | [[bench]] 62 | name = "r1cs" 63 | harness = false 64 | required-features = ["yoloproofs"] 65 | 66 | [[bench]] 67 | name = "linear_proof" 68 | harness = false 69 | 70 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Chain, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | FEATURES := yoloproofs 2 | 3 | doc: 4 | cargo rustdoc --features "$(FEATURES)" -- --html-in-header docs/assets/rustdoc-include-katex-header.html 5 | 6 | doc-internal: 7 | cargo rustdoc --features "$(FEATURES)" -- --html-in-header docs/assets/rustdoc-include-katex-header.html --document-private-items 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bulletproofs 2 | 3 | 7 | 8 | The fastest [Bulletproofs][bp_website] implementation ever, featuring 9 | single and aggregated range proofs, strongly-typed multiparty 10 | computation, and a programmable constraint system API for proving 11 | arbitrary statements (under development). 12 | 13 | This library implements Bulletproofs using [Ristretto][ristretto], 14 | using the `ristretto255` implementation in 15 | [`curve25519-dalek`][curve25519_dalek]. When using the [parallel 16 | formulas][parallel_edwards] in the `curve25519-dalek` AVX2 backend, it 17 | can verify 64-bit rangeproofs **approximately twice as fast** as the 18 | original `libsecp256k1`-based Bulletproofs implementation. 19 | 20 | This library provides implementations of: 21 | 22 | * Single-party proofs of single or multiple ranges, using the 23 | aggregated rangeproof construction; 24 | 25 | * Online multi-party computation for rangeproof aggregation between 26 | multiple parties, using [session types][session_type_blog] to 27 | statically enforce correct protocol flow; 28 | 29 | * A programmable constraint system API for expressing rank-1 30 | constraint systems, and proving and verifying proofs of arbitrary 31 | statements (unstable, under development with the `yoloproofs` feature); 32 | 33 | * Online multi-party computation for aggregated constraint system proofs 34 | (planned future work). 35 | 36 | These proofs are implemented using [Merlin transcripts][doc_merlin], 37 | allowing them to be arbitrarily composed with other proofs without 38 | implementation changes. 39 | 40 | The development roadmap can be found in the 41 | [Milestones][gh_milestones] section of the [Github repo][gh_repo]. 42 | 43 | The constraint system API is provided **FOR EXPERIMENTS ONLY**, and must be 44 | enabled by specifying the `yoloproofs` feature. It is not covered by semver 45 | compatibility and is **SUBJECT TO CHANGE WITHOUT NOTICE**. 46 | 47 | Currently, the `yoloproofs` feature is disabled in the published version of the 48 | crate, so it can only be used by specifying a git dependency on the `develop` 49 | branch. This means that it is not possible to publish a crate using the R1CS 50 | API, because it is **FOR EXPERIMENTS ONLY**. 51 | 52 | ## Documentation 53 | 54 | The user-facing documentation for this functionality can be [found 55 | here][doc_external]. In addition, the library *also* contains 56 | extensive notes on how Bulletproofs work. These notes can be found in 57 | the library's [internal documentation][doc_internal]: 58 | 59 | * how [Bulletproofs work][bp_notes]; 60 | * how [the range proof protocol works][rp_notes]; 61 | * how [the inner product proof protocol works][ipp_notes]; 62 | * how [the aggregation protocol works][agg_notes]; 63 | * how the Bulletproof constraint system proofs work (under development); 64 | * how the constraint system reduction works (under development); 65 | * how the aggregated constraint system proofs work (future work). 66 | 67 | ## Comparative Performance 68 | 69 | The following table gives comparative timings for proving and verification of a 70 | 64-bit rangeproof on an Intel Skylake-X i7-7800X (@3.5GHz, Turbo Boost 71 | disabled). Times are in microseconds (lower is better), with the relative 72 | speed compared to the fastest implementation. 73 | 74 | | Implementation | Group | Proving (μs) | rel | Verification (μs) | rel | 75 | |----------------|------------------|-------------:|----------:|------------------:|----------:| 76 | | ours (avx2) | ristretto255 | 7300 | **1.00x** | 1040 | **1.00x** | 77 | | ours (u64) | ristretto255 | 11300 | **1.54x** | 1490 | **1.43x** | 78 | | libsecp+endo | secp256k1 | 14300 | **1.96x** | 1900 | **1.83x** | 79 | | libsecp-endo | secp256k1 | 16800 | **2.30x** | 2080 | **2.00x** | 80 | | Monero | ed25519 (unsafe) | 53300 | **7.30x** | 4810 | **4.63x** | 81 | 82 | Use of the `curve25519-dalek` IFMA backend gives another 1.5x speedup on a 83 | Cannonlake i3-8121U, increasing the verification speedup **3x** over libsecp 84 | and **7x** over Monero, but these processors are not yet generally available. 85 | 86 | This crate also contains other benchmarks; see the *Tests and Benchmarks* 87 | section below for details on how to run them all. 88 | 89 | ## Example 90 | 91 | The following example shows how to create and verify a 32-bit rangeproof. 92 | 93 | ```rust 94 | # // The #-commented lines are hidden in Rustdoc but not in raw 95 | # // markdown rendering, and contain boilerplate code so that the 96 | # // code in the README.md is actually run as part of the test suite. 97 | # 98 | # extern crate rand; 99 | # use rand::thread_rng; 100 | # 101 | # extern crate curve25519_dalek; 102 | # use curve25519_dalek::scalar::Scalar; 103 | # 104 | # extern crate merlin; 105 | # use merlin::Transcript; 106 | # 107 | # extern crate bulletproofs; 108 | # use bulletproofs::{BulletproofGens, PedersenGens, RangeProof}; 109 | # 110 | # fn main() { 111 | // Generators for Pedersen commitments. These can be selected 112 | // independently of the Bulletproofs generators. 113 | let pc_gens = PedersenGens::default(); 114 | 115 | // Generators for Bulletproofs, valid for proofs up to bitsize 64 116 | // and aggregation size up to 1. 117 | let bp_gens = BulletproofGens::new(64, 1); 118 | 119 | // A secret value we want to prove lies in the range [0, 2^32) 120 | let secret_value = 1037578891u64; 121 | 122 | // The API takes a blinding factor for the commitment. 123 | let blinding = Scalar::random(&mut thread_rng()); 124 | 125 | // The proof can be chained to an existing transcript. 126 | // Here we create a transcript with a doctest domain separator. 127 | let mut prover_transcript = Transcript::new(b"doctest example"); 128 | 129 | // Create a 32-bit rangeproof. 130 | let (proof, committed_value) = RangeProof::prove_single( 131 | &bp_gens, 132 | &pc_gens, 133 | &mut prover_transcript, 134 | secret_value, 135 | &blinding, 136 | 32, 137 | ).expect("A real program could handle errors"); 138 | 139 | // Verification requires a transcript with identical initial state: 140 | let mut verifier_transcript = Transcript::new(b"doctest example"); 141 | assert!( 142 | proof 143 | .verify_single(&bp_gens, &pc_gens, &mut verifier_transcript, &committed_value, 32) 144 | .is_ok() 145 | ); 146 | # } 147 | ``` 148 | ## Building 149 | 150 | To compile successfully, you will need to have nightly Rust installed, rather than stable. 151 | 152 | You can install nightly Rust with rustup: 153 | 154 | ```text 155 | rustup default nightly 156 | ``` 157 | 158 | ## Tests and Benchmarks 159 | 160 | Run tests with `cargo test`. Run benchmarks with `cargo bench`. This crate 161 | uses [criterion.rs][criterion] for benchmarks. 162 | 163 | ## Features 164 | 165 | The `yoloproofs` feature enables support for rank-1 constraint system proofs. 166 | It is **UNSTABLE AND UNSUITABLE FOR DEPLOYMENT**, and **PROVIDED FOR TESTING 167 | ONLY**. 168 | 169 | The `avx2_backend` feature enables `curve25519-dalek`'s AVX2 backend, 170 | which implements curve arithmetic using [parallel 171 | formulas][parallel_edwards]. To use it for Bulletproofs, the 172 | `target_cpu` must support AVX2: 173 | 174 | ```text 175 | RUSTFLAGS="-C target_cpu=skylake" cargo bench --features "avx2_backend" 176 | ``` 177 | 178 | Skylake-X CPUs have double the AVX2 registers. To use them, try 179 | 180 | ```text 181 | RUSTFLAGS="-C target_cpu=skylake-avx512" cargo bench --features "avx2_backend" 182 | ``` 183 | 184 | This prevents spills in the AVX2 parallel field multiplication code, but causes 185 | worse code generation elsewhere ¯\\\_(ツ)\_/¯ 186 | 187 | ## About 188 | 189 | This is a research project sponsored by [Interstellar][interstellar], 190 | developed by Henry de Valence, Cathie Yun, and Oleg Andreev. 191 | 192 | [bp_website]: https://crypto.stanford.edu/bulletproofs/ 193 | [ristretto]: https://ristretto.group 194 | [doc_merlin]: https://doc.dalek.rs/merlin/index.html 195 | [doc_external]: https://doc.dalek.rs/bulletproofs/index.html 196 | [doc_internal]: https://doc-internal.dalek.rs/bulletproofs/index.html 197 | [bp_notes]: https://doc-internal.dalek.rs/bulletproofs/notes/index.html 198 | [rp_notes]: https://doc-internal.dalek.rs/bulletproofs/range_proof/index.html 199 | [ipp_notes]: https://doc-internal.dalek.rs/bulletproofs/inner_product_proof/index.html 200 | [agg_notes]: https://doc-internal.dalek.rs/bulletproofs/notes/index.html#aggregated-range-proof 201 | [criterion]: https://github.com/japaric/criterion.rs 202 | [session_type_blog]: https://blog.chain.com/bulletproof-multi-party-computation-in-rust-with-session-types-b3da6e928d5d 203 | [curve25519_dalek]: https://doc.dalek.rs/curve25519_dalek/index.html 204 | [parallel_edwards]: https://medium.com/@hdevalence/accelerating-edwards-curve-arithmetic-with-parallel-formulas-ac12cf5015be 205 | [gh_repo]: https://github.com/dalek-cryptography/bulletproofs/ 206 | [gh_milestones]: https://github.com/dalek-cryptography/bulletproofs/milestones 207 | [interstellar]: https://interstellar.com/ 208 | -------------------------------------------------------------------------------- /Testfile: -------------------------------------------------------------------------------- 1 | rustfmt: rustfmt --version && cargo fmt -- --write-mode=diff 2 | cargotest: cargo test 3 | -------------------------------------------------------------------------------- /benches/generators.rs: -------------------------------------------------------------------------------- 1 | use bulletproofs::{BulletproofGens, PedersenGens}; 2 | 3 | #[macro_use] 4 | extern crate criterion; 5 | use criterion::Criterion; 6 | 7 | fn pc_gens(c: &mut Criterion) { 8 | c.bench_function("PedersenGens::new", |b| b.iter(|| PedersenGens::default())); 9 | } 10 | 11 | fn bp_gens(c: &mut Criterion) { 12 | c.bench_function_over_inputs( 13 | "BulletproofGens::new", 14 | |b, size| b.iter(|| BulletproofGens::new(*size, 1)), 15 | (0..10).map(|i| 2 << i), 16 | ); 17 | } 18 | 19 | criterion_group! { 20 | bp, 21 | bp_gens, 22 | pc_gens, 23 | } 24 | 25 | criterion_main!(bp); 26 | -------------------------------------------------------------------------------- /benches/linear_proof.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | #[macro_use] 4 | extern crate criterion; 5 | use criterion::Criterion; 6 | 7 | extern crate bulletproofs; 8 | extern crate curve25519_dalek; 9 | extern crate merlin; 10 | extern crate rand; 11 | 12 | use core::iter; 13 | 14 | use bulletproofs::LinearProof; 15 | use bulletproofs::{BulletproofGens, PedersenGens}; 16 | use curve25519_dalek::ristretto::RistrettoPoint; 17 | use curve25519_dalek::scalar::Scalar; 18 | use curve25519_dalek::traits::VartimeMultiscalarMul; 19 | use merlin::Transcript; 20 | 21 | /// Different linear proof vector lengths to try 22 | static TEST_SIZES: [usize; 5] = [64, 128, 256, 512, 1024]; 23 | 24 | fn create_linear_proof_helper(c: &mut Criterion) { 25 | c.bench_function_over_inputs( 26 | "linear proof creation", 27 | move |bench, n| { 28 | let mut rng = rand::thread_rng(); 29 | 30 | let bp_gens = BulletproofGens::new(*n, 1); 31 | // Calls `.G()` on generators, which should be a pub(crate) function only. 32 | // For now, make that function public so it can be accessed from benches. 33 | // We don't want to use bp_gens directly because we don't need the H generators. 34 | let G: Vec = bp_gens.share(0).G(*n).cloned().collect(); 35 | 36 | let pedersen_gens = PedersenGens::default(); 37 | let F = pedersen_gens.B; 38 | let B = pedersen_gens.B_blinding; 39 | 40 | // a and b are the vectors for which we want to prove c = 41 | let a: Vec<_> = (0..*n).map(|_| Scalar::random(&mut rng)).collect(); 42 | let b: Vec<_> = (0..*n).map(|_| Scalar::random(&mut rng)).collect(); 43 | 44 | let mut transcript = Transcript::new(b"LinearProofBenchmark"); 45 | 46 | // C = + r * B + * F 47 | let r = Scalar::random(&mut rng); 48 | let c = inner_product(&a, &b); 49 | let C = RistrettoPoint::vartime_multiscalar_mul( 50 | a.iter().chain(iter::once(&r)).chain(iter::once(&c)), 51 | G.iter().chain(iter::once(&B)).chain(iter::once(&F)), 52 | ) 53 | .compress(); 54 | 55 | // Make linear proof 56 | bench.iter(|| { 57 | LinearProof::create( 58 | &mut transcript, 59 | &mut rng, 60 | &C, 61 | r, 62 | a.clone(), 63 | b.clone(), 64 | G.clone(), 65 | &F, 66 | &B, 67 | ) 68 | .unwrap(); 69 | }) 70 | }, 71 | TEST_SIZES, 72 | ); 73 | } 74 | 75 | /// Copied from src/inner_product_proof.rs 76 | /// Computes an inner product of two vectors 77 | /// \\[ 78 | /// {\langle {\mathbf{a}}, {\mathbf{b}} \rangle} = \sum\_{i=0}^{n-1} a\_i \cdot b\_i. 79 | /// \\] 80 | /// Panics if the lengths of \\(\mathbf{a}\\) and \\(\mathbf{b}\\) are not equal. 81 | fn inner_product(a: &[Scalar], b: &[Scalar]) -> Scalar { 82 | let mut out = Scalar::zero(); 83 | if a.len() != b.len() { 84 | panic!("inner_product(a,b): lengths of vectors do not match"); 85 | } 86 | for i in 0..a.len() { 87 | out += a[i] * b[i]; 88 | } 89 | out 90 | } 91 | 92 | criterion_group! { 93 | name = create_linear_proof; 94 | // Lower the sample size to run faster; larger shuffle sizes are 95 | // long so we're not microbenchmarking anyways. 96 | config = Criterion::default().sample_size(10); 97 | targets = 98 | create_linear_proof_helper, 99 | } 100 | 101 | fn linear_verify(c: &mut Criterion) { 102 | c.bench_function_over_inputs( 103 | "linear proof verification", 104 | move |bench, n| { 105 | let bp_gens = BulletproofGens::new(*n, 1); 106 | let mut rng = rand::thread_rng(); 107 | 108 | // Calls `.G()` on generators, which should be a pub(crate) function only. 109 | // For now, make that function public so it can be accessed from benches. 110 | // We can't simply use bp_gens directly because we don't need the H generators. 111 | let G: Vec = bp_gens.share(0).G(*n).cloned().collect(); 112 | let pedersen_gens = PedersenGens::default(); 113 | let F = pedersen_gens.B; 114 | let B = pedersen_gens.B_blinding; 115 | 116 | let b: Vec<_> = (0..*n).map(|_| Scalar::random(&mut rng)).collect(); 117 | 118 | // Generate the proof in its own scope to prevent reuse of 119 | // prover variables by the verifier 120 | let (proof, C) = { 121 | // a and b are the vectors for which we want to prove c = 122 | let a: Vec<_> = (0..*n).map(|_| Scalar::random(&mut rng)).collect(); 123 | 124 | let mut transcript = Transcript::new(b"LinearProofBenchmark"); 125 | 126 | // C = + r * B + * F 127 | let r = Scalar::random(&mut rng); 128 | let c = inner_product(&a, &b); 129 | let C = RistrettoPoint::vartime_multiscalar_mul( 130 | a.iter().chain(iter::once(&r)).chain(iter::once(&c)), 131 | G.iter().chain(iter::once(&B)).chain(iter::once(&F)), 132 | ) 133 | .compress(); 134 | 135 | let proof = LinearProof::create( 136 | &mut transcript, 137 | &mut rng, 138 | &C, 139 | r, 140 | a.clone(), 141 | b.clone(), 142 | G.clone(), 143 | &F, 144 | &B, 145 | ) 146 | .unwrap(); 147 | 148 | (proof, C) 149 | }; 150 | 151 | // Verify linear proof 152 | bench.iter(|| { 153 | let mut verifier_transcript = Transcript::new(b"LinearProofBenchmark"); 154 | proof 155 | .verify(&mut verifier_transcript, &C, &G, &F, &B, b.clone()) 156 | .unwrap(); 157 | }); 158 | }, 159 | TEST_SIZES, 160 | ); 161 | } 162 | 163 | criterion_group! { 164 | name = verify_linear_proof; 165 | // Lower the sample size to run faster; larger shuffle sizes are 166 | // long so we're not microbenchmarking anyways. 167 | config = Criterion::default().sample_size(10); 168 | targets = 169 | linear_verify, 170 | } 171 | 172 | criterion_main!(create_linear_proof, verify_linear_proof); 173 | -------------------------------------------------------------------------------- /benches/r1cs.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | #[macro_use] 4 | extern crate criterion; 5 | use criterion::Criterion; 6 | 7 | // Code below copied from ../tests/r1cs.rs 8 | // 9 | // Ideally we wouldn't duplicate it, but AFAIK criterion requires a 10 | // seperate benchmark harness, while the test code uses a different 11 | // test harness, so I (hdevalence) just copied the code over. It 12 | // should not be edited here. In the future it would be good if 13 | // someone wants to figure a way to use #[path] attributes or 14 | // something to avoid the duplication. 15 | 16 | extern crate bulletproofs; 17 | extern crate curve25519_dalek; 18 | extern crate merlin; 19 | extern crate rand; 20 | 21 | use bulletproofs::r1cs::*; 22 | use bulletproofs::{BulletproofGens, PedersenGens}; 23 | use curve25519_dalek::ristretto::CompressedRistretto; 24 | use curve25519_dalek::scalar::Scalar; 25 | use merlin::Transcript; 26 | use rand::seq::SliceRandom; 27 | use rand::Rng; 28 | 29 | // Shuffle gadget (documented in markdown file) 30 | 31 | /// A proof-of-shuffle. 32 | struct ShuffleProof(R1CSProof); 33 | 34 | impl ShuffleProof { 35 | fn gadget( 36 | cs: &mut CS, 37 | x: Vec, 38 | y: Vec, 39 | ) -> Result<(), R1CSError> { 40 | assert_eq!(x.len(), y.len()); 41 | let k = x.len(); 42 | 43 | if k == 1 { 44 | cs.constrain(y[0] - x[0]); 45 | return Ok(()); 46 | } 47 | 48 | cs.specify_randomized_constraints(move |cs| { 49 | let z = cs.challenge_scalar(b"shuffle challenge"); 50 | 51 | // Make last x multiplier for i = k-1 and k-2 52 | let (_, _, last_mulx_out) = cs.multiply(x[k - 1] - z, x[k - 2] - z); 53 | 54 | // Make multipliers for x from i == [0, k-3] 55 | let first_mulx_out = (0..k - 2).rev().fold(last_mulx_out, |prev_out, i| { 56 | let (_, _, o) = cs.multiply(prev_out.into(), x[i] - z); 57 | o 58 | }); 59 | 60 | // Make last y multiplier for i = k-1 and k-2 61 | let (_, _, last_muly_out) = cs.multiply(y[k - 1] - z, y[k - 2] - z); 62 | 63 | // Make multipliers for y from i == [0, k-3] 64 | let first_muly_out = (0..k - 2).rev().fold(last_muly_out, |prev_out, i| { 65 | let (_, _, o) = cs.multiply(prev_out.into(), y[i] - z); 66 | o 67 | }); 68 | 69 | // Constrain last x mul output and last y mul output to be equal 70 | cs.constrain(first_mulx_out - first_muly_out); 71 | 72 | Ok(()) 73 | }) 74 | } 75 | } 76 | 77 | impl ShuffleProof { 78 | /// Attempt to construct a proof that `output` is a permutation of `input`. 79 | /// 80 | /// Returns a tuple `(proof, input_commitments || output_commitments)`. 81 | pub fn prove<'a, 'b>( 82 | pc_gens: &'b PedersenGens, 83 | bp_gens: &'b BulletproofGens, 84 | transcript: &'a mut Transcript, 85 | input: &[Scalar], 86 | output: &[Scalar], 87 | ) -> Result< 88 | ( 89 | ShuffleProof, 90 | Vec, 91 | Vec, 92 | ), 93 | R1CSError, 94 | > { 95 | // Apply a domain separator with the shuffle parameters to the transcript 96 | // XXX should this be part of the gadget? 97 | let k = input.len(); 98 | transcript.append_message(b"dom-sep", b"ShuffleProof"); 99 | transcript.append_u64(b"k", k as u64); 100 | 101 | let mut prover = Prover::new(&pc_gens, transcript); 102 | 103 | // Construct blinding factors using an RNG. 104 | // Note: a non-example implementation would want to operate on existing commitments. 105 | let mut blinding_rng = rand::thread_rng(); 106 | 107 | let (input_commitments, input_vars): (Vec<_>, Vec<_>) = input 108 | .into_iter() 109 | .map(|v| prover.commit(*v, Scalar::random(&mut blinding_rng))) 110 | .unzip(); 111 | 112 | let (output_commitments, output_vars): (Vec<_>, Vec<_>) = output 113 | .into_iter() 114 | .map(|v| prover.commit(*v, Scalar::random(&mut blinding_rng))) 115 | .unzip(); 116 | 117 | ShuffleProof::gadget(&mut prover, input_vars, output_vars)?; 118 | 119 | let proof = prover.prove(&bp_gens)?; 120 | 121 | Ok((ShuffleProof(proof), input_commitments, output_commitments)) 122 | } 123 | } 124 | 125 | impl ShuffleProof { 126 | /// Attempt to verify a `ShuffleProof`. 127 | pub fn verify<'a, 'b>( 128 | &self, 129 | pc_gens: &'b PedersenGens, 130 | bp_gens: &'b BulletproofGens, 131 | transcript: &'a mut Transcript, 132 | input_commitments: &Vec, 133 | output_commitments: &Vec, 134 | ) -> Result<(), R1CSError> { 135 | // Apply a domain separator with the shuffle parameters to the transcript 136 | // XXX should this be part of the gadget? 137 | let k = input_commitments.len(); 138 | transcript.append_message(b"dom-sep", b"ShuffleProof"); 139 | transcript.append_u64(b"k", k as u64); 140 | 141 | let mut verifier = Verifier::new(transcript); 142 | 143 | let input_vars: Vec<_> = input_commitments 144 | .iter() 145 | .map(|V| verifier.commit(*V)) 146 | .collect(); 147 | 148 | let output_vars: Vec<_> = output_commitments 149 | .iter() 150 | .map(|V| verifier.commit(*V)) 151 | .collect(); 152 | 153 | ShuffleProof::gadget(&mut verifier, input_vars, output_vars)?; 154 | 155 | verifier.verify(&self.0, &pc_gens, &bp_gens) 156 | } 157 | } 158 | 159 | // End of copied code. 160 | 161 | /// Binary logarithm of maximum shuffle size. 162 | const LG_MAX_SHUFFLE_SIZE: usize = 10; 163 | /// Maximum shuffle size to benchmark. 164 | const MAX_SHUFFLE_SIZE: usize = 1 << LG_MAX_SHUFFLE_SIZE; 165 | 166 | fn bench_kshuffle_prove(c: &mut Criterion) { 167 | // Construct Bulletproof generators externally 168 | let pc_gens = PedersenGens::default(); 169 | let bp_gens = BulletproofGens::new(2 * MAX_SHUFFLE_SIZE, 1); 170 | 171 | c.bench_function_over_inputs( 172 | "k-shuffle proof creation", 173 | move |b, k| { 174 | // Generate inputs and outputs to kshuffle 175 | let mut rng = rand::thread_rng(); 176 | let (min, max) = (0u64, std::u64::MAX); 177 | let input: Vec = (0..*k) 178 | .map(|_| Scalar::from(rng.gen_range(min, max))) 179 | .collect(); 180 | let mut output = input.clone(); 181 | output.shuffle(&mut rand::thread_rng()); 182 | 183 | // Make kshuffle proof 184 | b.iter(|| { 185 | let mut prover_transcript = Transcript::new(b"ShuffleBenchmark"); 186 | ShuffleProof::prove(&pc_gens, &bp_gens, &mut prover_transcript, &input, &output) 187 | .unwrap(); 188 | }) 189 | }, 190 | (1..=LG_MAX_SHUFFLE_SIZE) 191 | .map(|i| 1 << i) 192 | .collect::>(), 193 | ); 194 | } 195 | 196 | criterion_group! { 197 | name = kshuffle_prove; 198 | // Lower the sample size to run faster; larger shuffle sizes are 199 | // long so we're not microbenchmarking anyways. 200 | config = Criterion::default().sample_size(10); 201 | targets = 202 | bench_kshuffle_prove, 203 | } 204 | 205 | fn bench_kshuffle_verify(c: &mut Criterion) { 206 | // Construct Bulletproof generators externally 207 | let pc_gens = PedersenGens::default(); 208 | let bp_gens = BulletproofGens::new(2 * MAX_SHUFFLE_SIZE, 1); 209 | 210 | c.bench_function_over_inputs( 211 | "k-shuffle proof verification", 212 | move |b, k| { 213 | // Generate the proof in its own scope to prevent reuse of 214 | // prover variables by the verifier 215 | let (proof, input_commitments, output_commitments) = { 216 | // Generate inputs and outputs to kshuffle 217 | let mut rng = rand::thread_rng(); 218 | let (min, max) = (0u64, std::u64::MAX); 219 | let input: Vec = (0..*k) 220 | .map(|_| Scalar::from(rng.gen_range(min, max))) 221 | .collect(); 222 | let mut output = input.clone(); 223 | output.shuffle(&mut rand::thread_rng()); 224 | 225 | let mut prover_transcript = Transcript::new(b"ShuffleBenchmark"); 226 | 227 | ShuffleProof::prove(&pc_gens, &bp_gens, &mut prover_transcript, &input, &output) 228 | .unwrap() 229 | }; 230 | 231 | // Verify kshuffle proof 232 | b.iter(|| { 233 | let mut verifier_transcript = Transcript::new(b"ShuffleBenchmark"); 234 | proof 235 | .verify( 236 | &pc_gens, 237 | &bp_gens, 238 | &mut verifier_transcript, 239 | &input_commitments, 240 | &output_commitments, 241 | ) 242 | .unwrap(); 243 | }) 244 | }, 245 | (1..=LG_MAX_SHUFFLE_SIZE) 246 | .map(|i| 1 << i) 247 | .collect::>(), 248 | ); 249 | } 250 | 251 | criterion_group! { 252 | name = kshuffle_verify; 253 | // Lower the sample size to run faster; larger shuffle sizes are 254 | // long so we're not microbenchmarking anyways. 255 | config = Criterion::default().sample_size(10); 256 | targets = 257 | bench_kshuffle_verify, 258 | } 259 | 260 | criterion_main!(kshuffle_prove, kshuffle_verify); 261 | -------------------------------------------------------------------------------- /benches/range_proof.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | #[macro_use] 3 | extern crate criterion; 4 | use criterion::Criterion; 5 | 6 | use rand; 7 | use rand::Rng; 8 | 9 | use curve25519_dalek::scalar::Scalar; 10 | 11 | use merlin::Transcript; 12 | 13 | use bulletproofs::RangeProof; 14 | use bulletproofs::{BulletproofGens, PedersenGens}; 15 | 16 | static AGGREGATION_SIZES: [usize; 6] = [1, 2, 4, 8, 16, 32]; 17 | 18 | fn create_aggregated_rangeproof_helper(n: usize, c: &mut Criterion) { 19 | let label = format!("Aggregated {}-bit rangeproof creation", n); 20 | 21 | c.bench_function_over_inputs( 22 | &label, 23 | move |b, &&m| { 24 | let pc_gens = PedersenGens::default(); 25 | let bp_gens = BulletproofGens::new(n, m); 26 | let mut rng = rand::thread_rng(); 27 | 28 | let (min, max) = (0u64, ((1u128 << n) - 1) as u64); 29 | let values: Vec = (0..m).map(|_| rng.gen_range(min, max)).collect(); 30 | let blindings: Vec = (0..m).map(|_| Scalar::random(&mut rng)).collect(); 31 | 32 | b.iter(|| { 33 | // Each proof creation requires a clean transcript. 34 | let mut transcript = Transcript::new(b"AggregateRangeProofBenchmark"); 35 | 36 | RangeProof::prove_multiple( 37 | &bp_gens, 38 | &pc_gens, 39 | &mut transcript, 40 | &values, 41 | &blindings, 42 | n, 43 | ) 44 | }) 45 | }, 46 | &AGGREGATION_SIZES, 47 | ); 48 | } 49 | 50 | fn create_aggregated_rangeproof_n_8(c: &mut Criterion) { 51 | create_aggregated_rangeproof_helper(8, c); 52 | } 53 | 54 | fn create_aggregated_rangeproof_n_16(c: &mut Criterion) { 55 | create_aggregated_rangeproof_helper(16, c); 56 | } 57 | 58 | fn create_aggregated_rangeproof_n_32(c: &mut Criterion) { 59 | create_aggregated_rangeproof_helper(32, c); 60 | } 61 | 62 | fn create_aggregated_rangeproof_n_64(c: &mut Criterion) { 63 | create_aggregated_rangeproof_helper(64, c); 64 | } 65 | 66 | fn verify_aggregated_rangeproof_helper(n: usize, c: &mut Criterion) { 67 | let label = format!("Aggregated {}-bit rangeproof verification", n); 68 | 69 | c.bench_function_over_inputs( 70 | &label, 71 | move |b, &&m| { 72 | let pc_gens = PedersenGens::default(); 73 | let bp_gens = BulletproofGens::new(n, m); 74 | let mut rng = rand::thread_rng(); 75 | 76 | let (min, max) = (0u64, ((1u128 << n) - 1) as u64); 77 | let values: Vec = (0..m).map(|_| rng.gen_range(min, max)).collect(); 78 | let blindings: Vec = (0..m).map(|_| Scalar::random(&mut rng)).collect(); 79 | 80 | let mut transcript = Transcript::new(b"AggregateRangeProofBenchmark"); 81 | let (proof, value_commitments) = RangeProof::prove_multiple( 82 | &bp_gens, 83 | &pc_gens, 84 | &mut transcript, 85 | &values, 86 | &blindings, 87 | n, 88 | ) 89 | .unwrap(); 90 | 91 | b.iter(|| { 92 | // Each proof creation requires a clean transcript. 93 | let mut transcript = Transcript::new(b"AggregateRangeProofBenchmark"); 94 | 95 | proof.verify_multiple(&bp_gens, &pc_gens, &mut transcript, &value_commitments, n) 96 | }); 97 | }, 98 | &AGGREGATION_SIZES, 99 | ); 100 | } 101 | 102 | fn verify_aggregated_rangeproof_n_8(c: &mut Criterion) { 103 | verify_aggregated_rangeproof_helper(8, c); 104 | } 105 | 106 | fn verify_aggregated_rangeproof_n_16(c: &mut Criterion) { 107 | verify_aggregated_rangeproof_helper(16, c); 108 | } 109 | 110 | fn verify_aggregated_rangeproof_n_32(c: &mut Criterion) { 111 | verify_aggregated_rangeproof_helper(32, c); 112 | } 113 | 114 | fn verify_aggregated_rangeproof_n_64(c: &mut Criterion) { 115 | verify_aggregated_rangeproof_helper(64, c); 116 | } 117 | 118 | criterion_group! { 119 | name = create_rp; 120 | config = Criterion::default().sample_size(10); 121 | targets = 122 | create_aggregated_rangeproof_n_8, 123 | create_aggregated_rangeproof_n_16, 124 | create_aggregated_rangeproof_n_32, 125 | create_aggregated_rangeproof_n_64, 126 | } 127 | 128 | criterion_group! { 129 | name = verify_rp; 130 | config = Criterion::default(); 131 | targets = 132 | verify_aggregated_rangeproof_n_8, 133 | verify_aggregated_rangeproof_n_16, 134 | verify_aggregated_rangeproof_n_32, 135 | verify_aggregated_rangeproof_n_64, 136 | } 137 | 138 | criterion_main!(create_rp, verify_rp); 139 | -------------------------------------------------------------------------------- /docs/assets/aggregated-range-proof.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dalek-cryptography/bulletproofs/be67b6d5f5ad1c1f54d5511b52e6d645a1313d07/docs/assets/aggregated-range-proof.sketch -------------------------------------------------------------------------------- /docs/assets/bulletproofs-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dalek-cryptography/bulletproofs/be67b6d5f5ad1c1f54d5511b52e6d645a1313d07/docs/assets/bulletproofs-diagram.png -------------------------------------------------------------------------------- /docs/assets/rustdoc-include-katex-header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 18 | -------------------------------------------------------------------------------- /docs/cs-proof.md: -------------------------------------------------------------------------------- 1 | Constraint system proof protocol 2 | ================================ 3 | 4 | The `constraint_system` module contains an API for producing a proof that a set of secret values satisfies a given constraint system. 5 | 6 | For more background on the constraint system proofs, see these two sections in the `notes` module: 7 | 8 | * [Constraint system](../notes/index.html#constraint-system): description of the constraint system architecture. 9 | * [Constraint system proof](../notes/index.html#constraint-system-proof): the math behind the constraint system proofs. 10 | 11 | Prover’s algorithm 12 | ------------------ 13 | 14 | The protocol begins with the prover computing commitments to the secret values \\(\mathbf{v}\\) and adding all commitments to the protocol transcript. 15 | 16 | \\[ 17 | V_i \gets \operatorname{Com}(v_i, {\widetilde{v}\_i}) = v\_i \cdot B + {\widetilde{v}\_i} \cdot {\widetilde{B}} 18 | \\] where each \\(\widetilde{v}\_i\\) is sampled randomly. 19 | 20 | The prover then [builds constraints](#building-constraints) in two phases. 21 | 22 | In the first phase, the prover allocates necessary multiplication gates on the fly, fills in weights \\(\mathbf{W}\_L',\mathbf{W}\_R',\mathbf{W}\_O',\mathbf{W}\_V'\\), and assigns values to the left, right and output wires 23 | of the multiplication gates (\\(\mathbf{a}\_L', \mathbf{a}\_R', \mathbf{a}\_O'\\)) without using the challenge values. 24 | 25 | Once \\(n'\\) multiplication gates are assigned, the prover commits to them via vector Pedersen commitments: 26 | 27 | \\[ 28 | \begin{aligned} 29 | \tilde{a}' \\;&{\xleftarrow{\\$}}\\; \mathbb Z\_p \\\\ 30 | \tilde{o}' \\;&{\xleftarrow{\\$}}\\; \mathbb Z\_p \\\\ 31 | A_I' &\gets \widetilde{B} \cdot \tilde{a}' + \langle \mathbf{G}' , \mathbf{a}\_L' \rangle + \langle \mathbf{H}', \mathbf{a}\_R' \rangle \\\\ 32 | A_O' &\gets \widetilde{B} \cdot \tilde{o}' + \langle \mathbf{G}' , \mathbf{a}\_O' \rangle \\\\ 33 | \end{aligned} 34 | \\] 35 | 36 | The prover also computes blinding factors \\(\mathbf{s}\_L', \mathbf{s}\_R'\\) 37 | for the left and right multiplication values and commits to them: 38 | 39 | \\[ 40 | \begin{aligned} 41 | \mathbf{s}\_L' \\; &{\xleftarrow{\\$}}\\; {\mathbb Z\_p}^{n'} \\\\ 42 | \mathbf{s}\_R' \\; &{\xleftarrow{\\$}}\\; {\mathbb Z\_p}^{n'} \\\\ 43 | \tilde{s}' \\; &{\xleftarrow{\\$}}\\; \mathbb Z\_p \\\\ 44 | S' &\gets \widetilde{B} \cdot \tilde{s}' + \langle \mathbf{G}', \mathbf{s}\_L' \rangle + \langle \mathbf{H}', \mathbf{s}\_R' \rangle 45 | \end{aligned} 46 | \\] 47 | 48 | The prover adds \\(A_I'\\), \\(A_O'\\) and \\(S'\\) to the protocol transcript. 49 | 50 | In the second phase, the prover is allowed to use challenge values when allocating multiplication gates (\\(\mathbf{a}\_{L}'', \mathbf{a}\_{R}'', \mathbf{a}\_{O}''\\)) and computing weights \\(\mathbf{W}\_L'',\mathbf{W}\_R'',\mathbf{W}\_O'',\mathbf{W}\_V''\\). 51 | 52 | When additional \\(n''\\) multiplication gates are assigned, the prover commits to them via vector Pedersen commitments, along with the corresponding blinding factors \\(\mathbf{s}\_L'', \mathbf{s}\_R''\\): 53 | 54 | \\[ 55 | \begin{aligned} 56 | \tilde{a}'' \\;&{\xleftarrow{\\$}}\\; \mathbb Z\_p \\\\ 57 | \tilde{o}'' \\;&{\xleftarrow{\\$}}\\; \mathbb Z\_p \\\\ 58 | A_I'' &\gets \widetilde{B} \cdot \tilde{a}'' + \langle \mathbf{G}'' , \mathbf{a}\_L'' \rangle + \langle \mathbf{H}'', \mathbf{a}\_R'' \rangle \\\\ 59 | A_O'' &\gets \widetilde{B} \cdot \tilde{o}'' + \langle \mathbf{G}'' , \mathbf{a}\_O'' \rangle \\\\ 60 | \mathbf{s}\_L'' \\; &{\xleftarrow{\\$}}\\; {\mathbb Z\_p}^{n''} \\\\ 61 | \mathbf{s}\_R'' \\; &{\xleftarrow{\\$}}\\; {\mathbb Z\_p}^{n''} \\\\ 62 | \tilde{s}'' \\; &{\xleftarrow{\\$}}\\; \mathbb Z\_p \\\\ 63 | S'' &\gets \widetilde{B} \cdot \tilde{s}'' + \langle \mathbf{G}'' , \mathbf{s}\_L'' \rangle + \langle \mathbf{H}'', \mathbf{s}\_R'' \rangle 64 | \end{aligned} 65 | \\] 66 | 67 | The prover adds \\(A_I''\\), \\(A_O''\\) and \\(S''\\) to the protocol transcript 68 | and obtains challenge scalars \\(y,z \in {\mathbb Z\_p}\\) from the transcript. 69 | 70 | The prover then flattens the constraints using \\(q\\) powers of challenge \\(z\\): 71 | 72 | \\[ 73 | \begin{aligned} 74 | \mathbf{w}\_L &\gets z \mathbf{z}^q \cdot (\mathbf{W}\_L' || \mathbf{W}\_L''), \\\\ 75 | \mathbf{w}\_R &\gets z \mathbf{z}^q \cdot (\mathbf{W}\_R' || \mathbf{W}\_R''), \\\\ 76 | \mathbf{w}\_O &\gets z \mathbf{z}^q \cdot (\mathbf{W}\_O' || \mathbf{W}\_O''), \\\\ 77 | \mathbf{w}\_V &\gets z \mathbf{z}^q \cdot (\mathbf{W}\_V' || \mathbf{W}\_V''), 78 | \end{aligned} 79 | \\] 80 | where each of \\(\mathbf{w}\_L, \mathbf{w}\_R, \mathbf{w}\_O\\) has length \\(n = n' + n''\\) and \\(\mathbf{w}\_V\\) has length \\(m\\). 81 | 82 | The prover then constructs the blinded polynomials and their inner product: 83 | 84 | \\[ 85 | \begin{aligned} 86 | {\mathbf{l}}(x) &\gets (\mathbf{a}\_L' || \mathbf{a}\_L'') \cdot x + (\mathbf{s}\_L' || \mathbf{s}\_L'') \cdot x^3 + \mathbf{y}^{-n} \circ \mathbf{w}\_R \cdot x + (\mathbf{a}\_O' || \mathbf{a}\_O'') \cdot x^2 \\\\ 87 | {\mathbf{r}}(x) &\gets \mathbf{y}^n \circ (\mathbf{a}\_R' || \mathbf{a}\_R'') \cdot x + \mathbf{y}^n \circ (\mathbf{s}\_R' || \mathbf{s}\_R'') \cdot x^3 + \mathbf{w}\_L \cdot x - \mathbf{y}^n + \mathbf{w}\_O \\\\ 88 | t(x) &\gets {\langle {\mathbf{l}}(x), {\mathbf{r}}(x) \rangle} 89 | \end{aligned} 90 | \\] 91 | 92 | The prover generates blinding factors for terms \\(t\_1, t\_3, t\_4, t\_5, t\_6\\) and creates Pedersen commitments to them 93 | (term \\(t\_0\\) is known to be zero and term \\(t\_2\\) is being proven): 94 | 95 | \\[ 96 | \begin{aligned} 97 | &\tilde{t}\_1, \tilde{t}\_3, \tilde{t}\_4, \tilde{t}\_5, \tilde{t}\_6 \\;{\xleftarrow{\\$}}\\; \mathbb Z\_p \\\\ 98 | &T_i \gets \operatorname{Com}(t_i, {\tilde{t}\_i}) = t\_i \cdot B + {\tilde{t}\_i} \cdot {\widetilde{B}} \\\\ 99 | \end{aligned} 100 | \\] 101 | 102 | The prover adds \\(T_1, T_3, T_4, T_5, T_6\\) to the protocol transcript 103 | and obtains the challenge scalars \\(u,x \in {\mathbb Z\_p}\\) from the transcript. 104 | 105 | Using the concrete values \\(u, x\\), the prover computes 106 | the synthetic blinding factors \\({\tilde{t}}(x)\\) and \\(\tilde{e}\\): 107 | 108 | \\[ 109 | \begin{aligned} 110 | \tilde{t}\_2 &\gets \langle \mathbf{w}\_V, \tilde{\mathbf{v}} \rangle \\\\ 111 | {\tilde{t}}(x) &\gets \sum\_{i = 1}^{6} x^i \tilde{t}\_{i} \\\\ 112 | {\tilde{e}} &\gets (\tilde{a}' + u \tilde{a}'') \cdot x + (\tilde{o}' + u \tilde{o}'') \cdot x^2 + (\tilde{s}' + u \tilde{s}'') \cdot x^3 \\\\ 113 | \end{aligned} 114 | \\] 115 | 116 | The prover adds \\(t(x), {\tilde{t}}(x), {\tilde{e}}\\) to the protocol transcript, obtains a challenge scalar \\(w \in {\mathbb Z\_p}\\), and uses it to create a point \\(Q\\): 117 | 118 | \\[ 119 | Q \gets w \cdot B 120 | \\] 121 | 122 | The prover evaluates polynomials \\(\mathbf{l}(x), \mathbf{r}(x)\\) and 123 | [pads them to the next power of two](#padding-mathbflx-and-mathbfrx-for-the-inner-product-proof) \\(n \rightarrow n^{+}\\): 124 | 125 | \\[ 126 | \begin{aligned} 127 | n^{+} &\gets 2^{\lceil \log_2 n \rceil} \\\\ 128 | \mathbf{l}^{+} &\gets \mathbf{l}(x) \hspace{0.1cm} || \hspace{0.1cm} \mathbf{0} \\\\ 129 | \mathbf{r}^{+} &\gets \mathbf{r}(x) \hspace{0.1cm} || \hspace{0.1cm} [-y^n,...,-y^{n^{+}-1}] 130 | \end{aligned} 131 | \\] 132 | 133 | The prover transmutes generators using challenges \\(y\\) and \\(u\\): 134 | 135 | \\[ 136 | \begin{aligned} 137 | \hat{\mathbf{G}} &\gets \mathbf{G}' || (u \cdot \mathbf{G}'') \\\\ 138 | \hat{\mathbf{H}} &\gets \mathbf{y}^{-n} \circ \big( \mathbf{H}' || (u \cdot \mathbf{H}'') \big) \\\\ 139 | \end{aligned} 140 | \\] 141 | 142 | The prover also takes a larger slice of the generators \\(\mathbf{G}, \mathbf{H}\\): 143 | 144 | \\[ 145 | \begin{aligned} 146 | \hat{\mathbf{G}}^{+} &\gets \hat{\mathbf{G}} \hspace{0.1cm} || \hspace{0.1cm} u \cdot [G_n,...,G_{n^{+}-1}] \\\\ 147 | \hat{\mathbf{H}}^{+} &\gets \hat{\mathbf{H}} \hspace{0.1cm} || \hspace{0.1cm} u \cdot [y^{-n} H_n,..., y^{-(n^{+}-1)} H_{n^{+}-1}] \\\\ 148 | \end{aligned} 149 | \\] 150 | 151 | Finally, the prover performs the [inner product argument](../inner_product_proof/index.html) to prove the relation: 152 | \\[ 153 | \operatorname{PK}\left\\{ 154 | (\hat{\mathbf{G}}^{+}, \hat{\mathbf{H}}^{+} \in {\mathbb G}^{n^{+}}, P', Q \in {\mathbb G}; \mathbf{l}^{+}, \mathbf{r}^{+} \in {\mathbb Z\_p}^{n^{+}}) 155 | : P' = {\langle \mathbf{l}^{+}, \hat{\mathbf{G}}^{+} \rangle} + {\langle \mathbf{r}^{+}, \hat{\mathbf{H}}^{+} \rangle} + {\langle \mathbf{l}^{+}, \mathbf{r}^{+} \rangle} Q 156 | \right\\} 157 | \\] 158 | 159 | The result of the inner product proof is a list of \\(2k\\) points and \\(2\\) scalars, where \\(k = \lceil \log_2(n) \rceil\\): \\(\\{L\_k, R\_k, \\dots, L\_1, R\_1, a, b\\}\\). 160 | 161 | The complete proof consists of \\(16+2k\\) 32-byte elements: 162 | \\[ 163 | \\{A\_I', A\_O', S', A\_I'', A\_O'', S'', T\_1, T\_3, T\_4, T\_5, T\_6, t(x), {\tilde{t}}(x), \tilde{e}, L\_k, R\_k, \\dots, L\_1, R\_1, a, b\\} 164 | \\] 165 | 166 | 167 | 168 | Verifier’s algorithm 169 | -------------------- 170 | 171 | The input to the verifier is the aggregated proof, which contains the \\(m\\) value commitments \\(V_{(j)}\\), 172 | and \\(32 \cdot (16 + 2 k)\\) bytes of the proof data where \\(k = \lceil \log_2(n) \rceil\\) and \\(n\\) is a number of [multiplication gates](#multiplication-gates): 173 | 174 | \\[ 175 | \\{A\_I', A\_O', S', A\_I'', A\_O'', S'', T\_1, T\_3, T\_4, T\_5, T\_6, t(x), {\tilde{t}}(x), \tilde{e}, L\_k, R\_k, \\dots, L\_1, R\_1, a, b\\} 176 | \\] 177 | 178 | The verifier starts by adding all value commitments \\(V_i\\) to the protocol transcript. 179 | 180 | The verifier then [builds constraints](#building-constraints) in two phases. 181 | 182 | In the first phase, the verifier allocates \\(n'\\) multiplication gates and the first set of constraints without using challenges. 183 | 184 | Then, the verifier uses the Fiat-Shamir transform to generate challenges required by the gadgets 185 | by adding the intermediate commitments \\(A_I', A_O', S'\\) to the protocol transcript. 186 | 187 | In the second phase, the verifier allocates additional \\(n''\\) multiplication gates and the second set of constraints, 188 | providing necessary challenges to the gadgets that form the constraint system. 189 | 190 | The verifier obtains more challenges by adding the appropriate data sequentially to the protocol transcript: 191 | 192 | 1. \\(A_I'', A_O'', S''\\) are added to obtain challenge scalars \\(y,z \in {\mathbb Z\_p}\\), 193 | 2. \\(T_1, T_3, T_4, T_5, T_6\\) are added to obtain a challenge scalars \\(u,x \in {\mathbb Z\_p}\\), 194 | 3. \\(t(x), {\tilde{t}}(x), \tilde{e}\\) are added to obtain a challenge \\(w \in {\mathbb Z\_p}\\). 195 | 196 | The verifier flattens constraints: 197 | 198 | \\[ 199 | \begin{aligned} 200 | \mathbf{w}\_L &\gets z \mathbf{z}^q \cdot \mathbf{W}\_L, \\\\ 201 | \mathbf{w}\_R &\gets z \mathbf{z}^q \cdot \mathbf{W}\_R, \\\\ 202 | \mathbf{w}\_O &\gets z \mathbf{z}^q \cdot \mathbf{W}\_O, \\\\ 203 | \mathbf{w}\_V &\gets z \mathbf{z}^q \cdot \mathbf{W}\_V, \\\\ 204 | w\_c &\gets \langle z \mathbf{z}^q, \mathbf{c} \rangle, 205 | \end{aligned} 206 | \\] 207 | where each of \\(\mathbf{w}\_L, \mathbf{w}\_R, \mathbf{w}\_O\\) has length \\(n\\) and \\(\mathbf{w}\_V\\) has length \\(m\\). 208 | 209 | The verifier [pads the proof data](#padding-mathbflx-and-mathbfrx-for-the-inner-product-proof) 210 | by taking a larger slice of the generators \\(\mathbf{G},\mathbf{H}\\) and more powers of challenges \\(y\\) up to \\((n^{+}-1)\\): 211 | 212 | \\[ 213 | \begin{aligned} 214 | n^{+} &\gets 2^{\lceil \log_2 n \rceil} \\\\ 215 | \mathbf{G}^{+} &\gets \mathbf{G} \hspace{0.1cm} || \hspace{0.1cm} [G_n,...,G_{n^{+}-1}] \\\\ 216 | \mathbf{H}^{+} &\gets \mathbf{H} \hspace{0.1cm} || \hspace{0.1cm} [H_n,...,H_{n^{+}-1}] \\\\ 217 | \mathbf{y}^{n^{+}} &\gets \mathbf{y}^n \hspace{0.1cm} || \hspace{0.1cm} [y^n,...,y^{n^{+}-1}] \\\\ 218 | \end{aligned} 219 | \\] 220 | 221 | The verifier computes the following scalars for the [inner product argument](../inner_product_proof/index.html): 222 | 223 | \\[ 224 | \\{u\_{1}^{2}, \dots, u\_{k}^{2}, u\_{1}^{-2}, \dots, u\_{k}^{-2}, s_0, \dots, s_{n^{+}-1}\\} 225 | \\] 226 | 227 | The goal of the verifier is to check two equations. 228 | 229 | **First**, verify the second term of the polynomial \\(t(x)\\) (see [notes](#proving-that-t_2-is-correct)): 230 | 231 | \\[ 232 | \begin{aligned} 233 | t(x) B + {\tilde{t}}(x) {\widetilde{B}} 234 | &\stackrel{?}{=} 235 | x^2 \langle \mathbf{w}\_V, \mathbf{V} \rangle + x^2 \big(w\_c + \delta(y,z)\big) B + \sum\_{i = 1,3,4,5,6} x^i T\_{i},\\\\ 236 | \delta(y, z) &= \langle \mathbf{y}^{-n} \circ \mathbf{w}\_R, \mathbf{w}\_L \rangle \\\\ 237 | \end{aligned} 238 | \\] 239 | 240 | If we rewrite the check as a comparison with the identity point, we get: 241 | \\[ 242 | 0 \stackrel{?}{=} x^2 \langle \mathbf{w}\_V, \mathbf{V} \rangle + x^2 \big(w\_c + \delta(y,z)\big) B + \sum\_{i = 1,3,4,5,6} x^i T\_{i} - t(x) B - {\tilde{t}}(x) {\widetilde{B}} 243 | \\] 244 | 245 | **Second**, verify the inner product argument for the vectors \\(\mathbf{l}(x), \mathbf{r}(x)\\) that form the \\(t(x)\\) (see [inner-product protocol](../inner_product_proof/index.html#verification-equation)) 246 | 247 | \\[ 248 | P' \overset ? = {\langle a \cdot \mathbf{s}, \hat{\mathbf{G}}^{+} \rangle} + {\langle b/\mathbf{s}, \hat{\mathbf{H}}^{+} \rangle} + abQ - \sum\_{j=1}^{k} \left( L\_{j} u\_{j}^{2} + u\_{j}^{-2} R\_{j} \right), 249 | \\] 250 | where 251 | \\[ 252 | \begin{aligned} 253 | \hat{\mathbf{G}}^{+} &= \mathbf{G}' \hspace{0.1cm} || \hspace{0.1cm} u \cdot \mathbf{G}'' \hspace{0.1cm} || \hspace{0.1cm} u \cdot [G_n,...,G_{n^{+}-1}] \\\\ 254 | \hat{\mathbf{H}}^{+} &= \mathbf{y}^{-n^{+}} \circ \big( \mathbf{H}' \hspace{0.1cm} || \hspace{0.1cm} u \cdot \mathbf{H}'' \hspace{0.1cm} || \hspace{0.1cm} u \cdot [H_n,...,H_{n^{+}-1}]\big) \\\\ 255 | \end{aligned} 256 | \\] 257 | 258 | Rewriting as a comparison with the identity point and expanding \\(Q = wB\\) and \\(P' = P^{+} + t(x) wB\\) as [needed for transition to the inner-product protocol](../notes/index.html#inner-product-proof): 259 | 260 | \\[ 261 | 0 \overset ? = P^{+} + t(x) wB - {\langle a \cdot \mathbf{s}, \hat{\mathbf{G}}^{+} \rangle} - {\langle b/\mathbf{s}, \hat{\mathbf{H}}^{+} \rangle} - abwB + \sum\_{j=1}^{k} \left( L\_{j} u\_{j}^{2} + u\_{j}^{-2} R\_{j} \right), 262 | \\] 263 | where the [definition](#proving-that-mathbflx-mathbfrx-are-correct) of \\(P^{+}\\) is: 264 | 265 | \\[ 266 | \begin{aligned} 267 | P^{+} = &-{\widetilde{e}} {\widetilde{B}} + x \cdot (A_I' + u \cdot A_I'') + x^2 \cdot (A_O' + u \cdot A_O'') \\\\ 268 | &-\langle \mathbf{1}, \mathbf{H}' \rangle - u \cdot \langle \mathbf{1}, {\mathbf{H}''} \rangle - u \cdot [H_n,...,H_{n^{+}-1}]\\\\ 269 | &+x \cdot \langle \mathbf{w}\_L, \hat{\mathbf{H}} \rangle + x \cdot \langle \mathbf{w}\_R, \hat{\mathbf{G}} \rangle + \langle \mathbf{w}\_O, \hat{\mathbf{H}} \rangle + 270 | x^3 \cdot (S' + u \cdot S'') 271 | \end{aligned} 272 | \\] 273 | 274 | The verifier combines two equations in one by sampling a random factor \\(r \\; {\xleftarrow{\\$}} \\; {\mathbb Z\_p}\\), 275 | multiplying the first equation by \\(r\\), and adding it with the second equation. 276 | 277 | Finally, verifier groups all scalars by each point and performs a single multiscalar multiplication: 278 | 279 | \\[ 280 | \begin{aligned} 281 | 0 \quad \stackrel{?}{=} & \quad x \cdot A\_I' \\\\ 282 | + & \quad x^2 \cdot A\_O' \\\\ 283 | + & \quad x^3 \cdot S' \\\\ 284 | + & \quad u \cdot x \cdot A\_I'' \\\\ 285 | + & \quad u \cdot x^2 \cdot A\_O'' \\\\ 286 | + & \quad u \cdot x^3 \cdot S'' \\\\ 287 | + & \quad \langle r \cdot x^2 \cdot \mathbf{w}\_V, \mathbf{V} \rangle \\\\ 288 | + & \quad \sum\_{i = 1,3,4,5,6} r \cdot x^i \cdot T\_{i} \\\\ 289 | + & \quad \Big(w \cdot \big(t(x) - a \cdot b\big) + r \cdot \big(x^2 \cdot (w\_c + \delta(y,z)) - t(x)\big) \Big) \cdot B \\\\ 290 | + & \quad (-{\widetilde{e}} - r \cdot {\tilde{t}}(x)) \cdot \widetilde{B} \\\\ 291 | + & \quad {\langle x \cdot \mathbf{y}^{-n^{+}}\_{[0:n']} \circ \mathbf{w}\_R' - a \cdot \mathbf{s}\_{[0:n']}, \mathbf{G}^{+}\_{[0:n']} \rangle}\\\\ 292 | + & \quad {\langle u \cdot \big( x \cdot \mathbf{y}^{-n^{+}}\_{[n':n]} \circ \mathbf{w}\_R'' - a \cdot \mathbf{s}\_{[n':n]} \big), \mathbf{G}^{+}\_{[n':n]} \rangle}\\\\ 293 | + & \quad {\langle -u \cdot a \cdot \mathbf{s}\_{[n:n^{+}]}, \mathbf{G}^{+}\_{[n:n^{+}]} \rangle}\\\\ 294 | + & \quad {\langle -\mathbf{1} + \mathbf{y}^{-n^{+}}\_{[0:n']} \circ (x \mathbf{w}\_L' + \mathbf{w}\_O' - b /\mathbf{s}\_{[0:n']} ), \mathbf{H}^{+}\_{[0:n']} \rangle}\\\\ 295 | + & \quad {\langle u \cdot \big(-\mathbf{1} + \mathbf{y}^{-n^{+}}\_{[n':n]} \circ (x \mathbf{w}\_L'' + \mathbf{w}\_O'' - b /\mathbf{s}\_{[n':n]} ) \big), \mathbf{H}^{+}\_{[n':n]} \rangle}\\\\ 296 | + & \quad {\langle u \cdot \big(-\mathbf{1} + \mathbf{y}^{-n^{+}}\_{[n:n^{+}]} \circ ( -b /\mathbf{s}\_{[n:n^{+}]} ) \big), \mathbf{H}^{+}\_{[n:n^{+}]} \rangle}\\\\ 297 | + & \quad {\langle [u_{1}^2, \dots, u_{k}^2 ], [L_1, \dots, L_{k}] \rangle}\\\\ 298 | + & \quad {\langle [u_{1}^{-2}, \dots, u_{k}^{-2} ], [R_1, \dots, R_{k}] \rangle} 299 | \end{aligned} 300 | \\] where \\(1/{\mathbf{s}}\\) are inverses of \\(\mathbf{s}\\), computed as a [reversed list](../inner_product_proof/index.html#verifiers-algorithm) of \\(\mathbf{s}\\). 301 | 302 | 303 | -------------------------------------------------------------------------------- /docs/inner-product-protocol.md: -------------------------------------------------------------------------------- 1 | The `inner_product_proof` module contains API for producing a compact proof of an inner product of two vectors. 2 | 3 | Inner product argument protocol 4 | =============================== 5 | 6 | These notes explain how the protocol is implemented in the [`InnerProductProof`](struct.InnerProductProof.html) type. 7 | 8 | We want to prove the relation 9 | \\[ 10 | \operatorname{PK}\left\\{ 11 | ({\mathbf{G}}, {\mathbf{H}} \in {\mathbb G}^n, P', Q \in {\mathbb G}; {\mathbf{a}}, {\mathbf{b}} \in {\mathbb Z\_p}^n) 12 | : P' = {\langle {\mathbf{a}}, {\mathbf{G}} \rangle} + {\langle {\mathbf{b}}, {\mathbf{H}} \rangle} + {\langle {\mathbf{a}}, {\mathbf{b}} \rangle} Q 13 | \right\\} 14 | \\] where \\(n = 2^{k}\\) is a power of \\(2\\). 15 | 16 | Prover’s algorithm 17 | ------------------ 18 | 19 | To start, we sketch the 20 | interactive version of this protocol, and then describe the 21 | optimizations discussed in the Bulletproofs paper for the 22 | non-interactive version. 23 | 24 | The protocol consists of \\(k = \lg n\\) rounds, indexed by 25 | \\(j = k,\ldots,1\\). In the \\(j\\)-th round, the prover computes 26 | \\[ 27 | \begin{aligned} 28 | L\_{j} &\gets {\langle {\mathbf{a}}\_{\operatorname{lo}}, {\mathbf{G}}\_{\operatorname{hi}} \rangle} + {\langle {\mathbf{b}}\_{\operatorname{hi}}, {\mathbf{H}}\_{\operatorname{lo}} \rangle} + {\langle {\mathbf{a}}\_{\operatorname{lo}}, {\mathbf{b}}\_{\operatorname{hi}} \rangle} Q, \\\\ 29 | R\_{j} &\gets {\langle {\mathbf{a}}\_{\operatorname{hi}}, {\mathbf{G}}\_{\operatorname{lo}} \rangle} + {\langle {\mathbf{b}}\_{\operatorname{lo}}, {\mathbf{H}}\_{\operatorname{hi}} \rangle} + {\langle {\mathbf{a}}\_{\operatorname{hi}}, {\mathbf{b}}\_{\operatorname{lo}} \rangle} Q, 30 | \end{aligned} 31 | \\] 32 | and sends \\(L\_{j}, R\_{j}\\) to the verifier. The verifier responds with a 33 | challenge value \\(u\_{j} {\xleftarrow{\\$}}{\mathbb{Z}\_p}\\). The prover uses 34 | \\(u\_{j}\\) to compute 35 | \\[ 36 | \begin{aligned} 37 | {\mathbf{a}} &\gets {\mathbf{a}}\_{\operatorname{lo}} \cdot u\_{j} + u\_{j}^{-1} \cdot {\mathbf{a}}\_{\operatorname{hi}}, \\\\ 38 | {\mathbf{b}} &\gets {\mathbf{b}}\_{\operatorname{lo}} \cdot u\_{j}^{-1} + u\_{j} \cdot {\mathbf{a}}\_{\operatorname{hi}}, 39 | \end{aligned} 40 | \\] 41 | the prover and verifier both compute 42 | \\[ 43 | \begin{aligned} 44 | {\mathbf{G}} &\gets {\mathbf{G}}\_{\operatorname{lo}} \cdot u\_{j}^{-1} + u\_{j} \cdot {\mathbf{G}}\_{\operatorname{hi}}, \\\\ 45 | {\mathbf{H}} &\gets {\mathbf{H}}\_{\operatorname{lo}} \cdot u\_{j} + u\_{j}^{-1} \cdot {\mathbf{H}}\_{\operatorname{hi}}, 46 | \end{aligned} 47 | \\] 48 | and use these vectors (all of length \\(2^{j-1}\\)) for the next round. 49 | After the last (\\(j = 1\\)) round, the prover sends 50 | \\(a, b = {\mathbf{a}}\_{0}, {\mathbf{b}}\_{0}\\) to the verifier, who accepts 51 | if and only if 52 | \\[ 53 | \begin{aligned} 54 | L\_{1} u\_{1}^{2} + \cdots + L\_{k} u\_{k}^{2} + P' + R\_{k} u\_{k}^{-2} + \cdots + R\_{1} u\_{1}^{-2}&\overset ? = aG + bH + abQ, 55 | \end{aligned} 56 | \\] 57 | where \\(G, H = {\mathbf{G}}\_{0}, {\mathbf{H}}\_{0}\\). 58 | 59 | To make the protocol noninteractive, we replace the transmission of the 60 | \\(L\_{j}\\) and \\(R\_{j}\\) and the response \\(u\_{j}\\) with a Fiat-Shamir 61 | challenge, so that each \\(u\_{j}\\) is generated as a hash of the transcript 62 | \\(L\_{k},R\_{k},\ldots,L\_{j},R\_{j}\\). At the end of the prover’s 63 | computation, they send \\(a,b,L\_{k},R\_{k},\ldots,L\_{1},R\_{1}\\) to the 64 | verifier. 65 | 66 | Verifier’s algorithm 67 | -------------------- 68 | 69 | Since the final \\(G\\) and \\(H\\) values are functions of the challenges 70 | \\(u\_{k},\ldots,u\_{1}\\), the verifier has to compute them as part of the 71 | verification process. However, while the prover needs to compute the 72 | intermediate vectors \\({\mathbf{G}}\\), \\({\mathbf{H}}\\) in order to compute 73 | the \\(L\_{j}\\) and \\(R\_{j}\\), the verifier doesn’t, and can compute the final 74 | \\(G\\), \\(H\\) directly from the vectors \\({\mathbf{G}}\\), \\({\mathbf{H}}\\) and 75 | the challenges \\(u\_{k}, \ldots, u\_{1}\\). 76 | 77 | Let \\({\mathbf{G}}^{(j)}\\) be the value of \\({\mathbf{G}}\\) in the \\(j\\)-th 78 | round, and let \\(G\_{i}\\) be the \\(i\\)-th entry of the initial vector 79 | \\({\mathbf{G}}^{(k)} = 80 | (G\_{0}, \ldots, G\_{n-1})\\). We have \\[ 81 | \begin{aligned} 82 | {\mathbf{G}}^{(j-1)} = ({\mathbf{G}}^{(j)})\_{\operatorname{lo}} u\_{j}^{-1} + ({\mathbf{G}}^{(j)})\_{\operatorname{hi}} u\_{j},\end{aligned} 83 | \\] 84 | so the coefficient of \\(G\_{i}\\) in the final \\(G\\) value is 85 | \\[ 86 | \begin{aligned} 87 | s\_{i} &= u\_{k}^{b(i,k)} \cdots u\_1^{b(i,1)},\end{aligned} 88 | \\] where 89 | \\(b(i,j)\\) is either \\(-1\\) or \\(+1\\), according to whether \\(G\_{i}\\) appears in 90 | the left or right half of \\({\mathbf{G}}^{(j)}\\). Since \\(G\_{i}\\) appears in 91 | the \\((i \mod 2^{j})\\)-th entry of \\({\mathbf{G}}^{j}\\), this is 92 | \\[ 93 | b(i,j) = 94 | \begin{cases} 95 | -1 & \text{if $(i \mod 2^{j}) < 2^{j-1}$ }\\\\ 96 | +1 & \text{if $(i \mod 2^{j}) \ge 2^{j-1}$ }\\\\ 97 | \end{cases}. 98 | \\] 99 | But this is exactly 100 | \\[ 101 | b(i,j) = 102 | \begin{cases} 103 | -1 & \text{if bit $j-1$ of $i$ is 0} \\\\ 104 | +1 & \text{if bit $j-1$ of $i$ is 1} \\\\ 105 | \end{cases}. 106 | \\] 107 | This shows that 108 | \\(G = {\langle {\mathbf{s}}, {\mathbf{G}} \rangle}\\). This formula differs 109 | slightly from the one in the paper, because we index vectors and bits 110 | from \\(0\\). 111 | 112 | Since \\(H\\) is computed similarly, but with the roles of 113 | \\({\mathbf{H}}\_{\operatorname{lo}}\\) and 114 | \\({\mathbf{H}}\_{\operatorname{hi}}\\) reversed, a similar argument shows 115 | that \\(H = {\langle 1/{\mathbf{s}}, {\mathbf{H}} \rangle}\\). 116 | 117 | Notice that if \\(i'\\) is the bitwise \\(\texttt{NOT}\\) of \\(i\\), then \\(s\_{i'} = 1/s\_{i}\\), 118 | and as \\(i\\) runs from \\(0\\) to \\((2^k - 1)\\), \\(i'\\) runs from \\((2^k - 1)\\) to \\(0\\), 119 | so the vector of inverses \\(1/{\mathbf{s}}\\) is a reversed 120 | vector \\({\mathbf{s}}\\) and no additional computation is required to 121 | obtain the \\(1/s\_{i}\\). 122 | 123 | ### Verification equation 124 | 125 | The verifier’s computation then becomes 126 | \\[ 127 | \begin{aligned} 128 | P' \overset ? =& aG +bH +abQ - \sum\_{j=1}^{k} \left( L\_{j} u\_{j}^{2} + u\_{j}^{-2} R\_{j} \right) \\\\ 129 | =& {\langle a \cdot {\mathbf{s}}, {\mathbf{G}} \rangle} + {\langle b /{\mathbf{s}}, {\mathbf{H}} \rangle} + abQ - \sum\_{j=1}^{k} \left( L\_{j} u\_{j}^{2} + u\_{j}^{-2} R\_{j} \right), 130 | \end{aligned} 131 | \\] 132 | a single multiscalar multiplication with 133 | \\(n + n + 1 + k + k = 2(n+k) + 1\\) points. 134 | 135 | In order to combine the computation above with other checks in a parent protocol, we can provide these scalars: 136 | 137 | \\[ 138 | \\{u\_{1}^{2}, \dots, u\_{k}^{2}, u\_{1}^{-2}, \dots, u\_{k}^{-2}, s_0, \dots, s_{n-1}\\}. 139 | \\] 140 | 141 | Use the [`InnerProductProof::verification_scalars`](struct.InnerProductProof.html#method.verification_scalars) method to produce these scalars for a given inner product proof. 142 | -------------------------------------------------------------------------------- /docs/notes-intro.md: -------------------------------------------------------------------------------- 1 | This module contains notes on how and why Bulletproofs work. 2 | 3 | These notes are organized as follows: 4 | 5 | Table of Contents 6 | ================= 7 | 8 | These notes explain how and why the proofs work: 9 | 10 | * [Inner product proof](::notes::inner_product_proof) 11 | * [Range proof](::notes::range_proof) 12 | * [Rank-1 constraint system proof](::notes::r1cs_proof) 13 | 14 | The description of what the protocols actually do is contained in these notes: 15 | 16 | * [`range_proof`](::range_proof): aggregated range proof protocol. 17 | * [`range_proof_mpc`](::range_proof_mpc): multi-party API for range proof aggregation. 18 | * [`inner_product_proof`](::inner_product_proof): inner product argument protocol. 19 | * [`r1cs`](::r1cs::notes): constraint system proof protocol. 20 | 21 | The types from the above modules are publicly re-exported from the crate root, 22 | so that the external documentation describes how to use the API, while the internal 23 | documentation describes how it works. 24 | 25 | (FIXME Streamline module structure?) 26 | 27 | Notation 28 | ======== 29 | 30 | We change notation from the original [Bulletproofs paper][bulletproofs_paper]. 31 | The primary motivation is that our implementation uses additive notation, and 32 | we would like our description of the protocol to use the same notation as the 33 | implementation. 34 | 35 | In general, we use lower-case letters 36 | \\(a, b, c\\) 37 | for scalars in 38 | \\({\mathbb Z\_p}\\) 39 | and upper-case letters 40 | \\(G,H,P,Q\\) 41 | for group elements in 42 | \\({\mathbb G}\\). 43 | Vectors are denoted as \\({\mathbf{a}}\\) and \\({\mathbf{G}}\\), 44 | and the inner product of two vectors is denoted by 45 | \\({\langle -, - \rangle}\\). Notice that 46 | \\({\langle {\mathbf{a}}, {\mathbf{b}} \rangle} \in {\mathbb Z\_p}\\) 47 | produces a scalar, while 48 | \\({\langle {\mathbf{a}}, {\mathbf{G}} \rangle} \in {\mathbb G}\\) 49 | is a multiscalar multiplication. The vectors of all \\(0\\) and all \\(1\\) are 50 | denoted by \\({\mathbf{0}}\\), \\({\mathbf{1}}\\) respectively. 51 | 52 | Vectors are indexed starting from \\(0\\), unlike the paper, which indexes 53 | from \\(1\\). For a scalar \\(y\\), we write 54 | \\[ 55 | \begin{aligned} 56 | {\mathbf{y}}^{n} &= (1,y,y^{2},\ldots,y^{n-1}) 57 | \end{aligned} 58 | \\] 59 | for the vector whose \\(i\\)-th entry is \\(y^{i}\\). For vectors 60 | \\({\mathbf{v}}\\) of even 61 | length \\(2k\\), we define \\({\mathbf{v}}\_{\operatorname{lo}}\\) and 62 | \\({\mathbf{v}}\_{\operatorname{hi}}\\) to be the low and high halves of 63 | \\({\mathbf{v}}\\): 64 | \\[ 65 | \begin{aligned} 66 | {\mathbf{v}}\_{\operatorname{lo}} &= (v\_0, \ldots, v\_{k-1})\\\\ 67 | {\mathbf{v}}\_{\operatorname{hi}} &= (v\_{k}, \ldots, v\_{2k-1}) 68 | \end{aligned} 69 | \\] 70 | Pedersen commitments are written as 71 | \\[ 72 | \begin{aligned} 73 | \operatorname{Com}(v) &= \operatorname{Com}(v, {\widetilde{v}}) = v \cdot B + {\widetilde{v}} \cdot {\widetilde{B}}, 74 | \end{aligned} 75 | \\] 76 | where \\(B\\) and \\({\widetilde{B}}\\) are the generators used for the values 77 | and blinding factors, respectively. We denote the blinding factor for 78 | the value \\(v\\) by \\({\widetilde{v}}\\), so that it is clear which blinding 79 | factor corresponds to which value, and write \\(\operatorname{Com}(v)\\) 80 | instead of \\(\operatorname{Com}(v, {\widetilde{v}})\\) for brevity. 81 | 82 | We also make use of *vector Pedersen commitments*, which we define for 83 | pairs of vectors as \\[ 84 | \begin{aligned} 85 | \operatorname{Com}({\mathbf{a}}\_{L}, {\mathbf{a}}\_{R}) 86 | &= \operatorname{Com}({\mathbf{a}}\_{L}, {\mathbf{a}}\_{R}, {\widetilde{a}}) 87 | = {\langle {\mathbf{a}}\_{L}, {\mathbf{G}} \rangle} + {\langle {\mathbf{a}}\_{R}, {\mathbf{H}} \rangle} + {\widetilde{a}} {\widetilde{B}},\end{aligned} 88 | \\] 89 | where \\({\mathbf{G}}\\) and \\({\mathbf{H}}\\) are vectors of generators. 90 | Notice that this is exactly the same as taking a commitment to the 91 | vector of values \\({\mathbf{a}}\_{L} \Vert {\mathbf{a}}\_{R}\\) with the 92 | vector of bases \\({\mathbf{G}} \Vert {\mathbf{H}}\\), but defining the 93 | commitment on pairs of vectors is a more convenient notation. 94 | 95 | The variable renaming is as follows: 96 | \\[ 97 | \begin{aligned} 98 | g &\xrightarrow{} B & \gamma &\xrightarrow{} \tilde{v} \\\\ 99 | h &\xrightarrow{} \widetilde{B} & \alpha &\xrightarrow{} \tilde{a} \\\\ 100 | {\mathbf{g}} &\xrightarrow{} {\mathbf{G}} & \rho &\xrightarrow{} \tilde{s} \\\\ 101 | {\mathbf{h}} &\xrightarrow{} {\mathbf{H}} & \tau\_i &\xrightarrow{} \tilde{t}\_i \\\\ 102 | & & \mu &\xrightarrow{} \tilde{e} \\\\ 103 | \end{aligned} 104 | \\] 105 | 106 | 107 | [bulletproofs_paper]: https://eprint.iacr.org/2017/1066.pdf 108 | [bp_website]: https://crypto.stanford.edu/bulletproofs/ 109 | -------------------------------------------------------------------------------- /docs/notes-ipp.md: -------------------------------------------------------------------------------- 1 | This module contains notes on how and why the inner product proof protocol works. 2 | 3 | Inner product proof 4 | =================== 5 | 6 | First, let’s observe that the prover can simply send vectors 7 | \\({\mathbf{l}}(x)\\) and \\({\mathbf{r}}(x)\\) and the verifier can check 8 | directly that the inner product \\(t(x)\\) and commitment \\(P\\) provided in 9 | the protocols 1 and 2 are correct. This will not leak information (the 10 | secret bits in these vectors are blinded), but will require us to 11 | transfer \\(2n\\) scalars between a prover and a verifier. 12 | 13 | To minimize the bandwidth cost we will use the inner-product argument 14 | protocol which enables us to prove *indirectly* and with \\(O(log(n))\\) 15 | communication cost, that a given inner product \\(t(x)\\) and a commitment 16 | \\(P\\) are related as: 17 | \\[ 18 | \begin{aligned} 19 | t(x) &= {\langle {\mathbf{l}}(x), {\mathbf{r}}(x) \rangle} \\\\ 20 | P &= {\langle {\mathbf{l}}(x), {\mathbf{G}} \rangle} + {\langle {\mathbf{r}}(x), {\mathbf{H}}' \rangle} 21 | \end{aligned} 22 | \\] 23 | To make the presentation 24 | cleaner, we will change the notation to one used specifically in the 25 | inner product argument which is not to be confused with the notation in 26 | the rangeproof protocol: 27 | \\[ 28 | \begin{aligned} 29 | {\mathbf{a}}, {\mathbf{b}} &\in {\mathbb Z\_{p}^{n}}\\\\ 30 | {\mathbf{G}}, {\mathbf{H}} &\in {\mathbb G^{n}}\\\\ 31 | c &= {\langle {\mathbf{a}}, {\mathbf{b}} \rangle}\\\\ 32 | P &= {\langle {\mathbf{a}}, {\mathbf{G}} \rangle} + {\langle {\mathbf{b}}, {\mathbf{H}} \rangle} 33 | \end{aligned} 34 | \\] 35 | Within the above definitions we need a proof of knowledge 36 | for the following relation: 37 | \\[ 38 | \begin{aligned} 39 | P &{}={}&& {\langle {\mathbf{a}}, {\mathbf{G}} \rangle} + {\langle {\mathbf{b}}, {\mathbf{H}} \rangle} \hspace{0.2cm} \wedge\\\\ 40 | c &{}={}&& {\langle {\mathbf{a}}, {\mathbf{b}} \rangle} 41 | \end{aligned} 42 | \\] 43 | Let’s combine these two statements into one equation using an 44 | indeterminate variable \\(w \in {\mathbb Z\_{p}^{\times}}\\) and multiplying the 45 | second equation by an orthogonal generator 46 | \\(B \in {\mathbb G}\\): 47 | \\[ 48 | \begin{aligned} 49 | P &{}={}&& {\langle {\mathbf{a}}, {\mathbf{G}} \rangle} + {\langle {\mathbf{b}}, {\mathbf{H}} \rangle}\\\\ 50 | &{}+{}&&\\\\ 51 | c w B &{}={}&& {\langle {\mathbf{a}}, {\mathbf{b}} \rangle} w B 52 | \end{aligned} 53 | \\] 54 | Let’s simplify the resulting equation using the following definitions: 55 | \\[ 56 | \begin{aligned} 57 | k &= \lg n \\\\ 58 | P' &= P + cwB \\\\ 59 | Q &= wB 60 | \end{aligned} 61 | \\] 62 | The equation becomes: 63 | \\[ 64 | P' = {\langle {\mathbf{a}}, {\mathbf{G}} \rangle} + {\langle {\mathbf{b}}, {\mathbf{H}} \rangle} + {\langle {\mathbf{a}}, {\mathbf{b}} \rangle} Q 65 | \\] 66 | The combined equation is useful because it will allow us 67 | to compress each vector in half and arrive to the same form. By doing 68 | such compression \\(\lg n\\) times we will end up with an equation where 69 | both vectors are one-element long and we can simply transmit them to 70 | check the final equality directly. 71 | 72 | If the prover can demonstrate that the above \\(P'\\) has such structure 73 | over generators \\({\mathbf{G}}\\), \\({\mathbf{H}}\\) and \\(Q\\) for all 74 | \\(w \in {\mathbb Z\_{p}^{\*}}\\), then the original \\(P\\) and \\(c\\) must satisfy 75 | the original relation 76 | \\((P = {\langle {\mathbf{a}}, {\mathbf{G}} \rangle} + {\langle {\mathbf{b}}, {\mathbf{H}} \rangle} 77 | \wedge c = {\langle {\mathbf{a}}, {\mathbf{b}} \rangle})\\). 78 | 79 | Let’s introduce an indeterminate variable \\(u\_k \in {\mathbb Z\_{p}^{\times}}\\) 80 | and compress the vectors by adding the left and the right halves 81 | separated by the variable \\(u\_k\\): 82 | \\[ 83 | \begin{aligned} 84 | {\mathbf{a}}^{(k-1)} &= {\mathbf{a}}\_{\operatorname{lo}} \cdot u\_k + u^{-1}\_k \cdot {\mathbf{a}}\_{\operatorname{hi}} \\\\ 85 | {\mathbf{b}}^{(k-1)} &= {\mathbf{b}}\_{\operatorname{lo}} \cdot u^{-1}\_k + u\_k \cdot {\mathbf{b}}\_{\operatorname{hi}} \\\\ 86 | {\mathbf{G}}^{(k-1)} &= {\mathbf{G}}\_{\operatorname{lo}} \cdot u^{-1}\_k + u\_k \cdot {\mathbf{G}}\_{\operatorname{hi}} \\\\ 87 | {\mathbf{H}}^{(k-1)} &= {\mathbf{H}}\_{\operatorname{lo}} \cdot u\_k + u^{-1}\_k \cdot {\mathbf{H}}\_{\operatorname{hi}} 88 | \end{aligned} 89 | \\] 90 | The powers of \\(u\_k\\) are chosen so they cancel out in the 91 | inner products of interest as will be shown below. 92 | 93 | Let \\(P\_k = P'\\) and define \\(P\_{k-1}\\) using the same equation as for \\(P\_k\\), but using the compressed vectors: 94 | \\[ 95 | P\_{k-1} = {\langle {\mathbf{a}}^{(k-1)}, {\mathbf{G}}^{(k-1)} \rangle} + {\langle {\mathbf{b}}^{(k-1)}, {\mathbf{H}}^{(k-1)} \rangle} + {\langle {\mathbf{a}}^{(k-1)}, {\mathbf{b}}^{(k-1)} \rangle} \cdot Q 96 | \\] 97 | Expanding it in terms of the original \\({\mathbf{a}}\\), \\({\mathbf{b}}\\), 98 | \\({\mathbf{G}}\\) and \\({\mathbf{H}}\\) gives: 99 | \\[ 100 | \begin{aligned} 101 | P\_{k-1} &{}={}& &{\langle {\mathbf{a}}\_{\operatorname{lo}} \cdot u\_k + u\_k^{-1} \cdot {\mathbf{a}}\_{\operatorname{hi}}, {\mathbf{G}}\_{\operatorname{lo}} \cdot u^{-1}\_k + u\_k \cdot {\mathbf{G}}\_{\operatorname{hi}} \rangle} + \\\\ 102 | && &{\langle {\mathbf{b}}\_{\operatorname{lo}} \cdot u^{-1}\_k + u\_k \cdot {\mathbf{b}}\_{\operatorname{hi}}, {\mathbf{H}}\_{\operatorname{lo}} \cdot u\_k + u^{-1}\_k \cdot {\mathbf{H}}\_{\operatorname{hi}} \rangle} + \\\\ 103 | && &{\langle {\mathbf{a}}\_{\operatorname{lo}} \cdot u\_k + u^{-1}\_k \cdot {\mathbf{a}}\_{\operatorname{hi}}, {\mathbf{b}}\_{\operatorname{lo}} \cdot u^{-1}\_k + u\_k \cdot {\mathbf{b}}\_{\operatorname{hi}} \rangle} \cdot Q 104 | \end{aligned} 105 | \\] 106 | Breaking down in simpler products: 107 | \\[ 108 | \begin{aligned} 109 | P\_{k-1} &{}={}& &{\langle {\mathbf{a}}\_{\operatorname{lo}}, {\mathbf{G}}\_{\operatorname{lo}} \rangle} + {\langle {\mathbf{a}}\_{\operatorname{hi}}, {\mathbf{G}}\_{\operatorname{hi}} \rangle} &{}+{}& u\_k^2 {\langle {\mathbf{a}}\_{\operatorname{lo}}, {\mathbf{G}}\_{\operatorname{hi}} \rangle} + u^{-2}\_k {\langle {\mathbf{a}}\_{\operatorname{hi}}, {\mathbf{G}}\_{\operatorname{lo}} \rangle} + \\\\ 110 | && &{\langle {\mathbf{b}}\_{\operatorname{lo}}, {\mathbf{H}}\_{\operatorname{lo}} \rangle} + {\langle {\mathbf{b}}\_{\operatorname{hi}}, {\mathbf{H}}\_{\operatorname{hi}} \rangle} &{}+{}& u^2\_k {\langle {\mathbf{b}}\_{\operatorname{hi}}, {\mathbf{H}}\_{\operatorname{lo}} \rangle} + u^{-2}\_k {\langle {\mathbf{b}}\_{\operatorname{lo}}, {\mathbf{H}}\_{\operatorname{hi}} \rangle} + \\\\ 111 | && &({\langle {\mathbf{a}}\_{\operatorname{lo}}, {\mathbf{b}}\_{\operatorname{lo}} \rangle} + {\langle {\mathbf{a}}\_{\operatorname{hi}}, {\mathbf{b}}\_{\operatorname{hi}} \rangle})\cdot Q &{}+{}& (u^2\_k {\langle {\mathbf{a}}\_{\operatorname{lo}}, {\mathbf{b}}\_{\operatorname{hi}} \rangle} + u^{-2}\_k {\langle {\mathbf{a}}\_{\operatorname{hi}}, {\mathbf{b}}\_{\operatorname{lo}} \rangle}) \cdot Q 112 | \end{aligned} 113 | \\] 114 | We now see that the left two columns in the above equation is the 115 | definition of \\(P\_k\\), while various cross terms on the right are 116 | separated from \\(P\_k\\) by an indeterminate variable \\(u\_k\\). Let’s group all 117 | terms with \\(u^2\_k\\) as \\(L\_k\\) and all terms with \\(u^{-2}\_k\\) as \\(R\_k\\): 118 | \\[ 119 | \begin{aligned} 120 | P\_{k-1} &= P\_k + u^2\_k \cdot L\_k + u^{-2}\_k \cdot R\_k\\\\ 121 | L\_k &= {\langle {\mathbf{a}}\_{\operatorname{lo}}, {\mathbf{G}}\_{\operatorname{hi}} \rangle} + {\langle {\mathbf{b}}\_{\operatorname{hi}}, {\mathbf{H}}\_{\operatorname{lo}} \rangle} + {\langle {\mathbf{a}}\_{\operatorname{lo}}, {\mathbf{b}}\_{\operatorname{hi}} \rangle} \cdot Q\\\\ 122 | R\_k &= {\langle {\mathbf{a}}\_{\operatorname{hi}}, {\mathbf{G}}\_{\operatorname{lo}} \rangle} + {\langle {\mathbf{b}}\_{\operatorname{lo}}, {\mathbf{H}}\_{\operatorname{hi}} \rangle} + {\langle {\mathbf{a}}\_{\operatorname{hi}}, {\mathbf{b}}\_{\operatorname{lo}} \rangle} \cdot Q 123 | \end{aligned} 124 | \\] 125 | If the prover commits to \\(L\_k\\) and \\(R\_k\\) before \\(u\_k\\) is randomly 126 | sampled, then if the statement about compressed vectors is proven to be 127 | true, it will follow that the original statement about uncompressed vectors 128 | is also true with an overwhelming probability. 129 | 130 | We can compress the resulting statement about \\(P\_{k-1}\\) using one more indeterminate 131 | variable \\(u\_{k-1}\\) in the same way as we used \\(u\_k\\) and arrive 132 | to even shorter vectors. We will continue doing so until we end up with 133 | vectors 134 | \\({\mathbf{a}}^{(0)}, {\mathbf{b}}^{(0)}, {\mathbf{G}}^{(0)}, {\mathbf{H}}^{(0)}\\), 135 | each containing one item, and \\(P\_0\\) containing all accumulated cross-terms at each step: 136 | \\[ 137 | \begin{aligned} 138 | P\_0 &= a^{(0)}\_0 G^{(0)}\_0 + b^{(0)}\_0 H^{(0)}\_0 + a^{(0)}\_0 b^{(0)}\_0 Q\\\\ 139 | P\_0 &= P\_k + \sum\_{j=1}^{k} \left( L\_{j} u\_{j}^{2} + u\_{j}^{-2} R\_{j} \right) 140 | \end{aligned} 141 | \\] 142 | 143 | Rewriting the above with the definitions \\(P\_k = P' = P + cwB\\) and \\(Q = wB\\) gives the 144 | final statement: 145 | \\[ 146 | P + c w B = a^{(0)}\_0 G^{(0)}\_0 + b^{(0)}\_0 H^{(0)}\_0 + a^{(0)}\_0 b^{(0)}\_0 wB - \sum\_{j=1}^{k} \left( L\_{j} u\_{j}^{2} + u\_{j}^{-2} R\_{j} \right) 147 | \\] 148 | 149 | At this point the prover can transmit two scalars \\(a^{(0)}\_0\\) and 150 | \\(b^{(0)}\_0\\) to the verifier, so they check the final statement directly 151 | by computing both sides of the equation. 152 | 153 | The resulting protocol has \\(\lg n\\) steps of compression where the prover 154 | sends a pair \\((L\_j,R\_j)\\) of points at each step \\(j = k\dots1\\). An 155 | additional and final step involves sending a pair of scalars 156 | \\((a^{(0)}\_0,b^{(0)}\_0)\\) and checking the final relation directly. 157 | -------------------------------------------------------------------------------- /docs/range-proof-protocol.md: -------------------------------------------------------------------------------- 1 | The `range_proof` module contains an API for producing a proof that multiple integer values are within a certain range. 2 | 3 | Aggregated range proof protocol 4 | =============================== 5 | 6 | This is a documentation for the internal implementation of the aggregated range proof. You may find the introduction to all the pieces of the range proof protocol in the [notes](../notes/index.html) module. 7 | 8 | The aggregated range proof is a zero-knowledge proof of the following relation: 9 | \\[ 10 | \operatorname{ZK-PK}\left\\{ 11 | v_{(0)}, v_{(1)}, \dots, v_{(m-1)} \in {\mathbb Z\_p} 12 | : v_{(0)}, v_{(1)}, \dots, v_{(m-1)} \in [0, 2^n) 13 | \right\\} 14 | \\] 15 | 16 | where \\(n\\) and \\(m\\) are both a power of \\(2\\). 17 | 18 | Party and Dealer's algorithm 19 | ---------------------------- 20 | 21 | To create the aggregated range proof, \\(m\\) individual parties which each have a secret value \\(v_{(j)}\\)exchange messages with one dealer, without revealing their secrets. You may find more information on how the parties and dealers are implemented in [aggregated protocol notes](../aggregation/index.html#api-for-the-aggregated-multiparty-computation-protocol). 22 | 23 | The protocol begins with each party \\(j\\) computing three commitments: to the value \\(v_{(j)}\\), to the bits of that value \\(\mathbf{a}\_{L, (j)}, \mathbf{a}\_{R, (j)}\\), and to the per-bit blinding factors \\(\mathbf{s}\_{L, (j)}, \mathbf{s}\_{R, (j)}\\). 24 | 25 | \\[ 26 | \begin{aligned} 27 | V_{(j)} &\gets \operatorname{Com}(v_{(j)}, {\widetilde{v}\_{(j)}}) && = v\_{(j)} \cdot B + {\widetilde{v}\_{(j)}} \cdot {\widetilde{B}} \\\\ 28 | A_{(j)} &\gets \operatorname{Com}({\mathbf{a}}\_{L, (j)}, {\mathbf{a}}\_{R, (j)}) && = {\langle {\mathbf{a}}\_{L, (j)}, {\mathbf{G}\_{(j)}} \rangle} + {\langle {\mathbf{a}}\_{R, (j)}, {\mathbf{H}\_{(j)}} \rangle} + {\widetilde{a}\_{(j)}} {\widetilde{B}} \\\\ 29 | S_{(j)} &\gets \operatorname{Com}({\mathbf{s}}\_{L, (j)}, {\mathbf{s}}\_{R, (j)}) && = {\langle {\mathbf{s}}\_{L, (j)}, {\mathbf{G}\_{(j)}} \rangle} + {\langle {\mathbf{s}}\_{R, (j)}, {\mathbf{H}\_{(j)}} \rangle} + {\widetilde{s}\_{(j)}} {\widetilde{B}} \\\\ 30 | \end{aligned} 31 | \\] where \\(\widetilde{v}\_{(j)}, \widetilde{a}\_{(j)}, \widetilde{s}\_{(j)}\\) are sampled randomly 32 | from \\({\mathbb Z\_p}\\) and \\(\mathbf{s}\_{L, (j)}, \mathbf{s}\_{R, (j)}\\) are sampled randomly from \\({\mathbb Z\_p}^{n}\\). 33 | 34 | The parties all send their \\(V_{(j)}\\), \\(A_{(j)}\\), and \\(S_{(j)}\\) values to the dealer as `BitCommitment`. The dealer adds each \\(V_{(j)}\\) value to the protocol transcript, in order. The dealer then computes \\(A\\) and \\(S\\) as follows: 35 | 36 | \\[ 37 | \begin{aligned} 38 | A &= \sum_{j=0}^{m-1} A_{(j)} \\\\ 39 | S &= \sum_{j=0}^{m-1} S_{(j)} 40 | \end{aligned} 41 | \\] 42 | 43 | The dealer adds \\(A\\) and \\(S\\) to the protocol transcript and obtains challenge scalars \\(y,z \in {\mathbb Z\_p}\\) from the transcript. The dealer sends \\(y, z\\) as `BitChallenge` to all of the parties. 44 | 45 | Using their secret vectors and the challenges \\(y, z\\) from `BitChallenge`, each party constructs vector polynomials: 46 | \\[ 47 | \begin{aligned} 48 | {\mathbf{l}}\_{(j)}(x) &= {\mathbf{l}}\_{0, (j)} + {\mathbf{l}}\_{1, (j)} x \\\\ 49 | {\mathbf{r}}\_{(j)}(x) &= {\mathbf{r}}\_{0, (j)} + {\mathbf{r}}\_{1, (j)} x \\\\ 50 | {\mathbf{l}}\_{0, (j)} &\gets {\mathbf{a}}\_{L, (j)} - z {\mathbf{1}} \\\\ 51 | {\mathbf{l}}\_{1, (j)} &\gets {\mathbf{s}}\_{L, (j)} \\\\ 52 | {\mathbf{r}}\_{0, (j)} &\gets {\mathbf{y}}^{n}\_{(j)} \circ ({\mathbf{a}}\_{R, (j)} + z {\mathbf{1}}) + z^{2} z_{(j)}{\mathbf{2}}^{n} \\\\ 53 | {\mathbf{r}}\_{1, (j)} &\gets {\mathbf{y}}^{n}\_{(j)} \circ {\mathbf{s}}\_{R, (j)} 54 | \end{aligned} 55 | \\] 56 | 57 | The inner product of the above vector polynomials is: 58 | \\[ 59 | t\_{(j)}(x) = {\langle {\mathbf{l}}\_{(j)}(x), {\mathbf{r}}\_{(j)}(x) \rangle} = t\_{0, (j)} + t\_{1, (j)} x + t\_{2, (j)} x^{2}, 60 | \\] 61 | 62 | Each party uses Karatsuba’s method to compute the coefficients of that polynomial as follows: 63 | \\[ 64 | \begin{aligned} 65 | t\_{0, (j)} &\gets {\langle {\mathbf{l}}\_{0, (j)}, {\mathbf{r}}\_{0, (j)} \rangle}, \\\\ 66 | t\_{2, (j)} &\gets {\langle {\mathbf{l}}\_{1, (j)}, {\mathbf{r}}\_{1, (j)} \rangle}, \\\\ 67 | t\_{1, (j)} &\gets {\langle {\mathbf{l}}\_{0, (j)}+ {\mathbf{l}}\_{1, (j)}, {\mathbf{r}}\_{0, (j)} + {\mathbf{r}}\_{1, (j)} \rangle} - t\_{0, (j)} - t\_{2, (j)} 68 | \end{aligned} 69 | \\] 70 | 71 | The party commits to the terms \\(t\_{1, (j)}, t\_{2, (j)}\\): 72 | \\[ 73 | \begin{aligned} 74 | T\_{1, (j)} &\gets \operatorname{Com}(t\_{1, (j)}, {\tilde{t}\_{(j1}}) && = t\_{1, (j)} \cdot B + {\tilde{t}\_{1, (j)}} \cdot {\widetilde{B}} \\\\ 75 | T\_{2, (j)} &\gets \operatorname{Com}(t\_{2, (j)}, {\tilde{t}\_{2, (j)}}) && = t\_{2, (j)} \cdot B + {\tilde{t}\_{2, (j)}} \cdot {\widetilde{B}} 76 | \end{aligned} 77 | \\] where \\(\tilde{t}\_{1, (j)}, \tilde{t}\_{2, (j)}\\) are sampled randomly from \\({\mathbb Z\_p}\\). 78 | 79 | The parties all send their \\(T_{1, (j)}\\) and \\(T_{2, (j)}\\) values to the dealer as `PolyCommitment`. The dealer then computes \\(T_1\\) and \\(T_2\\) as follows: 80 | \\[ 81 | \begin{aligned} 82 | T_1 &= \sum_{j=0}^{m-1} T_{1, (j)} \\\\ 83 | T_2 &= \sum_{j=0}^{m-1} T_{2, (j)} \\\\ 84 | \end{aligned} 85 | \\] 86 | 87 | The dealer adds \\(T_1\\) and \\(T_2\\) to the protocol transcript and obtains a challenge scalar \\(x \in {\mathbb Z\_p}\\) from the transcript. The dealer sends \\(x\\) as `PolyChallenge` to all of the parties. 88 | 89 | Each party uses \\(x\\) to evaluate their polynomials \\(\mathbf{l}\_{(j)}(x), \mathbf{r}\_{(j)}(x), t\_{(j)}(x)\\): 90 | 91 | \\[ 92 | \begin{aligned} 93 | \mathbf{l}\_{(j)} &\gets {\mathbf{l}}\_{0, (j)} + {\mathbf{l}}\_{1, (j)} x\\\\ 94 | \mathbf{r}\_{(j)} &\gets {\mathbf{r}}\_{0, (j)} + {\mathbf{r}}\_{1, (j)} x\\\\ 95 | t\_{(j)}(x) &\gets t\_{0, (j)} + t\_{1, (j)} x + t\_{2, (j)} x^{2} 96 | \end{aligned} 97 | \\] 98 | 99 | Next, each party computes their synthetic blinding factors: 100 | \\[ 101 | \begin{aligned} 102 | {\tilde{t}}\_{(j)}(x) &\gets z^{2} {\tilde{v}}\_{(j)} + x {\tilde{t}}\_{1, (j)} + x^{2} {\tilde{t}}\_{2, (j)} \\\\ 103 | \tilde{e}\_{(j)} &\gets {\widetilde{a}}\_{(j)} + x {\widetilde{s}}\_{(j)} 104 | \end{aligned} 105 | \\] 106 | 107 | The parties all send their values \\(t_{(j)}(x), {\tilde{t}}\_{(j)}(x), \tilde{e}\_{(j)}, \mathbf{l}\_{(j)}(x), \mathbf{r}\_{(j)}(x)\\) to the dealer as `ProofShare`. The dealer then computes \\(t(x), \tilde{t}(x), \tilde{e}\\) as follows: 108 | \\[ 109 | \begin{aligned} 110 | t(x) &= \sum_{j=0}^{m-1} t_{(j)}(x) \\\\ 111 | {\tilde{t}}(x) &= \sum_{j=0}^{m-1} {\tilde{t}}\_{(j)}(x) \\\\ 112 | {\tilde{e}} &= \sum_{j=0}^{m-1} \tilde{e}\_{(j)} 113 | \end{aligned} 114 | \\] 115 | 116 | The dealer adds \\(t(x), {\tilde{t}}(x), {\tilde{e}}\\) to the protocol transcript, obtains a challenge scalar \\(w \in {\mathbb Z\_p}\\), and uses it to create a point \\(Q\\): 117 | 118 | \\[ 119 | Q \gets w \cdot B 120 | \\] 121 | 122 | The dealer creates the aggregated vector polynomials \\(\mathbf{l}(x), \mathbf{r}(x)\\) by concatenating all of the individual party \\(\mathbf{l}\_{(j)}(x)\\) and \\(\mathbf{r}\_{(j)}(x) \\) values: 123 | 124 | \\[ 125 | \begin{aligned} 126 | {\mathbf{l}}(x) &= {\mathbf{l}}\_{(0)}(x) || {\mathbf{l}}\_{(1)}(x) || \dots || {\mathbf{l}}\_{(m-1)}(x) \\\\ 127 | {\mathbf{r}}(x) &= {\mathbf{r}}\_{(0)}(x) || {\mathbf{r}}\_{(1)}(x) || \dots || {\mathbf{r}}\_{(m-1)}(x) \\\\ 128 | \end{aligned} 129 | \\] 130 | 131 | The dealer then performs the [inner product argument](../inner_product_proof/index.html) to prove the relation: 132 | \\[ 133 | \operatorname{PK}\left\\{ 134 | ({\mathbf{G}}, {\mathbf{H}}' \in {\mathbb G}^{n \cdot m}, P', Q \in {\mathbb G}; {\mathbf{l}}, {\mathbf{r}} \in {\mathbb Z\_p}^{n\cdot m}) 135 | : P' = {\langle {\mathbf{l}}, {\mathbf{G}} \rangle} + {\langle {\mathbf{r}}, {\mathbf{H}}' \rangle} + {\langle {\mathbf{l}}, {\mathbf{r}} \rangle} Q 136 | \right\\} 137 | \\] where \\({\mathbf{H}}' = {\mathbf{y}}^{-n \cdot m} \circ {\mathbf{H}}\\). 138 | 139 | The result of the inner product proof is a list of \\(2k\\) points and \\(2\\) scalars, where \\(k = \log_2(n \cdot m)\\): \\(\\{L\_k, R\_k, \\dots, L\_1, R\_1, a, b\\}\\). 140 | 141 | The complete range proof consists of \\(9+2k\\) 32-byte elements: 142 | \\[ 143 | \\{A, S, T_1, T_2, t(x), {\tilde{t}}(x), \tilde{e}, L\_k, R\_k, \\dots, L\_1, R\_1, a, b\\} 144 | \\] 145 | 146 | Verifier's algorithm 147 | -------------------- 148 | 149 | The input to the verifier is the aggregated proof, which contains the range size \\(n\\), the \\(m\\) value commitments \\(V_{(j)}\\), and \\(32 \cdot (9 + 2 k)\\) bytes of the proof data where \\(k = \log_2(n \cdot m)\\): 150 | 151 | \\[ 152 | \\{A, S, T_1, T_2, t(x), {\tilde{t}}(x), \tilde{e}, L\_{k}, R\_{k}, \\dots, L\_1, R\_1, a, b\\} 153 | \\] 154 | 155 | The verifier uses the Fiat-Shamir transform to obtain challenges by adding the appropriate data sequentially to the protocol transcript: 156 | 157 | 1. \\(V_{(0)}, V_{(1)}, \dots, V_{(m)}, A, S\\) are added to obtain challenge scalars \\(y,z \in {\mathbb Z\_p}\\), 158 | 2. \\(T_1, T_2\\) are added to obtain a challenge \\(x \in {\mathbb Z\_p}\\), 159 | 3. \\(t(x), {\tilde{t}}(x), \tilde{e}\\) are added to obtain a challenge \\(w \in {\mathbb Z\_p}\\). 160 | 161 | The verifier computes the following scalars for the [inner product argument](../inner_product_proof/index.html): 162 | 163 | \\[ 164 | \\{u\_{1}^{2}, \dots, u\_{k}^{2}, u\_{1}^{-2}, \dots, u\_{k}^{-2}, s_0, \dots, s_{n-1}\\} 165 | \\] 166 | 167 | The goal of the verifier is to check two equations: 168 | 169 | 1. First, verify the constant term of the polynomial \\(t(x)\\) (see [notes](../notes/index.html#proving-that-t_0-is-correct-1)): 170 | 171 | \\[ 172 | \begin{aligned} 173 | t(x) B + {\tilde{t}}(x) {\widetilde{B}} \stackrel{?}{=} \sum_{j=0}^{m-1} z^{j+2} V_{(j)} + \delta(y,z) B + x T\_{1} + x^{2} T\_{2},\\\\ 174 | \delta(y,z) = (z - z^{2}) \cdot {\langle {\mathbf{1}}, {\mathbf{y}}^{n \cdot m} \rangle} - \sum_{j=0}^{m-1} z^{j+3} \cdot {\langle {\mathbf{1}}, {\mathbf{2}}^{n \cdot m} \rangle}\\\\ 175 | \end{aligned} 176 | \\] 177 | 178 | If we rewrite the check as a comparison with the identity point, we get: 179 | \\[ 180 | 0 \stackrel{?}{=} \sum_{j=0}^{m-1} z^{j+2} V_{(j)} + \delta(y,z) B + x T\_{1} + x^{2} T\_{2} - t(x) B - {\tilde{t}}(x) {\widetilde{B}}. 181 | \\] 182 | 183 | 2. Second, verify the inner product argument for the vectors \\(\mathbf{l}(x), \mathbf{r}(x)\\) that form the \\(t(x)\\) (see [inner-product protocol](../inner_product_proof/index.html#verification-equation)) 184 | 185 | \\[ 186 | P' \overset ? = {\langle a \cdot {\mathbf{s}}, {\mathbf{G}} \rangle} + {\langle {\mathbf{y}^{-n \cdot m}} \circ (b /{\mathbf{s}}), {\mathbf{H}} \rangle} + abQ - \sum\_{j=1}^{k} \left( L\_{j} u\_{j}^{2} + u\_{j}^{-2} R\_{j} \right). 187 | \\] 188 | 189 | Rewriting as a comparison with the identity point and expanding \\(Q = wB\\) and \\(P' = P + t(x) wB\\) as [needed for transition to the inner-product protocol](../notes/index.html#inner-product-proof): 190 | 191 | \\[ 192 | 0 \overset ? = P + t(x) wB - {\langle a \cdot {\mathbf{s}}, {\mathbf{G}} \rangle} - {\langle {\mathbf{y}^{-n \cdot m}} \circ (b /{\mathbf{s}}), {\mathbf{H}} \rangle} - abwB + \sum\_{j=1}^{k} \left( L\_{j} u\_{j}^{2} + u\_{j}^{-2} R\_{j} \right), 193 | \\] 194 | where the [definition](../notes/index.html#proving-that-mathbflx-mathbfrx-are-correct-1) of \\(P\\) is: 195 | 196 | \\[ 197 | \begin{aligned} 198 | P &= -{\widetilde{e}} {\widetilde{B}} + A + x S - z{\langle {\mathbf{1}}, {\mathbf{G}} \rangle} + z{\langle {\mathbf{y}}^{n \cdot m}, {\mathbf{H}'} \rangle} + \sum_{j=0}^{m-1} {\langle z^{j+2} \cdot {\mathbf{2}}^n, {\mathbf{H}'}\_{[j \cdot n : (j+1) \cdot n]} \rangle} \\\\ 199 | &= -{\widetilde{e}} {\widetilde{B}} + A + x S - z{\langle {\mathbf{1}}, {\mathbf{G}} \rangle} + z{\langle {\mathbf{1}}, {\mathbf{H}} \rangle} + \sum_{j=0}^{m-1} {\langle z^{j+2} \cdot {\mathbf{2}}^n \circ {\mathbf{y}}^{n \cdot m}\_{[j \cdot n : (j+1) \cdot n]}, {\mathbf{H}}\_{[j \cdot n : (j+1) \cdot n]} \rangle}\\\\ 200 | &= -{\widetilde{e}} {\widetilde{B}} + A + x S - z{\langle {\mathbf{1}}, {\mathbf{G}} \rangle} + z{\langle {\mathbf{1}}, {\mathbf{H}} \rangle} + {\langle {{\mathbf{y}}^{-n \cdot m} \circ z^2 (z^0 \mathbf{2}^n || z^1 \mathbf{2}^n || \dots || z^{m-1} \mathbf{2}^n)}, {\mathbf{H}} \rangle} 201 | \end{aligned} 202 | \\] 203 | 204 | 205 | 206 | The verifier combines two equations in one by sampling a random factor \\(c \\; {\xleftarrow{\\$}} \\; {\mathbb Z\_p}\\), 207 | multiplying the first equation by \\(c\\), and adding it with the second equation. 208 | 209 | Finally, verifier groups all scalars by each point and performs a single multiscalar multiplication: 210 | 211 | \\[ 212 | \begin{aligned} 213 | 0 \quad \stackrel{?}{=} & \quad 1 \cdot A \\\\ 214 | + & \quad x \cdot S \\\\ 215 | + & \quad cz^2 \cdot V_{(0)} + cz^3 \cdot V_{(1)} + \dots + cz^{m+1} \cdot V_{(m-1)} \\\\ 216 | + & \quad cx \cdot T_1 \\\\ 217 | + & \quad cx^2 \cdot T_2 \\\\ 218 | + & \quad \Big(w \big(t(x) - ab\big) + c \big(\delta(y,z) - t(x)\big) \Big) \cdot B\\\\ 219 | + & \quad (-{\widetilde{e}} - c{\tilde{t}}(x)) \cdot \widetilde{B} \\\\ 220 | + & \quad {\langle {-z\mathbf{1} - a\mathbf{s}}, {\mathbf{G}} \rangle}\\\\ 221 | + & \quad {\langle {z\mathbf{1} + {\mathbf{y}}^{-n \cdot m} \circ (z^2 (z^0 \mathbf{2}^n || z^1 \mathbf{2}^n || \dots || z^{m-1} \mathbf{2}^n) - b/{\mathbf{s}})}, {\mathbf{H}} \rangle}\\\\ 222 | + & \quad {\langle [u_{1}^2, \dots, u_{k}^2 ], [L_1, \dots, L_{k}] \rangle}\\\\ 223 | + & \quad {\langle [u_{1}^{-2}, \dots, u_{k}^{-2} ], [R_1, \dots, R_{k}] \rangle} 224 | \end{aligned} 225 | \\] where \\(1/{\mathbf{s}}\\) are inverses of \\(\mathbf{s}\\), computed as a reversed list of \\(\mathbf{s}\\). 226 | 227 | Individual share validation 228 | --------------------------- 229 | 230 | If the dealer is aggregating a proof across \\(m\\) parties, and if one of those parties is faulty (or malicious) and creates an invalid `ProofShare`, then the `AggregatedProof` that the dealer creates will also be invalid. Therefore, it is helpful to be able to check the validity of an individual `ProofShare`, in order to determine if a party is at fault and if so, to block it. 231 | 232 | 233 | 234 | The math for checking `ProofShare` validity is very similar to checking `AggregatedProof` validity, but does not require combining values from multiple parties. The goal of checking `ProofShare` validity is to verify three equations: 235 | 236 | 1. Verify that \\(\langle \mathbf{l}\_{(j)}(x), \mathbf{r}\_{(j)}(x) \rangle = t_{(j)}(x)\\) 237 | 238 | The dealer can perform this check by simply taking the inner product of \\(\mathbf{l}\_{(j)}(x)\\) and \\( \mathbf{r}\_{(j)}(x)\\) and verifying that it is equal to \\(t_{(j)}(x)\\). Note that the dealer is not creating a proof, and thus the proof compactness of the inner product protocol is not a benefit in this situation, so it is sufficient to just perform an inner product multiplication. 239 | 240 | 2. Verify the constant term of the polynomial \\(t_{(j)}(x)\\) 241 | 242 | The dealer wants to check if this statement is correct: 243 | \\[ 244 | \begin{aligned} 245 | t\_{(j)}(x) B + {\tilde{t}}\_{(j)}(x) {\widetilde{B}} \stackrel{?}{=} z^{2} z\_{(j)} V_{(j)} + \delta(y,z)\_{(j)} B + x T\_{1, (j)} + x^{2} T\_{2, (j)},\\\\ 246 | \delta\_{(j)}(y,z) = (z - z^{2}) \cdot {\langle {\mathbf{1}}, {\mathbf{y}}^{n}\_{(j)} \rangle} - z^{3} z\_{(j)} \cdot {\langle {\mathbf{1}}, {\mathbf{2}}^{n} \rangle}\\\\ 247 | \end{aligned} 248 | \\] 249 | 250 | If we rewrite the check as a comparison with the identity point, and plug in the definitions \\(z_{(j)} = z^j\\) and \\({\mathbf{y}}^{n}\_{(j)} = {\mathbf{y}}^{n \cdot m}\_{[j\cdot n : (j+1) \cdot n]} = {\mathbf{y}}^n y^{j \cdot n}\\), we get: 251 | 252 | \\[ 253 | \begin{aligned} 254 | 0 \stackrel{?}{=} z^{j+2} V_{(j)} + \delta(y,z)\_{(j)} B + x T\_{1, (j)} + x^{2} T\_{2, (j)} - t\_{(j)}(x) B - {\tilde{t}}\_{(j)}(x) {\widetilde{B}},\\\\ 255 | \delta\_{(j)}(y,z) = (z - z^{2}) \cdot {\langle {\mathbf{1}}, {\mathbf{y}}^n \cdot y^{j \cdot n} \rangle} - z^{j+3} \cdot {\langle {\mathbf{1}}, {\mathbf{2}}^{n} \rangle}\\\\ 256 | \end{aligned} 257 | \\] 258 | 259 | 260 | 3. Prove that \\( \mathbf{l}\_{(j)}(x), \mathbf{r}\_{(j)}(x) \\) are correct 261 | 262 | The dealer wants to check if this statement is correct: 263 | \\[ 264 | \begin{aligned} 265 | A_{(j)} + xS_{(j)} - z{\langle {\mathbf{1}}, {\mathbf{G}}\_{(j)} \rangle} + z{\langle {\mathbf{1}}, {\mathbf{H}}\_{(j)} \rangle} + {\langle z^{2} z_{(j)} (\mathbf{y}^{n}\_{(j)})^{-1} \circ {\mathbf{2}}^n, {\mathbf{H}}\_{(j)} \rangle} \stackrel{?}{=} {\widetilde{e}}\_{(j)} {\widetilde{B}} + {\langle \mathbf{l}\_{(j)}(x), {\mathbf{G}}\_{(j)} \rangle} + {\langle \mathbf{r}\_{(j)}(x) \circ (\mathbf{y}^{n}\_{(j)})^{-1}, {\mathbf{H}}\_{(j)} \rangle} 266 | \end{aligned} 267 | \\] 268 | 269 | If we rewrite the check as a comparison with the identity point, and plug in the definitions \\(z_{(j)} = z^j\\) and \\({\mathbf{y}}^{n}\_{(j)} = {\mathbf{y}}^{n \cdot m}\_{[j\cdot n : (j+1) \cdot n]} = {\mathbf{y}}^n y^{j \cdot n}\\), we get: 270 | 271 | \\[ 272 | \begin{aligned} 273 | 0 \stackrel{?}{=}A_{(j)} + xS_{(j)} - z{\langle {\mathbf{1}}, {\mathbf{G}}\_{(j)} \rangle} + z{\langle {\mathbf{1}}, {\mathbf{H}}\_{(j)} \rangle} + {\langle z^{j+2} \cdot \mathbf{y}^{-n} y^{-j \cdot n} \circ {\mathbf{2}}^n, {\mathbf{H}}\_{(j)} \rangle} - {\widetilde{e}}\_{(j)} {\widetilde{B}} - {\langle \mathbf{l}\_{(j)}(x), {\mathbf{G}}\_{(j)} \rangle} - {\langle \mathbf{r}\_{(j)}(x) \circ \mathbf{y}^{-n} y^{-j \cdot n} , {\mathbf{H}}\_{(j)} \rangle} 274 | \end{aligned} 275 | \\] 276 | 277 | The dealer can combine equations `2` and `3` into one by sampling a random factor \\(c \\; {\xleftarrow{\\$}} \\; {\mathbb Z\_p}\\), 278 | multiplying equation `2` by \\(c\\), and adding it with equation `3`. Finally, the dealer groups all scalars by each point and performs a single multiscalar multiplication: 279 | 280 | \\[ 281 | \begin{aligned} 282 | 0 \quad \stackrel{?}{=} & \quad 1 \cdot A_{(j)} \\\\ 283 | + & \quad x \cdot S_{(j)} \\\\ 284 | + & \quad (-{\widetilde{e}\_{(j)}} - c{\tilde{t}}\_{(j)}(x)) \cdot \widetilde{B} \\\\ 285 | + & \quad c \big(\delta_{(j)}(y,z) - t_{(j)}(x)\big) \cdot B\\\\ 286 | + & \quad cz^{j+2} \cdot V_{(j)} \\\\ 287 | + & \quad cx \cdot T_{1, (j)} \\\\ 288 | + & \quad cx^2 \cdot T_{2, (j)} \\\\ 289 | + & \quad {\langle {- \mathbf{l}\_{(j)}(x)} -z\mathbf{1}, {\mathbf{G}\_{(j)}} \rangle}\\\\ 290 | + & \quad {\langle {- \mathbf{r}\_{(j)}(x)} \circ \mathbf{y}^{-n} y^{-j \cdot n} + z\mathbf{1} + z^{j+2} \cdot \mathbf{y}^{-n} y^{-j \cdot n} \circ {\mathbf{2}}^n, {\mathbf{H}}\_{(j)} \rangle}\\\\ 291 | \end{aligned} 292 | \\] 293 | -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly-2019-07-31 2 | -------------------------------------------------------------------------------- /src/errors.rs: -------------------------------------------------------------------------------- 1 | //! Errors related to proving and verifying proofs. 2 | 3 | extern crate alloc; 4 | use alloc::vec::Vec; 5 | 6 | #[cfg(feature = "std")] 7 | use thiserror::Error; 8 | 9 | /// Represents an error in proof creation, verification, or parsing. 10 | #[derive(Clone, Debug, Eq, PartialEq)] 11 | #[cfg_attr(feature = "std", derive(Error))] 12 | pub enum ProofError { 13 | /// This error occurs when a proof failed to verify. 14 | #[cfg_attr(feature = "std", error("Proof verification failed."))] 15 | VerificationError, 16 | /// This error occurs when the proof encoding is malformed. 17 | #[cfg_attr(feature = "std", error("Proof data could not be parsed."))] 18 | FormatError, 19 | /// This error occurs during proving if the number of blinding 20 | /// factors does not match the number of values. 21 | #[cfg_attr(feature = "std", error("Wrong number of blinding factors supplied."))] 22 | WrongNumBlindingFactors, 23 | /// This error occurs when attempting to create a proof with 24 | /// bitsize other than \\(8\\), \\(16\\), \\(32\\), or \\(64\\). 25 | #[cfg_attr(feature = "std", error("Invalid bitsize, must have n = 8,16,32,64."))] 26 | InvalidBitsize, 27 | /// This error occurs when attempting to create an aggregated 28 | /// proof with non-power-of-two aggregation size. 29 | #[cfg_attr( 30 | feature = "std", 31 | error("Invalid aggregation size, m must be a power of 2.") 32 | )] 33 | InvalidAggregation, 34 | /// This error occurs when there are insufficient generators for the proof. 35 | #[cfg_attr( 36 | feature = "std", 37 | error("Invalid generators size, too few generators for proof") 38 | )] 39 | InvalidGeneratorsLength, 40 | /// This error occurs when inputs are the incorrect length for the proof. 41 | #[cfg_attr( 42 | feature = "std", 43 | error("Invalid input size, incorrect input length for proof") 44 | )] 45 | InvalidInputLength, 46 | /// This error results from an internal error during proving. 47 | /// 48 | /// The single-party prover is implemented by performing 49 | /// multiparty computation with ourselves. However, because the 50 | /// MPC protocol is not exposed by the single-party API, we 51 | /// consider its errors to be internal errors. 52 | #[cfg_attr(feature = "std", error("Internal error during proof creation: {0}"))] 53 | ProvingError(MPCError), 54 | } 55 | 56 | impl From for ProofError { 57 | fn from(e: MPCError) -> ProofError { 58 | match e { 59 | MPCError::InvalidBitsize => ProofError::InvalidBitsize, 60 | MPCError::InvalidAggregation => ProofError::InvalidAggregation, 61 | MPCError::InvalidGeneratorsLength => ProofError::InvalidGeneratorsLength, 62 | _ => ProofError::ProvingError(e), 63 | } 64 | } 65 | } 66 | 67 | /// Represents an error during the multiparty computation protocol for 68 | /// proof aggregation. 69 | /// 70 | /// This is a separate type from the `ProofError` to allow a layered 71 | /// API: although the MPC protocol is used internally for single-party 72 | /// proving, its API should not expose the complexity of the MPC 73 | /// protocol. 74 | #[derive(Clone, Debug, Eq, PartialEq)] 75 | #[cfg_attr(feature = "std", derive(Error))] 76 | pub enum MPCError { 77 | /// This error occurs when the dealer gives a zero challenge, 78 | /// which would annihilate the blinding factors. 79 | #[cfg_attr(feature = "std", error("Dealer gave a malicious challenge value."))] 80 | MaliciousDealer, 81 | /// This error occurs when attempting to create a proof with 82 | /// bitsize other than \\(8\\), \\(16\\), \\(32\\), or \\(64\\). 83 | #[cfg_attr(feature = "std", error("Invalid bitsize, must have n = 8,16,32,64"))] 84 | InvalidBitsize, 85 | /// This error occurs when attempting to create an aggregated 86 | /// proof with non-power-of-two aggregation size. 87 | #[cfg_attr( 88 | feature = "std", 89 | error("Invalid aggregation size, m must be a power of 2") 90 | )] 91 | InvalidAggregation, 92 | /// This error occurs when there are insufficient generators for the proof. 93 | #[cfg_attr( 94 | feature = "std", 95 | error("Invalid generators size, too few generators for proof") 96 | )] 97 | InvalidGeneratorsLength, 98 | /// This error occurs when the dealer is given the wrong number of 99 | /// value commitments. 100 | #[cfg_attr(feature = "std", error("Wrong number of value commitments"))] 101 | WrongNumBitCommitments, 102 | /// This error occurs when the dealer is given the wrong number of 103 | /// polynomial commitments. 104 | #[cfg_attr(feature = "std", error("Wrong number of value commitments"))] 105 | WrongNumPolyCommitments, 106 | /// This error occurs when the dealer is given the wrong number of 107 | /// proof shares. 108 | #[cfg_attr(feature = "std", error("Wrong number of proof shares"))] 109 | WrongNumProofShares, 110 | /// This error occurs when one or more parties submit malformed 111 | /// proof shares. 112 | #[cfg_attr( 113 | feature = "std", 114 | error("Malformed proof shares from parties {bad_shares:?}") 115 | )] 116 | MalformedProofShares { 117 | /// A vector with the indexes of the parties whose shares were malformed. 118 | bad_shares: Vec, 119 | }, 120 | } 121 | 122 | /// Represents an error during the proving or verifying of a constraint system. 123 | /// 124 | /// XXX: should this be separate from a `ProofError`? 125 | #[cfg(feature = "yoloproofs")] 126 | #[derive(Clone, Debug, Eq, PartialEq)] 127 | #[cfg_attr(feature = "std", derive(Error))] 128 | pub enum R1CSError { 129 | /// Occurs when there are insufficient generators for the proof. 130 | #[cfg_attr( 131 | feature = "std", 132 | error("Invalid generators size, too few generators for proof") 133 | )] 134 | InvalidGeneratorsLength, 135 | /// This error occurs when the proof encoding is malformed. 136 | #[cfg_attr(feature = "std", error("Proof data could not be parsed."))] 137 | FormatError, 138 | /// Occurs when verification of an 139 | /// [`R1CSProof`](::r1cs::R1CSProof) fails. 140 | #[cfg_attr(feature = "std", error("R1CSProof did not verify correctly."))] 141 | VerificationError, 142 | 143 | /// Occurs when trying to use a missing variable assignment. 144 | /// Used by gadgets that build the constraint system to signal that 145 | /// a variable assignment is not provided when the prover needs it. 146 | #[cfg_attr(feature = "std", error("Variable does not have a value assignment."))] 147 | MissingAssignment, 148 | 149 | /// Occurs when a gadget receives an inconsistent input. 150 | #[cfg_attr(feature = "std", error("Gadget error: {description:?}"))] 151 | GadgetError { 152 | /// The description of the reasons for the error. 153 | description: String, 154 | }, 155 | } 156 | 157 | #[cfg(feature = "yoloproofs")] 158 | impl From for R1CSError { 159 | fn from(e: ProofError) -> R1CSError { 160 | match e { 161 | ProofError::InvalidGeneratorsLength => R1CSError::InvalidGeneratorsLength, 162 | ProofError::FormatError => R1CSError::FormatError, 163 | ProofError::VerificationError => R1CSError::VerificationError, 164 | _ => panic!("unexpected error type in conversion"), 165 | } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/generators.rs: -------------------------------------------------------------------------------- 1 | //! The `generators` module contains API for producing a 2 | //! set of generators for a rangeproof. 3 | 4 | #![allow(non_snake_case)] 5 | #![deny(missing_docs)] 6 | 7 | extern crate alloc; 8 | 9 | use alloc::vec::Vec; 10 | use curve25519_dalek::constants::RISTRETTO_BASEPOINT_COMPRESSED; 11 | use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT; 12 | use curve25519_dalek::ristretto::RistrettoPoint; 13 | use curve25519_dalek::scalar::Scalar; 14 | use curve25519_dalek::traits::MultiscalarMul; 15 | use digest::{ExtendableOutput, Input, XofReader}; 16 | use sha3::{Sha3XofReader, Sha3_512, Shake256}; 17 | 18 | /// Represents a pair of base points for Pedersen commitments. 19 | /// 20 | /// The Bulletproofs implementation and API is designed to support 21 | /// pluggable bases for Pedersen commitments, so that the choice of 22 | /// bases is not hard-coded. 23 | /// 24 | /// The default generators are: 25 | /// 26 | /// * `B`: the `ristretto255` basepoint; 27 | /// * `B_blinding`: the result of `ristretto255` SHA3-512 28 | /// hash-to-group on input `B_bytes`. 29 | #[derive(Copy, Clone)] 30 | pub struct PedersenGens { 31 | /// Base for the committed value 32 | pub B: RistrettoPoint, 33 | /// Base for the blinding factor 34 | pub B_blinding: RistrettoPoint, 35 | } 36 | 37 | impl PedersenGens { 38 | /// Creates a Pedersen commitment using the value scalar and a blinding factor. 39 | pub fn commit(&self, value: Scalar, blinding: Scalar) -> RistrettoPoint { 40 | RistrettoPoint::multiscalar_mul(&[value, blinding], &[self.B, self.B_blinding]) 41 | } 42 | } 43 | 44 | impl Default for PedersenGens { 45 | fn default() -> Self { 46 | PedersenGens { 47 | B: RISTRETTO_BASEPOINT_POINT, 48 | B_blinding: RistrettoPoint::hash_from_bytes::( 49 | RISTRETTO_BASEPOINT_COMPRESSED.as_bytes(), 50 | ), 51 | } 52 | } 53 | } 54 | 55 | /// The `GeneratorsChain` creates an arbitrary-long sequence of 56 | /// orthogonal generators. The sequence can be deterministically 57 | /// produced starting with an arbitrary point. 58 | struct GeneratorsChain { 59 | reader: Sha3XofReader, 60 | } 61 | 62 | impl GeneratorsChain { 63 | /// Creates a chain of generators, determined by the hash of `label`. 64 | fn new(label: &[u8]) -> Self { 65 | let mut shake = Shake256::default(); 66 | shake.input(b"GeneratorsChain"); 67 | shake.input(label); 68 | 69 | GeneratorsChain { 70 | reader: shake.xof_result(), 71 | } 72 | } 73 | 74 | /// Advances the reader n times, squeezing and discarding 75 | /// the result. 76 | fn fast_forward(mut self, n: usize) -> Self { 77 | for _ in 0..n { 78 | let mut buf = [0u8; 64]; 79 | self.reader.read(&mut buf); 80 | } 81 | self 82 | } 83 | } 84 | 85 | impl Default for GeneratorsChain { 86 | fn default() -> Self { 87 | Self::new(&[]) 88 | } 89 | } 90 | 91 | impl Iterator for GeneratorsChain { 92 | type Item = RistrettoPoint; 93 | 94 | fn next(&mut self) -> Option { 95 | let mut uniform_bytes = [0u8; 64]; 96 | self.reader.read(&mut uniform_bytes); 97 | 98 | Some(RistrettoPoint::from_uniform_bytes(&uniform_bytes)) 99 | } 100 | 101 | fn size_hint(&self) -> (usize, Option) { 102 | (usize::max_value(), None) 103 | } 104 | } 105 | 106 | /// The `BulletproofGens` struct contains all the generators needed 107 | /// for aggregating up to `m` range proofs of up to `n` bits each. 108 | /// 109 | /// # Extensible Generator Generation 110 | /// 111 | /// Instead of constructing a single vector of size `m*n`, as 112 | /// described in the Bulletproofs paper, we construct each party's 113 | /// generators separately. 114 | /// 115 | /// To construct an arbitrary-length chain of generators, we apply 116 | /// SHAKE256 to a domain separator label, and feed each 64 bytes of 117 | /// XOF output into the `ristretto255` hash-to-group function. 118 | /// Each of the `m` parties' generators are constructed using a 119 | /// different domain separation label, and proving and verification 120 | /// uses the first `n` elements of the arbitrary-length chain. 121 | /// 122 | /// This means that the aggregation size (number of 123 | /// parties) is orthogonal to the rangeproof size (number of bits), 124 | /// and allows using the same `BulletproofGens` object for different 125 | /// proving parameters. 126 | /// 127 | /// This construction is also forward-compatible with constraint 128 | /// system proofs, which use a much larger slice of the generator 129 | /// chain, and even forward-compatible to multiparty aggregation of 130 | /// constraint system proofs, since the generators are namespaced by 131 | /// their party index. 132 | #[derive(Clone)] 133 | pub struct BulletproofGens { 134 | /// The maximum number of usable generators for each party. 135 | pub gens_capacity: usize, 136 | /// Number of values or parties 137 | pub party_capacity: usize, 138 | /// Precomputed \\(\mathbf G\\) generators for each party. 139 | G_vec: Vec>, 140 | /// Precomputed \\(\mathbf H\\) generators for each party. 141 | H_vec: Vec>, 142 | } 143 | 144 | impl BulletproofGens { 145 | /// Create a new `BulletproofGens` object. 146 | /// 147 | /// # Inputs 148 | /// 149 | /// * `gens_capacity` is the number of generators to precompute 150 | /// for each party. For rangeproofs, it is sufficient to pass 151 | /// `64`, the maximum bitsize of the rangeproofs. For circuit 152 | /// proofs, the capacity must be greater than the number of 153 | /// multipliers, rounded up to the next power of two. 154 | /// 155 | /// * `party_capacity` is the maximum number of parties that can 156 | /// produce an aggregated proof. 157 | pub fn new(gens_capacity: usize, party_capacity: usize) -> Self { 158 | let mut gens = BulletproofGens { 159 | gens_capacity: 0, 160 | party_capacity, 161 | G_vec: (0..party_capacity).map(|_| Vec::new()).collect(), 162 | H_vec: (0..party_capacity).map(|_| Vec::new()).collect(), 163 | }; 164 | gens.increase_capacity(gens_capacity); 165 | gens 166 | } 167 | 168 | /// Returns j-th share of generators, with an appropriate 169 | /// slice of vectors G and H for the j-th range proof. 170 | pub fn share(&self, j: usize) -> BulletproofGensShare<'_> { 171 | BulletproofGensShare { 172 | gens: &self, 173 | share: j, 174 | } 175 | } 176 | 177 | /// Increases the generators' capacity to the amount specified. 178 | /// If less than or equal to the current capacity, does nothing. 179 | pub fn increase_capacity(&mut self, new_capacity: usize) { 180 | use byteorder::{ByteOrder, LittleEndian}; 181 | 182 | if self.gens_capacity >= new_capacity { 183 | return; 184 | } 185 | 186 | for i in 0..self.party_capacity { 187 | let party_index = i as u32; 188 | let mut label = [b'G', 0, 0, 0, 0]; 189 | LittleEndian::write_u32(&mut label[1..5], party_index); 190 | self.G_vec[i].extend( 191 | &mut GeneratorsChain::new(&label) 192 | .fast_forward(self.gens_capacity) 193 | .take(new_capacity - self.gens_capacity), 194 | ); 195 | 196 | label[0] = b'H'; 197 | self.H_vec[i].extend( 198 | &mut GeneratorsChain::new(&label) 199 | .fast_forward(self.gens_capacity) 200 | .take(new_capacity - self.gens_capacity), 201 | ); 202 | } 203 | self.gens_capacity = new_capacity; 204 | } 205 | 206 | /// Return an iterator over the aggregation of the parties' G generators with given size `n`. 207 | pub(crate) fn G(&self, n: usize, m: usize) -> impl Iterator { 208 | AggregatedGensIter { 209 | n, 210 | m, 211 | array: &self.G_vec, 212 | party_idx: 0, 213 | gen_idx: 0, 214 | } 215 | } 216 | 217 | /// Return an iterator over the aggregation of the parties' H generators with given size `n`. 218 | pub(crate) fn H(&self, n: usize, m: usize) -> impl Iterator { 219 | AggregatedGensIter { 220 | n, 221 | m, 222 | array: &self.H_vec, 223 | party_idx: 0, 224 | gen_idx: 0, 225 | } 226 | } 227 | } 228 | 229 | struct AggregatedGensIter<'a> { 230 | array: &'a Vec>, 231 | n: usize, 232 | m: usize, 233 | party_idx: usize, 234 | gen_idx: usize, 235 | } 236 | 237 | impl<'a> Iterator for AggregatedGensIter<'a> { 238 | type Item = &'a RistrettoPoint; 239 | 240 | fn next(&mut self) -> Option { 241 | if self.gen_idx >= self.n { 242 | self.gen_idx = 0; 243 | self.party_idx += 1; 244 | } 245 | 246 | if self.party_idx >= self.m { 247 | None 248 | } else { 249 | let cur_gen = self.gen_idx; 250 | self.gen_idx += 1; 251 | Some(&self.array[self.party_idx][cur_gen]) 252 | } 253 | } 254 | 255 | fn size_hint(&self) -> (usize, Option) { 256 | let size = self.n * (self.m - self.party_idx) - self.gen_idx; 257 | (size, Some(size)) 258 | } 259 | } 260 | 261 | /// Represents a view of the generators used by a specific party in an 262 | /// aggregated proof. 263 | /// 264 | /// The `BulletproofGens` struct represents generators for an aggregated 265 | /// range proof `m` proofs of `n` bits each; the `BulletproofGensShare` 266 | /// provides a view of the generators for one of the `m` parties' shares. 267 | /// 268 | /// The `BulletproofGensShare` is produced by [`BulletproofGens::share()`]. 269 | #[derive(Copy, Clone)] 270 | pub struct BulletproofGensShare<'a> { 271 | /// The parent object that this is a view into 272 | gens: &'a BulletproofGens, 273 | /// Which share we are 274 | share: usize, 275 | } 276 | 277 | impl<'a> BulletproofGensShare<'a> { 278 | /// Return an iterator over this party's G generators with given size `n`. 279 | pub fn G(&self, n: usize) -> impl Iterator { 280 | self.gens.G_vec[self.share].iter().take(n) 281 | } 282 | 283 | /// Return an iterator over this party's H generators with given size `n`. 284 | pub(crate) fn H(&self, n: usize) -> impl Iterator { 285 | self.gens.H_vec[self.share].iter().take(n) 286 | } 287 | } 288 | 289 | #[cfg(test)] 290 | mod tests { 291 | use super::*; 292 | 293 | #[test] 294 | fn aggregated_gens_iter_matches_flat_map() { 295 | let gens = BulletproofGens::new(64, 8); 296 | 297 | let helper = |n: usize, m: usize| { 298 | let agg_G: Vec = gens.G(n, m).cloned().collect(); 299 | let flat_G: Vec = gens 300 | .G_vec 301 | .iter() 302 | .take(m) 303 | .flat_map(move |G_j| G_j.iter().take(n)) 304 | .cloned() 305 | .collect(); 306 | 307 | let agg_H: Vec = gens.H(n, m).cloned().collect(); 308 | let flat_H: Vec = gens 309 | .H_vec 310 | .iter() 311 | .take(m) 312 | .flat_map(move |H_j| H_j.iter().take(n)) 313 | .cloned() 314 | .collect(); 315 | 316 | assert_eq!(agg_G, flat_G); 317 | assert_eq!(agg_H, flat_H); 318 | }; 319 | 320 | helper(64, 8); 321 | helper(64, 4); 322 | helper(64, 2); 323 | helper(64, 1); 324 | helper(32, 8); 325 | helper(32, 4); 326 | helper(32, 2); 327 | helper(32, 1); 328 | helper(16, 8); 329 | helper(16, 4); 330 | helper(16, 2); 331 | helper(16, 1); 332 | } 333 | 334 | #[test] 335 | fn resizing_small_gens_matches_creating_bigger_gens() { 336 | let gens = BulletproofGens::new(64, 8); 337 | 338 | let mut gen_resized = BulletproofGens::new(32, 8); 339 | gen_resized.increase_capacity(64); 340 | 341 | let helper = |n: usize, m: usize| { 342 | let gens_G: Vec = gens.G(n, m).cloned().collect(); 343 | let gens_H: Vec = gens.H(n, m).cloned().collect(); 344 | 345 | let resized_G: Vec = gen_resized.G(n, m).cloned().collect(); 346 | let resized_H: Vec = gen_resized.H(n, m).cloned().collect(); 347 | 348 | assert_eq!(gens_G, resized_G); 349 | assert_eq!(gens_H, resized_H); 350 | }; 351 | 352 | helper(64, 8); 353 | helper(32, 8); 354 | helper(16, 8); 355 | } 356 | } 357 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "std"), no_std)] 2 | #![feature(nll)] 3 | #![feature(external_doc)] 4 | #![feature(try_trait)] 5 | #![deny(missing_docs)] 6 | #![doc(include = "../README.md")] 7 | #![doc(html_logo_url = "https://doc.dalek.rs/assets/dalek-logo-clear.png")] 8 | #![doc(html_root_url = "https://docs.rs/bulletproofs/2.0.0")] 9 | 10 | extern crate alloc; 11 | 12 | #[macro_use] 13 | extern crate serde_derive; 14 | 15 | mod util; 16 | 17 | #[doc(include = "../docs/notes-intro.md")] 18 | mod notes { 19 | #[doc(include = "../docs/notes-ipp.md")] 20 | mod inner_product_proof {} 21 | #[doc(include = "../docs/notes-rp.md")] 22 | mod range_proof {} 23 | #[doc(include = "../docs/notes-r1cs.md")] 24 | mod r1cs_proof {} 25 | } 26 | 27 | mod errors; 28 | mod generators; 29 | mod inner_product_proof; 30 | mod linear_proof; 31 | mod range_proof; 32 | mod transcript; 33 | 34 | pub use crate::errors::ProofError; 35 | pub use crate::generators::{BulletproofGens, BulletproofGensShare, PedersenGens}; 36 | pub use crate::linear_proof::LinearProof; 37 | pub use crate::range_proof::RangeProof; 38 | 39 | #[doc(include = "../docs/aggregation-api.md")] 40 | pub mod range_proof_mpc { 41 | pub use crate::errors::MPCError; 42 | pub use crate::range_proof::dealer; 43 | pub use crate::range_proof::messages; 44 | pub use crate::range_proof::party; 45 | } 46 | 47 | #[cfg(feature = "yoloproofs")] 48 | #[cfg(feature = "std")] 49 | pub mod r1cs; 50 | -------------------------------------------------------------------------------- /src/linear_proof.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | extern crate alloc; 4 | 5 | use alloc::vec::Vec; 6 | 7 | use core::iter; 8 | use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint}; 9 | use curve25519_dalek::scalar::Scalar; 10 | use curve25519_dalek::traits::VartimeMultiscalarMul; 11 | use merlin::Transcript; 12 | use rand_core::{CryptoRng, RngCore}; 13 | 14 | use crate::errors::ProofError; 15 | use crate::inner_product_proof::inner_product; 16 | use crate::transcript::TranscriptProtocol; 17 | 18 | /// A linear proof, which is an "lightweight" version of a Bulletproofs inner-product proof 19 | /// Protocol: Section E.3 of [GHL'21](https://eprint.iacr.org/2021/1397.pdf) 20 | /// 21 | /// Prove that = c where a is secret and b is public. 22 | #[derive(Clone, Debug)] 23 | pub struct LinearProof { 24 | pub(crate) L_vec: Vec, 25 | pub(crate) R_vec: Vec, 26 | /// A commitment to the base case elements 27 | pub(crate) S: CompressedRistretto, 28 | /// a_star, corresponding to the base case `a` 29 | pub(crate) a: Scalar, 30 | /// r_star, corresponding to the base case `r` 31 | pub(crate) r: Scalar, 32 | } 33 | 34 | impl LinearProof { 35 | /// Create a linear proof, a lightweight variant of a Bulletproofs inner-product proof. 36 | /// This proves that = c where a is secret and b is public. 37 | /// 38 | /// The lengths of the vectors must all be the same, and must all be either 0 or a power of 2. 39 | /// The proof is created with respect to the bases \\(G\\). 40 | pub fn create( 41 | transcript: &mut Transcript, 42 | rng: &mut T, 43 | // Commitment to witness 44 | C: &CompressedRistretto, 45 | // Blinding factor for C 46 | mut r: Scalar, 47 | // Secret scalar vector a 48 | mut a_vec: Vec, 49 | // Public scalar vector b 50 | mut b_vec: Vec, 51 | // Generator vector 52 | mut G_vec: Vec, 53 | // Pedersen generator F, for committing to the secret value 54 | F: &RistrettoPoint, 55 | // Pedersen generator B, for committing to the blinding value 56 | B: &RistrettoPoint, 57 | ) -> Result { 58 | let mut n = b_vec.len(); 59 | // All of the input vectors must have the same length. 60 | if G_vec.len() != n { 61 | return Err(ProofError::InvalidGeneratorsLength); 62 | } 63 | if a_vec.len() != n { 64 | return Err(ProofError::InvalidInputLength); 65 | } 66 | // All of the input vectors must have a length that is a power of two. 67 | if !n.is_power_of_two() { 68 | return Err(ProofError::InvalidInputLength); 69 | } 70 | 71 | // Append all public data to the transcript 72 | transcript.innerproduct_domain_sep(n as u64); 73 | transcript.append_point(b"C", &C); 74 | for b_i in &b_vec { 75 | transcript.append_scalar(b"b_i", b_i); 76 | } 77 | for G_i in &G_vec { 78 | transcript.append_point(b"G_i", &G_i.compress()); 79 | } 80 | transcript.append_point(b"F", &F.compress()); 81 | transcript.append_point(b"B", &B.compress()); 82 | 83 | // Create slices G, H, a, b backed by their respective 84 | // vectors. This lets us reslice as we compress the lengths 85 | // of the vectors in the main loop below. 86 | let mut G = &mut G_vec[..]; 87 | let mut a = &mut a_vec[..]; 88 | let mut b = &mut b_vec[..]; 89 | 90 | let lg_n = n.next_power_of_two().trailing_zeros() as usize; 91 | let mut L_vec = Vec::with_capacity(lg_n); 92 | let mut R_vec = Vec::with_capacity(lg_n); 93 | 94 | while n != 1 { 95 | n = n / 2; 96 | let (a_L, a_R) = a.split_at_mut(n); 97 | let (b_L, b_R) = b.split_at_mut(n); 98 | let (G_L, G_R) = G.split_at_mut(n); 99 | 100 | let c_L = inner_product(&a_L, &b_R); 101 | let c_R = inner_product(&a_R, &b_L); 102 | 103 | let s_j = Scalar::random(rng); 104 | let t_j = Scalar::random(rng); 105 | 106 | // L = a_L * G_R + s_j * B + c_L * F 107 | let L = RistrettoPoint::vartime_multiscalar_mul( 108 | a_L.iter().chain(iter::once(&s_j)).chain(iter::once(&c_L)), 109 | G_R.iter().chain(iter::once(B)).chain(iter::once(F)), 110 | ) 111 | .compress(); 112 | 113 | // R = a_R * G_L + t_j * B + c_R * F 114 | let R = RistrettoPoint::vartime_multiscalar_mul( 115 | a_R.iter().chain(iter::once(&t_j)).chain(iter::once(&c_R)), 116 | G_L.iter().chain(iter::once(B)).chain(iter::once(F)), 117 | ) 118 | .compress(); 119 | 120 | L_vec.push(L); 121 | R_vec.push(R); 122 | 123 | transcript.append_point(b"L", &L); 124 | transcript.append_point(b"R", &R); 125 | 126 | let x_j = transcript.challenge_scalar(b"x_j"); 127 | let x_j_inv = x_j.invert(); 128 | 129 | for i in 0..n { 130 | // a_L = a_L + x_j^{-1} * a_R 131 | a_L[i] = a_L[i] + x_j_inv * a_R[i]; 132 | // b_L = b_L + x_j * b_R 133 | b_L[i] = b_L[i] + x_j * b_R[i]; 134 | // G_L = G_L + x_j * G_R 135 | G_L[i] = RistrettoPoint::vartime_multiscalar_mul( 136 | &[Scalar::one(), x_j], 137 | &[G_L[i], G_R[i]], 138 | ); 139 | } 140 | a = a_L; 141 | b = b_L; 142 | G = G_L; 143 | r = r + x_j * s_j + x_j_inv * t_j; 144 | } 145 | 146 | let s_star = Scalar::random(rng); 147 | let t_star = Scalar::random(rng); 148 | let S = (t_star * B + s_star * b[0] * F + s_star * G[0]).compress(); 149 | transcript.append_point(b"S", &S); 150 | 151 | let x_star = transcript.challenge_scalar(b"x_star"); 152 | let a_star = s_star + x_star * a[0]; 153 | let r_star = t_star + x_star * r; 154 | 155 | Ok(LinearProof { 156 | L_vec, 157 | R_vec, 158 | S, 159 | a: a_star, 160 | r: r_star, 161 | }) 162 | } 163 | 164 | pub fn verify( 165 | &self, 166 | transcript: &mut Transcript, 167 | // Commitment to witness 168 | C: &CompressedRistretto, 169 | // Generator vector 170 | G: &[RistrettoPoint], 171 | // Pedersen generator F, for committing to the secret value 172 | F: &RistrettoPoint, 173 | // Pedersen generator B, for committing to the blinding value 174 | B: &RistrettoPoint, 175 | // Public scalar vector b 176 | b_vec: Vec, 177 | ) -> Result<(), ProofError> { 178 | let n = b_vec.len(); 179 | if G.len() != n { 180 | return Err(ProofError::InvalidGeneratorsLength); 181 | } 182 | 183 | // Append all public data to the transcript 184 | transcript.innerproduct_domain_sep(n as u64); 185 | transcript.append_point(b"C", &C); 186 | for b_i in &b_vec { 187 | transcript.append_scalar(b"b_i", b_i); 188 | } 189 | for G_i in G { 190 | transcript.append_point(b"G_i", &G_i.compress()); 191 | } 192 | transcript.append_point(b"F", &F.compress()); 193 | transcript.append_point(b"B", &B.compress()); 194 | 195 | let (x_vec, x_inv_vec, b_0) = self.verification_scalars(n, transcript, b_vec)?; 196 | transcript.append_point(b"S", &self.S); 197 | let x_star = transcript.challenge_scalar(b"x_star"); 198 | 199 | // Decompress the compressed L values 200 | let Ls = self 201 | .L_vec 202 | .iter() 203 | .map(|p| p.decompress().ok_or(ProofError::VerificationError)) 204 | .collect::, _>>()?; 205 | 206 | // Decompress the compressed R values 207 | let Rs = self 208 | .R_vec 209 | .iter() 210 | .map(|p| p.decompress().ok_or(ProofError::VerificationError)) 211 | .collect::, _>>()?; 212 | 213 | // L_R_factors = sum_{j=0}^{l-1} (x_j * L_j + x_j^{-1} * R_j) 214 | // 215 | // Note: in GHL'21 the verification equation is incorrect (as of 05/03/22), with x_j and x_j^{-1} reversed. 216 | // (Incorrect paper equation: sum_{j=0}^{l-1} (x_j^{-1} * L_j + x_j * R_j) ) 217 | let L_R_factors: RistrettoPoint = RistrettoPoint::vartime_multiscalar_mul( 218 | x_vec.iter().chain(x_inv_vec.iter()), 219 | Ls.iter().chain(Rs.iter()), 220 | ); 221 | 222 | // This is an optimized way to compute the base case G (G_0 in the paper): 223 | // G_0 = sum_{i=0}^{2^{l-1}} (x * G_i) 224 | let s = self.subset_product(n, x_vec); 225 | let G_0: RistrettoPoint = RistrettoPoint::vartime_multiscalar_mul(s.iter(), G.iter()); 226 | 227 | let S = self.S.decompress().ok_or(ProofError::VerificationError)?; 228 | let C = C.decompress().ok_or(ProofError::VerificationError)?; 229 | 230 | // This matches the verification equation: 231 | // S == r_star * B + a_star * b_0 * F 232 | // - x_star * (C + sum_{j=0}^{l-1} (x_j * L_j + x_j^{-1} * R_j)) 233 | // + a_star * sum_{i=0}^{2^{l-1}} (x * G_i) 234 | // 235 | // Where L_R_factors = sum_{j=0}^{l-1} (x_j * L_j + x_j^{-1} * R_j) 236 | // and G_0 = sum_{i=0}^{2^{l-1}} (x * G_i) 237 | let expect_S = self.r * B + self.a * b_0 * F - x_star * (C + L_R_factors) + self.a * G_0; 238 | 239 | if expect_S == S { 240 | Ok(()) 241 | } else { 242 | Err(ProofError::VerificationError) 243 | } 244 | } 245 | 246 | /// Computes the vector of challenge scalars \\([x\_{i}]\\), and its inverse \\([x\_{i}^{-1}]\\) 247 | /// for combined multiscalar multiplication in a parent protocol. 248 | /// Also computes \\(b_0\\) which is the base case for public vector \\(b\\). 249 | /// 250 | /// The verifier must provide the input length \\(n\\) explicitly to avoid unbounded allocation. 251 | pub(crate) fn verification_scalars( 252 | &self, 253 | n: usize, 254 | transcript: &mut Transcript, 255 | mut b_vec: Vec, 256 | ) -> Result<(Vec, Vec, Scalar), ProofError> { 257 | let lg_n = self.L_vec.len(); 258 | if lg_n >= 32 { 259 | // 4 billion multiplications should be enough for anyone 260 | // and this check prevents overflow in 1<\\). 296 | /// 297 | /// Note that this is different from the Bulletproofs \\(s_i\\) generation, 298 | /// where \\(b(i, j)\\) = 1 if the jth bit of (i-1) is 1, and -1 otherwise. 299 | fn subset_product(&self, n: usize, challenges: Vec) -> Vec { 300 | let lg_n = self.L_vec.len(); 301 | 302 | let mut s = Vec::with_capacity(n); 303 | s.push(Scalar::one()); 304 | for i in 1..n { 305 | let lg_i = (32 - 1 - (i as u32).leading_zeros()) as usize; 306 | let k = 1 << lg_i; 307 | // The challenges are stored in "creation order" as [x_k,...,x_1], 308 | // so x_{lg(i)+1} = is indexed by (lg_n-1) - lg_i 309 | let x_lg_i = challenges[(lg_n - 1) - lg_i]; 310 | s.push(s[i - k] * x_lg_i); 311 | } 312 | 313 | s 314 | } 315 | 316 | /// Returns the size in bytes required to serialize the linear proof. 317 | /// 318 | /// For vectors of length `n` the proof size is 319 | /// \\(32 \cdot (2\lg n+3)\\) bytes. 320 | pub fn serialized_size(&self) -> usize { 321 | (self.L_vec.len() * 2 + 3) * 32 322 | } 323 | 324 | /// Serializes the proof into a byte array of \\(2n+3\\) 32-byte elements. 325 | /// The layout of the linear proof is: 326 | /// * \\(n\\) pairs of compressed Ristretto points \\(L_0, R_0 \dots, L_{n-1}, R_{n-1}\\), 327 | /// * one compressed Ristretto point \\(S\\), 328 | /// * two scalars \\(a, r\\). 329 | pub fn to_bytes(&self) -> Vec { 330 | let mut buf = Vec::with_capacity(self.serialized_size()); 331 | for (l, r) in self.L_vec.iter().zip(self.R_vec.iter()) { 332 | buf.extend_from_slice(l.as_bytes()); 333 | buf.extend_from_slice(r.as_bytes()); 334 | } 335 | buf.extend_from_slice(self.S.as_bytes()); 336 | buf.extend_from_slice(self.a.as_bytes()); 337 | buf.extend_from_slice(self.r.as_bytes()); 338 | buf 339 | } 340 | 341 | /// Converts the proof into a byte iterator over serialized view of the proof. 342 | /// The layout of the inner product proof is: 343 | /// * \\(n\\) pairs of compressed Ristretto points \\(L_0, R_0 \dots, L_{n-1}, R_{n-1}\\), 344 | /// * one compressed Ristretto point \\(S\\), 345 | /// * two scalars \\(a, r\\). 346 | #[inline] 347 | #[allow(dead_code)] 348 | pub(crate) fn to_bytes_iter(&self) -> impl Iterator + '_ { 349 | self.L_vec 350 | .iter() 351 | .zip(self.R_vec.iter()) 352 | .flat_map(|(l, r)| l.as_bytes().iter().chain(r.as_bytes())) 353 | .chain(self.S.as_bytes()) 354 | .chain(self.a.as_bytes()) 355 | .chain(self.r.as_bytes()) 356 | .copied() 357 | } 358 | 359 | /// Deserializes the proof from a byte slice. 360 | /// Returns an error in the following cases: 361 | /// * the slice does not have \\(2n+3\\) 32-byte elements, 362 | /// * \\(n\\) is larger or equal to 32 (proof is too big), 363 | /// * any of \\(2n + 1\\) points are not valid compressed Ristretto points, 364 | /// * any of 2 scalars are not canonical scalars modulo Ristretto group order. 365 | pub fn from_bytes(slice: &[u8]) -> Result { 366 | let b = slice.len(); 367 | if b % 32 != 0 { 368 | return Err(ProofError::FormatError); 369 | } 370 | let num_elements = b / 32; 371 | if num_elements < 3 { 372 | return Err(ProofError::FormatError); 373 | } 374 | if (num_elements - 3) % 2 != 0 { 375 | return Err(ProofError::FormatError); 376 | } 377 | let lg_n = (num_elements - 3) / 2; 378 | if lg_n >= 32 { 379 | return Err(ProofError::FormatError); 380 | } 381 | 382 | use crate::util::read32; 383 | 384 | let mut L_vec: Vec = Vec::with_capacity(lg_n); 385 | let mut R_vec: Vec = Vec::with_capacity(lg_n); 386 | for i in 0..lg_n { 387 | let pos = 2 * i * 32; 388 | L_vec.push(CompressedRistretto(read32(&slice[pos..]))); 389 | R_vec.push(CompressedRistretto(read32(&slice[pos + 32..]))); 390 | } 391 | 392 | let pos = 2 * lg_n * 32; 393 | let S = CompressedRistretto(read32(&slice[pos..])); 394 | let a = Scalar::from_canonical_bytes(read32(&slice[pos + 32..])) 395 | .ok_or(ProofError::FormatError)?; 396 | let r = Scalar::from_canonical_bytes(read32(&slice[pos + 64..])) 397 | .ok_or(ProofError::FormatError)?; 398 | 399 | Ok(LinearProof { 400 | L_vec, 401 | R_vec, 402 | S, 403 | a, 404 | r, 405 | }) 406 | } 407 | } 408 | 409 | #[cfg(test)] 410 | mod tests { 411 | use super::*; 412 | 413 | fn test_helper(n: usize) { 414 | let mut rng = rand::thread_rng(); 415 | 416 | use crate::generators::{BulletproofGens, PedersenGens}; 417 | let bp_gens = BulletproofGens::new(n, 1); 418 | let G: Vec = bp_gens.share(0).G(n).cloned().collect(); 419 | 420 | let pedersen_gens = PedersenGens::default(); 421 | let F = pedersen_gens.B; 422 | let B = pedersen_gens.B_blinding; 423 | 424 | // a and b are the vectors for which we want to prove c = 425 | // a is a private vector, b is a public vector 426 | let a: Vec<_> = (0..n).map(|_| Scalar::random(&mut rng)).collect(); 427 | let b: Vec<_> = (0..n).map(|_| Scalar::random(&mut rng)).collect(); 428 | 429 | let mut prover_transcript = Transcript::new(b"linearprooftest"); 430 | 431 | // C = + r * B + * F 432 | let r = Scalar::random(&mut rng); 433 | let c = inner_product(&a, &b); 434 | let C = RistrettoPoint::vartime_multiscalar_mul( 435 | a.iter().chain(iter::once(&r)).chain(iter::once(&c)), 436 | G.iter().chain(Some(&B)).chain(iter::once(&F)), 437 | ) 438 | .compress(); 439 | 440 | let proof = LinearProof::create( 441 | &mut prover_transcript, 442 | &mut rng, 443 | &C, 444 | r, 445 | a, 446 | b.clone(), 447 | G.clone(), 448 | &F, 449 | &B, 450 | ) 451 | .unwrap(); 452 | 453 | let mut verifier_transcript = Transcript::new(b"linearprooftest"); 454 | assert!(proof 455 | .verify(&mut verifier_transcript, &C, &G, &F, &B, b.clone()) 456 | .is_ok()); 457 | 458 | // Test serialization and deserialization 459 | let serialized_proof = proof.to_bytes(); 460 | assert_eq!(proof.serialized_size(), serialized_proof.len()); 461 | 462 | let deserialized_proof = LinearProof::from_bytes(&serialized_proof).unwrap(); 463 | let mut serde_verifier_transcript = Transcript::new(b"linearprooftest"); 464 | assert!(deserialized_proof 465 | .verify(&mut serde_verifier_transcript, &C, &G, &F, &B, b) 466 | .is_ok()); 467 | } 468 | 469 | #[test] 470 | fn test_linear_proof_base() { 471 | test_helper(1); 472 | } 473 | 474 | #[test] 475 | fn test_linear_proof_16() { 476 | test_helper(16); 477 | } 478 | 479 | #[test] 480 | fn test_linear_proof_32() { 481 | test_helper(32); 482 | } 483 | 484 | #[test] 485 | fn test_linear_proof_64() { 486 | test_helper(64); 487 | } 488 | } 489 | -------------------------------------------------------------------------------- /src/r1cs/constraint_system.rs: -------------------------------------------------------------------------------- 1 | //! Definition of the constraint system trait. 2 | 3 | use super::{LinearCombination, R1CSError, Variable}; 4 | use curve25519_dalek::scalar::Scalar; 5 | use merlin::Transcript; 6 | 7 | /// The interface for a constraint system, abstracting over the prover 8 | /// and verifier's roles. 9 | /// 10 | /// Statements to be proved by an [`R1CSProof`](::r1cs::R1CSProof) are specified by 11 | /// programmatically constructing constraints. These constraints need 12 | /// to be identical between the prover and verifier, since the prover 13 | /// and verifier need to construct the same statement. 14 | /// 15 | /// To prevent code duplication or mismatches between the prover and 16 | /// verifier, gadgets for the constraint system should be written 17 | /// using the `ConstraintSystem` trait, so that the prover and 18 | /// verifier share the logic for specifying constraints. 19 | pub trait ConstraintSystem { 20 | /// Leases the proof transcript to the user, so they can 21 | /// add extra data to which the proof must be bound, but which 22 | /// is not available before creation of the constraint system. 23 | fn transcript(&mut self) -> &mut Transcript; 24 | 25 | /// Allocate and constrain multiplication variables. 26 | /// 27 | /// Allocate variables `left`, `right`, and `out` 28 | /// with the implicit constraint that 29 | /// ```text 30 | /// left * right = out 31 | /// ``` 32 | /// and add the explicit constraints that 33 | /// ```text 34 | /// left = left_constraint 35 | /// right = right_constraint 36 | /// ``` 37 | /// 38 | /// Returns `(left, right, out)` for use in further constraints. 39 | fn multiply( 40 | &mut self, 41 | left: LinearCombination, 42 | right: LinearCombination, 43 | ) -> (Variable, Variable, Variable); 44 | 45 | /// Allocate a single variable. 46 | /// 47 | /// This either allocates a new multiplier and returns its `left` variable, 48 | /// or returns a `right` variable of a multiplier previously allocated by this method. 49 | /// The output of a multiplier is assigned on a even call, when `right` is assigned. 50 | /// 51 | /// When CS is committed at the end of the first or second phase, the half-assigned multiplier 52 | /// has the `right` assigned to zero and all its variables committed. 53 | /// 54 | /// Returns unconstrained `Variable` for use in further constraints. 55 | fn allocate(&mut self, assignment: Option) -> Result; 56 | 57 | /// Allocate variables `left`, `right`, and `out` 58 | /// with the implicit constraint that 59 | /// ```text 60 | /// left * right = out 61 | /// ``` 62 | /// 63 | /// Returns `(left, right, out)` for use in further constraints. 64 | fn allocate_multiplier( 65 | &mut self, 66 | input_assignments: Option<(Scalar, Scalar)>, 67 | ) -> Result<(Variable, Variable, Variable), R1CSError>; 68 | 69 | /// Counts the amount of allocated multipliers. 70 | fn multipliers_len(&self) -> usize; 71 | 72 | /// Enforce the explicit constraint that 73 | /// ```text 74 | /// lc = 0 75 | /// ``` 76 | fn constrain(&mut self, lc: LinearCombination); 77 | } 78 | 79 | /// An extension to the constraint system trait that permits randomized constraints. 80 | /// Gadgets that do not use randomization should use trait bound `CS: ConstraintSystem`, 81 | /// while gadgets that need randomization should use trait bound `CS: RandomizedConstraintSystem`. 82 | /// Gadgets generally _should not_ use this trait as a bound on the CS argument: it should be used 83 | /// by the higher-order protocol that composes gadgets together. 84 | pub trait RandomizableConstraintSystem: ConstraintSystem { 85 | /// Represents a concrete type for the CS in a randomization phase. 86 | type RandomizedCS: RandomizedConstraintSystem; 87 | 88 | /// Specify additional variables and constraints randomized using a challenge scalar 89 | /// bound to the assignments of the non-randomized variables. 90 | /// 91 | /// If the constraint system’s low-level variables have not been committed yet, 92 | /// the call returns `Ok()` and saves a callback until later. 93 | /// 94 | /// If the constraint system’s low-level variables are committed already, 95 | /// the callback is invoked immediately and its result is return from this method. 96 | /// 97 | /// ### Usage 98 | /// 99 | /// Inside the closure you can generate one or more challenges using `challenge_scalar` method. 100 | /// 101 | /// ```text 102 | /// cs.specify_randomized_constraints(move |cs| { 103 | /// let z = cs.challenge_scalar(b"some challenge"); 104 | /// // ... 105 | /// }) 106 | /// ``` 107 | fn specify_randomized_constraints(&mut self, callback: F) -> Result<(), R1CSError> 108 | where 109 | F: 'static + Fn(&mut Self::RandomizedCS) -> Result<(), R1CSError>; 110 | } 111 | 112 | /// Represents a constraint system in the second phase: 113 | /// when the challenges can be sampled to create randomized constraints. 114 | /// 115 | /// Note: this trait also includes `ConstraintSystem` trait 116 | /// in order to allow composition of gadgets: e.g. a shuffle gadget can be used in both phases. 117 | pub trait RandomizedConstraintSystem: ConstraintSystem { 118 | /// Generates a challenge scalar. 119 | /// 120 | /// ### Usage 121 | /// 122 | /// This method is available only within the scope of a closure provided 123 | /// to `specify_randomized_constraints`, which implements 124 | /// the "randomization" phase of the protocol. 125 | /// 126 | /// Arbitrary number of challenges can be generated with additional calls. 127 | /// 128 | /// ```text 129 | /// cs.specify_randomized_constraints(move |cs| { 130 | /// let z = cs.challenge_scalar(b"some challenge"); 131 | /// // ... 132 | /// }) 133 | /// ``` 134 | fn challenge_scalar(&mut self, label: &'static [u8]) -> Scalar; 135 | } 136 | -------------------------------------------------------------------------------- /src/r1cs/linear_combination.rs: -------------------------------------------------------------------------------- 1 | //! Definition of linear combinations. 2 | 3 | use curve25519_dalek::scalar::Scalar; 4 | use std::iter::FromIterator; 5 | use std::ops::{Add, Mul, Neg, Sub}; 6 | 7 | /// Represents a variable in a constraint system. 8 | #[derive(Copy, Clone, Debug, PartialEq)] 9 | pub enum Variable { 10 | /// Represents an external input specified by a commitment. 11 | Committed(usize), 12 | /// Represents the left input of a multiplication gate. 13 | MultiplierLeft(usize), 14 | /// Represents the right input of a multiplication gate. 15 | MultiplierRight(usize), 16 | /// Represents the output of a multiplication gate. 17 | MultiplierOutput(usize), 18 | /// Represents the constant 1. 19 | One(), 20 | } 21 | 22 | impl From for LinearCombination { 23 | fn from(v: Variable) -> LinearCombination { 24 | LinearCombination { 25 | terms: vec![(v, Scalar::one())], 26 | } 27 | } 28 | } 29 | 30 | impl> From for LinearCombination { 31 | fn from(s: S) -> LinearCombination { 32 | LinearCombination { 33 | terms: vec![(Variable::One(), s.into())], 34 | } 35 | } 36 | } 37 | 38 | // Arithmetic on variables produces linear combinations 39 | 40 | impl Neg for Variable { 41 | type Output = LinearCombination; 42 | 43 | fn neg(self) -> Self::Output { 44 | -LinearCombination::from(self) 45 | } 46 | } 47 | 48 | impl> Add for Variable { 49 | type Output = LinearCombination; 50 | 51 | fn add(self, other: L) -> Self::Output { 52 | LinearCombination::from(self) + other.into() 53 | } 54 | } 55 | 56 | impl> Sub for Variable { 57 | type Output = LinearCombination; 58 | 59 | fn sub(self, other: L) -> Self::Output { 60 | LinearCombination::from(self) - other.into() 61 | } 62 | } 63 | 64 | impl> Mul for Variable { 65 | type Output = LinearCombination; 66 | 67 | fn mul(self, other: S) -> Self::Output { 68 | LinearCombination { 69 | terms: vec![(self, other.into())], 70 | } 71 | } 72 | } 73 | 74 | // Arithmetic on scalars with variables produces linear combinations 75 | 76 | impl Add for Scalar { 77 | type Output = LinearCombination; 78 | 79 | fn add(self, other: Variable) -> Self::Output { 80 | LinearCombination { 81 | terms: vec![(Variable::One(), self), (other, Scalar::one())], 82 | } 83 | } 84 | } 85 | 86 | impl Sub for Scalar { 87 | type Output = LinearCombination; 88 | 89 | fn sub(self, other: Variable) -> Self::Output { 90 | LinearCombination { 91 | terms: vec![(Variable::One(), self), (other, -Scalar::one())], 92 | } 93 | } 94 | } 95 | 96 | impl Mul for Scalar { 97 | type Output = LinearCombination; 98 | 99 | fn mul(self, other: Variable) -> Self::Output { 100 | LinearCombination { 101 | terms: vec![(other, self)], 102 | } 103 | } 104 | } 105 | 106 | /// Represents a linear combination of 107 | /// [`Variables`](::r1cs::Variable). Each term is represented by a 108 | /// `(Variable, Scalar)` pair. 109 | #[derive(Clone, Debug, PartialEq)] 110 | pub struct LinearCombination { 111 | pub(super) terms: Vec<(Variable, Scalar)>, 112 | } 113 | 114 | impl Default for LinearCombination { 115 | fn default() -> Self { 116 | LinearCombination { terms: Vec::new() } 117 | } 118 | } 119 | 120 | impl FromIterator<(Variable, Scalar)> for LinearCombination { 121 | fn from_iter(iter: T) -> Self 122 | where 123 | T: IntoIterator, 124 | { 125 | LinearCombination { 126 | terms: iter.into_iter().collect(), 127 | } 128 | } 129 | } 130 | 131 | impl<'a> FromIterator<&'a (Variable, Scalar)> for LinearCombination { 132 | fn from_iter(iter: T) -> Self 133 | where 134 | T: IntoIterator, 135 | { 136 | LinearCombination { 137 | terms: iter.into_iter().cloned().collect(), 138 | } 139 | } 140 | } 141 | 142 | // Arithmetic on linear combinations 143 | 144 | impl> Add for LinearCombination { 145 | type Output = Self; 146 | 147 | fn add(mut self, rhs: L) -> Self::Output { 148 | self.terms.extend(rhs.into().terms.iter().cloned()); 149 | LinearCombination { terms: self.terms } 150 | } 151 | } 152 | 153 | impl> Sub for LinearCombination { 154 | type Output = Self; 155 | 156 | fn sub(mut self, rhs: L) -> Self::Output { 157 | self.terms 158 | .extend(rhs.into().terms.iter().map(|(var, coeff)| (*var, -coeff))); 159 | LinearCombination { terms: self.terms } 160 | } 161 | } 162 | 163 | impl Mul for Scalar { 164 | type Output = LinearCombination; 165 | 166 | fn mul(self, other: LinearCombination) -> Self::Output { 167 | let out_terms = other 168 | .terms 169 | .into_iter() 170 | .map(|(var, scalar)| (var, scalar * self)) 171 | .collect(); 172 | LinearCombination { terms: out_terms } 173 | } 174 | } 175 | 176 | impl Neg for LinearCombination { 177 | type Output = Self; 178 | 179 | fn neg(mut self) -> Self::Output { 180 | for (_, s) in self.terms.iter_mut() { 181 | *s = -*s 182 | } 183 | self 184 | } 185 | } 186 | 187 | impl> Mul for LinearCombination { 188 | type Output = Self; 189 | 190 | fn mul(mut self, other: S) -> Self::Output { 191 | let other = other.into(); 192 | for (_, s) in self.terms.iter_mut() { 193 | *s *= other 194 | } 195 | self 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/r1cs/mod.rs: -------------------------------------------------------------------------------- 1 | #![doc(include = "../../docs/r1cs-docs-example.md")] 2 | 3 | #[doc(include = "../../docs/cs-proof.md")] 4 | mod notes {} 5 | 6 | mod constraint_system; 7 | mod linear_combination; 8 | mod proof; 9 | mod prover; 10 | mod verifier; 11 | 12 | pub use self::constraint_system::{ 13 | ConstraintSystem, RandomizableConstraintSystem, RandomizedConstraintSystem, 14 | }; 15 | pub use self::linear_combination::{LinearCombination, Variable}; 16 | pub use self::proof::R1CSProof; 17 | pub use self::prover::Prover; 18 | pub use self::verifier::Verifier; 19 | 20 | pub use crate::errors::R1CSError; 21 | -------------------------------------------------------------------------------- /src/r1cs/proof.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | //! Definition of the proof struct. 3 | 4 | use curve25519_dalek::ristretto::CompressedRistretto; 5 | use curve25519_dalek::scalar::Scalar; 6 | use curve25519_dalek::traits::{Identity, IsIdentity}; 7 | 8 | use crate::errors::R1CSError; 9 | use crate::inner_product_proof::InnerProductProof; 10 | use crate::util; 11 | 12 | use serde::de::Visitor; 13 | use serde::{self, Deserialize, Deserializer, Serialize, Serializer}; 14 | 15 | const ONE_PHASE_COMMITMENTS: u8 = 0; 16 | const TWO_PHASE_COMMITMENTS: u8 = 1; 17 | 18 | /// A proof of some statement specified by a 19 | /// [`ConstraintSystem`](::r1cs::ConstraintSystem). 20 | /// 21 | /// Statements are specified by writing gadget functions which add 22 | /// constraints to a [`ConstraintSystem`](::r1cs::ConstraintSystem) 23 | /// implementation. To construct an [`R1CSProof`], a prover constructs 24 | /// a [`ProverCS`](::r1cs::ProverCS), then passes it to gadget 25 | /// functions to build the constraint system, then consumes the 26 | /// constraint system using 27 | /// [`ProverCS::prove`](::r1cs::ProverCS::prove) to produce an 28 | /// [`R1CSProof`]. To verify an [`R1CSProof`], a verifier constructs a 29 | /// [`VerifierCS`](::r1cs::VerifierCS), then passes it to the same 30 | /// gadget functions to (re)build the constraint system, then consumes 31 | /// the constraint system using 32 | /// [`VerifierCS::verify`](::r1cs::VerifierCS::verify) to verify the 33 | /// proof. 34 | #[derive(Clone, Debug)] 35 | #[allow(non_snake_case)] 36 | pub struct R1CSProof { 37 | /// Commitment to the values of input wires in the first phase. 38 | pub(super) A_I1: CompressedRistretto, 39 | /// Commitment to the values of output wires in the first phase. 40 | pub(super) A_O1: CompressedRistretto, 41 | /// Commitment to the blinding factors in the first phase. 42 | pub(super) S1: CompressedRistretto, 43 | /// Commitment to the values of input wires in the second phase. 44 | pub(super) A_I2: CompressedRistretto, 45 | /// Commitment to the values of output wires in the second phase. 46 | pub(super) A_O2: CompressedRistretto, 47 | /// Commitment to the blinding factors in the second phase. 48 | pub(super) S2: CompressedRistretto, 49 | /// Commitment to the \\(t_1\\) coefficient of \\( t(x) \\) 50 | pub(super) T_1: CompressedRistretto, 51 | /// Commitment to the \\(t_3\\) coefficient of \\( t(x) \\) 52 | pub(super) T_3: CompressedRistretto, 53 | /// Commitment to the \\(t_4\\) coefficient of \\( t(x) \\) 54 | pub(super) T_4: CompressedRistretto, 55 | /// Commitment to the \\(t_5\\) coefficient of \\( t(x) \\) 56 | pub(super) T_5: CompressedRistretto, 57 | /// Commitment to the \\(t_6\\) coefficient of \\( t(x) \\) 58 | pub(super) T_6: CompressedRistretto, 59 | /// Evaluation of the polynomial \\(t(x)\\) at the challenge point \\(x\\) 60 | pub(super) t_x: Scalar, 61 | /// Blinding factor for the synthetic commitment to \\( t(x) \\) 62 | pub(super) t_x_blinding: Scalar, 63 | /// Blinding factor for the synthetic commitment to the 64 | /// inner-product arguments 65 | pub(super) e_blinding: Scalar, 66 | /// Proof data for the inner-product argument. 67 | pub(super) ipp_proof: InnerProductProof, 68 | } 69 | 70 | impl R1CSProof { 71 | /// Serializes the proof into a byte array of 1 version byte + \\((13 or 16) + 2k\\) 32-byte elements, 72 | /// where \\(k=\lceil \log_2(n) \rceil\\) and \\(n\\) is the number of multiplication gates. 73 | /// 74 | /// # Layout 75 | /// 76 | /// The layout of the r1cs proof encoding is: 77 | /// * 1 version byte indicating whether the proof contains second-phase commitments or not, 78 | /// * 8 or 11 compressed Ristretto points \\(A_{I1},A_{O1},S_1,(A_{I2},A_{O2},S_2),T_1,...,T_6\\) 79 | /// (\\(A_{I2},A_{O2},S_2\\) are skipped if there were no multipliers added in the randomized phase), 80 | /// * three scalars \\(t_x, \tilde{t}_x, \tilde{e}\\), 81 | /// * \\(k\\) pairs of compressed Ristretto points \\(L_0,R_0\dots,L_{k-1},R_{k-1}\\), 82 | /// * two scalars \\(a, b\\). 83 | pub fn to_bytes(&self) -> Vec { 84 | let mut buf = Vec::with_capacity(self.serialized_size()); 85 | if self.missing_phase2_commitments() { 86 | buf.push(ONE_PHASE_COMMITMENTS); 87 | buf.extend_from_slice(self.A_I1.as_bytes()); 88 | buf.extend_from_slice(self.A_O1.as_bytes()); 89 | buf.extend_from_slice(self.S1.as_bytes()); 90 | } else { 91 | buf.push(TWO_PHASE_COMMITMENTS); 92 | buf.extend_from_slice(self.A_I1.as_bytes()); 93 | buf.extend_from_slice(self.A_O1.as_bytes()); 94 | buf.extend_from_slice(self.S1.as_bytes()); 95 | buf.extend_from_slice(self.A_I2.as_bytes()); 96 | buf.extend_from_slice(self.A_O2.as_bytes()); 97 | buf.extend_from_slice(self.S2.as_bytes()); 98 | } 99 | buf.extend_from_slice(self.T_1.as_bytes()); 100 | buf.extend_from_slice(self.T_3.as_bytes()); 101 | buf.extend_from_slice(self.T_4.as_bytes()); 102 | buf.extend_from_slice(self.T_5.as_bytes()); 103 | buf.extend_from_slice(self.T_6.as_bytes()); 104 | buf.extend_from_slice(self.t_x.as_bytes()); 105 | buf.extend_from_slice(self.t_x_blinding.as_bytes()); 106 | buf.extend_from_slice(self.e_blinding.as_bytes()); 107 | buf.extend(self.ipp_proof.to_bytes_iter()); 108 | buf 109 | } 110 | 111 | /// Returns the size in bytes required to serialize the `R1CSProof`. 112 | pub fn serialized_size(&self) -> usize { 113 | // version tag + (11 or 14) elements + the ipp 114 | let elements = if self.missing_phase2_commitments() { 115 | 11 116 | } else { 117 | 14 118 | }; 119 | 1 + elements * 32 + self.ipp_proof.serialized_size() 120 | } 121 | 122 | fn missing_phase2_commitments(&self) -> bool { 123 | self.A_I2.is_identity() && self.A_O2.is_identity() && self.S2.is_identity() 124 | } 125 | 126 | /// Deserializes the proof from a byte slice. 127 | /// 128 | /// Returns an error if the byte slice cannot be parsed into a `R1CSProof`. 129 | pub fn from_bytes(slice: &[u8]) -> Result { 130 | if slice.len() < 1 { 131 | return Err(R1CSError::FormatError); 132 | } 133 | let version = slice[0]; 134 | let mut slice = &slice[1..]; 135 | 136 | if slice.len() % 32 != 0 { 137 | return Err(R1CSError::FormatError); 138 | } 139 | 140 | let minlength = match version { 141 | ONE_PHASE_COMMITMENTS => 11 * 32, 142 | TWO_PHASE_COMMITMENTS => 14 * 32, 143 | _ => return Err(R1CSError::FormatError), 144 | }; 145 | 146 | if slice.len() < minlength { 147 | return Err(R1CSError::FormatError); 148 | } 149 | 150 | // This macro takes care of counting bytes in the slice 151 | macro_rules! read32 { 152 | () => {{ 153 | let tmp = util::read32(slice); 154 | slice = &slice[32..]; 155 | tmp 156 | }}; 157 | } 158 | 159 | let A_I1 = CompressedRistretto(read32!()); 160 | let A_O1 = CompressedRistretto(read32!()); 161 | let S1 = CompressedRistretto(read32!()); 162 | let (A_I2, A_O2, S2) = if version == ONE_PHASE_COMMITMENTS { 163 | ( 164 | CompressedRistretto::identity(), 165 | CompressedRistretto::identity(), 166 | CompressedRistretto::identity(), 167 | ) 168 | } else { 169 | ( 170 | CompressedRistretto(read32!()), 171 | CompressedRistretto(read32!()), 172 | CompressedRistretto(read32!()), 173 | ) 174 | }; 175 | let T_1 = CompressedRistretto(read32!()); 176 | let T_3 = CompressedRistretto(read32!()); 177 | let T_4 = CompressedRistretto(read32!()); 178 | let T_5 = CompressedRistretto(read32!()); 179 | let T_6 = CompressedRistretto(read32!()); 180 | let t_x = Scalar::from_canonical_bytes(read32!()).ok_or(R1CSError::FormatError)?; 181 | let t_x_blinding = Scalar::from_canonical_bytes(read32!()).ok_or(R1CSError::FormatError)?; 182 | let e_blinding = Scalar::from_canonical_bytes(read32!()).ok_or(R1CSError::FormatError)?; 183 | 184 | // XXX: IPPProof from_bytes gives ProofError. 185 | let ipp_proof = InnerProductProof::from_bytes(slice).map_err(|_| R1CSError::FormatError)?; 186 | 187 | Ok(R1CSProof { 188 | A_I1, 189 | A_O1, 190 | S1, 191 | A_I2, 192 | A_O2, 193 | S2, 194 | T_1, 195 | T_3, 196 | T_4, 197 | T_5, 198 | T_6, 199 | t_x, 200 | t_x_blinding, 201 | e_blinding, 202 | ipp_proof, 203 | }) 204 | } 205 | } 206 | 207 | impl Serialize for R1CSProof { 208 | fn serialize(&self, serializer: S) -> Result 209 | where 210 | S: Serializer, 211 | { 212 | serializer.serialize_bytes(&self.to_bytes()[..]) 213 | } 214 | } 215 | 216 | impl<'de> Deserialize<'de> for R1CSProof { 217 | fn deserialize(deserializer: D) -> Result 218 | where 219 | D: Deserializer<'de>, 220 | { 221 | struct R1CSProofVisitor; 222 | 223 | impl<'de> Visitor<'de> for R1CSProofVisitor { 224 | type Value = R1CSProof; 225 | 226 | fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { 227 | formatter.write_str("a valid R1CSProof") 228 | } 229 | 230 | fn visit_bytes(self, v: &[u8]) -> Result 231 | where 232 | E: serde::de::Error, 233 | { 234 | // Using Error::custom requires T: Display, which our error 235 | // type only implements when it implements std::error::Error. 236 | #[cfg(feature = "std")] 237 | return R1CSProof::from_bytes(v).map_err(serde::de::Error::custom); 238 | // In no-std contexts, drop the error message. 239 | #[cfg(not(feature = "std"))] 240 | return R1CSProof::from_bytes(v) 241 | .map_err(|_| serde::de::Error::custom("deserialization error")); 242 | } 243 | } 244 | 245 | deserializer.deserialize_bytes(R1CSProofVisitor) 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /src/range_proof/dealer.rs: -------------------------------------------------------------------------------- 1 | //! The `dealer` module contains the API for the dealer state while the dealer is 2 | //! engaging in an aggregated multiparty computation protocol. 3 | //! 4 | //! For more explanation of how the `dealer`, `party`, and `messages` modules orchestrate the protocol execution, see 5 | //! [the API for the aggregated multiparty computation protocol](../aggregation/index.html#api-for-the-aggregated-multiparty-computation-protocol). 6 | 7 | use core::iter; 8 | 9 | extern crate alloc; 10 | 11 | use alloc::vec::Vec; 12 | 13 | use curve25519_dalek::ristretto::RistrettoPoint; 14 | use curve25519_dalek::scalar::Scalar; 15 | use merlin::Transcript; 16 | 17 | use crate::errors::MPCError; 18 | use crate::generators::{BulletproofGens, PedersenGens}; 19 | use crate::inner_product_proof; 20 | use crate::range_proof::RangeProof; 21 | use crate::transcript::TranscriptProtocol; 22 | 23 | use rand_core::{CryptoRng, RngCore}; 24 | 25 | use crate::util; 26 | 27 | #[cfg(feature = "std")] 28 | use rand::thread_rng; 29 | 30 | use super::messages::*; 31 | 32 | /// Used to construct a dealer for the aggregated rangeproof MPC protocol. 33 | pub struct Dealer {} 34 | 35 | impl Dealer { 36 | /// Creates a new dealer coordinating `m` parties proving `n`-bit ranges. 37 | pub fn new<'a, 'b>( 38 | bp_gens: &'b BulletproofGens, 39 | pc_gens: &'b PedersenGens, 40 | transcript: &'a mut Transcript, 41 | n: usize, 42 | m: usize, 43 | ) -> Result, MPCError> { 44 | if !(n == 8 || n == 16 || n == 32 || n == 64) { 45 | return Err(MPCError::InvalidBitsize); 46 | } 47 | if !m.is_power_of_two() { 48 | return Err(MPCError::InvalidAggregation); 49 | } 50 | if bp_gens.gens_capacity < n { 51 | return Err(MPCError::InvalidGeneratorsLength); 52 | } 53 | if bp_gens.party_capacity < m { 54 | return Err(MPCError::InvalidGeneratorsLength); 55 | } 56 | 57 | // At the end of the protocol, the dealer will attempt to 58 | // verify the proof, and if it fails, determine which party's 59 | // shares were invalid. 60 | // 61 | // However, verifying the proof requires either knowledge of 62 | // all of the challenges, or a copy of the initial transcript 63 | // state. 64 | // 65 | // The dealer has all of the challenges, but using them for 66 | // verification would require duplicating the verification 67 | // logic. Instead, we keep a copy of the initial transcript 68 | // state. 69 | let initial_transcript = transcript.clone(); 70 | 71 | transcript.rangeproof_domain_sep(n as u64, m as u64); 72 | 73 | Ok(DealerAwaitingBitCommitments { 74 | bp_gens, 75 | pc_gens, 76 | transcript, 77 | initial_transcript, 78 | n, 79 | m, 80 | }) 81 | } 82 | } 83 | 84 | /// A dealer waiting for the parties to send their [`BitCommitment`]s. 85 | pub struct DealerAwaitingBitCommitments<'a, 'b> { 86 | bp_gens: &'b BulletproofGens, 87 | pc_gens: &'b PedersenGens, 88 | transcript: &'a mut Transcript, 89 | /// The dealer keeps a copy of the initial transcript state, so 90 | /// that it can attempt to verify the aggregated proof at the end. 91 | initial_transcript: Transcript, 92 | n: usize, 93 | m: usize, 94 | } 95 | 96 | impl<'a, 'b> DealerAwaitingBitCommitments<'a, 'b> { 97 | /// Receive each party's [`BitCommitment`]s and compute the [`BitChallenge`]. 98 | pub fn receive_bit_commitments( 99 | self, 100 | bit_commitments: Vec, 101 | ) -> Result<(DealerAwaitingPolyCommitments<'a, 'b>, BitChallenge), MPCError> { 102 | if self.m != bit_commitments.len() { 103 | return Err(MPCError::WrongNumBitCommitments); 104 | } 105 | 106 | // Commit each V_j individually 107 | for vc in bit_commitments.iter() { 108 | self.transcript.append_point(b"V", &vc.V_j); 109 | } 110 | 111 | // Commit aggregated A_j, S_j 112 | let A: RistrettoPoint = bit_commitments.iter().map(|vc| vc.A_j).sum(); 113 | self.transcript.append_point(b"A", &A.compress()); 114 | 115 | let S: RistrettoPoint = bit_commitments.iter().map(|vc| vc.S_j).sum(); 116 | self.transcript.append_point(b"S", &S.compress()); 117 | 118 | let y = self.transcript.challenge_scalar(b"y"); 119 | let z = self.transcript.challenge_scalar(b"z"); 120 | let bit_challenge = BitChallenge { y, z }; 121 | 122 | Ok(( 123 | DealerAwaitingPolyCommitments { 124 | n: self.n, 125 | m: self.m, 126 | transcript: self.transcript, 127 | initial_transcript: self.initial_transcript, 128 | bp_gens: self.bp_gens, 129 | pc_gens: self.pc_gens, 130 | bit_challenge, 131 | bit_commitments, 132 | A, 133 | S, 134 | }, 135 | bit_challenge, 136 | )) 137 | } 138 | } 139 | 140 | /// A dealer which has sent the [`BitChallenge`] to the parties and 141 | /// is waiting for their [`PolyCommitment`]s. 142 | pub struct DealerAwaitingPolyCommitments<'a, 'b> { 143 | n: usize, 144 | m: usize, 145 | transcript: &'a mut Transcript, 146 | initial_transcript: Transcript, 147 | bp_gens: &'b BulletproofGens, 148 | pc_gens: &'b PedersenGens, 149 | bit_challenge: BitChallenge, 150 | bit_commitments: Vec, 151 | /// Aggregated commitment to the parties' bits 152 | A: RistrettoPoint, 153 | /// Aggregated commitment to the parties' bit blindings 154 | S: RistrettoPoint, 155 | } 156 | 157 | impl<'a, 'b> DealerAwaitingPolyCommitments<'a, 'b> { 158 | /// Receive [`PolyCommitment`]s from the parties and compute the 159 | /// [`PolyChallenge`]. 160 | pub fn receive_poly_commitments( 161 | self, 162 | poly_commitments: Vec, 163 | ) -> Result<(DealerAwaitingProofShares<'a, 'b>, PolyChallenge), MPCError> { 164 | if self.m != poly_commitments.len() { 165 | return Err(MPCError::WrongNumPolyCommitments); 166 | } 167 | 168 | // Commit sums of T_1_j's and T_2_j's 169 | let T_1: RistrettoPoint = poly_commitments.iter().map(|pc| pc.T_1_j).sum(); 170 | let T_2: RistrettoPoint = poly_commitments.iter().map(|pc| pc.T_2_j).sum(); 171 | 172 | self.transcript.append_point(b"T_1", &T_1.compress()); 173 | self.transcript.append_point(b"T_2", &T_2.compress()); 174 | 175 | let x = self.transcript.challenge_scalar(b"x"); 176 | let poly_challenge = PolyChallenge { x }; 177 | 178 | Ok(( 179 | DealerAwaitingProofShares { 180 | n: self.n, 181 | m: self.m, 182 | transcript: self.transcript, 183 | initial_transcript: self.initial_transcript, 184 | bp_gens: self.bp_gens, 185 | pc_gens: self.pc_gens, 186 | bit_challenge: self.bit_challenge, 187 | bit_commitments: self.bit_commitments, 188 | A: self.A, 189 | S: self.S, 190 | poly_challenge, 191 | poly_commitments, 192 | T_1, 193 | T_2, 194 | }, 195 | poly_challenge, 196 | )) 197 | } 198 | } 199 | 200 | /// A dealer which has sent the [`PolyChallenge`] to the parties and 201 | /// is waiting to aggregate their [`ProofShare`]s into a 202 | /// [`RangeProof`]. 203 | pub struct DealerAwaitingProofShares<'a, 'b> { 204 | n: usize, 205 | m: usize, 206 | transcript: &'a mut Transcript, 207 | initial_transcript: Transcript, 208 | bp_gens: &'b BulletproofGens, 209 | pc_gens: &'b PedersenGens, 210 | bit_challenge: BitChallenge, 211 | bit_commitments: Vec, 212 | poly_challenge: PolyChallenge, 213 | poly_commitments: Vec, 214 | A: RistrettoPoint, 215 | S: RistrettoPoint, 216 | T_1: RistrettoPoint, 217 | T_2: RistrettoPoint, 218 | } 219 | 220 | impl<'a, 'b> DealerAwaitingProofShares<'a, 'b> { 221 | /// Assembles proof shares into an `RangeProof`. 222 | /// 223 | /// Used as a helper function by `receive_trusted_shares` (which 224 | /// just hands back the result) and `receive_shares` (which 225 | /// validates the proof shares. 226 | fn assemble_shares(&mut self, proof_shares: &[ProofShare]) -> Result { 227 | if self.m != proof_shares.len() { 228 | return Err(MPCError::WrongNumProofShares); 229 | } 230 | 231 | // Validate lengths for each share 232 | let mut bad_shares = Vec::::new(); // no allocations until we append 233 | for (j, share) in proof_shares.iter().enumerate() { 234 | share 235 | .check_size(self.n, &self.bp_gens, j) 236 | .unwrap_or_else(|_| { 237 | bad_shares.push(j); 238 | }); 239 | } 240 | 241 | if bad_shares.len() > 0 { 242 | return Err(MPCError::MalformedProofShares { bad_shares }); 243 | } 244 | 245 | let t_x: Scalar = proof_shares.iter().map(|ps| ps.t_x).sum(); 246 | let t_x_blinding: Scalar = proof_shares.iter().map(|ps| ps.t_x_blinding).sum(); 247 | let e_blinding: Scalar = proof_shares.iter().map(|ps| ps.e_blinding).sum(); 248 | 249 | self.transcript.append_scalar(b"t_x", &t_x); 250 | self.transcript 251 | .append_scalar(b"t_x_blinding", &t_x_blinding); 252 | self.transcript.append_scalar(b"e_blinding", &e_blinding); 253 | 254 | // Get a challenge value to combine statements for the IPP 255 | let w = self.transcript.challenge_scalar(b"w"); 256 | let Q = w * self.pc_gens.B; 257 | 258 | let G_factors: Vec = iter::repeat(Scalar::one()).take(self.n * self.m).collect(); 259 | let H_factors: Vec = util::exp_iter(self.bit_challenge.y.invert()) 260 | .take(self.n * self.m) 261 | .collect(); 262 | 263 | let l_vec: Vec = proof_shares 264 | .iter() 265 | .flat_map(|ps| ps.l_vec.clone().into_iter()) 266 | .collect(); 267 | let r_vec: Vec = proof_shares 268 | .iter() 269 | .flat_map(|ps| ps.r_vec.clone().into_iter()) 270 | .collect(); 271 | 272 | let ipp_proof = inner_product_proof::InnerProductProof::create( 273 | self.transcript, 274 | &Q, 275 | &G_factors, 276 | &H_factors, 277 | self.bp_gens.G(self.n, self.m).cloned().collect(), 278 | self.bp_gens.H(self.n, self.m).cloned().collect(), 279 | l_vec, 280 | r_vec, 281 | ); 282 | 283 | Ok(RangeProof { 284 | A: self.A.compress(), 285 | S: self.S.compress(), 286 | T_1: self.T_1.compress(), 287 | T_2: self.T_2.compress(), 288 | t_x, 289 | t_x_blinding, 290 | e_blinding, 291 | ipp_proof, 292 | }) 293 | } 294 | 295 | /// Assemble the final aggregated [`RangeProof`] from the given 296 | /// `proof_shares`, then validate the proof to ensure that all 297 | /// `ProofShare`s were well-formed. 298 | /// 299 | /// This is a convenience wrapper around receive_shares_with_rng 300 | /// 301 | #[cfg(feature = "std")] 302 | pub fn receive_shares(self, proof_shares: &[ProofShare]) -> Result { 303 | self.receive_shares_with_rng(proof_shares, &mut thread_rng()) 304 | } 305 | 306 | /// Assemble the final aggregated [`RangeProof`] from the given 307 | /// `proof_shares`, then validate the proof to ensure that all 308 | /// `ProofShare`s were well-formed. 309 | /// 310 | /// If the aggregated proof fails to validate, this function 311 | /// audits the submitted shares to determine which shares were 312 | /// invalid. This information is returned as part of the 313 | /// [`MPCError`]. 314 | /// 315 | /// If the proof shares are known to be trusted, for instance when 316 | /// performing local aggregation, 317 | /// [`receive_trusted_shares`](DealerAwaitingProofShares::receive_trusted_shares) 318 | /// saves time by skipping verification of the aggregated proof. 319 | pub fn receive_shares_with_rng( 320 | mut self, 321 | proof_shares: &[ProofShare], 322 | rng: &mut T, 323 | ) -> Result { 324 | let proof = self.assemble_shares(proof_shares)?; 325 | 326 | let Vs: Vec<_> = self.bit_commitments.iter().map(|vc| vc.V_j).collect(); 327 | 328 | // See comment in `Dealer::new` for why we use `initial_transcript` 329 | let transcript = &mut self.initial_transcript; 330 | if proof 331 | .verify_multiple_with_rng(self.bp_gens, self.pc_gens, transcript, &Vs, self.n, rng) 332 | .is_ok() 333 | { 334 | Ok(proof) 335 | } else { 336 | // Proof verification failed. Now audit the parties: 337 | let mut bad_shares = Vec::new(); 338 | for j in 0..self.m { 339 | match proof_shares[j].audit_share( 340 | &self.bp_gens, 341 | &self.pc_gens, 342 | j, 343 | &self.bit_commitments[j], 344 | &self.bit_challenge, 345 | &self.poly_commitments[j], 346 | &self.poly_challenge, 347 | ) { 348 | Ok(_) => {} 349 | Err(_) => bad_shares.push(j), 350 | } 351 | } 352 | Err(MPCError::MalformedProofShares { bad_shares }) 353 | } 354 | } 355 | 356 | /// Assemble the final aggregated [`RangeProof`] from the given 357 | /// `proof_shares`, but skip validation of the proof. 358 | /// 359 | /// ## WARNING 360 | /// 361 | /// This function does **NOT** validate the proof shares. It is 362 | /// suitable for creating aggregated proofs when all parties are 363 | /// known by the dealer to be honest (for instance, when there's 364 | /// only one party playing all roles). 365 | /// 366 | /// Otherwise, use 367 | /// [`receive_shares`](DealerAwaitingProofShares::receive_shares), 368 | /// which validates that all shares are well-formed, or else 369 | /// detects which party(ies) submitted malformed shares. 370 | pub fn receive_trusted_shares( 371 | mut self, 372 | proof_shares: &[ProofShare], 373 | ) -> Result { 374 | self.assemble_shares(proof_shares) 375 | } 376 | } 377 | -------------------------------------------------------------------------------- /src/range_proof/messages.rs: -------------------------------------------------------------------------------- 1 | //! The `messages` module contains the API for the messages passed between the parties and the dealer 2 | //! in an aggregated multiparty computation protocol. 3 | //! 4 | //! For more explanation of how the `dealer`, `party`, and `messages` modules orchestrate the protocol execution, see 5 | //! [the API for the aggregated multiparty computation protocol](../aggregation/index.html#api-for-the-aggregated-multiparty-computation-protocol). 6 | 7 | extern crate alloc; 8 | 9 | use alloc::vec::Vec; 10 | use core::iter; 11 | use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint}; 12 | use curve25519_dalek::scalar::Scalar; 13 | 14 | use crate::generators::{BulletproofGens, PedersenGens}; 15 | 16 | /// A commitment to the bits of a party's value. 17 | #[derive(Serialize, Deserialize, Copy, Clone, Debug)] 18 | pub struct BitCommitment { 19 | pub(super) V_j: CompressedRistretto, 20 | pub(super) A_j: RistrettoPoint, 21 | pub(super) S_j: RistrettoPoint, 22 | } 23 | 24 | /// Challenge values derived from all parties' [`BitCommitment`]s. 25 | #[derive(Serialize, Deserialize, Copy, Clone, Debug)] 26 | pub struct BitChallenge { 27 | pub(super) y: Scalar, 28 | pub(super) z: Scalar, 29 | } 30 | 31 | /// A commitment to a party's polynomial coefficents. 32 | #[derive(Serialize, Deserialize, Copy, Clone, Debug)] 33 | pub struct PolyCommitment { 34 | pub(super) T_1_j: RistrettoPoint, 35 | pub(super) T_2_j: RistrettoPoint, 36 | } 37 | 38 | /// Challenge values derived from all parties' [`PolyCommitment`]s. 39 | #[derive(Serialize, Deserialize, Copy, Clone, Debug)] 40 | pub struct PolyChallenge { 41 | pub(super) x: Scalar, 42 | } 43 | 44 | /// A party's proof share, ready for aggregation into the final 45 | /// [`RangeProof`](::RangeProof). 46 | #[derive(Serialize, Deserialize, Clone, Debug)] 47 | pub struct ProofShare { 48 | pub(super) t_x: Scalar, 49 | pub(super) t_x_blinding: Scalar, 50 | pub(super) e_blinding: Scalar, 51 | pub(super) l_vec: Vec, 52 | pub(super) r_vec: Vec, 53 | } 54 | 55 | impl ProofShare { 56 | /// Checks consistency of all sizes in the proof share and returns the size of the l/r vector. 57 | pub(super) fn check_size( 58 | &self, 59 | expected_n: usize, 60 | bp_gens: &BulletproofGens, 61 | j: usize, 62 | ) -> Result<(), ()> { 63 | if self.l_vec.len() != expected_n { 64 | return Err(()); 65 | } 66 | 67 | if self.r_vec.len() != expected_n { 68 | return Err(()); 69 | } 70 | 71 | if expected_n > bp_gens.gens_capacity { 72 | return Err(()); 73 | } 74 | 75 | if j >= bp_gens.party_capacity { 76 | return Err(()); 77 | } 78 | 79 | Ok(()) 80 | } 81 | 82 | /// Audit an individual proof share to determine whether it is 83 | /// malformed. 84 | pub(super) fn audit_share( 85 | &self, 86 | bp_gens: &BulletproofGens, 87 | pc_gens: &PedersenGens, 88 | j: usize, 89 | bit_commitment: &BitCommitment, 90 | bit_challenge: &BitChallenge, 91 | poly_commitment: &PolyCommitment, 92 | poly_challenge: &PolyChallenge, 93 | ) -> Result<(), ()> { 94 | use curve25519_dalek::traits::{IsIdentity, VartimeMultiscalarMul}; 95 | 96 | use crate::inner_product_proof::inner_product; 97 | use crate::util; 98 | 99 | let n = self.l_vec.len(); 100 | 101 | self.check_size(n, bp_gens, j)?; 102 | 103 | let (y, z) = (&bit_challenge.y, &bit_challenge.z); 104 | let x = &poly_challenge.x; 105 | 106 | // Precompute some variables 107 | let zz = z * z; 108 | let minus_z = -z; 109 | let z_j = util::scalar_exp_vartime(z, j as u64); // z^j 110 | let y_jn = util::scalar_exp_vartime(y, (j * n) as u64); // y^(j*n) 111 | let y_jn_inv = y_jn.invert(); // y^(-j*n) 112 | let y_inv = y.invert(); // y^(-1) 113 | 114 | if self.t_x != inner_product(&self.l_vec, &self.r_vec) { 115 | return Err(()); 116 | } 117 | 118 | let g = self.l_vec.iter().map(|l_i| minus_z - l_i); 119 | let h = self 120 | .r_vec 121 | .iter() 122 | .zip(util::exp_iter(Scalar::from(2u64))) 123 | .zip(util::exp_iter(y_inv)) 124 | .map(|((r_i, exp_2), exp_y_inv)| { 125 | z + exp_y_inv * y_jn_inv * (-r_i) + exp_y_inv * y_jn_inv * (zz * z_j * exp_2) 126 | }); 127 | 128 | let P_check = RistrettoPoint::vartime_multiscalar_mul( 129 | iter::once(Scalar::one()) 130 | .chain(iter::once(*x)) 131 | .chain(iter::once(-self.e_blinding)) 132 | .chain(g) 133 | .chain(h), 134 | iter::once(&bit_commitment.A_j) 135 | .chain(iter::once(&bit_commitment.S_j)) 136 | .chain(iter::once(&pc_gens.B_blinding)) 137 | .chain(bp_gens.share(j).G(n)) 138 | .chain(bp_gens.share(j).H(n)), 139 | ); 140 | if !P_check.is_identity() { 141 | return Err(()); 142 | } 143 | 144 | let V_j = bit_commitment.V_j.decompress().ok_or(())?; 145 | 146 | let sum_of_powers_y = util::sum_of_powers(&y, n); 147 | let sum_of_powers_2 = util::sum_of_powers(&Scalar::from(2u64), n); 148 | let delta = (z - zz) * sum_of_powers_y * y_jn - z * zz * sum_of_powers_2 * z_j; 149 | let t_check = RistrettoPoint::vartime_multiscalar_mul( 150 | iter::once(zz * z_j) 151 | .chain(iter::once(*x)) 152 | .chain(iter::once(x * x)) 153 | .chain(iter::once(delta - self.t_x)) 154 | .chain(iter::once(-self.t_x_blinding)), 155 | iter::once(&V_j) 156 | .chain(iter::once(&poly_commitment.T_1_j)) 157 | .chain(iter::once(&poly_commitment.T_2_j)) 158 | .chain(iter::once(&pc_gens.B)) 159 | .chain(iter::once(&pc_gens.B_blinding)), 160 | ); 161 | 162 | if t_check.is_identity() { 163 | Ok(()) 164 | } else { 165 | Err(()) 166 | } 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/range_proof/party.rs: -------------------------------------------------------------------------------- 1 | //! The `party` module contains the API for the party state while the party is 2 | //! engaging in an aggregated multiparty computation protocol. 3 | //! 4 | //! Each state of the MPC protocol is represented by a different Rust 5 | //! type. The state transitions consume the previous state, making it 6 | //! a compile error to perform the steps out of order or to repeat a 7 | //! step. 8 | //! 9 | //! For more explanation of how the `dealer`, `party`, and `messages` 10 | //! modules orchestrate the protocol execution, see the documentation 11 | //! in the [`aggregation`](::range_proof_mpc) module. 12 | 13 | extern crate alloc; 14 | 15 | use alloc::vec::Vec; 16 | use clear_on_drop::clear::Clear; 17 | use core::iter; 18 | use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint}; 19 | use curve25519_dalek::scalar::Scalar; 20 | use curve25519_dalek::traits::MultiscalarMul; 21 | use rand_core::{CryptoRng, RngCore}; 22 | 23 | use crate::errors::MPCError; 24 | use crate::generators::{BulletproofGens, PedersenGens}; 25 | use crate::util; 26 | 27 | #[cfg(feature = "std")] 28 | use rand::thread_rng; 29 | 30 | use super::messages::*; 31 | 32 | /// Used to construct a party for the aggregated rangeproof MPC protocol. 33 | pub struct Party {} 34 | 35 | impl Party { 36 | /// Constructs a `PartyAwaitingPosition` with the given rangeproof parameters. 37 | pub fn new<'a>( 38 | bp_gens: &'a BulletproofGens, 39 | pc_gens: &'a PedersenGens, 40 | v: u64, 41 | v_blinding: Scalar, 42 | n: usize, 43 | ) -> Result, MPCError> { 44 | if !(n == 8 || n == 16 || n == 32 || n == 64) { 45 | return Err(MPCError::InvalidBitsize); 46 | } 47 | if bp_gens.gens_capacity < n { 48 | return Err(MPCError::InvalidGeneratorsLength); 49 | } 50 | 51 | let V = pc_gens.commit(v.into(), v_blinding).compress(); 52 | 53 | Ok(PartyAwaitingPosition { 54 | bp_gens, 55 | pc_gens, 56 | n, 57 | v, 58 | v_blinding, 59 | V, 60 | }) 61 | } 62 | } 63 | 64 | /// A party waiting for the dealer to assign their position in the aggregation. 65 | pub struct PartyAwaitingPosition<'a> { 66 | bp_gens: &'a BulletproofGens, 67 | pc_gens: &'a PedersenGens, 68 | n: usize, 69 | v: u64, 70 | v_blinding: Scalar, 71 | V: CompressedRistretto, 72 | } 73 | 74 | impl<'a> PartyAwaitingPosition<'a> { 75 | /// Assigns a position in the aggregated proof to this party, 76 | /// allowing the party to commit to the bits of their value. 77 | #[cfg(feature = "std")] 78 | pub fn assign_position( 79 | self, 80 | j: usize, 81 | ) -> Result<(PartyAwaitingBitChallenge<'a>, BitCommitment), MPCError> { 82 | self.assign_position_with_rng(j, &mut thread_rng()) 83 | } 84 | 85 | /// Assigns a position in the aggregated proof to this party, 86 | /// allowing the party to commit to the bits of their value. 87 | pub fn assign_position_with_rng( 88 | self, 89 | j: usize, 90 | rng: &mut T, 91 | ) -> Result<(PartyAwaitingBitChallenge<'a>, BitCommitment), MPCError> { 92 | if self.bp_gens.party_capacity <= j { 93 | return Err(MPCError::InvalidGeneratorsLength); 94 | } 95 | 96 | let bp_share = self.bp_gens.share(j); 97 | 98 | let a_blinding = Scalar::random(rng); 99 | // Compute A = + + a_blinding * B_blinding 100 | let mut A = self.pc_gens.B_blinding * a_blinding; 101 | 102 | use subtle::{Choice, ConditionallySelectable}; 103 | let mut i = 0; 104 | for (G_i, H_i) in bp_share.G(self.n).zip(bp_share.H(self.n)) { 105 | // If v_i = 0, we add a_L[i] * G[i] + a_R[i] * H[i] = - H[i] 106 | // If v_i = 1, we add a_L[i] * G[i] + a_R[i] * H[i] = G[i] 107 | let v_i = Choice::from(((self.v >> i) & 1) as u8); 108 | let mut point = -H_i; 109 | point.conditional_assign(G_i, v_i); 110 | A += point; 111 | i += 1; 112 | } 113 | 114 | let s_blinding = Scalar::random(rng); 115 | let s_L: Vec = (0..self.n).map(|_| Scalar::random(rng)).collect(); 116 | let s_R: Vec = (0..self.n).map(|_| Scalar::random(rng)).collect(); 117 | 118 | // Compute S = + + s_blinding * B_blinding 119 | let S = RistrettoPoint::multiscalar_mul( 120 | iter::once(&s_blinding).chain(s_L.iter()).chain(s_R.iter()), 121 | iter::once(&self.pc_gens.B_blinding) 122 | .chain(bp_share.G(self.n)) 123 | .chain(bp_share.H(self.n)), 124 | ); 125 | 126 | // Return next state and all commitments 127 | let bit_commitment = BitCommitment { 128 | V_j: self.V, 129 | A_j: A, 130 | S_j: S, 131 | }; 132 | let next_state = PartyAwaitingBitChallenge { 133 | n: self.n, 134 | v: self.v, 135 | v_blinding: self.v_blinding, 136 | pc_gens: self.pc_gens, 137 | j, 138 | a_blinding, 139 | s_blinding, 140 | s_L, 141 | s_R, 142 | }; 143 | Ok((next_state, bit_commitment)) 144 | } 145 | } 146 | 147 | /// Overwrite secrets with null bytes when they go out of scope. 148 | impl<'a> Drop for PartyAwaitingPosition<'a> { 149 | fn drop(&mut self) { 150 | self.v.clear(); 151 | self.v_blinding.clear(); 152 | } 153 | } 154 | 155 | /// A party which has committed to the bits of its value 156 | /// and is waiting for the aggregated value challenge from the dealer. 157 | pub struct PartyAwaitingBitChallenge<'a> { 158 | n: usize, // bitsize of the range 159 | v: u64, 160 | v_blinding: Scalar, 161 | j: usize, 162 | pc_gens: &'a PedersenGens, 163 | a_blinding: Scalar, 164 | s_blinding: Scalar, 165 | s_L: Vec, 166 | s_R: Vec, 167 | } 168 | 169 | impl<'a> PartyAwaitingBitChallenge<'a> { 170 | /// Receive a [`BitChallenge`] from the dealer and use it to 171 | /// compute commitments to the party's polynomial coefficients. 172 | #[cfg(feature = "std")] 173 | pub fn apply_challenge( 174 | self, 175 | vc: &BitChallenge, 176 | ) -> (PartyAwaitingPolyChallenge, PolyCommitment) { 177 | self.apply_challenge_with_rng(vc, &mut thread_rng()) 178 | } 179 | 180 | /// Receive a [`BitChallenge`] from the dealer and use it to 181 | /// compute commitments to the party's polynomial coefficients. 182 | pub fn apply_challenge_with_rng( 183 | self, 184 | vc: &BitChallenge, 185 | rng: &mut T, 186 | ) -> (PartyAwaitingPolyChallenge, PolyCommitment) { 187 | let n = self.n; 188 | let offset_y = util::scalar_exp_vartime(&vc.y, (self.j * n) as u64); 189 | let offset_z = util::scalar_exp_vartime(&vc.z, self.j as u64); 190 | 191 | // Calculate t by calculating vectors l0, l1, r0, r1 and multiplying 192 | let mut l_poly = util::VecPoly1::zero(n); 193 | let mut r_poly = util::VecPoly1::zero(n); 194 | 195 | let offset_zz = vc.z * vc.z * offset_z; 196 | let mut exp_y = offset_y; // start at y^j 197 | let mut exp_2 = Scalar::one(); // start at 2^0 = 1 198 | for i in 0..n { 199 | let a_L_i = Scalar::from((self.v >> i) & 1); 200 | let a_R_i = a_L_i - Scalar::one(); 201 | 202 | l_poly.0[i] = a_L_i - vc.z; 203 | l_poly.1[i] = self.s_L[i]; 204 | r_poly.0[i] = exp_y * (a_R_i + vc.z) + offset_zz * exp_2; 205 | r_poly.1[i] = exp_y * self.s_R[i]; 206 | 207 | exp_y *= vc.y; // y^i -> y^(i+1) 208 | exp_2 = exp_2 + exp_2; // 2^i -> 2^(i+1) 209 | } 210 | 211 | let t_poly = l_poly.inner_product(&r_poly); 212 | 213 | // Generate x by committing to T_1, T_2 (line 49-54) 214 | let t_1_blinding = Scalar::random(rng); 215 | let t_2_blinding = Scalar::random(rng); 216 | let T_1 = self.pc_gens.commit(t_poly.1, t_1_blinding); 217 | let T_2 = self.pc_gens.commit(t_poly.2, t_2_blinding); 218 | 219 | let poly_commitment = PolyCommitment { 220 | T_1_j: T_1, 221 | T_2_j: T_2, 222 | }; 223 | 224 | let papc = PartyAwaitingPolyChallenge { 225 | v_blinding: self.v_blinding, 226 | a_blinding: self.a_blinding, 227 | s_blinding: self.s_blinding, 228 | offset_zz, 229 | l_poly, 230 | r_poly, 231 | t_poly, 232 | t_1_blinding, 233 | t_2_blinding, 234 | }; 235 | 236 | (papc, poly_commitment) 237 | } 238 | } 239 | 240 | /// Overwrite secrets with null bytes when they go out of scope. 241 | impl<'a> Drop for PartyAwaitingBitChallenge<'a> { 242 | fn drop(&mut self) { 243 | self.v.clear(); 244 | self.v_blinding.clear(); 245 | self.a_blinding.clear(); 246 | self.s_blinding.clear(); 247 | 248 | // Important: due to how ClearOnDrop auto-implements InitializableFromZeroed 249 | // for T: Default, calling .clear() on Vec compiles, but does not 250 | // clear the content. Instead, it only clears the Vec's header. 251 | // Clearing the underlying buffer item-by-item will do the job, but will 252 | // keep the header as-is, which is fine since the header does not contain secrets. 253 | for e in self.s_L.iter_mut() { 254 | e.clear(); 255 | } 256 | for e in self.s_R.iter_mut() { 257 | e.clear(); 258 | } 259 | } 260 | } 261 | 262 | /// A party which has committed to their polynomial coefficents 263 | /// and is waiting for the polynomial challenge from the dealer. 264 | pub struct PartyAwaitingPolyChallenge { 265 | offset_zz: Scalar, 266 | l_poly: util::VecPoly1, 267 | r_poly: util::VecPoly1, 268 | t_poly: util::Poly2, 269 | v_blinding: Scalar, 270 | a_blinding: Scalar, 271 | s_blinding: Scalar, 272 | t_1_blinding: Scalar, 273 | t_2_blinding: Scalar, 274 | } 275 | 276 | impl PartyAwaitingPolyChallenge { 277 | /// Receive a [`PolyChallenge`] from the dealer and compute the 278 | /// party's proof share. 279 | pub fn apply_challenge(self, pc: &PolyChallenge) -> Result { 280 | // Prevent a malicious dealer from annihilating the blinding 281 | // factors by supplying a zero challenge. 282 | if pc.x == Scalar::zero() { 283 | return Err(MPCError::MaliciousDealer); 284 | } 285 | 286 | let t_blinding_poly = util::Poly2( 287 | self.offset_zz * self.v_blinding, 288 | self.t_1_blinding, 289 | self.t_2_blinding, 290 | ); 291 | 292 | let t_x = self.t_poly.eval(pc.x); 293 | let t_x_blinding = t_blinding_poly.eval(pc.x); 294 | let e_blinding = self.a_blinding + self.s_blinding * &pc.x; 295 | let l_vec = self.l_poly.eval(pc.x); 296 | let r_vec = self.r_poly.eval(pc.x); 297 | 298 | Ok(ProofShare { 299 | t_x_blinding, 300 | t_x, 301 | e_blinding, 302 | l_vec, 303 | r_vec, 304 | }) 305 | } 306 | } 307 | 308 | /// Overwrite secrets with null bytes when they go out of scope. 309 | impl Drop for PartyAwaitingPolyChallenge { 310 | fn drop(&mut self) { 311 | self.v_blinding.clear(); 312 | self.a_blinding.clear(); 313 | self.s_blinding.clear(); 314 | self.t_1_blinding.clear(); 315 | self.t_2_blinding.clear(); 316 | 317 | // Note: polynomials r_poly, l_poly and t_poly 318 | // are cleared within their own Drop impls. 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /src/transcript.rs: -------------------------------------------------------------------------------- 1 | //! Defines a `TranscriptProtocol` trait for using a Merlin transcript. 2 | 3 | use curve25519_dalek::ristretto::CompressedRistretto; 4 | use curve25519_dalek::scalar::Scalar; 5 | use merlin::Transcript; 6 | 7 | use crate::errors::ProofError; 8 | 9 | pub trait TranscriptProtocol { 10 | /// Append a domain separator for an `n`-bit, `m`-party range proof. 11 | fn rangeproof_domain_sep(&mut self, n: u64, m: u64); 12 | 13 | /// Append a domain separator for a length-`n` inner product proof. 14 | fn innerproduct_domain_sep(&mut self, n: u64); 15 | 16 | /// Append a domain separator for a constraint system. 17 | fn r1cs_domain_sep(&mut self); 18 | 19 | /// Commit a domain separator for a CS without randomized constraints. 20 | fn r1cs_1phase_domain_sep(&mut self); 21 | 22 | /// Commit a domain separator for a CS with randomized constraints. 23 | fn r1cs_2phase_domain_sep(&mut self); 24 | 25 | /// Append a `scalar` with the given `label`. 26 | fn append_scalar(&mut self, label: &'static [u8], scalar: &Scalar); 27 | 28 | /// Append a `point` with the given `label`. 29 | fn append_point(&mut self, label: &'static [u8], point: &CompressedRistretto); 30 | 31 | /// Check that a point is not the identity, then append it to the 32 | /// transcript. Otherwise, return an error. 33 | fn validate_and_append_point( 34 | &mut self, 35 | label: &'static [u8], 36 | point: &CompressedRistretto, 37 | ) -> Result<(), ProofError>; 38 | 39 | /// Compute a `label`ed challenge variable. 40 | fn challenge_scalar(&mut self, label: &'static [u8]) -> Scalar; 41 | } 42 | 43 | impl TranscriptProtocol for Transcript { 44 | fn rangeproof_domain_sep(&mut self, n: u64, m: u64) { 45 | self.append_message(b"dom-sep", b"rangeproof v1"); 46 | self.append_u64(b"n", n); 47 | self.append_u64(b"m", m); 48 | } 49 | 50 | fn innerproduct_domain_sep(&mut self, n: u64) { 51 | self.append_message(b"dom-sep", b"ipp v1"); 52 | self.append_u64(b"n", n); 53 | } 54 | 55 | fn r1cs_domain_sep(&mut self) { 56 | self.append_message(b"dom-sep", b"r1cs v1"); 57 | } 58 | 59 | fn r1cs_1phase_domain_sep(&mut self) { 60 | self.append_message(b"dom-sep", b"r1cs-1phase"); 61 | } 62 | 63 | fn r1cs_2phase_domain_sep(&mut self) { 64 | self.append_message(b"dom-sep", b"r1cs-2phase"); 65 | } 66 | 67 | fn append_scalar(&mut self, label: &'static [u8], scalar: &Scalar) { 68 | self.append_message(label, scalar.as_bytes()); 69 | } 70 | 71 | fn append_point(&mut self, label: &'static [u8], point: &CompressedRistretto) { 72 | self.append_message(label, point.as_bytes()); 73 | } 74 | 75 | fn validate_and_append_point( 76 | &mut self, 77 | label: &'static [u8], 78 | point: &CompressedRistretto, 79 | ) -> Result<(), ProofError> { 80 | use curve25519_dalek::traits::IsIdentity; 81 | 82 | if point.is_identity() { 83 | Err(ProofError::VerificationError) 84 | } else { 85 | Ok(self.append_message(label, point.as_bytes())) 86 | } 87 | } 88 | 89 | fn challenge_scalar(&mut self, label: &'static [u8]) -> Scalar { 90 | let mut buf = [0u8; 64]; 91 | self.challenge_bytes(label, &mut buf); 92 | 93 | Scalar::from_bytes_mod_order_wide(&buf) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | #![deny(missing_docs)] 2 | #![allow(non_snake_case)] 3 | 4 | extern crate alloc; 5 | 6 | use alloc::vec; 7 | use alloc::vec::Vec; 8 | use clear_on_drop::clear::Clear; 9 | use curve25519_dalek::scalar::Scalar; 10 | 11 | use crate::inner_product_proof::inner_product; 12 | 13 | /// Represents a degree-1 vector polynomial \\(\mathbf{a} + \mathbf{b} \cdot x\\). 14 | pub struct VecPoly1(pub Vec, pub Vec); 15 | 16 | /// Represents a degree-3 vector polynomial 17 | /// \\(\mathbf{a} + \mathbf{b} \cdot x + \mathbf{c} \cdot x^2 + \mathbf{d} \cdot x^3 \\). 18 | #[cfg(feature = "yoloproofs")] 19 | pub struct VecPoly3( 20 | pub Vec, 21 | pub Vec, 22 | pub Vec, 23 | pub Vec, 24 | ); 25 | 26 | /// Represents a degree-2 scalar polynomial \\(a + b \cdot x + c \cdot x^2\\) 27 | pub struct Poly2(pub Scalar, pub Scalar, pub Scalar); 28 | 29 | /// Represents a degree-6 scalar polynomial, without the zeroth degree 30 | /// \\(a \cdot x + b \cdot x^2 + c \cdot x^3 + d \cdot x^4 + e \cdot x^5 + f \cdot x^6\\) 31 | #[cfg(feature = "yoloproofs")] 32 | pub struct Poly6 { 33 | pub t1: Scalar, 34 | pub t2: Scalar, 35 | pub t3: Scalar, 36 | pub t4: Scalar, 37 | pub t5: Scalar, 38 | pub t6: Scalar, 39 | } 40 | 41 | /// Provides an iterator over the powers of a `Scalar`. 42 | /// 43 | /// This struct is created by the `exp_iter` function. 44 | pub struct ScalarExp { 45 | x: Scalar, 46 | next_exp_x: Scalar, 47 | } 48 | 49 | impl Iterator for ScalarExp { 50 | type Item = Scalar; 51 | 52 | fn next(&mut self) -> Option { 53 | let exp_x = self.next_exp_x; 54 | self.next_exp_x *= self.x; 55 | Some(exp_x) 56 | } 57 | 58 | fn size_hint(&self) -> (usize, Option) { 59 | (usize::max_value(), None) 60 | } 61 | } 62 | 63 | /// Return an iterator of the powers of `x`. 64 | pub fn exp_iter(x: Scalar) -> ScalarExp { 65 | let next_exp_x = Scalar::one(); 66 | ScalarExp { x, next_exp_x } 67 | } 68 | 69 | pub fn add_vec(a: &[Scalar], b: &[Scalar]) -> Vec { 70 | if a.len() != b.len() { 71 | // throw some error 72 | //println!("lengths of vectors don't match for vector addition"); 73 | } 74 | let mut out = vec![Scalar::zero(); b.len()]; 75 | for i in 0..a.len() { 76 | out[i] = a[i] + b[i]; 77 | } 78 | out 79 | } 80 | 81 | impl VecPoly1 { 82 | pub fn zero(n: usize) -> Self { 83 | VecPoly1(vec![Scalar::zero(); n], vec![Scalar::zero(); n]) 84 | } 85 | 86 | pub fn inner_product(&self, rhs: &VecPoly1) -> Poly2 { 87 | // Uses Karatsuba's method 88 | let l = self; 89 | let r = rhs; 90 | 91 | let t0 = inner_product(&l.0, &r.0); 92 | let t2 = inner_product(&l.1, &r.1); 93 | 94 | let l0_plus_l1 = add_vec(&l.0, &l.1); 95 | let r0_plus_r1 = add_vec(&r.0, &r.1); 96 | 97 | let t1 = inner_product(&l0_plus_l1, &r0_plus_r1) - t0 - t2; 98 | 99 | Poly2(t0, t1, t2) 100 | } 101 | 102 | pub fn eval(&self, x: Scalar) -> Vec { 103 | let n = self.0.len(); 104 | let mut out = vec![Scalar::zero(); n]; 105 | for i in 0..n { 106 | out[i] = self.0[i] + self.1[i] * x; 107 | } 108 | out 109 | } 110 | } 111 | 112 | #[cfg(feature = "yoloproofs")] 113 | impl VecPoly3 { 114 | pub fn zero(n: usize) -> Self { 115 | VecPoly3( 116 | vec![Scalar::zero(); n], 117 | vec![Scalar::zero(); n], 118 | vec![Scalar::zero(); n], 119 | vec![Scalar::zero(); n], 120 | ) 121 | } 122 | 123 | /// Compute an inner product of `lhs`, `rhs` which have the property that: 124 | /// - `lhs.0` is zero; 125 | /// - `rhs.2` is zero; 126 | /// This is the case in the constraint system proof. 127 | pub fn special_inner_product(lhs: &Self, rhs: &Self) -> Poly6 { 128 | // TODO: make checks that l_poly.0 and r_poly.2 are zero. 129 | 130 | let t1 = inner_product(&lhs.1, &rhs.0); 131 | let t2 = inner_product(&lhs.1, &rhs.1) + inner_product(&lhs.2, &rhs.0); 132 | let t3 = inner_product(&lhs.2, &rhs.1) + inner_product(&lhs.3, &rhs.0); 133 | let t4 = inner_product(&lhs.1, &rhs.3) + inner_product(&lhs.3, &rhs.1); 134 | let t5 = inner_product(&lhs.2, &rhs.3); 135 | let t6 = inner_product(&lhs.3, &rhs.3); 136 | 137 | Poly6 { 138 | t1, 139 | t2, 140 | t3, 141 | t4, 142 | t5, 143 | t6, 144 | } 145 | } 146 | 147 | pub fn eval(&self, x: Scalar) -> Vec { 148 | let n = self.0.len(); 149 | let mut out = vec![Scalar::zero(); n]; 150 | for i in 0..n { 151 | out[i] = self.0[i] + x * (self.1[i] + x * (self.2[i] + x * self.3[i])); 152 | } 153 | out 154 | } 155 | } 156 | 157 | impl Poly2 { 158 | pub fn eval(&self, x: Scalar) -> Scalar { 159 | self.0 + x * (self.1 + x * self.2) 160 | } 161 | } 162 | 163 | #[cfg(feature = "yoloproofs")] 164 | impl Poly6 { 165 | pub fn eval(&self, x: Scalar) -> Scalar { 166 | x * (self.t1 + x * (self.t2 + x * (self.t3 + x * (self.t4 + x * (self.t5 + x * self.t6))))) 167 | } 168 | } 169 | 170 | impl Drop for VecPoly1 { 171 | fn drop(&mut self) { 172 | for e in self.0.iter_mut() { 173 | e.clear(); 174 | } 175 | for e in self.1.iter_mut() { 176 | e.clear(); 177 | } 178 | } 179 | } 180 | 181 | impl Drop for Poly2 { 182 | fn drop(&mut self) { 183 | self.0.clear(); 184 | self.1.clear(); 185 | self.2.clear(); 186 | } 187 | } 188 | 189 | #[cfg(feature = "yoloproofs")] 190 | impl Drop for VecPoly3 { 191 | fn drop(&mut self) { 192 | for e in self.0.iter_mut() { 193 | e.clear(); 194 | } 195 | for e in self.1.iter_mut() { 196 | e.clear(); 197 | } 198 | for e in self.2.iter_mut() { 199 | e.clear(); 200 | } 201 | for e in self.3.iter_mut() { 202 | e.clear(); 203 | } 204 | } 205 | } 206 | 207 | #[cfg(feature = "yoloproofs")] 208 | impl Drop for Poly6 { 209 | fn drop(&mut self) { 210 | self.t1.clear(); 211 | self.t2.clear(); 212 | self.t3.clear(); 213 | self.t4.clear(); 214 | self.t5.clear(); 215 | self.t6.clear(); 216 | } 217 | } 218 | 219 | /// Raises `x` to the power `n` using binary exponentiation, 220 | /// with (1 to 2)*lg(n) scalar multiplications. 221 | /// TODO: a consttime version of this would be awfully similar to a Montgomery ladder. 222 | pub fn scalar_exp_vartime(x: &Scalar, mut n: u64) -> Scalar { 223 | let mut result = Scalar::one(); 224 | let mut aux = *x; // x, x^2, x^4, x^8, ... 225 | while n > 0 { 226 | let bit = n & 1; 227 | if bit == 1 { 228 | result = result * aux; 229 | } 230 | n = n >> 1; 231 | aux = aux * aux; // FIXME: one unnecessary mult at the last step here! 232 | } 233 | result 234 | } 235 | 236 | /// Takes the sum of all the powers of `x`, up to `n` 237 | /// If `n` is a power of 2, it uses the efficient algorithm with `2*lg n` multiplications and additions. 238 | /// If `n` is not a power of 2, it uses the slow algorithm with `n` multiplications and additions. 239 | /// In the Bulletproofs case, all calls to `sum_of_powers` should have `n` as a power of 2. 240 | pub fn sum_of_powers(x: &Scalar, n: usize) -> Scalar { 241 | if !n.is_power_of_two() { 242 | return sum_of_powers_slow(x, n); 243 | } 244 | if n == 0 || n == 1 { 245 | return Scalar::from(n as u64); 246 | } 247 | let mut m = n; 248 | let mut result = Scalar::one() + x; 249 | let mut factor = *x; 250 | while m > 2 { 251 | factor = factor * factor; 252 | result = result + factor * result; 253 | m = m / 2; 254 | } 255 | result 256 | } 257 | 258 | // takes the sum of all of the powers of x, up to n 259 | fn sum_of_powers_slow(x: &Scalar, n: usize) -> Scalar { 260 | exp_iter(*x).take(n).sum() 261 | } 262 | 263 | /// Given `data` with `len >= 32`, return the first 32 bytes. 264 | pub fn read32(data: &[u8]) -> [u8; 32] { 265 | let mut buf32 = [0u8; 32]; 266 | buf32[..].copy_from_slice(&data[..32]); 267 | buf32 268 | } 269 | 270 | #[cfg(test)] 271 | mod tests { 272 | use super::*; 273 | 274 | #[test] 275 | fn exp_2_is_powers_of_2() { 276 | let exp_2: Vec<_> = exp_iter(Scalar::from(2u64)).take(4).collect(); 277 | 278 | assert_eq!(exp_2[0], Scalar::from(1u64)); 279 | assert_eq!(exp_2[1], Scalar::from(2u64)); 280 | assert_eq!(exp_2[2], Scalar::from(4u64)); 281 | assert_eq!(exp_2[3], Scalar::from(8u64)); 282 | } 283 | 284 | #[test] 285 | fn test_inner_product() { 286 | let a = vec![ 287 | Scalar::from(1u64), 288 | Scalar::from(2u64), 289 | Scalar::from(3u64), 290 | Scalar::from(4u64), 291 | ]; 292 | let b = vec![ 293 | Scalar::from(2u64), 294 | Scalar::from(3u64), 295 | Scalar::from(4u64), 296 | Scalar::from(5u64), 297 | ]; 298 | assert_eq!(Scalar::from(40u64), inner_product(&a, &b)); 299 | } 300 | 301 | /// Raises `x` to the power `n`. 302 | fn scalar_exp_vartime_slow(x: &Scalar, n: u64) -> Scalar { 303 | let mut result = Scalar::one(); 304 | for _ in 0..n { 305 | result = result * x; 306 | } 307 | result 308 | } 309 | 310 | #[test] 311 | fn test_scalar_exp() { 312 | let x = Scalar::from_bits( 313 | *b"\x84\xfc\xbcOx\x12\xa0\x06\xd7\x91\xd9z:'\xdd\x1e!CE\xf7\xb1\xb9Vz\x810sD\x96\x85\xb5\x07", 314 | ); 315 | assert_eq!(scalar_exp_vartime(&x, 0), Scalar::one()); 316 | assert_eq!(scalar_exp_vartime(&x, 1), x); 317 | assert_eq!(scalar_exp_vartime(&x, 2), x * x); 318 | assert_eq!(scalar_exp_vartime(&x, 3), x * x * x); 319 | assert_eq!(scalar_exp_vartime(&x, 4), x * x * x * x); 320 | assert_eq!(scalar_exp_vartime(&x, 5), x * x * x * x * x); 321 | assert_eq!(scalar_exp_vartime(&x, 64), scalar_exp_vartime_slow(&x, 64)); 322 | assert_eq!( 323 | scalar_exp_vartime(&x, 0b11001010), 324 | scalar_exp_vartime_slow(&x, 0b11001010) 325 | ); 326 | } 327 | 328 | #[test] 329 | fn test_sum_of_powers() { 330 | let x = Scalar::from(10u64); 331 | assert_eq!(sum_of_powers_slow(&x, 0), sum_of_powers(&x, 0)); 332 | assert_eq!(sum_of_powers_slow(&x, 1), sum_of_powers(&x, 1)); 333 | assert_eq!(sum_of_powers_slow(&x, 2), sum_of_powers(&x, 2)); 334 | assert_eq!(sum_of_powers_slow(&x, 4), sum_of_powers(&x, 4)); 335 | assert_eq!(sum_of_powers_slow(&x, 8), sum_of_powers(&x, 8)); 336 | assert_eq!(sum_of_powers_slow(&x, 16), sum_of_powers(&x, 16)); 337 | assert_eq!(sum_of_powers_slow(&x, 32), sum_of_powers(&x, 32)); 338 | assert_eq!(sum_of_powers_slow(&x, 64), sum_of_powers(&x, 64)); 339 | } 340 | 341 | #[test] 342 | fn test_sum_of_powers_slow() { 343 | let x = Scalar::from(10u64); 344 | assert_eq!(sum_of_powers_slow(&x, 0), Scalar::zero()); 345 | assert_eq!(sum_of_powers_slow(&x, 1), Scalar::one()); 346 | assert_eq!(sum_of_powers_slow(&x, 2), Scalar::from(11u64)); 347 | assert_eq!(sum_of_powers_slow(&x, 3), Scalar::from(111u64)); 348 | assert_eq!(sum_of_powers_slow(&x, 4), Scalar::from(1111u64)); 349 | assert_eq!(sum_of_powers_slow(&x, 5), Scalar::from(11111u64)); 350 | assert_eq!(sum_of_powers_slow(&x, 6), Scalar::from(111111u64)); 351 | } 352 | 353 | #[test] 354 | fn vec_of_scalars_clear_on_drop() { 355 | let mut v = vec![Scalar::from(24u64), Scalar::from(42u64)]; 356 | 357 | for e in v.iter_mut() { 358 | e.clear(); 359 | } 360 | 361 | fn flat_slice(x: &[T]) -> &[u8] { 362 | use core::mem; 363 | use core::slice; 364 | 365 | unsafe { slice::from_raw_parts(x.as_ptr() as *const u8, mem::size_of_val(x)) } 366 | } 367 | 368 | assert_eq!(flat_slice(&v.as_slice()), &[0u8; 64][..]); 369 | assert_eq!(v[0], Scalar::zero()); 370 | assert_eq!(v[1], Scalar::zero()); 371 | } 372 | 373 | #[test] 374 | fn tuple_of_scalars_clear_on_drop() { 375 | let mut v = Poly2( 376 | Scalar::from(24u64), 377 | Scalar::from(42u64), 378 | Scalar::from(255u64), 379 | ); 380 | 381 | v.0.clear(); 382 | v.1.clear(); 383 | v.2.clear(); 384 | 385 | fn as_bytes(x: &T) -> &[u8] { 386 | use core::mem; 387 | use core::slice; 388 | 389 | unsafe { slice::from_raw_parts(x as *const T as *const u8, mem::size_of_val(x)) } 390 | } 391 | 392 | assert_eq!(as_bytes(&v), &[0u8; 96][..]); 393 | assert_eq!(v.0, Scalar::zero()); 394 | assert_eq!(v.1, Scalar::zero()); 395 | assert_eq!(v.2, Scalar::zero()); 396 | } 397 | } 398 | -------------------------------------------------------------------------------- /tests/r1cs.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | extern crate bulletproofs; 4 | extern crate curve25519_dalek; 5 | extern crate merlin; 6 | extern crate rand; 7 | 8 | use bulletproofs::r1cs::*; 9 | use bulletproofs::{BulletproofGens, PedersenGens}; 10 | use curve25519_dalek::ristretto::CompressedRistretto; 11 | use curve25519_dalek::scalar::Scalar; 12 | use merlin::Transcript; 13 | use rand::seq::SliceRandom; 14 | use rand::thread_rng; 15 | 16 | // Shuffle gadget (documented in markdown file) 17 | 18 | /// A proof-of-shuffle. 19 | struct ShuffleProof(R1CSProof); 20 | 21 | impl ShuffleProof { 22 | fn gadget( 23 | cs: &mut CS, 24 | x: Vec, 25 | y: Vec, 26 | ) -> Result<(), R1CSError> { 27 | assert_eq!(x.len(), y.len()); 28 | let k = x.len(); 29 | 30 | if k == 1 { 31 | cs.constrain(y[0] - x[0]); 32 | return Ok(()); 33 | } 34 | 35 | cs.specify_randomized_constraints(move |cs| { 36 | let z = cs.challenge_scalar(b"shuffle challenge"); 37 | 38 | // Make last x multiplier for i = k-1 and k-2 39 | let (_, _, last_mulx_out) = cs.multiply(x[k - 1] - z, x[k - 2] - z); 40 | 41 | // Make multipliers for x from i == [0, k-3] 42 | let first_mulx_out = (0..k - 2).rev().fold(last_mulx_out, |prev_out, i| { 43 | let (_, _, o) = cs.multiply(prev_out.into(), x[i] - z); 44 | o 45 | }); 46 | 47 | // Make last y multiplier for i = k-1 and k-2 48 | let (_, _, last_muly_out) = cs.multiply(y[k - 1] - z, y[k - 2] - z); 49 | 50 | // Make multipliers for y from i == [0, k-3] 51 | let first_muly_out = (0..k - 2).rev().fold(last_muly_out, |prev_out, i| { 52 | let (_, _, o) = cs.multiply(prev_out.into(), y[i] - z); 53 | o 54 | }); 55 | 56 | // Constrain last x mul output and last y mul output to be equal 57 | cs.constrain(first_mulx_out - first_muly_out); 58 | 59 | Ok(()) 60 | }) 61 | } 62 | } 63 | 64 | impl ShuffleProof { 65 | /// Attempt to construct a proof that `output` is a permutation of `input`. 66 | /// 67 | /// Returns a tuple `(proof, input_commitments || output_commitments)`. 68 | pub fn prove<'a, 'b>( 69 | pc_gens: &'b PedersenGens, 70 | bp_gens: &'b BulletproofGens, 71 | transcript: &'a mut Transcript, 72 | input: &[Scalar], 73 | output: &[Scalar], 74 | ) -> Result< 75 | ( 76 | ShuffleProof, 77 | Vec, 78 | Vec, 79 | ), 80 | R1CSError, 81 | > { 82 | // Apply a domain separator with the shuffle parameters to the transcript 83 | // XXX should this be part of the gadget? 84 | let k = input.len(); 85 | transcript.append_message(b"dom-sep", b"ShuffleProof"); 86 | transcript.append_u64(b"k", k as u64); 87 | 88 | let mut prover = Prover::new(&pc_gens, transcript); 89 | 90 | // Construct blinding factors using an RNG. 91 | // Note: a non-example implementation would want to operate on existing commitments. 92 | let mut blinding_rng = rand::thread_rng(); 93 | 94 | let (input_commitments, input_vars): (Vec<_>, Vec<_>) = input 95 | .into_iter() 96 | .map(|v| prover.commit(*v, Scalar::random(&mut blinding_rng))) 97 | .unzip(); 98 | 99 | let (output_commitments, output_vars): (Vec<_>, Vec<_>) = output 100 | .into_iter() 101 | .map(|v| prover.commit(*v, Scalar::random(&mut blinding_rng))) 102 | .unzip(); 103 | 104 | ShuffleProof::gadget(&mut prover, input_vars, output_vars)?; 105 | 106 | let proof = prover.prove(&bp_gens)?; 107 | 108 | Ok((ShuffleProof(proof), input_commitments, output_commitments)) 109 | } 110 | } 111 | 112 | impl ShuffleProof { 113 | /// Attempt to verify a `ShuffleProof`. 114 | pub fn verify<'a, 'b>( 115 | &self, 116 | pc_gens: &'b PedersenGens, 117 | bp_gens: &'b BulletproofGens, 118 | transcript: &'a mut Transcript, 119 | input_commitments: &Vec, 120 | output_commitments: &Vec, 121 | ) -> Result<(), R1CSError> { 122 | // Apply a domain separator with the shuffle parameters to the transcript 123 | // XXX should this be part of the gadget? 124 | let k = input_commitments.len(); 125 | transcript.append_message(b"dom-sep", b"ShuffleProof"); 126 | transcript.append_u64(b"k", k as u64); 127 | 128 | let mut verifier = Verifier::new(transcript); 129 | 130 | let input_vars: Vec<_> = input_commitments 131 | .iter() 132 | .map(|V| verifier.commit(*V)) 133 | .collect(); 134 | 135 | let output_vars: Vec<_> = output_commitments 136 | .iter() 137 | .map(|V| verifier.commit(*V)) 138 | .collect(); 139 | 140 | ShuffleProof::gadget(&mut verifier, input_vars, output_vars)?; 141 | 142 | verifier.verify(&self.0, &pc_gens, &bp_gens) 143 | } 144 | } 145 | 146 | fn kshuffle_helper(k: usize) { 147 | use rand::Rng; 148 | 149 | // Common code 150 | let pc_gens = PedersenGens::default(); 151 | let bp_gens = BulletproofGens::new((2 * k).next_power_of_two(), 1); 152 | 153 | let (proof, input_commitments, output_commitments) = { 154 | // Randomly generate inputs and outputs to kshuffle 155 | let mut rng = rand::thread_rng(); 156 | let (min, max) = (0u64, std::u64::MAX); 157 | let input: Vec = (0..k) 158 | .map(|_| Scalar::from(rng.gen_range(min, max))) 159 | .collect(); 160 | let mut output = input.clone(); 161 | output.shuffle(&mut rand::thread_rng()); 162 | 163 | let mut prover_transcript = Transcript::new(b"ShuffleProofTest"); 164 | ShuffleProof::prove(&pc_gens, &bp_gens, &mut prover_transcript, &input, &output).unwrap() 165 | }; 166 | 167 | { 168 | let mut verifier_transcript = Transcript::new(b"ShuffleProofTest"); 169 | assert!(proof 170 | .verify( 171 | &pc_gens, 172 | &bp_gens, 173 | &mut verifier_transcript, 174 | &input_commitments, 175 | &output_commitments 176 | ) 177 | .is_ok()); 178 | } 179 | } 180 | 181 | #[test] 182 | fn shuffle_gadget_test_1() { 183 | kshuffle_helper(1); 184 | } 185 | 186 | #[test] 187 | fn shuffle_gadget_test_2() { 188 | kshuffle_helper(2); 189 | } 190 | 191 | #[test] 192 | fn shuffle_gadget_test_3() { 193 | kshuffle_helper(3); 194 | } 195 | 196 | #[test] 197 | fn shuffle_gadget_test_4() { 198 | kshuffle_helper(4); 199 | } 200 | 201 | #[test] 202 | fn shuffle_gadget_test_5() { 203 | kshuffle_helper(5); 204 | } 205 | 206 | #[test] 207 | fn shuffle_gadget_test_6() { 208 | kshuffle_helper(6); 209 | } 210 | 211 | #[test] 212 | fn shuffle_gadget_test_7() { 213 | kshuffle_helper(7); 214 | } 215 | 216 | #[test] 217 | fn shuffle_gadget_test_24() { 218 | kshuffle_helper(24); 219 | } 220 | 221 | #[test] 222 | fn shuffle_gadget_test_42() { 223 | kshuffle_helper(42); 224 | } 225 | 226 | /// Constrains (a1 + a2) * (b1 + b2) = (c1 + c2) 227 | fn example_gadget( 228 | cs: &mut CS, 229 | a1: LinearCombination, 230 | a2: LinearCombination, 231 | b1: LinearCombination, 232 | b2: LinearCombination, 233 | c1: LinearCombination, 234 | c2: LinearCombination, 235 | ) { 236 | let (_, _, c_var) = cs.multiply(a1 + a2, b1 + b2); 237 | cs.constrain(c1 + c2 - c_var); 238 | } 239 | 240 | // Prover's scope 241 | fn example_gadget_proof( 242 | pc_gens: &PedersenGens, 243 | bp_gens: &BulletproofGens, 244 | a1: u64, 245 | a2: u64, 246 | b1: u64, 247 | b2: u64, 248 | c1: u64, 249 | c2: u64, 250 | ) -> Result<(R1CSProof, Vec), R1CSError> { 251 | let mut transcript = Transcript::new(b"R1CSExampleGadget"); 252 | 253 | // 1. Create a prover 254 | let mut prover = Prover::new(pc_gens, &mut transcript); 255 | 256 | // 2. Commit high-level variables 257 | let (commitments, vars): (Vec<_>, Vec<_>) = [a1, a2, b1, b2, c1] 258 | .into_iter() 259 | .map(|x| prover.commit(Scalar::from(*x), Scalar::random(&mut thread_rng()))) 260 | .unzip(); 261 | 262 | // 3. Build a CS 263 | example_gadget( 264 | &mut prover, 265 | vars[0].into(), 266 | vars[1].into(), 267 | vars[2].into(), 268 | vars[3].into(), 269 | vars[4].into(), 270 | Scalar::from(c2).into(), 271 | ); 272 | 273 | // 4. Make a proof 274 | let proof = prover.prove(bp_gens)?; 275 | 276 | Ok((proof, commitments)) 277 | } 278 | 279 | // Verifier logic 280 | fn example_gadget_verify( 281 | pc_gens: &PedersenGens, 282 | bp_gens: &BulletproofGens, 283 | c2: u64, 284 | proof: R1CSProof, 285 | commitments: Vec, 286 | ) -> Result<(), R1CSError> { 287 | let mut transcript = Transcript::new(b"R1CSExampleGadget"); 288 | 289 | // 1. Create a verifier 290 | let mut verifier = Verifier::new(&mut transcript); 291 | 292 | // 2. Commit high-level variables 293 | let vars: Vec<_> = commitments.iter().map(|V| verifier.commit(*V)).collect(); 294 | 295 | // 3. Build a CS 296 | example_gadget( 297 | &mut verifier, 298 | vars[0].into(), 299 | vars[1].into(), 300 | vars[2].into(), 301 | vars[3].into(), 302 | vars[4].into(), 303 | Scalar::from(c2).into(), 304 | ); 305 | 306 | // 4. Verify the proof 307 | verifier 308 | .verify(&proof, &pc_gens, &bp_gens) 309 | .map_err(|_| R1CSError::VerificationError) 310 | } 311 | 312 | fn example_gadget_roundtrip_helper( 313 | a1: u64, 314 | a2: u64, 315 | b1: u64, 316 | b2: u64, 317 | c1: u64, 318 | c2: u64, 319 | ) -> Result<(), R1CSError> { 320 | // Common 321 | let pc_gens = PedersenGens::default(); 322 | let bp_gens = BulletproofGens::new(128, 1); 323 | 324 | let (proof, commitments) = example_gadget_proof(&pc_gens, &bp_gens, a1, a2, b1, b2, c1, c2)?; 325 | 326 | example_gadget_verify(&pc_gens, &bp_gens, c2, proof, commitments) 327 | } 328 | 329 | fn example_gadget_roundtrip_serialization_helper( 330 | a1: u64, 331 | a2: u64, 332 | b1: u64, 333 | b2: u64, 334 | c1: u64, 335 | c2: u64, 336 | ) -> Result<(), R1CSError> { 337 | // Common 338 | let pc_gens = PedersenGens::default(); 339 | let bp_gens = BulletproofGens::new(128, 1); 340 | 341 | let (proof, commitments) = example_gadget_proof(&pc_gens, &bp_gens, a1, a2, b1, b2, c1, c2)?; 342 | 343 | let proof = proof.to_bytes(); 344 | 345 | let proof = R1CSProof::from_bytes(&proof)?; 346 | 347 | example_gadget_verify(&pc_gens, &bp_gens, c2, proof, commitments) 348 | } 349 | 350 | #[test] 351 | fn example_gadget_test() { 352 | // (3 + 4) * (6 + 1) = (40 + 9) 353 | assert!(example_gadget_roundtrip_helper(3, 4, 6, 1, 40, 9).is_ok()); 354 | // (3 + 4) * (6 + 1) != (40 + 10) 355 | assert!(example_gadget_roundtrip_helper(3, 4, 6, 1, 40, 10).is_err()); 356 | } 357 | 358 | #[test] 359 | fn example_gadget_serialization_test() { 360 | // (3 + 4) * (6 + 1) = (40 + 9) 361 | assert!(example_gadget_roundtrip_serialization_helper(3, 4, 6, 1, 40, 9).is_ok()); 362 | // (3 + 4) * (6 + 1) != (40 + 10) 363 | assert!(example_gadget_roundtrip_serialization_helper(3, 4, 6, 1, 40, 10).is_err()); 364 | } 365 | 366 | // Range Proof gadget 367 | 368 | /// Enforces that the quantity of v is in the range [0, 2^n). 369 | pub fn range_proof( 370 | cs: &mut CS, 371 | mut v: LinearCombination, 372 | v_assignment: Option, 373 | n: usize, 374 | ) -> Result<(), R1CSError> { 375 | let mut exp_2 = Scalar::one(); 376 | for i in 0..n { 377 | // Create low-level variables and add them to constraints 378 | let (a, b, o) = cs.allocate_multiplier(v_assignment.map(|q| { 379 | let bit: u64 = (q >> i) & 1; 380 | ((1 - bit).into(), bit.into()) 381 | }))?; 382 | 383 | // Enforce a * b = 0, so one of (a,b) is zero 384 | cs.constrain(o.into()); 385 | 386 | // Enforce that a = 1 - b, so they both are 1 or 0. 387 | cs.constrain(a + (b - 1u64)); 388 | 389 | // Add `-b_i*2^i` to the linear combination 390 | // in order to form the following constraint by the end of the loop: 391 | // v = Sum(b_i * 2^i, i = 0..n-1) 392 | v = v - b * exp_2; 393 | 394 | exp_2 = exp_2 + exp_2; 395 | } 396 | 397 | // Enforce that v = Sum(b_i * 2^i, i = 0..n-1) 398 | cs.constrain(v); 399 | 400 | Ok(()) 401 | } 402 | 403 | #[test] 404 | fn range_proof_gadget() { 405 | use rand::thread_rng; 406 | use rand::Rng; 407 | 408 | let mut rng = thread_rng(); 409 | let m = 3; // number of values to test per `n` 410 | 411 | for n in [2, 10, 32, 63].iter() { 412 | let (min, max) = (0u64, ((1u128 << n) - 1) as u64); 413 | let values: Vec = (0..m).map(|_| rng.gen_range(min, max)).collect(); 414 | for v in values { 415 | assert!(range_proof_helper(v.into(), *n).is_ok()); 416 | } 417 | assert!(range_proof_helper((max + 1).into(), *n).is_err()); 418 | } 419 | } 420 | 421 | fn range_proof_helper(v_val: u64, n: usize) -> Result<(), R1CSError> { 422 | // Common 423 | let pc_gens = PedersenGens::default(); 424 | let bp_gens = BulletproofGens::new(128, 1); 425 | 426 | // Prover's scope 427 | let (proof, commitment) = { 428 | // Prover makes a `ConstraintSystem` instance representing a range proof gadget 429 | let mut prover_transcript = Transcript::new(b"RangeProofTest"); 430 | let mut rng = rand::thread_rng(); 431 | 432 | let mut prover = Prover::new(&pc_gens, &mut prover_transcript); 433 | 434 | let (com, var) = prover.commit(v_val.into(), Scalar::random(&mut rng)); 435 | assert!(range_proof(&mut prover, var.into(), Some(v_val), n).is_ok()); 436 | 437 | let proof = prover.prove(&bp_gens)?; 438 | 439 | (proof, com) 440 | }; 441 | 442 | // Verifier makes a `ConstraintSystem` instance representing a merge gadget 443 | let mut verifier_transcript = Transcript::new(b"RangeProofTest"); 444 | let mut verifier = Verifier::new(&mut verifier_transcript); 445 | 446 | let var = verifier.commit(commitment); 447 | 448 | // Verifier adds constraints to the constraint system 449 | assert!(range_proof(&mut verifier, var.into(), None, n).is_ok()); 450 | 451 | // Verifier verifies proof 452 | Ok(verifier.verify(&proof, &pc_gens, &bp_gens)?) 453 | } 454 | --------------------------------------------------------------------------------