monero_ed25519/
commitment.rs

1use std_shims::{sync::LazyLock, io};
2
3use subtle::{Choice, ConstantTimeEq};
4use zeroize::{Zeroize, ZeroizeOnDrop};
5
6use monero_io::read_u64;
7
8use crate::{CompressedPoint, Point, Scalar};
9
10// A static for `H` as it's frequently used yet this decompression is expensive.
11static H: LazyLock<curve25519_dalek::EdwardsPoint> = LazyLock::new(|| {
12  curve25519_dalek::edwards::CompressedEdwardsY(CompressedPoint::H.to_bytes())
13    .decompress()
14    .expect("couldn't decompress `CompressedPoint::H`")
15});
16
17/// The opening for a Pedersen commitment commiting to a `u64`.
18#[derive(Clone, Zeroize, ZeroizeOnDrop)]
19pub struct Commitment {
20  /// The mask for this commitment.
21  pub mask: Scalar,
22  /// The amount committed to by this commitment.
23  pub amount: u64,
24}
25
26impl ConstantTimeEq for Commitment {
27  fn ct_eq(&self, other: &Self) -> Choice {
28    self.mask.ct_eq(&other.mask) & self.amount.ct_eq(&other.amount)
29  }
30}
31
32impl core::fmt::Debug for Commitment {
33  /// This implementation reveals the `Commitment`'s amount.
34  fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
35    fmt.debug_struct("Commitment").field("amount", &self.amount).finish_non_exhaustive()
36  }
37}
38
39impl Commitment {
40  /// A commitment to 0, defined with a mask of 1 (as to not be the identity).
41  ///
42  /// This follows the Monero protocol's definition for a commitment without randomness.
43  /// https://github.com/monero-project/monero
44  ///   /blob/ac02af92867590ca80b2779a7bbeafa99ff94dcb/src/ringct/rctOps.cpp#L333
45  #[doc(hidden)] // TODO: Remove this for `without_randomness`, taking an amount? How is this used?
46  pub fn zero() -> Commitment {
47    Commitment { mask: Scalar::from(curve25519_dalek::Scalar::ONE), amount: 0 }
48  }
49
50  /// Create a new `Commitment`.
51  pub fn new(mask: Scalar, amount: u64) -> Commitment {
52    Commitment { mask, amount }
53  }
54
55  /// Commit to the value within this opening.
56  // TODO: Optimize around how `amount` is short.
57  pub fn commit(&self) -> Point {
58    Point::from(
59      <curve25519_dalek::EdwardsPoint as curve25519_dalek::traits::MultiscalarMul>::multiscalar_mul(
60        [self.mask.into(), self.amount.into()],
61        [curve25519_dalek::constants::ED25519_BASEPOINT_POINT, *H],
62      ),
63    )
64  }
65
66  /// Write the `Commitment`.
67  ///
68  /// This is not a Monero protocol defined struct, and this is accordingly not a Monero protocol
69  /// defined serialization. This may run in time variable to its value.
70  pub fn write<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
71    self.mask.write(w)?;
72    w.write_all(&self.amount.to_le_bytes())
73  }
74
75  /// Read a `Commitment`.
76  ///
77  /// This is not a Monero protocol defined struct, and this is accordingly not a Monero protocol
78  /// defined serialization. This may run in time variable to its value.
79  pub fn read<R: io::Read>(r: &mut R) -> io::Result<Commitment> {
80    Ok(Commitment::new(Scalar::read(r)?, read_u64(r)?))
81  }
82}