From d25c668ee4192862d9dfdccf26cac8288fe6e4a6 Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Wed, 28 Jun 2023 21:16:33 -0400 Subject: [PATCH] Replace lazy_static with OnceLock inside monero-serai lazy_static, if no_std environments were used, effectively required always using spin locks. This resolves the ergonomics of that while adopting Rust std code. no_std does still use a spin based solution. Theoretically, we could use atomics, yet writing our own Mutex wasn't a priority. --- Cargo.lock | 29 ++++++++-- coins/monero/Cargo.toml | 3 +- coins/monero/build.rs | 7 ++- coins/monero/generators/Cargo.toml | 3 - coins/monero/generators/src/lib.rs | 17 ++++-- coins/monero/src/lib.rs | 21 +++---- coins/monero/src/ringct/bulletproofs/core.rs | 29 +++++----- .../src/ringct/bulletproofs/original.rs | 39 +++++++------ coins/monero/src/ringct/bulletproofs/plus.rs | 38 +++++++------ coins/monero/src/ringct/clsag/mod.rs | 11 +--- coins/monero/src/wallet/decoys.rs | 23 ++++---- coins/monero/src/wallet/seed/classic.rs | 48 ++++++++-------- coins/monero/tests/runner.rs | 10 +--- common/std-shims/Cargo.toml | 3 +- common/std-shims/src/lib.rs | 1 + common/std-shims/src/sync.rs | 57 +++++++++++++++++++ tests/no-std/Cargo.toml | 2 +- 17 files changed, 212 insertions(+), 129 deletions(-) create mode 100644 common/std-shims/src/sync.rs diff --git a/Cargo.lock b/Cargo.lock index 8e8f015e..9b35a7ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -179,6 +179,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56fc6cf8dc8c4158eed8649f9b8b0ea1518eb62b544fe9490d66fa0b349eafe9" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -3400,6 +3406,10 @@ name = "hashbrown" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +dependencies = [ + "ahash 0.8.3", + "allocator-api2", +] [[package]] name = "hashers" @@ -4193,7 +4203,7 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" dependencies = [ - "spin", + "spin 0.5.2", ] [[package]] @@ -5105,7 +5115,6 @@ dependencies = [ "curve25519-dalek 3.2.0", "dalek-ff-group", "group 0.13.0", - "lazy_static", "sha3", "std-shims", "subtle", @@ -5147,7 +5156,6 @@ dependencies = [ "group 0.13.0", "hex", "hex-literal 0.4.1", - "lazy_static", "modular-frost", "monero-epee-bin-serde", "monero-generators", @@ -5161,6 +5169,7 @@ dependencies = [ "serde", "serde_json", "sha3", + "std-shims", "subtle", "thiserror", "tokio", @@ -7010,7 +7019,7 @@ dependencies = [ "cc", "libc", "once_cell", - "spin", + "spin 0.5.2", "untrusted", "web-sys", "winapi", @@ -9720,6 +9729,15 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + [[package]] name = "spki" version = "0.6.0" @@ -9799,7 +9817,8 @@ dependencies = [ name = "std-shims" version = "0.1.0" dependencies = [ - "hashbrown 0.13.2", + "hashbrown 0.14.0", + "spin 0.9.8", ] [[package]] diff --git a/coins/monero/Cargo.toml b/coins/monero/Cargo.toml index f3e80546..6eb567bb 100644 --- a/coins/monero/Cargo.toml +++ b/coins/monero/Cargo.toml @@ -12,9 +12,10 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [dependencies] +std-shims = { path = "../../common/std-shims", version = "0.1", default-features = false } + futures = "0.3" -lazy_static = "1" async-trait = "0.1" thiserror = "1" diff --git a/coins/monero/build.rs b/coins/monero/build.rs index 2eafa201..34c34b6b 100644 --- a/coins/monero/build.rs +++ b/coins/monero/build.rs @@ -41,15 +41,16 @@ fn generators(prefix: &'static str, path: &str) { .write_all( format!( " - lazy_static! {{ - pub static ref GENERATORS: Generators = Generators {{ + pub static GENERATORS_CELL: OnceLock = OnceLock::new(); + pub fn GENERATORS() -> &'static Generators {{ + GENERATORS_CELL.get_or_init(|| Generators {{ G: [ {G_str} ], H: [ {H_str} ], - }}; + }}) }} ", ) diff --git a/coins/monero/generators/Cargo.toml b/coins/monero/generators/Cargo.toml index 9f646d73..5e221370 100644 --- a/coins/monero/generators/Cargo.toml +++ b/coins/monero/generators/Cargo.toml @@ -14,8 +14,6 @@ rustdoc-args = ["--cfg", "docsrs"] [dependencies] std-shims = { path = "../../../common/std-shims", version = "0.1", default-features = false } -lazy_static = "1" - subtle = { version = "^2.4", default-features = false } sha3 = { version = "0.10", default-features = false } @@ -26,6 +24,5 @@ group = { version = "0.13", default-features = false } dalek-ff-group = { path = "../../../crypto/dalek-ff-group", version = "0.3" } [features] -alloc = ["lazy_static/spin_no_std"] std = ["std-shims/std"] default = ["std"] diff --git a/coins/monero/generators/src/lib.rs b/coins/monero/generators/src/lib.rs index d3783e14..d25d927f 100644 --- a/coins/monero/generators/src/lib.rs +++ b/coins/monero/generators/src/lib.rs @@ -1,10 +1,12 @@ //! Generators used by Monero in both its Pedersen commitments and Bulletproofs(+). +//! //! An implementation of Monero's `ge_fromfe_frombytes_vartime`, simply called //! `hash_to_point` here, is included, as needed to generate generators. #![cfg_attr(not(feature = "std"), no_std)] -use lazy_static::lazy_static; +use core::cell::OnceCell; +use std_shims::sync::Mutex; use sha3::{Digest, Keccak256}; @@ -23,13 +25,16 @@ fn hash(data: &[u8]) -> [u8; 32] { Keccak256::digest(data).into() } -lazy_static! { - /// Monero alternate generator `H`, used for amounts in Pedersen commitments. - pub static ref H: DalekPoint = +/// Monero alternate generator `H`, used for amounts in Pedersen commitments. +static H_CELL: Mutex> = Mutex::new(OnceCell::new()); +#[allow(non_snake_case)] +pub fn H() -> DalekPoint { + *H_CELL.lock().get_or_init(|| { CompressedEdwardsY(hash(&EdwardsPoint::generator().to_bytes())) .decompress() .unwrap() - .mul_by_cofactor(); + .mul_by_cofactor() + }) } const MAX_M: usize = 16; @@ -50,7 +55,7 @@ pub fn bulletproofs_generators(dst: &'static [u8]) -> Generators { for i in 0 .. MAX_MN { let i = 2 * i; - let mut even = H.compress().to_bytes().to_vec(); + let mut even = H().compress().to_bytes().to_vec(); even.extend(dst); let mut odd = even.clone(); diff --git a/coins/monero/src/lib.rs b/coins/monero/src/lib.rs index 5822ef67..05ac830e 100644 --- a/coins/monero/src/lib.rs +++ b/coins/monero/src/lib.rs @@ -1,20 +1,15 @@ #![cfg_attr(docsrs, feature(doc_auto_cfg))] #![doc = include_str!("../README.md")] -use std::io; +use std_shims::{sync::OnceLock, io}; -use lazy_static::lazy_static; use rand_core::{RngCore, CryptoRng}; use zeroize::{Zeroize, ZeroizeOnDrop}; use sha3::{Digest, Keccak256}; -use curve25519_dalek::{ - constants::ED25519_BASEPOINT_TABLE, - scalar::Scalar, - edwards::{EdwardsPoint, EdwardsBasepointTable}, -}; +use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE, scalar::Scalar, edwards::EdwardsPoint}; pub use monero_generators::H; @@ -37,6 +32,12 @@ pub mod wallet; #[cfg(test)] mod tests; +static INV_EIGHT_CELL: OnceLock = OnceLock::new(); +#[allow(non_snake_case)] +pub(crate) fn INV_EIGHT() -> Scalar { + *INV_EIGHT_CELL.get_or_init(|| Scalar::from(8u8).invert()) +} + /// Monero protocol version. v15 is omitted as v15 was simply v14 and v16 being active at the same /// time, with regards to the transactions supported. Accordingly, v16 should be used during v15. #[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)] @@ -107,10 +108,6 @@ impl Protocol { } } -lazy_static! { - static ref H_TABLE: EdwardsBasepointTable = EdwardsBasepointTable::create(&H); -} - /// Transparent structure representing a Pedersen commitment's contents. #[allow(non_snake_case)] #[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)] @@ -131,7 +128,7 @@ impl Commitment { /// Calculate a Pedersen commitment, as a point, from the transparent structure. pub fn calculate(&self) -> EdwardsPoint { - (&self.mask * &ED25519_BASEPOINT_TABLE) + (&Scalar::from(self.amount) * &*H_TABLE) + (&self.mask * &ED25519_BASEPOINT_TABLE) + (Scalar::from(self.amount) * H()) } } diff --git a/coins/monero/src/ringct/bulletproofs/core.rs b/coins/monero/src/ringct/bulletproofs/core.rs index 46641ade..9d2f00f9 100644 --- a/coins/monero/src/ringct/bulletproofs/core.rs +++ b/coins/monero/src/ringct/bulletproofs/core.rs @@ -1,7 +1,5 @@ -// Required to be for this entire file, which isn't an issue, as it wouldn't bind to the static -#![allow(non_upper_case_globals)] +use std_shims::sync::OnceLock; -use lazy_static::lazy_static; use rand_core::{RngCore, CryptoRng}; use subtle::{Choice, ConditionallySelectable}; @@ -15,13 +13,17 @@ use multiexp::multiexp as multiexp_const; pub(crate) use monero_generators::Generators; -use crate::{H as DALEK_H, Commitment, hash_to_scalar as dalek_hash}; +use crate::{INV_EIGHT as DALEK_INV_EIGHT, H as DALEK_H, Commitment, hash_to_scalar as dalek_hash}; pub(crate) use crate::ringct::bulletproofs::scalar_vector::*; -// Bring things into ff/group -lazy_static! { - pub(crate) static ref INV_EIGHT: Scalar = Scalar::from(8u8).invert().unwrap(); - pub(crate) static ref H: EdwardsPoint = EdwardsPoint(*DALEK_H); +#[inline] +pub(crate) fn INV_EIGHT() -> Scalar { + Scalar(DALEK_INV_EIGHT()) +} + +#[inline] +pub(crate) fn H() -> EdwardsPoint { + EdwardsPoint(DALEK_H()) } pub(crate) fn hash_to_scalar(data: &[u8]) -> Scalar { @@ -34,7 +36,7 @@ pub(crate) const LOG_N: usize = 6; // 2 << 6 == N pub(crate) const N: usize = 64; pub(crate) fn prove_multiexp(pairs: &[(Scalar, EdwardsPoint)]) -> EdwardsPoint { - multiexp_const(pairs) * *INV_EIGHT + multiexp_const(pairs) * INV_EIGHT() } pub(crate) fn vector_exponent( @@ -91,7 +93,7 @@ pub(crate) fn bit_decompose(commitments: &[Commitment]) -> (ScalarVector, Scalar pub(crate) fn hash_commitments>( commitments: C, ) -> (Scalar, Vec) { - let V = commitments.into_iter().map(|c| EdwardsPoint(c) * *INV_EIGHT).collect::>(); + let V = commitments.into_iter().map(|c| EdwardsPoint(c) * INV_EIGHT()).collect::>(); (hash_to_scalar(&V.iter().flat_map(|V| V.compress().to_bytes()).collect::>()), V) } @@ -102,7 +104,7 @@ pub(crate) fn alpha_rho( aR: &ScalarVector, ) -> (Scalar, EdwardsPoint) { let ar = Scalar::random(rng); - (ar, (vector_exponent(generators, aL, aR) + (EdwardsPoint::generator() * ar)) * *INV_EIGHT) + (ar, (vector_exponent(generators, aL, aR) + (EdwardsPoint::generator() * ar)) * INV_EIGHT()) } pub(crate) fn LR_statements( @@ -124,8 +126,9 @@ pub(crate) fn LR_statements( res } -lazy_static! { - pub(crate) static ref TWO_N: ScalarVector = ScalarVector::powers(Scalar::from(2u8), N); +static TWO_N_CELL: OnceLock = OnceLock::new(); +pub(crate) fn TWO_N() -> &'static ScalarVector { + TWO_N_CELL.get_or_init(|| ScalarVector::powers(Scalar::from(2u8), N)) } pub(crate) fn challenge_products(w: &[Scalar], winv: &[Scalar]) -> Vec { diff --git a/coins/monero/src/ringct/bulletproofs/original.rs b/coins/monero/src/ringct/bulletproofs/original.rs index 0dda32a8..3301f1b5 100644 --- a/coins/monero/src/ringct/bulletproofs/original.rs +++ b/coins/monero/src/ringct/bulletproofs/original.rs @@ -1,4 +1,5 @@ -use lazy_static::lazy_static; +use std_shims::sync::OnceLock; + use rand_core::{RngCore, CryptoRng}; use zeroize::Zeroize; @@ -14,9 +15,9 @@ use crate::{Commitment, ringct::bulletproofs::core::*}; include!(concat!(env!("OUT_DIR"), "/generators.rs")); -lazy_static! { - static ref ONE_N: ScalarVector = ScalarVector(vec![Scalar::ONE; N]); - static ref IP12: Scalar = inner_product(&ONE_N, &TWO_N); +static IP12_CELL: OnceLock = OnceLock::new(); +pub(crate) fn IP12() -> Scalar { + *IP12_CELL.get_or_init(|| inner_product(&ScalarVector(vec![Scalar::ONE; N]), TWO_N())) } #[derive(Clone, PartialEq, Eq, Debug)] @@ -48,8 +49,9 @@ impl OriginalStruct { let (sL, sR) = ScalarVector((0 .. (MN * 2)).map(|_| Scalar::random(&mut *rng)).collect::>()).split(); - let (mut alpha, A) = alpha_rho(&mut *rng, &GENERATORS, &aL, &aR); - let (mut rho, S) = alpha_rho(&mut *rng, &GENERATORS, &sL, &sR); + let generators = GENERATORS(); + let (mut alpha, A) = alpha_rho(&mut *rng, generators, &aL, &aR); + let (mut rho, S) = alpha_rho(&mut *rng, generators, &sL, &sR); let y = hash_cache(&mut cache, &[A.compress().to_bytes(), S.compress().to_bytes()]); let mut cache = hash_to_scalar(&y.to_bytes()); @@ -62,7 +64,7 @@ impl OriginalStruct { let zpow = ScalarVector::powers(z, M + 2); for j in 0 .. M { for i in 0 .. N { - zero_twos.push(zpow[j + 2] * TWO_N[i]); + zero_twos.push(zpow[j + 2] * TWO_N()[i]); } } @@ -77,8 +79,8 @@ impl OriginalStruct { let mut tau1 = Scalar::random(&mut *rng); let mut tau2 = Scalar::random(&mut *rng); - let T1 = prove_multiexp(&[(t1, *H), (tau1, EdwardsPoint::generator())]); - let T2 = prove_multiexp(&[(t2, *H), (tau2, EdwardsPoint::generator())]); + let T1 = prove_multiexp(&[(t1, H()), (tau1, EdwardsPoint::generator())]); + let T2 = prove_multiexp(&[(t2, H()), (tau2, EdwardsPoint::generator())]); let x = hash_cache(&mut cache, &[z.to_bytes(), T1.compress().to_bytes(), T2.compress().to_bytes()]); @@ -112,10 +114,10 @@ impl OriginalStruct { let yinv = y.invert().unwrap(); let yinvpow = ScalarVector::powers(yinv, MN); - let mut G_proof = GENERATORS.G[.. a.len()].to_vec(); - let mut H_proof = GENERATORS.H[.. a.len()].to_vec(); + let mut G_proof = generators.G[.. a.len()].to_vec(); + let mut H_proof = generators.H[.. a.len()].to_vec(); H_proof.iter_mut().zip(yinvpow.0.iter()).for_each(|(this_H, yinvpow)| *this_H *= yinvpow); - let U = *H * x_ip; + let U = H() * x_ip; let mut L = Vec::with_capacity(logMN); let mut R = Vec::with_capacity(logMN); @@ -230,10 +232,10 @@ impl OriginalStruct { let ip1y = ScalarVector::powers(y, M * N).sum(); let mut k = -(zpow[2] * ip1y); for j in 1 ..= M { - k -= zpow[j + 2] * *IP12; + k -= zpow[j + 2] * IP12(); } let y1 = Scalar(self.t) - ((z * ip1y) + k); - proof.push((-y1, *H)); + proof.push((-y1, H())); proof.push((-Scalar(self.taux), G)); @@ -247,7 +249,7 @@ impl OriginalStruct { proof = Vec::with_capacity(4 + (2 * (MN + logMN))); let z3 = (Scalar(self.t) - (Scalar(self.a) * Scalar(self.b))) * x_ip; - proof.push((z3, *H)); + proof.push((z3, H())); proof.push((-Scalar(self.mu), G)); proof.push((Scalar::ONE, A)); @@ -260,13 +262,14 @@ impl OriginalStruct { let w_cache = challenge_products(&w, &winv); + let generators = GENERATORS(); for i in 0 .. MN { let g = (Scalar(self.a) * w_cache[i]) + z; - proof.push((-g, GENERATORS.G[i])); + proof.push((-g, generators.G[i])); let mut h = Scalar(self.b) * yinvpow[i] * w_cache[(!i) & (MN - 1)]; - h -= ((zpow[(i / N) + 2] * TWO_N[i % N]) + (z * ypow[i])) * yinvpow[i]; - proof.push((-h, GENERATORS.H[i])); + h -= ((zpow[(i / N) + 2] * TWO_N()[i % N]) + (z * ypow[i])) * yinvpow[i]; + proof.push((-h, generators.H[i])); } } diff --git a/coins/monero/src/ringct/bulletproofs/plus.rs b/coins/monero/src/ringct/bulletproofs/plus.rs index cd07fa55..7031c179 100644 --- a/coins/monero/src/ringct/bulletproofs/plus.rs +++ b/coins/monero/src/ringct/bulletproofs/plus.rs @@ -1,4 +1,5 @@ -use lazy_static::lazy_static; +use std_shims::sync::OnceLock; + use rand_core::{RngCore, CryptoRng}; use zeroize::Zeroize; @@ -17,15 +18,17 @@ use crate::{ include!(concat!(env!("OUT_DIR"), "/generators_plus.rs")); -lazy_static! { - static ref TRANSCRIPT: [u8; 32] = - EdwardsPoint(raw_hash_to_point(hash(b"bulletproof_plus_transcript"))).compress().to_bytes(); +static TRANSCRIPT_CELL: OnceLock<[u8; 32]> = OnceLock::new(); +pub(crate) fn TRANSCRIPT() -> [u8; 32] { + *TRANSCRIPT_CELL.get_or_init(|| { + EdwardsPoint(raw_hash_to_point(hash(b"bulletproof_plus_transcript"))).compress().to_bytes() + }) } // TRANSCRIPT isn't a Scalar, so we need this alternative for the first hash fn hash_plus>(commitments: C) -> (Scalar, Vec) { let (cache, commitments) = hash_commitments(commitments); - (hash_to_scalar(&[&*TRANSCRIPT as &[u8], &cache.to_bytes()].concat()), commitments) + (hash_to_scalar(&[TRANSCRIPT().as_ref(), &cache.to_bytes()].concat()), commitments) } // d[j*N+i] = z**(2*(j+1)) * 2**i @@ -34,7 +37,7 @@ fn d(z: Scalar, M: usize, MN: usize) -> (ScalarVector, ScalarVector) { let mut d = vec![Scalar::ZERO; MN]; for j in 0 .. M { for i in 0 .. N { - d[(j * N) + i] = zpow[j] * TWO_N[i]; + d[(j * N) + i] = zpow[j] * TWO_N()[i]; } } (zpow, ScalarVector(d)) @@ -57,12 +60,14 @@ impl PlusStruct { rng: &mut R, commitments: &[Commitment], ) -> PlusStruct { + let generators = GENERATORS(); + let (logMN, M, MN) = MN(commitments.len()); let (aL, aR) = bit_decompose(commitments); let commitments_points = commitments.iter().map(Commitment::calculate).collect::>(); let (mut cache, _) = hash_plus(commitments_points.clone()); - let (mut alpha1, A) = alpha_rho(&mut *rng, &GENERATORS, &aL, &aR); + let (mut alpha1, A) = alpha_rho(&mut *rng, generators, &aL, &aR); let y = hash_cache(&mut cache, &[A.compress().to_bytes()]); let mut cache = hash_to_scalar(&y.to_bytes()); @@ -87,8 +92,8 @@ impl PlusStruct { let yinv = y.invert().unwrap(); let yinvpow = ScalarVector::powers(yinv, MN); - let mut G_proof = GENERATORS.G[.. a.len()].to_vec(); - let mut H_proof = GENERATORS.H[.. a.len()].to_vec(); + let mut G_proof = generators.G[.. a.len()].to_vec(); + let mut H_proof = generators.H[.. a.len()].to_vec(); let mut L = Vec::with_capacity(logMN); let mut R = Vec::with_capacity(logMN); @@ -105,12 +110,12 @@ impl PlusStruct { let (G_L, G_R) = G_proof.split_at(aL.len()); let (H_L, H_R) = H_proof.split_at(aL.len()); - let mut L_i = LR_statements(&(&aL * yinvpow[aL.len()]), G_R, &bR, H_L, cL, *H); + let mut L_i = LR_statements(&(&aL * yinvpow[aL.len()]), G_R, &bR, H_L, cL, H()); L_i.push((dL, G)); let L_i = prove_multiexp(&L_i); L.push(L_i); - let mut R_i = LR_statements(&(&aR * ypow[aR.len()]), G_L, &bL, H_R, cR, *H); + let mut R_i = LR_statements(&(&aR * ypow[aR.len()]), G_L, &bL, H_R, cR, H()); R_i.push((dR, G)); let R_i = prove_multiexp(&R_i); R.push(R_i); @@ -139,9 +144,9 @@ impl PlusStruct { (r, G_proof[0]), (s, H_proof[0]), (d, G), - ((r * y * b[0]) + (s * y * a[0]), *H), + ((r * y * b[0]) + (s * y * a[0]), H()), ]); - let B = prove_multiexp(&[(r * y * s, *H), (eta, G)]); + let B = prove_multiexp(&[(r * y * s, H()), (eta, G)]); let e = hash_cache(&mut cache, &[A1.compress().to_bytes(), B.compress().to_bytes()]); let r1 = (a[0] * e) + r; @@ -248,7 +253,7 @@ impl PlusStruct { 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, + H(), )); let w_cache = challenge_products(&w, &winv); @@ -259,11 +264,12 @@ impl PlusStruct { let minus_esq_z = -esq_z; let mut minus_esq_y = minus_esq * yMN; + let generators = GENERATORS(); for i in 0 .. MN { - proof.push((e_r1_y * w_cache[i] + esq_z, GENERATORS.G[i])); + proof.push((e_r1_y * w_cache[i] + esq_z, generators.G[i])); proof.push(( (e_s1 * w_cache[(!i) & (MN - 1)]) + minus_esq_z + (minus_esq_y * d[i]), - GENERATORS.H[i], + generators.H[i], )); e_r1_y *= yinv; diff --git a/coins/monero/src/ringct/clsag/mod.rs b/coins/monero/src/ringct/clsag/mod.rs index 5c4462fb..8008e4db 100644 --- a/coins/monero/src/ringct/clsag/mod.rs +++ b/coins/monero/src/ringct/clsag/mod.rs @@ -3,7 +3,6 @@ use core::ops::Deref; use std::io::{self, Read, Write}; -use lazy_static::lazy_static; use thiserror::Error; use rand_core::{RngCore, CryptoRng}; @@ -18,8 +17,8 @@ use curve25519_dalek::{ }; use crate::{ - Commitment, random_scalar, hash_to_scalar, wallet::decoys::Decoys, ringct::hash_to_point, - serialize::*, + INV_EIGHT, Commitment, random_scalar, hash_to_scalar, wallet::decoys::Decoys, + ringct::hash_to_point, serialize::*, }; #[cfg(feature = "multisig")] @@ -29,10 +28,6 @@ pub use multisig::{ClsagDetails, ClsagAddendum, ClsagMultisig}; #[cfg(feature = "multisig")] pub(crate) use multisig::add_key_image_share; -lazy_static! { - static ref INV_EIGHT: Scalar = Scalar::from(8u8).invert(); -} - /// Errors returned when CLSAG signing fails. #[derive(Clone, Copy, PartialEq, Eq, Debug, Error)] pub enum ClsagError { @@ -103,7 +98,7 @@ fn core( let n = ring.len(); let images_precomp = VartimeEdwardsPrecomputation::new([I, D]); - let D = D * *INV_EIGHT; + let D = D * INV_EIGHT(); // Generate the transcript // Instead of generating multiple, a single transcript is created and then edited as needed diff --git a/coins/monero/src/wallet/decoys.rs b/coins/monero/src/wallet/decoys.rs index 07b05d42..09a04003 100644 --- a/coins/monero/src/wallet/decoys.rs +++ b/coins/monero/src/wallet/decoys.rs @@ -1,8 +1,6 @@ -use std::collections::HashSet; +use std_shims::{sync::OnceLock, collections::HashSet}; -use futures::lock::{Mutex, MutexGuard}; - -use lazy_static::lazy_static; +use futures::lock::Mutex; use rand_core::{RngCore, CryptoRng}; use rand_distr::{Distribution, Gamma}; @@ -23,18 +21,19 @@ const BLOCK_TIME: usize = 120; const BLOCKS_PER_YEAR: usize = 365 * 24 * 60 * 60 / BLOCK_TIME; const TIP_APPLICATION: f64 = (LOCK_WINDOW * BLOCK_TIME) as f64; -lazy_static! { - static ref GAMMA: Gamma = Gamma::new(19.28, 1.0 / 1.61).unwrap(); - // TODO: Expose an API to reset this in case a reorg occurs/the RPC fails/returns garbage - // TODO: Update this when scanning a block, as possible - static ref DISTRIBUTION: Mutex> = Mutex::new(Vec::with_capacity(3000000)); +// TODO: Expose an API to reset this in case a reorg occurs/the RPC fails/returns garbage +// TODO: Update this when scanning a block, as possible +static DISTRIBUTION_CELL: OnceLock>> = OnceLock::new(); +#[allow(non_snake_case)] +fn DISTRIBUTION() -> &'static Mutex> { + DISTRIBUTION_CELL.get_or_init(|| Mutex::new(Vec::with_capacity(3000000))) } #[allow(clippy::too_many_arguments)] async fn select_n<'a, R: RngCore + CryptoRng, RPC: RpcConnection>( rng: &mut R, rpc: &Rpc, - distribution: &MutexGuard<'a, Vec>, + distribution: &[u64], height: usize, high: u64, per_second: f64, @@ -60,7 +59,7 @@ async fn select_n<'a, R: RngCore + CryptoRng, RPC: RpcConnection>( } // Use a gamma distribution - let mut age = GAMMA.sample(rng).exp(); + let mut age = Gamma::::new(19.28, 1.0 / 1.61).unwrap().sample(rng).exp(); if age > TIP_APPLICATION { age -= TIP_APPLICATION; } else { @@ -144,7 +143,7 @@ impl Decoys { height: usize, inputs: &[SpendableOutput], ) -> Result, RpcError> { - let mut distribution = DISTRIBUTION.lock().await; + let mut distribution = DISTRIBUTION().lock().await; let decoy_count = ring_len - 1; diff --git a/coins/monero/src/wallet/seed/classic.rs b/coins/monero/src/wallet/seed/classic.rs index 6afbe91b..171b0742 100644 --- a/coins/monero/src/wallet/seed/classic.rs +++ b/coins/monero/src/wallet/seed/classic.rs @@ -1,7 +1,5 @@ use core::ops::Deref; -use std::collections::HashMap; - -use lazy_static::lazy_static; +use std_shims::{sync::OnceLock, collections::HashMap}; use zeroize::{Zeroize, Zeroizing}; use rand_core::{RngCore, CryptoRng}; @@ -47,28 +45,32 @@ impl WordList { } } -lazy_static! { - static ref LANGUAGES: HashMap = HashMap::from([ - (Language::Chinese, WordList::new(include!("./classic/zh.rs"), 1)), - (Language::English, WordList::new(include!("./classic/en.rs"), 3)), - (Language::Dutch, WordList::new(include!("./classic/nl.rs"), 4)), - (Language::French, WordList::new(include!("./classic/fr.rs"), 4)), - (Language::Spanish, WordList::new(include!("./classic/es.rs"), 4)), - (Language::German, WordList::new(include!("./classic/de.rs"), 4)), - (Language::Italian, WordList::new(include!("./classic/it.rs"), 4)), - (Language::Portuguese, WordList::new(include!("./classic/pt.rs"), 4)), - (Language::Japanese, WordList::new(include!("./classic/ja.rs"), 3)), - (Language::Russian, WordList::new(include!("./classic/ru.rs"), 4)), - (Language::Esperanto, WordList::new(include!("./classic/eo.rs"), 4)), - (Language::Lojban, WordList::new(include!("./classic/jbo.rs"), 4)), - (Language::EnglishOld, WordList::new(include!("./classic/ang.rs"), 4)), - ]); +static LANGUAGES_CELL: OnceLock> = OnceLock::new(); +#[allow(non_snake_case)] +fn LANGUAGES() -> &'static HashMap { + LANGUAGES_CELL.get_or_init(|| { + HashMap::from([ + (Language::Chinese, WordList::new(include!("./classic/zh.rs"), 1)), + (Language::English, WordList::new(include!("./classic/en.rs"), 3)), + (Language::Dutch, WordList::new(include!("./classic/nl.rs"), 4)), + (Language::French, WordList::new(include!("./classic/fr.rs"), 4)), + (Language::Spanish, WordList::new(include!("./classic/es.rs"), 4)), + (Language::German, WordList::new(include!("./classic/de.rs"), 4)), + (Language::Italian, WordList::new(include!("./classic/it.rs"), 4)), + (Language::Portuguese, WordList::new(include!("./classic/pt.rs"), 4)), + (Language::Japanese, WordList::new(include!("./classic/ja.rs"), 3)), + (Language::Russian, WordList::new(include!("./classic/ru.rs"), 4)), + (Language::Esperanto, WordList::new(include!("./classic/eo.rs"), 4)), + (Language::Lojban, WordList::new(include!("./classic/jbo.rs"), 4)), + (Language::EnglishOld, WordList::new(include!("./classic/ang.rs"), 4)), + ]) + }) } #[cfg(test)] pub(crate) fn trim_by_lang(word: &str, lang: Language) -> String { if lang != Language::EnglishOld { - word.chars().take(LANGUAGES[&lang].unique_prefix_length).collect() + word.chars().take(LANGUAGES()[&lang].unique_prefix_length).collect() } else { word.to_string() } @@ -92,7 +94,7 @@ fn key_to_seed(lang: Language, key: Zeroizing) -> ClassicSeed { let bytes = Zeroizing::new(key.to_bytes()); // get the language words - let words = &LANGUAGES[&lang].word_list; + let words = &LANGUAGES()[&lang].word_list; let list_len = u64::try_from(words.len()).unwrap(); // To store the found words & add the checksum word later. @@ -126,7 +128,7 @@ fn key_to_seed(lang: Language, key: Zeroizing) -> ClassicSeed { // create a checksum word for all languages except old english if lang != Language::EnglishOld { - let checksum = seed[checksum_index(&seed, &LANGUAGES[&lang])].clone(); + let checksum = seed[checksum_index(&seed, &LANGUAGES()[&lang])].clone(); seed.push(checksum); } @@ -154,7 +156,7 @@ pub(crate) fn seed_to_bytes(words: &str) -> Result<(Language, Zeroizing<[u8; 32] let mut matched_indices = Zeroizing::new(vec![]); // Iterate through all the languages - 'language: for (lang_name, lang) in LANGUAGES.iter() { + 'language: for (lang_name, lang) in LANGUAGES().iter() { matched_indices.zeroize(); matched_indices.clear(); diff --git a/coins/monero/tests/runner.rs b/coins/monero/tests/runner.rs index ad4e65ce..8a480563 100644 --- a/coins/monero/tests/runner.rs +++ b/coins/monero/tests/runner.rs @@ -1,7 +1,5 @@ use core::ops::Deref; -use std::collections::HashSet; - -use lazy_static::lazy_static; +use std_shims::{sync::OnceLock, collections::HashSet}; use zeroize::Zeroizing; use rand_core::OsRng; @@ -98,9 +96,7 @@ pub async fn rpc() -> Rpc { rpc } -lazy_static! { - pub static ref SEQUENTIAL: Mutex<()> = Mutex::new(()); -} +pub static SEQUENTIAL: OnceLock> = OnceLock::new(); #[macro_export] macro_rules! async_sequential { @@ -108,7 +104,7 @@ macro_rules! async_sequential { $( #[tokio::test] async fn $name() { - let guard = runner::SEQUENTIAL.lock().await; + let guard = runner::SEQUENTIAL.get_or_init(|| tokio::sync::Mutex::new(())).lock().await; let local = tokio::task::LocalSet::new(); local.run_until(async move { if let Err(err) = tokio::task::spawn_local(async move { $body }).await { diff --git a/common/std-shims/Cargo.toml b/common/std-shims/Cargo.toml index a5c3a299..0f20c333 100644 --- a/common/std-shims/Cargo.toml +++ b/common/std-shims/Cargo.toml @@ -13,7 +13,8 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [dependencies] -hashbrown = "0.13" +spin = "0.9" +hashbrown = "0.14" [features] std = [] diff --git a/common/std-shims/src/lib.rs b/common/std-shims/src/lib.rs index e092e04d..700c4bae 100644 --- a/common/std-shims/src/lib.rs +++ b/common/std-shims/src/lib.rs @@ -6,6 +6,7 @@ #[macro_use] pub extern crate alloc; +pub mod sync; pub mod collections; pub mod io; diff --git a/common/std-shims/src/sync.rs b/common/std-shims/src/sync.rs new file mode 100644 index 00000000..c84bba8d --- /dev/null +++ b/common/std-shims/src/sync.rs @@ -0,0 +1,57 @@ +pub use core::sync::*; + +mod mutex_shim { + #[cfg(feature = "std")] + pub use std::sync::*; + #[cfg(not(feature = "std"))] + pub use spin::*; + + #[derive(Default, Debug)] + pub struct ShimMutex(Mutex); + impl ShimMutex { + pub const fn new(value: T) -> Self { + Self(Mutex::new(value)) + } + + pub fn lock(&self) -> MutexGuard<'_, T> { + #[cfg(feature = "std")] + let res = self.0.lock().unwrap(); + #[cfg(not(feature = "std"))] + let res = self.0.lock(); + res + } + } +} +pub use mutex_shim::{ShimMutex as Mutex, MutexGuard}; + +#[cfg(feature = "std")] +pub use std::sync::OnceLock; +#[cfg(not(feature = "std"))] +mod oncelock_shim { + pub struct OnceLock(super::Mutex<()>, Option); + impl OnceLock { + pub const fn new() -> OnceLock { + OnceLock(Mutex::new(), None) + } + + pub fn get(&self) -> Option<&T> { + self.1.as_ref() + } + + pub fn get_mut(&mut self) -> Option<&mut T> { + self.1.as_mut() + } + + pub fn get_or_init T>(&self, f: F) -> &T { + let lock = self.0.lock(); + if self.1.is_none() { + self.1 = Some(f()); + } + drop(lock); + + self.1.as_ref().unwrap() + } + } +} +#[cfg(not(feature = "std"))] +pub use oncelock_shim::*; diff --git a/tests/no-std/Cargo.toml b/tests/no-std/Cargo.toml index 834ddc4e..a4ab6ddf 100644 --- a/tests/no-std/Cargo.toml +++ b/tests/no-std/Cargo.toml @@ -30,4 +30,4 @@ dkg = { path = "../../crypto/dkg", default-features = false } # modular-frost = { path = "../../crypto/frost", default-features = false } # frost-schnorrkel = { path = "../../crypto/schnorrkel", default-features = false } -monero-generators = { path = "../../coins/monero/generators", default-features = false, features = ["alloc"] } +monero-generators = { path = "../../coins/monero/generators", default-features = false }