Mostly lint Monero

This commit is contained in:
Luke Parker 2023-07-08 00:56:43 -04:00
parent dd5fb0df47
commit f93106af6b
No known key found for this signature in database
35 changed files with 553 additions and 459 deletions

View file

@ -15,7 +15,11 @@
-A clippy::redundant_pub_crate
-A clippy::similar_names
# Flags on any debug_assert using an RNG
-A clippy::debug_assert_with_mut_call
# Frequently used
-A clippy::large_types_passed_by_value
-A clippy::wildcard_imports
-A clippy::too_many_lines

View file

@ -28,10 +28,10 @@ fn serialize(generators_string: &mut String, points: &[EdwardsPoint]) {
fn generators(prefix: &'static str, path: &str) {
let generators = bulletproofs_generators(prefix.as_bytes());
#[allow(non_snake_case)]
let mut G_str = "".to_string();
let mut G_str = String::new();
serialize(&mut G_str, &generators.G);
#[allow(non_snake_case)]
let mut H_str = "".to_string();
let mut H_str = String::new();
serialize(&mut H_str, &generators.H);
let path = Path::new(&env::var("OUT_DIR").unwrap()).join(path);

View file

@ -8,8 +8,10 @@ use dalek_ff_group::FieldElement;
use crate::hash;
/// Monero's hash to point function, as named `ge_fromfe_frombytes_vartime`.
#[allow(clippy::many_single_char_names)]
#[must_use]
pub fn hash_to_point(bytes: [u8; 32]) -> EdwardsPoint {
#[allow(non_snake_case)]
#[allow(non_snake_case, clippy::unreadable_literal)]
let A = FieldElement::from(486662u64);
let v = FieldElement::from_square(hash(&bytes)).double();

View file

@ -61,6 +61,7 @@ pub struct Generators {
}
/// Generate generators as needed for Bulletproofs(+), as Monero does.
#[must_use]
pub fn bulletproofs_generators(dst: &'static [u8]) -> Generators {
let mut res =
Generators { G: [EdwardsPoint::identity(); MAX_MN], H: [EdwardsPoint::identity(); MAX_MN] };

View file

@ -1,6 +1,8 @@
use std_shims::io::{self, Write};
const VARINT_CONTINUATION_MASK: u8 = 0b1000_0000;
#[allow(clippy::trivially_copy_pass_by_ref)] // &u64 is needed for API consistency
pub(crate) fn write_varint<W: Write>(varint: &u64, w: &mut W) -> io::Result<()> {
let mut varint = *varint;
while {

View file

@ -33,14 +33,15 @@ impl BlockHeader {
w.write_all(&self.nonce.to_le_bytes())
}
#[must_use]
pub fn serialize(&self) -> Vec<u8> {
let mut serialized = vec![];
self.write(&mut serialized).unwrap();
serialized
}
pub fn read<R: Read>(r: &mut R) -> io::Result<BlockHeader> {
Ok(BlockHeader {
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
Ok(Self {
major_version: read_varint(r)?,
minor_version: read_varint(r)?,
timestamp: read_varint(r)?,
@ -58,6 +59,7 @@ pub struct Block {
}
impl Block {
#[must_use]
pub fn number(&self) -> usize {
match self.miner_tx.prefix.inputs.get(0) {
Some(Input::Gen(number)) => (*number).try_into().unwrap(),
@ -91,6 +93,7 @@ impl Block {
out
}
#[must_use]
pub fn hash(&self) -> [u8; 32] {
let hash = hash(&self.serialize_hashable());
if hash == CORRECT_BLOCK_HASH_202612 {
@ -100,14 +103,15 @@ impl Block {
hash
}
#[must_use]
pub fn serialize(&self) -> Vec<u8> {
let mut serialized = vec![];
self.write(&mut serialized).unwrap();
serialized
}
pub fn read<R: Read>(r: &mut R) -> io::Result<Block> {
Ok(Block {
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
Ok(Self {
header: BlockHeader::read(r)?,
miner_tx: Transaction::read(r)?,
txs: (0 .. read_varint(r)?).map(|_| read_bytes(r)).collect::<Result<_, _>>()?,

View file

@ -60,39 +60,42 @@ pub enum Protocol {
impl Protocol {
/// Amount of ring members under this protocol version.
pub fn ring_len(&self) -> usize {
#[must_use]
pub const fn ring_len(&self) -> usize {
match self {
Protocol::v14 => 11,
Protocol::v16 => 16,
Protocol::Custom { ring_len, .. } => *ring_len,
Self::v14 => 11,
Self::v16 => 16,
Self::Custom { ring_len, .. } => *ring_len,
}
}
/// Whether or not the specified version uses Bulletproofs or Bulletproofs+.
///
/// This method will likely be reworked when versions not using Bulletproofs at all are added.
pub fn bp_plus(&self) -> bool {
#[must_use]
pub const fn bp_plus(&self) -> bool {
match self {
Protocol::v14 => false,
Protocol::v16 => true,
Protocol::Custom { bp_plus, .. } => *bp_plus,
Self::v14 => false,
Self::v16 => true,
Self::Custom { bp_plus, .. } => *bp_plus,
}
}
// TODO: Make this an Option when we support pre-RCT protocols
pub fn optimal_rct_type(&self) -> RctType {
#[must_use]
pub const fn optimal_rct_type(&self) -> RctType {
match self {
Protocol::v14 => RctType::Clsag,
Protocol::v16 => RctType::BulletproofsPlus,
Protocol::Custom { optimal_rct_type, .. } => *optimal_rct_type,
Self::v14 => RctType::Clsag,
Self::v16 => RctType::BulletproofsPlus,
Self::Custom { optimal_rct_type, .. } => *optimal_rct_type,
}
}
pub(crate) fn write<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
match self {
Protocol::v14 => w.write_all(&[0, 14]),
Protocol::v16 => w.write_all(&[0, 16]),
Protocol::Custom { ring_len, bp_plus, optimal_rct_type } => {
Self::v14 => w.write_all(&[0, 14]),
Self::v16 => w.write_all(&[0, 16]),
Self::Custom { ring_len, bp_plus, optimal_rct_type } => {
// Custom, version 0
w.write_all(&[1, 0])?;
w.write_all(&u16::try_from(*ring_len).unwrap().to_le_bytes())?;
@ -102,17 +105,17 @@ impl Protocol {
}
}
pub(crate) fn read<R: io::Read>(r: &mut R) -> io::Result<Protocol> {
pub(crate) fn read<R: io::Read>(r: &mut R) -> io::Result<Self> {
Ok(match read_byte(r)? {
// Monero protocol
0 => match read_byte(r)? {
14 => Protocol::v14,
16 => Protocol::v16,
14 => Self::v14,
16 => Self::v16,
_ => Err(io::Error::new(io::ErrorKind::Other, "unrecognized monero protocol"))?,
},
// Custom
1 => match read_byte(r)? {
0 => Protocol::Custom {
0 => Self::Custom {
ring_len: read_u16(r)?.into(),
bp_plus: match read_byte(r)? {
0 => false,
@ -140,22 +143,26 @@ pub struct Commitment {
}
impl Commitment {
/// The zero commitment, defined as a mask of 1 (as to not be the identity) and a 0 amount.
pub fn zero() -> Commitment {
Commitment { mask: Scalar::one(), amount: 0 }
/// A commitment to zero, defined with a mask of 1 (as to not be the identity).
#[must_use]
pub fn zero() -> Self {
Self { mask: Scalar::one(), amount: 0 }
}
pub fn new(mask: Scalar, amount: u64) -> Commitment {
Commitment { mask, amount }
#[must_use]
pub const fn new(mask: Scalar, amount: u64) -> Self {
Self { mask, amount }
}
/// Calculate a Pedersen commitment, as a point, from the transparent structure.
#[must_use]
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.
#[must_use]
pub fn random_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Scalar {
let mut r = [0; 64];
rng.fill_bytes(&mut r);
@ -167,6 +174,7 @@ pub(crate) fn hash(data: &[u8]) -> [u8; 32] {
}
/// Hash the provided data to a scalar via keccak256(data) % l.
#[must_use]
pub fn hash_to_scalar(data: &[u8]) -> Scalar {
let scalar = Scalar::from_bytes_mod_order(hash(data));
// Monero will explicitly error in this case

View file

@ -2,7 +2,8 @@ use std_shims::vec::Vec;
use crate::hash;
pub fn merkle_root(root: [u8; 32], leafs: &[[u8; 32]]) -> [u8; 32] {
#[must_use]
pub(crate) fn merkle_root(root: [u8; 32], leafs: &[[u8; 32]]) -> [u8; 32] {
match leafs.len() {
0 => root,
1 => hash(&[root, leafs[0]].concat()),

View file

@ -26,19 +26,15 @@ pub struct BorromeanSignatures {
}
impl BorromeanSignatures {
pub fn read<R: Read>(r: &mut R) -> io::Result<BorromeanSignatures> {
Ok(BorromeanSignatures {
s0: read_array(read_bytes, r)?,
s1: read_array(read_bytes, r)?,
ee: read_bytes(r)?,
})
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
Ok(Self { s0: read_array(read_bytes, r)?, s1: read_array(read_bytes, r)?, ee: read_bytes(r)? })
}
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
for s0 in self.s0.iter() {
for s0 in &self.s0 {
w.write_all(s0)?;
}
for s1 in self.s1.iter() {
for s1 in &self.s1 {
w.write_all(s1)?;
}
w.write_all(&self.ee)
@ -79,11 +75,8 @@ pub struct BorromeanRange {
}
impl BorromeanRange {
pub fn read<R: Read>(r: &mut R) -> io::Result<BorromeanRange> {
Ok(BorromeanRange {
sigs: BorromeanSignatures::read(r)?,
bit_commitments: read_array(read_point, r)?,
})
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
Ok(Self { sigs: BorromeanSignatures::read(r)?, bit_commitments: read_array(read_point, r)? })
}
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
self.sigs.write(w)?;
@ -91,6 +84,7 @@ impl BorromeanRange {
}
#[cfg(feature = "experimental")]
#[must_use]
pub fn verify(&self, commitment: &EdwardsPoint) -> bool {
if &self.bit_commitments.iter().sum::<EdwardsPoint>() != commitment {
return false;

View file

@ -50,13 +50,13 @@ pub(crate) fn vector_exponent(
pub(crate) fn hash_cache(cache: &mut Scalar, mash: &[[u8; 32]]) -> Scalar {
let slice =
&[cache.to_bytes().as_ref(), mash.iter().cloned().flatten().collect::<Vec<_>>().as_ref()]
&[cache.to_bytes().as_ref(), mash.iter().copied().flatten().collect::<Vec<_>>().as_ref()]
.concat();
*cache = hash_to_scalar(slice);
*cache
}
pub(crate) fn MN(outputs: usize) -> (usize, usize, usize) {
pub(crate) const fn MN(outputs: usize) -> (usize, usize, usize) {
let mut logM = 0;
let mut M;
while {
@ -78,10 +78,8 @@ pub(crate) fn bit_decompose(commitments: &[Commitment]) -> (ScalarVector, Scalar
for j in 0 .. M {
for i in (0 .. N).rev() {
let mut bit = Choice::from(0);
if j < sv.len() {
bit = Choice::from((sv[j][i / 8] >> (i % 8)) & 1);
}
let bit =
if j < sv.len() { Choice::from((sv[j][i / 8] >> (i % 8)) & 1) } else { Choice::from(0) };
aL.0[(j * N) + i] = Scalar::conditional_select(&Scalar::ZERO, &Scalar::ONE, bit);
aR.0[(j * N) + i] = Scalar::conditional_select(&-Scalar::ONE, &Scalar::ZERO, bit);
}
@ -118,9 +116,9 @@ pub(crate) fn LR_statements(
let mut res = a
.0
.iter()
.cloned()
.zip(G_i.iter().cloned())
.chain(b.0.iter().cloned().zip(H_i.iter().cloned()))
.copied()
.zip(G_i.iter().copied())
.chain(b.0.iter().copied().zip(H_i.iter().copied()))
.collect::<Vec<_>>();
res.push((cL, U));
res

View file

@ -62,14 +62,14 @@ impl Bulletproofs {
rng: &mut R,
outputs: &[Commitment],
plus: bool,
) -> Result<Bulletproofs, TransactionError> {
) -> Result<Self, TransactionError> {
if outputs.len() > MAX_OUTPUTS {
return Err(TransactionError::TooManyOutputs)?;
}
Ok(if !plus {
Bulletproofs::Original(OriginalStruct::prove(rng, outputs))
Self::Plus(PlusStruct::prove(rng, outputs))
} else {
Bulletproofs::Plus(PlusStruct::prove(rng, outputs))
Self::Original(OriginalStruct::prove(rng, outputs))
})
}
@ -77,8 +77,8 @@ impl Bulletproofs {
#[must_use]
pub fn verify<R: RngCore + CryptoRng>(&self, rng: &mut R, commitments: &[EdwardsPoint]) -> bool {
match self {
Bulletproofs::Original(bp) => bp.verify(rng, commitments),
Bulletproofs::Plus(bp) => bp.verify(rng, commitments),
Self::Original(bp) => bp.verify(rng, commitments),
Self::Plus(bp) => bp.verify(rng, commitments),
}
}
@ -94,8 +94,8 @@ impl Bulletproofs {
commitments: &[EdwardsPoint],
) -> bool {
match self {
Bulletproofs::Original(bp) => bp.batch_verify(rng, verifier, id, commitments),
Bulletproofs::Plus(bp) => bp.batch_verify(rng, verifier, id, commitments),
Self::Original(bp) => bp.batch_verify(rng, verifier, id, commitments),
Self::Plus(bp) => bp.batch_verify(rng, verifier, id, commitments),
}
}
@ -105,7 +105,7 @@ impl Bulletproofs {
specific_write_vec: F,
) -> io::Result<()> {
match self {
Bulletproofs::Original(bp) => {
Self::Original(bp) => {
write_point(&bp.A, w)?;
write_point(&bp.S, w)?;
write_point(&bp.T1, w)?;
@ -119,7 +119,7 @@ impl Bulletproofs {
write_scalar(&bp.t, w)
}
Bulletproofs::Plus(bp) => {
Self::Plus(bp) => {
write_point(&bp.A, w)?;
write_point(&bp.A1, w)?;
write_point(&bp.B, w)?;
@ -140,6 +140,7 @@ impl Bulletproofs {
self.write_core(w, |points, w| write_vec(write_point, points, w))
}
#[must_use]
pub fn serialize(&self) -> Vec<u8> {
let mut serialized = vec![];
self.write(&mut serialized).unwrap();
@ -147,8 +148,8 @@ impl Bulletproofs {
}
/// Read Bulletproofs.
pub fn read<R: Read>(r: &mut R) -> io::Result<Bulletproofs> {
Ok(Bulletproofs::Original(OriginalStruct {
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
Ok(Self::Original(OriginalStruct {
A: read_point(r)?,
S: read_point(r)?,
T1: read_point(r)?,
@ -164,8 +165,8 @@ impl Bulletproofs {
}
/// Read Bulletproofs+.
pub fn read_plus<R: Read>(r: &mut R) -> io::Result<Bulletproofs> {
Ok(Bulletproofs::Plus(PlusStruct {
pub fn read_plus<R: Read>(r: &mut R) -> io::Result<Self> {
Ok(Self::Plus(PlusStruct {
A: read_point(r)?,
A1: read_point(r)?,
B: read_point(r)?,

View file

@ -36,10 +36,8 @@ pub struct OriginalStruct {
}
impl OriginalStruct {
pub(crate) fn prove<R: RngCore + CryptoRng>(
rng: &mut R,
commitments: &[Commitment],
) -> OriginalStruct {
#[allow(clippy::many_single_char_names)]
pub(crate) fn prove<R: RngCore + CryptoRng>(rng: &mut R, commitments: &[Commitment]) -> Self {
let (logMN, M, MN) = MN(commitments.len());
let (aL, aR) = bit_decompose(commitments);
@ -134,8 +132,8 @@ impl OriginalStruct {
let L_i = prove_multiexp(&LR_statements(&aL, G_R, &bR, H_L, cL, U));
let R_i = prove_multiexp(&LR_statements(&aR, G_L, &bL, H_R, cR, U));
L.push(L_i);
R.push(R_i);
L.push(*L_i);
R.push(*R_i);
let w = hash_cache(&mut cache, &[L_i.compress().to_bytes(), R_i.compress().to_bytes()]);
let winv = w.invert().unwrap();
@ -149,15 +147,15 @@ impl OriginalStruct {
}
}
let res = OriginalStruct {
let res = Self {
A: *A,
S: *S,
T1: *T1,
T2: *T2,
taux: *taux,
mu: *mu,
L: L.drain(..).map(|L| *L).collect(),
R: R.drain(..).map(|R| *R).collect(),
L,
R,
a: *a[0],
b: *b[0],
t: *t,
@ -166,6 +164,7 @@ impl OriginalStruct {
res
}
#[allow(clippy::many_single_char_names)]
#[must_use]
fn verify_core<ID: Copy + Zeroize, R: RngCore + CryptoRng>(
&self,
@ -190,7 +189,7 @@ impl OriginalStruct {
}
// Rebuild all challenges
let (mut cache, commitments) = hash_commitments(commitments.iter().cloned());
let (mut cache, commitments) = hash_commitments(commitments.iter().copied());
let y = hash_cache(&mut cache, &[self.A.compress().to_bytes(), self.S.compress().to_bytes()]);
let z = hash_to_scalar(&y.to_bytes());
@ -223,7 +222,7 @@ impl OriginalStruct {
let A = normalize(&self.A);
let S = normalize(&self.S);
let commitments = commitments.iter().map(|c| c.mul_by_cofactor()).collect::<Vec<_>>();
let commitments = commitments.iter().map(EdwardsPoint::mul_by_cofactor).collect::<Vec<_>>();
// Verify it
let mut proof = Vec::with_capacity(4 + commitments.len());

View file

@ -56,10 +56,8 @@ pub struct PlusStruct {
}
impl PlusStruct {
pub(crate) fn prove<R: RngCore + CryptoRng>(
rng: &mut R,
commitments: &[Commitment],
) -> PlusStruct {
#[allow(clippy::many_single_char_names)]
pub(crate) fn prove<R: RngCore + CryptoRng>(rng: &mut R, commitments: &[Commitment]) -> Self {
let generators = GENERATORS();
let (logMN, M, MN) = MN(commitments.len());
@ -113,12 +111,12 @@ impl PlusStruct {
let mut L_i = LR_statements(&(&aL * yinvpow[aL.len()]), G_R, &bR, H_L, cL, H());
L_i.push((dL, G));
let L_i = prove_multiexp(&L_i);
L.push(L_i);
L.push(*L_i);
let mut R_i = LR_statements(&(&aR * ypow[aR.len()]), G_L, &bL, H_R, cR, H());
R_i.push((dR, G));
let R_i = prove_multiexp(&R_i);
R.push(R_i);
R.push(*R_i);
let w = hash_cache(&mut cache, &[L_i.compress().to_bytes(), R_i.compress().to_bytes()]);
let winv = w.invert().unwrap();
@ -158,20 +156,12 @@ impl PlusStruct {
eta.zeroize();
alpha1.zeroize();
let res = PlusStruct {
A: *A,
A1: *A1,
B: *B,
r1: *r1,
s1: *s1,
d1: *d1,
L: L.drain(..).map(|L| *L).collect(),
R: R.drain(..).map(|R| *R).collect(),
};
let res = Self { A: *A, A1: *A1, B: *B, r1: *r1, s1: *s1, d1: *d1, L, R };
debug_assert!(res.verify(rng, &commitments_points));
res
}
#[allow(clippy::many_single_char_names)]
#[must_use]
fn verify_core<ID: Copy + Zeroize, R: RngCore + CryptoRng>(
&self,
@ -196,7 +186,7 @@ impl PlusStruct {
}
// Rebuild all challenges
let (mut cache, commitments) = hash_plus(commitments.iter().cloned());
let (mut cache, commitments) = hash_plus(commitments.iter().copied());
let y = hash_cache(&mut cache, &[self.A.compress().to_bytes()]);
let yinv = y.invert().unwrap();
let z = hash_to_scalar(&y.to_bytes());
@ -220,8 +210,6 @@ impl PlusStruct {
let A1 = normalize(&self.A1);
let B = normalize(&self.B);
let mut commitments = commitments.iter().map(|c| c.mul_by_cofactor()).collect::<Vec<_>>();
// Verify it
let mut proof = Vec::with_capacity(logMN + 5 + (2 * (MN + logMN)));
@ -237,7 +225,7 @@ impl PlusStruct {
let esq = e * e;
let minus_esq = -esq;
let commitment_weight = minus_esq * yMNy;
for (i, commitment) in commitments.drain(..).enumerate() {
for (i, commitment) in commitments.iter().map(EdwardsPoint::mul_by_cofactor).enumerate() {
proof.push((commitment_weight * zpow[i], commitment));
}

View file

@ -13,9 +13,9 @@ pub(crate) struct ScalarVector(pub(crate) Vec<Scalar>);
macro_rules! math_op {
($Op: ident, $op: ident, $f: expr) => {
impl $Op<Scalar> for ScalarVector {
type Output = ScalarVector;
fn $op(self, b: Scalar) -> ScalarVector {
ScalarVector(self.0.iter().map(|a| $f((a, &b))).collect())
type Output = Self;
fn $op(self, b: Scalar) -> Self {
Self(self.0.iter().map(|a| $f((a, &b))).collect())
}
}
@ -27,16 +27,16 @@ macro_rules! math_op {
}
impl $Op<ScalarVector> for ScalarVector {
type Output = ScalarVector;
fn $op(self, b: ScalarVector) -> ScalarVector {
type Output = Self;
fn $op(self, b: Self) -> Self {
debug_assert_eq!(self.len(), b.len());
ScalarVector(self.0.iter().zip(b.0.iter()).map($f).collect())
Self(self.0.iter().zip(b.0.iter()).map($f).collect())
}
}
impl $Op<&ScalarVector> for &ScalarVector {
impl $Op<Self> for &ScalarVector {
type Output = ScalarVector;
fn $op(self, b: &ScalarVector) -> ScalarVector {
fn $op(self, b: Self) -> ScalarVector {
debug_assert_eq!(self.len(), b.len());
ScalarVector(self.0.iter().zip(b.0.iter()).map($f).collect())
}
@ -48,11 +48,11 @@ math_op!(Sub, sub, |(a, b): (&Scalar, &Scalar)| *a - *b);
math_op!(Mul, mul, |(a, b): (&Scalar, &Scalar)| *a * *b);
impl ScalarVector {
pub(crate) fn new(len: usize) -> ScalarVector {
ScalarVector(vec![Scalar::ZERO; len])
pub(crate) fn new(len: usize) -> Self {
Self(vec![Scalar::ZERO; len])
}
pub(crate) fn powers(x: Scalar, len: usize) -> ScalarVector {
pub(crate) fn powers(x: Scalar, len: usize) -> Self {
debug_assert!(len != 0);
let mut res = Vec::with_capacity(len);
@ -60,16 +60,16 @@ impl ScalarVector {
for i in 1 .. len {
res.push(res[i - 1] * x);
}
ScalarVector(res)
Self(res)
}
pub(crate) fn even_powers(x: Scalar, pow: usize) -> ScalarVector {
pub(crate) fn even_powers(x: Scalar, pow: usize) -> Self {
debug_assert!(pow != 0);
// Verify pow is a power of two
debug_assert_eq!(((pow - 1) & pow), 0);
let xsq = x * x;
let mut res = ScalarVector(Vec::with_capacity(pow / 2));
let mut res = Self(Vec::with_capacity(pow / 2));
res.0.push(xsq);
let mut prev = 2;
@ -89,9 +89,9 @@ impl ScalarVector {
self.0.len()
}
pub(crate) fn split(self) -> (ScalarVector, ScalarVector) {
pub(crate) fn split(self) -> (Self, Self) {
let (l, r) = self.0.split_at(self.0.len() / 2);
(ScalarVector(l.to_vec()), ScalarVector(r.to_vec()))
(Self(l.to_vec()), Self(r.to_vec()))
}
}
@ -119,7 +119,7 @@ impl Mul<&[EdwardsPoint]> for &ScalarVector {
type Output = EdwardsPoint;
fn mul(self, b: &[EdwardsPoint]) -> EdwardsPoint {
debug_assert_eq!(self.len(), b.len());
multiexp(&self.0.iter().cloned().zip(b.iter().cloned()).collect::<Vec<_>>())
multiexp(&self.0.iter().copied().zip(b.iter().copied()).collect::<Vec<_>>())
}
}

View file

@ -30,27 +30,31 @@ pub use multisig::{ClsagDetails, ClsagAddendum, ClsagMultisig};
#[cfg(feature = "multisig")]
pub(crate) use multisig::add_key_image_share;
/// Errors returned when CLSAG signing fails.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "std", derive(thiserror::Error))]
pub enum ClsagError {
#[cfg_attr(feature = "std", error("internal error ({0})"))]
InternalError(&'static str),
#[cfg_attr(feature = "std", error("invalid ring"))]
InvalidRing,
#[cfg_attr(feature = "std", error("invalid ring member (member {0}, ring size {1})"))]
InvalidRingMember(u8, u8),
#[cfg_attr(feature = "std", error("invalid commitment"))]
InvalidCommitment,
#[cfg_attr(feature = "std", error("invalid key image"))]
InvalidImage,
#[cfg_attr(feature = "std", error("invalid D"))]
InvalidD,
#[cfg_attr(feature = "std", error("invalid s"))]
InvalidS,
#[cfg_attr(feature = "std", error("invalid c1"))]
InvalidC1,
#[allow(clippy::std_instead_of_core)]
mod clsag_error {
/// Errors returned when CLSAG signing fails.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "std", derive(thiserror::Error))]
pub enum ClsagError {
#[cfg_attr(feature = "std", error("internal error ({0})"))]
InternalError(&'static str),
#[cfg_attr(feature = "std", error("invalid ring"))]
InvalidRing,
#[cfg_attr(feature = "std", error("invalid ring member (member {0}, ring size {1})"))]
InvalidRingMember(u8, u8),
#[cfg_attr(feature = "std", error("invalid commitment"))]
InvalidCommitment,
#[cfg_attr(feature = "std", error("invalid key image"))]
InvalidImage,
#[cfg_attr(feature = "std", error("invalid D"))]
InvalidD,
#[cfg_attr(feature = "std", error("invalid s"))]
InvalidS,
#[cfg_attr(feature = "std", error("invalid c1"))]
InvalidC1,
}
}
pub use clsag_error::ClsagError;
/// Input being signed for.
#[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)]
@ -62,7 +66,7 @@ pub struct ClsagInput {
}
impl ClsagInput {
pub fn new(commitment: Commitment, decoys: Decoys) -> Result<ClsagInput, ClsagError> {
pub fn new(commitment: Commitment, decoys: Decoys) -> Result<Self, ClsagError> {
let n = decoys.len();
if n > u8::MAX.into() {
Err(ClsagError::InternalError("max ring size in this library is u8 max"))?;
@ -77,7 +81,7 @@ impl ClsagInput {
Err(ClsagError::InvalidCommitment)?;
}
Ok(ClsagInput { commitment, decoys })
Ok(Self { commitment, decoys })
}
}
@ -205,6 +209,7 @@ pub struct Clsag {
impl Clsag {
// Sign core is the extension of core as needed for signing, yet is shared between single signer
// and multisig, hence why it's still core
#[allow(clippy::many_single_char_names)]
pub(crate) fn sign_core<R: RngCore + CryptoRng>(
rng: &mut R,
I: &EdwardsPoint,
@ -213,7 +218,7 @@ impl Clsag {
msg: &[u8; 32],
A: EdwardsPoint,
AH: EdwardsPoint,
) -> (Clsag, EdwardsPoint, Scalar, Scalar) {
) -> (Self, EdwardsPoint, Scalar, Scalar) {
let r: usize = input.decoys.i.into();
let pseudo_out = Commitment::new(mask, input.commitment.amount).calculate();
@ -228,18 +233,19 @@ impl Clsag {
let ((D, p, c), c1) =
core(&input.decoys.ring, I, &pseudo_out, msg, &D, &s, Mode::Sign(r, A, AH));
(Clsag { D, s, c1 }, pseudo_out, p, c * z)
(Self { D, s, c1 }, pseudo_out, p, c * z)
}
/// Generate CLSAG signatures for the given inputs.
/// inputs is of the form (private key, key image, input).
/// sum_outputs is for the sum of the outputs' commitment masks.
#[must_use]
pub fn sign<R: RngCore + CryptoRng>(
rng: &mut R,
mut inputs: Vec<(Zeroizing<Scalar>, EdwardsPoint, ClsagInput)>,
sum_outputs: Scalar,
msg: [u8; 32],
) -> Vec<(Clsag, EdwardsPoint)> {
) -> Vec<(Self, EdwardsPoint)> {
let mut res = Vec::with_capacity(inputs.len());
let mut sum_pseudo_outs = Scalar::zero();
for i in 0 .. inputs.len() {
@ -251,7 +257,7 @@ impl Clsag {
}
let mut nonce = Zeroizing::new(random_scalar(rng));
let (mut clsag, pseudo_out, p, c) = Clsag::sign_core(
let (mut clsag, pseudo_out, p, c) = Self::sign_core(
rng,
&inputs[i].1,
&inputs[i].2,
@ -308,7 +314,7 @@ impl Clsag {
Ok(())
}
pub(crate) fn fee_weight(ring_len: usize) -> usize {
pub(crate) const fn fee_weight(ring_len: usize) -> usize {
(ring_len * 32) + 32 + 32
}
@ -318,7 +324,7 @@ impl Clsag {
write_point(&self.D, w)
}
pub fn read<R: Read>(decoys: usize, r: &mut R) -> io::Result<Clsag> {
Ok(Clsag { s: read_raw_vec(read_scalar, decoys, r)?, c1: read_scalar(r)?, D: read_point(r)? })
pub fn read<R: Read>(decoys: usize, r: &mut R) -> io::Result<Self> {
Ok(Self { s: read_raw_vec(read_scalar, decoys, r)?, c1: read_scalar(r)?, D: read_point(r)? })
}
}

View file

@ -1,6 +1,9 @@
use core::{ops::Deref, fmt::Debug};
use std_shims::io::{self, Read, Write};
use std::sync::{Arc, RwLock};
use std_shims::{
sync::Arc,
io::{self, Read, Write},
};
use std::sync::RwLock;
use rand_core::{RngCore, CryptoRng, SeedableRng};
use rand_chacha::ChaCha20Rng;
@ -48,7 +51,7 @@ impl ClsagInput {
// if in use
transcript.append_message(b"member", [u8::try_from(i).expect("ring size exceeded 255")]);
transcript.append_message(b"key", pair[0].compress().to_bytes());
transcript.append_message(b"commitment", pair[1].compress().to_bytes())
transcript.append_message(b"commitment", pair[1].compress().to_bytes());
}
// Doesn't include the commitment's parts as the above ring + index includes the commitment
@ -65,8 +68,9 @@ pub struct ClsagDetails {
}
impl ClsagDetails {
pub fn new(input: ClsagInput, mask: Scalar) -> ClsagDetails {
ClsagDetails { input, mask }
#[must_use]
pub fn new(input: ClsagInput, mask: Scalar) -> Self {
Self { input, mask }
}
}
@ -112,12 +116,13 @@ pub struct ClsagMultisig {
}
impl ClsagMultisig {
#[must_use]
pub fn new(
transcript: RecommendedTranscript,
output_key: EdwardsPoint,
details: Arc<RwLock<Option<ClsagDetails>>>,
) -> ClsagMultisig {
ClsagMultisig {
) -> Self {
Self {
transcript,
H: hash_to_point(output_key),

View file

@ -3,6 +3,7 @@ use curve25519_dalek::edwards::EdwardsPoint;
pub use monero_generators::{hash_to_point as raw_hash_to_point};
/// Monero's hash to point function, as named `ge_fromfe_frombytes_vartime`.
#[must_use]
pub fn hash_to_point(key: EdwardsPoint) -> EdwardsPoint {
raw_hash_to_point(key.compress().to_bytes())
}

View file

@ -19,20 +19,21 @@ pub struct Mlsag {
impl Mlsag {
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
for ss in self.ss.iter() {
for ss in &self.ss {
write_raw_vec(write_scalar, ss, w)?;
}
write_scalar(&self.cc, w)
}
pub fn read<R: Read>(mixins: usize, r: &mut R) -> io::Result<Mlsag> {
Ok(Mlsag {
pub fn read<R: Read>(mixins: usize, r: &mut R) -> io::Result<Self> {
Ok(Self {
ss: (0 .. mixins).map(|_| read_array(read_scalar, r)).collect::<Result<_, _>>()?,
cc: read_scalar(r)?,
})
}
#[cfg(feature = "experimental")]
#[must_use]
pub fn verify(
&self,
msg: &[u8; 32],

View file

@ -27,6 +27,7 @@ use crate::{
};
/// Generate a key image for a given key. Defined as `x * hash_to_point(xG)`.
#[must_use]
pub fn generate_key_image(secret: &Zeroizing<Scalar>) -> EdwardsPoint {
hash_to_point(&ED25519_BASEPOINT_TABLE * secret.deref()) * secret.deref()
}
@ -38,21 +39,21 @@ pub enum EncryptedAmount {
}
impl EncryptedAmount {
pub fn read<R: Read>(compact: bool, r: &mut R) -> io::Result<EncryptedAmount> {
Ok(if !compact {
EncryptedAmount::Original { mask: read_bytes(r)?, amount: read_bytes(r)? }
pub fn read<R: Read>(compact: bool, r: &mut R) -> io::Result<Self> {
Ok(if compact {
Self::Compact { amount: read_bytes(r)? }
} else {
EncryptedAmount::Compact { amount: read_bytes(r)? }
Self::Original { mask: read_bytes(r)?, amount: read_bytes(r)? }
})
}
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
match self {
EncryptedAmount::Original { mask, amount } => {
Self::Original { mask, amount } => {
w.write_all(mask)?;
w.write_all(amount)
}
EncryptedAmount::Compact { amount } => w.write_all(amount),
Self::Compact { amount } => w.write_all(amount),
}
}
}
@ -77,40 +78,38 @@ pub enum RctType {
}
impl RctType {
pub fn to_byte(self) -> u8 {
#[must_use]
pub const fn to_byte(self) -> u8 {
match self {
RctType::Null => 0,
RctType::MlsagAggregate => 1,
RctType::MlsagIndividual => 2,
RctType::Bulletproofs => 3,
RctType::BulletproofsCompactAmount => 4,
RctType::Clsag => 5,
RctType::BulletproofsPlus => 6,
Self::Null => 0,
Self::MlsagAggregate => 1,
Self::MlsagIndividual => 2,
Self::Bulletproofs => 3,
Self::BulletproofsCompactAmount => 4,
Self::Clsag => 5,
Self::BulletproofsPlus => 6,
}
}
#[must_use]
pub fn from_byte(byte: u8) -> Option<Self> {
Some(match byte {
0 => RctType::Null,
1 => RctType::MlsagAggregate,
2 => RctType::MlsagIndividual,
3 => RctType::Bulletproofs,
4 => RctType::BulletproofsCompactAmount,
5 => RctType::Clsag,
6 => RctType::BulletproofsPlus,
0 => Self::Null,
1 => Self::MlsagAggregate,
2 => Self::MlsagIndividual,
3 => Self::Bulletproofs,
4 => Self::BulletproofsCompactAmount,
5 => Self::Clsag,
6 => Self::BulletproofsPlus,
_ => None?,
})
}
pub fn compact_encrypted_amounts(&self) -> bool {
#[must_use]
pub const fn compact_encrypted_amounts(&self) -> bool {
match self {
RctType::Null => false,
RctType::MlsagAggregate => false,
RctType::MlsagIndividual => false,
RctType::Bulletproofs => false,
RctType::BulletproofsCompactAmount => true,
RctType::Clsag => true,
RctType::BulletproofsPlus => true,
Self::Null | Self::MlsagAggregate | Self::MlsagIndividual | Self::Bulletproofs => false,
Self::BulletproofsCompactAmount | Self::Clsag | Self::BulletproofsPlus => true,
}
}
}
@ -124,7 +123,7 @@ pub struct RctBase {
}
impl RctBase {
pub(crate) fn fee_weight(outputs: usize) -> usize {
pub(crate) const fn fee_weight(outputs: usize) -> usize {
1 + 8 + (outputs * (8 + 32))
}
@ -132,7 +131,12 @@ impl RctBase {
w.write_all(&[rct_type.to_byte()])?;
match rct_type {
RctType::Null => Ok(()),
_ => {
RctType::MlsagAggregate |
RctType::MlsagIndividual |
RctType::Bulletproofs |
RctType::BulletproofsCompactAmount |
RctType::Clsag |
RctType::BulletproofsPlus => {
write_varint(&self.fee, w)?;
if rct_type == RctType::MlsagIndividual {
write_raw_vec(write_point, &self.pseudo_outs, w)?;
@ -145,14 +149,12 @@ impl RctBase {
}
}
pub fn read<R: Read>(inputs: usize, outputs: usize, r: &mut R) -> io::Result<(RctBase, RctType)> {
pub fn read<R: Read>(inputs: usize, outputs: usize, r: &mut R) -> io::Result<(Self, RctType)> {
let rct_type = RctType::from_byte(read_byte(r)?)
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "invalid RCT type"))?;
match rct_type {
RctType::Null => {}
RctType::MlsagAggregate => {}
RctType::MlsagIndividual => {}
RctType::Null | RctType::MlsagAggregate | RctType::MlsagIndividual => {}
RctType::Bulletproofs |
RctType::BulletproofsCompactAmount |
RctType::Clsag |
@ -170,9 +172,9 @@ impl RctBase {
Ok((
if rct_type == RctType::Null {
RctBase { fee: 0, pseudo_outs: vec![], encrypted_amounts: vec![], commitments: vec![] }
Self { fee: 0, pseudo_outs: vec![], encrypted_amounts: vec![], commitments: vec![] }
} else {
RctBase {
Self {
fee: read_varint(r)?,
pseudo_outs: if rct_type == RctType::MlsagIndividual {
read_raw_vec(read_point, inputs, r)?
@ -217,12 +219,12 @@ impl RctPrunable {
pub fn write<W: Write>(&self, w: &mut W, rct_type: RctType) -> io::Result<()> {
match self {
RctPrunable::Null => Ok(()),
RctPrunable::MlsagBorromean { borromean, mlsags } => {
Self::Null => Ok(()),
Self::MlsagBorromean { borromean, mlsags } => {
write_raw_vec(BorromeanRange::write, borromean, w)?;
write_raw_vec(Mlsag::write, mlsags, w)
}
RctPrunable::MlsagBulletproofs { bulletproofs, mlsags, pseudo_outs } => {
Self::MlsagBulletproofs { bulletproofs, mlsags, pseudo_outs } => {
if rct_type == RctType::Bulletproofs {
w.write_all(&1u32.to_le_bytes())?;
} else {
@ -233,7 +235,7 @@ impl RctPrunable {
write_raw_vec(Mlsag::write, mlsags, w)?;
write_raw_vec(write_point, pseudo_outs, w)
}
RctPrunable::Clsag { bulletproofs, clsags, pseudo_outs } => {
Self::Clsag { bulletproofs, clsags, pseudo_outs } => {
w.write_all(&[1])?;
bulletproofs.write(w)?;
@ -243,6 +245,7 @@ impl RctPrunable {
}
}
#[must_use]
pub fn serialize(&self, rct_type: RctType) -> Vec<u8> {
let mut serialized = vec![];
self.write(&mut serialized, rct_type).unwrap();
@ -254,31 +257,29 @@ impl RctPrunable {
decoys: &[usize],
outputs: usize,
r: &mut R,
) -> io::Result<RctPrunable> {
) -> io::Result<Self> {
Ok(match rct_type {
RctType::Null => RctPrunable::Null,
RctType::MlsagAggregate | RctType::MlsagIndividual => RctPrunable::MlsagBorromean {
RctType::Null => Self::Null,
RctType::MlsagAggregate | RctType::MlsagIndividual => Self::MlsagBorromean {
borromean: read_raw_vec(BorromeanRange::read, outputs, r)?,
mlsags: decoys.iter().map(|d| Mlsag::read(*d, r)).collect::<Result<_, _>>()?,
},
RctType::Bulletproofs | RctType::BulletproofsCompactAmount => {
RctPrunable::MlsagBulletproofs {
bulletproofs: {
if (if rct_type == RctType::Bulletproofs {
u64::from(read_u32(r)?)
} else {
read_varint(r)?
}) != 1
{
Err(io::Error::new(io::ErrorKind::Other, "n bulletproofs instead of one"))?;
}
Bulletproofs::read(r)?
},
mlsags: decoys.iter().map(|d| Mlsag::read(*d, r)).collect::<Result<_, _>>()?,
pseudo_outs: read_raw_vec(read_point, decoys.len(), r)?,
}
}
RctType::Clsag | RctType::BulletproofsPlus => RctPrunable::Clsag {
RctType::Bulletproofs | RctType::BulletproofsCompactAmount => Self::MlsagBulletproofs {
bulletproofs: {
if (if rct_type == RctType::Bulletproofs {
u64::from(read_u32(r)?)
} else {
read_varint(r)?
}) != 1
{
Err(io::Error::new(io::ErrorKind::Other, "n bulletproofs instead of one"))?;
}
Bulletproofs::read(r)?
},
mlsags: decoys.iter().map(|d| Mlsag::read(*d, r)).collect::<Result<_, _>>()?,
pseudo_outs: read_raw_vec(read_point, decoys.len(), r)?,
},
RctType::Clsag | RctType::BulletproofsPlus => Self::Clsag {
bulletproofs: {
if read_varint(r)? != 1 {
Err(io::Error::new(io::ErrorKind::Other, "n bulletproofs instead of one"))?;
@ -295,12 +296,10 @@ impl RctPrunable {
pub(crate) fn signature_write<W: Write>(&self, w: &mut W) -> io::Result<()> {
match self {
RctPrunable::Null => panic!("Serializing RctPrunable::Null for a signature"),
RctPrunable::MlsagBorromean { borromean, .. } => {
borromean.iter().try_for_each(|rs| rs.write(w))
}
RctPrunable::MlsagBulletproofs { bulletproofs, .. } => bulletproofs.signature_write(w),
RctPrunable::Clsag { bulletproofs, .. } => bulletproofs.signature_write(w),
Self::Null => panic!("Serializing RctPrunable::Null for a signature"),
Self::MlsagBorromean { borromean, .. } => borromean.iter().try_for_each(|rs| rs.write(w)),
Self::MlsagBulletproofs { bulletproofs, .. } => bulletproofs.signature_write(w),
Self::Clsag { bulletproofs, .. } => bulletproofs.signature_write(w),
}
}
}
@ -313,6 +312,7 @@ pub struct RctSignatures {
impl RctSignatures {
/// RctType for a given RctSignatures struct.
#[must_use]
pub fn rct_type(&self) -> RctType {
match &self.prunable {
RctPrunable::Null => RctType::Null,
@ -376,14 +376,15 @@ impl RctSignatures {
self.prunable.write(w, rct_type)
}
#[must_use]
pub fn serialize(&self) -> Vec<u8> {
let mut serialized = vec![];
self.write(&mut serialized).unwrap();
serialized
}
pub fn read<R: Read>(decoys: Vec<usize>, outputs: usize, r: &mut R) -> io::Result<RctSignatures> {
pub fn read<R: Read>(decoys: Vec<usize>, outputs: usize, r: &mut R) -> io::Result<Self> {
let base = RctBase::read(decoys.len(), outputs, r)?;
Ok(RctSignatures { base: base.0, prunable: RctPrunable::read(base.1, &decoys, outputs, r)? })
Ok(Self { base: base.0, prunable: RctPrunable::read(base.1, &decoys, outputs, r)? })
}
}

View file

@ -17,7 +17,7 @@ impl HttpRpc {
///
/// A daemon requiring authentication can be used via including the username and password in the
/// URL.
pub fn new(mut url: String) -> Result<Rpc<HttpRpc>, RpcError> {
pub fn new(mut url: String) -> Result<Rpc<Self>, RpcError> {
// Parse out the username and password
let userpass = if url.contains('@') {
let url_clone = url;
@ -47,7 +47,7 @@ impl HttpRpc {
None
};
Ok(Rpc(HttpRpc { client: Client::new(), userpass, url }))
Ok(Rpc(Self { client: Client::new(), userpass, url }))
}
}

View file

@ -28,7 +28,7 @@ mod http;
pub use http::*;
#[derive(Deserialize, Debug)]
pub struct EmptyResponse {}
pub struct EmptyResponse;
#[derive(Deserialize, Debug)]
pub struct JsonRpcResponse<T> {
result: T,
@ -47,26 +47,31 @@ struct TransactionsResponse {
txs: Vec<TransactionResponse>,
}
#[derive(Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "std", derive(thiserror::Error))]
pub enum RpcError {
#[cfg_attr(feature = "std", error("internal error ({0})"))]
InternalError(&'static str),
#[cfg_attr(feature = "std", error("connection error"))]
ConnectionError,
#[cfg_attr(feature = "std", error("invalid node"))]
InvalidNode,
#[cfg_attr(feature = "std", error("unsupported protocol version ({0})"))]
UnsupportedProtocol(usize),
#[cfg_attr(feature = "std", error("transactions not found"))]
TransactionsNotFound(Vec<[u8; 32]>),
#[cfg_attr(feature = "std", error("invalid point ({0})"))]
InvalidPoint(String),
#[cfg_attr(feature = "std", error("pruned transaction"))]
PrunedTransaction,
#[cfg_attr(feature = "std", error("invalid transaction ({0:?})"))]
InvalidTransaction([u8; 32]),
#[allow(clippy::std_instead_of_core)]
mod rpc_error {
use std_shims::{vec::Vec, string::String};
#[derive(Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "std", derive(thiserror::Error))]
pub enum RpcError {
#[cfg_attr(feature = "std", error("internal error ({0})"))]
InternalError(&'static str),
#[cfg_attr(feature = "std", error("connection error"))]
ConnectionError,
#[cfg_attr(feature = "std", error("invalid node"))]
InvalidNode,
#[cfg_attr(feature = "std", error("unsupported protocol version ({0})"))]
UnsupportedProtocol(usize),
#[cfg_attr(feature = "std", error("transactions not found"))]
TransactionsNotFound(Vec<[u8; 32]>),
#[cfg_attr(feature = "std", error("invalid point ({0})"))]
InvalidPoint(String),
#[cfg_attr(feature = "std", error("pruned transaction"))]
PrunedTransaction,
#[cfg_attr(feature = "std", error("invalid transaction ({0:?})"))]
InvalidTransaction([u8; 32]),
}
}
pub use rpc_error::RpcError;
fn rpc_hex(value: &str) -> Result<Vec<u8>, RpcError> {
hex::decode(value).map_err(|_| RpcError::InvalidNode)
@ -299,15 +304,15 @@ impl<R: RpcConnection> Rpc<R> {
match self.get_block(self.get_block_hash(number).await?).await {
Ok(block) => {
// Make sure this is actually the block for this number
match block.miner_tx.prefix.inputs[0] {
Input::Gen(actual) => {
if usize::try_from(actual).unwrap() == number {
match block.miner_tx.prefix.inputs.get(0) {
Some(Input::Gen(actual)) => {
if usize::try_from(*actual).unwrap() == number {
Ok(block)
} else {
Err(RpcError::InvalidNode)
}
}
_ => Err(RpcError::InvalidNode),
Some(Input::ToKey { .. }) | None => Err(RpcError::InvalidNode),
}
}
e => e,

View file

@ -125,7 +125,7 @@ pub(crate) fn read_point<R: Read>(r: &mut R) -> io::Result<EdwardsPoint> {
pub(crate) fn read_torsion_free_point<R: Read>(r: &mut R) -> io::Result<EdwardsPoint> {
read_point(r)
.ok()
.filter(|point| point.is_torsion_free())
.filter(EdwardsPoint::is_torsion_free)
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "invalid point"))
}

View file

@ -1,6 +1,8 @@
use core::ops::Deref;
#[cfg(feature = "multisig")]
use std::sync::{Arc, RwLock};
use std_shims::sync::Arc;
#[cfg(feature = "multisig")]
use std::sync::RwLock;
use zeroize::Zeroizing;
use rand_core::{RngCore, OsRng};
@ -45,13 +47,12 @@ fn clsag() {
for i in 0 .. RING_LEN {
let dest = Zeroizing::new(random_scalar(&mut OsRng));
let mask = random_scalar(&mut OsRng);
let amount;
if i == real {
let amount = if i == real {
secrets = (dest.clone(), mask);
amount = AMOUNT;
AMOUNT
} else {
amount = OsRng.next_u64();
}
OsRng.next_u64()
};
ring
.push([dest.deref() * &ED25519_BASEPOINT_TABLE, Commitment::new(mask, amount).calculate()]);
}
@ -90,16 +91,15 @@ fn clsag_multisig() {
for i in 0 .. RING_LEN {
let dest;
let mask;
let amount;
if i != u64::from(RING_INDEX) {
dest = &random_scalar(&mut OsRng) * &ED25519_BASEPOINT_TABLE;
mask = random_scalar(&mut OsRng);
amount = OsRng.next_u64();
} else {
let amount = if i == u64::from(RING_INDEX) {
dest = keys[&Participant::new(1).unwrap()].group_key().0;
mask = randomness;
amount = AMOUNT;
}
AMOUNT
} else {
dest = &random_scalar(&mut OsRng) * &ED25519_BASEPOINT_TABLE;
mask = random_scalar(&mut OsRng);
OsRng.next_u64()
};
ring.push([dest, Commitment::new(mask, amount).calculate()]);
}

View file

@ -25,7 +25,7 @@ pub enum Input {
impl Input {
// Worst-case predictive len
pub(crate) fn fee_weight(ring_len: usize) -> usize {
pub(crate) const fn fee_weight(ring_len: usize) -> usize {
// Uses 1 byte for the VarInt amount due to amount being 0
// Uses 1 byte for the VarInt encoding of the length of the ring as well
1 + 1 + 1 + (8 * ring_len) + 32
@ -33,12 +33,12 @@ impl Input {
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
match self {
Input::Gen(height) => {
Self::Gen(height) => {
w.write_all(&[255])?;
write_varint(height, w)
}
Input::ToKey { amount, key_offsets, key_image } => {
Self::ToKey { amount, key_offsets, key_image } => {
w.write_all(&[2])?;
write_varint(&amount.unwrap_or(0), w)?;
write_vec(write_varint, key_offsets, w)?;
@ -47,19 +47,20 @@ impl Input {
}
}
#[must_use]
pub fn serialize(&self) -> Vec<u8> {
let mut res = vec![];
self.write(&mut res).unwrap();
res
}
pub fn read<R: Read>(interpret_as_rct: bool, r: &mut R) -> io::Result<Input> {
pub fn read<R: Read>(interpret_as_rct: bool, r: &mut R) -> io::Result<Self> {
Ok(match read_byte(r)? {
255 => Input::Gen(read_varint(r)?),
255 => Self::Gen(read_varint(r)?),
2 => {
let amount = read_varint(r)?;
let amount = if (amount == 0) && interpret_as_rct { None } else { Some(amount) };
Input::ToKey {
Self::ToKey {
amount,
key_offsets: read_vec(read_varint, r)?,
key_image: read_torsion_free_point(r)?,
@ -81,7 +82,7 @@ pub struct Output {
}
impl Output {
pub(crate) fn fee_weight() -> usize {
pub(crate) const fn fee_weight() -> usize {
1 + 1 + 32 + 1
}
@ -95,13 +96,14 @@ impl Output {
Ok(())
}
#[must_use]
pub fn serialize(&self) -> Vec<u8> {
let mut res = Vec::with_capacity(8 + 1 + 32);
self.write(&mut res).unwrap();
res
}
pub fn read<R: Read>(interpret_as_rct: bool, r: &mut R) -> io::Result<Output> {
pub fn read<R: Read>(interpret_as_rct: bool, r: &mut R) -> io::Result<Self> {
let amount = read_varint(r)?;
let amount = if interpret_as_rct {
if amount != 0 {
@ -121,7 +123,7 @@ impl Output {
))?,
};
Ok(Output {
Ok(Self {
amount,
key: CompressedEdwardsY(read_bytes(r)?),
view_tag: if view_tag { Some(read_byte(r)?) } else { None },
@ -137,22 +139,22 @@ pub enum Timelock {
}
impl Timelock {
fn from_raw(raw: u64) -> Timelock {
fn from_raw(raw: u64) -> Self {
if raw == 0 {
Timelock::None
Self::None
} else if raw < 500_000_000 {
Timelock::Block(usize::try_from(raw).unwrap())
Self::Block(usize::try_from(raw).unwrap())
} else {
Timelock::Time(raw)
Self::Time(raw)
}
}
fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
write_varint(
&match self {
Timelock::None => 0,
Timelock::Block(block) => (*block).try_into().unwrap(),
Timelock::Time(time) => *time,
Self::None => 0,
Self::Block(block) => (*block).try_into().unwrap(),
Self::Time(time) => *time,
},
w,
)
@ -162,9 +164,9 @@ impl Timelock {
impl PartialOrd for Timelock {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (self, other) {
(Timelock::None, _) => Some(Ordering::Less),
(Timelock::Block(a), Timelock::Block(b)) => a.partial_cmp(b),
(Timelock::Time(a), Timelock::Time(b)) => a.partial_cmp(b),
(Self::None, _) => Some(Ordering::Less),
(Self::Block(a), Self::Block(b)) => a.partial_cmp(b),
(Self::Time(a), Self::Time(b)) => a.partial_cmp(b),
_ => None,
}
}
@ -200,13 +202,14 @@ impl TransactionPrefix {
w.write_all(&self.extra)
}
#[must_use]
pub fn serialize(&self) -> Vec<u8> {
let mut res = vec![];
self.write(&mut res).unwrap();
res
}
pub fn read<R: Read>(r: &mut R) -> io::Result<TransactionPrefix> {
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
let version = read_varint(r)?;
// TODO: Create an enum out of version
if (version == 0) || (version > 2) {
@ -221,7 +224,7 @@ impl TransactionPrefix {
}
let is_miner_tx = matches!(inputs[0], Input::Gen { .. });
let mut prefix = TransactionPrefix {
let mut prefix = Self {
version,
timelock,
inputs,
@ -232,6 +235,7 @@ impl TransactionPrefix {
Ok(prefix)
}
#[must_use]
pub fn hash(&self) -> [u8; 32] {
hash(&self.serialize())
}
@ -273,13 +277,14 @@ impl Transaction {
}
}
#[must_use]
pub fn serialize(&self) -> Vec<u8> {
let mut res = Vec::with_capacity(2048);
self.write(&mut res).unwrap();
res
}
pub fn read<R: Read>(r: &mut R) -> io::Result<Transaction> {
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
let prefix = TransactionPrefix::read(r)?;
let mut signatures = vec![];
let mut rct_signatures = RctSignatures {
@ -328,9 +333,10 @@ impl Transaction {
Err(io::Error::new(io::ErrorKind::Other, "Tried to deserialize unknown version"))?;
}
Ok(Transaction { prefix, signatures, rct_signatures })
Ok(Self { prefix, signatures, rct_signatures })
}
#[must_use]
pub fn hash(&self) -> [u8; 32] {
let mut buf = Vec::with_capacity(2048);
if self.prefix.version == 1 {
@ -359,6 +365,7 @@ impl Transaction {
}
/// Calculate the hash of this transaction as needed for signing it.
#[must_use]
pub fn signature_hash(&self) -> [u8; 32] {
let mut buf = Vec::with_capacity(2048);
let mut sig_hash = Vec::with_capacity(96);

View file

@ -32,18 +32,21 @@ pub struct SubaddressIndex {
}
impl SubaddressIndex {
pub const fn new(account: u32, address: u32) -> Option<SubaddressIndex> {
#[must_use]
pub const fn new(account: u32, address: u32) -> Option<Self> {
if (account == 0) && (address == 0) {
return None;
}
Some(SubaddressIndex { account, address })
Some(Self { account, address })
}
pub fn account(&self) -> u32 {
#[must_use]
pub const fn account(&self) -> u32 {
self.account
}
pub fn address(&self) -> u32 {
#[must_use]
pub const fn address(&self) -> u32 {
self.address
}
}
@ -58,23 +61,25 @@ pub enum AddressSpec {
}
impl AddressType {
pub fn is_subaddress(&self) -> bool {
matches!(self, AddressType::Subaddress) ||
matches!(self, AddressType::Featured { subaddress: true, .. })
#[must_use]
pub const fn is_subaddress(&self) -> bool {
matches!(self, Self::Subaddress) || matches!(self, Self::Featured { subaddress: true, .. })
}
pub fn payment_id(&self) -> Option<[u8; 8]> {
if let AddressType::Integrated(id) = self {
#[must_use]
pub const fn payment_id(&self) -> Option<[u8; 8]> {
if let Self::Integrated(id) = self {
Some(*id)
} else if let AddressType::Featured { payment_id, .. } = self {
} else if let Self::Featured { payment_id, .. } = self {
*payment_id
} else {
None
}
}
pub fn is_guaranteed(&self) -> bool {
matches!(self, AddressType::Featured { guaranteed: true, .. })
#[must_use]
pub const fn is_guaranteed(&self) -> bool {
matches!(self, Self::Featured { guaranteed: true, .. })
}
}
@ -142,8 +147,9 @@ impl<B: AddressBytes> AddressMeta<B> {
}
/// Create an address's metadata.
pub fn new(network: Network, kind: AddressType) -> Self {
AddressMeta { _bytes: PhantomData, network, kind }
#[must_use]
pub const fn new(network: Network, kind: AddressType) -> Self {
Self { _bytes: PhantomData, network, kind }
}
// Returns an incomplete instantiation in the case of Integrated/Featured addresses
@ -160,7 +166,7 @@ impl<B: AddressBytes> AddressMeta<B> {
}
_ => None,
} {
meta = Some(AddressMeta::new(network, kind));
meta = Some(Self::new(network, kind));
break;
}
}
@ -168,15 +174,18 @@ impl<B: AddressBytes> AddressMeta<B> {
meta.ok_or(AddressError::InvalidByte)
}
pub fn is_subaddress(&self) -> bool {
#[must_use]
pub const fn is_subaddress(&self) -> bool {
self.kind.is_subaddress()
}
pub fn payment_id(&self) -> Option<[u8; 8]> {
#[must_use]
pub const fn payment_id(&self) -> Option<[u8; 8]> {
self.kind.payment_id()
}
pub fn is_guaranteed(&self) -> bool {
#[must_use]
pub const fn is_guaranteed(&self) -> bool {
self.kind.is_guaranteed()
}
}
@ -216,8 +225,9 @@ impl<B: AddressBytes> ToString for Address<B> {
}
impl<B: AddressBytes> Address<B> {
pub fn new(meta: AddressMeta<B>, spend: EdwardsPoint, view: EdwardsPoint) -> Self {
Address { meta, spend, view }
#[must_use]
pub const fn new(meta: AddressMeta<B>, spend: EdwardsPoint, view: EdwardsPoint) -> Self {
Self { meta, spend, view }
}
pub fn from_str_raw(s: &str) -> Result<Self, AddressError> {
@ -267,7 +277,7 @@ impl<B: AddressBytes> Address<B> {
id.copy_from_slice(&raw[(read - 8) .. read]);
}
Ok(Address { meta, spend, view })
Ok(Self { meta, spend, view })
}
pub fn from_str(network: Network, s: &str) -> Result<Self, AddressError> {
@ -280,30 +290,35 @@ impl<B: AddressBytes> Address<B> {
})
}
pub fn network(&self) -> Network {
#[must_use]
pub const fn network(&self) -> Network {
self.meta.network
}
pub fn is_subaddress(&self) -> bool {
#[must_use]
pub const fn is_subaddress(&self) -> bool {
self.meta.is_subaddress()
}
pub fn payment_id(&self) -> Option<[u8; 8]> {
#[must_use]
pub const fn payment_id(&self) -> Option<[u8; 8]> {
self.meta.payment_id()
}
pub fn is_guaranteed(&self) -> bool {
#[must_use]
pub const fn is_guaranteed(&self) -> bool {
self.meta.is_guaranteed()
}
}
/// Instantiation of the Address type with Monero's network bytes.
pub type MoneroAddress = Address<MoneroAddressBytes>;
// Allow re-interpreting of an arbitrary address as a monero address so it can be used with the
// Allow re-interpreting of an arbitrary address as a Monero address so it can be used with the
// rest of this library. Doesn't use From as it was conflicting with From<T> for T.
impl MoneroAddress {
pub fn from<B: AddressBytes>(address: Address<B>) -> MoneroAddress {
MoneroAddress::new(
#[must_use]
pub const fn from<B: AddressBytes>(address: Address<B>) -> Self {
Self::new(
AddressMeta::new(address.meta.network, address.meta.kind),
address.spend,
address.view,

View file

@ -31,7 +31,7 @@ const TIP_APPLICATION: f64 = (LOCK_WINDOW * BLOCK_TIME) as f64;
static DISTRIBUTION_CELL: OnceLock<Mutex<Vec<u64>>> = OnceLock::new();
#[allow(non_snake_case)]
fn DISTRIBUTION() -> &'static Mutex<Vec<u64>> {
DISTRIBUTION_CELL.get_or_init(|| Mutex::new(Vec::with_capacity(3000000)))
DISTRIBUTION_CELL.get_or_init(|| Mutex::new(Vec::with_capacity(3_000_000)))
}
#[allow(clippy::too_many_arguments)]
@ -141,6 +141,7 @@ pub struct Decoys {
}
impl Decoys {
#[must_use]
pub fn len(&self) -> usize {
self.offsets.len()
}
@ -152,7 +153,7 @@ impl Decoys {
ring_len: usize,
height: usize,
inputs: &[SpendableOutput],
) -> Result<Vec<Decoys>, RpcError> {
) -> Result<Vec<Self>, RpcError> {
#[cfg(not(feature = "std"))]
let mut distribution = DISTRIBUTION().lock();
#[cfg(feature = "std")]
@ -265,7 +266,7 @@ impl Decoys {
// members
}
res.push(Decoys {
res.push(Self {
// Binary searches for the real spend since we don't know where it sorted to
i: u8::try_from(ring.partition_point(|x| x.0 < o.0)).unwrap(),
offsets: offset(&ring.iter().map(|output| output.0).collect::<Vec<_>>()),

View file

@ -30,14 +30,14 @@ pub enum PaymentId {
}
impl BitXor<[u8; 8]> for PaymentId {
type Output = PaymentId;
type Output = Self;
fn bitxor(self, bytes: [u8; 8]) -> PaymentId {
fn bitxor(self, bytes: [u8; 8]) -> Self {
match self {
// Don't perform the xor since this isn't intended to be encrypted with xor
PaymentId::Unencrypted(_) => self,
PaymentId::Encrypted(id) => {
PaymentId::Encrypted((u64::from_le_bytes(id) ^ u64::from_le_bytes(bytes)).to_le_bytes())
Self::Unencrypted(_) => self,
Self::Encrypted(id) => {
Self::Encrypted((u64::from_le_bytes(id) ^ u64::from_le_bytes(bytes)).to_le_bytes())
}
}
}
@ -46,11 +46,11 @@ impl BitXor<[u8; 8]> for PaymentId {
impl PaymentId {
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
match self {
PaymentId::Unencrypted(id) => {
Self::Unencrypted(id) => {
w.write_all(&[PAYMENT_ID_MARKER])?;
w.write_all(id)?;
}
PaymentId::Encrypted(id) => {
Self::Encrypted(id) => {
w.write_all(&[ENCRYPTED_PAYMENT_ID_MARKER])?;
w.write_all(id)?;
}
@ -58,10 +58,10 @@ impl PaymentId {
Ok(())
}
pub fn read<R: Read>(r: &mut R) -> io::Result<PaymentId> {
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
Ok(match read_byte(r)? {
0 => PaymentId::Unencrypted(read_bytes(r)?),
1 => PaymentId::Encrypted(read_bytes(r)?),
0 => Self::Unencrypted(read_bytes(r)?),
1 => Self::Encrypted(read_bytes(r)?),
_ => Err(io::Error::new(io::ErrorKind::Other, "unknown payment ID type"))?,
})
}
@ -79,20 +79,20 @@ pub enum ExtraField {
impl ExtraField {
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
match self {
ExtraField::PublicKey(key) => {
Self::PublicKey(key) => {
w.write_all(&[1])?;
w.write_all(&key.compress().to_bytes())?;
}
ExtraField::Nonce(data) => {
Self::Nonce(data) => {
w.write_all(&[2])?;
write_vec(write_byte, data, w)?;
}
ExtraField::MergeMining(height, merkle) => {
Self::MergeMining(height, merkle) => {
w.write_all(&[3])?;
write_varint(&u64::try_from(*height).unwrap(), w)?;
w.write_all(merkle)?;
}
ExtraField::PublicKeys(keys) => {
Self::PublicKeys(keys) => {
w.write_all(&[4])?;
write_vec(write_point, keys, w)?;
}
@ -100,22 +100,22 @@ impl ExtraField {
Ok(())
}
pub fn read<R: Read>(r: &mut R) -> io::Result<ExtraField> {
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
Ok(match read_byte(r)? {
1 => ExtraField::PublicKey(read_point(r)?),
2 => ExtraField::Nonce({
1 => Self::PublicKey(read_point(r)?),
2 => Self::Nonce({
let nonce = read_vec(read_byte, r)?;
if nonce.len() > MAX_TX_EXTRA_NONCE_SIZE {
Err(io::Error::new(io::ErrorKind::Other, "too long nonce"))?;
}
nonce
}),
3 => ExtraField::MergeMining(
3 => Self::MergeMining(
usize::try_from(read_varint(r)?)
.map_err(|_| io::Error::new(io::ErrorKind::Other, "varint for height exceeds usize"))?,
read_bytes(r)?,
),
4 => ExtraField::PublicKeys(read_vec(read_point, r)?),
4 => Self::PublicKeys(read_vec(read_point, r)?),
_ => Err(io::Error::new(io::ErrorKind::Other, "unknown extra field"))?,
})
}
@ -124,6 +124,7 @@ impl ExtraField {
#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
pub struct Extra(Vec<ExtraField>);
impl Extra {
#[must_use]
pub fn keys(&self) -> Option<(EdwardsPoint, Option<Vec<EdwardsPoint>>)> {
let mut key = None;
let mut additional = None;
@ -131,7 +132,7 @@ impl Extra {
match field.clone() {
ExtraField::PublicKey(this_key) => key = key.or(Some(this_key)),
ExtraField::PublicKeys(these_additional) => {
additional = additional.or(Some(these_additional))
additional = additional.or(Some(these_additional));
}
_ => (),
}
@ -140,6 +141,7 @@ impl Extra {
key.map(|key| (key, additional))
}
#[must_use]
pub fn payment_id(&self) -> Option<PaymentId> {
for field in &self.0 {
if let ExtraField::Nonce(data) = field {
@ -149,6 +151,7 @@ impl Extra {
None
}
#[must_use]
pub fn data(&self) -> Vec<Vec<u8>> {
let mut res = vec![];
for field in &self.0 {
@ -161,8 +164,8 @@ impl Extra {
res
}
pub(crate) fn new(key: EdwardsPoint, additional: Vec<EdwardsPoint>) -> Extra {
let mut res = Extra(Vec::with_capacity(3));
pub(crate) fn new(key: EdwardsPoint, additional: Vec<EdwardsPoint>) -> Self {
let mut res = Self(Vec::with_capacity(3));
res.push(ExtraField::PublicKey(key));
if !additional.is_empty() {
res.push(ExtraField::PublicKeys(additional));
@ -198,14 +201,15 @@ impl Extra {
Ok(())
}
#[must_use]
pub fn serialize(&self) -> Vec<u8> {
let mut buf = vec![];
self.write(&mut buf).unwrap();
buf
}
pub fn read<R: Read>(r: &mut R) -> io::Result<Extra> {
let mut res = Extra(vec![]);
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
let mut res = Self(vec![]);
let mut field;
while {
field = ExtraField::read(r);

View file

@ -80,7 +80,7 @@ pub(crate) fn shared_key(
// uniqueness ||
let shared_key = if let Some(uniqueness) = uniqueness {
[uniqueness.as_ref(), &output_derivation].concat().to_vec()
[uniqueness.as_ref(), &output_derivation].concat()
} else {
output_derivation
};
@ -141,14 +141,17 @@ pub struct ViewPair {
}
impl ViewPair {
pub fn new(spend: EdwardsPoint, view: Zeroizing<Scalar>) -> ViewPair {
ViewPair { spend, view }
#[must_use]
pub const fn new(spend: EdwardsPoint, view: Zeroizing<Scalar>) -> Self {
Self { spend, view }
}
pub fn spend(&self) -> EdwardsPoint {
#[must_use]
pub const fn spend(&self) -> EdwardsPoint {
self.spend
}
#[must_use]
pub fn view(&self) -> EdwardsPoint {
self.view.deref() * &ED25519_BASEPOINT_TABLE
}
@ -173,6 +176,7 @@ impl ViewPair {
}
/// Returns an address with the provided specification.
#[must_use]
pub fn address(&self, network: Network, spec: AddressSpec) -> MoneroAddress {
let mut spend = self.spend;
let mut view: EdwardsPoint = self.view.deref() * &ED25519_BASEPOINT_TABLE;
@ -241,15 +245,20 @@ impl ZeroizeOnDrop for Scanner {}
impl Scanner {
/// Create a Scanner from a ViewPair.
///
/// burning_bug is a HashSet of used keys, intended to prevent key reuse which would burn funds.
///
/// When an output is successfully scanned, the output key MUST be saved to disk.
///
/// When a new scanner is created, ALL saved output keys must be passed in to be secure.
///
/// If None is passed, a modified shared key derivation is used which is immune to the burning
/// bug (specifically the Guaranteed feature from Featured Addresses).
pub fn from_view(pair: ViewPair, burning_bug: Option<HashSet<CompressedEdwardsY>>) -> Scanner {
#[must_use]
pub fn from_view(pair: ViewPair, burning_bug: Option<HashSet<CompressedEdwardsY>>) -> Self {
let mut subaddresses = HashMap::new();
subaddresses.insert(pair.spend.compress(), None);
Scanner { pair, subaddresses, burning_bug }
Self { pair, subaddresses, burning_bug }
}
/// Register a subaddress.

View file

@ -32,14 +32,15 @@ impl AbsoluteId {
w.write_all(&[self.o])
}
#[must_use]
pub fn serialize(&self) -> Vec<u8> {
let mut serialized = Vec::with_capacity(32 + 1);
self.write(&mut serialized).unwrap();
serialized
}
pub fn read<R: Read>(r: &mut R) -> io::Result<AbsoluteId> {
Ok(AbsoluteId { tx: read_bytes(r)?, o: read_byte(r)? })
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
Ok(Self { tx: read_bytes(r)?, o: read_byte(r)? })
}
}
@ -60,14 +61,15 @@ impl OutputData {
w.write_all(&self.commitment.amount.to_le_bytes())
}
#[must_use]
pub fn serialize(&self) -> Vec<u8> {
let mut serialized = Vec::with_capacity(32 + 32 + 32 + 8);
self.write(&mut serialized).unwrap();
serialized
}
pub fn read<R: Read>(r: &mut R) -> io::Result<OutputData> {
Ok(OutputData {
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
Ok(Self {
key: read_point(r)?,
key_offset: read_scalar(r)?,
commitment: Commitment::new(read_scalar(r)?, read_u64(r)?),
@ -108,13 +110,14 @@ impl Metadata {
Ok(())
}
#[must_use]
pub fn serialize(&self) -> Vec<u8> {
let mut serialized = Vec::with_capacity(1 + 8 + 1);
self.write(&mut serialized).unwrap();
serialized
}
pub fn read<R: Read>(r: &mut R) -> io::Result<Metadata> {
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
let subaddress = if read_byte(r)? == 1 {
Some(
SubaddressIndex::new(read_u32(r)?, read_u32(r)?)
@ -124,7 +127,7 @@ impl Metadata {
None
};
Ok(Metadata {
Ok(Self {
subaddress,
payment_id: read_bytes(r)?,
arbitrary_data: {
@ -148,18 +151,22 @@ pub struct ReceivedOutput {
}
impl ReceivedOutput {
pub fn key(&self) -> EdwardsPoint {
#[must_use]
pub const fn key(&self) -> EdwardsPoint {
self.data.key
}
pub fn key_offset(&self) -> Scalar {
#[must_use]
pub const fn key_offset(&self) -> Scalar {
self.data.key_offset
}
#[must_use]
pub fn commitment(&self) -> Commitment {
self.data.commitment.clone()
}
#[must_use]
pub fn arbitrary_data(&self) -> &[Vec<u8>] {
&self.metadata.arbitrary_data
}
@ -170,14 +177,15 @@ impl ReceivedOutput {
self.metadata.write(w)
}
#[must_use]
pub fn serialize(&self) -> Vec<u8> {
let mut serialized = vec![];
self.write(&mut serialized).unwrap();
serialized
}
pub fn read<R: Read>(r: &mut R) -> io::Result<ReceivedOutput> {
Ok(ReceivedOutput {
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
Ok(Self {
absolute: AbsoluteId::read(r)?,
data: OutputData::read(r)?,
metadata: Metadata::read(r)?,
@ -209,24 +217,28 @@ impl SpendableOutput {
pub async fn from<RPC: RpcConnection>(
rpc: &Rpc<RPC>,
output: ReceivedOutput,
) -> Result<SpendableOutput, RpcError> {
let mut output = SpendableOutput { output, global_index: 0 };
) -> Result<Self, RpcError> {
let mut output = Self { output, global_index: 0 };
output.refresh_global_index(rpc).await?;
Ok(output)
}
pub fn key(&self) -> EdwardsPoint {
#[must_use]
pub const fn key(&self) -> EdwardsPoint {
self.output.key()
}
pub fn key_offset(&self) -> Scalar {
#[must_use]
pub const fn key_offset(&self) -> Scalar {
self.output.key_offset()
}
#[must_use]
pub fn commitment(&self) -> Commitment {
self.output.commitment()
}
#[must_use]
pub fn arbitrary_data(&self) -> &[Vec<u8>] {
self.output.arbitrary_data()
}
@ -242,8 +254,8 @@ impl SpendableOutput {
serialized
}
pub fn read<R: Read>(r: &mut R) -> io::Result<SpendableOutput> {
Ok(SpendableOutput { output: ReceivedOutput::read(r)?, global_index: read_u64(r)? })
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
Ok(Self { output: ReceivedOutput::read(r)?, global_index: read_u64(r)? })
}
}
@ -258,11 +270,13 @@ impl<O: Clone + Zeroize> Drop for Timelocked<O> {
impl<O: Clone + Zeroize> ZeroizeOnDrop for Timelocked<O> {}
impl<O: Clone + Zeroize> Timelocked<O> {
pub fn timelock(&self) -> Timelock {
#[must_use]
pub const fn timelock(&self) -> Timelock {
self.0
}
/// Return the outputs if they're not timelocked, or an empty vector if they are.
#[must_use]
pub fn not_locked(&self) -> Vec<O> {
if self.0 == Timelock::None {
return self.1.clone();
@ -271,6 +285,7 @@ impl<O: Clone + Zeroize> Timelocked<O> {
}
/// Returns None if the Timelocks aren't comparable. Returns Some(vec![]) if none are unlocked.
#[must_use]
pub fn unlocked(&self, timelock: Timelock) -> Option<Vec<O>> {
// If the Timelocks are comparable, return the outputs if they're now unlocked
if self.0 <= timelock {
@ -280,6 +295,7 @@ impl<O: Clone + Zeroize> Timelocked<O> {
}
}
#[must_use]
pub fn ignore_timelock(&self) -> Vec<O> {
self.1.clone()
}
@ -293,16 +309,11 @@ impl Scanner {
return Timelocked(tx.prefix.timelock, vec![]);
}
let extra = Extra::read::<&[u8]>(&mut tx.prefix.extra.as_ref());
let extra = if let Ok(extra) = extra {
extra
} else {
let Ok(extra) = Extra::read::<&[u8]>(&mut tx.prefix.extra.as_ref()) else {
return Timelocked(tx.prefix.timelock, vec![]);
};
let (tx_key, additional) = if let Some((tx_key, additional)) = extra.keys() {
(tx_key, additional)
} else {
let Some((tx_key, additional)) = extra.keys() else {
return Timelocked(tx.prefix.timelock, vec![]);
};
@ -324,17 +335,17 @@ impl Scanner {
let output_key = output_key.unwrap();
for key in [Some(Some(&tx_key)), additional.as_ref().map(|additional| additional.get(o))] {
let key = if let Some(Some(key)) = key {
key
} else if let Some(None) = key {
// This is non-standard. There were additional keys, yet not one for this output
// https://github.com/monero-project/monero/
// blob/04a1e2875d6e35e27bb21497988a6c822d319c28/
// src/cryptonote_basic/cryptonote_format_utils.cpp#L1062
// TODO: Should this return? Where does Monero set the trap handler for this exception?
continue;
} else {
break;
let Some(Some(key)) = key else {
if let Some(None) = key {
// This is non-standard. There were additional keys, yet not one for this output
// https://github.com/monero-project/monero/
// blob/04a1e2875d6e35e27bb21497988a6c822d319c28/
// src/cryptonote_basic/cryptonote_format_utils.cpp#L1062
// TODO: Should this return? Where does Monero set the trap handler for this exception?
continue;
} else {
break;
}
};
let (view_tag, shared_key, payment_id_xor) = shared_key(
if self.burning_bug.is_none() { Some(uniqueness(&tx.prefix.inputs)) } else { None },

View file

@ -33,8 +33,8 @@ struct WordList {
}
impl WordList {
fn new(word_list: Vec<&'static str>, prefix_length: usize) -> WordList {
let mut lang = WordList {
fn new(word_list: Vec<&'static str>, prefix_length: usize) -> Self {
let mut lang = Self {
word_list,
word_map: HashMap::new(),
trimmed_word_map: HashMap::new(),
@ -74,10 +74,10 @@ fn LANGUAGES() -> &'static HashMap<Language, WordList> {
#[cfg(test)]
pub(crate) fn trim_by_lang(word: &str, lang: Language) -> String {
if lang != Language::EnglishOld {
word.chars().take(LANGUAGES()[&lang].unique_prefix_length).collect()
} else {
if lang == Language::EnglishOld {
word.to_string()
} else {
word.chars().take(LANGUAGES()[&lang].unique_prefix_length).collect()
}
}
@ -239,11 +239,11 @@ pub(crate) fn seed_to_bytes(words: &str) -> Result<(Language, Zeroizing<[u8; 32]
#[derive(Clone, PartialEq, Eq, Zeroize)]
pub struct ClassicSeed(Zeroizing<String>);
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) -> Self {
key_to_seed(lang, Zeroizing::new(random_scalar(rng)))
}
pub fn from_string(words: Zeroizing<String>) -> Result<ClassicSeed, SeedError> {
pub fn from_string(words: Zeroizing<String>) -> Result<Self, SeedError> {
let (lang, entropy) = seed_to_bytes(&words)?;
// Make sure this is a valid scalar
@ -257,7 +257,7 @@ impl ClassicSeed {
Ok(Self::from_entropy(lang, entropy).unwrap())
}
pub fn from_entropy(lang: Language, entropy: Zeroizing<[u8; 32]>) -> Option<ClassicSeed> {
pub fn from_entropy(lang: Language, entropy: Zeroizing<[u8; 32]>) -> Option<Self> {
Scalar::from_canonical_bytes(*entropy).map(|scalar| key_to_seed(lang, Zeroizing::new(scalar)))
}

View file

@ -7,21 +7,25 @@ use rand_core::{RngCore, CryptoRng};
pub(crate) mod classic;
use classic::{CLASSIC_SEED_LENGTH, CLASSIC_SEED_LENGTH_WITH_CHECKSUM, ClassicSeed};
/// Error when decoding a seed.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "std", derive(thiserror::Error))]
pub enum SeedError {
#[cfg_attr(feature = "std", error("invalid number of words in seed"))]
InvalidSeedLength,
#[cfg_attr(feature = "std", error("unknown language"))]
UnknownLanguage,
#[cfg_attr(feature = "std", error("invalid checksum"))]
InvalidChecksum,
#[cfg_attr(feature = "std", error("english old seeds don't support checksums"))]
EnglishOldWithChecksum,
#[cfg_attr(feature = "std", error("invalid seed"))]
InvalidSeed,
#[allow(clippy::std_instead_of_core)]
mod seed_error {
/// Error when decoding a seed.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "std", derive(thiserror::Error))]
pub enum SeedError {
#[cfg_attr(feature = "std", error("invalid number of words in seed"))]
InvalidSeedLength,
#[cfg_attr(feature = "std", error("unknown language"))]
UnknownLanguage,
#[cfg_attr(feature = "std", error("invalid checksum"))]
InvalidChecksum,
#[cfg_attr(feature = "std", error("english old seeds don't support checksums"))]
EnglishOldWithChecksum,
#[cfg_attr(feature = "std", error("invalid seed"))]
InvalidSeed,
}
}
pub use seed_error::SeedError;
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum Language {
@ -50,43 +54,46 @@ pub enum Seed {
impl fmt::Debug for Seed {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Seed::Classic(_) => f.debug_struct("Seed::Classic").finish_non_exhaustive(),
Self::Classic(_) => f.debug_struct("Seed::Classic").finish_non_exhaustive(),
}
}
}
impl Seed {
/// Create a new seed.
pub fn new<R: RngCore + CryptoRng>(rng: &mut R, lang: Language) -> Seed {
Seed::Classic(ClassicSeed::new(rng, lang))
#[must_use]
pub fn new<R: RngCore + CryptoRng>(rng: &mut R, lang: Language) -> Self {
Self::Classic(ClassicSeed::new(rng, lang))
}
/// Parse a seed from a String.
pub fn from_string(words: Zeroizing<String>) -> Result<Seed, SeedError> {
pub fn from_string(words: Zeroizing<String>) -> Result<Self, SeedError> {
match words.split_whitespace().count() {
CLASSIC_SEED_LENGTH | CLASSIC_SEED_LENGTH_WITH_CHECKSUM => {
ClassicSeed::from_string(words).map(Seed::Classic)
ClassicSeed::from_string(words).map(Self::Classic)
}
_ => Err(SeedError::InvalidSeedLength)?,
}
}
/// Create a Seed from entropy.
pub fn from_entropy(lang: Language, entropy: Zeroizing<[u8; 32]>) -> Option<Seed> {
ClassicSeed::from_entropy(lang, entropy).map(Seed::Classic)
#[must_use]
pub fn from_entropy(lang: Language, entropy: Zeroizing<[u8; 32]>) -> Option<Self> {
ClassicSeed::from_entropy(lang, entropy).map(Self::Classic)
}
/// Convert a seed to a String.
pub fn to_string(&self) -> Zeroizing<String> {
match self {
Seed::Classic(seed) => seed.to_string(),
Self::Classic(seed) => seed.to_string(),
}
}
/// Return the entropy for this seed.
#[must_use]
pub fn entropy(&self) -> Zeroizing<[u8; 32]> {
match self {
Seed::Classic(seed) => seed.entropy(),
Self::Classic(seed) => seed.entropy(),
}
}
}

View file

@ -25,7 +25,7 @@ struct SignableTransactionBuilderInternal {
impl SignableTransactionBuilderInternal {
// Takes in the change address so users don't miss that they have to manually set one
// If they don't, all leftover funds will become part of the fee
fn new(protocol: Protocol, fee: Fee, change_address: Option<Change>) -> Self {
const fn new(protocol: Protocol, fee: Fee, change_address: Option<Change>) -> Self {
Self {
protocol,
fee,
@ -81,15 +81,17 @@ impl Eq for SignableTransactionBuilder {}
impl Zeroize for SignableTransactionBuilder {
fn zeroize(&mut self) {
self.0.write().unwrap().zeroize()
self.0.write().unwrap().zeroize();
}
}
#[allow(clippy::return_self_not_must_use)]
impl SignableTransactionBuilder {
fn shallow_copy(&self) -> Self {
Self(self.0.clone())
}
#[must_use]
pub fn new(protocol: Protocol, fee: Fee, change_address: Option<Change>) -> Self {
Self(Arc::new(RwLock::new(SignableTransactionBuilderInternal::new(
protocol,

View file

@ -72,7 +72,7 @@ impl SendOutput {
output: (usize, (MoneroAddress, u64)),
ecdh: EdwardsPoint,
R: EdwardsPoint,
) -> (SendOutput, Option<[u8; 8]>) {
) -> (Self, Option<[u8; 8]>) {
let o = output.0;
let output = output.1;
@ -80,7 +80,7 @@ impl SendOutput {
shared_key(Some(unique).filter(|_| output.0.is_guaranteed()), ecdh, o);
(
SendOutput {
Self {
R,
view_tag,
dest: ((&shared_key * &ED25519_BASEPOINT_TABLE) + output.0.spend),
@ -98,16 +98,16 @@ impl SendOutput {
r: &Zeroizing<Scalar>,
unique: [u8; 32],
output: (usize, (MoneroAddress, u64)),
) -> (SendOutput, Option<[u8; 8]>) {
) -> (Self, Option<[u8; 8]>) {
let address = output.1 .0;
SendOutput::internal(
Self::internal(
unique,
output,
r.deref() * address.view,
if !address.is_subaddress() {
r.deref() * &ED25519_BASEPOINT_TABLE
} else {
if address.is_subaddress() {
r.deref() * address.spend
} else {
r.deref() * &ED25519_BASEPOINT_TABLE
},
)
}
@ -116,8 +116,8 @@ impl SendOutput {
ecdh: EdwardsPoint,
unique: [u8; 32],
output: (usize, (MoneroAddress, u64)),
) -> (SendOutput, Option<[u8; 8]>) {
SendOutput::internal(unique, output, ecdh, ED25519_BASEPOINT_POINT)
) -> (Self, Option<[u8; 8]>) {
Self::internal(unique, output, ecdh, ED25519_BASEPOINT_POINT)
}
}
@ -211,6 +211,7 @@ pub struct Fee {
}
impl Fee {
#[must_use]
pub fn calculate(&self, weight: usize) -> u64 {
((((self.per_weight * u64::try_from(weight).unwrap()) - 1) / self.mask) + 1) * self.mask
}
@ -261,8 +262,9 @@ impl fmt::Debug for Change {
impl Change {
/// Create a change output specification from a ViewPair, as needed to maintain privacy.
pub fn new(view: &ViewPair, guaranteed: bool) -> Change {
Change {
#[must_use]
pub fn new(view: &ViewPair, guaranteed: bool) -> Self {
Self {
address: view.address(
Network::Mainnet,
if !guaranteed {
@ -275,10 +277,12 @@ impl Change {
}
}
/// Create a fingerprintable change output specification which will harm privacy. Only use this
/// if you know what you're doing.
pub fn fingerprintable(address: MoneroAddress) -> Change {
Change { address, view: None }
/// Create a fingerprintable change output specification which will harm privacy.
///
/// Only use this if you know what you're doing.
#[must_use]
pub const fn fingerprintable(address: MoneroAddress) -> Self {
Self { address, view: None }
}
}
@ -296,17 +300,17 @@ impl SignableTransaction {
protocol: Protocol,
r_seed: Option<Zeroizing<[u8; 32]>>,
inputs: Vec<SpendableOutput>,
mut payments: Vec<(MoneroAddress, u64)>,
payments: Vec<(MoneroAddress, u64)>,
change_address: Option<Change>,
data: Vec<Vec<u8>>,
fee_rate: Fee,
) -> Result<SignableTransaction, TransactionError> {
) -> Result<Self, TransactionError> {
// Make sure there's only one payment ID
let mut has_payment_id = {
let mut payment_ids = 0;
let mut count = |addr: MoneroAddress| {
if addr.payment_id().is_some() {
payment_ids += 1
payment_ids += 1;
}
};
for payment in &payments {
@ -382,15 +386,16 @@ impl SignableTransaction {
Err(TransactionError::TooManyOutputs)?;
}
let mut payments = payments.drain(..).map(InternalPayment::Payment).collect::<Vec<_>>();
let mut payments = payments.into_iter().map(InternalPayment::Payment).collect::<Vec<_>>();
if let Some(change) = change_address {
payments.push(InternalPayment::Change(change, in_amount - out_amount));
}
Ok(SignableTransaction { protocol, r_seed, inputs, payments, data, fee })
Ok(Self { protocol, r_seed, inputs, payments, data, fee })
}
pub fn fee(&self) -> u64 {
#[must_use]
pub const fn fee(&self) -> u64 {
self.fee
}
@ -444,11 +449,8 @@ impl SignableTransaction {
0;
// We need additional keys if we have any subaddresses
let mut additional = subaddresses;
// Unless the above change view key path is taken
if (payments.len() == 2) && has_change_view {
additional = false;
}
// UNLESS there's only two payments and we have the view-key for the change output
let additional = if (payments.len() == 2) && has_change_view { false } else { subaddresses };
let modified_change_ecdh = subaddresses && (!additional);
// If we're using the aR rewrite, update tx_public_key from rG to rB
@ -562,11 +564,13 @@ impl SignableTransaction {
}
/// Returns the eventuality of this transaction.
///
/// The eventuality is defined as the TX extra/outputs this transaction will create, if signed
/// with the specified seed. This eventuality can be compared to on-chain transactions to see
/// if the transaction has already been signed and published.
#[must_use]
pub fn eventuality(&self) -> Option<Eventuality> {
let inputs = self.inputs.iter().map(|input| input.key()).collect::<Vec<_>>();
let inputs = self.inputs.iter().map(SpendableOutput::key).collect::<Vec<_>>();
let (tx_key, additional, outputs, id) = Self::prepare_payments(
self.r_seed.as_ref()?,
&inputs,
@ -606,7 +610,7 @@ impl SignableTransaction {
let (tx_key, additional, outputs, id) = Self::prepare_payments(
&r_seed,
&self.inputs.iter().map(|input| input.key()).collect::<Vec<_>>(),
&self.inputs.iter().map(SpendableOutput::key).collect::<Vec<_>>(),
&mut self.payments,
uniqueness,
);
@ -656,7 +660,7 @@ impl SignableTransaction {
fee,
encrypted_amounts,
pseudo_outs: vec![],
commitments: commitments.iter().map(|commitment| commitment.calculate()).collect(),
commitments: commitments.iter().map(Commitment::calculate).collect(),
},
prunable: RctPrunable::Clsag { bulletproofs: bp, clsags: vec![], pseudo_outs: vec![] },
},
@ -704,7 +708,9 @@ impl SignableTransaction {
clsags.append(&mut clsag_pairs.iter().map(|clsag| clsag.0.clone()).collect::<Vec<_>>());
pseudo_outs.append(&mut clsag_pairs.iter().map(|clsag| clsag.1).collect::<Vec<_>>());
}
_ => unreachable!("attempted to sign a TX which wasn't CLSAG"),
RctPrunable::MlsagBorromean { .. } | RctPrunable::MlsagBulletproofs { .. } => {
unreachable!("attempted to sign a TX which wasn't CLSAG")
}
}
Ok(tx)
}
@ -713,13 +719,19 @@ impl SignableTransaction {
impl Eventuality {
/// Enables building a HashMap of Extra -> Eventuality for efficiently checking if an on-chain
/// transaction may match this eventuality.
///
/// This extra is cryptographically bound to:
/// 1) A specific set of inputs (via their output key)
/// 2) A specific seed for the ephemeral keys
///
/// This extra may be used with a transaction with a distinct set of inputs, yet no honest
/// transaction which doesn't satisfy this Eventuality will contain it.
#[must_use]
pub fn extra(&self) -> &[u8] {
&self.extra
}
#[must_use]
pub fn matches(&self, tx: &Transaction) -> bool {
if self.payments.len() != tx.prefix.outputs.len() {
return false;
@ -752,9 +764,10 @@ impl Eventuality {
}
// TODO: Remove this when the following for loop is updated
if !rct_type.compact_encrypted_amounts() {
panic!("created an Eventuality for a very old RctType we don't support proving for");
}
assert!(
rct_type.compact_encrypted_amounts(),
"created an Eventuality for a very old RctType we don't support proving for"
);
for (o, (expected, actual)) in outputs.iter().zip(tx.prefix.outputs.iter()).enumerate() {
// Verify the output, commitment, and encrypted amount.
@ -804,18 +817,19 @@ impl Eventuality {
write_vec(write_byte, &self.extra, w)
}
#[must_use]
pub fn serialize(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(128);
self.write(&mut buf).unwrap();
buf
}
pub fn read<R: io::Read>(r: &mut R) -> io::Result<Eventuality> {
pub fn read<R: io::Read>(r: &mut R) -> io::Result<Self> {
fn read_address<R: io::Read>(r: &mut R) -> io::Result<MoneroAddress> {
String::from_utf8(read_vec(read_byte, r)?)
.ok()
.and_then(|str| MoneroAddress::from_str_raw(&str).ok())
.ok_or(io::Error::new(io::ErrorKind::Other, "invalid address"))
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "invalid address"))
}
fn read_payment<R: io::Read>(r: &mut R) -> io::Result<InternalPayment> {
@ -836,7 +850,7 @@ impl Eventuality {
})
}
Ok(Eventuality {
Ok(Self {
protocol: Protocol::read(r)?,
r_seed: Zeroizing::new(read_bytes::<_, 32>(r)?),
inputs: read_vec(read_point, r)?,

View file

@ -1,9 +1,10 @@
use std_shims::{
sync::Arc,
vec::Vec,
io::{self, Read},
collections::HashMap,
};
use std::sync::{Arc, RwLock};
use std::sync::RwLock;
use zeroize::Zeroizing;
@ -267,14 +268,15 @@ impl SignMachine<Transaction> for TransactionSignMachine {
mut commitments: HashMap<Participant, Self::Preprocess>,
msg: &[u8],
) -> Result<(TransactionSignatureMachine, Self::SignatureShare), FrostError> {
if !msg.is_empty() {
panic!("message was passed to the TransactionMachine when it generates its own");
}
assert!(
msg.is_empty(),
"message was passed to the TransactionMachine when it generates its own"
);
// Find out who's included
// This may not be a valid set of signers yet the algorithm machine will error if it's not
commitments.remove(&self.i); // Remove, if it was included for some reason
let mut included = commitments.keys().cloned().collect::<Vec<_>>();
let mut included = commitments.keys().copied().collect::<Vec<_>>();
included.push(self.i);
included.sort_unstable();
@ -325,7 +327,7 @@ impl SignMachine<Transaction> for TransactionSignMachine {
// Remove our preprocess which shouldn't be here. It was just the easiest way to implement the
// above
for map in commitments.iter_mut() {
for map in &mut commitments {
map.remove(&self.i);
}

View file

@ -1,4 +1,5 @@
pub use core::sync::*;
pub use alloc::sync::*;
mod mutex_shim {
#[cfg(feature = "std")]