mirror of
https://github.com/serai-dex/serai.git
synced 2024-12-22 03:29:34 +00:00
Rust 1.80
Some checks failed
Coordinator Tests / build (push) Has been cancelled
crypto/ Tests / test-crypto (push) Has been cancelled
Full Stack Tests / build (push) Has been cancelled
Lint / clippy (macos-13) (push) Has been cancelled
Lint / clippy (macos-14) (push) Has been cancelled
Lint / clippy (ubuntu-latest) (push) Has been cancelled
Lint / clippy (windows-latest) (push) Has been cancelled
Lint / deny (push) Has been cancelled
Lint / fmt (push) Has been cancelled
Lint / machete (push) Has been cancelled
Message Queue Tests / build (push) Has been cancelled
Monero Tests / unit-tests (push) Has been cancelled
Reproducible Runtime / build (push) Has been cancelled
Tests / test-infra (push) Has been cancelled
common/ Tests / test-common (push) Has been cancelled
Monero Tests / integration-tests (v0.17.3.2) (push) Has been cancelled
Monero Tests / integration-tests (v0.18.2.0) (push) Has been cancelled
networks/ Tests / test-networks (push) Has been cancelled
no-std build / build (push) Has been cancelled
Processor Tests / build (push) Has been cancelled
Tests / test-substrate (push) Has been cancelled
Tests / test-serai-client (push) Has been cancelled
Some checks failed
Coordinator Tests / build (push) Has been cancelled
crypto/ Tests / test-crypto (push) Has been cancelled
Full Stack Tests / build (push) Has been cancelled
Lint / clippy (macos-13) (push) Has been cancelled
Lint / clippy (macos-14) (push) Has been cancelled
Lint / clippy (ubuntu-latest) (push) Has been cancelled
Lint / clippy (windows-latest) (push) Has been cancelled
Lint / deny (push) Has been cancelled
Lint / fmt (push) Has been cancelled
Lint / machete (push) Has been cancelled
Message Queue Tests / build (push) Has been cancelled
Monero Tests / unit-tests (push) Has been cancelled
Reproducible Runtime / build (push) Has been cancelled
Tests / test-infra (push) Has been cancelled
common/ Tests / test-common (push) Has been cancelled
Monero Tests / integration-tests (v0.17.3.2) (push) Has been cancelled
Monero Tests / integration-tests (v0.18.2.0) (push) Has been cancelled
networks/ Tests / test-networks (push) Has been cancelled
no-std build / build (push) Has been cancelled
Processor Tests / build (push) Has been cancelled
Tests / test-substrate (push) Has been cancelled
Tests / test-serai-client (push) Has been cancelled
Preserves the fn accessors within the Monero crates so that we can use statics in some cfgs yet not all (in order to provide support for more low-memory devices) with the exception of `H` (which truly should be cached).
This commit is contained in:
parent
6f34c2ff77
commit
880565cb81
38 changed files with 147 additions and 195 deletions
|
@ -17,7 +17,7 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
spin = { version = "0.9", default-features = false, features = ["use_ticket_mutex", "once"] }
|
||||
spin = { version = "0.9", default-features = false, features = ["use_ticket_mutex", "lazy"] }
|
||||
hashbrown = { version = "0.14", default-features = false, features = ["ahash", "inline-more"] }
|
||||
|
||||
[features]
|
||||
|
|
|
@ -26,27 +26,6 @@ mod mutex_shim {
|
|||
pub use mutex_shim::{ShimMutex as Mutex, MutexGuard};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub use std::sync::OnceLock;
|
||||
pub use std::sync::LazyLock;
|
||||
#[cfg(not(feature = "std"))]
|
||||
mod oncelock_shim {
|
||||
use spin::Once;
|
||||
|
||||
pub struct OnceLock<T>(Once<T>);
|
||||
impl<T> OnceLock<T> {
|
||||
pub const fn new() -> OnceLock<T> {
|
||||
OnceLock(Once::new())
|
||||
}
|
||||
pub fn get(&self) -> Option<&T> {
|
||||
self.0.poll()
|
||||
}
|
||||
pub fn get_mut(&mut self) -> Option<&mut T> {
|
||||
self.0.get_mut()
|
||||
}
|
||||
|
||||
pub fn get_or_init<F: FnOnce() -> T>(&self, f: F) -> &T {
|
||||
self.0.call_once(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub use oncelock_shim::*;
|
||||
pub use spin::Lazy as LazyLock;
|
||||
|
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
repository = "https://github.com/serai-dex/serai/tree/develop/networks/bitcoin"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>", "Vrx <vrx00@proton.me>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.79"
|
||||
rust-version = "1.80"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
use std::sync::OnceLock;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use bitcoin_serai::rpc::Rpc;
|
||||
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
static SEQUENTIAL_CELL: OnceLock<Mutex<()>> = OnceLock::new();
|
||||
#[allow(non_snake_case)]
|
||||
pub fn SEQUENTIAL() -> &'static Mutex<()> {
|
||||
SEQUENTIAL_CELL.get_or_init(|| Mutex::new(()))
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub(crate) static SEQUENTIAL: LazyLock<Mutex<()>> = LazyLock::new(|| Mutex::new(()));
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) async fn rpc() -> Rpc {
|
||||
|
@ -34,7 +31,7 @@ macro_rules! async_sequential {
|
|||
$(
|
||||
#[tokio::test]
|
||||
async fn $name() {
|
||||
let guard = runner::SEQUENTIAL().lock().await;
|
||||
let guard = runner::SEQUENTIAL.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 {
|
||||
|
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
repository = "https://github.com/serai-dex/serai/tree/develop/networks/monero"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.79"
|
||||
rust-version = "1.80"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#![deny(missing_docs)]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
use std_shims::{sync::OnceLock, vec::Vec};
|
||||
use std_shims::{sync::LazyLock, vec::Vec};
|
||||
|
||||
use sha3::{Digest, Keccak256};
|
||||
|
||||
|
@ -21,33 +21,30 @@ fn keccak256(data: &[u8]) -> [u8; 32] {
|
|||
Keccak256::digest(data).into()
|
||||
}
|
||||
|
||||
static H_CELL: OnceLock<EdwardsPoint> = OnceLock::new();
|
||||
/// Monero's `H` generator.
|
||||
///
|
||||
/// Contrary to convention (`G` for values, `H` for randomness), `H` is used by Monero for amounts
|
||||
/// within Pedersen commitments.
|
||||
#[allow(non_snake_case)]
|
||||
pub fn H() -> EdwardsPoint {
|
||||
*H_CELL.get_or_init(|| {
|
||||
decompress_point(keccak256(&ED25519_BASEPOINT_POINT.compress().to_bytes()))
|
||||
.unwrap()
|
||||
.mul_by_cofactor()
|
||||
})
|
||||
}
|
||||
pub static H: LazyLock<EdwardsPoint> = LazyLock::new(|| {
|
||||
decompress_point(keccak256(&ED25519_BASEPOINT_POINT.compress().to_bytes()))
|
||||
.unwrap()
|
||||
.mul_by_cofactor()
|
||||
});
|
||||
|
||||
static H_POW_2_CELL: OnceLock<[EdwardsPoint; 64]> = OnceLock::new();
|
||||
static H_POW_2_CELL: LazyLock<[EdwardsPoint; 64]> = LazyLock::new(|| {
|
||||
let mut res = [*H; 64];
|
||||
for i in 1 .. 64 {
|
||||
res[i] = res[i - 1] + res[i - 1];
|
||||
}
|
||||
res
|
||||
});
|
||||
/// Monero's `H` generator, multiplied by 2**i for i in 1 ..= 64.
|
||||
///
|
||||
/// This table is useful when working with amounts, which are u64s.
|
||||
#[allow(non_snake_case)]
|
||||
pub fn H_pow_2() -> &'static [EdwardsPoint; 64] {
|
||||
H_POW_2_CELL.get_or_init(|| {
|
||||
let mut res = [H(); 64];
|
||||
for i in 1 .. 64 {
|
||||
res[i] = res[i - 1] + res[i - 1];
|
||||
}
|
||||
res
|
||||
})
|
||||
&H_POW_2_CELL
|
||||
}
|
||||
|
||||
/// The maximum amount of commitments provable for within a single range proof.
|
||||
|
@ -74,7 +71,7 @@ pub fn bulletproofs_generators(dst: &'static [u8]) -> Generators {
|
|||
// The maximum amount of bits used within a single range proof.
|
||||
const MAX_MN: usize = MAX_COMMITMENTS * COMMITMENT_BITS;
|
||||
|
||||
let mut preimage = H().compress().to_bytes().to_vec();
|
||||
let mut preimage = H.compress().to_bytes().to_vec();
|
||||
preimage.extend(dst);
|
||||
|
||||
let mut res = Generators { G: Vec::with_capacity(MAX_MN), H: Vec::with_capacity(MAX_MN) };
|
||||
|
|
|
@ -6,6 +6,7 @@ license = "MIT"
|
|||
repository = "https://github.com/serai-dex/serai/tree/develop/networks/monero/io"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.80"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
repository = "https://github.com/serai-dex/serai/tree/develop/networks/monero/primitives"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.79"
|
||||
rust-version = "1.80"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
use std_shims::{io, vec::Vec};
|
||||
#[cfg(feature = "std")]
|
||||
use std_shims::sync::OnceLock;
|
||||
use std_shims::sync::LazyLock;
|
||||
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
|
||||
|
@ -28,15 +28,15 @@ mod tests;
|
|||
|
||||
// On std, we cache some variables in statics.
|
||||
#[cfg(feature = "std")]
|
||||
static INV_EIGHT_CELL: OnceLock<Scalar> = OnceLock::new();
|
||||
/// The inverse of 8 over l.
|
||||
static INV_EIGHT_CELL: LazyLock<Scalar> = LazyLock::new(|| Scalar::from(8u8).invert());
|
||||
/// The inverse of 8 over l, the prime factor of the order of Ed25519.
|
||||
#[cfg(feature = "std")]
|
||||
#[allow(non_snake_case)]
|
||||
pub fn INV_EIGHT() -> Scalar {
|
||||
*INV_EIGHT_CELL.get_or_init(|| Scalar::from(8u8).invert())
|
||||
*INV_EIGHT_CELL
|
||||
}
|
||||
// In no-std environments, we prefer the reduced memory use and calculate it ad-hoc.
|
||||
/// The inverse of 8 over l.
|
||||
/// The inverse of 8 over l, the prime factor of the order of Ed25519.
|
||||
#[cfg(not(feature = "std"))]
|
||||
#[allow(non_snake_case)]
|
||||
pub fn INV_EIGHT() -> Scalar {
|
||||
|
@ -44,12 +44,13 @@ pub fn INV_EIGHT() -> Scalar {
|
|||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
static G_PRECOMP_CELL: OnceLock<VartimeEdwardsPrecomputation> = OnceLock::new();
|
||||
static G_PRECOMP_CELL: LazyLock<VartimeEdwardsPrecomputation> =
|
||||
LazyLock::new(|| VartimeEdwardsPrecomputation::new([ED25519_BASEPOINT_POINT]));
|
||||
/// A cached (if std) pre-computation of the Ed25519 generator, G.
|
||||
#[cfg(feature = "std")]
|
||||
#[allow(non_snake_case)]
|
||||
pub fn G_PRECOMP() -> &'static VartimeEdwardsPrecomputation {
|
||||
G_PRECOMP_CELL.get_or_init(|| VartimeEdwardsPrecomputation::new([ED25519_BASEPOINT_POINT]))
|
||||
&G_PRECOMP_CELL
|
||||
}
|
||||
/// A cached (if std) pre-computation of the Ed25519 generator, G.
|
||||
#[cfg(not(feature = "std"))]
|
||||
|
@ -105,7 +106,7 @@ impl Commitment {
|
|||
|
||||
/// Calculate the Pedersen commitment, as a point, from this transparent structure.
|
||||
pub fn calculate(&self) -> EdwardsPoint {
|
||||
EdwardsPoint::vartime_double_scalar_mul_basepoint(&Scalar::from(self.amount), &H(), &self.mask)
|
||||
EdwardsPoint::vartime_double_scalar_mul_basepoint(&Scalar::from(self.amount), &H, &self.mask)
|
||||
}
|
||||
|
||||
/// Write the Commitment.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use core::cmp::Ordering;
|
||||
use std_shims::{
|
||||
sync::OnceLock,
|
||||
sync::LazyLock,
|
||||
io::{self, *},
|
||||
};
|
||||
|
||||
|
@ -10,18 +10,14 @@ use curve25519_dalek::scalar::Scalar;
|
|||
|
||||
use monero_io::*;
|
||||
|
||||
static PRECOMPUTED_SCALARS_CELL: OnceLock<[Scalar; 8]> = OnceLock::new();
|
||||
// Precomputed scalars used to recover an incorrectly reduced scalar.
|
||||
#[allow(non_snake_case)]
|
||||
fn PRECOMPUTED_SCALARS() -> [Scalar; 8] {
|
||||
*PRECOMPUTED_SCALARS_CELL.get_or_init(|| {
|
||||
let mut precomputed_scalars = [Scalar::ONE; 8];
|
||||
for (i, scalar) in precomputed_scalars.iter_mut().enumerate().skip(1) {
|
||||
*scalar = Scalar::from(u8::try_from((i * 2) + 1).unwrap());
|
||||
}
|
||||
precomputed_scalars
|
||||
})
|
||||
}
|
||||
static PRECOMPUTED_SCALARS: LazyLock<[Scalar; 8]> = LazyLock::new(|| {
|
||||
let mut precomputed_scalars = [Scalar::ONE; 8];
|
||||
for (i, scalar) in precomputed_scalars.iter_mut().enumerate().skip(1) {
|
||||
*scalar = Scalar::from(u8::try_from((i * 2) + 1).unwrap());
|
||||
}
|
||||
precomputed_scalars
|
||||
});
|
||||
|
||||
/// An unreduced scalar.
|
||||
///
|
||||
|
@ -127,14 +123,12 @@ impl UnreducedScalar {
|
|||
return Scalar::from_bytes_mod_order(self.0);
|
||||
}
|
||||
|
||||
let precomputed_scalars = PRECOMPUTED_SCALARS();
|
||||
|
||||
let mut recovered = Scalar::ZERO;
|
||||
for &numb in self.non_adjacent_form().iter().rev() {
|
||||
recovered += recovered;
|
||||
match numb.cmp(&0) {
|
||||
Ordering::Greater => recovered += precomputed_scalars[usize::try_from(numb).unwrap() / 2],
|
||||
Ordering::Less => recovered -= precomputed_scalars[usize::try_from(-numb).unwrap() / 2],
|
||||
Ordering::Greater => recovered += PRECOMPUTED_SCALARS[usize::try_from(numb).unwrap() / 2],
|
||||
Ordering::Less => recovered -= PRECOMPUTED_SCALARS[usize::try_from(-numb).unwrap() / 2],
|
||||
Ordering::Equal => (),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
repository = "https://github.com/serai-dex/serai/tree/develop/networks/monero/ringct/borromean"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.79"
|
||||
rust-version = "1.80"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
repository = "https://github.com/serai-dex/serai/tree/develop/networks/monero/ringct/bulletproofs"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.79"
|
||||
rust-version = "1.80"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
|
|
@ -40,17 +40,14 @@ fn generators(prefix: &'static str, path: &str) {
|
|||
.write_all(
|
||||
format!(
|
||||
"
|
||||
static GENERATORS_CELL: OnceLock<Generators> = OnceLock::new();
|
||||
pub(crate) fn GENERATORS() -> &'static Generators {{
|
||||
GENERATORS_CELL.get_or_init(|| Generators {{
|
||||
G: std_shims::vec![
|
||||
{G_str}
|
||||
],
|
||||
H: std_shims::vec![
|
||||
{H_str}
|
||||
],
|
||||
}})
|
||||
}}
|
||||
pub(crate) static GENERATORS: LazyLock<Generators> = LazyLock::new(|| Generators {{
|
||||
G: std_shims::vec![
|
||||
{G_str}
|
||||
],
|
||||
H: std_shims::vec![
|
||||
{H_str}
|
||||
],
|
||||
}});
|
||||
",
|
||||
)
|
||||
.as_bytes(),
|
||||
|
@ -67,12 +64,9 @@ fn generators(prefix: &'static str, path: &str) {
|
|||
.write_all(
|
||||
format!(
|
||||
r#"
|
||||
static GENERATORS_CELL: OnceLock<Generators> = OnceLock::new();
|
||||
pub(crate) fn GENERATORS() -> &'static Generators {{
|
||||
GENERATORS_CELL.get_or_init(|| {{
|
||||
monero_generators::bulletproofs_generators(b"{prefix}")
|
||||
}})
|
||||
}}
|
||||
pub(crate) static GENERATORS: LazyLock<Generators> = LazyLock::new(|| {{
|
||||
monero_generators::bulletproofs_generators(b"{prefix}")
|
||||
}});
|
||||
"#,
|
||||
)
|
||||
.as_bytes(),
|
||||
|
|
|
@ -7,7 +7,7 @@ use curve25519_dalek::{
|
|||
edwards::EdwardsPoint,
|
||||
};
|
||||
|
||||
use monero_generators::{H, Generators};
|
||||
use monero_generators::{H as MONERO_H, Generators};
|
||||
|
||||
use crate::{original, plus};
|
||||
|
||||
|
@ -57,7 +57,7 @@ pub(crate) struct BulletproofsBatchVerifier(pub(crate) InternalBatchVerifier);
|
|||
impl BulletproofsBatchVerifier {
|
||||
#[must_use]
|
||||
pub(crate) fn verify(self) -> bool {
|
||||
self.0.verify(ED25519_BASEPOINT_POINT, H(), original::GENERATORS())
|
||||
self.0.verify(ED25519_BASEPOINT_POINT, *MONERO_H, &original::GENERATORS)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ impl BulletproofsPlusBatchVerifier {
|
|||
pub(crate) fn verify(self) -> bool {
|
||||
// Bulletproofs+ is written as per the paper, with G for the value and H for the mask
|
||||
// Monero uses H for the value and G for the mask
|
||||
self.0.verify(H(), ED25519_BASEPOINT_POINT, plus::GENERATORS())
|
||||
self.0.verify(*MONERO_H, ED25519_BASEPOINT_POINT, &plus::GENERATORS)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -96,13 +96,13 @@ impl IpStatement {
|
|||
mut transcript: Scalar,
|
||||
witness: IpWitness,
|
||||
) -> Result<IpProof, IpError> {
|
||||
let generators = crate::original::GENERATORS();
|
||||
let generators = &crate::original::GENERATORS;
|
||||
let g_bold_slice = &generators.G[.. witness.a.len()];
|
||||
let h_bold_slice = &generators.H[.. witness.a.len()];
|
||||
|
||||
let (mut g_bold, mut h_bold, u, mut a, mut b) = {
|
||||
let IpStatement { h_bold_weights, u } = self;
|
||||
let u = H() * u;
|
||||
let u = *H * u;
|
||||
|
||||
// Ensure we have the exact amount of weights
|
||||
if h_bold_weights.len() != g_bold_slice.len() {
|
||||
|
@ -218,7 +218,7 @@ impl IpStatement {
|
|||
verifier_weight: Scalar,
|
||||
proof: IpProof,
|
||||
) -> Result<(), IpError> {
|
||||
let generators = crate::original::GENERATORS();
|
||||
let generators = &crate::original::GENERATORS;
|
||||
let g_bold_slice = &generators.G[.. ip_rows];
|
||||
let h_bold_slice = &generators.H[.. ip_rows];
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std_shims::{sync::OnceLock, vec::Vec};
|
||||
use std_shims::{sync::LazyLock, vec::Vec};
|
||||
|
||||
use rand_core::{RngCore, CryptoRng};
|
||||
|
||||
|
@ -6,7 +6,7 @@ use zeroize::Zeroize;
|
|||
|
||||
use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, Scalar, EdwardsPoint};
|
||||
|
||||
use monero_generators::{H, Generators, MAX_COMMITMENTS, COMMITMENT_BITS};
|
||||
use monero_generators::{H as MONERO_H, Generators, MAX_COMMITMENTS, COMMITMENT_BITS};
|
||||
use monero_primitives::{Commitment, INV_EIGHT, keccak256_to_scalar};
|
||||
use crate::{core::multiexp, scalar_vector::ScalarVector, BulletproofsBatchVerifier};
|
||||
|
||||
|
@ -107,7 +107,7 @@ impl<'a> AggregateRangeStatement<'a> {
|
|||
None?
|
||||
};
|
||||
|
||||
let generators = GENERATORS();
|
||||
let generators = &GENERATORS;
|
||||
|
||||
let (mut transcript, _) = self.initial_transcript();
|
||||
|
||||
|
@ -186,7 +186,7 @@ impl<'a> AggregateRangeStatement<'a> {
|
|||
|
||||
let tau_1 = Scalar::random(&mut *rng);
|
||||
let T1 = {
|
||||
let mut T1_terms = [(t1, H()), (tau_1, ED25519_BASEPOINT_POINT)];
|
||||
let mut T1_terms = [(t1, *MONERO_H), (tau_1, ED25519_BASEPOINT_POINT)];
|
||||
for term in &mut T1_terms {
|
||||
term.0 *= INV_EIGHT();
|
||||
}
|
||||
|
@ -196,7 +196,7 @@ impl<'a> AggregateRangeStatement<'a> {
|
|||
};
|
||||
let tau_2 = Scalar::random(&mut *rng);
|
||||
let T2 = {
|
||||
let mut T2_terms = [(t2, H()), (tau_2, ED25519_BASEPOINT_POINT)];
|
||||
let mut T2_terms = [(t2, *MONERO_H), (tau_2, ED25519_BASEPOINT_POINT)];
|
||||
for term in &mut T2_terms {
|
||||
term.0 *= INV_EIGHT();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#![allow(non_snake_case)]
|
||||
|
||||
use std_shims::sync::OnceLock;
|
||||
use std_shims::sync::LazyLock;
|
||||
|
||||
use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, scalar::Scalar, edwards::EdwardsPoint};
|
||||
|
||||
|
@ -39,7 +39,7 @@ include!(concat!(env!("OUT_DIR"), "/generators_plus.rs"));
|
|||
impl BpPlusGenerators {
|
||||
#[allow(clippy::new_without_default)]
|
||||
pub(crate) fn new() -> Self {
|
||||
let gens = GENERATORS();
|
||||
let gens = &GENERATORS;
|
||||
BpPlusGenerators { g_bold: &gens.G, h_bold: &gens.H }
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ impl BpPlusGenerators {
|
|||
}
|
||||
|
||||
pub(crate) fn g() -> EdwardsPoint {
|
||||
H()
|
||||
*H
|
||||
}
|
||||
|
||||
pub(crate) fn h() -> EdwardsPoint {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std_shims::{sync::OnceLock, vec::Vec};
|
||||
use std_shims::{sync::LazyLock, vec::Vec};
|
||||
|
||||
use curve25519_dalek::{scalar::Scalar, edwards::EdwardsPoint};
|
||||
|
||||
|
@ -6,15 +6,12 @@ use monero_generators::hash_to_point;
|
|||
use monero_primitives::{keccak256, keccak256_to_scalar};
|
||||
|
||||
// Monero starts BP+ transcripts with the following constant.
|
||||
static TRANSCRIPT_CELL: OnceLock<[u8; 32]> = OnceLock::new();
|
||||
pub(crate) fn TRANSCRIPT() -> [u8; 32] {
|
||||
// Why this uses a hash_to_point is completely unknown.
|
||||
*TRANSCRIPT_CELL
|
||||
.get_or_init(|| hash_to_point(keccak256(b"bulletproof_plus_transcript")).compress().to_bytes())
|
||||
}
|
||||
// Why this uses a hash_to_point is completely unknown.
|
||||
pub(crate) static TRANSCRIPT: LazyLock<[u8; 32]> =
|
||||
LazyLock::new(|| hash_to_point(keccak256(b"bulletproof_plus_transcript")).compress().to_bytes());
|
||||
|
||||
pub(crate) fn initial_transcript(commitments: core::slice::Iter<'_, EdwardsPoint>) -> Scalar {
|
||||
let commitments_hash =
|
||||
keccak256_to_scalar(commitments.flat_map(|V| V.compress().to_bytes()).collect::<Vec<_>>());
|
||||
keccak256_to_scalar([TRANSCRIPT().as_ref(), &commitments_hash.to_bytes()].concat())
|
||||
keccak256_to_scalar([TRANSCRIPT.as_ref(), &commitments_hash.to_bytes()].concat())
|
||||
}
|
||||
|
|
|
@ -35,12 +35,12 @@ fn test_zero_inner_product() {
|
|||
#[test]
|
||||
fn test_inner_product() {
|
||||
// P = sum(g_bold * a, h_bold * b, g * u * <a, b>)
|
||||
let generators = GENERATORS();
|
||||
let generators = &GENERATORS;
|
||||
let mut verifier = BulletproofsBatchVerifier::default();
|
||||
verifier.0.g_bold = vec![Scalar::ZERO; 32];
|
||||
verifier.0.h_bold = vec![Scalar::ZERO; 32];
|
||||
for i in [1, 2, 4, 8, 16, 32] {
|
||||
let g = H();
|
||||
let g = *H;
|
||||
let mut g_bold = vec![];
|
||||
let mut h_bold = vec![];
|
||||
for i in 0 .. i {
|
||||
|
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
repository = "https://github.com/serai-dex/serai/tree/develop/networks/monero/ringct/clsag"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.79"
|
||||
rust-version = "1.80"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
repository = "https://github.com/serai-dex/serai/tree/develop/networks/monero/ringct/mlsag"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.79"
|
||||
rust-version = "1.80"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
|
|
@ -203,7 +203,7 @@ impl AggregateRingMatrixBuilder {
|
|||
AggregateRingMatrixBuilder {
|
||||
key_ring: vec![],
|
||||
amounts_ring: vec![],
|
||||
sum_out: commitments.iter().sum::<EdwardsPoint>() + (H() * Scalar::from(fee)),
|
||||
sum_out: commitments.iter().sum::<EdwardsPoint>() + (*H * Scalar::from(fee)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
repository = "https://github.com/serai-dex/serai/tree/develop/networks/monero/rpc"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.79"
|
||||
rust-version = "1.80"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
repository = "https://github.com/serai-dex/serai/tree/develop/networks/monero/rpc/simple-request"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.79"
|
||||
rust-version = "1.80"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::sync::OnceLock;
|
||||
use std::sync::LazyLock;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use monero_address::{Network, MoneroAddress};
|
||||
|
@ -8,7 +8,7 @@ use monero_address::{Network, MoneroAddress};
|
|||
// Accordingly, we test monero-rpc here (implicitly testing the simple-request transport)
|
||||
use monero_simple_request_rpc::*;
|
||||
|
||||
static SEQUENTIAL: OnceLock<Mutex<()>> = OnceLock::new();
|
||||
static SEQUENTIAL: LazyLock<Mutex<()>> = LazyLock::new(|| Mutex::new(()));
|
||||
|
||||
const ADDRESS: &str =
|
||||
"4B33mFPMq6mKi7Eiyd5XuyKRVMGVZz1Rqb9ZTyGApXW5d1aT7UBDZ89ewmnWFkzJ5wPd2SFbn313vCT8a4E2Qf4KQH4pNey";
|
||||
|
@ -17,7 +17,7 @@ const ADDRESS: &str =
|
|||
async fn test_rpc() {
|
||||
use monero_rpc::Rpc;
|
||||
|
||||
let guard = SEQUENTIAL.get_or_init(|| Mutex::new(())).lock().await;
|
||||
let guard = SEQUENTIAL.lock().await;
|
||||
|
||||
let rpc =
|
||||
SimpleRequestRpc::new("http://serai:seraidex@127.0.0.1:18081".to_string()).await.unwrap();
|
||||
|
@ -68,7 +68,7 @@ async fn test_rpc() {
|
|||
async fn test_decoy_rpc() {
|
||||
use monero_rpc::{Rpc, DecoyRpc};
|
||||
|
||||
let guard = SEQUENTIAL.get_or_init(|| Mutex::new(())).lock().await;
|
||||
let guard = SEQUENTIAL.lock().await;
|
||||
|
||||
let rpc =
|
||||
SimpleRequestRpc::new("http://serai:seraidex@127.0.0.1:18081".to_string()).await.unwrap();
|
||||
|
@ -122,7 +122,7 @@ async fn test_decoy_rpc() {
|
|||
async fn test_zero_out_tx_o_indexes() {
|
||||
use monero_rpc::Rpc;
|
||||
|
||||
let guard = SEQUENTIAL.get_or_init(|| Mutex::new(())).lock().await;
|
||||
let guard = SEQUENTIAL.lock().await;
|
||||
|
||||
let rpc = SimpleRequestRpc::new("https://node.sethforprivacy.com".to_string()).await.unwrap();
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
repository = "https://github.com/serai-dex/serai/tree/develop/networks/monero/verify-chain"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.79"
|
||||
rust-version = "1.80"
|
||||
publish = false
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
|
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
repository = "https://github.com/serai-dex/serai/tree/develop/networks/monero/wallet"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.79"
|
||||
rust-version = "1.80"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
repository = "https://github.com/serai-dex/serai/tree/develop/networks/monero/wallet/address"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.79"
|
||||
rust-version = "1.80"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
repository = "https://github.com/serai-dex/serai/tree/develop/networks/monero/wallet/polyseed"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.79"
|
||||
rust-version = "1.80"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
use core::fmt;
|
||||
use std_shims::{sync::OnceLock, string::String, collections::HashMap};
|
||||
use std_shims::{sync::LazyLock, string::String, collections::HashMap};
|
||||
#[cfg(feature = "std")]
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
|
@ -163,30 +163,26 @@ impl WordList {
|
|||
}
|
||||
}
|
||||
|
||||
static LANGUAGES_CELL: OnceLock<HashMap<Language, WordList>> = OnceLock::new();
|
||||
#[allow(non_snake_case)]
|
||||
fn LANGUAGES() -> &'static HashMap<Language, WordList> {
|
||||
LANGUAGES_CELL.get_or_init(|| {
|
||||
HashMap::from([
|
||||
(Language::Czech, WordList::new(include!("./words/cs.rs"), true, false)),
|
||||
(Language::French, WordList::new(include!("./words/fr.rs"), true, true)),
|
||||
(Language::Korean, WordList::new(include!("./words/ko.rs"), false, false)),
|
||||
(Language::English, WordList::new(include!("./words/en.rs"), true, false)),
|
||||
(Language::Italian, WordList::new(include!("./words/it.rs"), true, false)),
|
||||
(Language::Spanish, WordList::new(include!("./words/es.rs"), true, true)),
|
||||
(Language::Japanese, WordList::new(include!("./words/ja.rs"), false, false)),
|
||||
(Language::Portuguese, WordList::new(include!("./words/pt.rs"), true, false)),
|
||||
(
|
||||
Language::ChineseSimplified,
|
||||
WordList::new(include!("./words/zh_simplified.rs"), false, false),
|
||||
),
|
||||
(
|
||||
Language::ChineseTraditional,
|
||||
WordList::new(include!("./words/zh_traditional.rs"), false, false),
|
||||
),
|
||||
])
|
||||
})
|
||||
}
|
||||
static LANGUAGES: LazyLock<HashMap<Language, WordList>> = LazyLock::new(|| {
|
||||
HashMap::from([
|
||||
(Language::Czech, WordList::new(include!("./words/cs.rs"), true, false)),
|
||||
(Language::French, WordList::new(include!("./words/fr.rs"), true, true)),
|
||||
(Language::Korean, WordList::new(include!("./words/ko.rs"), false, false)),
|
||||
(Language::English, WordList::new(include!("./words/en.rs"), true, false)),
|
||||
(Language::Italian, WordList::new(include!("./words/it.rs"), true, false)),
|
||||
(Language::Spanish, WordList::new(include!("./words/es.rs"), true, true)),
|
||||
(Language::Japanese, WordList::new(include!("./words/ja.rs"), false, false)),
|
||||
(Language::Portuguese, WordList::new(include!("./words/pt.rs"), true, false)),
|
||||
(
|
||||
Language::ChineseSimplified,
|
||||
WordList::new(include!("./words/zh_simplified.rs"), false, false),
|
||||
),
|
||||
(
|
||||
Language::ChineseTraditional,
|
||||
WordList::new(include!("./words/zh_traditional.rs"), false, false),
|
||||
),
|
||||
])
|
||||
});
|
||||
|
||||
/// A Polyseed.
|
||||
#[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
|
||||
|
@ -317,7 +313,7 @@ impl Polyseed {
|
|||
let mut poly = [0; POLYSEED_LENGTH];
|
||||
|
||||
// Validate words are in the lang word list
|
||||
let lang_word_list: &WordList = &LANGUAGES()[&lang];
|
||||
let lang_word_list: &WordList = &LANGUAGES[&lang];
|
||||
for (i, word) in seed.split_whitespace().enumerate() {
|
||||
// Find the word's index
|
||||
fn check_if_matches<S: AsRef<str>, I: Iterator<Item = S>>(
|
||||
|
@ -464,7 +460,7 @@ impl Polyseed {
|
|||
|
||||
// Output words
|
||||
let mut seed = Zeroizing::new(String::new());
|
||||
let words = &LANGUAGES()[&self.language].words;
|
||||
let words = &LANGUAGES[&self.language].words;
|
||||
for i in 0 .. poly.len() {
|
||||
seed.push_str(words[usize::from(poly[i])]);
|
||||
if i < poly.len() - 1 {
|
||||
|
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
repository = "https://github.com/serai-dex/serai/tree/develop/networks/monero/wallet/seed"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.79"
|
||||
rust-version = "1.80"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
use core::{ops::Deref, fmt};
|
||||
use std_shims::{
|
||||
sync::OnceLock,
|
||||
sync::LazyLock,
|
||||
vec,
|
||||
vec::Vec,
|
||||
string::{String, ToString},
|
||||
|
@ -102,27 +102,23 @@ impl WordList {
|
|||
}
|
||||
}
|
||||
|
||||
static LANGUAGES_CELL: OnceLock<HashMap<Language, WordList>> = OnceLock::new();
|
||||
#[allow(non_snake_case)]
|
||||
fn LANGUAGES() -> &'static HashMap<Language, WordList> {
|
||||
LANGUAGES_CELL.get_or_init(|| {
|
||||
HashMap::from([
|
||||
(Language::Chinese, WordList::new(include!("./words/zh.rs"), 1)),
|
||||
(Language::English, WordList::new(include!("./words/en.rs"), 3)),
|
||||
(Language::Dutch, WordList::new(include!("./words/nl.rs"), 4)),
|
||||
(Language::French, WordList::new(include!("./words/fr.rs"), 4)),
|
||||
(Language::Spanish, WordList::new(include!("./words/es.rs"), 4)),
|
||||
(Language::German, WordList::new(include!("./words/de.rs"), 4)),
|
||||
(Language::Italian, WordList::new(include!("./words/it.rs"), 4)),
|
||||
(Language::Portuguese, WordList::new(include!("./words/pt.rs"), 4)),
|
||||
(Language::Japanese, WordList::new(include!("./words/ja.rs"), 3)),
|
||||
(Language::Russian, WordList::new(include!("./words/ru.rs"), 4)),
|
||||
(Language::Esperanto, WordList::new(include!("./words/eo.rs"), 4)),
|
||||
(Language::Lojban, WordList::new(include!("./words/jbo.rs"), 4)),
|
||||
(Language::DeprecatedEnglish, WordList::new(include!("./words/ang.rs"), 4)),
|
||||
])
|
||||
})
|
||||
}
|
||||
static LANGUAGES: LazyLock<HashMap<Language, WordList>> = LazyLock::new(|| {
|
||||
HashMap::from([
|
||||
(Language::Chinese, WordList::new(include!("./words/zh.rs"), 1)),
|
||||
(Language::English, WordList::new(include!("./words/en.rs"), 3)),
|
||||
(Language::Dutch, WordList::new(include!("./words/nl.rs"), 4)),
|
||||
(Language::French, WordList::new(include!("./words/fr.rs"), 4)),
|
||||
(Language::Spanish, WordList::new(include!("./words/es.rs"), 4)),
|
||||
(Language::German, WordList::new(include!("./words/de.rs"), 4)),
|
||||
(Language::Italian, WordList::new(include!("./words/it.rs"), 4)),
|
||||
(Language::Portuguese, WordList::new(include!("./words/pt.rs"), 4)),
|
||||
(Language::Japanese, WordList::new(include!("./words/ja.rs"), 3)),
|
||||
(Language::Russian, WordList::new(include!("./words/ru.rs"), 4)),
|
||||
(Language::Esperanto, WordList::new(include!("./words/eo.rs"), 4)),
|
||||
(Language::Lojban, WordList::new(include!("./words/jbo.rs"), 4)),
|
||||
(Language::DeprecatedEnglish, WordList::new(include!("./words/ang.rs"), 4)),
|
||||
])
|
||||
});
|
||||
|
||||
fn checksum_index(words: &[Zeroizing<String>], lang: &WordList) -> usize {
|
||||
let mut trimmed_words = Zeroizing::new(String::new());
|
||||
|
@ -170,7 +166,7 @@ fn key_to_seed(lang: Language, key: Zeroizing<Scalar>) -> Seed {
|
|||
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.
|
||||
|
@ -204,7 +200,7 @@ fn key_to_seed(lang: Language, key: Zeroizing<Scalar>) -> Seed {
|
|||
|
||||
// create a checksum word for all languages except old english
|
||||
if lang != Language::DeprecatedEnglish {
|
||||
let checksum = seed[checksum_index(&seed, &LANGUAGES()[&lang])].clone();
|
||||
let checksum = seed[checksum_index(&seed, &LANGUAGES[&lang])].clone();
|
||||
seed.push(checksum);
|
||||
}
|
||||
|
||||
|
@ -232,7 +228,7 @@ fn seed_to_bytes(lang: Language, words: &str) -> Result<Zeroizing<[u8; 32]>, See
|
|||
}
|
||||
|
||||
// Validate words are in the language word list
|
||||
let lang_word_list: &WordList = &LANGUAGES()[&lang];
|
||||
let lang_word_list: &WordList = &LANGUAGES[&lang];
|
||||
let matched_indices = (|| {
|
||||
let has_checksum = words.len() == SEED_LENGTH_WITH_CHECKSUM;
|
||||
let mut matched_indices = Zeroizing::new(vec![]);
|
||||
|
|
|
@ -183,7 +183,7 @@ fn test_original_seed() {
|
|||
for vector in vectors {
|
||||
fn trim_by_lang(word: &str, lang: Language) -> String {
|
||||
if lang != Language::DeprecatedEnglish {
|
||||
word.chars().take(LANGUAGES()[&lang].unique_prefix_length).collect()
|
||||
word.chars().take(LANGUAGES[&lang].unique_prefix_length).collect()
|
||||
} else {
|
||||
word.to_string()
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use core::ops::Deref;
|
||||
use std_shims::sync::OnceLock;
|
||||
use std_shims::sync::LazyLock;
|
||||
|
||||
use zeroize::Zeroizing;
|
||||
use rand_core::OsRng;
|
||||
|
@ -145,7 +145,7 @@ pub async fn rpc() -> SimpleRequestRpc {
|
|||
rpc
|
||||
}
|
||||
|
||||
pub static SEQUENTIAL: OnceLock<Mutex<()>> = OnceLock::new();
|
||||
pub(crate) static SEQUENTIAL: LazyLock<Mutex<()>> = LazyLock::new(|| Mutex::new(()));
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! async_sequential {
|
||||
|
@ -153,7 +153,7 @@ macro_rules! async_sequential {
|
|||
$(
|
||||
#[tokio::test]
|
||||
async fn $name() {
|
||||
let guard = runner::SEQUENTIAL.get_or_init(|| tokio::sync::Mutex::new(())).lock().await;
|
||||
let guard = runner::SEQUENTIAL.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 {
|
||||
|
|
|
@ -6,7 +6,7 @@ license = "MIT"
|
|||
repository = "https://github.com/serai-dex/serai/tree/develop/networks/monero/wallet/util"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.79"
|
||||
rust-version = "1.80"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# rust:1.79.0-slim-bookworm as of June 14th, 2024 (GMT)
|
||||
FROM --platform=linux/amd64 rust@sha256:fa189cd885739dd17fc6bb4e132687fce43f2bf42983c0ac39b60e4943201e9c as deterministic
|
||||
# rust:1.80.0-slim-bookworm as of July 27th, 2024 (GMT)
|
||||
FROM --platform=linux/amd64 rust@sha256:37e6f90f98b3afd15c2526d7abb257a1f4cb7d49808fe3729d9d62020b07b544 as deterministic
|
||||
|
||||
# Move to a Debian package snapshot
|
||||
RUN rm -rf /etc/apt/sources.list.d/debian.sources && \
|
||||
|
|
|
@ -146,7 +146,7 @@ fn build_serai_service(prelude: &str, release: bool, features: &str, package: &s
|
|||
|
||||
format!(
|
||||
r#"
|
||||
FROM rust:1.79-slim-bookworm as builder
|
||||
FROM rust:1.80-slim-bookworm as builder
|
||||
|
||||
COPY --from=mimalloc-debian libmimalloc.so /usr/lib
|
||||
RUN echo "/usr/lib/libmimalloc.so" >> /etc/ld.so.preload
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[toolchain]
|
||||
channel = "1.79"
|
||||
channel = "1.80"
|
||||
targets = ["wasm32-unknown-unknown"]
|
||||
profile = "minimal"
|
||||
components = ["rust-src", "rustfmt", "clippy"]
|
||||
|
|
Loading…
Reference in a new issue