use core::marker::PhantomData;
use crate::{
rand_core::{CryptoRng, RngCore, SeedableRng},
typenum::{
marker_traits::NonZero, type_operators::IsLessOrEqual, PartialDiv, Unsigned, U32, U64,
},
Sigma, Writable,
};
use digest::{BlockInput, FixedOutput, Update};
use generic_array::GenericArray;
pub trait Transcript<S: Sigma>: Clone {
fn add_name<N: Writable + ?Sized>(&mut self, name: &N);
fn add_statement(&mut self, sigma: &S, statement: &S::Statement);
fn get_challenge(
self,
sigma: &S,
announcement: &S::Announcement,
) -> GenericArray<u8, S::ChallengeLength>;
}
pub trait ProverTranscript<S: Sigma> {
type Rng: CryptoRng + RngCore;
fn gen_rng<R: CryptoRng + RngCore>(
&self,
sigma: &S,
witness: &S::Witness,
in_rng: Option<&mut R>,
) -> Self::Rng;
}
#[derive(Clone, Debug)]
pub struct HashTranscript<H, R = ()> {
hash: H,
rng: PhantomData<R>,
}
impl<H: Default, R> Default for HashTranscript<H, R> {
fn default() -> Self {
HashTranscript {
hash: H::default(),
rng: PhantomData,
}
}
}
#[derive(Clone)]
struct WriteHash<H>(H);
impl<H: Update> core::fmt::Write for WriteHash<H> {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
self.0.update(s.as_bytes());
Ok(())
}
}
impl<H, S: Sigma, R: Clone> Transcript<S> for HashTranscript<H, R>
where
S::ChallengeLength: IsLessOrEqual<U32>,
<S::ChallengeLength as IsLessOrEqual<U32>>::Output: NonZero,
H: BlockInput<BlockSize = U64> + FixedOutput<OutputSize = U32> + Update + Default + Clone,
{
fn add_name<N: Writable + ?Sized>(&mut self, name: &N) {
let hashed_tag = {
let mut hash = WriteHash(H::default());
name.write_to(&mut hash)
.expect("writing to hash won't fail");
hash.0.finalize_fixed()
};
let fill_block =
<<H::BlockSize as PartialDiv<H::OutputSize>>::Output as Unsigned>::to_usize();
for _ in 0..fill_block {
self.hash.update(&hashed_tag[..]);
}
}
fn add_statement(&mut self, sigma: &S, statement: &S::Statement) {
sigma.hash_statement(&mut self.hash, statement);
}
fn get_challenge(
mut self,
sigma: &S,
announce: &S::Announcement,
) -> GenericArray<u8, S::ChallengeLength> {
sigma.hash_announcement(&mut self.hash, announce);
let challenge_bytes = self.hash.finalize_fixed();
GenericArray::clone_from_slice(&challenge_bytes[..S::ChallengeLength::to_usize()])
}
}
impl<S, H, R> ProverTranscript<S> for HashTranscript<H, R>
where
S: Sigma,
H: Update + FixedOutput<OutputSize = U32> + Clone,
R: SeedableRng + CryptoRng + RngCore + Clone,
R::Seed: From<GenericArray<u8, U32>>,
{
type Rng = R;
fn gen_rng<SysRng: CryptoRng + RngCore>(
&self,
sigma: &S,
witness: &S::Witness,
in_rng: Option<&mut SysRng>,
) -> Self::Rng {
let mut rng_hash = self.hash.clone();
sigma.hash_witness(&mut rng_hash, witness);
if let Some(rng) = in_rng {
let mut randomness = [0u8; 32];
rng.fill_bytes(&mut randomness);
rng_hash.update(randomness);
}
let secret_seed = rng_hash.finalize_fixed();
R::from_seed(secret_seed.into())
}
}