use crate::{
algorithms::crypto_hash::PoseidonCryptoHashGadget,
bits::{Boolean, ToBytesGadget},
integers::uint::UInt8,
traits::{
algorithms::SignatureGadget,
alloc::AllocGadget,
curves::GroupGadget,
eq::{ConditionalEqGadget, EqGadget},
select::CondSelectGadget,
},
CompressedGroupGadget,
CryptoHashGadget,
FpGadget,
ToBitsLEGadget,
ToConstraintFieldGadget,
};
use snarkvm_algorithms::{
crypto_hash::PoseidonDefaultParametersField,
signature::{AleoSignature, AleoSignatureScheme},
SignatureScheme,
};
use snarkvm_curves::{templates::twisted_edwards_extended::Affine as TEAffine, TwistedEdwardsParameters};
use snarkvm_fields::{FieldParameters, PrimeField};
use snarkvm_r1cs::{errors::SynthesisError, ConstraintSystem};
use snarkvm_utilities::{FromBytes, ToBytes};
use anyhow::Result;
use itertools::Itertools;
use std::{borrow::Borrow, marker::PhantomData};
type TEAffineGadget<TE, F> = crate::curves::templates::twisted_edwards::AffineGadget<TE, F, FpGadget<F>>;
#[derive(Derivative)]
#[derivative(
Clone(bound = "TE: TwistedEdwardsParameters<BaseField = F>, F: PrimeField"),
PartialEq(bound = "TE: TwistedEdwardsParameters<BaseField = F>, F: PrimeField"),
Eq(bound = "TE: TwistedEdwardsParameters<BaseField = F>, F: PrimeField"),
Debug(bound = "TE: TwistedEdwardsParameters<BaseField = F>, F: PrimeField")
)]
pub struct AleoSignaturePublicKeyGadget<TE: TwistedEdwardsParameters<BaseField = F>, F: PrimeField>(
TEAffineGadget<TE, F>,
);
impl<TE: TwistedEdwardsParameters<BaseField = F>, F: PrimeField> AllocGadget<TEAffine<TE>, F>
for AleoSignaturePublicKeyGadget<TE, F>
{
fn alloc_constant<Fn: FnOnce() -> Result<T, SynthesisError>, T: Borrow<TEAffine<TE>>, CS: ConstraintSystem<F>>(
cs: CS,
value_gen: Fn,
) -> Result<Self, SynthesisError> {
let public_key = *value_gen()?.borrow();
Ok(Self(TEAffineGadget::<TE, F>::alloc_constant(cs, || Ok(public_key))?))
}
fn alloc<Fn: FnOnce() -> Result<T, SynthesisError>, T: Borrow<TEAffine<TE>>, CS: ConstraintSystem<F>>(
cs: CS,
value_gen: Fn,
) -> Result<Self, SynthesisError> {
let public_key = *value_gen()?.borrow();
Ok(Self(TEAffineGadget::<TE, F>::alloc_checked(cs, || Ok(public_key))?))
}
fn alloc_input<Fn: FnOnce() -> Result<T, SynthesisError>, T: Borrow<TEAffine<TE>>, CS: ConstraintSystem<F>>(
mut cs: CS,
value_gen: Fn,
) -> Result<Self, SynthesisError> {
let point = if let Ok(pk) = value_gen() {
*pk.borrow()
} else {
TEAffine::<TE>::default()
};
let x_coordinate_gadget =
FpGadget::<TE::BaseField>::alloc_input(cs.ns(|| "input x coordinate"), || Ok(point.x))?;
let allocated_gadget =
TEAffineGadget::<TE, F>::alloc_checked(cs.ns(|| "input the allocated point"), || Ok(point))?;
allocated_gadget
.x
.enforce_equal(cs.ns(|| "check x consistency"), &x_coordinate_gadget)?;
Ok(Self(allocated_gadget))
}
}
impl<TE: TwistedEdwardsParameters<BaseField = F>, F: PrimeField> ConditionalEqGadget<F>
for AleoSignaturePublicKeyGadget<TE, F>
{
#[inline]
fn conditional_enforce_equal<CS: ConstraintSystem<F>>(
&self,
mut cs: CS,
other: &Self,
condition: &Boolean,
) -> Result<(), SynthesisError> {
self.0
.conditional_enforce_equal(&mut cs.ns(|| "conditional_enforce_equal"), &other.0, condition)?;
Ok(())
}
fn cost() -> usize {
<TEAffineGadget<TE, F> as ConditionalEqGadget<F>>::cost()
}
}
impl<TE: TwistedEdwardsParameters<BaseField = F>, F: PrimeField> EqGadget<F> for AleoSignaturePublicKeyGadget<TE, F> {
fn is_eq<CS: ConstraintSystem<F>>(&self, mut cs: CS, other: &Self) -> Result<Boolean, SynthesisError> {
self.0.is_eq(&mut cs.ns(|| "is_eq"), &other.0)
}
}
impl<TE: TwistedEdwardsParameters<BaseField = F>, F: PrimeField> ToBytesGadget<F>
for AleoSignaturePublicKeyGadget<TE, F>
{
fn to_bytes<CS: ConstraintSystem<F>>(&self, cs: CS) -> Result<Vec<UInt8>, SynthesisError> {
self.0.x.to_bytes(cs)
}
fn to_bytes_strict<CS: ConstraintSystem<F>>(&self, cs: CS) -> Result<Vec<UInt8>, SynthesisError> {
self.0.x.to_bytes_strict(cs)
}
}
#[derive(Derivative)]
#[derivative(
Clone(bound = "TE: TwistedEdwardsParameters<BaseField = F>, F: PrimeField"),
PartialEq(bound = "TE: TwistedEdwardsParameters<BaseField = F>, F: PrimeField"),
Eq(bound = "TE: TwistedEdwardsParameters<BaseField = F>, F: PrimeField"),
Debug(bound = "TE: TwistedEdwardsParameters<BaseField = F>, F: PrimeField")
)]
pub struct AleoSignatureGadget<TE: TwistedEdwardsParameters<BaseField = F>, F: PrimeField> {
pub(crate) prover_response: FpGadget<F>,
pub(crate) verifier_challenge: FpGadget<F>,
pub(crate) root_public_key: TEAffineGadget<TE, F>,
pub(crate) root_randomizer: TEAffineGadget<TE, F>,
}
impl<TE: TwistedEdwardsParameters<BaseField = F>, F: PrimeField> AllocGadget<AleoSignature<TE>, F>
for AleoSignatureGadget<TE, F>
{
fn alloc<Fn: FnOnce() -> Result<T, SynthesisError>, T: Borrow<AleoSignature<TE>>, CS: ConstraintSystem<F>>(
mut cs: CS,
value_gen: Fn,
) -> Result<Self, SynthesisError> {
let value = value_gen()?;
let signature = *value.borrow();
let prover_response: F = FromBytes::read_le(&signature.prover_response.to_bytes_le()?[..])?;
let verifier_challenge: F = FromBytes::read_le(&signature.verifier_challenge.to_bytes_le()?[..])?;
let prover_response = FpGadget::<F>::alloc(cs.ns(|| "alloc_prover_response"), || Ok(&prover_response))?;
let verifier_challenge =
FpGadget::<F>::alloc(cs.ns(|| "alloc_verifier_challenge"), || Ok(&verifier_challenge))?;
let root_public_key =
TEAffineGadget::<TE, F>::alloc(cs.ns(|| "alloc_root_public_key"), || Ok(signature.root_public_key()?))?;
let root_randomizer =
TEAffineGadget::<TE, F>::alloc(cs.ns(|| "alloc_root_randomizer"), || Ok(signature.root_randomizer()?))?;
Ok(Self {
prover_response,
verifier_challenge,
root_public_key,
root_randomizer,
})
}
fn alloc_constant<
Fn: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<AleoSignature<TE>>,
CS: ConstraintSystem<F>,
>(
mut cs: CS,
value_gen: Fn,
) -> Result<Self, SynthesisError> {
let value = value_gen()?;
let signature = *value.borrow();
let prover_response: F = FromBytes::read_le(&signature.prover_response.to_bytes_le()?[..])?;
let verifier_challenge: F = FromBytes::read_le(&signature.verifier_challenge.to_bytes_le()?[..])?;
let prover_response =
FpGadget::<F>::alloc_constant(cs.ns(|| "alloc_constant_prover_response"), || Ok(&prover_response))?;
let verifier_challenge = FpGadget::<F>::alloc_constant(cs.ns(|| "alloc_constant_verifier_challenge"), || {
Ok(&verifier_challenge)
})?;
let root_public_key =
TEAffineGadget::<TE, F>::alloc_constant(cs.ns(|| "alloc_constant_root_public_key"), || {
Ok(signature.root_public_key()?)
})?;
let root_randomizer =
TEAffineGadget::<TE, F>::alloc_constant(cs.ns(|| "alloc_constant_root_randomizer"), || {
Ok(signature.root_randomizer()?)
})?;
Ok(Self {
prover_response,
verifier_challenge,
root_public_key,
root_randomizer,
})
}
fn alloc_input<Fn: FnOnce() -> Result<T, SynthesisError>, T: Borrow<AleoSignature<TE>>, CS: ConstraintSystem<F>>(
mut cs: CS,
value_gen: Fn,
) -> Result<Self, SynthesisError> {
let value = value_gen()?;
let signature = *value.borrow();
let prover_response: F = FromBytes::read_le(&signature.prover_response.to_bytes_le()?[..])?;
let verifier_challenge: F = FromBytes::read_le(&signature.verifier_challenge.to_bytes_le()?[..])?;
let prover_response =
FpGadget::<F>::alloc_input(cs.ns(|| "alloc_input_prover_response"), || Ok(&prover_response))?;
let verifier_challenge =
FpGadget::<F>::alloc_input(cs.ns(|| "alloc_input_verifier_challenge"), || Ok(&verifier_challenge))?;
let root_public_key = TEAffineGadget::<TE, F>::alloc_input(cs.ns(|| "alloc_input_root_public_key"), || {
Ok(signature.root_public_key()?)
})?;
let root_randomizer = TEAffineGadget::<TE, F>::alloc_input(cs.ns(|| "alloc_input_root_randomizer"), || {
Ok(signature.root_randomizer()?)
})?;
Ok(Self {
prover_response,
verifier_challenge,
root_public_key,
root_randomizer,
})
}
}
impl<TE: TwistedEdwardsParameters<BaseField = F>, F: PrimeField> ConditionalEqGadget<F> for AleoSignatureGadget<TE, F> {
#[inline]
fn conditional_enforce_equal<CS: ConstraintSystem<F>>(
&self,
mut cs: CS,
other: &Self,
condition: &Boolean,
) -> Result<(), SynthesisError> {
self.prover_response.conditional_enforce_equal(
&mut cs.ns(|| "prover_response_conditional_enforce_equal"),
&other.prover_response,
condition,
)?;
self.verifier_challenge.conditional_enforce_equal(
&mut cs.ns(|| "verifier_challenge_conditional_enforce_equal"),
&other.verifier_challenge,
condition,
)?;
self.root_public_key.conditional_enforce_equal(
&mut cs.ns(|| "root_public_key_conditional_enforce_equal"),
&other.root_public_key,
condition,
)?;
self.root_randomizer.conditional_enforce_equal(
&mut cs.ns(|| "root_randomizer_conditional_enforce_equal"),
&other.root_randomizer,
condition,
)?;
Ok(())
}
fn cost() -> usize {
<FpGadget<F> as ConditionalEqGadget<F>>::cost() * 2
}
}
impl<TE: TwistedEdwardsParameters<BaseField = F>, F: PrimeField> EqGadget<F> for AleoSignatureGadget<TE, F> {}
impl<TE: TwistedEdwardsParameters<BaseField = F>, F: PrimeField> ToBytesGadget<F> for AleoSignatureGadget<TE, F> {
fn to_bytes<CS: ConstraintSystem<F>>(&self, mut cs: CS) -> Result<Vec<UInt8>, SynthesisError> {
let mut result = Vec::new();
result.extend(
self.prover_response
.to_bytes(&mut cs.ns(|| "prover_response_to_bytes"))?,
);
result.extend(
self.verifier_challenge
.to_bytes(&mut cs.ns(|| "verifier_challenge_to_bytes"))?,
);
result.extend(
self.root_public_key
.to_bytes(&mut cs.ns(|| "root_public_key_to_bytes"))?,
);
result.extend(
self.root_randomizer
.to_bytes(&mut cs.ns(|| "root_randomizer_to_bytes"))?,
);
Ok(result)
}
fn to_bytes_strict<CS: ConstraintSystem<F>>(&self, mut cs: CS) -> Result<Vec<UInt8>, SynthesisError> {
let mut result = Vec::new();
result.extend(
self.prover_response
.to_bytes_strict(&mut cs.ns(|| "prover_response_to_bytes_strict"))?,
);
result.extend(
self.verifier_challenge
.to_bytes_strict(&mut cs.ns(|| "verifier_challenge_to_bytes_strict"))?,
);
result.extend(
self.root_public_key
.to_bytes_strict(&mut cs.ns(|| "root_public_key_to_bytes_strict"))?,
);
result.extend(
self.root_randomizer
.to_bytes_strict(&mut cs.ns(|| "root_randomizer_to_bytes_strict"))?,
);
Ok(result)
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct AleoComputeKeyGadget {
pub(crate) sk_prf_bits: Vec<Boolean>,
}
impl<F: PrimeField> ToBitsLEGadget<F> for AleoComputeKeyGadget {
fn to_bits_le<CS: ConstraintSystem<F>>(&self, _cs: CS) -> Result<Vec<Boolean>, SynthesisError> {
Ok(self.sk_prf_bits.to_vec())
}
fn to_bits_le_strict<CS: ConstraintSystem<F>>(&self, cs: CS) -> Result<Vec<Boolean>, SynthesisError> {
self.to_bits_le(cs)
}
}
pub struct AleoSignatureSchemeGadget<
TE: TwistedEdwardsParameters<BaseField = F>,
F: PrimeField + PoseidonDefaultParametersField,
> {
pub(crate) signature: AleoSignatureScheme<TE>,
pub(crate) _engine: PhantomData<F>,
}
impl<TE: TwistedEdwardsParameters<BaseField = F>, F: PrimeField + PoseidonDefaultParametersField>
AllocGadget<AleoSignatureScheme<TE>, F> for AleoSignatureSchemeGadget<TE, F>
{
fn alloc_constant<
Fn: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<AleoSignatureScheme<TE>>,
CS: ConstraintSystem<F>,
>(
_cs: CS,
value_gen: Fn,
) -> Result<Self, SynthesisError> {
Ok(Self {
signature: (*value_gen()?.borrow()).clone(),
_engine: PhantomData,
})
}
fn alloc<Fn: FnOnce() -> Result<T, SynthesisError>, T: Borrow<AleoSignatureScheme<TE>>, CS: ConstraintSystem<F>>(
_cs: CS,
_value_gen: Fn,
) -> Result<Self, SynthesisError> {
unimplemented!()
}
fn alloc_input<
Fn: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<AleoSignatureScheme<TE>>,
CS: ConstraintSystem<F>,
>(
_cs: CS,
_value_gen: Fn,
) -> Result<Self, SynthesisError> {
unimplemented!()
}
}
impl<TE: TwistedEdwardsParameters<BaseField = F>, F: PrimeField + PoseidonDefaultParametersField>
SignatureGadget<AleoSignatureScheme<TE>, F> for AleoSignatureSchemeGadget<TE, F>
{
type ComputeKeyGadget = AleoComputeKeyGadget;
type PublicKeyGadget = AleoSignaturePublicKeyGadget<TE, F>;
type SignatureGadget = AleoSignatureGadget<TE, F>;
fn compute_key<CS: ConstraintSystem<F>>(
&self,
mut cs: CS,
signature: &Self::SignatureGadget,
) -> Result<Self::ComputeKeyGadget, SynthesisError> {
let output = PoseidonCryptoHashGadget::<F, 4, false>::check_evaluation_gadget(
&mut cs.ns(|| "Hash root_public_key and root_randomizer"),
&[
signature.root_public_key.to_x_coordinate(),
signature.root_randomizer.to_x_coordinate(),
],
)?;
let mut sk_prf_bits = output.to_bits_le_strict(&mut cs.ns(|| "Output hash to bytes"))?;
sk_prf_bits.resize(
<TE::ScalarField as PrimeField>::Parameters::CAPACITY as usize,
Boolean::Constant(false),
);
Ok(AleoComputeKeyGadget { sk_prf_bits })
}
fn verify<CS: ConstraintSystem<F>>(
&self,
mut cs: CS,
public_key: &Self::PublicKeyGadget,
message: &[UInt8],
signature: &Self::SignatureGadget,
) -> Result<Boolean, SynthesisError> {
let zero_affine: TEAffineGadget<TE, F> =
<TEAffineGadget<TE, F> as GroupGadget<TEAffine<TE>, F>>::zero(cs.ns(|| "affine zero"))?;
let s = {
let mut s_bits = signature
.prover_response
.to_bits_le_strict(cs.ns(|| "prover_response to_bits_le_strict"))?;
s_bits.resize(
<TE::ScalarField as PrimeField>::Parameters::MODULUS_BITS as usize,
Boolean::Constant(false),
);
s_bits
};
let g_s = {
let mut g_s = zero_affine.clone();
for (i, (base, bit)) in self.signature.parameters().iter().zip_eq(s).enumerate() {
let added = g_s.add_constant(cs.ns(|| format!("add_g_s_{}", i)), base)?;
g_s = TEAffineGadget::<TE, F>::conditionally_select(
cs.ns(|| format!("cond_select_g_s_{}", i)),
&bit,
&added,
&g_s,
)?;
}
g_s
};
let c = {
let mut c_bits = signature
.verifier_challenge
.to_bits_le_strict(cs.ns(|| "verifier_challenge to_bits_le_strict"))?;
c_bits.resize(
<TE::ScalarField as PrimeField>::Parameters::MODULUS_BITS as usize,
Boolean::Constant(false),
);
c_bits
};
let g_sk_sig = &signature.root_public_key;
let g_sk_sig_c = <TEAffineGadget<TE, F> as GroupGadget<TEAffine<TE>, F>>::mul_bits(
g_sk_sig,
cs.ns(|| "G^sk_sig^c"),
&zero_affine,
c.into_iter(),
)?;
let g_r = <TEAffineGadget<TE, F> as GroupGadget<TEAffine<TE>, F>>::add(
&g_s,
cs.ns(|| "G^r := G^s G^sk_sig^c"),
&g_sk_sig_c,
)?;
let candidate_verifier_challenge = {
let mut preimage = Vec::new();
preimage.extend_from_slice(
&public_key
.0
.x
.to_constraint_field(cs.ns(|| "public_key to constraint field"))?,
);
preimage.extend_from_slice(&g_r.x.to_constraint_field(cs.ns(|| "G^r to constraint field"))?);
preimage.push(FpGadget::<F>::Constant(F::from(message.len() as u128)));
preimage.extend_from_slice(&message.to_constraint_field(cs.ns(|| "convert message into field elements"))?);
let hash = PoseidonCryptoHashGadget::<F, 4, false>::check_evaluation_gadget(
cs.ns(|| "Poseidon of preimage"),
&preimage,
)?;
let mut hash_bits = hash.to_bits_le(cs.ns(|| "convert the hash into bits"))?;
hash_bits.resize(
<TE::ScalarField as PrimeField>::Parameters::CAPACITY as usize,
Boolean::Constant(false),
);
Boolean::le_bits_to_fp_var(cs.ns(|| "obtain the truncated hash"), &hash_bits)?
};
let g_r_sig = &signature.root_randomizer;
let candidate_public_key = {
let sk_prf = {
let output = PoseidonCryptoHashGadget::<F, 4, false>::check_evaluation_gadget(
cs.ns(|| "Poseidon(G^sk_sig || G^r_sig)"),
&[g_sk_sig.x.clone(), g_r_sig.x.clone()],
)?;
let mut sk_prf_bits = output.to_bits_le(cs.ns(|| "Convert the output into bits"))?;
sk_prf_bits.resize(
<TE::ScalarField as PrimeField>::Parameters::CAPACITY as usize,
Boolean::Constant(false),
);
sk_prf_bits.push(Boolean::Constant(false)); sk_prf_bits
};
let g_sk_prf = {
let mut g_sk_prf = zero_affine;
for (i, (base, bit)) in self.signature.parameters().iter().zip_eq(sk_prf).enumerate() {
let added = g_sk_prf.add_constant(cs.ns(|| format!("add_g_sk_prf_{}", i)), base)?;
g_sk_prf = TEAffineGadget::<TE, F>::conditionally_select(
cs.ns(|| format!("cond_select_g_sk_prf_{}", i)),
&bit,
&added,
&g_sk_prf,
)?;
}
g_sk_prf
};
let g_sk_sig_g_r_sig = <TEAffineGadget<TE, F> as GroupGadget<TEAffine<TE>, F>>::add(
g_sk_sig,
cs.ns(|| "G^sk_sig G^r_sig"),
g_r_sig,
)?;
<TEAffineGadget<TE, F> as GroupGadget<TEAffine<TE>, F>>::add(
&g_sk_sig_g_r_sig,
cs.ns(|| "G^sk_sig G^r_sig G^sk_prf"),
&g_sk_prf,
)?
};
let verifier_challenge_equals = signature
.verifier_challenge
.is_eq(cs.ns(|| "Check verifier challenge"), &candidate_verifier_challenge)?;
let public_key_equals = public_key
.0
.is_eq(cs.ns(|| "Check public key"), &candidate_public_key)?;
Boolean::and(cs.ns(|| "a ^ b"), &verifier_challenge_equals, &public_key_equals)
}
}