Add init function for BP statics

Considering they take 7 seconds to generate, thanks to #68, the ability 
to generate them at the start instead of on first BP is greatly 
appreciated.

Also performs minor cleanups regarding BPs.
This commit is contained in:
Luke Parker 2022-08-02 15:52:27 -04:00
parent 9221dbf048
commit a30568ff57
No known key found for this signature in database
GPG key ID: F9F1386DB1E119B6
9 changed files with 68 additions and 24 deletions

View file

@ -34,7 +34,7 @@ pub enum Protocol {
}
impl Protocol {
pub(crate) fn ring_len(&self) -> usize {
pub fn ring_len(&self) -> usize {
match self {
Protocol::Unsupported => panic!("Unsupported protocol version"),
Protocol::v14 => 11,
@ -42,7 +42,7 @@ impl Protocol {
}
}
pub(crate) fn bp_plus(&self) -> bool {
pub fn bp_plus(&self) -> bool {
match self {
Protocol::Unsupported => panic!("Unsupported protocol version"),
Protocol::v14 => false,

View file

@ -29,6 +29,7 @@ pub(crate) fn hash_to_scalar(data: &[u8]) -> Scalar {
// Components common between variants
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;
@ -77,9 +78,6 @@ pub(crate) fn hash_cache(cache: &mut Scalar, mash: &[[u8; 32]]) -> Scalar {
}
pub(crate) fn MN(outputs: usize) -> (usize, usize, usize) {
let logN = 6;
debug_assert_eq!(N, 1 << logN);
let mut logM = 0;
let mut M;
while {
@ -89,7 +87,7 @@ pub(crate) fn MN(outputs: usize) -> (usize, usize, usize) {
logM += 1;
}
(logM + logN, M, M * N)
(logM + LOG_N, M, M * N)
}
pub(crate) fn bit_decompose(commitments: &[Commitment]) -> (ScalarVector, ScalarVector) {
@ -152,6 +150,12 @@ 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

@ -41,6 +41,14 @@ 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

@ -32,6 +32,13 @@ 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],
@ -268,7 +275,7 @@ impl OriginalStruct {
rng: &mut R,
commitments: &[DalekPoint],
) -> bool {
let mut verifier = BatchVerifier::new(4 + commitments.len() + 4 + (2 * (MAX_MN + 10)));
let mut verifier = BatchVerifier::new(1);
if self.verify_core(rng, &mut verifier, (), commitments) {
verifier.verify_vartime()
} else {

View file

@ -17,6 +17,13 @@ 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();
static ref TWO_SIXTY_FOUR_MINUS_ONE: Scalar = {
let mut temp = Scalar::from(2u8);
for _ in 0 .. LOG_N {
temp *= temp;
}
temp - Scalar::one()
};
}
// TRANSCRIPT isn't a Scalar, so we need this alternative for the first hash
@ -50,6 +57,12 @@ 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],
@ -214,7 +227,6 @@ impl PlusStruct {
let (zpow, d) = d(z, M, MN);
let zsq = zpow[0];
assert_eq!(zsq, z * z);
let esq = e * e;
let minus_esq = -esq;
@ -223,19 +235,16 @@ impl PlusStruct {
proof.push((commitment_weight * zpow[i], commitment));
}
// Invert B, instead of the Scalar, as the latter is only 2x as expensive yet enables reduction
// to a single addition under vartime for the first BP verified in the batch, which is expected
// to be much more significant
proof.push((Scalar::one(), -B));
proof.push((-e, A1));
proof.push((minus_esq, A));
proof.push((Scalar(self.d1), G));
let mut twoSixtyFourMinusOne = Scalar::from(2u8);
for _ in 0 .. 6 {
twoSixtyFourMinusOne *= twoSixtyFourMinusOne;
}
twoSixtyFourMinusOne -= Scalar::one();
let d_sum = zpow.sum() * twoSixtyFourMinusOne;
let y_sum = ScalarVector(ScalarVector::powers(y, MN + 1).0[1 ..].to_vec()).sum();
let d_sum = zpow.sum() * *TWO_SIXTY_FOUR_MINUS_ONE;
let y_sum = weighted_powers(y, MN).sum();
proof.push((
Scalar(self.r1 * y.0 * self.s1) + (esq * ((yMNy * z * d_sum) + ((zsq - z) * y_sum))),
*H,
@ -275,7 +284,7 @@ impl PlusStruct {
rng: &mut R,
commitments: &[DalekPoint],
) -> bool {
let mut verifier = BatchVerifier::new(4 + commitments.len() + 4 + (2 * (MAX_MN + 10)));
let mut verifier = BatchVerifier::new(1);
if self.verify_core(rng, &mut verifier, (), commitments) {
verifier.verify_vartime()
} else {

View file

@ -103,9 +103,13 @@ pub(crate) fn inner_product(a: &ScalarVector, b: &ScalarVector) -> Scalar {
(a * b).sum()
}
pub(crate) fn weighted_powers(x: Scalar, len: usize) -> ScalarVector {
ScalarVector(ScalarVector::powers(x, len + 1).0[1 ..].to_vec())
}
pub(crate) fn weighted_inner_product(a: &ScalarVector, b: &ScalarVector, y: Scalar) -> Scalar {
// y ** 0 is not used as a power
(a * b * ScalarVector(ScalarVector::powers(y, a.len() + 1).0[1 ..].to_vec())).sum()
(a * b * weighted_powers(y, a.len())).sum()
}
impl Mul<&[EdwardsPoint]> for &ScalarVector {

View file

@ -57,7 +57,12 @@ fn bulletproofs_vector() {
}
macro_rules! bulletproofs_tests {
($name: ident, $max: ident, $plus: literal) => {
($init: ident, $name: ident, $max: ident, $plus: literal) => {
#[test]
fn $init() {
Bulletproofs::init($plus);
}
#[test]
fn $name() {
// Create Bulletproofs for all possible output quantities
@ -86,5 +91,5 @@ macro_rules! bulletproofs_tests {
};
}
bulletproofs_tests!(bulletproofs, bulletproofs_max, false);
bulletproofs_tests!(bulletproofs_plus, bulletproofs_plus_max, true);
bulletproofs_tests!(bulletproofs_init, bulletproofs, bulletproofs_max, false);
bulletproofs_tests!(bulletproofs_plus_init, bulletproofs_plus, bulletproofs_plus_max, true);

View file

@ -9,6 +9,7 @@ use transcript::RecommendedTranscript;
use frost::{curve::Ed25519, FrostKeys};
use monero_serai::{
ringct::bulletproofs::Bulletproofs,
transaction::Transaction,
rpc::Rpc,
wallet::{
@ -69,9 +70,15 @@ pub struct Monero {
}
impl Monero {
pub fn new(url: String) -> Monero {
pub async fn new(url: String) -> Monero {
let view = view_key::<Monero>(0).0;
Monero { rpc: Rpc::new(url), view }
let res = Monero { rpc: Rpc::new(url), view };
// Initialize Bulletproofs now to prevent the first call from taking several seconds
// TODO: Do this for both, unless we're sure we're only working on a single protocol
Bulletproofs::init(res.rpc.get_protocol().await.unwrap().bp_plus());
res
}
fn view_pair(&self, spend: dfg::EdwardsPoint) -> ViewPair {

View file

@ -111,7 +111,7 @@ async fn test_send<C: Coin + Clone>(coin: C, fee: C::Fee) {
#[tokio::test]
async fn monero() {
let monero = Monero::new("http://127.0.0.1:18081".to_string());
let monero = Monero::new("http://127.0.0.1:18081".to_string()).await;
let fee = monero.rpc.get_fee().await.unwrap();
test_send(monero, fee).await;
}