From a30568ff575c9a9a02c5a309335518787f7fdd84 Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Tue, 2 Aug 2022 15:52:27 -0400 Subject: [PATCH] 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. --- coins/monero/src/lib.rs | 4 +-- coins/monero/src/ringct/bulletproofs/core.rs | 12 +++++--- coins/monero/src/ringct/bulletproofs/mod.rs | 8 +++++ .../src/ringct/bulletproofs/original.rs | 9 +++++- coins/monero/src/ringct/bulletproofs/plus.rs | 29 ++++++++++++------- .../src/ringct/bulletproofs/scalar_vector.rs | 6 +++- coins/monero/src/tests/bulletproofs.rs | 11 +++++-- processor/src/coin/monero.rs | 11 +++++-- processor/src/tests/mod.rs | 2 +- 9 files changed, 68 insertions(+), 24 deletions(-) diff --git a/coins/monero/src/lib.rs b/coins/monero/src/lib.rs index daf56aaf..952eff5d 100644 --- a/coins/monero/src/lib.rs +++ b/coins/monero/src/lib.rs @@ -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, diff --git a/coins/monero/src/ringct/bulletproofs/core.rs b/coins/monero/src/ringct/bulletproofs/core.rs index 9a6086b9..fc6f9fc3 100644 --- a/coins/monero/src/ringct/bulletproofs/core.rs +++ b/coins/monero/src/ringct/bulletproofs/core.rs @@ -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 { let mut products = vec![Scalar::zero(); 1 << w.len()]; products[0] = winv[0]; diff --git a/coins/monero/src/ringct/bulletproofs/mod.rs b/coins/monero/src/ringct/bulletproofs/mod.rs index bf5976da..de5b449c 100644 --- a/coins/monero/src/ringct/bulletproofs/mod.rs +++ b/coins/monero/src/ringct/bulletproofs/mod.rs @@ -41,6 +41,14 @@ impl Bulletproofs { len + clawback } + pub fn init(plus: bool) { + if !plus { + OriginalStruct::init(); + } else { + PlusStruct::init(); + } + } + pub fn prove( rng: &mut R, outputs: &[Commitment], diff --git a/coins/monero/src/ringct/bulletproofs/original.rs b/coins/monero/src/ringct/bulletproofs/original.rs index d84eef46..82a38890 100644 --- a/coins/monero/src/ringct/bulletproofs/original.rs +++ b/coins/monero/src/ringct/bulletproofs/original.rs @@ -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( 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 { diff --git a/coins/monero/src/ringct/bulletproofs/plus.rs b/coins/monero/src/ringct/bulletproofs/plus.rs index 4c869999..551c6f20 100644 --- a/coins/monero/src/ringct/bulletproofs/plus.rs +++ b/coins/monero/src/ringct/bulletproofs/plus.rs @@ -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( 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 { diff --git a/coins/monero/src/ringct/bulletproofs/scalar_vector.rs b/coins/monero/src/ringct/bulletproofs/scalar_vector.rs index a9e9c13e..094a0f0f 100644 --- a/coins/monero/src/ringct/bulletproofs/scalar_vector.rs +++ b/coins/monero/src/ringct/bulletproofs/scalar_vector.rs @@ -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 { diff --git a/coins/monero/src/tests/bulletproofs.rs b/coins/monero/src/tests/bulletproofs.rs index 4ef3f5d2..a7ee9d50 100644 --- a/coins/monero/src/tests/bulletproofs.rs +++ b/coins/monero/src/tests/bulletproofs.rs @@ -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); diff --git a/processor/src/coin/monero.rs b/processor/src/coin/monero.rs index 1840d596..4a56244e 100644 --- a/processor/src/coin/monero.rs +++ b/processor/src/coin/monero.rs @@ -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::(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 { diff --git a/processor/src/tests/mod.rs b/processor/src/tests/mod.rs index 99c003bc..9d7d559e 100644 --- a/processor/src/tests/mod.rs +++ b/processor/src/tests/mod.rs @@ -111,7 +111,7 @@ async fn test_send(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; }