Generate Bulletproofs(+) generators at compile time

Creates a new monero-generators crate so the monero crate can run the 
code in question at build time.

Saves several seconds from running the tests.

Closes https://github.com/serai-dex/serai/issues/101.
This commit is contained in:
Luke Parker
2022-08-21 06:36:53 -04:00
parent 577fe99a08
commit 603a3f8c9f
19 changed files with 274 additions and 133 deletions

View File

@@ -13,10 +13,9 @@ use dalek_ff_group::{Scalar, EdwardsPoint};
use multiexp::multiexp as multiexp_const;
use crate::{
H as DALEK_H, Commitment, hash, hash_to_scalar as dalek_hash,
ringct::hash_to_point::raw_hash_to_point, serialize::write_varint,
};
pub(crate) use monero_generators::Generators;
use crate::{H as DALEK_H, Commitment, hash_to_scalar as dalek_hash};
pub(crate) use crate::ringct::bulletproofs::scalar_vector::*;
// Bring things into ff/group
@@ -33,29 +32,6 @@ pub(crate) fn hash_to_scalar(data: &[u8]) -> Scalar {
pub(crate) const MAX_M: usize = 16;
pub(crate) const LOG_N: usize = 6; // 2 << 6 == N
pub(crate) const N: usize = 64;
pub(crate) const MAX_MN: usize = MAX_M * N;
pub(crate) struct Generators {
pub(crate) G: Vec<EdwardsPoint>,
pub(crate) H: Vec<EdwardsPoint>,
}
pub(crate) fn generators_core(prefix: &'static [u8]) -> Generators {
let mut res = Generators { G: Vec::with_capacity(MAX_MN), H: Vec::with_capacity(MAX_MN) };
for i in 0 .. MAX_MN {
let i = 2 * i;
let mut even = (*H).compress().to_bytes().to_vec();
even.extend(prefix);
let mut odd = even.clone();
write_varint(&i.try_into().unwrap(), &mut even).unwrap();
write_varint(&(i + 1).try_into().unwrap(), &mut odd).unwrap();
res.H.push(EdwardsPoint(raw_hash_to_point(hash(&even))));
res.G.push(EdwardsPoint(raw_hash_to_point(hash(&odd))));
}
res
}
pub(crate) fn prove_multiexp(pairs: &[(Scalar, EdwardsPoint)]) -> EdwardsPoint {
multiexp_const(pairs) * *INV_EIGHT
@@ -153,12 +129,6 @@ lazy_static! {
pub(crate) static ref TWO_N: ScalarVector = ScalarVector::powers(Scalar::from(2u8), N);
}
pub(crate) fn init() {
let _ = &*INV_EIGHT;
let _ = &*H;
let _ = &*TWO_N;
}
pub(crate) fn challenge_products(w: &[Scalar], winv: &[Scalar]) -> Vec<Scalar> {
let mut products = vec![Scalar::zero(); 1 << w.len()];
products[0] = winv[0];

View File

@@ -43,14 +43,6 @@ impl Bulletproofs {
len + clawback
}
pub fn init(plus: bool) {
if !plus {
OriginalStruct::init();
} else {
PlusStruct::init();
}
}
pub fn prove<R: RngCore + CryptoRng>(
rng: &mut R,
outputs: &[Commitment],

View File

@@ -12,8 +12,9 @@ use multiexp::BatchVerifier;
use crate::{Commitment, ringct::bulletproofs::core::*};
include!("../../../.generators/generators.rs");
lazy_static! {
static ref GENERATORS: Generators = generators_core(b"bulletproof");
static ref ONE_N: ScalarVector = ScalarVector(vec![Scalar::one(); N]);
static ref IP12: Scalar = inner_product(&ONE_N, &TWO_N);
}
@@ -34,13 +35,6 @@ pub struct OriginalStruct {
}
impl OriginalStruct {
pub(crate) fn init() {
init();
let _ = &*GENERATORS;
let _ = &*ONE_N;
let _ = &*IP12;
}
pub(crate) fn prove<R: RngCore + CryptoRng>(
rng: &mut R,
commitments: &[Commitment],

View File

@@ -15,8 +15,9 @@ use crate::{
ringct::{hash_to_point::raw_hash_to_point, bulletproofs::core::*},
};
include!("../../../.generators/generators_plus.rs");
lazy_static! {
static ref GENERATORS: Generators = generators_core(b"bulletproof_plus");
static ref TRANSCRIPT: [u8; 32] =
EdwardsPoint(raw_hash_to_point(hash(b"bulletproof_plus_transcript"))).compress().to_bytes();
}
@@ -52,12 +53,6 @@ pub struct PlusStruct {
}
impl PlusStruct {
pub(crate) fn init() {
init();
let _ = &*GENERATORS;
let _ = &*TRANSCRIPT;
}
pub(crate) fn prove<R: RngCore + CryptoRng>(
rng: &mut R,
commitments: &[Commitment],

View File

@@ -1,54 +1,6 @@
use subtle::ConditionallySelectable;
use curve25519_dalek::edwards::EdwardsPoint;
use curve25519_dalek::edwards::{CompressedEdwardsY, EdwardsPoint};
use group::ff::{Field, PrimeField};
use dalek_ff_group::field::FieldElement;
use crate::hash;
#[allow(dead_code)]
pub(crate) fn raw_hash_to_point(bytes: [u8; 32]) -> EdwardsPoint {
#[allow(non_snake_case)]
let A = FieldElement::from(486662u64);
let v = FieldElement::from_square(hash(&bytes)).double();
let w = v + FieldElement::one();
let x = w.square() + (-A.square() * v);
// This isn't the complete X, yet its initial value
// We don't calculate the full X, and instead solely calculate Y, letting dalek reconstruct X
// While inefficient, it solves API boundaries and reduces the amount of work done here
#[allow(non_snake_case)]
let X = {
let u = w;
let v = x;
let v3 = v * v * v;
let uv3 = u * v3;
let v7 = v3 * v3 * v;
let uv7 = u * v7;
uv3 * uv7.pow((-FieldElement::from(5u8)) * FieldElement::from(8u8).invert().unwrap())
};
let x = X.square() * x;
let y = w - x;
let non_zero_0 = !y.is_zero();
let y_if_non_zero_0 = w + x;
let sign = non_zero_0 & (!y_if_non_zero_0.is_zero());
let mut z = -A;
z *= FieldElement::conditional_select(&v, &FieldElement::from(1u8), sign);
#[allow(non_snake_case)]
let Z = z + w;
#[allow(non_snake_case)]
let mut Y = z - w;
Y *= Z.invert().unwrap();
let mut bytes = Y.to_repr();
bytes[31] |= sign.unwrap_u8() << 7;
CompressedEdwardsY(bytes).decompress().unwrap().mul_by_cofactor()
}
pub(crate) use monero_generators::{hash_to_point as raw_hash_to_point};
pub fn hash_to_point(key: EdwardsPoint) -> EdwardsPoint {
raw_hash_to_point(key.compress().to_bytes())