mod util;
#[allow(clippy::module_inception)]
pub mod preprocessing;
pub mod prover;
pub mod verifier;
use crate::algebra::*;
use crate::consts::*;
use crate::crypto::*;
use crate::oracle::RandomOracle;
use crate::util::*;
use crate::{Instruction, Instructions};
use std::marker::PhantomData;
use std::sync::Arc;
use async_channel::{Receiver, SendError, Sender};
use async_std::task;
use serde::{Deserialize, Serialize};
async fn feed<D: Domain, PI: Iterator<Item = Instruction<D::Scalar>>>(
senders: &mut [Sender<Arc<Instructions<D>>>],
program: &mut PI,
) -> bool {
let ps = Arc::new(read_n(program, BATCH_SIZE));
if ps.len() == 0 {
return false;
}
for sender in senders {
sender.send(ps.clone()).await.unwrap();
}
true
}
#[derive(Clone, Serialize, Deserialize)]
pub struct Proof<D: Domain> {
hidden: Vec<Hash>,
random: TreePRF,
_ph: PhantomData<D>,
}
impl<D: Domain> Proof<D> {
pub fn serialize(&self) -> Vec<u8> {
bincode::serialize(&self).unwrap()
}
pub fn deserialize(encoded: &[u8]) -> Option<Self> {
bincode::deserialize(encoded).ok()
}
}
pub struct Run {
pub(crate) seed: [u8; KEY_SIZE],
pub(crate) union: Hash,
pub(crate) commitments: Vec<Hash>,
}
pub struct PreprocessingOutput<D: Domain> {
pub(crate) branches: Arc<Vec<Vec<D::Batch>>>,
pub(crate) hidden: Vec<Run>,
}
pub struct Output<D: Domain> {
pub(crate) hidden: Vec<Hash>,
_ph: PhantomData<D>,
}
pub fn pack_branch<D: Domain>(branch: &[D::Scalar]) -> Vec<D::Batch> {
let mut res: Vec<D::Batch> = Vec::with_capacity(branch.len() / D::Batch::DIMENSION + 1);
for chunk in branch.chunks(D::Batch::DIMENSION) {
let mut batch = D::Batch::ZERO;
for (i, s) in chunk.iter().cloned().enumerate() {
batch.set(i, s);
}
res.push(batch)
}
res
}
pub fn pack_branches<D: Domain>(branches: &[&[D::Scalar]]) -> Vec<Vec<D::Batch>> {
let mut batches: Vec<Vec<D::Batch>> = Vec::with_capacity(branches.len());
for branch in branches {
batches.push(pack_branch::<D>(branch));
}
batches
}
impl<D: Domain> Proof<D> {
async fn preprocess<PI: Iterator<Item = Instruction<D::Scalar>>>(
seeds: &[[u8; KEY_SIZE]],
branches: Arc<Vec<Vec<D::Batch>>>,
mut program: PI,
) -> Vec<(Hash, Vec<Hash>)> {
assert!(
branches.len() > 0,
"even when the branch feature is not used, the branch should still be provided and should be a singleton list with an empty element"
);
async fn process<D: Domain>(
root: [u8; KEY_SIZE],
branches: Arc<Vec<Vec<D::Batch>>>,
outputs: Sender<()>,
inputs: Receiver<Arc<Instructions<D>>>,
) -> Result<(Hash, Vec<Hash>), SendError<()>> {
let mut preprocessing: preprocessing::PreprocessingExecution<D> =
preprocessing::PreprocessingExecution::new(root, &branches[..]);
loop {
match inputs.recv().await {
Ok(program) => {
preprocessing.prove(&program[..]);
outputs.send(()).await?;
}
Err(_) => {
return Ok(preprocessing.done());
}
}
}
}
let mut tasks = Vec::with_capacity(D::PREPROCESSING_REPETITIONS);
let mut inputs: Vec<Sender<Arc<Instructions<D>>>> =
Vec::with_capacity(D::PREPROCESSING_REPETITIONS);
let mut outputs = Vec::with_capacity(D::PREPROCESSING_REPETITIONS);
for seed in seeds.iter().cloned() {
let (send_inputs, recv_inputs) = async_channel::bounded(5);
let (send_outputs, recv_outputs) = async_channel::bounded(5);
tasks.push(task::spawn(process::<D>(
seed,
branches.clone(),
send_outputs,
recv_inputs,
)));
inputs.push(send_inputs);
outputs.push(recv_outputs);
}
let mut scheduled = 0;
scheduled += feed::<D, _>(&mut inputs[..], &mut program).await as usize;
scheduled += feed::<D, _>(&mut inputs[..], &mut program).await as usize;
while scheduled > 0 {
for rx in outputs.iter_mut() {
let _ = rx.recv().await;
}
scheduled -= 1;
scheduled += feed::<D, _>(&mut inputs[..], &mut program).await as usize;
}
inputs.clear();
let mut results: Vec<(Hash, Vec<Hash>)> = Vec::with_capacity(D::PREPROCESSING_REPETITIONS);
for t in tasks.into_iter() {
results.push(t.await.unwrap());
}
results
}
pub async fn verify<PI: Iterator<Item = Instruction<D::Scalar>>>(
&self,
branches: &[&[D::Scalar]],
program: PI,
) -> Option<Output<D>> {
let branches = Arc::new(pack_branches::<D>(branches));
let mut roots: Vec<Option<[u8; KEY_SIZE]>> = vec![None; D::PREPROCESSING_REPETITIONS];
self.random.expand(&mut roots);
let mut opened: Vec<bool> = Vec::with_capacity(D::PREPROCESSING_REPETITIONS);
let mut hidden: Vec<usize> = Vec::with_capacity(D::ONLINE_REPETITIONS);
for (i, key) in roots.iter().enumerate() {
opened.push(key.is_some());
if key.is_none() {
hidden.push(i)
}
}
if hidden.len() != D::ONLINE_REPETITIONS {
return None;
}
let opened_roots: Vec<[u8; KEY_SIZE]> = roots
.iter()
.filter(|v| v.is_some())
.map(|v| v.unwrap())
.collect();
debug_assert_eq!(
opened_roots.len(),
D::PREPROCESSING_REPETITIONS - D::ONLINE_REPETITIONS
);
let opened_results = Self::preprocess(&opened_roots[..], branches, program).await;
debug_assert_eq!(
opened_results.len(),
D::PREPROCESSING_REPETITIONS - D::ONLINE_REPETITIONS
);
let mut hashes = Vec::with_capacity(D::PREPROCESSING_REPETITIONS);
{
let mut open_hsh = opened_results.iter().map(|(comm, _)| comm);
let mut hide_hsh = self.hidden.iter();
for open in opened {
if open {
hashes.push(open_hsh.next().unwrap())
} else {
hashes.push(hide_hsh.next().unwrap())
}
}
}
debug_assert_eq!(hashes.len(), D::PREPROCESSING_REPETITIONS);
let mut challenge_prg = {
let mut oracle = RandomOracle::new(CONTEXT_ORACLE_PREPROCESSING, None);
for hash in hashes.iter() {
oracle.feed(hash.as_bytes());
}
oracle.query()
};
let subset: Vec<usize> = random_subset(
&mut challenge_prg,
D::PREPROCESSING_REPETITIONS,
D::ONLINE_REPETITIONS,
);
if hidden[..] == subset[..] {
Some(Output {
hidden: self.hidden.to_vec(),
_ph: PhantomData,
})
} else {
None
}
}
pub fn new<PI: Iterator<Item = Instruction<D::Scalar>>>(
global: [u8; KEY_SIZE],
branches: &[&[D::Scalar]],
program: PI,
) -> (Self, PreprocessingOutput<D>) {
let branches = Arc::new(pack_branches::<D>(branches));
let mut roots: Vec<[u8; KEY_SIZE]> = vec![[0; KEY_SIZE]; D::PREPROCESSING_REPETITIONS];
TreePRF::expand_full(&mut roots, global);
let results = task::block_on(Self::preprocess(&roots[..], branches.clone(), program));
let mut challenge_prg = {
let mut oracle = RandomOracle::new(CONTEXT_ORACLE_PREPROCESSING, None);
for (hash, _) in results.iter() {
oracle.feed(hash.as_bytes());
}
oracle.query()
};
let hidden: Vec<usize> = random_subset(
&mut challenge_prg,
D::PREPROCESSING_REPETITIONS,
D::ONLINE_REPETITIONS,
);
let mut tree: TreePRF = TreePRF::new(D::PREPROCESSING_REPETITIONS, global);
for i in hidden.iter().cloned() {
tree = tree.puncture(i);
}
let mut hidden_runs: Vec<Run> = Vec::with_capacity(D::ONLINE_REPETITIONS);
let mut hidden_hashes: Vec<Hash> = Vec::with_capacity(D::ONLINE_REPETITIONS);
let mut results = results.into_iter().enumerate();
for i in hidden.iter().cloned() {
let result = loop {
let (j, elem) = results.next().unwrap();
if i == j {
break elem;
}
};
hidden_runs.push(Run {
seed: roots[i],
union: result.0.clone(),
commitments: result.1,
});
hidden_hashes.push(result.0.clone());
}
(
Proof {
hidden: hidden_hashes,
random: tree,
_ph: PhantomData,
},
PreprocessingOutput {
branches,
hidden: hidden_runs,
},
)
}
}
#[cfg(test)]
mod tests {
use super::super::algebra::gf2::{BitScalar, GF2P8};
use super::*;
use rand::Rng;
#[test]
fn test_preprocessing_n8() {
let program = vec![
Instruction::Input(1),
Instruction::Input(2),
Instruction::Mul(0, 1, 2),
];
let mut rng = rand::thread_rng();
let seed: [u8; KEY_SIZE] = rng.gen();
let branch: Vec<BitScalar> = vec![];
let branches: Vec<&[BitScalar]> = vec![&branch];
let proof = Proof::<GF2P8>::new(seed, &branches[..], program.iter().cloned());
assert!(task::block_on(proof.0.verify(&branches[..], program.into_iter())).is_some());
}
}
#[cfg(test)]
#[cfg(not(debug_assertions))]
mod benchmark {
use super::super::algebra::gf2::{BitScalar, GF2P8};
use super::*;
use test::Bencher;
const MULT: usize = 1_000_000;
#[bench]
fn bench_preprocessing_proof_gen_n8(b: &mut Bencher) {
let mut program = vec![Instruction::Input(1), Instruction::Input(2)];
program.resize(MULT + 2, Instruction::Mul(0, 1, 2));
let branch: Vec<BitScalar> = vec![];
let branches: Vec<&[BitScalar]> = vec![&branch];
b.iter(|| Proof::<GF2P8>::new([0u8; KEY_SIZE], &branches, program.iter().cloned()));
}
#[bench]
fn bench_preprocessing_proof_verify_n8(b: &mut Bencher) {
let mut program = vec![Instruction::Input(1), Instruction::Input(2)];
program.resize(MULT + 2, Instruction::Mul(0, 1, 2));
let branch: Vec<BitScalar> = vec![];
let branches: Vec<&[BitScalar]> = vec![&branch];
let (proof, _) = Proof::<GF2P8>::new([0u8; KEY_SIZE], &branches, program.iter().cloned());
b.iter(|| task::block_on(proof.verify(&branches, program.iter().cloned())));
}
}