refactor(zk): convert ark types to custom types before serialization

This commit is contained in:
Nicolas Sarlin
2024-08-16 14:38:09 +02:00
committed by Nicolas Sarlin
parent 5cfc57f51a
commit e76503984a
5 changed files with 476 additions and 32 deletions

View File

@@ -1,29 +1,14 @@
use ark_ec::pairing::PairingOutput;
use ark_ec::short_weierstrass::Affine;
use ark_ec::{AdditiveGroup as Group, CurveGroup, VariableBaseMSM};
use ark_ff::{BigInt, Field, MontFp, Zero};
use ark_poly::univariate::DensePolynomial;
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Compress, Validate};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use core::fmt;
use core::ops::{Add, AddAssign, Div, Mul, Neg, Sub, SubAssign};
use serde::{Deserialize, Serialize};
fn ark_se<S, A: CanonicalSerialize>(a: &A, s: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut bytes = vec![];
a.serialize_with_mode(&mut bytes, Compress::Yes)
.map_err(serde::ser::Error::custom)?;
s.serialize_bytes(&bytes)
}
fn ark_de<'de, D, A: CanonicalDeserialize>(data: D) -> Result<A, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let s: Vec<u8> = serde::de::Deserialize::deserialize(data)?;
let a = A::deserialize_with_mode(s.as_slice(), Compress::Yes, Validate::Yes);
a.map_err(serde::de::Error::custom)
}
use crate::serialization::{SerializableAffine, SerializableFp, SerializableFp12, SerializableFp2};
struct MontIntDisplay<'a, T>(&'a T);

View File

@@ -34,6 +34,8 @@ fn bigint_to_le_bytes(x: [u64; 6]) -> [u8; 6 * 8] {
}
mod g1 {
use crate::serialization::InvalidSerializedAffineError;
use super::*;
#[derive(
@@ -48,12 +50,31 @@ mod g1 {
CanonicalSerialize,
CanonicalDeserialize,
)]
#[serde(
try_from = "SerializableAffine<SerializableFp>",
into = "SerializableAffine<SerializableFp>"
)]
#[repr(transparent)]
pub struct G1Affine {
#[serde(serialize_with = "ark_se", deserialize_with = "ark_de")]
pub(crate) inner: ark_bls12_381::g1::G1Affine,
}
impl From<G1Affine> for SerializableAffine<SerializableFp> {
fn from(value: G1Affine) -> Self {
SerializableAffine::compressed(value.inner)
}
}
impl TryFrom<SerializableAffine<SerializableFp>> for G1Affine {
type Error = InvalidSerializedAffineError;
fn try_from(value: SerializableAffine<SerializableFp>) -> Result<Self, Self::Error> {
Ok(Self {
inner: value.try_into()?,
})
}
}
impl G1Affine {
pub fn multi_mul_scalar(bases: &[Self], scalars: &[Zp]) -> G1 {
// SAFETY: interpreting a `repr(transparent)` pointer as its contents.
@@ -80,12 +101,31 @@ mod g1 {
CanonicalSerialize,
CanonicalDeserialize,
)]
#[serde(
try_from = "SerializableAffine<SerializableFp>",
into = "SerializableAffine<SerializableFp>"
)]
#[repr(transparent)]
pub struct G1 {
#[serde(serialize_with = "ark_se", deserialize_with = "ark_de")]
pub(crate) inner: ark_bls12_381::G1Projective,
}
impl From<G1> for SerializableAffine<SerializableFp> {
fn from(value: G1) -> Self {
SerializableAffine::compressed(value.inner.into_affine())
}
}
impl TryFrom<SerializableAffine<SerializableFp>> for G1 {
type Error = InvalidSerializedAffineError;
fn try_from(value: SerializableAffine<SerializableFp>) -> Result<Self, Self::Error> {
Ok(Self {
inner: Affine::try_from(value)?.into(),
})
}
}
impl fmt::Debug for G1 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("G1")
@@ -210,6 +250,8 @@ mod g1 {
}
mod g2 {
use crate::serialization::InvalidSerializedAffineError;
use super::*;
#[derive(
@@ -224,12 +266,31 @@ mod g2 {
CanonicalSerialize,
CanonicalDeserialize,
)]
#[serde(
try_from = "SerializableAffine<SerializableFp2>",
into = "SerializableAffine<SerializableFp2>"
)]
#[repr(transparent)]
pub struct G2Affine {
#[serde(serialize_with = "ark_se", deserialize_with = "ark_de")]
pub(crate) inner: ark_bls12_381::g2::G2Affine,
}
impl From<G2Affine> for SerializableAffine<SerializableFp2> {
fn from(value: G2Affine) -> Self {
SerializableAffine::compressed(value.inner)
}
}
impl TryFrom<SerializableAffine<SerializableFp2>> for G2Affine {
type Error = InvalidSerializedAffineError;
fn try_from(value: SerializableAffine<SerializableFp2>) -> Result<Self, Self::Error> {
Ok(Self {
inner: value.try_into()?,
})
}
}
impl G2Affine {
pub fn multi_mul_scalar(bases: &[Self], scalars: &[Zp]) -> G2 {
// SAFETY: interpreting a `repr(transparent)` pointer as its contents.
@@ -256,12 +317,31 @@ mod g2 {
CanonicalSerialize,
CanonicalDeserialize,
)]
#[serde(
try_from = "SerializableAffine<SerializableFp2>",
into = "SerializableAffine<SerializableFp2>"
)]
#[repr(transparent)]
pub struct G2 {
#[serde(serialize_with = "ark_se", deserialize_with = "ark_de")]
pub(crate) inner: ark_bls12_381::G2Projective,
}
impl From<G2> for SerializableAffine<SerializableFp2> {
fn from(value: G2) -> Self {
SerializableAffine::compressed(value.inner.into_affine())
}
}
impl TryFrom<SerializableAffine<SerializableFp2>> for G2 {
type Error = InvalidSerializedAffineError;
fn try_from(value: SerializableAffine<SerializableFp2>) -> Result<Self, Self::Error> {
Ok(Self {
inner: Affine::try_from(value)?.into(),
})
}
}
impl fmt::Debug for G2 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[allow(dead_code)]
@@ -433,16 +513,34 @@ mod g2 {
}
mod gt {
use crate::serialization::InvalidArraySizeError;
use super::*;
use ark_ec::pairing::Pairing;
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
#[serde(try_from = "SerializableFp12", into = "SerializableFp12")]
#[repr(transparent)]
pub struct Gt {
#[serde(serialize_with = "ark_se", deserialize_with = "ark_de")]
inner: ark_ec::pairing::PairingOutput<ark_bls12_381::Bls12_381>,
}
impl From<Gt> for SerializableFp12 {
fn from(value: Gt) -> Self {
value.inner.0.into()
}
}
impl TryFrom<SerializableFp12> for Gt {
type Error = InvalidArraySizeError;
fn try_from(value: SerializableFp12) -> Result<Self, Self::Error> {
Ok(Self {
inner: PairingOutput(value.try_into()?),
})
}
}
impl fmt::Debug for Gt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[allow(dead_code)]
@@ -566,6 +664,8 @@ mod gt {
}
mod zp {
use crate::serialization::InvalidArraySizeError;
use super::*;
use ark_ff::Fp;
use zeroize::Zeroize;
@@ -605,12 +705,27 @@ mod zp {
}
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, Zeroize)]
#[serde(try_from = "SerializableFp", into = "SerializableFp")]
#[repr(transparent)]
pub struct Zp {
#[serde(serialize_with = "ark_se", deserialize_with = "ark_de")]
pub(crate) inner: ark_bls12_381::Fr,
}
impl From<Zp> for SerializableFp {
fn from(value: Zp) -> Self {
value.inner.into()
}
}
impl TryFrom<SerializableFp> for Zp {
type Error = InvalidArraySizeError;
fn try_from(value: SerializableFp) -> Result<Self, Self::Error> {
Ok(Self {
inner: value.try_into()?,
})
}
}
impl fmt::Debug for Zp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Zp")

View File

@@ -34,6 +34,8 @@ fn bigint_to_le_bytes(x: [u64; 7]) -> [u8; 7 * 8] {
}
mod g1 {
use crate::serialization::InvalidSerializedAffineError;
use super::*;
#[derive(
@@ -45,15 +47,34 @@ mod g1 {
Serialize,
Deserialize,
Hash,
CanonicalSerialize,
CanonicalDeserialize,
CanonicalSerialize,
)]
#[serde(
try_from = "SerializableAffine<SerializableFp>",
into = "SerializableAffine<SerializableFp>"
)]
#[repr(transparent)]
pub struct G1Affine {
#[serde(serialize_with = "ark_se", deserialize_with = "ark_de")]
pub(crate) inner: crate::curve_446::g1::G1Affine,
}
impl From<G1Affine> for SerializableAffine<SerializableFp> {
fn from(value: G1Affine) -> Self {
SerializableAffine::compressed(value.inner)
}
}
impl TryFrom<SerializableAffine<SerializableFp>> for G1Affine {
type Error = InvalidSerializedAffineError;
fn try_from(value: SerializableAffine<SerializableFp>) -> Result<Self, Self::Error> {
Ok(Self {
inner: value.try_into()?,
})
}
}
impl G1Affine {
#[track_caller]
pub fn multi_mul_scalar(bases: &[Self], scalars: &[Zp]) -> G1 {
@@ -81,12 +102,31 @@ mod g1 {
CanonicalSerialize,
CanonicalDeserialize,
)]
#[serde(
try_from = "SerializableAffine<SerializableFp>",
into = "SerializableAffine<SerializableFp>"
)]
#[repr(transparent)]
pub struct G1 {
#[serde(serialize_with = "ark_se", deserialize_with = "ark_de")]
pub(crate) inner: crate::curve_446::g1::G1Projective,
}
impl From<G1> for SerializableAffine<SerializableFp> {
fn from(value: G1) -> Self {
SerializableAffine::compressed(value.inner.into_affine())
}
}
impl TryFrom<SerializableAffine<SerializableFp>> for G1 {
type Error = InvalidSerializedAffineError;
fn try_from(value: SerializableAffine<SerializableFp>) -> Result<Self, Self::Error> {
Ok(Self {
inner: Affine::try_from(value)?.into(),
})
}
}
impl fmt::Debug for G1 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("G1")
@@ -212,6 +252,7 @@ mod g1 {
mod g2 {
use super::*;
use crate::serialization::InvalidSerializedAffineError;
#[derive(
Copy,
@@ -225,12 +266,31 @@ mod g2 {
CanonicalSerialize,
CanonicalDeserialize,
)]
#[serde(
try_from = "SerializableAffine<SerializableFp2>",
into = "SerializableAffine<SerializableFp2>"
)]
#[repr(transparent)]
pub struct G2Affine {
#[serde(serialize_with = "ark_se", deserialize_with = "ark_de")]
pub(crate) inner: crate::curve_446::g2::G2Affine,
}
impl From<G2Affine> for SerializableAffine<SerializableFp2> {
fn from(value: G2Affine) -> Self {
SerializableAffine::compressed(value.inner)
}
}
impl TryFrom<SerializableAffine<SerializableFp2>> for G2Affine {
type Error = InvalidSerializedAffineError;
fn try_from(value: SerializableAffine<SerializableFp2>) -> Result<Self, Self::Error> {
Ok(Self {
inner: value.try_into()?,
})
}
}
impl G2Affine {
#[track_caller]
pub fn multi_mul_scalar(bases: &[Self], scalars: &[Zp]) -> G2 {
@@ -348,12 +408,31 @@ mod g2 {
CanonicalSerialize,
CanonicalDeserialize,
)]
#[serde(
try_from = "SerializableAffine<SerializableFp2>",
into = "SerializableAffine<SerializableFp2>"
)]
#[repr(transparent)]
pub struct G2 {
#[serde(serialize_with = "ark_se", deserialize_with = "ark_de")]
pub(crate) inner: crate::curve_446::g2::G2Projective,
}
impl From<G2> for SerializableAffine<SerializableFp2> {
fn from(value: G2) -> Self {
SerializableAffine::compressed(value.inner.into_affine())
}
}
impl TryFrom<SerializableAffine<SerializableFp2>> for G2 {
type Error = InvalidSerializedAffineError;
fn try_from(value: SerializableAffine<SerializableFp2>) -> Result<Self, Self::Error> {
Ok(Self {
inner: Affine::try_from(value)?.into(),
})
}
}
impl fmt::Debug for G2 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[allow(dead_code)]
@@ -525,6 +604,7 @@ mod g2 {
mod gt {
use crate::curve_446::{Fq, Fq12, Fq2};
use crate::serialization::InvalidSerializedAffineError;
use super::*;
use ark_ec::pairing::{MillerLoopOutput, Pairing};
@@ -699,12 +779,28 @@ mod gt {
}
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
#[serde(try_from = "SerializableFp12", into = "SerializableFp12")]
#[repr(transparent)]
pub struct Gt {
#[serde(serialize_with = "ark_se", deserialize_with = "ark_de")]
pub(crate) inner: ark_ec::pairing::PairingOutput<crate::curve_446::Bls12_446>,
}
impl From<Gt> for SerializableFp12 {
fn from(value: Gt) -> Self {
value.inner.0.into()
}
}
impl TryFrom<SerializableFp12> for Gt {
type Error = InvalidSerializedAffineError;
fn try_from(value: SerializableFp12) -> Result<Self, Self::Error> {
Ok(Self {
inner: PairingOutput(value.try_into()?),
})
}
}
impl fmt::Debug for Gt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[allow(dead_code)]
@@ -827,6 +923,7 @@ mod gt {
mod zp {
use super::*;
use crate::serialization::InvalidArraySizeError;
use ark_ff::Fp;
use zeroize::Zeroize;
@@ -865,12 +962,27 @@ mod zp {
}
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, Zeroize)]
#[serde(try_from = "SerializableFp", into = "SerializableFp")]
#[repr(transparent)]
pub struct Zp {
#[serde(serialize_with = "ark_se", deserialize_with = "ark_de")]
pub inner: crate::curve_446::Fr,
}
impl From<Zp> for SerializableFp {
fn from(value: Zp) -> Self {
value.inner.into()
}
}
impl TryFrom<SerializableFp> for Zp {
type Error = InvalidArraySizeError;
fn try_from(value: SerializableFp) -> Result<Self, Self::Error> {
Ok(Self {
inner: value.try_into()?,
})
}
}
impl fmt::Debug for Zp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Zp")

View File

@@ -3,5 +3,6 @@ pub use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Compress, Vali
pub mod curve_446;
pub mod curve_api;
pub mod proofs;
pub mod serialization;
mod four_squares;

View File

@@ -0,0 +1,231 @@
use std::error::Error;
use std::fmt::Display;
use std::marker::PhantomData;
use ark_ec::short_weierstrass::{Affine, SWCurveConfig};
use ark_ec::AffineRepr;
use ark_ff::{BigInt, Field, Fp, Fp2, Fp6, Fp6Config, FpConfig, QuadExtConfig, QuadExtField};
use serde::{Deserialize, Serialize};
/// Error returned when a conversion from a vec to a fixed size array failed because the vec size is
/// incorrect
#[derive(Debug)]
pub struct InvalidArraySizeError {
expected_len: usize,
found_len: usize,
}
impl Display for InvalidArraySizeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Invalid serialized array: found array of size {}, expected {}",
self.found_len, self.expected_len
)
}
}
impl Error for InvalidArraySizeError {}
/// Tries to convert a Vec into a constant size array, and returns an [`InvalidArraySizeError`] if
/// the size does not match
fn try_vec_to_array<T, const N: usize>(vec: Vec<T>) -> Result<[T; N], InvalidArraySizeError> {
let len = vec.len();
vec.try_into().map_err(|_| InvalidArraySizeError {
expected_len: len,
found_len: N,
})
}
/// Serialization equivalent of the [`Fp`] struct, where the bigint is split into
/// multiple u64.
#[derive(Serialize, Deserialize)]
pub struct SerializableFp {
val: Vec<u64>, // Use a Vec<u64> since serde does not support fixed size arrays with a generic
}
impl<P: FpConfig<N>, const N: usize> From<Fp<P, N>> for SerializableFp {
fn from(value: Fp<P, N>) -> Self {
Self {
val: value.0 .0.to_vec(),
}
}
}
impl<P: FpConfig<N>, const N: usize> TryFrom<SerializableFp> for Fp<P, N> {
type Error = InvalidArraySizeError;
fn try_from(value: SerializableFp) -> Result<Self, Self::Error> {
Ok(Fp(BigInt(try_vec_to_array(value.val)?), PhantomData))
}
}
#[derive(Debug)]
pub enum InvalidSerializedAffineError {
InvalidFp(InvalidArraySizeError),
InvalidCompressedXCoordinate,
}
impl Display for InvalidSerializedAffineError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
InvalidSerializedAffineError::InvalidFp(fp_error) => {
write!(f, "Invalid fp element in affine: {}", fp_error)
}
InvalidSerializedAffineError::InvalidCompressedXCoordinate => {
write!(
f,
"Cannot uncompress affine: X coordinate does not belong to the curve"
)
}
}
}
}
impl Error for InvalidSerializedAffineError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
InvalidSerializedAffineError::InvalidFp(fp_error) => Some(fp_error),
InvalidSerializedAffineError::InvalidCompressedXCoordinate => None,
}
}
}
impl From<InvalidArraySizeError> for InvalidSerializedAffineError {
fn from(value: InvalidArraySizeError) -> Self {
Self::InvalidFp(value)
}
}
/// Serialization equivalent to the [`Affine`], which support an optional compression mode
/// where only the `x` coordinate is stored, and the `y` is computed on load.
#[derive(Serialize, Deserialize)]
pub(crate) enum SerializableAffine<F> {
Infinity,
Compressed { x: F, take_largest_y: bool },
Uncompressed { x: F, y: F },
}
impl<F> SerializableAffine<F> {
#[allow(unused)]
pub fn uncompressed<BaseField: Into<F> + Field, C: SWCurveConfig<BaseField = BaseField>>(
value: Affine<C>,
) -> Self {
if value.is_zero() {
Self::Infinity
} else {
Self::Uncompressed {
x: value.x.into(),
y: value.y.into(),
}
}
}
pub fn compressed<BaseField: Into<F> + Field, C: SWCurveConfig<BaseField = BaseField>>(
value: Affine<C>,
) -> Self {
if value.is_zero() {
Self::Infinity
} else {
let take_largest_y = value.y > -value.y;
Self::Compressed {
x: value.x.into(),
take_largest_y,
}
}
}
}
impl<F, C: SWCurveConfig> TryFrom<SerializableAffine<F>> for Affine<C>
where
F: TryInto<C::BaseField, Error = InvalidArraySizeError>,
{
type Error = InvalidSerializedAffineError;
fn try_from(value: SerializableAffine<F>) -> Result<Self, Self::Error> {
match value {
SerializableAffine::Infinity => Ok(Self::zero()),
SerializableAffine::Compressed { x, take_largest_y } => {
Self::get_point_from_x_unchecked(x.try_into()?, take_largest_y)
.ok_or(InvalidSerializedAffineError::InvalidCompressedXCoordinate)
}
SerializableAffine::Uncompressed { x, y } => {
Ok(Self::new_unchecked(x.try_into()?, y.try_into()?))
}
}
}
}
#[derive(Serialize, Deserialize)]
pub(crate) struct SerializableQuadExtField<F> {
c0: F,
c1: F,
}
pub(crate) type SerializableFp2 = SerializableQuadExtField<SerializableFp>;
impl<F, P: QuadExtConfig> From<QuadExtField<P>> for SerializableQuadExtField<F>
where
F: From<P::BaseField>,
{
fn from(value: QuadExtField<P>) -> Self {
Self {
c0: value.c0.into(),
c1: value.c1.into(),
}
}
}
impl<F, P: QuadExtConfig> TryFrom<SerializableQuadExtField<F>> for QuadExtField<P>
where
F: TryInto<P::BaseField, Error = InvalidArraySizeError>,
{
type Error = InvalidArraySizeError;
fn try_from(value: SerializableQuadExtField<F>) -> Result<Self, Self::Error> {
Ok(QuadExtField {
c0: value.c0.try_into()?,
c1: value.c1.try_into()?,
})
}
}
#[derive(Serialize, Deserialize)]
pub(crate) struct SerializableCubicExtField<F> {
c0: F,
c1: F,
c2: F,
}
type SerializableFp6 = SerializableCubicExtField<SerializableFp2>;
impl<F, P6: Fp6Config> From<Fp6<P6>> for SerializableCubicExtField<F>
where
F: From<Fp2<P6::Fp2Config>>,
{
fn from(value: Fp6<P6>) -> Self {
Self {
c0: value.c0.into(),
c1: value.c1.into(),
c2: value.c2.into(),
}
}
}
impl<F, P6: Fp6Config> TryFrom<SerializableCubicExtField<F>> for Fp6<P6>
where
F: TryInto<Fp2<P6::Fp2Config>, Error = InvalidArraySizeError>,
{
type Error = InvalidArraySizeError;
fn try_from(value: SerializableCubicExtField<F>) -> Result<Self, Self::Error> {
Ok(Fp6 {
c0: value.c0.try_into()?,
c1: value.c1.try_into()?,
c2: value.c2.try_into()?,
})
}
}
pub(crate) type SerializableFp12 = SerializableQuadExtField<SerializableFp6>;