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
6#[allow(unused_imports)]
7use std_shims::prelude::*;
8use std_shims::{io, vec::Vec};
9#[cfg(feature = "std")]
10use std_shims::sync::LazyLock;
11
12use zeroize::{Zeroize, ZeroizeOnDrop};
13
14use sha3::{Digest, Keccak256};
15use curve25519_dalek::{
16 constants::ED25519_BASEPOINT_POINT,
17 traits::VartimePrecomputedMultiscalarMul,
18 scalar::Scalar,
19 edwards::{EdwardsPoint, VartimeEdwardsPrecomputation},
20};
21
22use monero_io::*;
23use monero_generators::H;
24
25mod unreduced_scalar;
26pub use unreduced_scalar::UnreducedScalar;
27
28#[cfg(test)]
29mod tests;
30
31#[cfg(feature = "std")]
33static INV_EIGHT_CELL: LazyLock<Scalar> = LazyLock::new(|| Scalar::from(8u8).invert());
34#[cfg(feature = "std")]
36#[allow(non_snake_case)]
37pub fn INV_EIGHT() -> Scalar {
38 *INV_EIGHT_CELL
39}
40#[cfg(not(feature = "std"))]
43#[allow(non_snake_case)]
44pub fn INV_EIGHT() -> Scalar {
45 Scalar::from(8u8).invert()
46}
47
48#[cfg(feature = "std")]
49static G_PRECOMP_CELL: LazyLock<VartimeEdwardsPrecomputation> =
50 LazyLock::new(|| VartimeEdwardsPrecomputation::new([ED25519_BASEPOINT_POINT]));
51#[cfg(feature = "std")]
53#[allow(non_snake_case)]
54pub fn G_PRECOMP() -> &'static VartimeEdwardsPrecomputation {
55 &G_PRECOMP_CELL
56}
57#[cfg(not(feature = "std"))]
59#[allow(non_snake_case)]
60pub fn G_PRECOMP() -> VartimeEdwardsPrecomputation {
61 VartimeEdwardsPrecomputation::new([ED25519_BASEPOINT_POINT])
62}
63
64pub fn keccak256(data: impl AsRef<[u8]>) -> [u8; 32] {
66 Keccak256::digest(data.as_ref()).into()
67}
68
69pub fn keccak256_to_scalar(data: impl AsRef<[u8]>) -> Scalar {
73 let scalar = Scalar::from_bytes_mod_order(keccak256(&data));
74 assert!(
79 scalar != Scalar::ZERO,
80 "keccak256(preimage) \\cong 0 \\mod l! Preimage: {:?}",
81 data.as_ref()
82 );
83 scalar
84}
85
86#[allow(non_snake_case)]
88#[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
89pub struct Commitment {
90 pub mask: Scalar,
92 pub amount: u64,
94}
95
96impl core::fmt::Debug for Commitment {
97 fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
98 fmt.debug_struct("Commitment").field("amount", &self.amount).finish_non_exhaustive()
99 }
100}
101
102impl Commitment {
103 pub fn zero() -> Commitment {
105 Commitment { mask: Scalar::ONE, amount: 0 }
106 }
107
108 pub fn new(mask: Scalar, amount: u64) -> Commitment {
110 Commitment { mask, amount }
111 }
112
113 pub fn calculate(&self) -> EdwardsPoint {
115 EdwardsPoint::vartime_double_scalar_mul_basepoint(&Scalar::from(self.amount), &H, &self.mask)
116 }
117
118 pub fn write<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
123 w.write_all(&self.mask.to_bytes())?;
124 w.write_all(&self.amount.to_le_bytes())
125 }
126
127 pub fn serialize(&self) -> Vec<u8> {
132 let mut res = Vec::with_capacity(32 + 8);
133 self.write(&mut res).expect("write failed but <Vec as io::Write> doesn't fail");
134 res
135 }
136
137 pub fn read<R: io::Read>(r: &mut R) -> io::Result<Commitment> {
142 Ok(Commitment::new(read_scalar(r)?, read_u64(r)?))
143 }
144}
145
146#[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
148pub struct Decoys {
149 offsets: Vec<u64>,
150 signer_index: u8,
151 ring: Vec<[EdwardsPoint; 2]>,
152}
153
154impl core::fmt::Debug for Decoys {
155 fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
156 fmt
157 .debug_struct("Decoys")
158 .field("offsets", &self.offsets)
159 .field("ring", &self.ring)
160 .finish_non_exhaustive()
161 }
162}
163
164#[allow(clippy::len_without_is_empty)]
165impl Decoys {
166 pub fn new(offsets: Vec<u64>, signer_index: u8, ring: Vec<[EdwardsPoint; 2]>) -> Option<Self> {
171 if (offsets.len() > usize::from(u8::MAX)) ||
172 (offsets.len() != ring.len()) ||
173 (usize::from(signer_index) >= ring.len())
174 {
175 None?;
176 }
177 if offsets.iter().copied().try_fold(0, u64::checked_add).is_none() {
179 None?;
180 }
181 Some(Decoys { offsets, signer_index, ring })
182 }
183
184 pub fn len(&self) -> usize {
186 self.offsets.len()
187 }
188
189 pub fn offsets(&self) -> &[u64] {
194 &self.offsets
195 }
196
197 pub fn positions(&self) -> Vec<u64> {
199 let mut res = Vec::with_capacity(self.len());
200 res.push(self.offsets[0]);
201 for m in 1 .. self.len() {
202 res.push(res[m - 1] + self.offsets[m]);
203 }
204 res
205 }
206
207 pub fn signer_index(&self) -> u8 {
209 self.signer_index
210 }
211
212 pub fn ring(&self) -> &[[EdwardsPoint; 2]] {
214 &self.ring
215 }
216
217 pub fn signer_ring_members(&self) -> [EdwardsPoint; 2] {
219 self.ring[usize::from(self.signer_index)]
220 }
221
222 pub fn write(&self, w: &mut impl io::Write) -> io::Result<()> {
227 write_vec(write_varint, &self.offsets, w)?;
228 w.write_all(&[self.signer_index])?;
229 write_raw_vec(
230 |pair, w| {
231 write_point(&pair[0], w)?;
232 write_point(&pair[1], w)
233 },
234 &self.ring,
235 w,
236 )
237 }
238
239 pub fn serialize(&self) -> Vec<u8> {
244 let mut res =
245 Vec::with_capacity((1 + (2 * self.offsets.len())) + 1 + 1 + (self.ring.len() * 64));
246 self.write(&mut res).expect("write failed but <Vec as io::Write> doesn't fail");
247 res
248 }
249
250 pub fn read(r: &mut impl io::Read) -> io::Result<Decoys> {
255 let offsets = read_vec(read_varint, None, r)?;
256 let len = offsets.len();
257 Decoys::new(
258 offsets,
259 read_byte(r)?,
260 read_raw_vec(|r| Ok([read_point(r)?, read_point(r)?]), len, r)?,
261 )
262 .ok_or_else(|| io::Error::other("invalid Decoys"))
263 }
264}