Add Classic/Compromise DLEqs and a benchmark

Formatted results from my laptop:

EfficientLinear had a average prove time of 188ms
EfficientLinear had a average verify time of 126ms

CompromiseLinear had a average prove time of 176ms
CompromiseLinear had a average verify time of 141ms

ConciseLinear had a average prove time of 191ms
ConciseLinear had a average verify time of 160ms

ClassicLinear had a average prove time of 214ms
ClassicLinear had a average verify time of 159ms

There is a decent error margin here. Concise is a drop-in replacement 
for Classic, in practice *not* theory. Efficient is optimal for 
performance, yet largest. Compromise is a middleground.
This commit is contained in:
Luke Parker
2022-07-07 08:26:59 -04:00
parent 1a2e6dc5cf
commit 44e0a41ca1
4 changed files with 105 additions and 35 deletions

View File

@@ -13,30 +13,38 @@ use std::io::{Read, Write};
use crate::cross_group::read_point;
pub(crate) enum BitSignature {
ClassicLinear,
ConciseLinear,
EfficientLinear
EfficientLinear,
CompromiseLinear
}
impl BitSignature {
pub(crate) const fn to_u8(&self) -> u8 {
match self {
BitSignature::ConciseLinear => 0,
BitSignature::EfficientLinear => 1
BitSignature::ClassicLinear => 0,
BitSignature::ConciseLinear => 1,
BitSignature::EfficientLinear => 2,
BitSignature::CompromiseLinear => 3
}
}
pub(crate) const fn from(algorithm: u8) -> BitSignature {
match algorithm {
0 => BitSignature::ConciseLinear,
1 => BitSignature::EfficientLinear,
0 => BitSignature::ClassicLinear,
1 => BitSignature::ConciseLinear,
2 => BitSignature::EfficientLinear,
3 => BitSignature::CompromiseLinear,
_ => panic!("Unknown algorithm")
}
}
pub(crate) const fn bits(&self) -> usize {
match self {
BitSignature::ClassicLinear => 1,
BitSignature::ConciseLinear => 2,
BitSignature::EfficientLinear => 1
BitSignature::EfficientLinear => 1,
BitSignature::CompromiseLinear => 2
}
}
@@ -46,8 +54,10 @@ impl BitSignature {
fn aos_form<G0: PrimeGroup, G1: PrimeGroup>(&self) -> Re<G0, G1> {
match self {
BitSignature::ClassicLinear => Re::e_default(),
BitSignature::ConciseLinear => Re::e_default(),
BitSignature::EfficientLinear => Re::R_default()
BitSignature::EfficientLinear => Re::R_default(),
BitSignature::CompromiseLinear => Re::R_default()
}
}
}

View File

@@ -62,23 +62,39 @@ pub struct DLEqProof<
poks: (SchnorrPoK<G0>, SchnorrPoK<G1>)
}
pub type ConciseLinearDLEq<G0, G1> = DLEqProof<
G0,
G1,
{ BitSignature::ConciseLinear.to_u8() },
{ BitSignature::ConciseLinear.ring_len() },
// There may not be a remainder, yet if there is, it'll be just one bit
// A ring for one bit has a RING_LEN of 2
2
>;
macro_rules! dleq {
($name: ident, $signature: expr, $remainder: expr) => {
pub type $name<G0, G1> = DLEqProof<
G0,
G1,
{ $signature.to_u8() },
{ $signature.ring_len() },
// There may not be a remainder, yet if there is one, it'll be just one bit
// A ring for one bit has a RING_LEN of 2
{ if $remainder { 2 } else { 0 } }
>;
}
}
pub type EfficientLinearDLEq<G0, G1> = DLEqProof<
G0,
G1,
{ BitSignature::EfficientLinear.to_u8() },
{ BitSignature::EfficientLinear.ring_len() },
0
>;
// Proves for 1-bit at a time with the signature form (e, s), as originally described in MRL-0010.
// Uses a merged challenge, unlike MRL-0010, for the ring signature, saving an element from each
// bit and removing a hash while slightly reducing challenge security. This security reduction is
// already applied to the scalar being proven for, a result of the requirement it's mutually valid
// over both scalar fields, hence its application here as well. This is mainly here as a point of
// reference for the following DLEq proofs, all which use merged challenges
dleq!(ClassicLinearDLEq, BitSignature::ClassicLinear, false);
// Proves for 2-bits at a time to save 3/7 elements of every other bit
dleq!(ConciseLinearDLEq, BitSignature::ConciseLinear, true);
// Uses AOS signatures of the form R, s, to enable the final step of the ring signature to be
// batch verified, at the cost of adding an additional element per bit
dleq!(EfficientLinearDLEq, BitSignature::EfficientLinear, false);
// Proves for 2-bits at a time while using the R, s form. This saves 3/7 elements of every other
// bit, while adding 1 element to every bit, and is more efficient than ConciseLinear yet less
// efficient than EfficientLinear due to having more ring signature steps which aren't batched
dleq!(CompromiseLinearDLEq, BitSignature::CompromiseLinear, true);
impl<
G0: PrimeGroup,
@@ -279,8 +295,10 @@ impl<
Self::transcript(transcript, generators, keys);
let batch_capacity = match BitSignature::from(SIGNATURE) {
BitSignature::ClassicLinear => 3,
BitSignature::ConciseLinear => 3,
BitSignature::EfficientLinear => (self.bits.len() + 1) * 3
BitSignature::EfficientLinear => (self.bits.len() + 1) * 3,
BitSignature::CompromiseLinear => (self.bits.len() + 1) * 3
};
let mut batch = (BatchVerifier::new(batch_capacity), BatchVerifier::new(batch_capacity));