Begin crate smashing

This commit is contained in:
Luke Parker
2024-06-13 18:54:18 -04:00
parent 5cdae6eeb8
commit 784a273747
42 changed files with 606 additions and 250 deletions

49
Cargo.lock generated
View File

@@ -4751,6 +4751,27 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "monero-clsag"
version = "0.1.0"
dependencies = [
"curve25519-dalek",
"dalek-ff-group",
"flexible-transcript",
"group",
"modular-frost",
"monero-generators",
"monero-io",
"monero-primitives",
"rand_chacha",
"rand_core",
"sha3",
"std-shims",
"subtle",
"thiserror",
"zeroize",
]
[[package]] [[package]]
name = "monero-generators" name = "monero-generators"
version = "0.4.0" version = "0.4.0"
@@ -4759,11 +4780,33 @@ dependencies = [
"dalek-ff-group", "dalek-ff-group",
"group", "group",
"hex", "hex",
"monero-io",
"sha3", "sha3",
"std-shims", "std-shims",
"subtle", "subtle",
] ]
[[package]]
name = "monero-io"
version = "0.1.0"
dependencies = [
"curve25519-dalek",
"std-shims",
]
[[package]]
name = "monero-primitives"
version = "0.1.0"
dependencies = [
"curve25519-dalek",
"monero-generators",
"monero-io",
"rand_core",
"sha3",
"std-shims",
"zeroize",
]
[[package]] [[package]]
name = "monero-serai" name = "monero-serai"
version = "0.1.4-alpha" version = "0.1.4-alpha"
@@ -4778,7 +4821,10 @@ dependencies = [
"hex", "hex",
"hex-literal", "hex-literal",
"modular-frost", "modular-frost",
"monero-clsag",
"monero-generators", "monero-generators",
"monero-io",
"monero-primitives",
"multiexp", "multiexp",
"pbkdf2 0.12.2", "pbkdf2 0.12.2",
"rand", "rand",
@@ -8035,7 +8081,10 @@ dependencies = [
"dleq", "dleq",
"flexible-transcript", "flexible-transcript",
"minimal-ed448", "minimal-ed448",
"monero-clsag",
"monero-generators", "monero-generators",
"monero-io",
"monero-primitives",
"monero-serai", "monero-serai",
"multiexp", "multiexp",
"schnorr-signatures", "schnorr-signatures",

View File

@@ -43,7 +43,10 @@ members = [
"coins/ethereum", "coins/ethereum",
"coins/ethereum/relayer", "coins/ethereum/relayer",
"coins/monero/io",
"coins/monero/generators", "coins/monero/generators",
"coins/monero/primitives",
"coins/monero/ringct/clsag",
"coins/monero", "coins/monero",
"message-queue", "message-queue",

View File

@@ -45,7 +45,10 @@ multiexp = { path = "../../crypto/multiexp", version = "0.4", default-features =
transcript = { package = "flexible-transcript", path = "../../crypto/transcript", version = "0.3", default-features = false, features = ["recommended"], optional = true } transcript = { package = "flexible-transcript", path = "../../crypto/transcript", version = "0.3", default-features = false, features = ["recommended"], optional = true }
frost = { package = "modular-frost", path = "../../crypto/frost", version = "0.8", default-features = false, features = ["ed25519"], optional = true } frost = { package = "modular-frost", path = "../../crypto/frost", version = "0.8", default-features = false, features = ["ed25519"], optional = true }
monero-io = { path = "io", version = "0.1", default-features = false }
monero-generators = { path = "generators", version = "0.4", default-features = false } monero-generators = { path = "generators", version = "0.4", default-features = false }
monero-primitives = { path = "primitives", version = "0.1", default-features = false }
monero-clsag = { path = "ringct/clsag", version = "0.1", default-features = false }
hex-literal = "0.4" hex-literal = "0.4"
hex = { version = "0.4", default-features = false, features = ["alloc"] } hex = { version = "0.4", default-features = false, features = ["alloc"] }
@@ -89,7 +92,10 @@ std = [
"transcript/std", "transcript/std",
"monero-io/std",
"monero-generators/std", "monero-generators/std",
"monero-primitives/std",
"monero-clsag/std",
"hex/std", "hex/std",
"serde/std", "serde/std",
@@ -99,7 +105,7 @@ std = [
] ]
http-rpc = ["digest_auth", "simple-request", "tokio"] http-rpc = ["digest_auth", "simple-request", "tokio"]
multisig = ["transcript", "frost", "std"] multisig = ["transcript", "frost", "monero-clsag/multisig", "std"]
binaries = ["tokio/rt-multi-thread", "tokio/macros", "http-rpc"] binaries = ["tokio/rt-multi-thread", "tokio/macros", "http-rpc"]
default = ["std", "http-rpc"] default = ["std", "http-rpc"]

View File

@@ -1,7 +1,7 @@
[package] [package]
name = "monero-generators" name = "monero-generators"
version = "0.4.0" version = "0.4.0"
description = "Monero's hash_to_point and generators" description = "Monero's hash to point function and generators"
license = "MIT" license = "MIT"
repository = "https://github.com/serai-dex/serai/tree/develop/coins/monero/generators" repository = "https://github.com/serai-dex/serai/tree/develop/coins/monero/generators"
authors = ["Luke Parker <lukeparker5132@gmail.com>"] authors = ["Luke Parker <lukeparker5132@gmail.com>"]
@@ -20,15 +20,28 @@ std-shims = { path = "../../../common/std-shims", version = "^0.1.1", default-fe
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 }
curve25519-dalek = { version = "4", default-features = false, features = ["alloc", "zeroize"] }
curve25519-dalek = { version = "4", default-features = false, features = ["alloc", "zeroize", "precomputed-tables"] }
group = { version = "0.13", default-features = false } group = { version = "0.13", default-features = false }
dalek-ff-group = { path = "../../../crypto/dalek-ff-group", version = "0.4", default-features = false } dalek-ff-group = { path = "../../../crypto/dalek-ff-group", version = "0.4", default-features = false }
monero-io = { path = "../io", version = "0.1", default-features = false }
[dev-dependencies] [dev-dependencies]
hex = "0.4" hex = "0.4"
[features] [features]
std = ["std-shims/std", "subtle/std", "sha3/std", "dalek-ff-group/std"] std = [
"std-shims/std",
"subtle/std",
"sha3/std",
"curve25519-dalek/precomputed-tables",
"group/alloc",
"dalek-ff-group/std",
"monero-io/std"
]
default = ["std"] default = ["std"]

View File

@@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2022-2023 Luke Parker Copyright (c) 2022-2024 Luke Parker
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,7 +1,8 @@
# Monero Generators # Monero Generators
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 `hash_to_ec` is included, as needed to generate
`hash_to_point` here, is included, as needed to generate generators. the generators.
This library is usable under no-std when the `std` feature is disabled. This library is usable under no-std when the `std` feature (on by default) is
disabled.

View File

@@ -1,27 +1,20 @@
use subtle::ConditionallySelectable; use subtle::ConditionallySelectable;
use curve25519_dalek::edwards::{EdwardsPoint, CompressedEdwardsY}; use curve25519_dalek::edwards::EdwardsPoint;
use group::ff::{Field, PrimeField}; use group::ff::{Field, PrimeField};
use dalek_ff_group::FieldElement; use dalek_ff_group::FieldElement;
use crate::hash; use monero_io::decompress_point;
/// Decompress canonically encoded ed25519 point use crate::keccak256;
/// It does not check if the point is in the prime order subgroup
pub fn decompress_point(bytes: [u8; 32]) -> Option<EdwardsPoint> {
CompressedEdwardsY(bytes)
.decompress()
// Ban points which are either unreduced or -0
.filter(|point| point.compress().to_bytes() == bytes)
}
/// Monero's hash to point function, as named `hash_to_ec`. /// Monero's hash to point function, as named `hash_to_ec`.
pub fn hash_to_point(bytes: [u8; 32]) -> EdwardsPoint { pub fn hash_to_point(bytes: [u8; 32]) -> EdwardsPoint {
#[allow(non_snake_case)] #[allow(non_snake_case)]
let A = FieldElement::from(486662u64); let A = FieldElement::from(486662u64);
let v = FieldElement::from_square(hash(&bytes)).double(); let v = FieldElement::from_square(keccak256(&bytes)).double();
let w = v + FieldElement::ONE; let w = v + FieldElement::ONE;
let x = w.square() + (-A.square() * v); let x = w.square() + (-A.square() * v);

View File

@@ -1,8 +1,5 @@
//! Generators used by Monero in both its Pedersen commitments and Bulletproofs(+). #![cfg_attr(docsrs, feature(doc_auto_cfg))]
//! #![doc = include_str!("../README.md")]
//! 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)] #![cfg_attr(not(feature = "std"), no_std)]
use std_shims::{sync::OnceLock, vec::Vec}; use std_shims::{sync::OnceLock, vec::Vec};
@@ -14,16 +11,15 @@ use curve25519_dalek::edwards::{EdwardsPoint as DalekPoint};
use group::{Group, GroupEncoding}; use group::{Group, GroupEncoding};
use dalek_ff_group::EdwardsPoint; use dalek_ff_group::EdwardsPoint;
mod varint; use monero_io::{write_varint, decompress_point};
use varint::write_varint;
mod hash_to_point; mod hash_to_point;
pub use hash_to_point::{hash_to_point, decompress_point}; pub use hash_to_point::hash_to_point;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
fn hash(data: &[u8]) -> [u8; 32] { fn keccak256(data: &[u8]) -> [u8; 32] {
Keccak256::digest(data).into() Keccak256::digest(data).into()
} }
@@ -32,7 +28,7 @@ static H_CELL: OnceLock<DalekPoint> = OnceLock::new();
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn H() -> DalekPoint { pub fn H() -> DalekPoint {
*H_CELL.get_or_init(|| { *H_CELL.get_or_init(|| {
decompress_point(hash(&EdwardsPoint::generator().to_bytes())).unwrap().mul_by_cofactor() decompress_point(keccak256(&EdwardsPoint::generator().to_bytes())).unwrap().mul_by_cofactor()
}) })
} }
@@ -70,10 +66,10 @@ pub fn bulletproofs_generators(dst: &'static [u8]) -> Generators {
even.extend(dst); even.extend(dst);
let mut odd = even.clone(); let mut odd = even.clone();
write_varint(&i.try_into().unwrap(), &mut even).unwrap(); write_varint::<Vec<u8>, u64>(&i.try_into().unwrap(), &mut even).unwrap();
write_varint(&(i + 1).try_into().unwrap(), &mut odd).unwrap(); write_varint::<Vec<u8>, u64>(&(i + 1).try_into().unwrap(), &mut odd).unwrap();
res.H.push(EdwardsPoint(hash_to_point(hash(&even)))); res.H.push(EdwardsPoint(hash_to_point(keccak256(&even))));
res.G.push(EdwardsPoint(hash_to_point(hash(&odd)))); res.G.push(EdwardsPoint(hash_to_point(keccak256(&odd))));
} }
res res
} }

View File

@@ -1,16 +0,0 @@
use std_shims::io::{self, Write};
const VARINT_CONTINUATION_MASK: u8 = 0b1000_0000;
pub(crate) fn write_varint<W: Write>(varint: &u64, w: &mut W) -> io::Result<()> {
let mut varint = *varint;
while {
let mut b = u8::try_from(varint & u64::from(!VARINT_CONTINUATION_MASK)).unwrap();
varint >>= 7;
if varint != 0 {
b |= VARINT_CONTINUATION_MASK;
}
w.write_all(&[b])?;
varint != 0
} {}
Ok(())
}

View File

@@ -0,0 +1,24 @@
[package]
name = "monero-io"
version = "0.1.0"
description = "Serialization functions, as within the Monero protocol"
license = "MIT"
repository = "https://github.com/serai-dex/serai/tree/develop/coins/monero/io"
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
edition = "2021"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[lints]
workspace = true
[dependencies]
std-shims = { path = "../../../common/std-shims", version = "^0.1.1", default-features = false }
curve25519-dalek = { version = "4", default-features = false, features = ["alloc"] }
[features]
std = ["std-shims/std"]
default = ["std"]

21
coins/monero/io/LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022-2024 Luke Parker
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,6 @@
# Monero IO
Serialization functions, as within the Monero protocol.
This library is usable under no-std when the `std` feature (on by default) is
disabled.

View File

@@ -1,12 +1,18 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc = include_str!("../README.md")]
#![cfg_attr(not(feature = "std"), no_std)]
use core::fmt::Debug; use core::fmt::Debug;
use std_shims::{ use std_shims::{
vec,
vec::Vec, vec::Vec,
io::{self, Read, Write}, io::{self, Read, Write},
}; };
use curve25519_dalek::{scalar::Scalar, edwards::EdwardsPoint}; use curve25519_dalek::{
scalar::Scalar,
use monero_generators::decompress_point; edwards::{EdwardsPoint, CompressedEdwardsY},
};
const VARINT_CONTINUATION_MASK: u8 = 0b1000_0000; const VARINT_CONTINUATION_MASK: u8 = 0b1000_0000;
@@ -29,17 +35,17 @@ mod sealed {
} }
// This will panic if the VarInt exceeds u64::MAX // This will panic if the VarInt exceeds u64::MAX
pub(crate) fn varint_len<U: sealed::VarInt>(varint: U) -> usize { pub fn varint_len<U: sealed::VarInt>(varint: U) -> usize {
let varint_u64: u64 = varint.try_into().map_err(|_| "varint exceeded u64").unwrap(); let varint_u64: u64 = varint.try_into().map_err(|_| "varint exceeded u64").unwrap();
((usize::try_from(u64::BITS - varint_u64.leading_zeros()).unwrap().saturating_sub(1)) / 7) + 1 ((usize::try_from(u64::BITS - varint_u64.leading_zeros()).unwrap().saturating_sub(1)) / 7) + 1
} }
pub(crate) fn write_byte<W: Write>(byte: &u8, w: &mut W) -> io::Result<()> { pub fn write_byte<W: Write>(byte: &u8, w: &mut W) -> io::Result<()> {
w.write_all(&[*byte]) w.write_all(&[*byte])
} }
// This will panic if the VarInt exceeds u64::MAX // This will panic if the VarInt exceeds u64::MAX
pub(crate) fn write_varint<W: Write, U: sealed::VarInt>(varint: &U, w: &mut W) -> io::Result<()> { pub fn write_varint<W: Write, U: sealed::VarInt>(varint: &U, w: &mut W) -> io::Result<()> {
let mut varint: u64 = (*varint).try_into().map_err(|_| "varint exceeded u64").unwrap(); let mut varint: u64 = (*varint).try_into().map_err(|_| "varint exceeded u64").unwrap();
while { while {
let mut b = u8::try_from(varint & u64::from(!VARINT_CONTINUATION_MASK)).unwrap(); let mut b = u8::try_from(varint & u64::from(!VARINT_CONTINUATION_MASK)).unwrap();
@@ -53,15 +59,15 @@ pub(crate) fn write_varint<W: Write, U: sealed::VarInt>(varint: &U, w: &mut W) -
Ok(()) Ok(())
} }
pub(crate) fn write_scalar<W: Write>(scalar: &Scalar, w: &mut W) -> io::Result<()> { pub fn write_scalar<W: Write>(scalar: &Scalar, w: &mut W) -> io::Result<()> {
w.write_all(&scalar.to_bytes()) w.write_all(&scalar.to_bytes())
} }
pub(crate) fn write_point<W: Write>(point: &EdwardsPoint, w: &mut W) -> io::Result<()> { pub fn write_point<W: Write>(point: &EdwardsPoint, w: &mut W) -> io::Result<()> {
w.write_all(&point.compress().to_bytes()) w.write_all(&point.compress().to_bytes())
} }
pub(crate) fn write_raw_vec<T, W: Write, F: Fn(&T, &mut W) -> io::Result<()>>( pub fn write_raw_vec<T, W: Write, F: Fn(&T, &mut W) -> io::Result<()>>(
f: F, f: F,
values: &[T], values: &[T],
w: &mut W, w: &mut W,
@@ -72,7 +78,7 @@ pub(crate) fn write_raw_vec<T, W: Write, F: Fn(&T, &mut W) -> io::Result<()>>(
Ok(()) Ok(())
} }
pub(crate) fn write_vec<T, W: Write, F: Fn(&T, &mut W) -> io::Result<()>>( pub fn write_vec<T, W: Write, F: Fn(&T, &mut W) -> io::Result<()>>(
f: F, f: F,
values: &[T], values: &[T],
w: &mut W, w: &mut W,
@@ -81,29 +87,29 @@ pub(crate) fn write_vec<T, W: Write, F: Fn(&T, &mut W) -> io::Result<()>>(
write_raw_vec(f, values, w) write_raw_vec(f, values, w)
} }
pub(crate) fn read_bytes<R: Read, const N: usize>(r: &mut R) -> io::Result<[u8; N]> { pub fn read_bytes<R: Read, const N: usize>(r: &mut R) -> io::Result<[u8; N]> {
let mut res = [0; N]; let mut res = [0; N];
r.read_exact(&mut res)?; r.read_exact(&mut res)?;
Ok(res) Ok(res)
} }
pub(crate) fn read_byte<R: Read>(r: &mut R) -> io::Result<u8> { pub fn read_byte<R: Read>(r: &mut R) -> io::Result<u8> {
Ok(read_bytes::<_, 1>(r)?[0]) Ok(read_bytes::<_, 1>(r)?[0])
} }
pub(crate) fn read_u16<R: Read>(r: &mut R) -> io::Result<u16> { pub fn read_u16<R: Read>(r: &mut R) -> io::Result<u16> {
read_bytes(r).map(u16::from_le_bytes) read_bytes(r).map(u16::from_le_bytes)
} }
pub(crate) fn read_u32<R: Read>(r: &mut R) -> io::Result<u32> { pub fn read_u32<R: Read>(r: &mut R) -> io::Result<u32> {
read_bytes(r).map(u32::from_le_bytes) read_bytes(r).map(u32::from_le_bytes)
} }
pub(crate) fn read_u64<R: Read>(r: &mut R) -> io::Result<u64> { pub fn read_u64<R: Read>(r: &mut R) -> io::Result<u64> {
read_bytes(r).map(u64::from_le_bytes) read_bytes(r).map(u64::from_le_bytes)
} }
pub(crate) fn read_varint<R: Read, U: sealed::VarInt>(r: &mut R) -> io::Result<U> { pub fn read_varint<R: Read, U: sealed::VarInt>(r: &mut R) -> io::Result<U> {
let mut bits = 0; let mut bits = 0;
let mut res = 0; let mut res = 0;
while { while {
@@ -128,24 +134,34 @@ pub(crate) fn read_varint<R: Read, U: sealed::VarInt>(r: &mut R) -> io::Result<U
// for now. There's also further edge cases as noted by // for now. There's also further edge cases as noted by
// https://github.com/monero-project/monero/issues/8438, where some scalars had an archaic // https://github.com/monero-project/monero/issues/8438, where some scalars had an archaic
// reduction applied // reduction applied
pub(crate) fn read_scalar<R: Read>(r: &mut R) -> io::Result<Scalar> { pub fn read_scalar<R: Read>(r: &mut R) -> io::Result<Scalar> {
Option::from(Scalar::from_canonical_bytes(read_bytes(r)?)) Option::from(Scalar::from_canonical_bytes(read_bytes(r)?))
.ok_or_else(|| io::Error::other("unreduced scalar")) .ok_or_else(|| io::Error::other("unreduced scalar"))
} }
pub(crate) fn read_point<R: Read>(r: &mut R) -> io::Result<EdwardsPoint> { /// Decompress a canonically encoded ed25519 point.
///
/// This function does not check if the point is within the prime order subgroup.
pub fn decompress_point(bytes: [u8; 32]) -> Option<EdwardsPoint> {
CompressedEdwardsY(bytes)
.decompress()
// Ban points which are either unreduced or -0
.filter(|point| point.compress().to_bytes() == bytes)
}
pub fn read_point<R: Read>(r: &mut R) -> io::Result<EdwardsPoint> {
let bytes = read_bytes(r)?; let bytes = read_bytes(r)?;
decompress_point(bytes).ok_or_else(|| io::Error::other("invalid point")) decompress_point(bytes).ok_or_else(|| io::Error::other("invalid point"))
} }
pub(crate) fn read_torsion_free_point<R: Read>(r: &mut R) -> io::Result<EdwardsPoint> { pub fn read_torsion_free_point<R: Read>(r: &mut R) -> io::Result<EdwardsPoint> {
read_point(r) read_point(r)
.ok() .ok()
.filter(EdwardsPoint::is_torsion_free) .filter(EdwardsPoint::is_torsion_free)
.ok_or_else(|| io::Error::other("invalid point")) .ok_or_else(|| io::Error::other("invalid point"))
} }
pub(crate) fn read_raw_vec<R: Read, T, F: Fn(&mut R) -> io::Result<T>>( pub fn read_raw_vec<R: Read, T, F: Fn(&mut R) -> io::Result<T>>(
f: F, f: F,
len: usize, len: usize,
r: &mut R, r: &mut R,
@@ -157,16 +173,13 @@ pub(crate) fn read_raw_vec<R: Read, T, F: Fn(&mut R) -> io::Result<T>>(
Ok(res) Ok(res)
} }
pub(crate) fn read_array<R: Read, T: Debug, F: Fn(&mut R) -> io::Result<T>, const N: usize>( pub fn read_array<R: Read, T: Debug, F: Fn(&mut R) -> io::Result<T>, const N: usize>(
f: F, f: F,
r: &mut R, r: &mut R,
) -> io::Result<[T; N]> { ) -> io::Result<[T; N]> {
read_raw_vec(f, N, r).map(|vec| vec.try_into().unwrap()) read_raw_vec(f, N, r).map(|vec| vec.try_into().unwrap())
} }
pub(crate) fn read_vec<R: Read, T, F: Fn(&mut R) -> io::Result<T>>( pub fn read_vec<R: Read, T, F: Fn(&mut R) -> io::Result<T>>(f: F, r: &mut R) -> io::Result<Vec<T>> {
f: F,
r: &mut R,
) -> io::Result<Vec<T>> {
read_raw_vec(f, read_varint(r)?, r) read_raw_vec(f, read_varint(r)?, r)
} }

View File

@@ -0,0 +1,44 @@
[package]
name = "monero-primitives"
version = "0.1.0"
description = "Primitives for the Monero protocol"
license = "MIT"
repository = "https://github.com/serai-dex/serai/tree/develop/coins/monero/primitives"
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
edition = "2021"
rust-version = "1.79"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[lints]
workspace = true
[dependencies]
std-shims = { path = "../../../common/std-shims", version = "^0.1.1", default-features = false }
rand_core = { version = "0.6", default-features = false }
zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] }
# Cryptographic dependencies
sha3 = { version = "0.10", default-features = false }
curve25519-dalek = { version = "4", default-features = false, features = ["alloc", "zeroize"] }
# Other Monero dependencies
monero-io = { path = "../io", version = "0.1", default-features = false }
monero-generators = { path = "../generators", version = "0.4", default-features = false }
[features]
std = [
"std-shims/std",
"rand_core/std",
"zeroize/std",
"sha3/std",
"curve25519-dalek/precomputed-tables",
"monero-generators/std",
]
default = ["std"]

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022-2024 Luke Parker
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,6 @@
# Monero Primitives
Primitive structures and functions for the Monero protocol.
This library is usable under no-std when the `std` feature (on by default) is
disabled.

View File

@@ -0,0 +1,122 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc = include_str!("../README.md")]
#![cfg_attr(not(feature = "std"), no_std)]
use std_shims::{vec, vec::Vec};
#[cfg(feature = "std")]
use std_shims::sync::OnceLock;
use zeroize::{Zeroize, ZeroizeOnDrop};
use sha3::{Digest, Keccak256};
use curve25519_dalek::{
constants::ED25519_BASEPOINT_POINT,
traits::VartimePrecomputedMultiscalarMul,
scalar::Scalar,
edwards::{EdwardsPoint, VartimeEdwardsPrecomputation},
};
use monero_io::varint_len;
use monero_generators::H;
// TODO: Replace this with a const
#[cfg(feature = "std")]
static INV_EIGHT_CELL: OnceLock<Scalar> = OnceLock::new();
#[cfg(feature = "std")]
#[allow(non_snake_case)]
pub fn INV_EIGHT() -> Scalar {
*INV_EIGHT_CELL.get_or_init(|| Scalar::from(8u8).invert())
}
#[cfg(not(feature = "std"))]
#[allow(non_snake_case)]
pub fn INV_EIGHT() -> Scalar {
Scalar::from(8u8).invert()
}
// On std, we cache this in a static
// In no-std environments, we prefer the reduced memory use and calculate it ad-hoc
#[cfg(feature = "std")]
static BASEPOINT_PRECOMP_CELL: OnceLock<VartimeEdwardsPrecomputation> = OnceLock::new();
#[cfg(feature = "std")]
#[allow(non_snake_case)]
pub fn BASEPOINT_PRECOMP() -> &'static VartimeEdwardsPrecomputation {
BASEPOINT_PRECOMP_CELL
.get_or_init(|| VartimeEdwardsPrecomputation::new([ED25519_BASEPOINT_POINT]))
}
#[cfg(not(feature = "std"))]
#[allow(non_snake_case)]
pub fn BASEPOINT_PRECOMP() -> VartimeEdwardsPrecomputation {
VartimeEdwardsPrecomputation::new([ED25519_BASEPOINT_POINT])
}
pub fn keccak256(data: impl AsRef<[u8]>) -> [u8; 32] {
Keccak256::digest(data.as_ref()).into()
}
/// Hash the provided data to a scalar via keccak256(data) % l.
pub fn keccak256_to_scalar(data: impl AsRef<[u8]>) -> Scalar {
let scalar = Scalar::from_bytes_mod_order(keccak256(data.as_ref()));
// Monero will explicitly error in this case
// This library acknowledges its practical impossibility of it occurring, and doesn't bother to
// code in logic to handle it. That said, if it ever occurs, something must happen in order to
// not generate/verify a proof we believe to be valid when it isn't
assert!(scalar != Scalar::ZERO, "ZERO HASH: {:?}", data.as_ref());
scalar
}
/// Transparent structure representing a Pedersen commitment's contents.
#[allow(non_snake_case)]
#[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
pub struct Commitment {
pub mask: Scalar,
pub amount: u64,
}
impl core::fmt::Debug for Commitment {
fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
fmt.debug_struct("Commitment").field("amount", &self.amount).finish_non_exhaustive()
}
}
impl Commitment {
/// A commitment to zero, defined with a mask of 1 (as to not be the identity).
pub fn zero() -> Commitment {
Commitment { mask: Scalar::ONE, amount: 0 }
}
pub fn new(mask: Scalar, amount: u64) -> Commitment {
Commitment { mask, amount }
}
/// Calculate a Pedersen commitment, as a point, from the transparent structure.
pub fn calculate(&self) -> EdwardsPoint {
EdwardsPoint::vartime_double_scalar_mul_basepoint(&Scalar::from(self.amount), &H(), &self.mask)
}
}
/// Decoy data, containing the actual member as well (at index `i`).
#[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)]
pub struct Decoys {
pub i: u8,
pub offsets: Vec<u64>,
pub ring: Vec<[EdwardsPoint; 2]>,
}
#[allow(clippy::len_without_is_empty)]
impl Decoys {
pub fn fee_weight(offsets: &[u64]) -> usize {
varint_len(offsets.len()) + offsets.iter().map(|offset| varint_len(*offset)).sum::<usize>()
}
pub fn len(&self) -> usize {
self.offsets.len()
}
pub fn indexes(&self) -> Vec<u64> {
let mut res = vec![self.offsets[0]; self.len()];
for m in 1 .. res.len() {
res[m] = res[m - 1] + self.offsets[m];
}
res
}
}

View File

@@ -0,0 +1,66 @@
[package]
name = "monero-clsag"
version = "0.1.0"
description = "The CLSAG linkable ring signature, as defined by the Monero protocol"
license = "MIT"
repository = "https://github.com/serai-dex/serai/tree/develop/coins/monero/ringct/clsag"
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
edition = "2021"
rust-version = "1.79"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[lints]
workspace = true
[dependencies]
std-shims = { path = "../../../../common/std-shims", version = "^0.1.1", default-features = false }
thiserror = { version = "1", default-features = false, optional = true }
rand_core = { version = "0.6", default-features = false }
zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] }
subtle = { version = "^2.4", default-features = false }
# Cryptographic dependencies
sha3 = { version = "0.10", default-features = false }
curve25519-dalek = { version = "4", default-features = false, features = ["alloc", "zeroize"] }
# Multisig dependencies
rand_chacha = { version = "0.3", default-features = false, optional = true }
transcript = { package = "flexible-transcript", path = "../../../../crypto/transcript", version = "0.3", default-features = false, features = ["recommended"], optional = true }
group = { version = "0.13", default-features = false, optional = true }
dalek-ff-group = { path = "../../../../crypto/dalek-ff-group", version = "0.4", default-features = false, optional = true }
frost = { package = "modular-frost", path = "../../../../crypto/frost", default-features = false, features = ["ed25519"], optional = true }
# Other Monero dependencies
monero-io = { path = "../../io", version = "0.1", default-features = false }
monero-generators = { path = "../../generators", version = "0.4", default-features = false }
monero-primitives = { path = "../../primitives", version = "0.1", default-features = false }
[features]
std = [
"std-shims/std",
"thiserror",
"rand_core/std",
"zeroize/std",
"subtle/std",
"sha3/std",
"curve25519-dalek/precomputed-tables",
"rand_chacha?/std",
"transcript?/std",
"group?/alloc",
"dalek-ff-group?/std",
"monero-io/std",
"monero-generators/std",
"monero-primitives/std",
]
multisig = ["rand_chacha", "transcript", "group", "dalek-ff-group", "frost", "std"]
default = ["std"]

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022-2024 Luke Parker
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,6 @@
# Monero CLSAG
The CLSAG linkable ring signature, as defined by the Monero protocol.
This library is usable under no-std when the `std` feature (on by default) is
disabled.

View File

@@ -1,3 +1,6 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc = include_str!("../README.md")]
#![cfg_attr(not(feature = "std"), no_std)]
#![allow(non_snake_case)] #![allow(non_snake_case)]
use core::ops::Deref; use core::ops::Deref;
@@ -18,15 +21,14 @@ use curve25519_dalek::{
edwards::{EdwardsPoint, VartimeEdwardsPrecomputation}, edwards::{EdwardsPoint, VartimeEdwardsPrecomputation},
}; };
use crate::{ use monero_io::*;
INV_EIGHT, BASEPOINT_PRECOMP, Commitment, random_scalar, hash_to_scalar, wallet::decoys::Decoys, use monero_generators::hash_to_point;
ringct::hash_to_point, serialize::*, use monero_primitives::{INV_EIGHT, BASEPOINT_PRECOMP, Commitment, Decoys, keccak256_to_scalar};
};
#[cfg(feature = "multisig")] #[cfg(feature = "multisig")]
mod multisig; mod multisig;
#[cfg(feature = "multisig")] #[cfg(feature = "multisig")]
pub(crate) use multisig::{ClsagDetails, ClsagAddendum, ClsagMultisig}; pub use multisig::{ClsagDetails, ClsagAddendum, ClsagMultisig};
/// Errors when working with CLSAGs. /// Errors when working with CLSAGs.
#[derive(Clone, Copy, PartialEq, Eq, Debug)] #[derive(Clone, Copy, PartialEq, Eq, Debug)]
@@ -59,9 +61,9 @@ pub enum ClsagError {
#[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)] #[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)]
pub struct ClsagInput { pub struct ClsagInput {
// The actual commitment for the true spend // The actual commitment for the true spend
pub(crate) commitment: Commitment, pub commitment: Commitment,
// True spend index, offsets, and ring // True spend index, offsets, and ring
pub(crate) decoys: Decoys, pub decoys: Decoys,
} }
impl ClsagInput { impl ClsagInput {
@@ -139,10 +141,10 @@ fn core(
to_hash.extend(D_INV_EIGHT.compress().to_bytes()); to_hash.extend(D_INV_EIGHT.compress().to_bytes());
to_hash.extend(pseudo_out.compress().to_bytes()); to_hash.extend(pseudo_out.compress().to_bytes());
// mu_P with agg_0 // mu_P with agg_0
let mu_P = hash_to_scalar(&to_hash); let mu_P = keccak256_to_scalar(&to_hash);
// mu_C with agg_1 // mu_C with agg_1
to_hash[PREFIX_AGG_0_LEN - 1] = b'1'; to_hash[PREFIX_AGG_0_LEN - 1] = b'1';
let mu_C = hash_to_scalar(&to_hash); let mu_C = keccak256_to_scalar(&to_hash);
// Truncate it for the round transcript, altering the DST as needed // Truncate it for the round transcript, altering the DST as needed
to_hash.truncate(((2 * n) + 1) * 32); to_hash.truncate(((2 * n) + 1) * 32);
@@ -164,7 +166,7 @@ fn core(
end = r + n; end = r + n;
to_hash.extend(A.compress().to_bytes()); to_hash.extend(A.compress().to_bytes());
to_hash.extend(AH.compress().to_bytes()); to_hash.extend(AH.compress().to_bytes());
c = hash_to_scalar(&to_hash); c = keccak256_to_scalar(&to_hash);
} }
Mode::Verify(c1) => { Mode::Verify(c1) => {
@@ -190,7 +192,7 @@ fn core(
} }
}; };
let PH = hash_to_point(&P[i]); let PH = hash_to_point(P[i].compress().0);
// (c_p * I) + (c_c * D) + (s_i * PH) // (c_p * I) + (c_c * D) + (s_i * PH)
let R = match A_c1 { let R = match A_c1 {
@@ -203,7 +205,7 @@ fn core(
to_hash.truncate(((2 * n) + 3) * 32); to_hash.truncate(((2 * n) + 3) * 32);
to_hash.extend(L.compress().to_bytes()); to_hash.extend(L.compress().to_bytes());
to_hash.extend(R.compress().to_bytes()); to_hash.extend(R.compress().to_bytes());
c = hash_to_scalar(&to_hash); c = keccak256_to_scalar(&to_hash);
// This will only execute once and shouldn't need to be constant time. Making it constant time // This will only execute once and shouldn't need to be constant time. Making it constant time
// removes the risk of branch prediction creating timing differences depending on ring index // removes the risk of branch prediction creating timing differences depending on ring index
@@ -220,7 +222,7 @@ fn core(
pub struct Clsag { pub struct Clsag {
D: EdwardsPoint, D: EdwardsPoint,
pub(crate) s: Vec<Scalar>, pub(crate) s: Vec<Scalar>,
pub(crate) c1: Scalar, pub c1: Scalar,
} }
pub(crate) struct ClsagSignCore { pub(crate) struct ClsagSignCore {
@@ -247,11 +249,11 @@ impl Clsag {
let pseudo_out = Commitment::new(mask, input.commitment.amount).calculate(); let pseudo_out = Commitment::new(mask, input.commitment.amount).calculate();
let mask_delta = input.commitment.mask - mask; let mask_delta = input.commitment.mask - mask;
let H = hash_to_point(&input.decoys.ring[r][0]); let H = hash_to_point(input.decoys.ring[r][0].compress().0);
let D = H * mask_delta; let D = H * mask_delta;
let mut s = Vec::with_capacity(input.decoys.ring.len()); let mut s = Vec::with_capacity(input.decoys.ring.len());
for _ in 0 .. input.decoys.ring.len() { for _ in 0 .. input.decoys.ring.len() {
s.push(random_scalar(rng)); s.push(Scalar::random(rng));
} }
let ((D, c_p, c_c), c1) = let ((D, c_p, c_c), c1) =
core(&input.decoys.ring, I, &pseudo_out, msg, &D, &s, &Mode::Sign(r, A, AH)); core(&input.decoys.ring, I, &pseudo_out, msg, &D, &s, &Mode::Sign(r, A, AH));
@@ -268,7 +270,7 @@ impl Clsag {
/// ///
/// inputs is of the form (private key, key image, input). /// inputs is of the form (private key, key image, input).
/// sum_outputs is for the sum of the outputs' commitment masks. /// sum_outputs is for the sum of the outputs' commitment masks.
pub(crate) fn sign<R: RngCore + CryptoRng>( pub fn sign<R: RngCore + CryptoRng>(
rng: &mut R, rng: &mut R,
mut inputs: Vec<(Zeroizing<Scalar>, EdwardsPoint, ClsagInput)>, mut inputs: Vec<(Zeroizing<Scalar>, EdwardsPoint, ClsagInput)>,
sum_outputs: Scalar, sum_outputs: Scalar,
@@ -277,14 +279,14 @@ impl Clsag {
let mut res = Vec::with_capacity(inputs.len()); let mut res = Vec::with_capacity(inputs.len());
let mut sum_pseudo_outs = Scalar::ZERO; let mut sum_pseudo_outs = Scalar::ZERO;
for i in 0 .. inputs.len() { for i in 0 .. inputs.len() {
let mut mask = random_scalar(rng); let mut mask = Scalar::random(rng);
if i == (inputs.len() - 1) { if i == (inputs.len() - 1) {
mask = sum_outputs - sum_pseudo_outs; mask = sum_outputs - sum_pseudo_outs;
} else { } else {
sum_pseudo_outs += mask; sum_pseudo_outs += mask;
} }
let mut nonce = Zeroizing::new(random_scalar(rng)); let mut nonce = Zeroizing::new(Scalar::random(rng));
let ClsagSignCore { mut incomplete_clsag, pseudo_out, key_challenge, challenged_mask } = let ClsagSignCore { mut incomplete_clsag, pseudo_out, key_challenge, challenged_mask } =
Clsag::sign_core( Clsag::sign_core(
rng, rng,
@@ -294,7 +296,9 @@ impl Clsag {
&msg, &msg,
nonce.deref() * ED25519_BASEPOINT_TABLE, nonce.deref() * ED25519_BASEPOINT_TABLE,
nonce.deref() * nonce.deref() *
hash_to_point(&inputs[i].2.decoys.ring[usize::from(inputs[i].2.decoys.i)][0]), hash_to_point(
inputs[i].2.decoys.ring[usize::from(inputs[i].2.decoys.i)][0].compress().0,
),
); );
// Effectively r - cx, except cx is (c_p x) + (c_c z), where z is the delta between a ring // Effectively r - cx, except cx is (c_p x) + (c_c z), where z is the delta between a ring
// member's commitment and our input commitment (which will only have a known discrete log // member's commitment and our input commitment (which will only have a known discrete log
@@ -349,7 +353,7 @@ impl Clsag {
Ok(()) Ok(())
} }
pub(crate) fn fee_weight(ring_len: usize) -> usize { pub fn fee_weight(ring_len: usize) -> usize {
(ring_len * 32) + 32 + 32 (ring_len * 32) + 32 + 32
} }

View File

@@ -26,10 +26,9 @@ use frost::{
algorithm::{WriteAddendum, Algorithm}, algorithm::{WriteAddendum, Algorithm},
}; };
use crate::ringct::{ use monero_generators::hash_to_point;
hash_to_point,
clsag::{ClsagInput, Clsag}, use crate::{ClsagInput, Clsag};
};
impl ClsagInput { impl ClsagInput {
fn transcript<T: Transcript>(&self, transcript: &mut T) { fn transcript<T: Transcript>(&self, transcript: &mut T) {
@@ -57,13 +56,13 @@ impl ClsagInput {
/// CLSAG input and the mask to use for it. /// CLSAG input and the mask to use for it.
#[derive(Clone, Debug, Zeroize, ZeroizeOnDrop)] #[derive(Clone, Debug, Zeroize, ZeroizeOnDrop)]
pub(crate) struct ClsagDetails { pub struct ClsagDetails {
input: ClsagInput, input: ClsagInput,
mask: Scalar, mask: Scalar,
} }
impl ClsagDetails { impl ClsagDetails {
pub(crate) fn new(input: ClsagInput, mask: Scalar) -> ClsagDetails { pub fn new(input: ClsagInput, mask: Scalar) -> ClsagDetails {
ClsagDetails { input, mask } ClsagDetails { input, mask }
} }
} }
@@ -71,7 +70,7 @@ impl ClsagDetails {
/// Addendum produced during the FROST signing process with relevant data. /// Addendum produced during the FROST signing process with relevant data.
#[derive(Clone, PartialEq, Eq, Zeroize, Debug)] #[derive(Clone, PartialEq, Eq, Zeroize, Debug)]
pub struct ClsagAddendum { pub struct ClsagAddendum {
pub(crate) key_image: dfg::EdwardsPoint, pub key_image: dfg::EdwardsPoint,
} }
impl WriteAddendum for ClsagAddendum { impl WriteAddendum for ClsagAddendum {
@@ -93,10 +92,10 @@ struct Interim {
/// FROST algorithm for producing a CLSAG signature. /// FROST algorithm for producing a CLSAG signature.
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(crate) struct ClsagMultisig { pub struct ClsagMultisig {
transcript: RecommendedTranscript, transcript: RecommendedTranscript,
pub(crate) H: EdwardsPoint, pub H: EdwardsPoint,
key_image_shares: HashMap<[u8; 32], dfg::EdwardsPoint>, key_image_shares: HashMap<[u8; 32], dfg::EdwardsPoint>,
image: Option<dfg::EdwardsPoint>, image: Option<dfg::EdwardsPoint>,
@@ -107,7 +106,7 @@ pub(crate) struct ClsagMultisig {
} }
impl ClsagMultisig { impl ClsagMultisig {
pub(crate) fn new( pub fn new(
transcript: RecommendedTranscript, transcript: RecommendedTranscript,
output_key: EdwardsPoint, output_key: EdwardsPoint,
details: Arc<RwLock<Option<ClsagDetails>>>, details: Arc<RwLock<Option<ClsagDetails>>>,
@@ -115,7 +114,7 @@ impl ClsagMultisig {
ClsagMultisig { ClsagMultisig {
transcript, transcript,
H: hash_to_point(&output_key), H: hash_to_point(output_key.compress().0),
key_image_shares: HashMap::new(), key_image_shares: HashMap::new(),
image: None, image: None,

View File

@@ -17,7 +17,7 @@ mod binaries {
rpc::{RpcError, Rpc, HttpRpc}, rpc::{RpcError, Rpc, HttpRpc},
}; };
pub(crate) use monero_generators::decompress_point; pub(crate) use monero_io::decompress_point;
pub(crate) use tokio::task::JoinHandle; pub(crate) use tokio::task::JoinHandle;

View File

@@ -6,26 +6,21 @@
#[macro_use] #[macro_use]
extern crate alloc; extern crate alloc;
use std_shims::{sync::OnceLock, io}; use std_shims::io;
use rand_core::{RngCore, CryptoRng}; use zeroize::Zeroize;
use zeroize::{Zeroize, ZeroizeOnDrop};
use sha3::{Digest, Keccak256}; use sha3::{Digest, Keccak256};
use curve25519_dalek::{ use curve25519_dalek::scalar::Scalar;
constants::{ED25519_BASEPOINT_TABLE, ED25519_BASEPOINT_POINT},
scalar::Scalar,
edwards::{EdwardsPoint, VartimeEdwardsPrecomputation},
traits::VartimePrecomputedMultiscalarMul,
};
pub use monero_generators::{H, decompress_point}; pub use monero_io::decompress_point;
pub use monero_generators::H;
pub use monero_primitives::INV_EIGHT;
mod merkle; mod merkle;
mod serialize; use monero_io as serialize;
use serialize::{read_byte, read_u16}; use serialize::{read_byte, read_u16};
/// UnreducedScalar struct with functionality for recovering incorrectly reduced scalars. /// UnreducedScalar struct with functionality for recovering incorrectly reduced scalars.
@@ -55,19 +50,6 @@ pub const DEFAULT_LOCK_WINDOW: usize = 10;
pub const COINBASE_LOCK_WINDOW: usize = 60; pub const COINBASE_LOCK_WINDOW: usize = 60;
pub const BLOCK_TIME: usize = 120; pub const BLOCK_TIME: usize = 120;
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())
}
static BASEPOINT_PRECOMP_CELL: OnceLock<VartimeEdwardsPrecomputation> = OnceLock::new();
#[allow(non_snake_case)]
pub(crate) fn BASEPOINT_PRECOMP() -> &'static VartimeEdwardsPrecomputation {
BASEPOINT_PRECOMP_CELL
.get_or_init(|| VartimeEdwardsPrecomputation::new([ED25519_BASEPOINT_POINT]))
}
/// Monero protocol version. /// Monero protocol version.
/// ///
/// v15 is omitted as v15 was simply v14 and v16 being active at the same time, with regards to the /// v15 is omitted as v15 was simply v14 and v16 being active at the same time, with regards to the
@@ -188,42 +170,7 @@ impl Protocol {
} }
} }
/// Transparent structure representing a Pedersen commitment's contents. pub use monero_primitives::Commitment;
#[allow(non_snake_case)]
#[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
pub struct Commitment {
pub mask: Scalar,
pub amount: u64,
}
impl core::fmt::Debug for Commitment {
fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
fmt.debug_struct("Commitment").field("amount", &self.amount).finish_non_exhaustive()
}
}
impl Commitment {
/// A commitment to zero, defined with a mask of 1 (as to not be the identity).
pub fn zero() -> Commitment {
Commitment { mask: Scalar::ONE, amount: 0 }
}
pub fn new(mask: Scalar, amount: u64) -> Commitment {
Commitment { mask, amount }
}
/// 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())
}
}
/// Support generating a random scalar using a modern rand, as dalek's is notoriously dated.
pub fn random_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Scalar {
let mut r = [0; 64];
rng.fill_bytes(&mut r);
Scalar::from_bytes_mod_order_wide(&r)
}
pub(crate) fn hash(data: &[u8]) -> [u8; 32] { pub(crate) fn hash(data: &[u8]) -> [u8; 32] {
Keccak256::digest(data).into() Keccak256::digest(data).into()

View File

@@ -14,7 +14,7 @@ pub use hash_to_point::{raw_hash_to_point, hash_to_point};
/// MLSAG struct, along with verifying functionality. /// MLSAG struct, along with verifying functionality.
pub mod mlsag; pub mod mlsag;
/// CLSAG struct, along with signing and verifying functionality. /// CLSAG struct, along with signing and verifying functionality.
pub mod clsag; pub use monero_clsag as clsag;
/// BorromeanRange struct, along with verifying functionality. /// BorromeanRange struct, along with verifying functionality.
pub mod borromean; pub mod borromean;
/// Bulletproofs(+) structs, along with proving and verifying functionality. /// Bulletproofs(+) structs, along with proving and verifying functionality.

View File

@@ -11,7 +11,7 @@ use async_trait::async_trait;
use curve25519_dalek::edwards::EdwardsPoint; use curve25519_dalek::edwards::EdwardsPoint;
use monero_generators::decompress_point; use monero_io::decompress_point;
use serde::{Serialize, Deserialize, de::DeserializeOwned}; use serde::{Serialize, Deserialize, de::DeserializeOwned};
use serde_json::{Value, json}; use serde_json::{Value, json};

View File

@@ -2,14 +2,11 @@ use hex_literal::hex;
use rand_core::{RngCore, OsRng}; use rand_core::{RngCore, OsRng};
use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE; use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE, scalar::Scalar};
use monero_generators::decompress_point; use monero_io::decompress_point;
use crate::{ use crate::wallet::address::{Network, AddressType, AddressMeta, MoneroAddress};
random_scalar,
wallet::address::{Network, AddressType, AddressMeta, MoneroAddress},
};
const SPEND: [u8; 32] = hex!("f8631661f6ab4e6fda310c797330d86e23a682f20d5bc8cc27b18051191f16d7"); const SPEND: [u8; 32] = hex!("f8631661f6ab4e6fda310c797330d86e23a682f20d5bc8cc27b18051191f16d7");
const VIEW: [u8; 32] = hex!("4a1535063ad1fee2dabbf909d4fd9a873e29541b401f0944754e17c9a41820ce"); const VIEW: [u8; 32] = hex!("4a1535063ad1fee2dabbf909d4fd9a873e29541b401f0944754e17c9a41820ce");
@@ -75,8 +72,8 @@ fn featured() {
[(Network::Mainnet, 'C'), (Network::Testnet, 'K'), (Network::Stagenet, 'F')] [(Network::Mainnet, 'C'), (Network::Testnet, 'K'), (Network::Stagenet, 'F')]
{ {
for _ in 0 .. 100 { for _ in 0 .. 100 {
let spend = &random_scalar(&mut OsRng) * ED25519_BASEPOINT_TABLE; let spend = &Scalar::random(&mut OsRng) * ED25519_BASEPOINT_TABLE;
let view = &random_scalar(&mut OsRng) * ED25519_BASEPOINT_TABLE; let view = &Scalar::random(&mut OsRng) * ED25519_BASEPOINT_TABLE;
for features in 0 .. (1 << 3) { for features in 0 .. (1 << 3) {
const SUBADDRESS_FEATURE_BIT: u8 = 1; const SUBADDRESS_FEATURE_BIT: u8 = 1;

View File

@@ -2,11 +2,11 @@ use hex_literal::hex;
use rand_core::OsRng; use rand_core::OsRng;
use curve25519_dalek::scalar::Scalar; use curve25519_dalek::scalar::Scalar;
use monero_generators::decompress_point; use monero_io::decompress_point;
use multiexp::BatchVerifier; use multiexp::BatchVerifier;
use crate::{ use crate::{
Commitment, random_scalar, Commitment,
ringct::bulletproofs::{Bulletproof, original::OriginalStruct}, ringct::bulletproofs::{Bulletproof, original::OriginalStruct},
wallet::TransactionError, wallet::TransactionError,
}; };
@@ -68,7 +68,7 @@ macro_rules! bulletproofs_tests {
let mut verifier = BatchVerifier::new(16); let mut verifier = BatchVerifier::new(16);
for i in 1 ..= 16 { for i in 1 ..= 16 {
let commitments = (1 ..= i) let commitments = (1 ..= i)
.map(|i| Commitment::new(random_scalar(&mut OsRng), u64::try_from(i).unwrap())) .map(|i| Commitment::new(Scalar::random(&mut OsRng), u64::try_from(i).unwrap()))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let bp = if $plus { let bp = if $plus {

View File

@@ -13,7 +13,7 @@ use transcript::{Transcript, RecommendedTranscript};
use frost::curve::Ed25519; use frost::curve::Ed25519;
use crate::{ use crate::{
Commitment, random_scalar, Commitment,
wallet::Decoys, wallet::Decoys,
ringct::{ ringct::{
generate_key_image, generate_key_image,
@@ -43,8 +43,8 @@ fn clsag() {
let mut secrets = (Zeroizing::new(Scalar::ZERO), Scalar::ZERO); let mut secrets = (Zeroizing::new(Scalar::ZERO), Scalar::ZERO);
let mut ring = vec![]; let mut ring = vec![];
for i in 0 .. RING_LEN { for i in 0 .. RING_LEN {
let dest = Zeroizing::new(random_scalar(&mut OsRng)); let dest = Zeroizing::new(Scalar::random(&mut OsRng));
let mask = random_scalar(&mut OsRng); let mask = Scalar::random(&mut OsRng);
let amount; let amount;
if i == real { if i == real {
secrets = (dest.clone(), mask); secrets = (dest.clone(), mask);
@@ -72,7 +72,7 @@ fn clsag() {
) )
.unwrap(), .unwrap(),
)], )],
random_scalar(&mut OsRng), Scalar::random(&mut OsRng),
msg, msg,
) )
.swap_remove(0); .swap_remove(0);
@@ -80,7 +80,7 @@ fn clsag() {
clsag.verify(&ring, &image, &pseudo_out, &msg).unwrap(); clsag.verify(&ring, &image, &pseudo_out, &msg).unwrap();
// make sure verification fails if we throw a random `c1` at it. // make sure verification fails if we throw a random `c1` at it.
clsag.c1 = random_scalar(&mut OsRng); clsag.c1 = Scalar::random(&mut OsRng);
assert!(clsag.verify(&ring, &image, &pseudo_out, &msg).is_err()); assert!(clsag.verify(&ring, &image, &pseudo_out, &msg).is_err());
} }
} }
@@ -90,15 +90,15 @@ fn clsag() {
fn clsag_multisig() { fn clsag_multisig() {
let keys = key_gen::<_, Ed25519>(&mut OsRng); let keys = key_gen::<_, Ed25519>(&mut OsRng);
let randomness = random_scalar(&mut OsRng); let randomness = Scalar::random(&mut OsRng);
let mut ring = vec![]; let mut ring = vec![];
for i in 0 .. RING_LEN { for i in 0 .. RING_LEN {
let dest; let dest;
let mask; let mask;
let amount; let amount;
if i != u64::from(RING_INDEX) { if i != u64::from(RING_INDEX) {
dest = &random_scalar(&mut OsRng) * ED25519_BASEPOINT_TABLE; dest = &Scalar::random(&mut OsRng) * ED25519_BASEPOINT_TABLE;
mask = random_scalar(&mut OsRng); mask = Scalar::random(&mut OsRng);
amount = OsRng.next_u64(); amount = OsRng.next_u64();
} else { } else {
dest = keys[&Participant::new(1).unwrap()].group_key().0; dest = keys[&Participant::new(1).unwrap()].group_key().0;
@@ -108,7 +108,7 @@ fn clsag_multisig() {
ring.push([dest, Commitment::new(mask, amount).calculate()]); ring.push([dest, Commitment::new(mask, amount).calculate()]);
} }
let mask_sum = random_scalar(&mut OsRng); let mask_sum = Scalar::random(&mut OsRng);
let algorithm = ClsagMultisig::new( let algorithm = ClsagMultisig::new(
RecommendedTranscript::new(b"Monero Serai CLSAG Test"), RecommendedTranscript::new(b"Monero Serai CLSAG Test"),
keys[&Participant::new(1).unwrap()].group_key().0, keys[&Participant::new(1).unwrap()].group_key().0,

View File

@@ -5,7 +5,7 @@ use zeroize::Zeroize;
use curve25519_dalek::edwards::EdwardsPoint; use curve25519_dalek::edwards::EdwardsPoint;
use monero_generators::decompress_point; use monero_io::decompress_point;
use base58_monero::base58::{encode_check, decode_check}; use base58_monero::base58::{encode_check, decode_check};

View File

@@ -1,6 +1,6 @@
use std_shims::{vec::Vec, collections::HashSet}; use std_shims::{vec::Vec, collections::HashSet};
use zeroize::{Zeroize, ZeroizeOnDrop}; use zeroize::Zeroize;
use rand_core::{RngCore, CryptoRng}; use rand_core::{RngCore, CryptoRng};
use rand_distr::{Distribution, Gamma}; use rand_distr::{Distribution, Gamma};
@@ -10,7 +10,6 @@ use rand_distr::num_traits::Float;
use curve25519_dalek::edwards::EdwardsPoint; use curve25519_dalek::edwards::EdwardsPoint;
use crate::{ use crate::{
serialize::varint_len,
wallet::SpendableOutput, wallet::SpendableOutput,
rpc::{RpcError, RpcConnection, Rpc}, rpc::{RpcError, RpcConnection, Rpc},
DEFAULT_LOCK_WINDOW, COINBASE_LOCK_WINDOW, BLOCK_TIME, DEFAULT_LOCK_WINDOW, COINBASE_LOCK_WINDOW, BLOCK_TIME,
@@ -272,35 +271,38 @@ async fn select_decoys<R: RngCore + CryptoRng, RPC: RpcConnection>(
Ok(res) Ok(res)
} }
/// Decoy data, containing the actual member as well (at index `i`). pub use monero_primitives::Decoys;
#[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)]
pub struct Decoys { // TODO: Remove this trait
pub(crate) i: u8, #[cfg(feature = "std")]
pub(crate) offsets: Vec<u64>, #[async_trait::async_trait]
pub(crate) ring: Vec<[EdwardsPoint; 2]>, pub trait DecoySelection {
} async fn select<R: Send + Sync + RngCore + CryptoRng, RPC: Send + Sync + RpcConnection>(
rng: &mut R,
#[allow(clippy::len_without_is_empty)] rpc: &Rpc<RPC>,
impl Decoys { ring_len: usize,
pub fn fee_weight(offsets: &[u64]) -> usize { height: usize,
varint_len(offsets.len()) + offsets.iter().map(|offset| varint_len(*offset)).sum::<usize>() inputs: &[SpendableOutput],
} ) -> Result<Vec<Decoys>, RpcError>;
pub fn len(&self) -> usize { async fn fingerprintable_canonical_select<
self.offsets.len() R: Send + Sync + RngCore + CryptoRng,
} RPC: Send + Sync + RpcConnection,
>(
pub fn indexes(&self) -> Vec<u64> { rng: &mut R,
let mut res = vec![self.offsets[0]; self.len()]; rpc: &Rpc<RPC>,
for m in 1 .. res.len() { ring_len: usize,
res[m] = res[m - 1] + self.offsets[m]; height: usize,
} inputs: &[SpendableOutput],
res ) -> Result<Vec<Decoys>, RpcError>;
} }
#[cfg(feature = "std")]
#[async_trait::async_trait]
impl DecoySelection for Decoys {
/// Select decoys using the same distribution as Monero. Relies on the monerod RPC /// Select decoys using the same distribution as Monero. Relies on the monerod RPC
/// response for an output's unlocked status, minimizing trips to the daemon. /// response for an output's unlocked status, minimizing trips to the daemon.
pub async fn select<R: RngCore + CryptoRng, RPC: RpcConnection>( async fn select<R: Send + Sync + RngCore + CryptoRng, RPC: Send + Sync + RpcConnection>(
rng: &mut R, rng: &mut R,
rpc: &Rpc<RPC>, rpc: &Rpc<RPC>,
ring_len: usize, ring_len: usize,
@@ -318,7 +320,10 @@ impl Decoys {
/// ///
/// TODO: upstream change to monerod get_outs RPC to accept a height param for checking /// TODO: upstream change to monerod get_outs RPC to accept a height param for checking
/// output's unlocked status and remove all usage of fingerprintable_canonical /// output's unlocked status and remove all usage of fingerprintable_canonical
pub async fn fingerprintable_canonical_select<R: RngCore + CryptoRng, RPC: RpcConnection>( async fn fingerprintable_canonical_select<
R: Send + Sync + RngCore + CryptoRng,
RPC: Send + Sync + RpcConnection,
>(
rng: &mut R, rng: &mut R,
rpc: &Rpc<RPC>, rpc: &Rpc<RPC>,
ring_len: usize, ring_len: usize,

View File

@@ -10,7 +10,8 @@ use curve25519_dalek::{
}; };
use crate::{ use crate::{
hash, hash_to_scalar, serialize::write_varint, Commitment, ringct::EncryptedAmount, transaction::Input, hash, hash_to_scalar, serialize::write_varint, Commitment, ringct::EncryptedAmount,
transaction::Input,
}; };
pub mod extra; pub mod extra;
@@ -26,8 +27,14 @@ use address::{Network, AddressType, SubaddressIndex, AddressSpec, AddressMeta, M
mod scan; mod scan;
pub use scan::{ReceivedOutput, SpendableOutput, Timelocked}; pub use scan::{ReceivedOutput, SpendableOutput, Timelocked};
#[cfg(feature = "std")]
pub mod decoys; pub mod decoys;
pub use decoys::Decoys; #[cfg(not(feature = "std"))]
pub mod decoys {
pub use monero_primitives::Decoys;
pub trait DecoySelection {}
}
pub use decoys::{DecoySelection, Decoys};
mod send; mod send;
pub use send::{FeePriority, Fee, TransactionError, Change, SignableTransaction, Eventuality}; pub use send::{FeePriority, Fee, TransactionError, Change, SignableTransaction, Eventuality};

View File

@@ -9,7 +9,7 @@ use zeroize::{Zeroize, ZeroizeOnDrop};
use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE, scalar::Scalar, edwards::EdwardsPoint}; use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE, scalar::Scalar, edwards::EdwardsPoint};
use monero_generators::decompress_point; use monero_io::decompress_point;
use crate::{ use crate::{
Commitment, Commitment,

View File

@@ -11,7 +11,7 @@ use rand_core::{RngCore, CryptoRng};
use curve25519_dalek::scalar::Scalar; use curve25519_dalek::scalar::Scalar;
use crate::{random_scalar, wallet::seed::SeedError}; use crate::wallet::seed::SeedError;
pub(crate) const CLASSIC_SEED_LENGTH: usize = 24; pub(crate) const CLASSIC_SEED_LENGTH: usize = 24;
pub(crate) const CLASSIC_SEED_LENGTH_WITH_CHECKSUM: usize = 25; pub(crate) const CLASSIC_SEED_LENGTH_WITH_CHECKSUM: usize = 25;
@@ -276,7 +276,7 @@ pub(crate) fn seed_to_bytes(lang: Language, words: &str) -> Result<Zeroizing<[u8
pub struct ClassicSeed(Language, Zeroizing<String>); pub struct ClassicSeed(Language, Zeroizing<String>);
impl ClassicSeed { impl ClassicSeed {
pub(crate) fn new<R: RngCore + CryptoRng>(rng: &mut R, lang: Language) -> ClassicSeed { pub(crate) fn new<R: RngCore + CryptoRng>(rng: &mut R, lang: Language) -> ClassicSeed {
key_to_seed(lang, Zeroizing::new(random_scalar(rng))) key_to_seed(lang, Zeroizing::new(Scalar::random(rng)))
} }
#[allow(clippy::needless_pass_by_value)] #[allow(clippy::needless_pass_by_value)]

View File

@@ -23,7 +23,7 @@ use dalek_ff_group as dfg;
use frost::FrostError; use frost::FrostError;
use crate::{ use crate::{
Protocol, Commitment, hash, random_scalar, Protocol, Commitment, hash,
serialize::{ serialize::{
read_byte, read_bytes, read_u64, read_scalar, read_point, read_vec, write_byte, write_scalar, read_byte, read_bytes, read_u64, read_scalar, read_point, read_vec, write_byte, write_scalar,
write_point, write_raw_vec, write_vec, write_point, write_raw_vec, write_vec,
@@ -616,7 +616,7 @@ impl SignableTransaction {
payments.shuffle(&mut rng); payments.shuffle(&mut rng);
// Used for all non-subaddress outputs, or if there's only one subaddress output and a change // Used for all non-subaddress outputs, or if there's only one subaddress output and a change
let tx_key = Zeroizing::new(random_scalar(&mut rng)); let tx_key = Zeroizing::new(Scalar::random(&mut rng));
let mut tx_public_key = tx_key.deref() * ED25519_BASEPOINT_TABLE; let mut tx_public_key = tx_key.deref() * ED25519_BASEPOINT_TABLE;
// If any of these outputs are to a subaddress, we need keys distinct to them // If any of these outputs are to a subaddress, we need keys distinct to them
@@ -660,7 +660,7 @@ impl SignableTransaction {
let (output, payment_id) = match payment { let (output, payment_id) = match payment {
InternalPayment::Payment(payment, need_dummy_payment_id) => { InternalPayment::Payment(payment, need_dummy_payment_id) => {
// If this is a subaddress, generate a dedicated r. Else, reuse the TX key // If this is a subaddress, generate a dedicated r. Else, reuse the TX key
let dedicated = Zeroizing::new(random_scalar(&mut rng)); let dedicated = Zeroizing::new(Scalar::random(&mut rng));
let use_dedicated = additional && payment.0.is_subaddress(); let use_dedicated = additional && payment.0.is_subaddress();
let r = if use_dedicated { &dedicated } else { &tx_key }; let r = if use_dedicated { &dedicated } else { &tx_key };

View File

@@ -26,7 +26,6 @@ use frost::{
}; };
use crate::{ use crate::{
random_scalar,
ringct::{ ringct::{
clsag::{ClsagInput, ClsagDetails, ClsagAddendum, ClsagMultisig}, clsag::{ClsagInput, ClsagDetails, ClsagAddendum, ClsagMultisig},
RctPrunable, RctPrunable,
@@ -348,7 +347,7 @@ impl SignMachine<Transaction> for TransactionSignMachine {
while !sorted.is_empty() { while !sorted.is_empty() {
let value = sorted.remove(0); let value = sorted.remove(0);
let mut mask = random_scalar(&mut rng); let mut mask = Scalar::random(&mut rng);
if sorted.is_empty() { if sorted.is_empty() {
mask = output_masks - sum_pseudo_outs; mask = output_masks - sum_pseudo_outs;
} else { } else {

View File

@@ -9,7 +9,6 @@ use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE, scalar::Scalar};
use tokio::sync::Mutex; use tokio::sync::Mutex;
use monero_serai::{ use monero_serai::{
random_scalar,
rpc::{HttpRpc, Rpc}, rpc::{HttpRpc, Rpc},
wallet::{ wallet::{
ViewPair, Scanner, ViewPair, Scanner,
@@ -21,9 +20,9 @@ use monero_serai::{
}; };
pub fn random_address() -> (Scalar, ViewPair, MoneroAddress) { pub fn random_address() -> (Scalar, ViewPair, MoneroAddress) {
let spend = random_scalar(&mut OsRng); let spend = Scalar::random(&mut OsRng);
let spend_pub = &spend * ED25519_BASEPOINT_TABLE; let spend_pub = &spend * ED25519_BASEPOINT_TABLE;
let view = Zeroizing::new(random_scalar(&mut OsRng)); let view = Zeroizing::new(Scalar::random(&mut OsRng));
( (
spend, spend,
ViewPair::new(spend_pub, view.clone()), ViewPair::new(spend_pub, view.clone()),
@@ -103,8 +102,8 @@ pub async fn rpc() -> Rpc<HttpRpc> {
let addr = MoneroAddress { let addr = MoneroAddress {
meta: AddressMeta::new(Network::Mainnet, AddressType::Standard), meta: AddressMeta::new(Network::Mainnet, AddressType::Standard),
spend: &random_scalar(&mut OsRng) * ED25519_BASEPOINT_TABLE, spend: &Scalar::random(&mut OsRng) * ED25519_BASEPOINT_TABLE,
view: &random_scalar(&mut OsRng) * ED25519_BASEPOINT_TABLE, view: &Scalar::random(&mut OsRng) * ED25519_BASEPOINT_TABLE,
} }
.to_string(); .to_string();
@@ -161,7 +160,7 @@ macro_rules! test {
use zeroize::Zeroizing; use zeroize::Zeroizing;
use rand_core::OsRng; use rand_core::OsRng;
use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE; use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE, scalar::Scalar};
#[cfg(feature = "multisig")] #[cfg(feature = "multisig")]
use transcript::{Transcript, RecommendedTranscript}; use transcript::{Transcript, RecommendedTranscript};
@@ -173,9 +172,9 @@ macro_rules! test {
}; };
use monero_serai::{ use monero_serai::{
random_scalar,
wallet::{ wallet::{
address::{Network, AddressSpec}, ViewPair, Scanner, Change, Decoys, FeePriority, address::{Network, AddressSpec},
ViewPair, Scanner, Change, DecoySelection, Decoys, FeePriority,
SignableTransaction, SignableTransactionBuilder, SignableTransaction, SignableTransactionBuilder,
}, },
}; };
@@ -196,7 +195,7 @@ macro_rules! test {
continue; continue;
} }
let spend = Zeroizing::new(random_scalar(&mut OsRng)); let spend = Zeroizing::new(Scalar::random(&mut OsRng));
#[cfg(feature = "multisig")] #[cfg(feature = "multisig")]
let keys = key_gen::<_, Ed25519>(&mut OsRng); let keys = key_gen::<_, Ed25519>(&mut OsRng);
@@ -211,7 +210,7 @@ macro_rules! test {
let rpc = rpc().await; let rpc = rpc().await;
let view = ViewPair::new(spend_pub, Zeroizing::new(random_scalar(&mut OsRng))); let view = ViewPair::new(spend_pub, Zeroizing::new(Scalar::random(&mut OsRng)));
let addr = view.address(Network::Mainnet, AddressSpec::Standard); let addr = view.address(Network::Mainnet, AddressSpec::Standard);
let miner_tx = get_miner_tx_output(&rpc, &view).await; let miner_tx = get_miner_tx_output(&rpc, &view).await;
@@ -223,8 +222,8 @@ macro_rules! test {
rpc.get_fee(protocol, FeePriority::Unimportant).await.unwrap(), rpc.get_fee(protocol, FeePriority::Unimportant).await.unwrap(),
Change::new( Change::new(
&ViewPair::new( &ViewPair::new(
&random_scalar(&mut OsRng) * ED25519_BASEPOINT_TABLE, &Scalar::random(&mut OsRng) * ED25519_BASEPOINT_TABLE,
Zeroizing::new(random_scalar(&mut OsRng)) Zeroizing::new(Scalar::random(&mut OsRng))
), ),
false false
), ),

View File

@@ -3,8 +3,8 @@ use rand_core::OsRng;
use monero_serai::{ use monero_serai::{
transaction::Transaction, transaction::Transaction,
wallet::{ wallet::{
extra::Extra, address::SubaddressIndex, ReceivedOutput, SpendableOutput, Decoys, extra::Extra, address::SubaddressIndex, ReceivedOutput, SpendableOutput, DecoySelection,
SignableTransactionBuilder, Decoys, SignableTransactionBuilder,
}, },
rpc::{Rpc, HttpRpc}, rpc::{Rpc, HttpRpc},
Protocol, Protocol,
@@ -104,8 +104,8 @@ test!(
use monero_serai::wallet::FeePriority; use monero_serai::wallet::FeePriority;
let change_view = ViewPair::new( let change_view = ViewPair::new(
&random_scalar(&mut OsRng) * ED25519_BASEPOINT_TABLE, &Scalar::random(&mut OsRng) * ED25519_BASEPOINT_TABLE,
Zeroizing::new(random_scalar(&mut OsRng)), Zeroizing::new(Scalar::random(&mut OsRng)),
); );
let mut builder = SignableTransactionBuilder::new( let mut builder = SignableTransactionBuilder::new(
@@ -117,8 +117,8 @@ test!(
// Send to a subaddress // Send to a subaddress
let sub_view = ViewPair::new( let sub_view = ViewPair::new(
&random_scalar(&mut OsRng) * ED25519_BASEPOINT_TABLE, &Scalar::random(&mut OsRng) * ED25519_BASEPOINT_TABLE,
Zeroizing::new(random_scalar(&mut OsRng)), Zeroizing::new(Scalar::random(&mut OsRng)),
); );
builder.add_payment( builder.add_payment(
sub_view sub_view

View File

@@ -22,7 +22,7 @@ use monero_serai::{
wallet::{ wallet::{
ViewPair, Scanner, ViewPair, Scanner,
address::{Network as MoneroNetwork, SubaddressIndex, AddressSpec}, address::{Network as MoneroNetwork, SubaddressIndex, AddressSpec},
Fee, SpendableOutput, Change, Decoys, TransactionError, Fee, SpendableOutput, Change, DecoySelection, Decoys, TransactionError,
SignableTransaction as MSignableTransaction, Eventuality, TransactionMachine, SignableTransaction as MSignableTransaction, Eventuality, TransactionMachine,
}, },
}; };

View File

@@ -349,7 +349,7 @@ async fn mint_and_burn_test() {
Protocol, Protocol,
transaction::Timelock, transaction::Timelock,
wallet::{ wallet::{
ViewPair, Scanner, Decoys, Change, FeePriority, SignableTransaction, ViewPair, Scanner, DecoySelection, Decoys, Change, FeePriority, SignableTransaction,
address::{Network, AddressType, AddressMeta, MoneroAddress}, address::{Network, AddressType, AddressMeta, MoneroAddress},
}, },
decompress_point, decompress_point,

View File

@@ -35,5 +35,8 @@ dkg = { path = "../../crypto/dkg", default-features = false }
bitcoin-serai = { path = "../../coins/bitcoin", default-features = false, features = ["hazmat"] } bitcoin-serai = { path = "../../coins/bitcoin", default-features = false, features = ["hazmat"] }
monero-io = { path = "../../coins/monero/io", default-features = false }
monero-generators = { path = "../../coins/monero/generators", default-features = false } monero-generators = { path = "../../coins/monero/generators", default-features = false }
monero-primitives = { path = "../../coins/monero/primitives", default-features = false }
monero-clsag = { path = "../../coins/monero/ringct/clsag", default-features = false }
monero-serai = { path = "../../coins/monero", default-features = false } monero-serai = { path = "../../coins/monero", default-features = false }

View File

@@ -440,7 +440,8 @@ impl Wallet {
Protocol, Protocol,
wallet::{ wallet::{
address::{Network, AddressType, AddressMeta, Address}, address::{Network, AddressType, AddressMeta, Address},
SpendableOutput, Decoys, Change, FeePriority, Scanner, SignableTransaction, SpendableOutput, DecoySelection, Decoys, Change, FeePriority, Scanner,
SignableTransaction,
}, },
rpc::HttpRpc, rpc::HttpRpc,
decompress_point, decompress_point,