├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── dist-primitives ├── Cargo.toml ├── examples │ ├── dfft_test.rs │ ├── dmsm_bench.rs │ ├── dmsm_test.rs │ ├── dpp_test.rs │ ├── local_dfft_test.rs │ └── msm_bench.rs └── src │ ├── channel │ ├── channel.rs │ └── mod.rs │ ├── dfft │ ├── dfft.rs │ └── mod.rs │ ├── dmsm │ ├── dmsm.rs │ └── mod.rs │ ├── dpp │ ├── dpp.rs │ └── mod.rs │ ├── lib.rs │ └── utils │ ├── deg_red.rs │ ├── mod.rs │ └── pack.rs ├── groth16 ├── Cargo.toml ├── README.md ├── examples │ ├── ext_wit_bench.rs │ ├── groth_bench.rs │ └── local_groth_bench.rs └── src │ ├── ext_wit.rs │ └── lib.rs ├── mpc-net ├── Cargo.toml ├── README.md ├── data │ └── 4 ├── src │ ├── lib.rs │ ├── multi.rs │ └── two.rs └── test.zsh ├── network-address ├── 4 └── 8 ├── plonk ├── Cargo.toml ├── README.md ├── examples │ ├── dpoly_commit_test.rs │ ├── local_plonk_test.rs │ └── plonk_bench.rs └── src │ ├── dplonk.rs │ ├── dpoly_commit.rs │ ├── lib.rs │ ├── localplonk.rs │ └── poly_commit.rs ├── scripts ├── dfft_test.zsh ├── dmsm_bench.zsh ├── dmsm_test.zsh ├── dpoly_commit_test.zsh ├── dpp_test.zsh ├── ext_wit_test.zsh ├── groth_bench.zsh └── plonk_bench.zsh └── secret-sharing ├── Cargo.toml └── src ├── lib.rs └── pss.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "secret-sharing", 4 | "dist-primitives", 5 | "mpc-net", 6 | "groth16", 7 | "plonk" 8 | ] 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright 2023 Guru-Vamsi Policharla 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 13 | all 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 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## ⚠️ Update: A production ready version of zkSaaS is being built at [webb-tools/zk-SaaS](https://github.com/webb-tools/zk-SaaS) 2 | 3 | # zkSaaS: Zero-Knowledge SNARKs as a Service [ePrint:2023/905](https://eprint.iacr.org/2023/905) 4 | 5 | Rust implementation of the zkSaaS protocol introduced in [ePrint:2023/905](https://eprint.iacr.org/2023/905) 6 | 7 | **WARNING:** This is an academic proof-of-concept prototype, and in particular has not received careful code review. This implementation is NOT ready for production use. 8 | 9 | ## Dependencies 10 | This project relies on the [arkworks](http://arkworks.rs) project for finite field and elliptic curve arithmetic. For communication we use the mpc-net crate from [collaborative-zksnark](https://github.com/alex-ozdemir/collaborative-zksnark). 11 | 12 | ## Overview 13 | * [`secret-sharing/`](secret-sharing): A packed secret sharing library built on top of the finite field generics in arkworks. 14 | * [`dist-primitives/`](dist-primitives): Contains implementations of the distributed fast-fourier transform, multiscalar multiplication and partial products, complete with correctness tests. 15 | * [`groth16/`](groth16): Contains a distributed and local version of groth16 used for benchmarking timings. (not a complete implementation) 16 | * [`plonk/`](plonk): Contains a distributed and local version of plonk used for benchmarking timings. (not a complete implementation) 17 | * [`scripts/`](scripts): Contains shell scripts to run various tests and benchmarks. 18 | 19 | ## License 20 | This library is released under the MIT License. 21 | -------------------------------------------------------------------------------- /dist-primitives/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dist-primitives" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | ark-std = {version = "0.4.0", default-features = false, features = [ "print-trace", "std" ]} 10 | ark-ff = {version = "0.4.0", default-features = false} 11 | ark-poly = {version = "0.4.0", default-features = false} 12 | ark-ec = {version = "0.4.0", default-features = false} 13 | ark-serialize = { version = "0.4.0", default-features = false, features = [ "derive" ] } 14 | 15 | ark-bls12-377 = {version = "0.4.0", default-features = false, features = ["curve"] } 16 | 17 | secret-sharing = { version = "0.1.0", path = "../secret-sharing" } 18 | mpc-net ={ version = "0.1.0", path = "../mpc-net" } 19 | 20 | log = {version = "0.4"} 21 | rand = { version = "0.8", default-features = false, features = ["std"] } 22 | digest = { version = "0.10" } 23 | sha2 = "0.10" 24 | structopt = "0.3" 25 | env_logger = "0.8" 26 | -------------------------------------------------------------------------------- /dist-primitives/examples/dfft_test.rs: -------------------------------------------------------------------------------- 1 | use ark_bls12_377::Fr; 2 | use ark_ff::{FftField, PrimeField}; 3 | use ark_poly::{EvaluationDomain, Radix2EvaluationDomain}; 4 | use dist_primitives::{ 5 | channel::channel::MpcSerNet, 6 | dfft::dfft::{d_fft, fft_in_place_rearrange, d_ifft}, 7 | utils::pack::transpose, 8 | Opt, 9 | }; 10 | use mpc_net::{MpcMultiNet as Net, MpcNet}; 11 | use secret_sharing::pss::PackedSharingParams; 12 | use structopt::StructOpt; 13 | 14 | pub fn d_fft_test( 15 | pp: &PackedSharingParams, 16 | dom: &Radix2EvaluationDomain, 17 | ) { 18 | let mut rng = ark_std::test_rng(); 19 | let mbyl: usize = dom.size() / pp.l; 20 | // We apply FFT on this vector 21 | 22 | let mut x_coeffs: Vec = Vec::new(); 23 | for i in 0..dom.size() { 24 | x_coeffs.push(F::from(i as u64)); 25 | } 26 | let x_evals = dom.fft(&x_coeffs); 27 | 28 | let mut x_coeff_shares = x_coeffs.clone(); 29 | fft_in_place_rearrange(&mut x_coeff_shares); 30 | 31 | let mut x_eval_shares = x_evals.clone(); 32 | fft_in_place_rearrange(&mut x_eval_shares); 33 | 34 | // packed coeffs 35 | let mut pcoeff: Vec> = Vec::new(); 36 | for i in 0..mbyl { 37 | pcoeff.push(x_coeff_shares.iter().skip(i).step_by(mbyl).cloned().collect::>()); 38 | pp.pack_from_public_in_place(&mut pcoeff[i]); 39 | } 40 | 41 | let pcoeff_share = pcoeff 42 | .iter() 43 | .map(|shares| shares[Net::party_id()]) 44 | .collect::>(); 45 | 46 | // packed evals 47 | let mut peval: Vec> = Vec::new(); 48 | for i in 0..mbyl { 49 | peval.push(x_eval_shares.iter().skip(i).step_by(mbyl).cloned().collect::>()); 50 | pp.pack_from_public_in_place(&mut peval[i]); 51 | } 52 | 53 | let peval_share = peval 54 | .iter() 55 | .map(|shares| shares[Net::party_id()]) 56 | .collect::>(); 57 | 58 | let fft_share = d_fft(pcoeff_share, false, 1, false, dom, pp); 59 | let ifft_share = d_ifft(peval_share, false, 1, false, dom, pp); 60 | 61 | // Send to king who reconstructs and checks the answer 62 | Net::send_to_king(&fft_share).map(|fft_share| { 63 | let fft_share = transpose(fft_share); 64 | 65 | let pevals: Vec = fft_share 66 | .into_iter() 67 | .flat_map(|shares| pp.unpack(&shares)) 68 | .collect(); 69 | // pevals.reverse(); // todo: implement such that we avoid this reverse 70 | 71 | if Net::am_king() { 72 | assert_eq!(x_evals, pevals); 73 | } 74 | }); 75 | 76 | Net::send_to_king(&ifft_share).map(|ifft_share| { 77 | let ifft_share = transpose(ifft_share); 78 | 79 | let pcoeffs: Vec = ifft_share 80 | .into_iter() 81 | .flat_map(|shares| pp.unpack(&shares)) 82 | .collect(); 83 | // pcoeffs.reverse(); // todo: implement such that we avoid this reverse 84 | 85 | if Net::am_king() { 86 | assert_eq!(x_coeffs, pcoeffs); 87 | } 88 | }); 89 | } 90 | 91 | pub fn main() { 92 | env_logger::builder().format_timestamp(None).init(); 93 | 94 | let opt = Opt::from_args(); 95 | 96 | Net::init_from_file(opt.input.to_str().unwrap(), opt.id); 97 | let pp = PackedSharingParams::::new(opt.l); 98 | let dom = Radix2EvaluationDomain::::new(opt.m).unwrap(); 99 | debug_assert_eq!( 100 | dom.size(), 101 | opt.m, 102 | "Failed to obtain domain of size {}", 103 | opt.m 104 | ); 105 | d_fft_test::(&pp, &dom); 106 | 107 | Net::deinit(); 108 | } 109 | -------------------------------------------------------------------------------- /dist-primitives/examples/dmsm_bench.rs: -------------------------------------------------------------------------------- 1 | use ark_bls12_377::Fr; 2 | use ark_ec::CurveGroup; 3 | use ark_poly::{EvaluationDomain, Radix2EvaluationDomain}; 4 | use ark_std::{end_timer, start_timer, UniformRand, Zero}; 5 | use dist_primitives::{dmsm::dmsm::d_msm, Opt}; 6 | use mpc_net::{MpcMultiNet as Net, MpcNet}; 7 | use secret_sharing::pss::PackedSharingParams; 8 | use structopt::StructOpt; 9 | 10 | pub fn d_msm_test( 11 | pp: &PackedSharingParams, 12 | dom: &Radix2EvaluationDomain, 13 | ) { 14 | // let m = pp.l*4; 15 | // let case_timer = start_timer!(||"affinemsm_test"); 16 | let mbyl: usize = dom.size() / pp.l; 17 | println!("m: {}, mbyl: {}", dom.size(), mbyl); 18 | 19 | let rng = &mut ark_std::test_rng(); 20 | 21 | let mut y_share: Vec = vec![G::ScalarField::zero(); dom.size()]; 22 | let mut x_share: Vec = vec![G::zero(); dom.size()]; 23 | 24 | for i in 0..dom.size() { 25 | y_share[i] = G::ScalarField::rand(rng); 26 | x_share[i] = G::rand(rng); 27 | } 28 | 29 | let x_share_aff: Vec = x_share.iter().map(|s| s.clone().into()).collect(); 30 | 31 | let dmsm = start_timer!(|| "Distributed msm"); 32 | d_msm::(&x_share_aff, &y_share, pp); 33 | end_timer!(dmsm); 34 | } 35 | 36 | fn main() { 37 | env_logger::builder().format_timestamp(None).init(); 38 | 39 | let opt = Opt::from_args(); 40 | 41 | Net::init_from_file(opt.input.to_str().unwrap(), opt.id); 42 | 43 | let pp = PackedSharingParams::::new(opt.l); 44 | for i in 10..20 { 45 | let dom = Radix2EvaluationDomain::::new(1 << i).unwrap(); 46 | println!("domain size: {}", dom.size()); 47 | d_msm_test::(&pp, &dom); 48 | } 49 | 50 | Net::deinit(); 51 | } 52 | -------------------------------------------------------------------------------- /dist-primitives/examples/dmsm_test.rs: -------------------------------------------------------------------------------- 1 | use ark_bls12_377::Fr; 2 | use ark_ec::CurveGroup; 3 | use ark_poly::{EvaluationDomain, Radix2EvaluationDomain}; 4 | use ark_std::{end_timer, start_timer, UniformRand}; 5 | use dist_primitives::dmsm::dmsm::packexp_from_public; 6 | use dist_primitives::{dmsm::dmsm::d_msm, Opt}; 7 | use mpc_net::{MpcMultiNet as Net, MpcNet}; 8 | use secret_sharing::pss::PackedSharingParams; 9 | use structopt::StructOpt; 10 | 11 | pub fn d_msm_test( 12 | pp: &PackedSharingParams, 13 | dom: &Radix2EvaluationDomain, 14 | ) { 15 | // let m = pp.l*4; 16 | // let case_timer = start_timer!(||"affinemsm_test"); 17 | let mbyl: usize = dom.size() / pp.l; 18 | println!("m: {}, mbyl: {}", dom.size(), mbyl); 19 | 20 | let rng = &mut ark_std::test_rng(); 21 | 22 | let mut y_pub: Vec = Vec::new(); 23 | let mut x_pub: Vec = Vec::new(); 24 | 25 | for _ in 0..dom.size() { 26 | y_pub.push(G::ScalarField::rand(rng)); 27 | x_pub.push(G::rand(rng)); 28 | } 29 | 30 | let x_share: Vec = x_pub 31 | .chunks(pp.l) 32 | .map(|s| packexp_from_public(&s.to_vec(), &pp)[Net::party_id()]) 33 | .collect(); 34 | 35 | let y_share: Vec = y_pub 36 | .chunks(pp.l) 37 | .map(|s| pp.pack_from_public(&s.to_vec())[Net::party_id()]) 38 | .collect(); 39 | 40 | let x_pub_aff: Vec = x_pub.iter().map(|s| s.clone().into()).collect(); 41 | let x_share_aff: Vec = x_share.iter().map(|s| s.clone().into()).collect(); 42 | 43 | // Will be comparing against this in the end 44 | let nmsm = start_timer!(|| "Ark msm"); 45 | let should_be_output = G::msm(&x_pub_aff.as_slice(), &y_pub.as_slice()).unwrap(); 46 | end_timer!(nmsm); 47 | 48 | let dmsm = start_timer!(|| "Distributed msm"); 49 | let output = d_msm::(&x_share_aff, &y_share, pp); 50 | end_timer!(dmsm); 51 | 52 | if Net::am_king() { 53 | assert_eq!(should_be_output, output); 54 | } 55 | } 56 | 57 | fn main() { 58 | env_logger::builder().format_timestamp(None).init(); 59 | 60 | let opt = Opt::from_args(); 61 | 62 | Net::init_from_file(opt.input.to_str().unwrap(), opt.id); 63 | 64 | let pp = PackedSharingParams::::new(opt.l); 65 | let dom = Radix2EvaluationDomain::::new(opt.m).unwrap(); 66 | d_msm_test::(&pp, &dom); 67 | 68 | Net::deinit(); 69 | } 70 | -------------------------------------------------------------------------------- /dist-primitives/examples/dpp_test.rs: -------------------------------------------------------------------------------- 1 | use ark_bls12_377::Fr; 2 | use ark_ff::{FftField, PrimeField}; 3 | use ark_poly::{EvaluationDomain, Radix2EvaluationDomain}; 4 | use dist_primitives::{ 5 | channel::channel::MpcSerNet, 6 | dpp::dpp::d_pp, 7 | utils::pack::{pack_vec, transpose}, 8 | Opt, 9 | }; 10 | use mpc_net::{MpcMultiNet as Net, MpcNet}; 11 | use secret_sharing::pss::PackedSharingParams; 12 | use structopt::StructOpt; 13 | 14 | pub fn d_pp_test( 15 | pp: &PackedSharingParams, 16 | dom: &Radix2EvaluationDomain, 17 | ) { 18 | // We apply FFT on this vector 19 | // let mut x = vec![F::ONE; cd.m]; 20 | let mut x: Vec = Vec::new(); 21 | for i in 0..dom.size() { 22 | x.push(F::from((i + 1) as u64)); 23 | } 24 | 25 | // Output to test against 26 | let should_be_output = vec![F::one(); dom.size()]; 27 | 28 | // pack x 29 | let px = transpose(pack_vec(&x, pp)); 30 | 31 | let px_share = px[Net::party_id()].clone(); 32 | let pp_px_share = d_pp(px_share.clone(), px_share.clone(), pp); 33 | 34 | // Send to king who reconstructs and checks the answer 35 | Net::send_to_king(&pp_px_share).map(|pp_px_shares| { 36 | let pp_px_shares = transpose(pp_px_shares); 37 | 38 | let pp_px: Vec = pp_px_shares 39 | .into_iter() 40 | .flat_map(|x| pp.unpack(&x)) 41 | .collect(); 42 | 43 | if Net::am_king() { 44 | debug_assert_eq!(should_be_output, pp_px); 45 | } 46 | }); 47 | } 48 | 49 | pub fn main() { 50 | env_logger::builder().format_timestamp(None).init(); 51 | 52 | let opt = Opt::from_args(); 53 | 54 | Net::init_from_file(opt.input.to_str().unwrap(), opt.id); 55 | let pp = PackedSharingParams::::new(opt.l); 56 | let cd = Radix2EvaluationDomain::::new(opt.m).unwrap(); 57 | d_pp_test::(&pp, &cd); 58 | 59 | Net::deinit(); 60 | } 61 | -------------------------------------------------------------------------------- /dist-primitives/examples/local_dfft_test.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | 3 | use ark_bls12_377::Fr; 4 | use ark_ff::{FftField, PrimeField}; 5 | use ark_poly::{EvaluationDomain, Radix2EvaluationDomain}; 6 | use ark_std::{end_timer, log2, start_timer}; 7 | use dist_primitives::dfft::dfft::fft_in_place_rearrange; 8 | use secret_sharing::pss::PackedSharingParams; 9 | 10 | pub fn local_dfft_test( 11 | pp: &PackedSharingParams, 12 | dom: &Radix2EvaluationDomain, 13 | ) { 14 | let mbyl: usize = dom.size() / pp.l; 15 | 16 | // We apply FFT on this vector 17 | // let mut x = vec![F::one();cd.m]; 18 | let mut x: Vec = Vec::new(); 19 | for i in 0..dom.size() { 20 | x.push(F::from(i as u64)); 21 | } 22 | 23 | // Output to test against 24 | let output = dom.fft(&x); 25 | 26 | // Rearranging x 27 | let myfft_timer = start_timer!(|| "MY FFT"); 28 | 29 | fft_in_place_rearrange::(&mut x); 30 | 31 | let mut px: Vec> = Vec::new(); 32 | for i in 0..mbyl { 33 | px.push(x.iter().skip(i).step_by(mbyl).cloned().collect::>()); 34 | } 35 | 36 | let mut s1 = px.clone(); 37 | 38 | let now = start_timer!(|| "FFT1"); 39 | 40 | // fft1 41 | for i in (log2(pp.l) + 1..=log2(dom.size())).rev() { 42 | let poly_size = dom.size() / 2usize.pow(i); 43 | let factor_stride = dom.element(2usize.pow(i - 1)); 44 | let mut factor = factor_stride; 45 | for k in 0..poly_size { 46 | for j in 0..2usize.pow(i - 1) / pp.l { 47 | for ii in 0..pp.l { 48 | let x = s1[(2 * j) * (poly_size) + k][ii]; 49 | let y = s1[(2 * j + 1) * (poly_size) + k][ii] * factor; 50 | s1[j * (2 * poly_size) + k][ii] = x + y; 51 | s1[j * (2 * poly_size) + k + poly_size][ii] = x - y; 52 | } 53 | } 54 | factor = factor * factor_stride; 55 | } 56 | } 57 | 58 | end_timer!(now); 59 | 60 | // psstoss 61 | let mut sx: Vec = Vec::new(); 62 | for i in 0..mbyl { 63 | sx.append(&mut s1[i]); 64 | } 65 | 66 | // fft2 67 | let mut s1 = sx.clone(); 68 | let mut s2 = sx.clone(); 69 | 70 | let now = start_timer!(|| "FFT2"); 71 | 72 | for i in (1..=log2(pp.l)).rev() { 73 | let poly_size = dom.size() / 2usize.pow(i); 74 | let factor_stride = dom.element(2usize.pow(i - 1)); 75 | let mut factor = factor_stride; 76 | for k in 0..poly_size { 77 | for j in 0..2usize.pow(i - 1) { 78 | let x = s1[k * (2usize.pow(i)) + 2 * j]; 79 | let y = s1[k * (2usize.pow(i)) + 2 * j + 1] * factor; 80 | s2[k * (2usize.pow(i - 1)) + j] = x + y; 81 | s2[(k + poly_size) * (2usize.pow(i - 1)) + j] = x - y; 82 | } 83 | factor = factor * factor_stride; 84 | } 85 | mem::swap(&mut s1, &mut s2); 86 | } 87 | end_timer!(now); 88 | 89 | end_timer!(myfft_timer); 90 | 91 | s1.rotate_right(1); 92 | 93 | assert_eq!(output, s1); 94 | } 95 | 96 | pub fn main() { 97 | let pp = PackedSharingParams::::new(2); 98 | let dom = Radix2EvaluationDomain::::new(8).unwrap(); 99 | local_dfft_test::(&pp, &dom); 100 | } 101 | -------------------------------------------------------------------------------- /dist-primitives/examples/msm_bench.rs: -------------------------------------------------------------------------------- 1 | use ark_bls12_377::Fr; 2 | use ark_ec::CurveGroup; 3 | use ark_poly::{EvaluationDomain, Radix2EvaluationDomain}; 4 | use ark_std::{end_timer, start_timer, UniformRand, Zero}; 5 | 6 | pub fn msm_test(dom: &Radix2EvaluationDomain) { 7 | let rng = &mut ark_std::test_rng(); 8 | 9 | let mut y_pub: Vec = vec![G::ScalarField::zero(); dom.size()]; 10 | let mut x_pub: Vec = vec![G::zero(); dom.size()]; 11 | 12 | for i in 0..dom.size() { 13 | y_pub[i] = G::ScalarField::rand(rng); 14 | x_pub[i] = G::rand(rng); 15 | } 16 | 17 | let x_pub_aff: Vec = x_pub.iter().map(|s| s.clone().into()).collect(); 18 | 19 | let nmsm = start_timer!(|| "Ark msm"); 20 | G::msm(&x_pub_aff.as_slice(), &y_pub.as_slice()).unwrap(); 21 | end_timer!(nmsm); 22 | } 23 | 24 | fn main() { 25 | for i in 10..20 { 26 | let dom = Radix2EvaluationDomain::::new(1 << i).unwrap(); 27 | println!("domain size: {}", dom.size()); 28 | msm_test::(&dom); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /dist-primitives/src/channel/channel.rs: -------------------------------------------------------------------------------- 1 | use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; 2 | 3 | use mpc_net::MpcNet; 4 | 5 | pub trait MpcSerNet: MpcNet { 6 | #[inline] 7 | fn broadcast(out: &T) -> Vec { 8 | let mut bytes_out = Vec::new(); 9 | out.serialize_compressed(&mut bytes_out).unwrap(); 10 | let bytes_in = Self::broadcast_bytes(&bytes_out); 11 | bytes_in 12 | .into_iter() 13 | .map(|b| T::deserialize_compressed(&b[..]).unwrap()) 14 | .collect() 15 | } 16 | 17 | #[inline] 18 | fn send_to_king(out: &T) -> Option> { 19 | let mut bytes_out = Vec::new(); 20 | out.serialize_compressed(&mut bytes_out).unwrap(); 21 | Self::send_bytes_to_king(&bytes_out).map(|bytes_in| { 22 | bytes_in 23 | .into_iter() 24 | .map(|b| T::deserialize_compressed(&b[..]).unwrap()) 25 | .collect() 26 | }) 27 | } 28 | 29 | #[inline] 30 | fn recv_from_king(out: Option>) -> T { 31 | let bytes_in = Self::recv_bytes_from_king(out.map(|outs| { 32 | outs.iter() 33 | .map(|out| { 34 | let mut bytes_out = Vec::new(); 35 | out.serialize_compressed(&mut bytes_out).unwrap(); 36 | bytes_out 37 | }) 38 | .collect() 39 | })); 40 | T::deserialize_compressed(&bytes_in[..]).unwrap() 41 | } 42 | } 43 | 44 | impl MpcSerNet for N {} 45 | -------------------------------------------------------------------------------- /dist-primitives/src/channel/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod channel; 2 | -------------------------------------------------------------------------------- /dist-primitives/src/dfft/dfft.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | channel::channel::MpcSerNet, 3 | utils::pack::{pack_vec, transpose}, 4 | }; 5 | use ark_ff::{FftField, PrimeField}; 6 | use ark_poly::{EvaluationDomain, Radix2EvaluationDomain}; 7 | use ark_std::{end_timer, log2, start_timer}; 8 | use log::debug; 9 | use mpc_net::{MpcMultiNet as Net, MpcNet}; 10 | use secret_sharing::pss::PackedSharingParams; 11 | use std::mem; 12 | 13 | /// Takes as input packed shares of evaluations a polynomial over dom and outputs shares of the FFT of the polynomial 14 | /// rearrange: whether or not to rearrange output shares 15 | /// pad: whether or not to pad output shares with zeros 16 | /// degree2: whether or not to do degree reduction n the input shares 17 | pub fn d_fft( 18 | mut pcoeff_share: Vec, 19 | rearrange: bool, 20 | pad: usize, 21 | degree2: bool, 22 | dom: &Radix2EvaluationDomain, 23 | pp: &PackedSharingParams, 24 | ) -> Vec { 25 | debug_assert_eq!( 26 | pcoeff_share.len() * pp.l, 27 | dom.size(), 28 | "Mismatch of size in FFT, {}, {}.", 29 | pcoeff_share.len() * pp.l, 30 | dom.size() 31 | ); 32 | 33 | // Parties apply FFT1 locally 34 | fft1_in_place(&mut pcoeff_share, dom, pp, dom.group_gen); 35 | // King applies FFT2 and parties receive shares of evals 36 | fft2_with_rearrange_pad(pcoeff_share, rearrange, pad, degree2, dom, pp, dom.group_gen) 37 | } 38 | 39 | pub fn d_ifft( 40 | mut peval_share: Vec, 41 | rearrange: bool, 42 | pad: usize, 43 | degree2: bool, 44 | dom: &Radix2EvaluationDomain, 45 | pp: &PackedSharingParams, 46 | ) -> Vec { 47 | debug_assert_eq!( 48 | peval_share.len() * pp.l, 49 | dom.size(), 50 | "Mismatch of size in IFFT, {}, {}.", 51 | peval_share.len() * pp.l, 52 | dom.size() 53 | ); 54 | 55 | let sizeinv = F::from(dom.size as u64).inverse().unwrap(); 56 | peval_share.iter_mut().for_each(|x| *x = *x * sizeinv); 57 | 58 | // Parties apply FFT1 locally 59 | fft1_in_place(&mut peval_share, dom, pp, dom.group_gen_inv); 60 | // King applies FFT2 and parties receive shares of evals 61 | fft2_with_rearrange_pad(peval_share, rearrange, pad, degree2, dom, pp, dom.group_gen_inv) 62 | } 63 | 64 | //////////////////////////////////////////////////////////////////////////////////////////////////// 65 | fn fft1_in_place( 66 | px: &mut Vec, 67 | dom: &Radix2EvaluationDomain, 68 | pp: &PackedSharingParams, 69 | gen: F 70 | ) { 71 | // FFT1 computation done locally on a vector of shares 72 | debug_assert_eq!( 73 | dom.group_gen_inv.pow(&[(px.len() * pp.l) as u64]), 74 | F::one(), 75 | "Mismatch of size in FFT1, input:{}", 76 | px.len() 77 | ); 78 | 79 | let now = start_timer!(|| "FFT1"); 80 | if Net::am_king() { 81 | debug!("Applying fft1"); 82 | } 83 | 84 | // fft1 85 | for i in (log2(pp.l) + 1..=log2(dom.size())).rev() { 86 | let poly_size = dom.size() / 2usize.pow(i); 87 | let factor_stride = gen.pow(&[2usize.pow(i - 1) as u64]); 88 | let mut factor = factor_stride; 89 | for k in 0..poly_size { 90 | for j in 0..2usize.pow(i - 1) / pp.l { 91 | let x = px[(2 * j) * (poly_size) + k]; 92 | let y = px[(2 * j + 1) * (poly_size) + k] * factor; 93 | px[j * (2 * poly_size) + k] = x + y; 94 | px[j * (2 * poly_size) + k + poly_size] = x - y; 95 | } 96 | factor = factor * factor_stride; 97 | } 98 | } 99 | 100 | end_timer!(now); 101 | 102 | if Net::am_king() { 103 | debug!("Finished fft1"); 104 | } 105 | } 106 | 107 | fn fft2_in_place( 108 | s1: &mut Vec, 109 | dom: &Radix2EvaluationDomain, 110 | pp: &PackedSharingParams, 111 | gen: F 112 | ) { 113 | // King applies fft2, packs the vectors as desired and sends shares to parties 114 | 115 | let now = start_timer!(|| "FFT2"); 116 | let mut s2 = vec![F::zero(); s1.len()]; //Remove this time permitting 117 | 118 | if Net::am_king() { 119 | debug!("Applying fft2"); 120 | } 121 | 122 | // fft2 123 | for i in (1..=log2(pp.l)).rev() { 124 | let poly_size = dom.size() / 2usize.pow(i); 125 | let factor_stride = gen.pow(&[2usize.pow(i - 1) as u64]); 126 | let mut factor = factor_stride; 127 | for k in 0..poly_size { 128 | for j in 0..2usize.pow(i - 1) { 129 | let x = s1[k * (2usize.pow(i)) + 2 * j]; 130 | let y = s1[k * (2usize.pow(i)) + 2 * j + 1] * factor; 131 | s2[k * (2usize.pow(i - 1)) + j] = x + y; 132 | s2[(k + poly_size) * (2usize.pow(i - 1)) + j] = x - y; 133 | } 134 | factor = factor * factor_stride; 135 | } 136 | mem::swap(s1, &mut s2); 137 | } 138 | 139 | s1.rotate_right(1); 140 | 141 | end_timer!(now); 142 | 143 | if Net::am_king() { 144 | debug!("Finished fft2"); 145 | } 146 | } 147 | 148 | /// Send shares after fft1 to king who finishes the protocol and returns packed shares 149 | fn fft2_with_rearrange_pad( 150 | px: Vec, 151 | rearrange: bool, 152 | pad: usize, 153 | degree2: bool, 154 | dom: &Radix2EvaluationDomain, 155 | pp: &PackedSharingParams, 156 | gen: F, 157 | ) -> Vec { 158 | // King applies FFT2 with rearrange 159 | 160 | let mbyl = px.len(); 161 | println!("mbyl: {}", mbyl); 162 | 163 | let communication_timer = start_timer!(|| "ComToKing"); 164 | let received_shares = Net::send_to_king(&px); 165 | end_timer!(communication_timer); 166 | 167 | let king_answer = received_shares.map(|all_shares| { 168 | let all_shares = transpose(all_shares); 169 | let mut s1: Vec = vec![F::zero(); px.len() * pp.l]; 170 | 171 | let open_shares_timer = start_timer!(|| "Opening shares"); 172 | for i in 0..mbyl { 173 | let tmp: Vec; 174 | if degree2 == true { 175 | tmp = pp.unpack2(&all_shares[i]); 176 | } else { 177 | tmp = pp.unpack(&all_shares[i]); 178 | } 179 | 180 | for j in 0..pp.l { 181 | s1[i * pp.l + j] = tmp[j]; 182 | } 183 | } 184 | end_timer!(open_shares_timer); 185 | 186 | fft2_in_place(&mut s1, dom, pp, gen); // s1 constains final output now 187 | 188 | // Optionally double length by padding zeros here 189 | if pad > 1 { 190 | s1.resize(pad * s1.len(), F::zero()); 191 | } 192 | 193 | // Optionally rearrange to get ready for next FFT/IFFT 194 | if rearrange == true { 195 | fft_in_place_rearrange(&mut s1); 196 | let mut out_shares: Vec> = Vec::new(); 197 | let pack_shares_timer = start_timer!(|| "Packing shares"); 198 | for i in 0..s1.len() / pp.l { 199 | out_shares.push( 200 | // This will cause issues with memory benchmarking since it assumes everyone creates this instead of receiving it from dealer 201 | pp.pack_from_public( 202 | &s1.iter() 203 | .skip(i) 204 | .step_by(s1.len() / pp.l) 205 | .cloned() 206 | .collect::>(), 207 | ), 208 | ); 209 | } 210 | end_timer!(pack_shares_timer); 211 | transpose(out_shares) 212 | } else { 213 | transpose(pack_vec(&s1, pp)) 214 | } 215 | }); 216 | 217 | drop(px); 218 | 219 | let communication_timer = start_timer!(|| "ComFromKing"); 220 | let got_from_king = Net::recv_from_king(king_answer); 221 | end_timer!(communication_timer); 222 | 223 | got_from_king 224 | } 225 | 226 | pub fn permute_index(size: usize, index: usize) -> usize { 227 | const USIZE_BITS: u32 = 0_usize.count_zeros(); 228 | 229 | debug_assert!(index < size); 230 | debug_assert!(size.is_power_of_two()); 231 | 232 | let bits = size.trailing_zeros(); 233 | index.reverse_bits().wrapping_shr(USIZE_BITS - bits) 234 | } 235 | 236 | pub fn fft_in_place_rearrange(data: &mut Vec) { 237 | let n = data.len(); 238 | for i in 0..n { 239 | let j = permute_index(n, i); 240 | if j > i { 241 | data.swap(i, j); 242 | } 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /dist-primitives/src/dfft/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod dfft; 2 | -------------------------------------------------------------------------------- /dist-primitives/src/dmsm/dmsm.rs: -------------------------------------------------------------------------------- 1 | use crate::channel::channel::MpcSerNet; 2 | use ark_ec::{CurveGroup, Group}; 3 | use ark_poly::EvaluationDomain; 4 | use ark_std::{end_timer, start_timer}; 5 | use mpc_net::{MpcMultiNet as Net, MpcNet}; 6 | use secret_sharing::pss::PackedSharingParams; 7 | 8 | pub fn unpackexp( 9 | shares: &Vec, 10 | degree2: bool, 11 | pp: &PackedSharingParams, 12 | ) -> Vec { 13 | let mut result = shares.to_vec(); 14 | 15 | // interpolate shares 16 | pp.share.ifft_in_place(&mut result); 17 | 18 | // Simplified this assertion using a zero check in the last n - d - 1 entries 19 | 20 | #[cfg(debug_assertions)] 21 | { 22 | let n = Net::n_parties(); 23 | let d: usize; 24 | if degree2 { 25 | d = 2 * (pp.t + pp.l) 26 | } else { 27 | d = pp.t + pp.l 28 | } 29 | 30 | for i in d + 1..n { 31 | debug_assert!( 32 | result[i].is_zero(), 33 | "Polynomial has degree > degree bound {})", 34 | d 35 | ); 36 | } 37 | } 38 | 39 | // Evalaute the polynomial on the coset to recover secrets 40 | if degree2 { 41 | pp.secret2.fft_in_place(&mut result); 42 | result[0..pp.l * 2] 43 | .iter() 44 | .step_by(2) 45 | .copied() 46 | .collect::>() 47 | } else { 48 | pp.secret.fft_in_place(&mut result); 49 | result[0..pp.l].to_vec() 50 | } 51 | } 52 | 53 | pub fn packexp_from_public( 54 | secrets: &Vec, 55 | pp: &PackedSharingParams, 56 | ) -> Vec { 57 | debug_assert_eq!(secrets.len(), pp.l); 58 | 59 | let mut result = secrets.to_vec(); 60 | // interpolate secrets 61 | pp.secret.ifft_in_place(&mut result); 62 | 63 | // evaluate polynomial to get shares 64 | pp.share.fft_in_place(&mut result); 65 | 66 | result 67 | } 68 | 69 | pub fn d_msm( 70 | bases: &[G::Affine], 71 | scalars: &[G::ScalarField], 72 | pp: &PackedSharingParams, 73 | ) -> G { 74 | // Using affine is important because we don't want to create an extra vector for converting Projective to Affine. 75 | // Eventually we do have to convert to Projective but this will be pp.l group elements instead of m() 76 | 77 | // First round of local computation done by parties 78 | println!("bases: {}, scalars: {}", bases.len(), scalars.len()); 79 | let basemsm_timer = start_timer!(|| "Base MSM"); 80 | let c_share = G::msm(&bases, scalars).unwrap(); 81 | end_timer!(basemsm_timer); 82 | // Now we do degree reduction -- psstoss 83 | // Send to king who reduces and sends shamir shares (not packed). 84 | // Should be randomized. First convert to projective share. 85 | 86 | let king_answer: Option> = Net::send_to_king(&c_share).map(|shares: Vec| { 87 | let output: G = unpackexp(&shares, true, pp).iter().sum(); 88 | vec![output; Net::n_parties()] 89 | }); 90 | 91 | Net::recv_from_king(king_answer) 92 | } 93 | 94 | #[cfg(test)] 95 | mod tests { 96 | use ark_ec::bls12::Bls12Config; 97 | use ark_ec::CurveGroup; 98 | use ark_ec::Group; 99 | use ark_ec::VariableBaseMSM; 100 | use ark_std::UniformRand; 101 | use ark_std::Zero; 102 | use secret_sharing::pss::PackedSharingParams; 103 | 104 | use ark_bls12_377::G1Affine; 105 | use ark_bls12_377::G1Projective as G1P; 106 | type F = ::G1Config, 108 | > as Group>::ScalarField; 109 | 110 | use crate::dmsm::dmsm::packexp_from_public; 111 | use crate::dmsm::dmsm::unpackexp; 112 | use crate::utils::pack::transpose; 113 | 114 | const L: usize = 2; 115 | const N: usize = L * 4; 116 | // const T:usize = N/2 - L - 1; 117 | const M: usize = 1 << 8; 118 | 119 | #[test] 120 | fn pack_unpack_test() { 121 | let pp = PackedSharingParams::::new(L); 122 | let rng = &mut ark_std::test_rng(); 123 | let secrets: [G1P; L] = UniformRand::rand(rng); 124 | let secrets = secrets.to_vec(); 125 | 126 | let shares = packexp_from_public(&secrets, &pp); 127 | let result = unpackexp(&shares, false, &pp); 128 | 129 | assert_eq!(secrets, result); 130 | } 131 | 132 | #[test] 133 | fn pack_unpack2_test() { 134 | let pp = PackedSharingParams::::new(L); 135 | let rng = &mut ark_std::test_rng(); 136 | 137 | let gsecrets: [G1P; M] = [G1P::rand(rng); M]; 138 | let gsecrets = gsecrets.to_vec(); 139 | 140 | let fsecrets: [F; M] = [F::from(1 as u32); M]; 141 | let fsecrets = fsecrets.to_vec(); 142 | 143 | /////////////////////////////////////// 144 | let gsecrets_aff: Vec = gsecrets.iter().map(|s| s.clone().into()).collect(); 145 | let expected = G1P::msm(&gsecrets_aff, &fsecrets).unwrap(); 146 | /////////////////////////////////////// 147 | let gshares: Vec> = gsecrets 148 | .chunks(L) 149 | .map(|s| packexp_from_public(&s.to_vec(), &pp)) 150 | .collect(); 151 | 152 | let fshares: Vec> = fsecrets 153 | .chunks(L) 154 | .map(|s| pp.pack_from_public(&s.to_vec())) 155 | .collect(); 156 | 157 | let gshares = transpose(gshares); 158 | let fshares = transpose(fshares); 159 | 160 | let mut result = vec![G1P::zero(); N]; 161 | 162 | for i in 0..N { 163 | let temp_aff: Vec< 164 | ::G1Config, 166 | > as CurveGroup>::Affine, 167 | > = gshares[i].iter().map(|s| s.clone().into()).collect(); 168 | result[i] = G1P::msm(&temp_aff, &fshares[i]).unwrap(); 169 | } 170 | 171 | let result: G1P = unpackexp(&result, true, &pp).iter().sum(); 172 | assert_eq!(expected, result); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /dist-primitives/src/dmsm/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod dmsm; 2 | -------------------------------------------------------------------------------- /dist-primitives/src/dpp/dpp.rs: -------------------------------------------------------------------------------- 1 | // Evalauting a distributed version of partial products 2 | // Given x1, x2, .., xn, output x1, x1*x2, x1*x2*x3, .., x1*x2*..*xn 3 | 4 | use crate::{ 5 | channel::channel::MpcSerNet, 6 | utils::{ 7 | deg_red::deg_red, 8 | pack::{pack_vec, transpose}, 9 | }, 10 | }; 11 | use ark_ff::{FftField, Field, PrimeField}; 12 | use ark_std::{end_timer, start_timer}; 13 | use mpc_net::MpcMultiNet as Net; 14 | use secret_sharing::pss::PackedSharingParams; 15 | 16 | // Given pre-processed randomness [s], [s^-1] 17 | pub fn d_pp( 18 | num: Vec, 19 | den: Vec, 20 | pp: &PackedSharingParams, 21 | ) -> Vec { 22 | // using some dummy randomness 23 | let s = F::from(1 as u32); 24 | let sinv = s.inverse().unwrap(); 25 | 26 | // multiply all entries of px by of s 27 | let dpp_rand_timer = start_timer!(|| "DppRand"); 28 | let num_rand = num.iter().map(|&x| x * s).collect::>(); 29 | let mut den_rand = den.iter().map(|&x| x * s).collect::>(); 30 | end_timer!(dpp_rand_timer); 31 | 32 | let mut numden_rand = num_rand; 33 | numden_rand.append(&mut den_rand); 34 | 35 | // Along with degree reduction 36 | // King recovers secrets, computes partial products and repacks 37 | let communication_timer = start_timer!(|| "ComToKing"); 38 | let received_shares = Net::send_to_king(&numden_rand); 39 | end_timer!(communication_timer); 40 | 41 | let king_answer: Option>> = received_shares.map(|numden_shares: Vec>| { 42 | // nx(m/l) -> (m/l)xn 43 | debug_assert_eq!(numden_shares.len(), pp.n, "Mismatch of size in d_pp"); 44 | let dpp_timer = start_timer!(|| "DPP"); 45 | let numden_shares = transpose(numden_shares); 46 | 47 | // Unpack the secrets 48 | // (m/l)xn -> m 49 | // iterate over pxss_shares, unpack to get a vector and append all the vectors 50 | let mut numden: Vec = numden_shares.iter().flat_map(|x| pp.unpack2(&x)).collect(); 51 | 52 | for i in 0..numden.len() / 2 { 53 | let den = numden[i + numden.len() / 2].inverse().unwrap(); 54 | numden[i] *= den; 55 | } 56 | 57 | numden.truncate(numden.len() / 2); 58 | 59 | // Compute the partial products across pxss 60 | for i in 1..numden.len() { 61 | let last = numden[i - 1]; 62 | numden[i] *= last; 63 | } 64 | 65 | // Pack the secrets 66 | // m -> (m/l)xn 67 | // (m/l)xl -> (m/l)xn 68 | let pp_numden_shares = pack_vec(&numden, pp); 69 | drop(numden); 70 | 71 | // send shares to parties 72 | // (m/l)xn -> nx(m/l) 73 | let pp_numden_shares = transpose(pp_numden_shares); 74 | end_timer!(dpp_timer); 75 | pp_numden_shares 76 | }); 77 | 78 | let communication_timer = start_timer!(|| "ComFromKing"); 79 | let mut pp_numden_rand = Net::recv_from_king(king_answer); 80 | end_timer!(communication_timer); 81 | 82 | // Finally, remove the ranomness in the partial products 83 | // multiply all entries of pp_pxss by of s 84 | // do degree reduction 85 | let dpp_rand_timer = start_timer!(|| "DppRand"); 86 | pp_numden_rand.iter_mut().for_each(|x| *x *= sinv); 87 | end_timer!(dpp_rand_timer); 88 | let pp_numden = deg_red(pp_numden_rand, pp); //packed shares of partial products 89 | 90 | pp_numden 91 | } 92 | -------------------------------------------------------------------------------- /dist-primitives/src/dpp/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod dpp; 2 | -------------------------------------------------------------------------------- /dist-primitives/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod channel; 2 | pub mod dfft; 3 | pub mod dmsm; 4 | pub mod dpp; 5 | pub mod utils; 6 | 7 | use std::path::PathBuf; 8 | use structopt::StructOpt; 9 | 10 | #[derive(Debug, Clone, StructOpt)] 11 | #[structopt(name = "example", about = "An example of StructOpt usage.")] 12 | pub struct Opt { 13 | /// Id 14 | pub id: usize, 15 | 16 | /// Input file 17 | #[structopt(parse(from_os_str))] 18 | pub input: PathBuf, 19 | 20 | /// Packing factor 21 | pub l: usize, 22 | 23 | /// Threshold 24 | pub t: usize, 25 | 26 | /// FFT size 27 | pub m: usize, 28 | } 29 | -------------------------------------------------------------------------------- /dist-primitives/src/utils/deg_red.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::{FftField, PrimeField}; 2 | use ark_std::{end_timer, start_timer}; 3 | use mpc_net::MpcMultiNet as Net; 4 | use secret_sharing::pss::PackedSharingParams; 5 | 6 | use crate::channel::channel::MpcSerNet; 7 | 8 | use super::pack::transpose; 9 | 10 | /// Reduces the degree of a poylnomial with the help of king 11 | pub fn deg_red(px: Vec, pp: &PackedSharingParams) -> Vec { 12 | let communication_timer = start_timer!(|| "ComToKing"); 13 | let received_shares = Net::send_to_king(&px); 14 | end_timer!(communication_timer); 15 | let king_answer: Option>> = received_shares.map(|px_shares: Vec>| { 16 | let repack_shares_timer = start_timer!(|| "Unpack Pack shares"); 17 | let mut px_shares = transpose(px_shares); 18 | for i in 0..px_shares.len() { 19 | pp.unpack2_in_place(&mut px_shares[i]); 20 | pp.pack_from_public_in_place(&mut px_shares[i]); 21 | } 22 | end_timer!(repack_shares_timer); 23 | transpose(px_shares) 24 | }); 25 | 26 | let communication_timer = start_timer!(|| "ComFromKing"); 27 | let got_from_king = Net::recv_from_king(king_answer); 28 | end_timer!(communication_timer); 29 | 30 | got_from_king 31 | } 32 | -------------------------------------------------------------------------------- /dist-primitives/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod deg_red; 2 | pub mod pack; 3 | -------------------------------------------------------------------------------- /dist-primitives/src/utils/pack.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::FftField; 2 | use ark_std::{end_timer, start_timer}; 3 | use secret_sharing::pss::PackedSharingParams; 4 | 5 | pub fn pack_vec(secrets: &Vec, pp: &PackedSharingParams) -> Vec> { 6 | debug_assert_eq!(secrets.len() % pp.l, 0, "Mismatch of size in pack_vec"); 7 | let pack_shares_timer = start_timer!(|| "Packing shares"); 8 | 9 | // pack shares 10 | let shares = secrets 11 | .chunks(pp.l) 12 | .map(|x| pp.pack_from_public(&x.to_vec())) 13 | .collect::>(); 14 | 15 | end_timer!(pack_shares_timer); 16 | shares 17 | } 18 | 19 | pub fn transpose(v: Vec>) -> Vec> { 20 | assert!(!v.is_empty()); 21 | let len = v[0].len(); 22 | let mut iters: Vec<_> = v.into_iter().map(|n| n.into_iter()).collect(); 23 | (0..len) 24 | .map(|_| { 25 | iters 26 | .iter_mut() 27 | .map(|n| n.next().unwrap()) 28 | .collect::>() 29 | }) 30 | .collect() 31 | } 32 | -------------------------------------------------------------------------------- /groth16/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "groth16" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | # ARK Libraries 8 | ark-std = {version = "0.4.0", default-features = false, features = [ "print-trace", "std" ]} 9 | ark-ff = {version = "0.4.0", default-features = false} 10 | ark-poly = {version = "0.4.0", default-features = false} 11 | ark-ec = {version = "0.4.0", default-features = false} 12 | ark-serialize = { version = "0.4.0", default-features = false, features = [ "derive" ] } 13 | 14 | # ARK curves 15 | ark-bls12-377 = {version = "0.4.0", default-features = false, features = ["curve"] } 16 | 17 | # PSS and MPC Libraries 18 | secret-sharing = { version = "0.1.0", path = "../secret-sharing" } 19 | mpc-net = { version = "0.1.0", path = "../mpc-net" } 20 | dist-primitives = {version = "0.1.0", path = "../dist-primitives" } 21 | 22 | # Other Libraries 23 | log = {version = "0.4"} 24 | rand = { version = "0.8", default-features = false, features = ["std"] } 25 | digest = { version = "0.10" } 26 | sha2 = "0.10" 27 | structopt = "0.3" 28 | env_logger = "0.8" -------------------------------------------------------------------------------- /groth16/README.md: -------------------------------------------------------------------------------- 1 | This is an incomplete implementation of the groth16 proof system only used to estimate the prover time after the witness has been secret shared to the distributed provers. -------------------------------------------------------------------------------- /groth16/examples/ext_wit_bench.rs: -------------------------------------------------------------------------------- 1 | use ark_bls12_377::Fr; 2 | use ark_ff::PrimeField; 3 | use dist_primitives::Opt; 4 | use groth16::{ext_wit::d_ext_wit, ConstraintDomain}; 5 | use mpc_net::{MpcMultiNet as Net, MpcNet}; 6 | use rand::Rng; 7 | use secret_sharing::pss::PackedSharingParams; 8 | use structopt::StructOpt; 9 | 10 | fn groth_ext_wit( 11 | rng: &mut R, 12 | cd: &ConstraintDomain, 13 | pp: &PackedSharingParams, 14 | ) -> Vec { 15 | let mut p_eval: Vec = vec![F::rand(rng); cd.m / pp.l]; 16 | // Shares of P, Q, W drop from the sky 17 | 18 | for i in 1..p_eval.len() { 19 | p_eval[i] = p_eval[i - 1].double(); 20 | } 21 | let q_eval: Vec = p_eval.clone(); 22 | let w_eval: Vec = p_eval.clone(); 23 | 24 | d_ext_wit(p_eval, q_eval, w_eval, rng, pp, cd) 25 | } 26 | 27 | fn main() { 28 | env_logger::builder().format_timestamp(None).init(); 29 | 30 | let opt = Opt::from_args(); 31 | 32 | Net::init_from_file(opt.input.to_str().unwrap(), opt.id); 33 | 34 | let rng = &mut ark_std::test_rng(); 35 | for i in 14..15 { 36 | let pp = PackedSharingParams::::new(opt.l); 37 | let cd = ConstraintDomain::::new(1 << i); 38 | groth_ext_wit(rng, &cd, &pp); 39 | } 40 | 41 | Net::deinit(); 42 | } 43 | -------------------------------------------------------------------------------- /groth16/examples/groth_bench.rs: -------------------------------------------------------------------------------- 1 | use ark_ec::{bls12::Bls12, pairing::Pairing}; 2 | use ark_ff::UniformRand; 3 | use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; 4 | use ark_std::{end_timer, start_timer}; 5 | use dist_primitives::{dmsm::dmsm, Opt}; 6 | use log::debug; 7 | use mpc_net::{MpcMultiNet as Net, MpcNet}; 8 | use rand::Rng; 9 | use secret_sharing::pss::PackedSharingParams; 10 | use structopt::StructOpt; 11 | 12 | use ark_bls12_377; 13 | type BlsE = Bls12; 14 | type BlsFr = as Pairing>::ScalarField; 15 | 16 | use groth16::{ext_wit::groth_ext_wit, ConstraintDomain}; 17 | 18 | #[derive(Clone, Debug, Default, PartialEq, CanonicalSerialize, CanonicalDeserialize)] 19 | struct PackProvingKeyShare { 20 | pub s: Vec, 21 | pub u: Vec, 22 | pub v: Vec, 23 | pub w: Vec, 24 | pub h: Vec, 25 | } 26 | 27 | impl PackProvingKeyShare { 28 | pub fn new( 29 | domain_size: usize, 30 | rng: &mut R, 31 | pp: &PackedSharingParams, 32 | ) -> Self { 33 | let outer_time = start_timer!(|| "Dummy CRS packing"); 34 | let inner_time = start_timer!(|| "Packing S"); 35 | // println!("a_query:{}", pk.a_query.len()); 36 | let mut s_shares: Vec = vec![E::G1Affine::rand(rng); domain_size / pp.l]; 37 | for i in 1..s_shares.len() { 38 | s_shares[i] = E::G1Affine::rand(rng); 39 | } 40 | end_timer!(inner_time); 41 | 42 | let inner_time = start_timer!(|| "Packing U"); 43 | // println!("h_query:{}", pk.h_query.len()); 44 | let mut u_shares = vec![E::G1Affine::rand(rng); domain_size * 2 / pp.l]; 45 | for i in 1..u_shares.len() { 46 | u_shares[i] = E::G1Affine::rand(rng); 47 | } 48 | end_timer!(inner_time); 49 | 50 | let inner_time = start_timer!(|| "Packing W"); 51 | // println!("l_query:{}", pk.l_query.len()); 52 | let mut w_shares = vec![E::G1Affine::rand(rng); domain_size / pp.l]; 53 | for i in 1..w_shares.len() { 54 | w_shares[i] = E::G1Affine::rand(rng); 55 | } 56 | end_timer!(inner_time); 57 | 58 | let inner_time = start_timer!(|| "Packing H"); 59 | // println!("b_g1_query:{}", pk.b_g1_query.len()); 60 | let mut h_shares = vec![E::G1Affine::rand(rng); domain_size / pp.l]; 61 | for i in 1..h_shares.len() { 62 | h_shares[i] = E::G1Affine::rand(rng); 63 | } 64 | end_timer!(inner_time); 65 | 66 | let inner_time = start_timer!(|| "Packing V"); 67 | // println!("b_g2_query:{}", pk.b_g2_query.len()); 68 | let mut v_shares = vec![E::G2Affine::rand(rng); domain_size / pp.l]; 69 | for i in 1..v_shares.len() { 70 | v_shares[i] = E::G2Affine::rand(rng); 71 | } 72 | end_timer!(inner_time); 73 | end_timer!(outer_time); 74 | 75 | PackProvingKeyShare:: { 76 | s: s_shares, 77 | u: u_shares, 78 | v: v_shares, 79 | w: w_shares, 80 | h: h_shares, 81 | } 82 | } 83 | } 84 | 85 | fn dgroth( 86 | pp: &PackedSharingParams, 87 | cd: &ConstraintDomain, 88 | ) { 89 | // Add preprocessing vectors of size 4m/l 90 | // process u and v to get ready for multiplication 91 | 92 | // Field operations 93 | // u, v, w -- m/l + m/l + m/l 94 | // Compute IFFT(u) 95 | // Compute FFT(u) 96 | // Compute IFFT(v) 97 | // Compute FFT(v) 98 | // Compute IFFT(w) 99 | // Compute FFT(w) 100 | // u, v - 2m/l + 2m/l shares 101 | // w - 2m/l shares 102 | // t - 2m/l shares (Can be dropped in later by king so not contributing to memory) 103 | 104 | // Former can be avoided if client provides 2Q evaluations of u,v,w instead of Q evaluations 105 | // Computing h 106 | // Compute h = (u.v - w).t -- 2m/l shares 107 | // Send to king to pack desired coefficients of h 108 | 109 | // Group operations 110 | // Packed CRS drops in from the sky 111 | // Do 5 MSMs to obtain shares of A, B and C 112 | // Done 113 | 114 | let rng = &mut ark_std::test_rng(); 115 | let crs_share: PackProvingKeyShare = PackProvingKeyShare::::new(cd.m, rng, pp); 116 | let a_share: Vec = vec![E::ScalarField::rand(rng); crs_share.s.len()]; 117 | 118 | let h_share: Vec = groth_ext_wit(rng, cd, pp); 119 | 120 | println!( 121 | "s:{}, v:{}, h:{}, w:{}, u:{}, a:{}, h:{}", 122 | crs_share.s.len(), 123 | crs_share.v.len(), 124 | crs_share.h.len(), 125 | crs_share.w.len(), 126 | crs_share.u.len(), 127 | a_share.len(), 128 | h_share.len() 129 | ); 130 | 131 | let msm_section = start_timer!(|| "MSM operations"); 132 | // Compute msm while dropping the base vectors as they are not used again 133 | let _pi_a_share: E::G1 = dmsm::d_msm(&crs_share.s, &a_share, pp); 134 | println!("s done"); 135 | let _pi_b_share: E::G2 = dmsm::d_msm(&crs_share.v, &a_share, pp); 136 | println!("v done"); 137 | let _pi_c_share1: E::G1 = dmsm::d_msm(&crs_share.h, &a_share, pp); 138 | println!("h done"); 139 | let _pi_c_share2: E::G1 = dmsm::d_msm(&crs_share.w, &a_share, pp); 140 | println!("w done"); 141 | let _pi_c_share3: E::G1 = dmsm::d_msm(&crs_share.u, &h_share, pp); 142 | println!("u done"); 143 | let _pi_c_share: E::G1 = _pi_c_share1 + _pi_c_share2 + _pi_c_share3; //Additive notation for groups 144 | // Send _pi_a_share, _pi_b_share, _pi_c_share to client 145 | end_timer!(msm_section); 146 | 147 | debug!("Done"); 148 | } 149 | 150 | fn main() { 151 | env_logger::builder().format_timestamp(None).init(); 152 | 153 | let opt = Opt::from_args(); 154 | 155 | Net::init_from_file(opt.input.to_str().unwrap(), opt.id); 156 | let pp = PackedSharingParams::::new(opt.l); 157 | let cd = ConstraintDomain::::new(opt.m); 158 | dgroth::(&pp, &cd); 159 | if Net::am_king() { 160 | println!("Stats: {:#?}", Net::stats()); 161 | } 162 | 163 | Net::deinit(); 164 | } 165 | -------------------------------------------------------------------------------- /groth16/examples/local_groth_bench.rs: -------------------------------------------------------------------------------- 1 | use ark_ec::{bls12::Bls12, pairing::Pairing, VariableBaseMSM}; 2 | use ark_ff::UniformRand; 3 | use ark_poly::EvaluationDomain; 4 | use ark_std::{end_timer, start_timer, One, Zero}; 5 | use groth16::ConstraintDomain; 6 | use log::debug; 7 | use rand::Rng; 8 | 9 | use ark_bls12_377; 10 | type BlsE = Bls12; 11 | type BlsFr = as Pairing>::ScalarField; 12 | 13 | #[derive(Clone, Debug, Default, PartialEq)] 14 | struct ProvingKey { 15 | pub s: Vec, 16 | pub u: Vec, 17 | pub v: Vec, 18 | pub w: Vec, 19 | pub h: Vec, 20 | } 21 | 22 | fn local_dummy_crs(domain_size: usize, rng: &mut R) -> ProvingKey { 23 | let outer_time = start_timer!(|| "Dummy CRS packing"); 24 | 25 | let mut s: Vec<::G1Affine> = vec![E::G1Affine::rand(rng); domain_size]; 26 | for i in 1..s.len() { 27 | s[i] = (s[i - 1] + s[i - 1]).into(); 28 | } 29 | 30 | let mut u = vec![E::G1Affine::rand(rng); domain_size * 2]; 31 | for i in 1..u.len() { 32 | u[i] = (u[i - 1] + u[i - 1]).into(); 33 | } 34 | 35 | let mut w = vec![E::G1Affine::rand(rng); domain_size]; 36 | for i in 1..w.len() { 37 | w[i] = (w[i - 1] + w[i - 1]).into(); 38 | } 39 | 40 | let mut h = vec![E::G1Affine::rand(rng); domain_size]; 41 | for i in 1..h.len() { 42 | h[i] = (h[i - 1] + h[i - 1]).into(); 43 | } 44 | 45 | let mut v = vec![E::G2Affine::rand(rng); domain_size]; 46 | for i in 1..v.len() { 47 | v[i] = (v[i - 1] + v[i - 1]).into(); 48 | } 49 | 50 | end_timer!(outer_time); 51 | 52 | ProvingKey:: { s, u, v, w, h } 53 | } 54 | 55 | // Add preprocessing vectors of size 4m/l 56 | // process u and v to get ready for multiplication 57 | 58 | // Compute h = (u.v - w).t -- 2m shares 59 | // Field operations 60 | // u, v, w -- m + m + m 61 | // Compute IFFT(u) -- m/l preprocessing for sending + 2m/l for receiving (output padded with zeros and re-arranged) 62 | // Compute FFT(u) -- 2m/l for sending + 2m/l for receiving preprocessing 63 | // Compute IFFT(v) -- m/l preprocessing for sending + 2m/l for receiving (output padded with zeros and re-arranged) 64 | // Compute FFT(v) -- 2m/l for sending + 2m/l for receiving preprocessing 65 | // Compute IFFT(w) -- m/l preprocessing for sending + 2m/l for receiving (output padded with zeros and re-arranged) 66 | // Compute FFT(w) -- 2m/l for sending + 2m/l for receiving preprocessing 67 | // u, v - 2m/l + 2m/l shares 68 | // w - 2m/l shares 69 | // t - 2m/l shares (Can be dropped in later by king so not contributing to memory) 70 | 71 | // Total preprocessing -- 21m/l for u, v, w and 4m/l for computing h 72 | // Former can be avoided if client provides 2Q evaluations of u,v,w instead of Q evaluations 73 | // Computing h 74 | // Compute h = (u.v - w).t -- 2m/l shares 75 | // Send to king to pack desired coefficients of h -- 2m/l for sending + 2m/l for receiving 76 | // (potentially all of them if we only have addition gates hence 2m/l for receiving as well) 77 | 78 | // Group operations 79 | // Can ignore preprocessing here as it is tiny O(l) 80 | // Packed CRS drops in from the sky 81 | // Do 5 MSMs to obtain shares of A, B and C 82 | // Done 83 | 84 | fn localgroth_test(cd: &ConstraintDomain) { 85 | let mut p_eval: Vec = vec![E::ScalarField::zero(); cd.m]; 86 | // Shares of P, Q, W drop from the sky 87 | for i in 0..cd.m { 88 | p_eval[i] = E::ScalarField::from(i as u64); 89 | } 90 | let mut q_eval: Vec = p_eval.clone(); 91 | let mut w_eval: Vec = p_eval.clone(); 92 | 93 | let fft_section = start_timer!(|| "Field operations"); 94 | 95 | /////////IFFT 96 | cd.constraint.ifft_in_place(&mut p_eval); 97 | cd.constraint.ifft_in_place(&mut q_eval); 98 | cd.constraint.ifft_in_place(&mut w_eval); 99 | 100 | /////////FFT 101 | cd.constraint2.fft_in_place(&mut p_eval); 102 | cd.constraint2.fft_in_place(&mut q_eval); 103 | cd.constraint2.fft_in_place(&mut w_eval); 104 | 105 | ///////////Multiply Shares 106 | let mut h_eval: Vec = vec![E::ScalarField::zero(); p_eval.len()]; 107 | let t_eval: Vec = vec![E::ScalarField::one(); h_eval.len()]; 108 | for i in 0..p_eval.len() { 109 | h_eval[i] = p_eval[i] * q_eval[i] - w_eval[i]; 110 | } 111 | 112 | drop(p_eval); 113 | drop(q_eval); 114 | drop(w_eval); 115 | 116 | // King drops shares of t 117 | for i in 0..h_eval.len() { 118 | h_eval[i] *= t_eval[i]; 119 | } 120 | 121 | ///////////IFFT 122 | cd.constraint2.ifft_in_place(&mut h_eval); 123 | end_timer!(fft_section); 124 | 125 | let rng = &mut ark_std::test_rng(); 126 | let crs: ProvingKey = local_dummy_crs(cd.m, rng); 127 | let a_share: Vec = vec![E::ScalarField::rand(rng); crs.s.len()]; 128 | 129 | println!( 130 | "s:{}, v:{}, h:{}, w:{}, u:{}, a:{}, h:{}", 131 | crs.s.len(), 132 | crs.v.len(), 133 | crs.h.len(), 134 | crs.w.len(), 135 | crs.u.len(), 136 | a_share.len(), 137 | h_eval.len() 138 | ); 139 | 140 | let msm_section = start_timer!(|| "MSM operations"); 141 | // Compute msm while dropping the base vectors as they are not used again 142 | let _pi_a_share = E::G1::msm(&crs.s, &a_share).unwrap(); 143 | println!("s done"); 144 | let _pi_b_share = E::G2::msm(&crs.v, &a_share).unwrap(); 145 | println!("v done"); 146 | let _pi_c_share1 = E::G1::msm(&crs.h, &a_share).unwrap(); 147 | println!("h done"); 148 | let _pi_c_share2 = E::G1::msm(&crs.w, &a_share).unwrap(); 149 | println!("w done"); 150 | let _pi_c_share3 = E::G1::msm(&crs.u, &h_eval).unwrap(); 151 | println!("u done"); 152 | let _pi_c_share = _pi_c_share1 + _pi_c_share2 + _pi_c_share3; //Additive notation for groups 153 | // Send _pi_a_share, _pi_b_share, _pi_c_share to client 154 | end_timer!(msm_section); 155 | } 156 | 157 | fn main() { 158 | debug!("Start"); 159 | let cd = ConstraintDomain::::new(1 << 15); 160 | localgroth_test::(&cd); 161 | 162 | debug!("Done"); 163 | } 164 | -------------------------------------------------------------------------------- /groth16/src/ext_wit.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::{FftField, PrimeField}; 2 | use ark_std::{end_timer, start_timer}; 3 | use dist_primitives::dfft::dfft::{d_fft, d_ifft}; 4 | use rand::Rng; 5 | use secret_sharing::pss::PackedSharingParams; 6 | 7 | use crate::ConstraintDomain; 8 | 9 | pub fn d_ext_wit( 10 | p_eval: Vec, 11 | q_eval: Vec, 12 | w_eval: Vec, 13 | rng: &mut R, 14 | pp: &PackedSharingParams, 15 | cd: &ConstraintDomain, 16 | ) -> Vec { 17 | // Preprocessing to account for memory usage 18 | let mut single_pp: Vec> = vec![vec![F::one(); cd.m / pp.l]; 3]; 19 | let mut double_pp: Vec> = vec![vec![F::one(); 2 * cd.m / pp.l]; 11]; 20 | 21 | let fft_section = start_timer!(|| "Field operations"); 22 | /////////////IFFT 23 | // Starting with shares of evals 24 | let p_coeff = d_ifft(p_eval, true, 2, false, &cd.constraint, pp); 25 | let q_coeff = d_ifft(q_eval, true, 2, false, &cd.constraint, pp); 26 | let w_coeff = d_ifft(w_eval, true, 2, false, &cd.constraint, pp); 27 | 28 | // deleting randomness used 29 | single_pp.truncate(single_pp.len() - 3); 30 | double_pp.truncate(double_pp.len() - 3); 31 | 32 | /////////////FFT 33 | // Starting with shares of coefficients 34 | let p_eval = d_fft(p_coeff, true, 1, false, &cd.constraint2, pp); 35 | let q_eval = d_fft(q_coeff, true, 1, false, &cd.constraint2, pp); 36 | let w_eval = d_fft(w_coeff, true, 1, false, &cd.constraint2, pp); 37 | 38 | // deleting randomness used 39 | double_pp.truncate(double_pp.len() - 6); 40 | 41 | ///////////Multiply Shares 42 | let mut h_eval: Vec = vec![F::zero(); p_eval.len()]; 43 | for i in 0..p_eval.len() { 44 | h_eval[i] = p_eval[i] * q_eval[i] - w_eval[i]; 45 | } 46 | drop(p_eval); 47 | drop(q_eval); 48 | drop(w_eval); 49 | 50 | // King drops shares of t 51 | let t_eval: Vec = vec![F::rand(rng); h_eval.len()]; 52 | for i in 0..h_eval.len() { 53 | h_eval[i] *= t_eval[i]; 54 | } 55 | 56 | // Interpolate h and extract the first u_len coefficients from it as the higher coefficients will be zero 57 | ///////////IFFT 58 | // Starting with shares of evals 59 | let sizeinv = F::one() / F::from(cd.constraint.size as u64); 60 | for i in 0..h_eval.len() { 61 | h_eval[i] *= sizeinv; 62 | } 63 | 64 | // Parties apply FFT1 locally 65 | let mut h_coeff = d_ifft(h_eval, false, 1, true, &cd.constraint2, pp); 66 | 67 | // deleting randomness used 68 | double_pp.truncate(double_pp.len() - 2); 69 | 70 | h_coeff.truncate(2 * cd.m); 71 | end_timer!(fft_section); 72 | 73 | h_coeff 74 | } 75 | 76 | pub fn groth_ext_wit( 77 | rng: &mut R, 78 | cd: &ConstraintDomain, 79 | pp: &PackedSharingParams, 80 | ) -> Vec { 81 | let mut p_eval: Vec = vec![F::rand(rng); cd.m / pp.l]; 82 | // Shares of P, Q, W drop from the sky 83 | 84 | for i in 1..p_eval.len() { 85 | p_eval[i] = p_eval[i - 1].double(); 86 | } 87 | let q_eval: Vec = p_eval.clone(); 88 | let w_eval: Vec = p_eval.clone(); 89 | 90 | d_ext_wit(p_eval, q_eval, w_eval, rng, pp, cd) 91 | } 92 | -------------------------------------------------------------------------------- /groth16/src/lib.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::FftField; 2 | use ark_poly::{domain::EvaluationDomain, Radix2EvaluationDomain}; 3 | 4 | pub mod ext_wit; 5 | 6 | #[derive(Debug, Clone, PartialEq)] 7 | pub struct ConstraintDomain 8 | where 9 | F: FftField, 10 | { 11 | pub m: usize, // Constraint size 12 | pub constraint: Radix2EvaluationDomain, // Constraint domain 13 | pub constraint2: Radix2EvaluationDomain, // Constraint2 domain 14 | } 15 | 16 | impl ConstraintDomain { 17 | #[allow(unused)] 18 | pub fn new(m: usize) -> Self { 19 | let constraint = Radix2EvaluationDomain::::new(m).unwrap(); 20 | let constraint2 = Radix2EvaluationDomain::::new(2 * m).unwrap(); 21 | 22 | debug_assert_eq!(constraint.size(), m); 23 | debug_assert_eq!(constraint2.size(), 2 * m); 24 | 25 | ConstraintDomain { 26 | m, 27 | constraint, 28 | constraint2, 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /mpc-net/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mpc-net" 3 | version = "0.1.0" 4 | authors = ["Alex Ozdemir "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | lazy_static = "1" 11 | log = {version = "0.4"} 12 | ark-std = {version = "0.4.0", default-features = false} 13 | derivative = { version = "2.0", features = ["use_core"]} 14 | rayon = "1.5.1" 15 | 16 | [dev-dependencies] 17 | structopt = { version = "0.3" } 18 | env_logger = "0.10" 19 | 20 | -------------------------------------------------------------------------------- /mpc-net/README.md: -------------------------------------------------------------------------------- 1 | This crate was copied from https://github.com/alex-ozdemir/collaborative-zksnark -------------------------------------------------------------------------------- /mpc-net/data/4: -------------------------------------------------------------------------------- 1 | 127.0.0.1:8000 2 | 127.0.0.1:8001 3 | 127.0.0.1:8002 4 | 127.0.0.1:8003 5 | -------------------------------------------------------------------------------- /mpc-net/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod multi; 2 | pub mod two; 3 | 4 | pub use multi::MpcMultiNet; 5 | pub use two::MpcTwoNet; 6 | 7 | #[derive(Clone, Debug)] 8 | pub struct Stats { 9 | pub bytes_sent: usize, 10 | pub bytes_recv: usize, 11 | pub broadcasts: usize, 12 | pub to_king: usize, 13 | pub from_king: usize, 14 | } 15 | 16 | impl std::default::Default for Stats { 17 | fn default() -> Self { 18 | Self { 19 | bytes_sent: 0, 20 | bytes_recv: 0, 21 | broadcasts: 0, 22 | to_king: 0, 23 | from_king: 0, 24 | } 25 | } 26 | } 27 | 28 | pub trait MpcNet { 29 | /// Am I the first party? 30 | #[inline] 31 | fn am_king() -> bool { 32 | Self::party_id() == 0 33 | } 34 | /// How many parties are there? 35 | fn n_parties() -> usize; 36 | /// What is my party number (0 to n-1)? 37 | fn party_id() -> usize; 38 | /// Initialize the network layer from a file. 39 | /// The file should contain one HOST:PORT setting per line, corresponding to the addresses of 40 | /// the parties in increasing order. 41 | /// 42 | /// Parties are zero-indexed. 43 | fn init_from_file(path: &str, party_id: usize); 44 | /// Is the network layer initalized? 45 | fn is_init() -> bool; 46 | /// Uninitialize the network layer, closing all connections. 47 | fn deinit(); 48 | /// Set statistics to zero. 49 | fn reset_stats(); 50 | /// Get statistics. 51 | fn stats() -> Stats; 52 | /// All parties send bytes to each other. 53 | fn broadcast_bytes(bytes: &[u8]) -> Vec>; 54 | /// All parties send bytes to the king. 55 | fn send_bytes_to_king(bytes: &[u8]) -> Option>>; 56 | /// All parties recv bytes from the king. 57 | /// Provide bytes iff you're the king! 58 | fn recv_bytes_from_king(bytes: Option>>) -> Vec; 59 | 60 | /// Everyone sends bytes to the king, who recieves those bytes, runs a computation on them, and 61 | /// redistributes the resulting bytes. 62 | /// 63 | /// The king's computation is given by a function, `f` 64 | /// proceeds. 65 | #[inline] 66 | fn king_compute(bytes: &[u8], f: impl Fn(Vec>) -> Vec>) -> Vec { 67 | let king_response = Self::send_bytes_to_king(bytes).map(f); 68 | Self::recv_bytes_from_king(king_response) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /mpc-net/src/multi.rs: -------------------------------------------------------------------------------- 1 | use lazy_static::lazy_static; 2 | use log::debug; 3 | use rayon::prelude::*; 4 | use std::fs::File; 5 | use std::io::{BufRead, BufReader, Read, Write}; 6 | use std::net::{SocketAddr, TcpListener, TcpStream}; 7 | use std::sync::Mutex; 8 | 9 | use ark_std::{end_timer, start_timer}; 10 | 11 | use super::{MpcNet, Stats}; 12 | 13 | lazy_static! { 14 | static ref CONNECTIONS: Mutex = Mutex::new(Connections::default()); 15 | } 16 | 17 | /// Macro for locking the FieldChannel singleton in the current scope. 18 | macro_rules! get_ch { 19 | () => { 20 | CONNECTIONS.lock().expect("Poisoned FieldChannel") 21 | }; 22 | } 23 | 24 | #[derive(Debug)] 25 | struct Peer { 26 | _id: usize, 27 | addr: SocketAddr, 28 | stream: Option, 29 | } 30 | 31 | #[derive(Default, Debug)] 32 | struct Connections { 33 | id: usize, 34 | peers: Vec, 35 | stats: Stats, 36 | } 37 | 38 | impl std::default::Default for Peer { 39 | fn default() -> Self { 40 | Self { 41 | _id: 0, 42 | addr: "127.0.0.1:8000".parse().unwrap(), 43 | stream: None, 44 | } 45 | } 46 | } 47 | 48 | impl Connections { 49 | /// Given a path and the `id` of oneself, initialize the structure 50 | fn init_from_path(&mut self, path: &str, id: usize) { 51 | let f = BufReader::new(File::open(path).expect("host configuration path")); 52 | let mut peer_id = 0; 53 | for line in f.lines() { 54 | let line = line.unwrap(); 55 | let trimmed = line.trim(); 56 | if trimmed.len() > 0 { 57 | let addr: SocketAddr = trimmed 58 | .parse() 59 | .unwrap_or_else(|e| panic!("bad socket address: {}:\n{}", trimmed, e)); 60 | let peer = Peer { 61 | _id: peer_id, 62 | addr, 63 | stream: None, 64 | }; 65 | self.peers.push(peer); 66 | peer_id += 1; 67 | } 68 | } 69 | assert!(id < self.peers.len()); 70 | self.id = id; 71 | } 72 | fn connect_to_all(&mut self) { 73 | let timer = start_timer!(|| "Connecting"); 74 | let n = self.peers.len(); 75 | for from_id in 0..n { 76 | for to_id in (from_id + 1)..n { 77 | debug!("{} to {}", from_id, to_id); 78 | if self.id == from_id { 79 | let to_addr = self.peers[to_id].addr; 80 | debug!("Contacting {}", to_id); 81 | let stream = loop { 82 | let mut ms_waited = 0; 83 | match TcpStream::connect(to_addr) { 84 | Ok(s) => break s, 85 | Err(e) => match e.kind() { 86 | std::io::ErrorKind::ConnectionRefused 87 | | std::io::ErrorKind::ConnectionReset => { 88 | ms_waited += 10; 89 | std::thread::sleep(std::time::Duration::from_millis(10)); 90 | if ms_waited % 3_000 == 0 { 91 | debug!("Still waiting"); 92 | } else if ms_waited > 30_000 { 93 | panic!("Could not find peer in 30s"); 94 | } 95 | } 96 | _ => { 97 | panic!("Error during FieldChannel::new: {}", e); 98 | } 99 | }, 100 | } 101 | }; 102 | stream.set_nodelay(true).unwrap(); 103 | self.peers[to_id].stream = Some(stream); 104 | } else if self.id == to_id { 105 | debug!("Awaiting {}", from_id); 106 | let listener = TcpListener::bind(self.peers[self.id].addr).unwrap(); 107 | let (stream, _addr) = listener.accept().unwrap(); 108 | stream.set_nodelay(true).unwrap(); 109 | self.peers[from_id].stream = Some(stream); 110 | } 111 | } 112 | // Sender for next round waits for note from this sender to prevent race on receipt. 113 | if from_id + 1 < n { 114 | if self.id == from_id { 115 | self.peers[self.id + 1] 116 | .stream 117 | .as_mut() 118 | .unwrap() 119 | .write_all(&[0u8]) 120 | .unwrap(); 121 | } else if self.id == from_id + 1 { 122 | self.peers[self.id - 1] 123 | .stream 124 | .as_mut() 125 | .unwrap() 126 | .read_exact(&mut [0u8]) 127 | .unwrap(); 128 | } 129 | } 130 | } 131 | // Do a round with the king, to be sure everyone is ready 132 | let from_all = self.send_to_king(&[self.id as u8]); 133 | self.recv_from_king(from_all); 134 | for id in 0..n { 135 | if id != self.id { 136 | assert!(self.peers[id].stream.is_some()); 137 | } 138 | } 139 | end_timer!(timer); 140 | } 141 | fn am_king(&self) -> bool { 142 | self.id == 0 143 | } 144 | fn broadcast(&mut self, bytes_out: &[u8]) -> Vec> { 145 | let timer = start_timer!(|| format!("Broadcast {}", bytes_out.len())); 146 | let m = bytes_out.len(); 147 | let own_id = self.id; 148 | self.stats.bytes_sent += (self.peers.len() - 1) * m; 149 | self.stats.bytes_recv += (self.peers.len() - 1) * m; 150 | self.stats.broadcasts += 1; 151 | let r = self 152 | .peers 153 | .par_iter_mut() 154 | .enumerate() 155 | .map(|(id, peer)| { 156 | let mut bytes_in = vec![0u8; m]; 157 | if id < own_id { 158 | let stream = peer.stream.as_mut().unwrap(); 159 | stream.read_exact(&mut bytes_in[..]).unwrap(); 160 | stream.write_all(bytes_out).unwrap(); 161 | } else if id == own_id { 162 | bytes_in.copy_from_slice(bytes_out); 163 | } else { 164 | let stream = peer.stream.as_mut().unwrap(); 165 | stream.write_all(bytes_out).unwrap(); 166 | stream.read_exact(&mut bytes_in[..]).unwrap(); 167 | }; 168 | bytes_in 169 | }) 170 | .collect(); 171 | end_timer!(timer); 172 | r 173 | } 174 | fn send_to_king(&mut self, bytes_out: &[u8]) -> Option>> { 175 | let timer = start_timer!(|| format!("To king {}", bytes_out.len())); 176 | let m = bytes_out.len(); 177 | let own_id = self.id; 178 | self.stats.to_king += 1; 179 | let r = if self.am_king() { 180 | self.stats.bytes_recv += (self.peers.len() - 1) * m; 181 | Some( 182 | self.peers 183 | .par_iter_mut() 184 | .enumerate() 185 | .map(|(id, peer)| { 186 | let mut bytes_in = vec![0u8; m]; 187 | if id == own_id { 188 | bytes_in.copy_from_slice(bytes_out); 189 | } else { 190 | let stream = peer.stream.as_mut().unwrap(); 191 | stream.read_exact(&mut bytes_in[..]).unwrap(); 192 | }; 193 | bytes_in 194 | }) 195 | .collect(), 196 | ) 197 | } else { 198 | self.stats.bytes_sent += m; 199 | self.peers[0] 200 | .stream 201 | .as_mut() 202 | .unwrap() 203 | .write_all(bytes_out) 204 | .unwrap(); 205 | None 206 | }; 207 | end_timer!(timer); 208 | r 209 | } 210 | fn recv_from_king(&mut self, bytes_out: Option>>) -> Vec { 211 | let own_id = self.id; 212 | self.stats.from_king += 1; 213 | if self.am_king() { 214 | let bytes_out = bytes_out.unwrap(); 215 | let m = bytes_out[0].len(); 216 | let timer = start_timer!(|| format!("From king {}", m)); 217 | let bytes_size = (m as u64).to_le_bytes(); 218 | self.stats.bytes_sent += (self.peers.len() - 1) * (m + 8); 219 | self.peers 220 | .par_iter_mut() 221 | .enumerate() 222 | .filter(|p| p.0 != own_id) 223 | .for_each(|(id, peer)| { 224 | let stream = peer.stream.as_mut().unwrap(); 225 | assert_eq!(bytes_out[id].len(), m); 226 | stream.write_all(&bytes_size).unwrap(); 227 | stream.write_all(&bytes_out[id]).unwrap(); 228 | }); 229 | end_timer!(timer); 230 | bytes_out[own_id].clone() 231 | } else { 232 | let stream = self.peers[0].stream.as_mut().unwrap(); 233 | let mut bytes_size = [0u8; 8]; 234 | stream.read_exact(&mut bytes_size).unwrap(); 235 | let m = u64::from_le_bytes(bytes_size) as usize; 236 | self.stats.bytes_recv += m; 237 | let mut bytes_in = vec![0u8; m]; 238 | stream.read_exact(&mut bytes_in).unwrap(); 239 | bytes_in 240 | } 241 | } 242 | fn uninit(&mut self) { 243 | for p in &mut self.peers { 244 | p.stream = None; 245 | } 246 | } 247 | } 248 | 249 | pub struct MpcMultiNet; 250 | 251 | impl MpcNet for MpcMultiNet { 252 | #[inline] 253 | fn party_id() -> usize { 254 | get_ch!().id 255 | } 256 | 257 | #[inline] 258 | fn n_parties() -> usize { 259 | get_ch!().peers.len() 260 | } 261 | 262 | #[inline] 263 | fn init_from_file(path: &str, party_id: usize) { 264 | let mut ch = get_ch!(); 265 | ch.init_from_path(path, party_id); 266 | ch.connect_to_all(); 267 | } 268 | 269 | #[inline] 270 | fn is_init() -> bool { 271 | get_ch!() 272 | .peers 273 | .first() 274 | .map(|p| p.stream.is_some()) 275 | .unwrap_or(false) 276 | } 277 | 278 | #[inline] 279 | fn deinit() { 280 | get_ch!().uninit() 281 | } 282 | 283 | #[inline] 284 | fn reset_stats() { 285 | get_ch!().stats = Stats::default(); 286 | } 287 | 288 | #[inline] 289 | fn stats() -> crate::Stats { 290 | get_ch!().stats.clone() 291 | } 292 | 293 | #[inline] 294 | fn broadcast_bytes(bytes: &[u8]) -> Vec> { 295 | get_ch!().broadcast(bytes) 296 | } 297 | 298 | #[inline] 299 | fn send_bytes_to_king(bytes: &[u8]) -> Option>> { 300 | get_ch!().send_to_king(bytes) 301 | } 302 | 303 | #[inline] 304 | fn recv_bytes_from_king(bytes: Option>>) -> Vec { 305 | get_ch!().recv_from_king(bytes) 306 | } 307 | } 308 | -------------------------------------------------------------------------------- /mpc-net/src/two.rs: -------------------------------------------------------------------------------- 1 | use lazy_static::lazy_static; 2 | use log::debug; 3 | use std::fs::File; 4 | use std::io::{BufRead, BufReader, Read, Write}; 5 | use std::net::{SocketAddr, TcpListener, TcpStream}; 6 | use std::sync::Mutex; 7 | 8 | use ark_std::{end_timer, start_timer}; 9 | 10 | use super::{MpcNet, Stats}; 11 | 12 | lazy_static! { 13 | pub static ref CH: Mutex = Mutex::new(FieldChannel::default()); 14 | } 15 | 16 | /// Macro for locking the FieldChannel singleton in the current scope. 17 | macro_rules! get_ch { 18 | () => { 19 | CH.lock().expect("Poisoned FieldChannel") 20 | }; 21 | } 22 | 23 | pub struct FieldChannel { 24 | /// Empty if unitialized 25 | pub stream: Option, 26 | pub self_addr: SocketAddr, 27 | pub other_addr: SocketAddr, 28 | pub stats: Stats, 29 | pub talk_first: bool, 30 | } 31 | 32 | impl std::default::Default for FieldChannel { 33 | #[inline] 34 | fn default() -> Self { 35 | Self { 36 | stream: None, 37 | self_addr: "127.0.0.1:8000".parse().unwrap(), 38 | other_addr: "127.0.0.1:8000".parse().unwrap(), 39 | stats: Stats::default(), 40 | talk_first: false, 41 | } 42 | } 43 | } 44 | 45 | impl FieldChannel { 46 | fn init_from_path(&mut self, path: &str, id: usize) { 47 | let f = BufReader::new(File::open(path).expect("host configuration path")); 48 | let mut addrs = Vec::new(); 49 | for line in f.lines() { 50 | let line = line.unwrap(); 51 | let trimmed = line.trim(); 52 | if trimmed.len() > 0 { 53 | let addr: SocketAddr = trimmed 54 | .parse() 55 | .unwrap_or_else(|e| panic!("bad socket address: {}:\n{}", trimmed, e)); 56 | addrs.push(addr); 57 | } 58 | } 59 | assert_eq!(addrs.len(), 2); 60 | assert!(id < addrs.len()); 61 | self.self_addr = addrs[id]; 62 | self.other_addr = addrs[1 - id]; 63 | self.talk_first = id == 0; 64 | } 65 | 66 | #[inline] 67 | pub fn connect(&mut self) { 68 | debug!("I am {}, connecting to {}", self.self_addr, self.other_addr); 69 | self.stream = Some(if self.talk_first { 70 | debug!("Attempting to contact peer"); 71 | loop { 72 | let mut ms_waited = 0; 73 | match TcpStream::connect(self.other_addr) { 74 | Ok(s) => break s, 75 | Err(e) => { 76 | if e.kind() == std::io::ErrorKind::ConnectionRefused { 77 | ms_waited += 100; 78 | std::thread::sleep(std::time::Duration::from_millis(100)); 79 | if ms_waited % 3_000 == 0 { 80 | debug!("Still waiting"); 81 | } else if ms_waited > 30_000 { 82 | panic!("Could not find peer in 30s"); 83 | } 84 | } else { 85 | panic!("Error during FieldChannel::new: {}", e); 86 | } 87 | } 88 | } 89 | } 90 | } else { 91 | let listener = TcpListener::bind(self.self_addr).unwrap(); 92 | debug!("Waiting for peer to contact us"); 93 | let (stream, _addr) = listener.accept().unwrap(); 94 | stream 95 | }); 96 | // disable nagle's alg 97 | self.stream.as_mut().unwrap().set_nodelay(true).unwrap(); 98 | self.stream.as_mut().unwrap().set_nonblocking(true).unwrap(); 99 | } 100 | #[inline] 101 | pub fn stream(&mut self) -> &mut TcpStream { 102 | self.stream 103 | .as_mut() 104 | .expect("Unitialized FieldChannel. Did you forget init(..)?") 105 | } 106 | 107 | #[inline] 108 | pub fn send_slice(&mut self, v: &[u8]) { 109 | let s = self.stream(); 110 | s.set_nonblocking(false).unwrap(); 111 | let bytes = (v.len() as u64).to_ne_bytes(); 112 | s.write_all(&bytes[..]).unwrap(); 113 | s.write_all(v).unwrap(); 114 | s.set_nonblocking(true).unwrap(); 115 | self.stats.bytes_sent += bytes.len() + v.len(); 116 | } 117 | 118 | #[inline] 119 | pub fn recv_vec(&mut self) -> Vec { 120 | let s = self.stream(); 121 | let mut len = [0u8; 8]; 122 | s.set_nonblocking(false).unwrap(); 123 | s.read_exact(&mut len[..]).unwrap(); 124 | let mut bytes = vec![0u8; u64::from_ne_bytes(len) as usize]; 125 | s.read_exact(&mut bytes[..]).unwrap(); 126 | s.set_nonblocking(true).unwrap(); 127 | self.stats.bytes_recv += bytes.len() + len.len(); 128 | bytes 129 | } 130 | 131 | #[inline] 132 | pub fn exchange_bytes(&mut self, bytes_out: &[u8]) -> std::io::Result> { 133 | let timer = start_timer!(|| format!("Exchanging {}", bytes_out.len())); 134 | let s = self.stream(); 135 | let n = bytes_out.len(); 136 | let mut bytes_in = vec![0u8; n]; 137 | let mut bytes_in_offset = 0; 138 | let mut bytes_out_offset = 0; 139 | while bytes_out_offset < n || bytes_in_offset < n { 140 | if bytes_out_offset < n { 141 | match s.write(&bytes_out[bytes_out_offset..]) { 142 | Ok(written) => { 143 | bytes_out_offset += written; 144 | let _e = s.flush(); 145 | } 146 | Err(e) => { 147 | if e.kind() == std::io::ErrorKind::WouldBlock { 148 | } else if e.kind() == std::io::ErrorKind::Interrupted { 149 | } else { 150 | return Err(e); 151 | } 152 | } 153 | } 154 | } 155 | if bytes_in_offset < n { 156 | match s.read(&mut bytes_in[bytes_in_offset..]) { 157 | Ok(read) => { 158 | bytes_in_offset += read; 159 | } 160 | Err(e) => { 161 | if e.kind() == std::io::ErrorKind::WouldBlock { 162 | } else if e.kind() == std::io::ErrorKind::Interrupted { 163 | } else { 164 | return Err(e); 165 | } 166 | } 167 | } 168 | } 169 | } 170 | self.stats.broadcasts += 1; 171 | self.stats.bytes_sent += n; 172 | self.stats.bytes_recv += n; 173 | end_timer!(timer); 174 | Ok(bytes_in) 175 | } 176 | 177 | #[inline] 178 | pub fn stats(&self) -> Stats { 179 | self.stats.clone() 180 | } 181 | 182 | #[inline] 183 | pub fn reset_stats(&mut self) { 184 | self.stats = Stats::default(); 185 | } 186 | } 187 | 188 | #[inline] 189 | /// Initialize the MPC 190 | pub fn init_from_path(path: &str, id: usize) { 191 | let mut ch = get_ch!(); 192 | assert!( 193 | ch.stream.is_none(), 194 | "FieldChannel should no be re-intialized. Did you call init(..) twice?" 195 | ); 196 | ch.init_from_path(path, id); 197 | ch.connect(); 198 | debug!("Connected"); 199 | } 200 | 201 | #[inline] 202 | pub fn deinit() { 203 | CH.lock().expect("Poisoned FieldChannel").stream = None; 204 | } 205 | 206 | #[inline] 207 | pub fn exchange_bytes(bytes_out: &[u8]) -> std::io::Result> { 208 | CH.lock() 209 | .expect("Poisoned FieldChannel") 210 | .exchange_bytes(bytes_out) 211 | } 212 | 213 | #[inline] 214 | pub fn is_init() -> bool { 215 | get_ch!().stream.is_some() 216 | } 217 | 218 | #[inline] 219 | pub fn stats() -> Stats { 220 | let ch = get_ch!(); 221 | ch.stats() 222 | } 223 | 224 | #[inline] 225 | pub fn reset_stats() { 226 | let mut ch = get_ch!(); 227 | ch.reset_stats(); 228 | } 229 | 230 | /// Are you the first party in the MPC? 231 | #[inline] 232 | pub fn am_first() -> bool { 233 | let ch = get_ch!(); 234 | assert!(ch.stream.is_some(), "uninit channel"); 235 | ch.talk_first 236 | } 237 | 238 | pub struct MpcTwoNet; 239 | 240 | impl MpcNet for MpcTwoNet { 241 | #[inline] 242 | fn party_id() -> usize { 243 | let first = get_ch!().talk_first; 244 | if first { 245 | 0 246 | } else { 247 | 1 248 | } 249 | } 250 | 251 | #[inline] 252 | fn n_parties() -> usize { 253 | 2 254 | } 255 | 256 | #[inline] 257 | fn init_from_file(path: &str, party_id: usize) { 258 | get_ch!().init_from_path(path, party_id); 259 | } 260 | 261 | #[inline] 262 | fn is_init() -> bool { 263 | get_ch!().stream.is_some() 264 | } 265 | 266 | #[inline] 267 | fn deinit() { 268 | get_ch!().stream = None; 269 | } 270 | 271 | #[inline] 272 | fn reset_stats() { 273 | get_ch!().stats = Stats::default(); 274 | } 275 | 276 | #[inline] 277 | fn stats() -> crate::Stats { 278 | get_ch!().stats.clone() 279 | } 280 | 281 | #[inline] 282 | fn broadcast_bytes(bytes: &[u8]) -> Vec> { 283 | let other = get_ch!().exchange_bytes(bytes).unwrap(); 284 | if Self::am_king() { 285 | vec![bytes.to_vec(), other] 286 | } else { 287 | vec![other, bytes.to_vec()] 288 | } 289 | } 290 | 291 | #[inline] 292 | fn send_bytes_to_king(bytes: &[u8]) -> Option>> { 293 | let mut ch = get_ch!(); 294 | ch.stats.to_king += 1; 295 | if ch.talk_first { 296 | let other = ch.recv_vec(); 297 | debug_assert_eq!(bytes.len(), other.len()); 298 | Some(vec![bytes.to_vec(), other]) 299 | } else { 300 | ch.send_slice(bytes); 301 | None 302 | } 303 | } 304 | 305 | #[inline] 306 | fn recv_bytes_from_king(bytes: Option>>) -> Vec { 307 | let mut ch = get_ch!(); 308 | ch.stats.from_king += 1; 309 | if ch.talk_first { 310 | let mut bytes = bytes.expect("king needs bytes"); 311 | assert_eq!(bytes.len(), 2); 312 | ch.send_slice(&bytes.pop().unwrap()); 313 | bytes.pop().unwrap() 314 | } else { 315 | ch.recv_vec() 316 | } 317 | } 318 | } 319 | -------------------------------------------------------------------------------- /mpc-net/test.zsh: -------------------------------------------------------------------------------- 1 | set -ex 2 | trap "exit" INT TERM 3 | trap "kill 0" EXIT 4 | 5 | cargo build --example broadcast 6 | BIN=./target/debug/examples/broadcast 7 | 8 | PROCS=() 9 | for i in 0 1 2 3 10 | do 11 | $BIN $i ./data/4 & 12 | #RUST_LOG=debug $BIN $i ./data/4 & 13 | pid=$! 14 | PROCS+=("$pid") 15 | done 16 | jobs -pr 17 | 18 | for pid in $PROCS 19 | do 20 | jobs -pr 21 | wait $pid 22 | jobs -pr 23 | done 24 | 25 | echo done 26 | 27 | -------------------------------------------------------------------------------- /network-address/4: -------------------------------------------------------------------------------- 1 | 127.0.0.1:8000 2 | 127.0.0.1:8001 3 | 127.0.0.1:8002 4 | 127.0.0.1:8003 -------------------------------------------------------------------------------- /network-address/8: -------------------------------------------------------------------------------- 1 | 127.0.0.1:8000 2 | 127.0.0.1:8001 3 | 127.0.0.1:8002 4 | 127.0.0.1:8003 5 | 127.0.0.1:8004 6 | 127.0.0.1:8005 7 | 127.0.0.1:8006 8 | 127.0.0.1:8007 -------------------------------------------------------------------------------- /plonk/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "plonk" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | # ARK Libraries 8 | ark-std = {version = "0.4.0", default-features = false, features = [ "print-trace", "std" ]} 9 | ark-ff = {version = "0.4.0", default-features = false} 10 | ark-poly = {version = "0.4.0", default-features = false} 11 | ark-ec = {version = "0.4.0", default-features = false} 12 | ark-serialize = { version = "0.4.0", default-features = false, features = [ "derive" ] } 13 | 14 | # ARK curves 15 | ark-bls12-377 = {version = "0.4.0", default-features = false, features = ["curve"] } 16 | 17 | # PSS and MPC Libraries 18 | secret-sharing = { version = "0.1.0", path = "../secret-sharing" } 19 | mpc-net = { version = "0.1.0", path = "../mpc-net" } 20 | dist-primitives = {version = "0.1.0", path = "../dist-primitives" } 21 | 22 | # Other Libraries 23 | log = {version = "0.4"} 24 | rand = { version = "0.8", default-features = false, features = ["std"] } 25 | digest = { version = "0.10" } 26 | sha2 = "0.10" 27 | structopt = "0.3" 28 | env_logger = "0.8" -------------------------------------------------------------------------------- /plonk/README.md: -------------------------------------------------------------------------------- 1 | This is an incomplete implementation of the plonk proof system only used to estimate the prover time after the witness has been secret shared to the distributed provers. -------------------------------------------------------------------------------- /plonk/examples/dpoly_commit_test.rs: -------------------------------------------------------------------------------- 1 | use ark_bls12_377::Bls12_377; 2 | use ark_bls12_377::Fr; 3 | use ark_ec::pairing::Pairing; 4 | use ark_poly::{EvaluationDomain, Radix2EvaluationDomain}; 5 | use ark_std::{end_timer, start_timer}; 6 | use dist_primitives::Opt; 7 | use mpc_net::{MpcMultiNet as Net, MpcNet}; 8 | use plonk::dpoly_commit::PackPolyCk; 9 | use plonk::poly_commit::PolyCk; 10 | use secret_sharing::pss::PackedSharingParams; 11 | use structopt::StructOpt; 12 | 13 | pub fn d_poly_commit_test( 14 | pp: &PackedSharingParams, 15 | dom: &Radix2EvaluationDomain, 16 | ) { 17 | let mbyl: usize = dom.size() / pp.l; 18 | println!("m: {}, mbyl: {}", dom.size(), mbyl); 19 | 20 | let rng = &mut ark_std::test_rng(); 21 | 22 | let pck = PackPolyCk::::new(dom.size(), rng, pp); 23 | let peval_share: Vec = 24 | (0..mbyl).map(|i| E::ScalarField::from(i as u32)).collect(); 25 | 26 | let dmsm = start_timer!(|| "Distributed poly_commit"); 27 | pck.commit(&peval_share, pp); 28 | end_timer!(dmsm); 29 | 30 | let dmsm = start_timer!(|| "Distributed commit_open"); 31 | pck.open(&peval_share, E::ScalarField::from(123 as u32), dom, pp); 32 | end_timer!(dmsm); 33 | 34 | if Net::am_king() { 35 | let ck = PolyCk::::new(dom.size(), rng); 36 | let pevals: Vec = (0..dom.size()) 37 | .map(|i| E::ScalarField::from(i as u32)) 38 | .collect(); 39 | let nmsm = start_timer!(|| "Ark poly_commit"); 40 | ck.commit(&pevals); 41 | end_timer!(nmsm); 42 | let nmsm = start_timer!(|| "Ark commit_open"); 43 | ck.open(&pevals, E::ScalarField::from(123 as u32), dom); 44 | end_timer!(nmsm); 45 | } 46 | } 47 | 48 | fn main() { 49 | env_logger::builder().format_timestamp(None).init(); 50 | 51 | let opt = Opt::from_args(); 52 | 53 | Net::init_from_file(opt.input.to_str().unwrap(), opt.id); 54 | 55 | let pp = PackedSharingParams::::new(opt.l); 56 | let dom = Radix2EvaluationDomain::::new(opt.m).unwrap(); 57 | d_poly_commit_test::(&pp, &dom); 58 | 59 | Net::deinit(); 60 | } 61 | -------------------------------------------------------------------------------- /plonk/examples/local_plonk_test.rs: -------------------------------------------------------------------------------- 1 | use ark_ec::{bls12::Bls12, pairing::Pairing}; 2 | use plonk::{localplonk::localplonk, PlonkDomain}; 3 | 4 | use ark_bls12_377; 5 | use structopt::StructOpt; 6 | type BlsE = Bls12; 7 | type BlsFr = as Pairing>::ScalarField; 8 | 9 | #[derive(Debug, Clone, StructOpt)] 10 | #[structopt(name = "example", about = "An example of StructOpt usage.")] 11 | struct Opt { 12 | /// size 13 | pub m: usize, 14 | } 15 | 16 | fn main() { 17 | env_logger::builder().format_timestamp(None).init(); 18 | let opt = Opt::from_args(); 19 | let cd = PlonkDomain::::new(opt.m); 20 | localplonk::(&cd); 21 | } 22 | -------------------------------------------------------------------------------- /plonk/examples/plonk_bench.rs: -------------------------------------------------------------------------------- 1 | use ark_ec::{bls12::Bls12, pairing::Pairing}; 2 | use dist_primitives::Opt; 3 | use log::debug; 4 | use mpc_net::{MpcMultiNet as Net, MpcNet}; 5 | use plonk::{dplonk::d_plonk_test, PlonkDomain}; 6 | 7 | use ark_bls12_377; 8 | use secret_sharing::pss::PackedSharingParams; 9 | use structopt::StructOpt; 10 | type BlsE = Bls12; 11 | type BlsFr = as Pairing>::ScalarField; 12 | 13 | fn main() { 14 | debug!("Start"); 15 | 16 | env_logger::builder().format_timestamp(None).init(); 17 | let opt = Opt::from_args(); 18 | Net::init_from_file(opt.input.to_str().unwrap(), opt.id); 19 | 20 | let pd = PlonkDomain::::new(opt.m); 21 | let pp = PackedSharingParams::::new(opt.l); 22 | d_plonk_test::(&pd, &pp); 23 | 24 | if Net::am_king() { 25 | println!("Stats: {:#?}", Net::stats()); 26 | } 27 | 28 | Net::deinit(); 29 | debug!("Done"); 30 | } 31 | -------------------------------------------------------------------------------- /plonk/src/dplonk.rs: -------------------------------------------------------------------------------- 1 | use crate::{dpoly_commit::PackPolyCk, PlonkDomain}; 2 | use ark_ec::pairing::Pairing; 3 | use ark_ff::{Field, UniformRand}; 4 | use ark_poly::EvaluationDomain; 5 | use ark_std::{end_timer, start_timer, One, Zero}; 6 | use dist_primitives::{ 7 | dfft::dfft::{d_fft, d_ifft}, 8 | dpp::dpp::d_pp, 9 | utils::deg_red::deg_red, 10 | }; 11 | use mpc_net::{MpcMultiNet as Net, MpcNet}; 12 | use rand::Rng; 13 | use secret_sharing::pss::PackedSharingParams; 14 | 15 | #[derive(Clone, Debug, Default, PartialEq)] 16 | pub struct PackProvingKey { 17 | pub ql: Vec, 18 | pub qr: Vec, 19 | pub qm: Vec, 20 | pub qo: Vec, 21 | pub qc: Vec, 22 | pub s1: Vec, 23 | pub s2: Vec, 24 | pub s3: Vec, 25 | } 26 | 27 | impl PackProvingKey { 28 | pub fn new( 29 | n_gates: usize, 30 | rng: &mut R, 31 | pp: &PackedSharingParams, 32 | ) -> Self { 33 | let outer_time = start_timer!(|| "Dummy CRS"); 34 | 35 | let mut qm: Vec = vec![E::ScalarField::rand(rng); 8 * n_gates / pp.l]; 36 | let mut ql: Vec = qm.clone(); 37 | let mut qr: Vec = qm.clone(); 38 | let mut qo: Vec = qm.clone(); 39 | let mut qc: Vec = qm.clone(); 40 | let mut s1: Vec = qm.clone(); 41 | let mut s2: Vec = qm.clone(); 42 | let mut s3: Vec = qm.clone(); 43 | 44 | for i in 0..qm.len() { 45 | qm[i] = E::ScalarField::rand(rng); 46 | ql[i] = E::ScalarField::rand(rng); 47 | qr[i] = E::ScalarField::rand(rng); 48 | qo[i] = E::ScalarField::rand(rng); 49 | qc[i] = E::ScalarField::rand(rng); 50 | s1[i] = E::ScalarField::rand(rng); 51 | s2[i] = E::ScalarField::rand(rng); 52 | s3[i] = E::ScalarField::rand(rng); 53 | } 54 | 55 | end_timer!(outer_time); 56 | 57 | PackProvingKey { 58 | qm, 59 | ql, 60 | qr, 61 | qo, 62 | qc, 63 | s1, 64 | s2, 65 | s3, 66 | } 67 | } 68 | } 69 | 70 | pub fn d_plonk_test( 71 | pd: &PlonkDomain, 72 | pp: &PackedSharingParams, 73 | ) { 74 | let mbyl = pd.n_gates / pp.l; 75 | if Net::am_king() { 76 | println!("mbyl: {}", mbyl); 77 | } 78 | // Generate CRS =========================================== 79 | if Net::am_king() { 80 | println!("Generating CRS==============================="); 81 | } 82 | let rng = &mut ark_std::test_rng(); 83 | let pk = PackProvingKey::::new(pd.n_gates, rng, pp); 84 | 85 | let ck: PackPolyCk = PackPolyCk::::new(pd.n_gates, rng, pp); 86 | let ck8: PackPolyCk = PackPolyCk::::new(8 * pd.n_gates, rng, pp); 87 | 88 | let prover_timer = start_timer!(|| "Prover"); 89 | if Net::am_king() { 90 | println!("Round 1==============================="); 91 | } 92 | // Round 1 ================================================ 93 | // Commit to a, b, c 94 | 95 | let mut aevals = vec![E::ScalarField::rand(rng); mbyl]; 96 | let mut bevals = aevals.clone(); 97 | let mut cevals = aevals.clone(); 98 | for i in 0..aevals.len() { 99 | aevals[i] = E::ScalarField::rand(rng); 100 | bevals[i] = E::ScalarField::rand(rng); 101 | cevals[i] = E::ScalarField::rand(rng); 102 | } 103 | 104 | println!("Committing to a, b, c"); 105 | ck.commit(&aevals, pp); 106 | ck.commit(&bevals, pp); 107 | ck.commit(&cevals, pp); 108 | println!("======================="); 109 | 110 | println!("Extending domain of a,b,c to 8n"); 111 | // do ifft and fft to get evals of a,b,c on the 8n domain 112 | let aevals8 = d_ifft(aevals.clone(), true, 8, false, &pd.gates, pp); 113 | let bevals8 = d_ifft(bevals.clone(), true, 8, false, &pd.gates, pp); 114 | let cevals8 = d_ifft(cevals.clone(), true, 8, false, &pd.gates, pp); 115 | 116 | let aevals8 = d_fft(aevals8, false, 1, false, &pd.gates8, pp); 117 | let bevals8 = d_fft(bevals8, false, 1, false, &pd.gates8, pp); 118 | let cevals8 = d_fft(cevals8, false, 1, false, &pd.gates8, pp); 119 | println!("======================="); 120 | 121 | if Net::am_king() { 122 | println!("Round 2==============================="); 123 | } 124 | // Round 2 ================================================ 125 | // Compute z 126 | let beta = E::ScalarField::rand(rng); 127 | let gamma = E::ScalarField::rand(rng); 128 | 129 | let omega = pd.gates8.element(1); 130 | let mut omegai = E::ScalarField::one(); 131 | 132 | let mut num = vec![E::ScalarField::one(); mbyl]; 133 | let mut den = vec![E::ScalarField::one(); mbyl]; 134 | 135 | let ldpp_timer = start_timer!(|| "Local DPP"); 136 | for i in 0..mbyl { 137 | // (w_j+σ∗(j)β+γ)(w_{n+j}+σ∗(n+j)β+γ)(w_{2n+j}+σ∗(2n+j)β+γ) 138 | den[i] = (aevals[i] + beta * pk.s1[i] + gamma) 139 | * (bevals[i] + beta * pk.s2[i] + gamma) 140 | * (cevals[i] + beta * pk.s3[i] + gamma); 141 | 142 | // (w_j+βωj+γ)(w_{n+j}+βk1ωj+γ)(w_{2n+j}+βk2ωj+γ) 143 | num[i] = (aevals[i] + beta * omegai + gamma) 144 | * (bevals[i] + beta * omegai + gamma) 145 | * (cevals[i] + beta * omegai + gamma); 146 | 147 | omegai *= omega; 148 | } 149 | end_timer!(ldpp_timer); 150 | // todo: benchmark this 151 | // partial products 152 | let zevals = d_pp(num, den, pp); 153 | 154 | // extend to zevals8 155 | let zevals8 = zevals.clone(); 156 | let zevals8 = d_ifft(zevals8, true, 8, false, &pd.gates, pp); 157 | let zevals8 = d_fft(zevals8, false, 1, false, &pd.gates8, pp); 158 | 159 | if Net::am_king() { 160 | println!("Round 3==============================="); 161 | } 162 | // Round 3 ================================================ 163 | // Compute t 164 | let alpha = E::ScalarField::rand(rng); 165 | 166 | let mut tevals8 = vec![E::ScalarField::rand(rng); 8 * mbyl]; 167 | 168 | let omega = pd.gates8.element(1); 169 | let omegan = pd.gates8.element(1).pow(&([pd.n_gates as u64])); 170 | let womegan = (pd.gates8.offset * pd.gates8.element(1)).pow(&([pd.n_gates as u64])); 171 | 172 | let mut omegai = E::ScalarField::one(); 173 | let mut omegani = E::ScalarField::one(); 174 | let mut womengani = E::ScalarField::one(); 175 | 176 | let t_timer = start_timer!(|| "Compute t"); 177 | for i in 0..8 * mbyl { 178 | // ((a(X)b(X)qM(X) + a(X)qL(X) + b(X)qR(X) + c(X)qO(X) + PI(X) + qC(X)) 179 | tevals8[i] += aevals8[i] * bevals8[i] * pk.qm[i] 180 | + aevals8[i] * pk.ql[i] 181 | + bevals8[i] * pk.qr[i] 182 | + cevals8[i] * pk.qo[i] 183 | + pk.qc[i]; 184 | 185 | // ((a(X) + βX + γ)(b(X) + βk1X + γ)(c(X) + βk2X + γ)z(X))*alpha 186 | tevals8[i] += (aevals8[i] + beta * omegai + gamma) 187 | * (bevals8[i] + beta * omegai + gamma) 188 | * (cevals8[i] + beta * omegai + gamma) 189 | * (omegani - E::ScalarField::one()) 190 | * alpha; 191 | 192 | // - ((a(X) + βSσ1(X) + γ)(b(X) + βSσ2(X) + γ)(c(X) + βSσ3(X) + γ)z(Xω))*alpha 193 | tevals8[i] -= (aevals8[i] + beta * pk.s1[i] + gamma) 194 | * (bevals8[i] + beta * pk.s2[i] + gamma) 195 | * (cevals8[i] + beta * pk.s3[i] + gamma) 196 | * (womengani - E::ScalarField::one()) 197 | * alpha; 198 | 199 | // + (z(X)−1)L1(X)*alpha^2)/Z 200 | // z(X) is computed using partial products 201 | tevals8[i] += (zevals8[i]-E::ScalarField::one()) 202 | *E::ScalarField::one() //todo:replace with L1 203 | *alpha*alpha; 204 | 205 | omegai *= omega; 206 | omegani *= omegan; 207 | womengani *= womegan; 208 | } 209 | end_timer!(t_timer); 210 | 211 | // divide by ZH 212 | let tcoeffs = d_ifft(tevals8, true, 1, false, &pd.gates8, pp); 213 | let mut tevals8 = d_fft(tcoeffs, false, 1, false, &pd.gates8, pp); //king actually needs to truncate 214 | 215 | let toep_mat = E::ScalarField::from(123 as u32); // packed shares of toeplitz matrix drop from sky 216 | tevals8.iter_mut().for_each(|x| *x *= toep_mat); 217 | 218 | let tevals8 = deg_red(tevals8, pp); 219 | 220 | if Net::am_king() { 221 | println!("Round 4==============================="); 222 | } 223 | // Round 4 ================================================ 224 | // commit to z and t 225 | // open a, b, c, s1, s2, s3, z, t 226 | // commit and open r = (open_a.open_b)qm + (open_a)ql + (open_b)qr + (open_c)qo + qc 227 | 228 | println!("Committing to z, t"); 229 | ck.commit(&zevals, pp); 230 | ck8.commit(&tevals8, pp); 231 | 232 | println!("Opening a, b, c"); 233 | let point = E::ScalarField::rand(rng); 234 | let open_a = ck.open(&aevals, point, &pd.gates, pp); 235 | let open_b = ck.open(&bevals, point, &pd.gates, pp); 236 | let open_c = ck.open(&cevals, point, &pd.gates, pp); 237 | 238 | println!("Opening s1, s2, s3"); 239 | // extract every 8th element of pk.s1 using iterators 240 | ck.open( 241 | &pk.s1.iter().step_by(8).copied().collect(), 242 | point, 243 | &pd.gates, 244 | pp, 245 | ); 246 | ck.open( 247 | &pk.s2.iter().step_by(8).copied().collect(), 248 | point, 249 | &pd.gates, 250 | pp, 251 | ); 252 | ck.open( 253 | &pk.s3.iter().step_by(8).copied().collect(), 254 | point, 255 | &pd.gates, 256 | pp, 257 | ); 258 | 259 | println!("Computing r"); 260 | let r_timer = start_timer!(|| "Compute r"); 261 | let open_ab = open_a * open_b; 262 | let mut revals = vec![E::ScalarField::zero(); mbyl]; 263 | for i in 0..mbyl { 264 | revals[i] = open_ab * pk.qm[i] 265 | + open_a * pk.ql[i] 266 | + open_b * pk.qr[i] 267 | + open_c * pk.qo[i] 268 | + pk.qc[i]; 269 | } 270 | end_timer!(r_timer); 271 | 272 | println!("Committing to r"); 273 | ck.commit(&revals, pp); 274 | ck.open(&revals, point, &pd.gates, pp); 275 | 276 | end_timer!(prover_timer); 277 | } 278 | -------------------------------------------------------------------------------- /plonk/src/dpoly_commit.rs: -------------------------------------------------------------------------------- 1 | use ark_ec::pairing::Pairing; 2 | use ark_ff::UniformRand; 3 | use ark_poly::{EvaluationDomain, Radix2EvaluationDomain}; 4 | use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; 5 | use ark_std::{end_timer, start_timer}; 6 | use dist_primitives::dfft::dfft::{d_fft, d_ifft}; 7 | use dist_primitives::dmsm::dmsm::d_msm; 8 | use dist_primitives::utils::deg_red::deg_red; 9 | use rand::Rng; 10 | use secret_sharing::pss::PackedSharingParams; 11 | 12 | #[derive(Clone, Debug, Default, PartialEq, CanonicalSerialize, CanonicalDeserialize)] 13 | pub struct PackPolyCk { 14 | pub powers_of_tau: Vec, //We assume that we have eval version 15 | } 16 | 17 | impl PackPolyCk { 18 | #[allow(unused)] 19 | pub fn new( 20 | domain_size: usize, 21 | rng: &mut R, 22 | pp: &PackedSharingParams, 23 | ) -> Self { 24 | // using dummy to speedup testing 25 | let mut powers_of_tau: Vec = vec![E::G1Affine::rand(rng); domain_size / pp.l]; 26 | for i in 0..(domain_size / pp.l) { 27 | powers_of_tau[i] = E::G1Affine::rand(rng); 28 | } 29 | PackPolyCk:: { 30 | powers_of_tau: powers_of_tau, 31 | } 32 | } 33 | 34 | /// Interactively commits to a polynomial give packed shares of the evals 35 | #[allow(unused)] 36 | pub fn commit( 37 | &self, 38 | peval_share: &Vec, 39 | pp: &PackedSharingParams, 40 | ) { 41 | let commitment = d_msm::(&self.powers_of_tau, peval_share.as_slice(), pp); 42 | // actually getting back shares but king can publish the commitment 43 | } 44 | 45 | /// Interactively creates an opening to a polynomial at a chosen point 46 | #[allow(unused)] 47 | pub fn open( 48 | &self, 49 | peval_share: &Vec, 50 | point: E::ScalarField, 51 | dom: &Radix2EvaluationDomain, 52 | pp: &PackedSharingParams, 53 | ) -> E::ScalarField { 54 | debug_assert_eq!( 55 | peval_share.len() * pp.l, 56 | dom.size(), 57 | "pevals length is not equal to m/l" 58 | ); 59 | // Interpolate pevals to get coeffs 60 | let pcoeff_share = d_ifft(peval_share.clone(), false, 1, false, dom, pp); 61 | 62 | // distributed poly evaluation 63 | let powers_of_r_share = E::ScalarField::from(123 as u32); // packed shares of r drop from sky 64 | let point_eval_share = pcoeff_share 65 | .iter() 66 | .map(|&a| a * powers_of_r_share) 67 | .sum::(); 68 | 69 | // do degree reduction and King publishes answer 70 | let point_eval_share = deg_red(vec![point_eval_share], pp)[0]; 71 | 72 | // Compute the quotient polynomial 73 | // During iFFT king sends over the "truncated pcoeff_shares". Do FFT on this 74 | 75 | let ptrunc_evals = d_fft(pcoeff_share, false, 1, false, dom, pp); 76 | let toep_mat_share = E::ScalarField::from(123 as u32); // packed shares of toeplitz matrix drop from sky 77 | let timer_div = start_timer!(|| "Division"); 78 | let q_evals = ptrunc_evals 79 | .into_iter() 80 | .map(|a| a * toep_mat_share) 81 | .collect::>(); 82 | end_timer!(timer_div); 83 | 84 | // don't have to do degree reduction since it's a secret value multiplied by two public values 85 | // we could pack two public values together but that would mean two msms instead of one 86 | 87 | // Compute the proof pi 88 | let pi: E::G1 = d_msm(&self.powers_of_tau, &q_evals, pp); 89 | 90 | point_eval_share 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /plonk/src/lib.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::FftField; 2 | use ark_poly::{EvaluationDomain, Radix2EvaluationDomain}; 3 | 4 | pub mod dplonk; 5 | pub mod dpoly_commit; 6 | pub mod localplonk; 7 | pub mod poly_commit; 8 | 9 | #[derive(Debug, Clone, PartialEq)] 10 | pub struct PlonkDomain 11 | where 12 | F: FftField, 13 | { 14 | pub n_gates: usize, 15 | pub gates: Radix2EvaluationDomain, 16 | pub gates8: Radix2EvaluationDomain, 17 | } 18 | 19 | impl PlonkDomain { 20 | #[allow(unused)] 21 | pub fn new(n_gates: usize) -> Self { 22 | let gates = Radix2EvaluationDomain::::new(n_gates).unwrap(); 23 | let gates8 = Radix2EvaluationDomain::::new(8 * n_gates).unwrap(); 24 | 25 | debug_assert_eq!(gates.size(), n_gates); 26 | debug_assert_eq!(gates8.size(), 8 * n_gates); 27 | 28 | PlonkDomain { 29 | n_gates, 30 | gates, 31 | gates8, 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /plonk/src/localplonk.rs: -------------------------------------------------------------------------------- 1 | use crate::{poly_commit::PolyCk, PlonkDomain}; 2 | use ark_ec::pairing::Pairing; 3 | use ark_ff::{Field, UniformRand}; 4 | use ark_poly::EvaluationDomain; 5 | use ark_std::{end_timer, start_timer, One, Zero}; 6 | use rand::Rng; 7 | 8 | #[derive(Clone, Debug, Default, PartialEq)] 9 | struct ProvingKey { 10 | pub ql: Vec, 11 | pub qr: Vec, 12 | pub qm: Vec, 13 | pub qo: Vec, 14 | pub qc: Vec, 15 | pub s1: Vec, 16 | pub s2: Vec, 17 | pub s3: Vec, 18 | } 19 | 20 | impl ProvingKey { 21 | fn new(n_gates: usize, rng: &mut R) -> Self { 22 | let outer_time = start_timer!(|| "Dummy CRS"); 23 | 24 | let mut qm: Vec = vec![E::ScalarField::rand(rng); 8 * n_gates]; 25 | let mut ql: Vec = qm.clone(); 26 | let mut qr: Vec = qm.clone(); 27 | let mut qo: Vec = qm.clone(); 28 | let mut qc: Vec = qm.clone(); 29 | let mut s1: Vec = qm.clone(); 30 | let mut s2: Vec = qm.clone(); 31 | let mut s3: Vec = qm.clone(); 32 | 33 | for i in 0..qm.len() { 34 | qm[i] = E::ScalarField::rand(rng); 35 | ql[i] = E::ScalarField::rand(rng); 36 | qr[i] = E::ScalarField::rand(rng); 37 | qo[i] = E::ScalarField::rand(rng); 38 | qc[i] = E::ScalarField::rand(rng); 39 | s1[i] = E::ScalarField::rand(rng); 40 | s2[i] = E::ScalarField::rand(rng); 41 | s3[i] = E::ScalarField::rand(rng); 42 | } 43 | 44 | end_timer!(outer_time); 45 | 46 | ProvingKey { 47 | qm, 48 | ql, 49 | qr, 50 | qo, 51 | qc, 52 | s1, 53 | s2, 54 | s3, 55 | } 56 | } 57 | } 58 | 59 | pub fn localplonk(pd: &PlonkDomain) { 60 | // Generate CRS =========================================== 61 | let rng = &mut ark_std::test_rng(); 62 | let pk = ProvingKey::::new(pd.n_gates, rng); 63 | let ck: PolyCk = PolyCk::::new(pd.n_gates, rng); 64 | let ck8: PolyCk = PolyCk::::new(8 * pd.n_gates, rng); 65 | 66 | let prover_timer = start_timer!(|| "Prover"); 67 | println!("Round 1==============================="); 68 | // Round 1 ================================================ 69 | // Commit to a, b, c 70 | let mut aevals = vec![E::ScalarField::rand(rng); pd.n_gates]; 71 | let mut bevals = aevals.clone(); 72 | let mut cevals = aevals.clone(); 73 | for i in 0..aevals.len() { 74 | aevals[i] = E::ScalarField::rand(rng); 75 | bevals[i] = E::ScalarField::rand(rng); 76 | cevals[i] = E::ScalarField::rand(rng); 77 | } 78 | 79 | println!("Committing to a, b, c"); 80 | ck.commit(&aevals); 81 | println!("aveals: {}", aevals.len()); 82 | ck.commit(&bevals); 83 | ck.commit(&cevals); 84 | println!("======================="); 85 | 86 | println!("Extending domain of a,b,c to 8n"); 87 | // do ifft and fft to get evals of a,b,c on the 8n domain 88 | let mut aevals8 = aevals.clone(); 89 | let mut bevals8 = bevals.clone(); 90 | let mut cevals8 = cevals.clone(); 91 | 92 | let fft_timer = start_timer!(|| "FFT"); 93 | pd.gates.ifft_in_place(&mut aevals8); 94 | pd.gates.ifft_in_place(&mut bevals8); 95 | pd.gates.ifft_in_place(&mut cevals8); 96 | 97 | pd.gates8.fft_in_place(&mut aevals8); 98 | pd.gates8.fft_in_place(&mut bevals8); 99 | pd.gates8.fft_in_place(&mut cevals8); 100 | end_timer!(fft_timer); 101 | 102 | println!("======================="); 103 | 104 | println!("Round 2==============================="); 105 | // Round 2 ================================================ 106 | // Compute z 107 | let beta = E::ScalarField::rand(rng); 108 | let gamma = E::ScalarField::rand(rng); 109 | 110 | let mut zevals = vec![E::ScalarField::zero(); pd.n_gates]; 111 | 112 | let omega = pd.gates8.element(1); 113 | let mut omegai = E::ScalarField::one(); 114 | 115 | let pp_timer = start_timer!(|| "PP"); 116 | for i in 0..pd.n_gates { 117 | // (w_j+σ∗(j)β+γ)(w_{n+j}+σ∗(n+j)β+γ)(w_{2n+j}+σ∗(2n+j)β+γ) 118 | let den = (aevals[i] + beta * pk.s1[i] + gamma) 119 | * (bevals[i] + beta * pk.s2[i] + gamma) 120 | * (cevals[i] + beta * pk.s3[i] + gamma); 121 | let den = den.inverse().unwrap(); 122 | 123 | // (w_j+βωj+γ)(w_{n+j}+βk1ωj+γ)(w_{2n+j}+βk2ωj+γ) 124 | zevals[i] = (aevals[i] + beta * omegai + gamma) 125 | * (bevals[i] + beta * omegai + gamma) 126 | * (cevals[i] + beta * omegai + gamma) 127 | * den; 128 | omegai *= omega; 129 | } 130 | 131 | // partial products 132 | for i in 1..pd.n_gates { 133 | let last = zevals[i - 1]; 134 | zevals[i] *= last; 135 | } 136 | end_timer!(pp_timer); 137 | 138 | // extend to zevals8 139 | let fft_timer = start_timer!(|| "FFT"); 140 | let mut zevals8 = zevals.clone(); 141 | pd.gates.ifft_in_place(&mut zevals8); 142 | pd.gates8.fft_in_place(&mut zevals8); 143 | end_timer!(fft_timer); 144 | 145 | println!("Round 3==============================="); 146 | // Round 3 ================================================ 147 | // Compute t 148 | let alpha = E::ScalarField::rand(rng); 149 | 150 | let mut tevals8 = vec![E::ScalarField::zero(); pd.gates8.size()]; 151 | 152 | let omega = pd.gates8.element(1); 153 | let omegan = pd.gates8.element(1).pow(&([pd.n_gates as u64])); 154 | let womegan = (pd.gates8.offset * pd.gates8.element(1)).pow(&([pd.n_gates as u64])); 155 | 156 | let mut omegai = E::ScalarField::one(); 157 | let mut omegani = E::ScalarField::one(); 158 | let mut womengani = E::ScalarField::one(); 159 | 160 | let t_timer = start_timer!(|| "Compute t"); 161 | for i in 0..tevals8.len() { 162 | // ((a(X)b(X)qM(X) + a(X)qL(X) + b(X)qR(X) + c(X)qO(X) + PI(X) + qC(X)) 163 | tevals8[i] += aevals8[i] * bevals8[i] * pk.qm[i] 164 | + aevals8[i] * pk.ql[i] 165 | + bevals8[i] * pk.qr[i] 166 | + cevals8[i] * pk.qo[i] 167 | + pk.qc[i]; 168 | 169 | // ((a(X) + βX + γ)(b(X) + βk1X + γ)(c(X) + βk2X + γ)z(X))*alpha 170 | tevals8[i] += (aevals8[i] + beta * omegai + gamma) 171 | * (bevals8[i] + beta * omegai + gamma) 172 | * (cevals8[i] + beta * omegai + gamma) 173 | * (omegani - E::ScalarField::one()) 174 | * alpha; 175 | 176 | // - ((a(X) + βSσ1(X) + γ)(b(X) + βSσ2(X) + γ)(c(X) + βSσ3(X) + γ)z(Xω))*alpha 177 | tevals8[i] -= (aevals8[i] + beta * pk.s1[i] + gamma) 178 | * (bevals8[i] + beta * pk.s2[i] + gamma) 179 | * (cevals8[i] + beta * pk.s3[i] + gamma) 180 | * (womengani - E::ScalarField::one()) 181 | * alpha; 182 | 183 | // + (z(X)−1)L1(X)*alpha^2)/Z 184 | // z(X) is computed using partial products 185 | tevals8[i] += (zevals8[i]-E::ScalarField::one()) 186 | *E::ScalarField::one() //todo:replace with L1 187 | *alpha*alpha; 188 | 189 | omegai *= omega; 190 | omegani *= omegan; 191 | womengani *= womegan; 192 | } 193 | end_timer!(t_timer); 194 | 195 | // divide by ZH 196 | let fft_timer = start_timer!(|| "FFT"); 197 | let tcoeffs = pd.gates8.ifft(&tevals8); 198 | let mut tevals8 = pd.gates8.fft(&tcoeffs[0..7 * pd.n_gates]); 199 | let toep_mat = E::ScalarField::from(123 as u32); // packed shares of toeplitz matrix drop from sky 200 | end_timer!(fft_timer); 201 | 202 | tevals8.iter_mut().for_each(|x| *x *= toep_mat); 203 | 204 | println!("Round 4==============================="); 205 | // Round 4 ================================================ 206 | // commit to z and t 207 | // open a, b, c, s1, s2, s3, z, t 208 | // commit and open r = (open_a.open_b)qm + (open_a)ql + (open_b)qr + (open_c)qo + qc 209 | 210 | ck.commit(&zevals); 211 | ck8.commit(&tevals8); 212 | 213 | let point = E::ScalarField::rand(rng); 214 | let open_a = ck.open(&aevals, point, &pd.gates); 215 | let open_b = ck.open(&bevals, point, &pd.gates); 216 | let open_c = ck.open(&cevals, point, &pd.gates); 217 | 218 | // extract every 8th element of pk.s1 using iterators 219 | ck.open( 220 | &pk.s1.iter().step_by(8).copied().collect(), 221 | point, 222 | &pd.gates, 223 | ); 224 | ck.open( 225 | &pk.s2.iter().step_by(8).copied().collect(), 226 | point, 227 | &pd.gates, 228 | ); 229 | ck.open( 230 | &pk.s3.iter().step_by(8).copied().collect(), 231 | point, 232 | &pd.gates, 233 | ); 234 | 235 | let open_ab = open_a * open_b; 236 | let mut revals = vec![E::ScalarField::zero(); pd.n_gates]; 237 | let timer_r = start_timer!(|| "Compute r"); 238 | for i in 0..pd.n_gates { 239 | revals[i] = open_ab * pk.qm[i] 240 | + open_a * pk.ql[i] 241 | + open_b * pk.qr[i] 242 | + open_c * pk.qo[i] 243 | + pk.qc[i]; 244 | } 245 | end_timer!(timer_r); 246 | 247 | ck.commit(&revals); 248 | ck.open(&revals, point, &pd.gates); 249 | 250 | end_timer!(prover_timer); 251 | } 252 | -------------------------------------------------------------------------------- /plonk/src/poly_commit.rs: -------------------------------------------------------------------------------- 1 | use ark_ec::pairing::Pairing; 2 | use ark_ec::VariableBaseMSM; 3 | use ark_ff::UniformRand; 4 | use ark_poly::univariate::DenseOrSparsePolynomial; 5 | use ark_poly::{polynomial::univariate::DensePolynomial, EvaluationDomain}; 6 | use ark_poly::{DenseUVPolynomial, Polynomial, Radix2EvaluationDomain}; 7 | use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; 8 | use ark_std::{end_timer, start_timer, One}; 9 | use rand::Rng; 10 | 11 | #[derive(Clone, Debug, Default, PartialEq, CanonicalSerialize, CanonicalDeserialize)] 12 | pub struct PolyCk { 13 | pub powers_of_tau: Vec, //We assume that we have eval version 14 | } 15 | 16 | impl PolyCk { 17 | #[allow(unused)] 18 | pub fn new(domain_size: usize, rng: &mut R) -> Self { 19 | // using dummy to speedup testing 20 | let mut powers_of_tau: Vec = vec![E::G1Affine::rand(rng); domain_size]; 21 | for i in 0..domain_size { 22 | powers_of_tau[i] = E::G1Affine::rand(rng); 23 | } 24 | PolyCk:: { 25 | powers_of_tau: powers_of_tau, 26 | } 27 | } 28 | 29 | /// Commits to a polynomial give the evals 30 | #[allow(unused)] 31 | pub fn commit(&self, pevals: &Vec) { 32 | let msm_time = start_timer!(|| "PolyCom MSM"); 33 | let commitment = E::G1::msm(&self.powers_of_tau, pevals).unwrap(); 34 | end_timer!(msm_time); 35 | } 36 | 37 | /// Creates an opening to a polynomial at a chosen point 38 | #[allow(unused)] 39 | pub fn open( 40 | &self, 41 | pevals: &Vec, 42 | point: E::ScalarField, 43 | dom: &Radix2EvaluationDomain, 44 | ) -> E::ScalarField { 45 | debug_assert_eq!(pevals.len(), dom.size(), "pevals length is not equal to m"); 46 | let open_timer = start_timer!(|| "PolyCom Open"); 47 | // Interpolate pevals to get coeffs 48 | let pcoeffs = dom.ifft(&pevals); 49 | let p = DensePolynomial::from_coefficients_vec(pcoeffs); 50 | let point_eval = p.evaluate(&point); // Evaluate pcoeffs at point 51 | 52 | // Compute the quotient polynomial 53 | let p = DenseOrSparsePolynomial::from(p); 54 | let divisor = DenseOrSparsePolynomial::from(DensePolynomial::from_coefficients_vec(vec![ 55 | -point, 56 | E::ScalarField::one(), 57 | ])); 58 | let qcoeffs = p.divide_with_q_and_r(&divisor).unwrap().1.coeffs().to_vec(); 59 | 60 | // convert to evals 61 | let qevals = dom.fft(&qcoeffs); 62 | 63 | // Compute the proof pi 64 | let pi = E::G1::msm(&self.powers_of_tau, &qevals).unwrap(); 65 | end_timer!(open_timer); 66 | 67 | point_eval 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /scripts/dfft_test.zsh: -------------------------------------------------------------------------------- 1 | set -ex 2 | trap "exit" INT TERM 3 | trap "kill 0" EXIT 4 | 5 | cargo build --example dfft_test 6 | BIN=../target/debug/examples/dfft_test 7 | 8 | # cargo build --release --example dfft_test 9 | # BIN=../target/release/examples/dfft_test 10 | 11 | l=2 12 | t=3 13 | m=8 14 | n=8 15 | 16 | for n_parties in $n 17 | do 18 | PROCS=() 19 | for i in $(seq 0 $(($n_parties - 1))) 20 | do 21 | #$BIN $i ./network-address/4 & 22 | if [ $i == 0 ] 23 | then 24 | RUST_BACKTRACE=0 RUST_LOG=fft $BIN $i ../network-address/$n_parties $l $t $m & 25 | pid=$! 26 | PROCS[$i]=$pid 27 | else 28 | RUST_LOG=fft $BIN $i ../network-address/$n_parties $l $t $m > /dev/null & 29 | pid=$! 30 | PROCS[$i]=$pid 31 | fi 32 | done 33 | 34 | for pid in ${PROCS[@]} 35 | do 36 | wait $pid || { echo "Process $pid exited with an error status"; exit 1; } 37 | done 38 | done 39 | 40 | echo done 41 | 42 | -------------------------------------------------------------------------------- /scripts/dmsm_bench.zsh: -------------------------------------------------------------------------------- 1 | set -ex 2 | trap "exit" INT TERM 3 | trap "kill 0" EXIT 4 | 5 | # cargo build --example dmsm_bench 6 | # BIN=../target/debug/examples/dmsm_bench 7 | 8 | cargo build --release --example dmsm_bench 9 | BIN=../target/release/examples/dmsm_bench 10 | 11 | l=2 12 | t=3 13 | m=32768 14 | n=8 15 | 16 | for n_parties in $n 17 | do 18 | PROCS=() 19 | for i in $(seq 0 $(($n_parties - 1))) 20 | do 21 | #$BIN $i ./network-address/4 & 22 | if [ $i == 0 ] 23 | then 24 | RUST_BACKTRACE=0 RUST_LOG=msm $BIN $i ../network-address/$n_parties $l $t $m & 25 | pid=$! 26 | PROCS[$i]=$pid 27 | else 28 | RUST_LOG=msm $BIN $i ../network-address/$n_parties $l $t $m > /dev/null & 29 | pid=$! 30 | PROCS[$i]=$pid 31 | fi 32 | done 33 | 34 | for pid in ${PROCS[@]} 35 | do 36 | wait $pid 37 | done 38 | done 39 | 40 | echo done 41 | 42 | -------------------------------------------------------------------------------- /scripts/dmsm_test.zsh: -------------------------------------------------------------------------------- 1 | set -ex 2 | trap "exit" INT TERM 3 | trap "kill 0" EXIT 4 | 5 | # cargo build --example dmsm_test 6 | # BIN=../target/debug/examples/dmsm_test 7 | 8 | cargo build --release --example dmsm_test 9 | BIN=../target/release/examples/dmsm_test 10 | 11 | l=2 12 | t=3 13 | m=256 14 | n=8 15 | 16 | for n_parties in $n 17 | do 18 | PROCS=() 19 | for i in $(seq 0 $(($n_parties - 1))) 20 | do 21 | #$BIN $i ./network-address/4 & 22 | if [ $i == 0 ] 23 | then 24 | RUST_BACKTRACE=0 RUST_LOG=msm $BIN $i ../network-address/$n_parties $l $t $m & 25 | pid=$! 26 | PROCS[$i]=$pid 27 | else 28 | RUST_LOG=msm $BIN $i ../network-address/$n_parties $l $t $m > /dev/null & 29 | pid=$! 30 | PROCS[$i]=$pid 31 | fi 32 | done 33 | 34 | for pid in ${PROCS[@]} 35 | do 36 | wait $pid || { echo "Process $pid exited with an error status"; exit 1; } 37 | done 38 | done 39 | 40 | echo done 41 | 42 | -------------------------------------------------------------------------------- /scripts/dpoly_commit_test.zsh: -------------------------------------------------------------------------------- 1 | set -ex 2 | trap "exit" INT TERM 3 | trap "kill 0" EXIT 4 | 5 | file=dpoly_commit_test 6 | cargo build --example $file 7 | BIN=../target/debug/examples/$file 8 | 9 | # cargo build --release --example $file 10 | # BIN=../target/release/examples/$file 11 | 12 | l=2 13 | t=3 14 | m=32768 15 | n=8 16 | 17 | for n_parties in $n 18 | do 19 | PROCS=() 20 | for i in $(seq 0 $(($n_parties - 1))) 21 | do 22 | #$BIN $i ./network-address/4 & 23 | if [ $i == 0 ] 24 | then 25 | RUST_BACKTRACE=0 RUST_LOG=poly_commit $BIN $i ../network-address/$n_parties $l $t $m & 26 | pid=$! 27 | PROCS[$i]=$pid 28 | else 29 | RUST_LOG=poly_commit $BIN $i ../network-address/$n_parties $l $t $m > /dev/null & 30 | pid=$! 31 | PROCS[$i]=$pid 32 | fi 33 | done 34 | 35 | for pid in ${PROCS[@]} 36 | do 37 | wait $pid 38 | done 39 | done 40 | 41 | echo done 42 | 43 | -------------------------------------------------------------------------------- /scripts/dpp_test.zsh: -------------------------------------------------------------------------------- 1 | set -ex 2 | trap "exit" INT TERM 3 | trap "kill 0" EXIT 4 | 5 | cargo build --example dpp_test 6 | BIN=../target/debug/examples/dpp_test 7 | 8 | # cargo build --release --example dpp_test 9 | # BIN=../target/release/examples/dpp_test 10 | 11 | l=2 12 | t=3 13 | m=16 14 | n=8 15 | 16 | for n_parties in $n 17 | do 18 | PROCS=() 19 | for i in $(seq 0 $(($n_parties - 1))) 20 | do 21 | #$BIN $i ./network-address/4 & 22 | if [ $i == 0 ] 23 | then 24 | RUST_BACKTRACE=0 RUST_LOG=fft $BIN $i ../network-address/$n_parties $l $t $m & 25 | pid=$! 26 | PROCS[$i]=$pid 27 | else 28 | RUST_LOG=fft $BIN $i ../network-address/$n_parties $l $t $m > /dev/null & 29 | pid=$! 30 | PROCS[$i]=$pid 31 | fi 32 | done 33 | 34 | for pid in ${PROCS[@]} 35 | do 36 | wait $pid 37 | done 38 | done 39 | 40 | echo done 41 | 42 | -------------------------------------------------------------------------------- /scripts/ext_wit_test.zsh: -------------------------------------------------------------------------------- 1 | set -ex 2 | trap "exit" INT TERM 3 | trap "kill 0" EXIT 4 | 5 | cargo build --example ext_wit_bench 6 | BIN=../target/debug/examples/ext_wit_bench 7 | 8 | # cargo build --release --example ext_wit_bench 9 | # BIN=../target/release/examples/ext_wit_bench 10 | 11 | l=2 12 | t=3 13 | m=0 #not using here 14 | n=8 15 | 16 | for n_parties in $n 17 | do 18 | PROCS=() 19 | for i in $(seq 0 $(($n_parties - 1))) 20 | do 21 | #$BIN $i ./network-address/4 & 22 | if [ $i == 0 ] 23 | then 24 | RUST_BACKTRACE=0 RUST_LOG=ext_wit $BIN $i ../network-address/$n_parties $l $t $m & 25 | pid=$! 26 | PROCS[$i]=$pid 27 | else 28 | RUST_LOG=ext_with $BIN $i ../network-address/$n_parties $l $t $m > /dev/null & 29 | pid=$! 30 | PROCS[$i]=$pid 31 | fi 32 | done 33 | 34 | for pid in ${PROCS[@]} 35 | do 36 | wait $pid 37 | done 38 | done 39 | 40 | echo done 41 | 42 | -------------------------------------------------------------------------------- /scripts/groth_bench.zsh: -------------------------------------------------------------------------------- 1 | set -ex 2 | trap "exit" INT TERM 3 | trap "kill 0" EXIT 4 | 5 | # cargo build --example groth_bench 6 | # BIN=../target/debug/examples/groth_bench 7 | 8 | cargo build --release --example groth_bench 9 | BIN=../target/release/examples/groth_bench 10 | 11 | l=2 12 | t=1 13 | m=32768 14 | n=8 15 | 16 | for n_parties in $n 17 | do 18 | PROCS=() 19 | for i in $(seq 0 $(($n_parties - 1))) 20 | do 21 | #$BIN $i ./network-address/4 & 22 | if [ $i == 0 ] 23 | then 24 | RUST_BACKTRACE=0 RUST_LOG=groth_bench $BIN $i ../network-address/$n_parties $l $t $m & 25 | pid=$! 26 | PROCS[$i]=$pid 27 | else 28 | RUST_LOG=groth_bench $BIN $i ../network-address/$n_parties $l $t $m > /dev/null & 29 | pid=$! 30 | PROCS[$i]=$pid 31 | fi 32 | done 33 | 34 | for pid in ${PROCS[@]} 35 | do 36 | wait $pid 37 | done 38 | done 39 | 40 | echo done 41 | 42 | -------------------------------------------------------------------------------- /scripts/plonk_bench.zsh: -------------------------------------------------------------------------------- 1 | set -ex 2 | trap "exit" INT TERM 3 | trap "kill 0" EXIT 4 | 5 | # cargo build --example plonk_bench 6 | # BIN=../target/debug/examples/plonk_bench 7 | 8 | cargo build --release --example plonk_bench 9 | BIN=../target/release/examples/plonk_bench 10 | 11 | l=2 12 | t=1 13 | m=32768 14 | n=8 15 | 16 | for n_parties in $n 17 | do 18 | PROCS=() 19 | for i in $(seq 0 $(($n_parties - 1))) 20 | do 21 | #$BIN $i ./network-address/4 & 22 | if [ $i == 0 ] 23 | then 24 | RUST_BACKTRACE=0 RUST_LOG=plonk_bench $BIN $i ../network-address/$n_parties $l $t $m & 25 | pid=$! 26 | PROCS[$i]=$pid 27 | else 28 | RUST_LOG=plonk_bench $BIN $i ../network-address/$n_parties $l $t $m > /dev/null & 29 | pid=$! 30 | PROCS[$i]=$pid 31 | fi 32 | done 33 | 34 | for pid in ${PROCS[@]} 35 | do 36 | wait $pid 37 | done 38 | done 39 | 40 | echo done 41 | 42 | -------------------------------------------------------------------------------- /secret-sharing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "secret-sharing" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | ark-poly = {version = "0.4.0", default-features = false} 10 | ark-ff = {version = "0.4.0", default-features = false} 11 | ark-ec = {version = "0.4.0", default-features = false} 12 | ark-std = {version = "0.4.0", default-features = false} 13 | ark-bls12-377 = {version = "0.4.0", default-features = false, features = ["curve"] } -------------------------------------------------------------------------------- /secret-sharing/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod pss; 2 | -------------------------------------------------------------------------------- /secret-sharing/src/pss.rs: -------------------------------------------------------------------------------- 1 | use ark_poly::{domain::EvaluationDomain, Radix2EvaluationDomain}; 2 | 3 | use ark_ff::FftField; 4 | 5 | /// Currently the packed secret sharing is deterministic, but it can easily be extended to add random values when packing 6 | #[derive(Debug, Clone, PartialEq)] 7 | pub struct PackedSharingParams 8 | where 9 | F: FftField, 10 | { 11 | pub t: usize, // Corruption threshold 12 | pub l: usize, // Packing factor 13 | pub n: usize, // Number of parties 14 | pub share: Radix2EvaluationDomain, // Share domain 15 | pub secret: Radix2EvaluationDomain, // Secrets domain 16 | pub secret2: Radix2EvaluationDomain, // Secrets2 domain 17 | } 18 | 19 | impl PackedSharingParams { 20 | #[allow(unused)] 21 | pub fn new(l: usize) -> Self { 22 | let n = l * 4; 23 | let t = l - 1; 24 | debug_assert_eq!(n, 2 * (t + l + 1)); 25 | 26 | let share = Radix2EvaluationDomain::::new(n).unwrap(); 27 | let secret = Radix2EvaluationDomain::::new(l + t + 1) 28 | .unwrap() 29 | .get_coset(F::GENERATOR) 30 | .unwrap(); 31 | let secret2 = Radix2EvaluationDomain::::new(2 * (l + t + 1)) 32 | .unwrap() 33 | .get_coset(F::GENERATOR) 34 | .unwrap(); 35 | 36 | debug_assert_eq!(share.size(), n); 37 | debug_assert_eq!(secret.size(), l + t + 1); 38 | debug_assert_eq!(secret2.size(), 2 * (l + t + 1)); 39 | 40 | PackedSharingParams { 41 | t, 42 | l, 43 | n, 44 | share, 45 | secret, 46 | secret2, 47 | } 48 | } 49 | 50 | /// Packs secrets into shares 51 | #[allow(unused)] 52 | pub fn pack_from_public(&self, secrets: &Vec) -> Vec { 53 | let mut result = secrets.clone(); 54 | self.pack_from_public_in_place(&mut result); 55 | 56 | result 57 | } 58 | 59 | /// Packs secrets into shares in place 60 | #[allow(unused)] 61 | pub fn pack_from_public_in_place(&self, mut secrets: &mut Vec) { 62 | // interpolating on secrets domain 63 | self.secret.ifft_in_place(&mut secrets); 64 | 65 | // evaluate on share domain 66 | self.share.fft_in_place(&mut secrets); 67 | } 68 | 69 | /// Unpacks shares of degree t+l into secrets 70 | #[allow(unused)] 71 | pub fn unpack(&self, shares: &Vec) -> Vec { 72 | let mut result = shares.clone(); 73 | self.unpack_in_place(&mut result); 74 | 75 | result 76 | } 77 | 78 | /// Unpacks shares of degree 2(t+l) into secrets 79 | #[allow(unused)] 80 | pub fn unpack2(&self, shares: &Vec) -> Vec { 81 | let mut result = shares.clone(); 82 | self.unpack2_in_place(&mut result); 83 | 84 | result 85 | } 86 | 87 | /// Unpacks shares of degree t+l into secrets in place 88 | #[allow(unused)] 89 | pub fn unpack_in_place(&self, mut shares: &mut Vec) { 90 | // interpolating on share domain 91 | self.share.ifft_in_place(&mut shares); 92 | 93 | // assert that all but first t+l+1 elements are zero 94 | // #[cfg(debug_assertions)] 95 | // { 96 | // for i in self.l + self.t + 1..shares.len() { 97 | // debug_assert!(shares[i].is_zero(), "Unpack failed"); 98 | // } 99 | // } 100 | 101 | // evaluate on secrets domain 102 | self.secret.fft_in_place(&mut shares); 103 | 104 | // truncate to remove the randomness 105 | shares.truncate(self.l); 106 | } 107 | 108 | /// Unpacks shares of degree 2(t+l) into secrets in place 109 | #[allow(unused)] 110 | pub fn unpack2_in_place(&self, mut shares: &mut Vec) { 111 | // interpolating on share domain 112 | self.share.ifft_in_place(&mut shares); 113 | 114 | // assert that all but first 2(t+l)+1 elements are zero 115 | #[cfg(debug_assertions)] 116 | { 117 | for i in 2 * (self.l + self.t) + 1..shares.len() { 118 | debug_assert!(shares[i].is_zero(), "Unpack2 failed"); 119 | } 120 | } 121 | 122 | // evaluate on secrets domain 123 | self.secret2.fft_in_place(&mut shares); 124 | 125 | // drop alternate elements from shares array and only iterate till 2l as the rest of it is randomness 126 | *shares = shares[0..2 * self.l].iter().step_by(2).copied().collect(); 127 | } 128 | } 129 | 130 | // Tests 131 | #[cfg(test)] 132 | mod tests { 133 | use super::*; 134 | use ark_bls12_377::Fr as F; 135 | use ark_std::UniformRand; 136 | use PackedSharingParams; 137 | 138 | const L: usize = 4; 139 | const N: usize = L * 4; 140 | const T: usize = N / 2 - L - 1; 141 | 142 | #[test] 143 | fn test_initialize() { 144 | let pp = PackedSharingParams::::new(L); 145 | assert_eq!(pp.t, L - 1); 146 | assert_eq!(pp.l, L); 147 | assert_eq!(pp.n, N); 148 | assert_eq!(pp.share.size(), N); 149 | assert_eq!(pp.secret.size(), L + T + 1); 150 | assert_eq!(pp.secret2.size(), 2 * (L + T + 1)); 151 | } 152 | 153 | #[test] 154 | fn test_pack_from_public() { 155 | let pp = PackedSharingParams::::new(L); 156 | 157 | let rng = &mut ark_std::test_rng(); 158 | let secrets: [F; L] = UniformRand::rand(rng); 159 | let mut secrets = secrets.to_vec(); 160 | 161 | let expected = secrets.clone(); 162 | 163 | pp.pack_from_public_in_place(&mut secrets); 164 | pp.unpack_in_place(&mut secrets); 165 | 166 | assert_eq!(expected, secrets); 167 | } 168 | 169 | #[test] 170 | fn test_multiplication() { 171 | let pp = PackedSharingParams::::new(L); 172 | 173 | let rng = &mut ark_std::test_rng(); 174 | let secrets: [F; L] = UniformRand::rand(rng); 175 | let mut secrets = secrets.to_vec(); 176 | let expected: Vec = secrets.iter().map(|x| (*x) * (*x)).collect(); 177 | 178 | pp.pack_from_public_in_place(&mut secrets); 179 | 180 | let mut shares: Vec = secrets.iter().map(|x| (*x) * (*x)).collect(); 181 | 182 | pp.unpack2_in_place(&mut shares); 183 | 184 | assert_eq!(expected, shares); 185 | } 186 | } 187 | --------------------------------------------------------------------------------