use std::convert::TryInto;
use super::gf2_192::gf2_192poly_from_byte_array;
use super::prover::ProofBytes;
use super::unchecked_tree::UncheckedConjecture;
use super::unchecked_tree::UncheckedLeaf;
use super::unchecked_tree::UncheckedTree;
use super::wscalar::Wscalar;
use super::GROUP_SIZE;
use super::SOUNDNESS_BYTES;
use crate::sigma_protocol::dht_protocol::SecondDhTupleProverMessage;
use crate::sigma_protocol::unchecked_tree::UncheckedDhTuple;
use crate::sigma_protocol::Challenge;
use crate::sigma_protocol::GroupSizedBytes;
use crate::sigma_protocol::UncheckedSchnorr;
use ergotree_ir::serialization::sigma_byte_reader;
use ergotree_ir::serialization::sigma_byte_reader::SigmaByteRead;
use ergotree_ir::serialization::sigma_byte_writer::SigmaByteWrite;
use ergotree_ir::serialization::sigma_byte_writer::SigmaByteWriter;
use ergotree_ir::sigma_protocol::sigma_boolean::SigmaBoolean;
use ergotree_ir::sigma_protocol::sigma_boolean::SigmaConjecture;
use ergotree_ir::sigma_protocol::sigma_boolean::SigmaProofOfKnowledgeTree;
use gf2_192::Gf2_192Error;
use thiserror::Error;
pub(crate) fn serialize_sig(tree: UncheckedTree) -> ProofBytes {
let mut data = Vec::with_capacity(SOUNDNESS_BYTES + GROUP_SIZE);
let mut w = SigmaByteWriter::new(&mut data, None);
#[allow(clippy::unwrap_used)]
sig_write_bytes(&tree, &mut w, true).unwrap();
ProofBytes::Some(data)
}
fn sig_write_bytes<W: SigmaByteWrite>(
node: &UncheckedTree,
w: &mut W,
write_challenges: bool,
) -> Result<(), std::io::Error> {
if write_challenges {
node.challenge().sigma_serialize(w)?;
}
match node {
UncheckedTree::UncheckedLeaf(leaf) => match leaf {
UncheckedLeaf::UncheckedSchnorr(us) => {
let mut sm_bytes = us.second_message.z.as_scalar_ref().to_bytes();
w.write_all(sm_bytes.as_mut_slice())?;
Ok(())
}
UncheckedLeaf::UncheckedDhTuple(dh) => {
let mut sm_bytes = dh.second_message.z.as_scalar_ref().to_bytes();
w.write_all(sm_bytes.as_mut_slice())
}
},
UncheckedTree::UncheckedConjecture(conj) => match conj {
UncheckedConjecture::CandUnchecked {
challenge: _,
children,
} => {
for child in children {
sig_write_bytes(child, w, false)?;
}
Ok(())
}
UncheckedConjecture::CorUnchecked {
challenge: _,
children,
} => {
let (last, elements) = children.split_last();
for child in elements {
sig_write_bytes(child, w, true)?;
}
sig_write_bytes(last, w, false)?;
Ok(())
}
UncheckedConjecture::CthresholdUnchecked {
challenge: _,
children,
k,
polynomial,
} => {
let mut polynomial_bytes = polynomial.to_bytes();
assert_eq!(
polynomial_bytes.len(),
(children.len() - *k as usize) * SOUNDNESS_BYTES
);
w.write_all(polynomial_bytes.as_mut_slice())?;
for child in children {
sig_write_bytes(child, w, false)?;
}
Ok(())
}
},
}
}
pub fn parse_sig_compute_challenges(
exp: &SigmaBoolean,
mut proof_bytes: Vec<u8>,
) -> Result<UncheckedTree, SigParsingError> {
if proof_bytes.is_empty() {
return Err(SigParsingError::EmptyProof(exp.clone()));
}
let mut r = sigma_byte_reader::from_bytes(proof_bytes.as_mut_slice());
parse_sig_compute_challenges_reader(exp, &mut r, None)
.map_err(|e| SigParsingError::TopLevelExpWrap(e.into(), exp.clone()))
}
fn parse_sig_compute_challenges_reader<R: SigmaByteRead>(
exp: &SigmaBoolean,
r: &mut R,
challenge_opt: Option<Challenge>,
) -> Result<UncheckedTree, SigParsingError> {
let challenge = if let Some(c) = challenge_opt {
c
} else {
Challenge::sigma_parse(r).map_err(|_| SigParsingError::ChallengeRead(exp.clone()))?
};
match exp {
SigmaBoolean::TrivialProp(b) => Err(SigParsingError::TrivialPropFound(*b)),
SigmaBoolean::ProofOfKnowledge(tree) => match tree {
SigmaProofOfKnowledgeTree::ProveDlog(dl) => {
let mut scalar_bytes: [u8; super::GROUP_SIZE] = [0; super::GROUP_SIZE];
r.read_exact(&mut scalar_bytes)
.map_err(|_| SigParsingError::ScalarReadProveDlog(exp.clone()))?;
let z = Wscalar::from(GroupSizedBytes(scalar_bytes.into()));
Ok(UncheckedSchnorr {
proposition: dl.clone(),
commitment_opt: None,
challenge,
second_message: z.into(),
}
.into())
}
SigmaProofOfKnowledgeTree::ProveDhTuple(dh) => {
let mut scalar_bytes: [u8; super::GROUP_SIZE] = [0; super::GROUP_SIZE];
r.read_exact(&mut scalar_bytes)
.map_err(|_| SigParsingError::ScalarReadProveDhTuple(exp.clone()))?;
let z = Wscalar::from(GroupSizedBytes(scalar_bytes.into()));
Ok(UncheckedDhTuple {
proposition: dh.clone(),
commitment_opt: None,
challenge,
second_message: SecondDhTupleProverMessage { z },
}
.into())
}
},
SigmaBoolean::SigmaConjecture(conj) => match conj {
SigmaConjecture::Cand(cand) => {
let children = cand.items.try_mapped_ref(|it| {
parse_sig_compute_challenges_reader(it, r, Some(challenge.clone()))
})?;
Ok(UncheckedConjecture::CandUnchecked {
challenge,
children,
}
.into())
}
SigmaConjecture::Cor(cor) => {
let mut children: Vec<UncheckedTree> = Vec::with_capacity(cor.items.len());
let (last, rest) = cor.items.split_last();
for it in rest {
children.push(parse_sig_compute_challenges_reader(it, r, None)?);
}
let xored_challenge = children
.clone()
.into_iter()
.map(|c| c.challenge())
.fold(challenge.clone(), |acc, c| acc.xor(c));
let last_child =
parse_sig_compute_challenges_reader(last, r, Some(xored_challenge))?;
children.push(last_child);
#[allow(clippy::unwrap_used)] Ok(UncheckedConjecture::CorUnchecked {
challenge,
children: children.try_into().unwrap(),
}
.into())
}
SigmaConjecture::Cthreshold(ct) => {
let n_children = ct.children.len();
let n_coeff = n_children - ct.k as usize;
let buf_size = n_coeff * SOUNDNESS_BYTES;
let mut coeff_bytes = vec![0u8; buf_size];
r.read_exact(&mut coeff_bytes)
.map_err(|_| SigParsingError::CthresholdCoeffRead(exp.clone()))?;
let polynomial = gf2_192poly_from_byte_array(challenge.clone(), coeff_bytes)?;
let children =
ct.children
.clone()
.enumerated()
.try_mapped_ref(|(idx, child)| {
let one_based_index = (idx + 1) as u8;
let ch = polynomial.evaluate(one_based_index).into();
parse_sig_compute_challenges_reader(child, r, Some(ch))
})?;
Ok(UncheckedConjecture::CthresholdUnchecked {
challenge,
children,
k: ct.k,
polynomial,
}
.into())
}
},
}
}
#[allow(missing_docs)]
#[derive(Error, PartialEq, Debug, Clone)]
pub enum SigParsingError {
#[error("Empty proof for exp: {0:?}")]
EmptyProof(SigmaBoolean),
#[error("Unexpected TrivialProp found: {0}")]
TrivialPropFound(bool),
#[error("gf2_192 error: {0}")]
Gf2_192Error(#[from] Gf2_192Error),
#[error("Empty commitment in UncheckedLeaf with proposition: {0:?}")]
EmptyCommitment(SigmaBoolean),
#[error("Challenge reading erorr with exp: {0:?}")]
ChallengeRead(SigmaBoolean),
#[error("Scalar in ProveDlog reading erorr with exp: {0:?}")]
ScalarReadProveDlog(SigmaBoolean),
#[error("Scalar in ProveDhTumple reading erorr with exp: {0:?}")]
ScalarReadProveDhTuple(SigmaBoolean),
#[error("Cthreshold coeff reading erorr with exp: {0:?}")]
CthresholdCoeffRead(SigmaBoolean),
#[error("Error: {0:?} for top level exp: {1:?}")]
TopLevelExpWrap(Box<SigParsingError>, SigmaBoolean),
}