// Copyright 2021 Riad S. Wahby // // This file is part of lcpc-ligero-pc, which is part of lcpc. // // Licensed under the Apache License, Version 2.0 (see // LICENSE or https://www.apache.org/licenses/LICENSE-2.0). // This file may not be copied, modified, or distributed // except according to those terms. use super::{LigeroCommit, LigeroEncoding, LigeroEncodingRho}; use blake3::Hasher as Blake3; use ff::Field; use itertools::iterate; use lcpc_2d::{LcCommit, LcEncoding}; use merlin::Transcript; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha20Rng; use std::iter::repeat_with; use lcpc_test_fields::{ft255::*, ft63::*, random_coeffs}; #[test] fn get_dims() { let mut rng = rand::thread_rng(); for _ in 0..128 { let lgl = 8 + rng.gen::() % 8; for _ in 0..128 { let len_base = 1 << (lgl - 1); let len = len_base + (rng.gen::() % len_base); let (n_rows, n_per_row, n_cols) = LigeroEncoding::::_get_dims(len).unwrap(); assert!(n_rows * n_per_row >= len); assert!((n_rows - 1) * n_per_row < len); assert!( n_per_row * LigeroEncoding::::_rho_den() / LigeroEncoding::::_rho_num() <= n_cols ); assert!(LigeroEncoding::::_dims_ok(n_per_row, n_cols)); } } } #[test] fn col_opens() { println!( "39/40: {}", LigeroEncodingRho::::_n_col_opens() ); println!( "1/2: {}", LigeroEncodingRho::::_n_col_opens() ); println!( "1/4: {}", LigeroEncodingRho::::_n_col_opens() ); } #[cfg(feature="isz")] use typenum::U38 as TLo; #[cfg(not(feature="isz"))] use typenum::U1 as TLo; #[cfg(feature="isz")] use typenum::U39 as THi; #[cfg(all(not(feature="isz"), feature="hlf"))] use typenum::U2 as THi; #[cfg(all(not(feature="isz"),not(feature="hlf")))] use typenum::U4 as THi; #[test] fn rate() { use typenum::Unsigned; println!("{} {}", TLo::to_usize(), THi::to_usize()); } const N_ITERS: usize = 10; #[test] #[ignore] fn rough_bench() { use std::time::Instant; for lgl in (13..=29).step_by(2) { // commit to random poly of specified size let coeffs = random_coeffs(lgl); let enc = LigeroEncodingRho::::new(coeffs.len()); let mut xxx = 0u8; let now = Instant::now(); for i in 0..N_ITERS { let comm = LcCommit::::commit(&coeffs, &enc).unwrap(); let root = comm.get_root(); xxx ^= root.as_ref()[i]; } let dur = now.elapsed().as_nanos() / N_ITERS as u128; println!("{}: {} {:?}", lgl, dur, xxx); } } #[test] #[ignore] fn prove_verify_size_bench() { use ff::PrimeField; use std::time::Instant; for lgl in (13..=29).step_by(2) { // commit to random poly of specified size let coeffs = random_coeffs(lgl); let enc = LigeroEncodingRho::::new(coeffs.len()); let comm = LcCommit::::commit(&coeffs, &enc).unwrap(); let root = comm.get_root(); // evaluate the random polynomial we just generated at a random point x let x = Ft255::random(&mut rand::thread_rng()); // compute the outer and inner tensors for powers of x // NOTE: we treat coeffs as a univariate polynomial, but it doesn't // really matter --- the only difference from a multilinear is the // way we compute outer_tensor and inner_tensor from the eval point let inner_tensor: Vec = iterate(Ft255::one(), |&v| v * x) .take(comm.get_n_per_row()) .collect(); let outer_tensor: Vec = { let xr = x * inner_tensor.last().unwrap(); iterate(Ft255::one(), |&v| v * xr) .take(comm.get_n_rows()) .collect() }; let mut xxx = 0u8; let now = Instant::now(); for i in 0..N_ITERS { let mut tr = Transcript::new(b"test transcript"); tr.append_message(b"polycommit", root.as_ref()); tr.append_message(b"ncols", &(enc.get_n_col_opens() as u64).to_be_bytes()[..]); let pf = comm.prove(&outer_tensor[..], &enc, &mut tr).unwrap(); let encoded: Vec = bincode::serialize(&pf).unwrap(); xxx ^= encoded[i]; } let pf_dur = now.elapsed().as_nanos() / N_ITERS as u128; let mut tr = Transcript::new(b"test transcript"); tr.append_message(b"polycommit", root.as_ref()); tr.append_message(b"ncols", &(enc.get_n_col_opens() as u64).to_be_bytes()[..]); let pf = comm.prove(&outer_tensor[..], &enc, &mut tr).unwrap(); let encoded: Vec = bincode::serialize(&pf).unwrap(); let len = encoded.len(); let now = Instant::now(); for i in 0..N_ITERS { let mut tr = Transcript::new(b"test transcript"); tr.append_message(b"polycommit", root.as_ref()); tr.append_message(b"ncols", &(enc.get_n_col_opens() as u64).to_be_bytes()[..]); xxx ^= pf .verify( root.as_ref(), &outer_tensor[..], &inner_tensor[..], &enc, &mut tr, ) .unwrap() .to_repr() .as_ref()[i]; } let vf_dur = now.elapsed().as_nanos() / N_ITERS as u128; println!("{}: {} {} {} {}", lgl, pf_dur, vf_dur, len, xxx); } } #[test] #[ignore] fn proof_sizes() { for lgl in (13..=29).step_by(2) { // Code1 = 80/81 // Code2 = 54/55 // Code3 = 39/40 // Code4 = 30/31 // Code5 = 25/26 // Code6 = 21/22 // commit to random poly of specified size let coeffs = random_coeffs(lgl); let enc = LigeroEncodingRho::::new(coeffs.len()); let comm = LcCommit::::commit(&coeffs, &enc).unwrap(); let root = comm.get_root(); // evaluate the random polynomial we just generated at a random point x let x = Ft255::random(&mut rand::thread_rng()); // compute the outer and inner tensors for powers of x // NOTE: we treat coeffs as a univariate polynomial, but it doesn't // really matter --- the only difference from a multilinear is the // way we compute outer_tensor and inner_tensor from the eval point let inner_tensor: Vec = iterate(Ft255::one(), |&v| v * x) .take(comm.get_n_per_row()) .collect(); let outer_tensor: Vec = { let xr = x * inner_tensor.last().unwrap(); iterate(Ft255::one(), |&v| v * xr) .take(comm.get_n_rows()) .collect() }; // compute an evaluation proof let mut tr1 = Transcript::new(b"test transcript"); tr1.append_message(b"polycommit", root.as_ref()); tr1.append_message(b"ncols", &(enc.get_n_col_opens() as u64).to_be_bytes()[..]); let pf = comm.prove(&outer_tensor[..], &enc, &mut tr1).unwrap(); let encoded: Vec = bincode::serialize(&pf).unwrap(); println!("{}: {}", lgl, encoded.len()); } } #[test] fn end_to_end() { // commit to a random polynomial at a random rate let coeffs = get_random_coeffs(); let enc = LigeroEncoding::new(coeffs.len()); let comm = LigeroCommit::::commit(&coeffs, &enc).unwrap(); // this is the polynomial commitment let root = comm.get_root(); // evaluate the random polynomial we just generated at a random point x let x = Ft63::random(&mut rand::thread_rng()); // compute the outer and inner tensors for powers of x // NOTE: we treat coeffs as a univariate polynomial, but it doesn't // really matter --- the only difference from a multilinear is the // way we compute outer_tensor and inner_tensor from the eval point let inner_tensor: Vec = iterate(Ft63::one(), |&v| v * x) .take(comm.get_n_per_row()) .collect(); let outer_tensor: Vec = { let xr = x * inner_tensor.last().unwrap(); iterate(Ft63::one(), |&v| v * xr) .take(comm.get_n_rows()) .collect() }; // compute an evaluation proof let mut tr1 = Transcript::new(b"test transcript"); tr1.append_message(b"polycommit", root.as_ref()); tr1.append_message(b"ncols", &(enc.get_n_col_opens() as u64).to_be_bytes()[..]); let pf = comm.prove(&outer_tensor[..], &enc, &mut tr1).unwrap(); // verify it and finish evaluation let mut tr2 = Transcript::new(b"test transcript"); tr2.append_message(b"polycommit", root.as_ref()); tr2.append_message(b"ncols", &(enc.get_n_col_opens() as u64).to_be_bytes()[..]); let enc2 = LigeroEncoding::new_from_dims(pf.get_n_per_row(), pf.get_n_cols()); pf.verify( root.as_ref(), &outer_tensor[..], &inner_tensor[..], &enc2, &mut tr2, ) .unwrap(); } #[test] fn end_to_end_one_proof_ml() { let mut rng = rand::thread_rng(); // commit to a random polynomial at a random rate let lgl = 12 + rng.gen::() % 8; let coeffs = random_coeffs(lgl); let enc = LigeroEncoding::new_ml(lgl); let comm = LigeroCommit::::commit(&coeffs, &enc).unwrap(); // this is the polynomial commitment let root = comm.get_root(); assert!(comm.get_n_rows() != 1); // evaluate the random polynomial we just generated at a random point x let x = Ft63::random(&mut rand::thread_rng()); // compute the outer and inner tensors for powers of x // NOTE: we treat coeffs as a univariate polynomial, but it doesn't // really matter --- the only difference from a multilinear is the // way we compute outer_tensor and inner_tensor from the eval point let inner_tensor: Vec = iterate(Ft63::one(), |&v| v * x) .take(comm.get_n_per_row()) .collect(); let outer_tensor: Vec = { let xr = x * inner_tensor.last().unwrap(); iterate(Ft63::one(), |&v| v * xr) .take(comm.get_n_rows()) .collect() }; // compute an evaluation proof let mut tr1 = Transcript::new(b"test transcript"); tr1.append_message(b"polycommit", root.as_ref()); tr1.append_message(b"ncols", &(enc.get_n_col_opens() as u64).to_be_bytes()[..]); let pf = comm.prove(&outer_tensor[..], &enc, &mut tr1).unwrap(); // verify it and finish evaluation let mut tr2 = Transcript::new(b"test transcript"); tr2.append_message(b"polycommit", root.as_ref()); tr2.append_message(b"ncols", &(enc.get_n_col_opens() as u64).to_be_bytes()[..]); let enc2 = LigeroEncoding::new_from_dims(pf.get_n_per_row(), pf.get_n_cols()); pf.verify( root.as_ref(), &outer_tensor[..], &inner_tensor[..], &enc2, &mut tr2, ) .unwrap(); } #[test] fn end_to_end_two_proofs() { // commit to a random polynomial at a random rate let coeffs = get_random_coeffs(); let enc = LigeroEncoding::new(coeffs.len()); let comm = LigeroCommit::::commit(&coeffs, &enc).unwrap(); // this is the polynomial commitment let root = comm.get_root(); // evaluate the random polynomial we just generated at a random point x let x = Ft63::random(&mut rand::thread_rng()); // compute the outer and inner tensors for powers of x // NOTE: we treat coeffs as a univariate polynomial, but it doesn't // really matter --- the only difference from a multilinear is the // way we compute outer_tensor and inner_tensor from the eval point let inner_tensor: Vec = iterate(Ft63::one(), |&v| v * x) .take(comm.get_n_per_row()) .collect(); let outer_tensor: Vec = { let xr = x * inner_tensor.last().unwrap(); iterate(Ft63::one(), |&v| v * xr) .take(comm.get_n_rows()) .collect() }; // compute an evaluation proof let mut tr1 = Transcript::new(b"test transcript"); tr1.append_message(b"polycommit", root.as_ref()); tr1.append_message(b"ncols", &(enc.get_n_col_opens() as u64).to_be_bytes()[..]); let pf = comm.prove(&outer_tensor[..], &enc, &mut tr1).unwrap(); let challenge_after_first_proof_prover = { let mut key: ::Seed = Default::default(); tr1.challenge_bytes(b"ligero-pc//challenge", &mut key); let mut deg_test_rng = ChaCha20Rng::from_seed(key); Ft63::random(&mut deg_test_rng) }; // produce a second proof with the same transcript tr1.append_message(b"polycommit", root.as_ref()); tr1.append_message(b"ncols", &(enc.get_n_col_opens() as u64).to_be_bytes()[..]); let pf2 = comm.prove(&outer_tensor[..], &enc, &mut tr1).unwrap(); // verify it and finish evaluation let mut tr2 = Transcript::new(b"test transcript"); tr2.append_message(b"polycommit", root.as_ref()); tr2.append_message(b"ncols", &(enc.get_n_col_opens() as u64).to_be_bytes()[..]); let enc2 = LigeroEncoding::new_from_dims(pf.get_n_per_row(), pf.get_n_cols()); let res = pf .verify( root.as_ref(), &outer_tensor[..], &inner_tensor[..], &enc2, &mut tr2, ) .unwrap(); let challenge_after_first_proof_verifier = { let mut key: ::Seed = Default::default(); tr2.challenge_bytes(b"ligero-pc//challenge", &mut key); let mut deg_test_rng = ChaCha20Rng::from_seed(key); Ft63::random(&mut deg_test_rng) }; assert_eq!( challenge_after_first_proof_prover, challenge_after_first_proof_verifier ); // second proof verification with the same transcript tr2.append_message(b"polycommit", root.as_ref()); tr2.append_message(b"ncols", &(enc.get_n_col_opens() as u64).to_be_bytes()[..]); let enc3 = LigeroEncoding::new_from_dims(pf2.get_n_per_row(), pf2.get_n_cols()); let res2 = pf2 .verify( root.as_ref(), &outer_tensor[..], &inner_tensor[..], &enc3, &mut tr2, ) .unwrap(); assert_eq!(res, res2); } fn get_random_coeffs() -> Vec { let mut rng = rand::thread_rng(); let lgl = 8 + rng.gen::() % 8; let len_base = 1 << (lgl - 1); let len = len_base + (rng.gen::() % len_base); repeat_with(|| Ft63::random(&mut rng)).take(len).collect() }