use crate::field::{FieldElement, FieldError};
#[derive(Debug, thiserror::Error)]
pub enum SerializeError {
#[error("serialized input has wrong length")]
UnpackInputSizeMismatch,
#[error("finite field operation error")]
Field(#[from] FieldError),
}
pub fn proof_length(dimension: usize) -> usize {
dimension + 3 + (dimension + 1).next_power_of_two()
}
#[derive(Debug)]
pub struct UnpackedProof<'a, F: FieldElement> {
pub data: &'a [F],
pub f0: &'a F,
pub g0: &'a F,
pub h0: &'a F,
pub points_h_packed: &'a [F],
}
#[derive(Debug)]
pub struct UnpackedProofMut<'a, F: FieldElement> {
pub data: &'a mut [F],
pub f0: &'a mut F,
pub g0: &'a mut F,
pub h0: &'a mut F,
pub points_h_packed: &'a mut [F],
}
pub(crate) fn unpack_proof<F: FieldElement>(
proof: &[F],
dimension: usize,
) -> Result<UnpackedProof<F>, SerializeError> {
if proof.len() != proof_length(dimension) {
return Err(SerializeError::UnpackInputSizeMismatch);
}
let (data, rest) = proof.split_at(dimension);
if let ([f0, g0, h0], points_h_packed) = rest.split_at(3) {
Ok(UnpackedProof {
data,
f0,
g0,
h0,
points_h_packed,
})
} else {
Err(SerializeError::UnpackInputSizeMismatch)
}
}
pub fn unpack_proof_mut<F: FieldElement>(
proof: &mut [F],
dimension: usize,
) -> Result<UnpackedProofMut<F>, SerializeError> {
if proof.len() != proof_length(dimension) {
return Err(SerializeError::UnpackInputSizeMismatch);
}
let (data, rest) = proof.split_at_mut(dimension);
if let ([f0, g0, h0], points_h_packed) = rest.split_at_mut(3) {
Ok(UnpackedProofMut {
data,
f0,
g0,
h0,
points_h_packed,
})
} else {
Err(SerializeError::UnpackInputSizeMismatch)
}
}
pub fn reconstruct_shares<F: FieldElement>(share1: &[F], share2: &[F]) -> Option<Vec<F>> {
if share1.len() != share2.len() {
return None;
}
let mut reconstructed: Vec<F> = vec![F::zero(); share1.len()];
for (r, (s1, s2)) in reconstructed
.iter_mut()
.zip(share1.iter().zip(share2.iter()))
{
*r = *s1 + *s2;
}
Some(reconstructed)
}
#[cfg(test)]
pub mod tests {
use super::*;
use crate::field::{Field32, Field64};
use assert_matches::assert_matches;
pub fn secret_share(share: &mut [Field32]) -> Vec<Field32> {
use rand::Rng;
let mut rng = rand::thread_rng();
let mut random = vec![0u32; share.len()];
let mut share2 = vec![Field32::zero(); share.len()];
rng.fill(&mut random[..]);
for (r, f) in random.iter().zip(share2.iter_mut()) {
*f = Field32::from(*r);
}
for (f1, f2) in share.iter_mut().zip(share2.iter()) {
*f1 -= *f2;
}
share2
}
#[test]
fn test_unpack_share_mut() {
let dim = 15;
let len = proof_length(dim);
let mut share = vec![Field32::from(0); len];
let unpacked = unpack_proof_mut(&mut share, dim).unwrap();
*unpacked.f0 = Field32::from(12);
assert_eq!(share[dim], 12);
let mut short_share = vec![Field32::from(0); len - 1];
assert_matches!(
unpack_proof_mut(&mut short_share, dim),
Err(SerializeError::UnpackInputSizeMismatch)
);
}
#[test]
fn test_unpack_share() {
let dim = 15;
let len = proof_length(dim);
let share = vec![Field64::from(0); len];
unpack_proof(&share, dim).unwrap();
let short_share = vec![Field64::from(0); len - 1];
assert_matches!(
unpack_proof(&short_share, dim),
Err(SerializeError::UnpackInputSizeMismatch)
);
}
#[test]
fn secret_sharing() {
let mut share1 = vec![Field32::zero(); 10];
share1[3] = 21.into();
share1[8] = 123.into();
let original_data = share1.clone();
let share2 = secret_share(&mut share1);
let reconstructed = reconstruct_shares(&share1, &share2).unwrap();
assert_eq!(reconstructed, original_data);
}
}