1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
2#![doc = include_str!("../README.md")]
3#![deny(missing_docs)]
4#![cfg_attr(not(feature = "std"), no_std)]
5
6use std_shims::{io, vec::Vec};
7#[cfg(feature = "std")]
8use std_shims::sync::LazyLock;
9
10use zeroize::{Zeroize, ZeroizeOnDrop};
11
12use sha3::{Digest, Keccak256};
13use curve25519_dalek::{
14 constants::ED25519_BASEPOINT_POINT,
15 traits::VartimePrecomputedMultiscalarMul,
16 scalar::Scalar,
17 edwards::{EdwardsPoint, VartimeEdwardsPrecomputation},
18};
19
20use monero_io::*;
21use monero_generators::H;
22
23mod unreduced_scalar;
24pub use unreduced_scalar::UnreducedScalar;
25
26#[cfg(test)]
27mod tests;
28
29#[cfg(feature = "std")]
31static INV_EIGHT_CELL: LazyLock<Scalar> = LazyLock::new(|| Scalar::from(8u8).invert());
32#[cfg(feature = "std")]
34#[allow(non_snake_case)]
35pub fn INV_EIGHT() -> Scalar {
36 *INV_EIGHT_CELL
37}
38#[cfg(not(feature = "std"))]
41#[allow(non_snake_case)]
42pub fn INV_EIGHT() -> Scalar {
43 Scalar::from(8u8).invert()
44}
45
46#[cfg(feature = "std")]
47static G_PRECOMP_CELL: LazyLock<VartimeEdwardsPrecomputation> =
48 LazyLock::new(|| VartimeEdwardsPrecomputation::new([ED25519_BASEPOINT_POINT]));
49#[cfg(feature = "std")]
51#[allow(non_snake_case)]
52pub fn G_PRECOMP() -> &'static VartimeEdwardsPrecomputation {
53 &G_PRECOMP_CELL
54}
55#[cfg(not(feature = "std"))]
57#[allow(non_snake_case)]
58pub fn G_PRECOMP() -> VartimeEdwardsPrecomputation {
59 VartimeEdwardsPrecomputation::new([ED25519_BASEPOINT_POINT])
60}
61
62pub fn keccak256(data: impl AsRef<[u8]>) -> [u8; 32] {
64 Keccak256::digest(data.as_ref()).into()
65}
66
67pub fn keccak256_to_scalar(data: impl AsRef<[u8]>) -> Scalar {
71 let scalar = Scalar::from_bytes_mod_order(keccak256(data.as_ref()));
72 assert!(
77 scalar != Scalar::ZERO,
78 "keccak256(preimage) \\cong 0 \\mod l! Preimage: {:?}",
79 data.as_ref()
80 );
81 scalar
82}
83
84#[allow(non_snake_case)]
86#[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
87pub struct Commitment {
88 pub mask: Scalar,
90 pub amount: u64,
92}
93
94impl core::fmt::Debug for Commitment {
95 fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
96 fmt.debug_struct("Commitment").field("amount", &self.amount).finish_non_exhaustive()
97 }
98}
99
100impl Commitment {
101 pub fn zero() -> Commitment {
103 Commitment { mask: Scalar::ONE, amount: 0 }
104 }
105
106 pub fn new(mask: Scalar, amount: u64) -> Commitment {
108 Commitment { mask, amount }
109 }
110
111 pub fn calculate(&self) -> EdwardsPoint {
113 EdwardsPoint::vartime_double_scalar_mul_basepoint(&Scalar::from(self.amount), &H, &self.mask)
114 }
115
116 pub fn write<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
121 w.write_all(&self.mask.to_bytes())?;
122 w.write_all(&self.amount.to_le_bytes())
123 }
124
125 pub fn serialize(&self) -> Vec<u8> {
130 let mut res = Vec::with_capacity(32 + 8);
131 self.write(&mut res).expect("write failed but <Vec as io::Write> doesn't fail");
132 res
133 }
134
135 pub fn read<R: io::Read>(r: &mut R) -> io::Result<Commitment> {
140 Ok(Commitment::new(read_scalar(r)?, read_u64(r)?))
141 }
142}
143
144#[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
146pub struct Decoys {
147 offsets: Vec<u64>,
148 signer_index: u8,
149 ring: Vec<[EdwardsPoint; 2]>,
150}
151
152impl core::fmt::Debug for Decoys {
153 fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
154 fmt
155 .debug_struct("Decoys")
156 .field("offsets", &self.offsets)
157 .field("ring", &self.ring)
158 .finish_non_exhaustive()
159 }
160}
161
162#[allow(clippy::len_without_is_empty)]
163impl Decoys {
164 pub fn new(offsets: Vec<u64>, signer_index: u8, ring: Vec<[EdwardsPoint; 2]>) -> Option<Self> {
169 if (offsets.len() > usize::from(u8::MAX)) ||
170 (offsets.len() != ring.len()) ||
171 (usize::from(signer_index) >= ring.len())
172 {
173 None?;
174 }
175 if offsets.iter().copied().try_fold(0, u64::checked_add).is_none() {
177 None?;
178 }
179 Some(Decoys { offsets, signer_index, ring })
180 }
181
182 pub fn len(&self) -> usize {
184 self.offsets.len()
185 }
186
187 pub fn offsets(&self) -> &[u64] {
192 &self.offsets
193 }
194
195 pub fn positions(&self) -> Vec<u64> {
197 let mut res = Vec::with_capacity(self.len());
198 res.push(self.offsets[0]);
199 for m in 1 .. self.len() {
200 res.push(res[m - 1] + self.offsets[m]);
201 }
202 res
203 }
204
205 pub fn signer_index(&self) -> u8 {
207 self.signer_index
208 }
209
210 pub fn ring(&self) -> &[[EdwardsPoint; 2]] {
212 &self.ring
213 }
214
215 pub fn signer_ring_members(&self) -> [EdwardsPoint; 2] {
217 self.ring[usize::from(self.signer_index)]
218 }
219
220 pub fn write(&self, w: &mut impl io::Write) -> io::Result<()> {
225 write_vec(write_varint, &self.offsets, w)?;
226 w.write_all(&[self.signer_index])?;
227 write_raw_vec(
228 |pair, w| {
229 write_point(&pair[0], w)?;
230 write_point(&pair[1], w)
231 },
232 &self.ring,
233 w,
234 )
235 }
236
237 pub fn serialize(&self) -> Vec<u8> {
242 let mut res =
243 Vec::with_capacity((1 + (2 * self.offsets.len())) + 1 + 1 + (self.ring.len() * 64));
244 self.write(&mut res).expect("write failed but <Vec as io::Write> doesn't fail");
245 res
246 }
247
248 pub fn read(r: &mut impl io::Read) -> io::Result<Decoys> {
253 let offsets = read_vec(read_varint, None, r)?;
254 let len = offsets.len();
255 Decoys::new(
256 offsets,
257 read_byte(r)?,
258 read_raw_vec(|r| Ok([read_point(r)?, read_point(r)?]), len, r)?,
259 )
260 .ok_or_else(|| io::Error::other("invalid Decoys"))
261 }
262}