mirror of
https://github.com/serai-dex/serai.git
synced 2024-11-16 17:07:35 +00:00
Clean AOS signatures
This commit is contained in:
parent
fd817a6958
commit
9f8d1aa220
3 changed files with 191 additions and 181 deletions
|
@ -12,8 +12,6 @@ rand_core = "0.6"
|
|||
|
||||
digest = "0.10"
|
||||
|
||||
subtle = "2.4"
|
||||
|
||||
transcript = { package = "flexible-transcript", path = "../transcript", version = "0.1" }
|
||||
|
||||
ff = "0.12"
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
use rand_core::{RngCore, CryptoRng};
|
||||
|
||||
use subtle::{ConstantTimeEq, ConditionallySelectable};
|
||||
|
||||
use transcript::Transcript;
|
||||
|
||||
use group::{ff::{Field, PrimeFieldBits}, prime::PrimeGroup};
|
||||
|
@ -10,7 +8,7 @@ use multiexp::BatchVerifier;
|
|||
|
||||
use crate::{
|
||||
Generators,
|
||||
cross_group::{DLEqError, scalar::{scalar_convert, mutual_scalar_from_bytes}, bits::RingSignature}
|
||||
cross_group::{DLEqError, scalar::{scalar_convert, mutual_scalar_from_bytes}}
|
||||
};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
|
@ -20,48 +18,33 @@ use ff::PrimeField;
|
|||
#[cfg(feature = "serialize")]
|
||||
use crate::{read_scalar, cross_group::read_point};
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn nonces<
|
||||
T: Transcript,
|
||||
G0: PrimeGroup,
|
||||
G1: PrimeGroup
|
||||
>(mut transcript: T, nonces: (G0, G1)) -> (G0::Scalar, G1::Scalar)
|
||||
where G0::Scalar: PrimeFieldBits, G1::Scalar: PrimeFieldBits {
|
||||
transcript.append_message(b"nonce_0", nonces.0.to_bytes().as_ref());
|
||||
transcript.append_message(b"nonce_1", nonces.1.to_bytes().as_ref());
|
||||
mutual_scalar_from_bytes(transcript.challenge(b"challenge").as_ref())
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn calculate_R<G0: PrimeGroup, G1: PrimeGroup>(
|
||||
generators: (Generators<G0>, Generators<G1>),
|
||||
s: (G0::Scalar, G1::Scalar),
|
||||
A: (G0, G1),
|
||||
e: (G0::Scalar, G1::Scalar)
|
||||
) -> (G0, G1) {
|
||||
(((generators.0.alt * s.0) - (A.0 * e.0)), ((generators.1.alt * s.1) - (A.1 * e.1)))
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn R_nonces<T: Transcript, G0: PrimeGroup, G1: PrimeGroup>(
|
||||
transcript: T,
|
||||
generators: (Generators<G0>, Generators<G1>),
|
||||
s: (G0::Scalar, G1::Scalar),
|
||||
A: (G0, G1),
|
||||
e: (G0::Scalar, G1::Scalar)
|
||||
) -> (G0::Scalar, G1::Scalar) where G0::Scalar: PrimeFieldBits, G1::Scalar: PrimeFieldBits {
|
||||
nonces(transcript, calculate_R(generators, s, A, e))
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct ClassicAos<G0: PrimeGroup, G1: PrimeGroup, const RING_LEN: usize> {
|
||||
pub(crate) enum Re<G0: PrimeGroup, G1: PrimeGroup> {
|
||||
R(G0, G1),
|
||||
// Merged challenges have a slight security reduction, yet one already applied to the scalar
|
||||
// being proven for, and this saves ~8kb. Alternatively, challenges could be redefined as a seed,
|
||||
// present here, which is then hashed for each of the two challenges, remaining unbiased/unique
|
||||
// while maintaining the bandwidth savings, yet also while adding 252 hashes for
|
||||
// Secp256k1/Ed25519
|
||||
e_0: G0::Scalar,
|
||||
e(G0::Scalar)
|
||||
}
|
||||
|
||||
impl<G0: PrimeGroup, G1: PrimeGroup> Re<G0, G1> {
|
||||
#[allow(non_snake_case)]
|
||||
pub(crate) fn R_default() -> Re<G0, G1> {
|
||||
Re::R(G0::identity(), G1::identity())
|
||||
}
|
||||
|
||||
pub(crate) fn e_default() -> Re<G0, G1> {
|
||||
Re::e(G0::Scalar::zero())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub(crate) struct Aos<G0: PrimeGroup, G1: PrimeGroup, const RING_LEN: usize> {
|
||||
Re_0: Re<G0, G1>,
|
||||
s: [(G0::Scalar, G1::Scalar); RING_LEN]
|
||||
}
|
||||
|
||||
|
@ -69,106 +52,24 @@ impl<
|
|||
G0: PrimeGroup,
|
||||
G1: PrimeGroup,
|
||||
const RING_LEN: usize
|
||||
> RingSignature<G0, G1> for ClassicAos<G0, G1, RING_LEN>
|
||||
where G0::Scalar: PrimeFieldBits, G1::Scalar: PrimeFieldBits {
|
||||
type Context = ();
|
||||
> Aos<G0, G1, RING_LEN> where G0::Scalar: PrimeFieldBits, G1::Scalar: PrimeFieldBits {
|
||||
#[allow(non_snake_case)]
|
||||
fn nonces<T: Transcript>(mut transcript: T, nonces: (G0, G1)) -> (G0::Scalar, G1::Scalar) {
|
||||
transcript.append_message(b"nonce_0", nonces.0.to_bytes().as_ref());
|
||||
transcript.append_message(b"nonce_1", nonces.1.to_bytes().as_ref());
|
||||
mutual_scalar_from_bytes(transcript.challenge(b"challenge").as_ref())
|
||||
}
|
||||
|
||||
const LEN: usize = RING_LEN;
|
||||
|
||||
fn prove<R: RngCore + CryptoRng, T: Clone + Transcript>(
|
||||
rng: &mut R,
|
||||
transcript: T,
|
||||
#[allow(non_snake_case)]
|
||||
fn R(
|
||||
generators: (Generators<G0>, Generators<G1>),
|
||||
ring: &[(G0, G1)],
|
||||
actual: usize,
|
||||
blinding_key: (G0::Scalar, G1::Scalar)
|
||||
) -> Self {
|
||||
// While it is possible to use larger values, it's not efficient to do so
|
||||
// 2 + 2 == 2^2, yet 2 + 2 + 2 < 2^3
|
||||
debug_assert!((RING_LEN == 2) || (RING_LEN == 4));
|
||||
|
||||
let mut e_0 = G0::Scalar::zero();
|
||||
let mut s = [(G0::Scalar::zero(), G1::Scalar::zero()); RING_LEN];
|
||||
|
||||
let r = (G0::Scalar::random(&mut *rng), G1::Scalar::random(&mut *rng));
|
||||
#[allow(non_snake_case)]
|
||||
let original_R = (generators.0.alt * r.0, generators.1.alt * r.1);
|
||||
#[allow(non_snake_case)]
|
||||
let mut R = original_R;
|
||||
|
||||
for i in ((actual + 1) .. (actual + RING_LEN + 1)).map(|i| i % RING_LEN) {
|
||||
let e = nonces(transcript.clone(), R);
|
||||
e_0 = G0::Scalar::conditional_select(&e_0, &e.0, usize::ct_eq(&i, &0));
|
||||
|
||||
// Solve for the real index
|
||||
if i == actual {
|
||||
s[i] = (r.0 + (e.0 * blinding_key.0), r.1 + (e.1 * blinding_key.1));
|
||||
debug_assert_eq!(calculate_R(generators, s[i], ring[actual], e), original_R);
|
||||
break;
|
||||
// Generate a decoy response
|
||||
} else {
|
||||
s[i] = (G0::Scalar::random(&mut *rng), G1::Scalar::random(&mut *rng));
|
||||
}
|
||||
|
||||
R = calculate_R(generators, s[i], ring[i], e);
|
||||
}
|
||||
|
||||
ClassicAos { e_0, s }
|
||||
s: (G0::Scalar, G1::Scalar),
|
||||
A: (G0, G1),
|
||||
e: (G0::Scalar, G1::Scalar)
|
||||
) -> (G0, G1) {
|
||||
(((generators.0.alt * s.0) - (A.0 * e.0)), ((generators.1.alt * s.1) - (A.1 * e.1)))
|
||||
}
|
||||
|
||||
fn verify<R: RngCore + CryptoRng, T: Clone + Transcript>(
|
||||
&self,
|
||||
_rng: &mut R,
|
||||
transcript: T,
|
||||
generators: (Generators<G0>, Generators<G1>),
|
||||
_: &mut Self::Context,
|
||||
ring: &[(G0, G1)]
|
||||
) -> Result<(), DLEqError> {
|
||||
debug_assert!((RING_LEN == 2) || (RING_LEN == 4));
|
||||
|
||||
let e_0 = (self.e_0, scalar_convert(self.e_0).ok_or(DLEqError::InvalidChallenge)?);
|
||||
let mut e = None;
|
||||
for i in 0 .. RING_LEN {
|
||||
e = Some(R_nonces(transcript.clone(), generators, self.s[i], ring[i], e.unwrap_or(e_0)));
|
||||
}
|
||||
|
||||
// Will panic if the above loop is never run somehow
|
||||
// If e wasn't an Option, and instead initially set to e_0, it'd always pass
|
||||
if e_0 != e.unwrap() {
|
||||
Err(DLEqError::InvalidProof)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
fn serialize<W: Write>(&self, w: &mut W) -> std::io::Result<()> {
|
||||
w.write_all(self.e_0.to_repr().as_ref())?;
|
||||
for i in 0 .. Self::LEN {
|
||||
w.write_all(self.s[i].0.to_repr().as_ref())?;
|
||||
w.write_all(self.s[i].1.to_repr().as_ref())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
fn deserialize<R: Read>(r: &mut R) -> std::io::Result<Self> {
|
||||
let e_0 = read_scalar(r)?;
|
||||
let mut s = [(G0::Scalar::zero(), G1::Scalar::zero()); RING_LEN];
|
||||
for i in 0 .. Self::LEN {
|
||||
s[i] = (read_scalar(r)?, read_scalar(r)?);
|
||||
}
|
||||
Ok(ClassicAos { e_0, s })
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct MultiexpAos<G0: PrimeGroup, G1: PrimeGroup> {
|
||||
R_0: (G0, G1),
|
||||
s: [(G0::Scalar, G1::Scalar); 2]
|
||||
}
|
||||
|
||||
impl<G0: PrimeGroup, G1: PrimeGroup> MultiexpAos<G0, G1> {
|
||||
#[allow(non_snake_case)]
|
||||
fn R_batch(
|
||||
generators: (Generators<G0>, Generators<G1>),
|
||||
|
@ -178,25 +79,34 @@ impl<G0: PrimeGroup, G1: PrimeGroup> MultiexpAos<G0, G1> {
|
|||
) -> (Vec<(G0::Scalar, G0)>, Vec<(G1::Scalar, G1)>) {
|
||||
(vec![(s.0, generators.0.alt), (-e.0, A.0)], vec![(s.1, generators.1.alt), (-e.1, A.1)])
|
||||
}
|
||||
}
|
||||
|
||||
impl<G0: PrimeGroup, G1: PrimeGroup> RingSignature<G0, G1> for MultiexpAos<G0, G1>
|
||||
where G0::Scalar: PrimeFieldBits, G1::Scalar: PrimeFieldBits {
|
||||
type Context = (BatchVerifier<(), G0>, BatchVerifier<(), G1>);
|
||||
#[allow(non_snake_case)]
|
||||
fn R_nonces<T: Transcript>(
|
||||
transcript: T,
|
||||
generators: (Generators<G0>, Generators<G1>),
|
||||
s: (G0::Scalar, G1::Scalar),
|
||||
A: (G0, G1),
|
||||
e: (G0::Scalar, G1::Scalar)
|
||||
) -> (G0::Scalar, G1::Scalar) {
|
||||
Self::nonces(transcript, Self::R(generators, s, A, e))
|
||||
}
|
||||
|
||||
const LEN: usize = 2;
|
||||
|
||||
fn prove<R: RngCore + CryptoRng, T: Clone + Transcript>(
|
||||
#[allow(non_snake_case)]
|
||||
pub(crate) fn prove<R: RngCore + CryptoRng, T: Clone + Transcript>(
|
||||
rng: &mut R,
|
||||
transcript: T,
|
||||
generators: (Generators<G0>, Generators<G1>),
|
||||
ring: &[(G0, G1)],
|
||||
actual: usize,
|
||||
blinding_key: (G0::Scalar, G1::Scalar)
|
||||
blinding_key: (G0::Scalar, G1::Scalar),
|
||||
mut Re_0: Re<G0, G1>
|
||||
) -> Self {
|
||||
#[allow(non_snake_case)]
|
||||
let mut R_0 = (G0::identity(), G1::identity());
|
||||
let mut s = [(G0::Scalar::zero(), G1::Scalar::zero()); 2]; // Can't use Self::LEN due to 76200
|
||||
// While it is possible to use larger values, it's not efficient to do so
|
||||
// 2 + 2 == 2^2, yet 2 + 2 + 2 < 2^3
|
||||
debug_assert!((RING_LEN == 2) || (RING_LEN == 4));
|
||||
debug_assert_eq!(RING_LEN, ring.len());
|
||||
|
||||
let mut s = [(G0::Scalar::zero(), G1::Scalar::zero()); RING_LEN];
|
||||
|
||||
let r = (G0::Scalar::random(&mut *rng), G1::Scalar::random(&mut *rng));
|
||||
#[allow(non_snake_case)]
|
||||
|
@ -204,75 +114,112 @@ impl<G0: PrimeGroup, G1: PrimeGroup> RingSignature<G0, G1> for MultiexpAos<G0, G
|
|||
#[allow(non_snake_case)]
|
||||
let mut R = original_R;
|
||||
|
||||
for i in ((actual + 1) .. (actual + Self::LEN + 1)).map(|i| i % Self::LEN) {
|
||||
for i in ((actual + 1) .. (actual + RING_LEN + 1)).map(|i| i % RING_LEN) {
|
||||
let e = Self::nonces(transcript.clone(), R);
|
||||
if i == 0 {
|
||||
R_0.0 = R.0;
|
||||
R_0.1 = R.1;
|
||||
match Re_0 {
|
||||
Re::R(ref mut R0_0, ref mut R1_0) => { *R0_0 = R.0; *R1_0 = R.1 },
|
||||
Re::e(ref mut e_0) => *e_0 = e.0
|
||||
}
|
||||
}
|
||||
|
||||
// Solve for the real index
|
||||
let e = nonces(transcript.clone(), R);
|
||||
if i == actual {
|
||||
s[i] = (r.0 + (e.0 * blinding_key.0), r.1 + (e.1 * blinding_key.1));
|
||||
debug_assert_eq!(calculate_R(generators, s[i], ring[actual], e), original_R);
|
||||
debug_assert_eq!(Self::R(generators, s[i], ring[actual], e), original_R);
|
||||
break;
|
||||
// Generate a decoy response
|
||||
} else {
|
||||
s[i] = (G0::Scalar::random(&mut *rng), G1::Scalar::random(&mut *rng));
|
||||
}
|
||||
|
||||
R = calculate_R(generators, s[i], ring[i], e);
|
||||
R = Self::R(generators, s[i], ring[i], e);
|
||||
}
|
||||
|
||||
MultiexpAos { R_0, s }
|
||||
Aos { Re_0, s }
|
||||
}
|
||||
|
||||
fn verify<R: RngCore + CryptoRng, T: Clone + Transcript>(
|
||||
// Assumes the ring has already been transcripted in some form. Critically insecure if it hasn't
|
||||
pub(crate) fn verify<R: RngCore + CryptoRng, T: Clone + Transcript>(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
transcript: T,
|
||||
generators: (Generators<G0>, Generators<G1>),
|
||||
batch: &mut Self::Context,
|
||||
batch: &mut (BatchVerifier<(), G0>, BatchVerifier<(), G1>),
|
||||
ring: &[(G0, G1)]
|
||||
) -> Result<(), DLEqError> {
|
||||
let mut e = nonces(transcript.clone(), self.R_0);
|
||||
for i in 0 .. (Self::LEN - 1) {
|
||||
e = R_nonces(transcript.clone(), generators, self.s[i], ring[i], e);
|
||||
}
|
||||
debug_assert!((RING_LEN == 2) || (RING_LEN == 4));
|
||||
debug_assert_eq!(RING_LEN, ring.len());
|
||||
|
||||
let mut statements = Self::R_batch(
|
||||
generators,
|
||||
*self.s.last().unwrap(),
|
||||
*ring.last().unwrap(),
|
||||
e
|
||||
);
|
||||
statements.0.push((-G0::Scalar::one(), self.R_0.0));
|
||||
statements.1.push((-G1::Scalar::one(), self.R_0.1));
|
||||
batch.0.queue(&mut *rng, (), statements.0);
|
||||
batch.1.queue(&mut *rng, (), statements.1);
|
||||
match self.Re_0 {
|
||||
Re::R(R0_0, R1_0) => {
|
||||
let mut e = Self::nonces(transcript.clone(), (R0_0, R1_0));
|
||||
for i in 0 .. (RING_LEN - 1) {
|
||||
e = Self::R_nonces(transcript.clone(), generators, self.s[i], ring[i], e);
|
||||
}
|
||||
|
||||
let mut statements = Self::R_batch(
|
||||
generators,
|
||||
*self.s.last().unwrap(),
|
||||
*ring.last().unwrap(),
|
||||
e
|
||||
);
|
||||
statements.0.push((-G0::Scalar::one(), R0_0));
|
||||
statements.1.push((-G1::Scalar::one(), R1_0));
|
||||
batch.0.queue(&mut *rng, (), statements.0);
|
||||
batch.1.queue(&mut *rng, (), statements.1);
|
||||
},
|
||||
|
||||
Re::e(e_0) => {
|
||||
let e_0 = (e_0, scalar_convert(e_0).ok_or(DLEqError::InvalidChallenge)?);
|
||||
let mut e = None;
|
||||
for i in 0 .. RING_LEN {
|
||||
e = Some(
|
||||
Self::R_nonces(transcript.clone(), generators, self.s[i], ring[i], e.unwrap_or(e_0))
|
||||
);
|
||||
}
|
||||
|
||||
// Will panic if the above loop is never run somehow
|
||||
// If e wasn't an Option, and instead initially set to e_0, it'd always pass
|
||||
if e_0 != e.unwrap() {
|
||||
Err(DLEqError::InvalidProof)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
fn serialize<W: Write>(&self, w: &mut W) -> std::io::Result<()> {
|
||||
w.write_all(self.R_0.0.to_bytes().as_ref())?;
|
||||
w.write_all(self.R_0.1.to_bytes().as_ref())?;
|
||||
for i in 0 .. Self::LEN {
|
||||
pub(crate) fn serialize<W: Write>(&self, w: &mut W) -> std::io::Result<()> {
|
||||
match self.Re_0 {
|
||||
Re::R(R0, R1) => {
|
||||
w.write_all(R0.to_bytes().as_ref())?;
|
||||
w.write_all(R1.to_bytes().as_ref())?;
|
||||
},
|
||||
Re::e(e) => w.write_all(e.to_repr().as_ref())?
|
||||
}
|
||||
|
||||
for i in 0 .. RING_LEN {
|
||||
w.write_all(self.s[i].0.to_repr().as_ref())?;
|
||||
w.write_all(self.s[i].1.to_repr().as_ref())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
fn deserialize<R: Read>(r: &mut R) -> std::io::Result<Self> {
|
||||
#[allow(non_snake_case)]
|
||||
let R_0 = (read_point(r)?, read_point(r)?);
|
||||
let mut s = [(G0::Scalar::zero(), G1::Scalar::zero()); 2];
|
||||
for i in 0 .. Self::LEN {
|
||||
pub(crate) fn deserialize<R: Read>(r: &mut R, mut Re_0: Re<G0, G1>) -> std::io::Result<Self> {
|
||||
match Re_0 {
|
||||
Re::R(ref mut R0, ref mut R1) => { *R0 = read_point(r)?; *R1 = read_point(r)? },
|
||||
Re::e(ref mut e) => *e = read_scalar(r)?
|
||||
}
|
||||
|
||||
let mut s = [(G0::Scalar::zero(), G1::Scalar::zero()); RING_LEN];
|
||||
for i in 0 .. RING_LEN {
|
||||
s[i] = (read_scalar(r)?, read_scalar(r)?);
|
||||
}
|
||||
Ok(MultiexpAos { R_0, s })
|
||||
|
||||
Ok(Aos { Re_0, s })
|
||||
}
|
||||
}
|
||||
|
|
65
crypto/dleq/src/tests/cross_group/linear/aos.rs
Normal file
65
crypto/dleq/src/tests/cross_group/linear/aos.rs
Normal file
|
@ -0,0 +1,65 @@
|
|||
use rand_core::OsRng;
|
||||
|
||||
use group::{ff::Field, Group};
|
||||
|
||||
use multiexp::BatchVerifier;
|
||||
|
||||
use crate::{
|
||||
cross_group::linear::aos::{Re, Aos},
|
||||
tests::cross_group::{G0, G1, transcript, generators}
|
||||
};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
fn test_aos_serialization<const RING_LEN: usize>(proof: Aos<G0, G1, RING_LEN>, Re_0: Re<G0, G1>) {
|
||||
let mut buf = vec![];
|
||||
proof.serialize(&mut buf).unwrap();
|
||||
let deserialized = Aos::deserialize(&mut std::io::Cursor::new(buf), Re_0).unwrap();
|
||||
assert_eq!(proof, deserialized);
|
||||
}
|
||||
|
||||
fn test_aos<const RING_LEN: usize>(default: Re<G0, G1>) {
|
||||
let generators = generators();
|
||||
|
||||
let mut ring_keys = [(<G0 as Group>::Scalar::zero(), <G1 as Group>::Scalar::zero()); RING_LEN];
|
||||
let mut ring = [(G0::identity(), G1::identity()); RING_LEN];
|
||||
for i in 0 .. RING_LEN {
|
||||
ring_keys[i] = (
|
||||
<G0 as Group>::Scalar::random(&mut OsRng),
|
||||
<G1 as Group>::Scalar::random(&mut OsRng)
|
||||
);
|
||||
ring[i] = (generators.0.alt * ring_keys[i].0, generators.1.alt * ring_keys[i].1);
|
||||
}
|
||||
|
||||
for actual in 0 .. RING_LEN {
|
||||
let proof = Aos::<_, _, RING_LEN>::prove(
|
||||
&mut OsRng,
|
||||
transcript(),
|
||||
generators,
|
||||
&ring,
|
||||
actual,
|
||||
ring_keys[actual],
|
||||
default.clone()
|
||||
);
|
||||
|
||||
let mut batch = (BatchVerifier::new(0), BatchVerifier::new(0));
|
||||
proof.verify(&mut OsRng, transcript(), generators, &mut batch, &ring).unwrap();
|
||||
// For e, these should have nothing. For R, these should have 6 elements each which sum to 0
|
||||
assert!(batch.0.verify_vartime());
|
||||
assert!(batch.1.verify_vartime());
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
test_aos_serialization(proof, default.clone());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_aos_e() {
|
||||
test_aos::<2>(Re::e_default());
|
||||
test_aos::<4>(Re::e_default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_aos_R() {
|
||||
// Batch verification appreciates the longer vectors, which means not batching bits
|
||||
test_aos::<2>(Re::R_default());
|
||||
}
|
Loading…
Reference in a new issue