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.
This commit is contained in:
Luke Parker
2023-06-28 21:16:33 -04:00
parent 8ced63eaac
commit d25c668ee4
17 changed files with 212 additions and 129 deletions

29
Cargo.lock generated
View File

@@ -179,6 +179,12 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "allocator-api2"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56fc6cf8dc8c4158eed8649f9b8b0ea1518eb62b544fe9490d66fa0b349eafe9"
[[package]] [[package]]
name = "android-tzdata" name = "android-tzdata"
version = "0.1.1" version = "0.1.1"
@@ -3400,6 +3406,10 @@ name = "hashbrown"
version = "0.14.0" version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
dependencies = [
"ahash 0.8.3",
"allocator-api2",
]
[[package]] [[package]]
name = "hashers" name = "hashers"
@@ -4193,7 +4203,7 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
dependencies = [ dependencies = [
"spin", "spin 0.5.2",
] ]
[[package]] [[package]]
@@ -5105,7 +5115,6 @@ dependencies = [
"curve25519-dalek 3.2.0", "curve25519-dalek 3.2.0",
"dalek-ff-group", "dalek-ff-group",
"group 0.13.0", "group 0.13.0",
"lazy_static",
"sha3", "sha3",
"std-shims", "std-shims",
"subtle", "subtle",
@@ -5147,7 +5156,6 @@ dependencies = [
"group 0.13.0", "group 0.13.0",
"hex", "hex",
"hex-literal 0.4.1", "hex-literal 0.4.1",
"lazy_static",
"modular-frost", "modular-frost",
"monero-epee-bin-serde", "monero-epee-bin-serde",
"monero-generators", "monero-generators",
@@ -5161,6 +5169,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"sha3", "sha3",
"std-shims",
"subtle", "subtle",
"thiserror", "thiserror",
"tokio", "tokio",
@@ -7010,7 +7019,7 @@ dependencies = [
"cc", "cc",
"libc", "libc",
"once_cell", "once_cell",
"spin", "spin 0.5.2",
"untrusted", "untrusted",
"web-sys", "web-sys",
"winapi", "winapi",
@@ -9720,6 +9729,15 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 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]] [[package]]
name = "spki" name = "spki"
version = "0.6.0" version = "0.6.0"
@@ -9799,7 +9817,8 @@ dependencies = [
name = "std-shims" name = "std-shims"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"hashbrown 0.13.2", "hashbrown 0.14.0",
"spin 0.9.8",
] ]
[[package]] [[package]]

View File

@@ -12,9 +12,10 @@ all-features = true
rustdoc-args = ["--cfg", "docsrs"] rustdoc-args = ["--cfg", "docsrs"]
[dependencies] [dependencies]
std-shims = { path = "../../common/std-shims", version = "0.1", default-features = false }
futures = "0.3" futures = "0.3"
lazy_static = "1"
async-trait = "0.1" async-trait = "0.1"
thiserror = "1" thiserror = "1"

View File

@@ -41,15 +41,16 @@ fn generators(prefix: &'static str, path: &str) {
.write_all( .write_all(
format!( format!(
" "
lazy_static! {{ pub static GENERATORS_CELL: OnceLock<Generators> = OnceLock::new();
pub static ref GENERATORS: Generators = Generators {{ pub fn GENERATORS() -> &'static Generators {{
GENERATORS_CELL.get_or_init(|| Generators {{
G: [ G: [
{G_str} {G_str}
], ],
H: [ H: [
{H_str} {H_str}
], ],
}}; }})
}} }}
", ",
) )

View File

@@ -14,8 +14,6 @@ rustdoc-args = ["--cfg", "docsrs"]
[dependencies] [dependencies]
std-shims = { path = "../../../common/std-shims", version = "0.1", default-features = false } std-shims = { path = "../../../common/std-shims", version = "0.1", default-features = false }
lazy_static = "1"
subtle = { version = "^2.4", default-features = false } subtle = { version = "^2.4", default-features = false }
sha3 = { version = "0.10", 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" } dalek-ff-group = { path = "../../../crypto/dalek-ff-group", version = "0.3" }
[features] [features]
alloc = ["lazy_static/spin_no_std"]
std = ["std-shims/std"] std = ["std-shims/std"]
default = ["std"] default = ["std"]

View File

@@ -1,10 +1,12 @@
//! Generators used by Monero in both its Pedersen commitments and Bulletproofs(+). //! Generators used by Monero in both its Pedersen commitments and Bulletproofs(+).
//!
//! An implementation of Monero's `ge_fromfe_frombytes_vartime`, simply called //! An implementation of Monero's `ge_fromfe_frombytes_vartime`, simply called
//! `hash_to_point` here, is included, as needed to generate generators. //! `hash_to_point` here, is included, as needed to generate generators.
#![cfg_attr(not(feature = "std"), no_std)] #![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}; use sha3::{Digest, Keccak256};
@@ -23,13 +25,16 @@ fn hash(data: &[u8]) -> [u8; 32] {
Keccak256::digest(data).into() Keccak256::digest(data).into()
} }
lazy_static! { /// Monero alternate generator `H`, used for amounts in Pedersen commitments.
/// Monero alternate generator `H`, used for amounts in Pedersen commitments. static H_CELL: Mutex<OnceCell<DalekPoint>> = Mutex::new(OnceCell::new());
pub static ref H: DalekPoint = #[allow(non_snake_case)]
pub fn H() -> DalekPoint {
*H_CELL.lock().get_or_init(|| {
CompressedEdwardsY(hash(&EdwardsPoint::generator().to_bytes())) CompressedEdwardsY(hash(&EdwardsPoint::generator().to_bytes()))
.decompress() .decompress()
.unwrap() .unwrap()
.mul_by_cofactor(); .mul_by_cofactor()
})
} }
const MAX_M: usize = 16; const MAX_M: usize = 16;
@@ -50,7 +55,7 @@ pub fn bulletproofs_generators(dst: &'static [u8]) -> Generators {
for i in 0 .. MAX_MN { for i in 0 .. MAX_MN {
let i = 2 * i; 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); even.extend(dst);
let mut odd = even.clone(); let mut odd = even.clone();

View File

@@ -1,20 +1,15 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc = include_str!("../README.md")] #![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 rand_core::{RngCore, CryptoRng};
use zeroize::{Zeroize, ZeroizeOnDrop}; use zeroize::{Zeroize, ZeroizeOnDrop};
use sha3::{Digest, Keccak256}; use sha3::{Digest, Keccak256};
use curve25519_dalek::{ use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE, scalar::Scalar, edwards::EdwardsPoint};
constants::ED25519_BASEPOINT_TABLE,
scalar::Scalar,
edwards::{EdwardsPoint, EdwardsBasepointTable},
};
pub use monero_generators::H; pub use monero_generators::H;
@@ -37,6 +32,12 @@ pub mod wallet;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
static INV_EIGHT_CELL: OnceLock<Scalar> = 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 /// 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. /// time, with regards to the transactions supported. Accordingly, v16 should be used during v15.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)] #[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. /// Transparent structure representing a Pedersen commitment's contents.
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)] #[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)]
@@ -131,7 +128,7 @@ impl Commitment {
/// Calculate a Pedersen commitment, as a point, from the transparent structure. /// Calculate a Pedersen commitment, as a point, from the transparent structure.
pub fn calculate(&self) -> EdwardsPoint { 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())
} }
} }

View File

@@ -1,7 +1,5 @@
// Required to be for this entire file, which isn't an issue, as it wouldn't bind to the static use std_shims::sync::OnceLock;
#![allow(non_upper_case_globals)]
use lazy_static::lazy_static;
use rand_core::{RngCore, CryptoRng}; use rand_core::{RngCore, CryptoRng};
use subtle::{Choice, ConditionallySelectable}; use subtle::{Choice, ConditionallySelectable};
@@ -15,13 +13,17 @@ use multiexp::multiexp as multiexp_const;
pub(crate) use monero_generators::Generators; 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::*; pub(crate) use crate::ringct::bulletproofs::scalar_vector::*;
// Bring things into ff/group #[inline]
lazy_static! { pub(crate) fn INV_EIGHT() -> Scalar {
pub(crate) static ref INV_EIGHT: Scalar = Scalar::from(8u8).invert().unwrap(); Scalar(DALEK_INV_EIGHT())
pub(crate) static ref H: EdwardsPoint = EdwardsPoint(*DALEK_H); }
#[inline]
pub(crate) fn H() -> EdwardsPoint {
EdwardsPoint(DALEK_H())
} }
pub(crate) fn hash_to_scalar(data: &[u8]) -> Scalar { 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) const N: usize = 64;
pub(crate) fn prove_multiexp(pairs: &[(Scalar, EdwardsPoint)]) -> EdwardsPoint { pub(crate) fn prove_multiexp(pairs: &[(Scalar, EdwardsPoint)]) -> EdwardsPoint {
multiexp_const(pairs) * *INV_EIGHT multiexp_const(pairs) * INV_EIGHT()
} }
pub(crate) fn vector_exponent( pub(crate) fn vector_exponent(
@@ -91,7 +93,7 @@ pub(crate) fn bit_decompose(commitments: &[Commitment]) -> (ScalarVector, Scalar
pub(crate) fn hash_commitments<C: IntoIterator<Item = DalekPoint>>( pub(crate) fn hash_commitments<C: IntoIterator<Item = DalekPoint>>(
commitments: C, commitments: C,
) -> (Scalar, Vec<EdwardsPoint>) { ) -> (Scalar, Vec<EdwardsPoint>) {
let V = commitments.into_iter().map(|c| EdwardsPoint(c) * *INV_EIGHT).collect::<Vec<_>>(); let V = commitments.into_iter().map(|c| EdwardsPoint(c) * INV_EIGHT()).collect::<Vec<_>>();
(hash_to_scalar(&V.iter().flat_map(|V| V.compress().to_bytes()).collect::<Vec<_>>()), V) (hash_to_scalar(&V.iter().flat_map(|V| V.compress().to_bytes()).collect::<Vec<_>>()), V)
} }
@@ -102,7 +104,7 @@ pub(crate) fn alpha_rho<R: RngCore + CryptoRng>(
aR: &ScalarVector, aR: &ScalarVector,
) -> (Scalar, EdwardsPoint) { ) -> (Scalar, EdwardsPoint) {
let ar = Scalar::random(rng); 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( pub(crate) fn LR_statements(
@@ -124,8 +126,9 @@ pub(crate) fn LR_statements(
res res
} }
lazy_static! { static TWO_N_CELL: OnceLock<ScalarVector> = OnceLock::new();
pub(crate) static ref TWO_N: ScalarVector = ScalarVector::powers(Scalar::from(2u8), N); 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<Scalar> { pub(crate) fn challenge_products(w: &[Scalar], winv: &[Scalar]) -> Vec<Scalar> {

View File

@@ -1,4 +1,5 @@
use lazy_static::lazy_static; use std_shims::sync::OnceLock;
use rand_core::{RngCore, CryptoRng}; use rand_core::{RngCore, CryptoRng};
use zeroize::Zeroize; use zeroize::Zeroize;
@@ -14,9 +15,9 @@ use crate::{Commitment, ringct::bulletproofs::core::*};
include!(concat!(env!("OUT_DIR"), "/generators.rs")); include!(concat!(env!("OUT_DIR"), "/generators.rs"));
lazy_static! { static IP12_CELL: OnceLock<Scalar> = OnceLock::new();
static ref ONE_N: ScalarVector = ScalarVector(vec![Scalar::ONE; N]); pub(crate) fn IP12() -> Scalar {
static ref IP12: Scalar = inner_product(&ONE_N, &TWO_N); *IP12_CELL.get_or_init(|| inner_product(&ScalarVector(vec![Scalar::ONE; N]), TWO_N()))
} }
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
@@ -48,8 +49,9 @@ impl OriginalStruct {
let (sL, sR) = let (sL, sR) =
ScalarVector((0 .. (MN * 2)).map(|_| Scalar::random(&mut *rng)).collect::<Vec<_>>()).split(); ScalarVector((0 .. (MN * 2)).map(|_| Scalar::random(&mut *rng)).collect::<Vec<_>>()).split();
let (mut alpha, A) = alpha_rho(&mut *rng, &GENERATORS, &aL, &aR); let generators = GENERATORS();
let (mut rho, S) = alpha_rho(&mut *rng, &GENERATORS, &sL, &sR); 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 y = hash_cache(&mut cache, &[A.compress().to_bytes(), S.compress().to_bytes()]);
let mut cache = hash_to_scalar(&y.to_bytes()); let mut cache = hash_to_scalar(&y.to_bytes());
@@ -62,7 +64,7 @@ impl OriginalStruct {
let zpow = ScalarVector::powers(z, M + 2); let zpow = ScalarVector::powers(z, M + 2);
for j in 0 .. M { for j in 0 .. M {
for i in 0 .. N { 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 tau1 = Scalar::random(&mut *rng);
let mut tau2 = Scalar::random(&mut *rng); let mut tau2 = Scalar::random(&mut *rng);
let T1 = prove_multiexp(&[(t1, *H), (tau1, EdwardsPoint::generator())]); let T1 = prove_multiexp(&[(t1, H()), (tau1, EdwardsPoint::generator())]);
let T2 = prove_multiexp(&[(t2, *H), (tau2, EdwardsPoint::generator())]); let T2 = prove_multiexp(&[(t2, H()), (tau2, EdwardsPoint::generator())]);
let x = let x =
hash_cache(&mut cache, &[z.to_bytes(), T1.compress().to_bytes(), T2.compress().to_bytes()]); 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 yinv = y.invert().unwrap();
let yinvpow = ScalarVector::powers(yinv, MN); let yinvpow = ScalarVector::powers(yinv, MN);
let mut G_proof = GENERATORS.G[.. 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 H_proof = generators.H[.. a.len()].to_vec();
H_proof.iter_mut().zip(yinvpow.0.iter()).for_each(|(this_H, yinvpow)| *this_H *= yinvpow); 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 L = Vec::with_capacity(logMN);
let mut R = 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 ip1y = ScalarVector::powers(y, M * N).sum();
let mut k = -(zpow[2] * ip1y); let mut k = -(zpow[2] * ip1y);
for j in 1 ..= M { for j in 1 ..= M {
k -= zpow[j + 2] * *IP12; k -= zpow[j + 2] * IP12();
} }
let y1 = Scalar(self.t) - ((z * ip1y) + k); let y1 = Scalar(self.t) - ((z * ip1y) + k);
proof.push((-y1, *H)); proof.push((-y1, H()));
proof.push((-Scalar(self.taux), G)); proof.push((-Scalar(self.taux), G));
@@ -247,7 +249,7 @@ impl OriginalStruct {
proof = Vec::with_capacity(4 + (2 * (MN + logMN))); proof = Vec::with_capacity(4 + (2 * (MN + logMN)));
let z3 = (Scalar(self.t) - (Scalar(self.a) * Scalar(self.b))) * x_ip; 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(self.mu), G));
proof.push((Scalar::ONE, A)); proof.push((Scalar::ONE, A));
@@ -260,13 +262,14 @@ impl OriginalStruct {
let w_cache = challenge_products(&w, &winv); let w_cache = challenge_products(&w, &winv);
let generators = GENERATORS();
for i in 0 .. MN { for i in 0 .. MN {
let g = (Scalar(self.a) * w_cache[i]) + z; 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)]; 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]; h -= ((zpow[(i / N) + 2] * TWO_N()[i % N]) + (z * ypow[i])) * yinvpow[i];
proof.push((-h, GENERATORS.H[i])); proof.push((-h, generators.H[i]));
} }
} }

View File

@@ -1,4 +1,5 @@
use lazy_static::lazy_static; use std_shims::sync::OnceLock;
use rand_core::{RngCore, CryptoRng}; use rand_core::{RngCore, CryptoRng};
use zeroize::Zeroize; use zeroize::Zeroize;
@@ -17,15 +18,17 @@ use crate::{
include!(concat!(env!("OUT_DIR"), "/generators_plus.rs")); include!(concat!(env!("OUT_DIR"), "/generators_plus.rs"));
lazy_static! { static TRANSCRIPT_CELL: OnceLock<[u8; 32]> = OnceLock::new();
static ref TRANSCRIPT: [u8; 32] = pub(crate) fn TRANSCRIPT() -> [u8; 32] {
EdwardsPoint(raw_hash_to_point(hash(b"bulletproof_plus_transcript"))).compress().to_bytes(); *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 // TRANSCRIPT isn't a Scalar, so we need this alternative for the first hash
fn hash_plus<C: IntoIterator<Item = DalekPoint>>(commitments: C) -> (Scalar, Vec<EdwardsPoint>) { fn hash_plus<C: IntoIterator<Item = DalekPoint>>(commitments: C) -> (Scalar, Vec<EdwardsPoint>) {
let (cache, commitments) = hash_commitments(commitments); 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 // 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]; let mut d = vec![Scalar::ZERO; MN];
for j in 0 .. M { for j in 0 .. M {
for i in 0 .. N { 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)) (zpow, ScalarVector(d))
@@ -57,12 +60,14 @@ impl PlusStruct {
rng: &mut R, rng: &mut R,
commitments: &[Commitment], commitments: &[Commitment],
) -> PlusStruct { ) -> PlusStruct {
let generators = GENERATORS();
let (logMN, M, MN) = MN(commitments.len()); let (logMN, M, MN) = MN(commitments.len());
let (aL, aR) = bit_decompose(commitments); let (aL, aR) = bit_decompose(commitments);
let commitments_points = commitments.iter().map(Commitment::calculate).collect::<Vec<_>>(); let commitments_points = commitments.iter().map(Commitment::calculate).collect::<Vec<_>>();
let (mut cache, _) = hash_plus(commitments_points.clone()); 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 y = hash_cache(&mut cache, &[A.compress().to_bytes()]);
let mut cache = hash_to_scalar(&y.to_bytes()); let mut cache = hash_to_scalar(&y.to_bytes());
@@ -87,8 +92,8 @@ impl PlusStruct {
let yinv = y.invert().unwrap(); let yinv = y.invert().unwrap();
let yinvpow = ScalarVector::powers(yinv, MN); let yinvpow = ScalarVector::powers(yinv, MN);
let mut G_proof = GENERATORS.G[.. 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 H_proof = generators.H[.. a.len()].to_vec();
let mut L = Vec::with_capacity(logMN); let mut L = Vec::with_capacity(logMN);
let mut R = 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 (G_L, G_R) = G_proof.split_at(aL.len());
let (H_L, H_R) = H_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)); L_i.push((dL, G));
let L_i = prove_multiexp(&L_i); let L_i = prove_multiexp(&L_i);
L.push(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)); R_i.push((dR, G));
let R_i = prove_multiexp(&R_i); let R_i = prove_multiexp(&R_i);
R.push(R_i); R.push(R_i);
@@ -139,9 +144,9 @@ impl PlusStruct {
(r, G_proof[0]), (r, G_proof[0]),
(s, H_proof[0]), (s, H_proof[0]),
(d, G), (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 e = hash_cache(&mut cache, &[A1.compress().to_bytes(), B.compress().to_bytes()]);
let r1 = (a[0] * e) + r; let r1 = (a[0] * e) + r;
@@ -248,7 +253,7 @@ impl PlusStruct {
let y_sum = weighted_powers(y, MN).sum(); let y_sum = weighted_powers(y, MN).sum();
proof.push(( proof.push((
Scalar(self.r1 * y.0 * self.s1) + (esq * ((yMNy * z * d_sum) + ((zsq - z) * y_sum))), 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); let w_cache = challenge_products(&w, &winv);
@@ -259,11 +264,12 @@ impl PlusStruct {
let minus_esq_z = -esq_z; let minus_esq_z = -esq_z;
let mut minus_esq_y = minus_esq * yMN; let mut minus_esq_y = minus_esq * yMN;
let generators = GENERATORS();
for i in 0 .. MN { 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(( proof.push((
(e_s1 * w_cache[(!i) & (MN - 1)]) + minus_esq_z + (minus_esq_y * d[i]), (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; e_r1_y *= yinv;

View File

@@ -3,7 +3,6 @@
use core::ops::Deref; use core::ops::Deref;
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use lazy_static::lazy_static;
use thiserror::Error; use thiserror::Error;
use rand_core::{RngCore, CryptoRng}; use rand_core::{RngCore, CryptoRng};
@@ -18,8 +17,8 @@ use curve25519_dalek::{
}; };
use crate::{ use crate::{
Commitment, random_scalar, hash_to_scalar, wallet::decoys::Decoys, ringct::hash_to_point, INV_EIGHT, Commitment, random_scalar, hash_to_scalar, wallet::decoys::Decoys,
serialize::*, ringct::hash_to_point, serialize::*,
}; };
#[cfg(feature = "multisig")] #[cfg(feature = "multisig")]
@@ -29,10 +28,6 @@ pub use multisig::{ClsagDetails, ClsagAddendum, ClsagMultisig};
#[cfg(feature = "multisig")] #[cfg(feature = "multisig")]
pub(crate) use multisig::add_key_image_share; 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. /// Errors returned when CLSAG signing fails.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Error)] #[derive(Clone, Copy, PartialEq, Eq, Debug, Error)]
pub enum ClsagError { pub enum ClsagError {
@@ -103,7 +98,7 @@ fn core(
let n = ring.len(); let n = ring.len();
let images_precomp = VartimeEdwardsPrecomputation::new([I, D]); let images_precomp = VartimeEdwardsPrecomputation::new([I, D]);
let D = D * *INV_EIGHT; let D = D * INV_EIGHT();
// Generate the transcript // Generate the transcript
// Instead of generating multiple, a single transcript is created and then edited as needed // Instead of generating multiple, a single transcript is created and then edited as needed

View File

@@ -1,8 +1,6 @@
use std::collections::HashSet; use std_shims::{sync::OnceLock, collections::HashSet};
use futures::lock::{Mutex, MutexGuard}; use futures::lock::Mutex;
use lazy_static::lazy_static;
use rand_core::{RngCore, CryptoRng}; use rand_core::{RngCore, CryptoRng};
use rand_distr::{Distribution, Gamma}; 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 BLOCKS_PER_YEAR: usize = 365 * 24 * 60 * 60 / BLOCK_TIME;
const TIP_APPLICATION: f64 = (LOCK_WINDOW * BLOCK_TIME) as f64; const TIP_APPLICATION: f64 = (LOCK_WINDOW * BLOCK_TIME) as f64;
lazy_static! { // TODO: Expose an API to reset this in case a reorg occurs/the RPC fails/returns garbage
static ref GAMMA: Gamma<f64> = Gamma::new(19.28, 1.0 / 1.61).unwrap(); // TODO: Update this when scanning a block, as possible
// TODO: Expose an API to reset this in case a reorg occurs/the RPC fails/returns garbage static DISTRIBUTION_CELL: OnceLock<Mutex<Vec<u64>>> = OnceLock::new();
// TODO: Update this when scanning a block, as possible #[allow(non_snake_case)]
static ref DISTRIBUTION: Mutex<Vec<u64>> = Mutex::new(Vec::with_capacity(3000000)); fn DISTRIBUTION() -> &'static Mutex<Vec<u64>> {
DISTRIBUTION_CELL.get_or_init(|| Mutex::new(Vec::with_capacity(3000000)))
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
async fn select_n<'a, R: RngCore + CryptoRng, RPC: RpcConnection>( async fn select_n<'a, R: RngCore + CryptoRng, RPC: RpcConnection>(
rng: &mut R, rng: &mut R,
rpc: &Rpc<RPC>, rpc: &Rpc<RPC>,
distribution: &MutexGuard<'a, Vec<u64>>, distribution: &[u64],
height: usize, height: usize,
high: u64, high: u64,
per_second: f64, per_second: f64,
@@ -60,7 +59,7 @@ async fn select_n<'a, R: RngCore + CryptoRng, RPC: RpcConnection>(
} }
// Use a gamma distribution // Use a gamma distribution
let mut age = GAMMA.sample(rng).exp(); let mut age = Gamma::<f64>::new(19.28, 1.0 / 1.61).unwrap().sample(rng).exp();
if age > TIP_APPLICATION { if age > TIP_APPLICATION {
age -= TIP_APPLICATION; age -= TIP_APPLICATION;
} else { } else {
@@ -144,7 +143,7 @@ impl Decoys {
height: usize, height: usize,
inputs: &[SpendableOutput], inputs: &[SpendableOutput],
) -> Result<Vec<Decoys>, RpcError> { ) -> Result<Vec<Decoys>, RpcError> {
let mut distribution = DISTRIBUTION.lock().await; let mut distribution = DISTRIBUTION().lock().await;
let decoy_count = ring_len - 1; let decoy_count = ring_len - 1;

View File

@@ -1,7 +1,5 @@
use core::ops::Deref; use core::ops::Deref;
use std::collections::HashMap; use std_shims::{sync::OnceLock, collections::HashMap};
use lazy_static::lazy_static;
use zeroize::{Zeroize, Zeroizing}; use zeroize::{Zeroize, Zeroizing};
use rand_core::{RngCore, CryptoRng}; use rand_core::{RngCore, CryptoRng};
@@ -47,8 +45,11 @@ impl WordList {
} }
} }
lazy_static! { static LANGUAGES_CELL: OnceLock<HashMap<Language, WordList>> = OnceLock::new();
static ref LANGUAGES: HashMap<Language, WordList> = HashMap::from([ #[allow(non_snake_case)]
fn LANGUAGES() -> &'static HashMap<Language, WordList> {
LANGUAGES_CELL.get_or_init(|| {
HashMap::from([
(Language::Chinese, WordList::new(include!("./classic/zh.rs"), 1)), (Language::Chinese, WordList::new(include!("./classic/zh.rs"), 1)),
(Language::English, WordList::new(include!("./classic/en.rs"), 3)), (Language::English, WordList::new(include!("./classic/en.rs"), 3)),
(Language::Dutch, WordList::new(include!("./classic/nl.rs"), 4)), (Language::Dutch, WordList::new(include!("./classic/nl.rs"), 4)),
@@ -62,13 +63,14 @@ lazy_static! {
(Language::Esperanto, WordList::new(include!("./classic/eo.rs"), 4)), (Language::Esperanto, WordList::new(include!("./classic/eo.rs"), 4)),
(Language::Lojban, WordList::new(include!("./classic/jbo.rs"), 4)), (Language::Lojban, WordList::new(include!("./classic/jbo.rs"), 4)),
(Language::EnglishOld, WordList::new(include!("./classic/ang.rs"), 4)), (Language::EnglishOld, WordList::new(include!("./classic/ang.rs"), 4)),
]); ])
})
} }
#[cfg(test)] #[cfg(test)]
pub(crate) fn trim_by_lang(word: &str, lang: Language) -> String { pub(crate) fn trim_by_lang(word: &str, lang: Language) -> String {
if lang != Language::EnglishOld { if lang != Language::EnglishOld {
word.chars().take(LANGUAGES[&lang].unique_prefix_length).collect() word.chars().take(LANGUAGES()[&lang].unique_prefix_length).collect()
} else { } else {
word.to_string() word.to_string()
} }
@@ -92,7 +94,7 @@ fn key_to_seed(lang: Language, key: Zeroizing<Scalar>) -> ClassicSeed {
let bytes = Zeroizing::new(key.to_bytes()); let bytes = Zeroizing::new(key.to_bytes());
// get the language words // 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(); let list_len = u64::try_from(words.len()).unwrap();
// To store the found words & add the checksum word later. // To store the found words & add the checksum word later.
@@ -126,7 +128,7 @@ fn key_to_seed(lang: Language, key: Zeroizing<Scalar>) -> ClassicSeed {
// create a checksum word for all languages except old english // create a checksum word for all languages except old english
if lang != Language::EnglishOld { 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); 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![]); let mut matched_indices = Zeroizing::new(vec![]);
// Iterate through all the languages // 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.zeroize();
matched_indices.clear(); matched_indices.clear();

View File

@@ -1,7 +1,5 @@
use core::ops::Deref; use core::ops::Deref;
use std::collections::HashSet; use std_shims::{sync::OnceLock, collections::HashSet};
use lazy_static::lazy_static;
use zeroize::Zeroizing; use zeroize::Zeroizing;
use rand_core::OsRng; use rand_core::OsRng;
@@ -98,9 +96,7 @@ pub async fn rpc() -> Rpc<HttpRpc> {
rpc rpc
} }
lazy_static! { pub static SEQUENTIAL: OnceLock<Mutex<()>> = OnceLock::new();
pub static ref SEQUENTIAL: Mutex<()> = Mutex::new(());
}
#[macro_export] #[macro_export]
macro_rules! async_sequential { macro_rules! async_sequential {
@@ -108,7 +104,7 @@ macro_rules! async_sequential {
$( $(
#[tokio::test] #[tokio::test]
async fn $name() { 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(); let local = tokio::task::LocalSet::new();
local.run_until(async move { local.run_until(async move {
if let Err(err) = tokio::task::spawn_local(async move { $body }).await { if let Err(err) = tokio::task::spawn_local(async move { $body }).await {

View File

@@ -13,7 +13,8 @@ all-features = true
rustdoc-args = ["--cfg", "docsrs"] rustdoc-args = ["--cfg", "docsrs"]
[dependencies] [dependencies]
hashbrown = "0.13" spin = "0.9"
hashbrown = "0.14"
[features] [features]
std = [] std = []

View File

@@ -6,6 +6,7 @@
#[macro_use] #[macro_use]
pub extern crate alloc; pub extern crate alloc;
pub mod sync;
pub mod collections; pub mod collections;
pub mod io; pub mod io;

View File

@@ -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<T>(Mutex<T>);
impl<T> ShimMutex<T> {
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<T>(super::Mutex<()>, Option<T>);
impl<T> OnceLock<T> {
pub const fn new() -> OnceLock<T> {
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<F: FnOnce() -> 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::*;

View File

@@ -30,4 +30,4 @@ dkg = { path = "../../crypto/dkg", default-features = false }
# modular-frost = { path = "../../crypto/frost", default-features = false } # modular-frost = { path = "../../crypto/frost", default-features = false }
# frost-schnorrkel = { path = "../../crypto/schnorrkel", 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 }