1use std_shims::{
2 vec,
3 vec::Vec,
4 io::{self, Read, Write},
5};
6
7use zeroize::Zeroize;
8
9use curve25519_dalek::edwards::CompressedEdwardsY;
10
11pub use monero_mlsag as mlsag;
12pub use monero_clsag as clsag;
13pub use monero_borromean as borromean;
14pub use monero_bulletproofs as bulletproofs;
15
16use crate::{
17 io::*,
18 ringct::{mlsag::Mlsag, clsag::Clsag, borromean::BorromeanRange, bulletproofs::Bulletproof},
19};
20
21#[derive(Clone, PartialEq, Eq, Debug)]
23pub enum EncryptedAmount {
24 Original {
26 mask: [u8; 32],
28 amount: [u8; 32],
30 },
31 Compact {
33 amount: [u8; 8],
35 },
36}
37
38impl EncryptedAmount {
39 pub fn read<R: Read>(compact: bool, r: &mut R) -> io::Result<EncryptedAmount> {
41 Ok(if !compact {
42 EncryptedAmount::Original { mask: read_bytes(r)?, amount: read_bytes(r)? }
43 } else {
44 EncryptedAmount::Compact { amount: read_bytes(r)? }
45 })
46 }
47
48 pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
50 match self {
51 EncryptedAmount::Original { mask, amount } => {
52 w.write_all(mask)?;
53 w.write_all(amount)
54 }
55 EncryptedAmount::Compact { amount } => w.write_all(amount),
56 }
57 }
58}
59
60#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
62pub enum RctType {
63 AggregateMlsagBorromean,
67 MlsagBorromean,
71 MlsagBulletproofs,
75 MlsagBulletproofsCompactAmount,
79 ClsagBulletproof,
83 ClsagBulletproofPlus,
87}
88
89impl From<RctType> for u8 {
90 fn from(rct_type: RctType) -> u8 {
91 match rct_type {
92 RctType::AggregateMlsagBorromean => 1,
93 RctType::MlsagBorromean => 2,
94 RctType::MlsagBulletproofs => 3,
95 RctType::MlsagBulletproofsCompactAmount => 4,
96 RctType::ClsagBulletproof => 5,
97 RctType::ClsagBulletproofPlus => 6,
98 }
99 }
100}
101
102impl TryFrom<u8> for RctType {
103 type Error = ();
104 fn try_from(byte: u8) -> Result<Self, ()> {
105 Ok(match byte {
106 1 => RctType::AggregateMlsagBorromean,
107 2 => RctType::MlsagBorromean,
108 3 => RctType::MlsagBulletproofs,
109 4 => RctType::MlsagBulletproofsCompactAmount,
110 5 => RctType::ClsagBulletproof,
111 6 => RctType::ClsagBulletproofPlus,
112 _ => Err(())?,
113 })
114 }
115}
116
117impl RctType {
118 pub fn compact_encrypted_amounts(&self) -> bool {
120 match self {
121 RctType::AggregateMlsagBorromean | RctType::MlsagBorromean | RctType::MlsagBulletproofs => {
122 false
123 }
124 RctType::MlsagBulletproofsCompactAmount |
125 RctType::ClsagBulletproof |
126 RctType::ClsagBulletproofPlus => true,
127 }
128 }
129
130 pub(crate) fn bulletproof(&self) -> bool {
132 match self {
133 RctType::MlsagBulletproofs |
134 RctType::MlsagBulletproofsCompactAmount |
135 RctType::ClsagBulletproof => true,
136 RctType::AggregateMlsagBorromean |
137 RctType::MlsagBorromean |
138 RctType::ClsagBulletproofPlus => false,
139 }
140 }
141
142 pub(crate) fn bulletproof_plus(&self) -> bool {
144 match self {
145 RctType::ClsagBulletproofPlus => true,
146 RctType::AggregateMlsagBorromean |
147 RctType::MlsagBorromean |
148 RctType::MlsagBulletproofs |
149 RctType::MlsagBulletproofsCompactAmount |
150 RctType::ClsagBulletproof => false,
151 }
152 }
153}
154
155#[derive(Clone, PartialEq, Eq, Debug)]
163pub struct RctBase {
164 pub fee: u64,
166 pub pseudo_outs: Vec<CompressedEdwardsY>,
170 pub encrypted_amounts: Vec<EncryptedAmount>,
172 pub commitments: Vec<CompressedEdwardsY>,
174}
175
176impl RctBase {
177 pub fn write<W: Write>(&self, w: &mut W, rct_type: RctType) -> io::Result<()> {
179 w.write_all(&[u8::from(rct_type)])?;
180
181 write_varint(&self.fee, w)?;
182 if rct_type == RctType::MlsagBorromean {
183 write_raw_vec(write_compressed_point, &self.pseudo_outs, w)?;
184 }
185 for encrypted_amount in &self.encrypted_amounts {
186 encrypted_amount.write(w)?;
187 }
188 write_raw_vec(write_compressed_point, &self.commitments, w)
189 }
190
191 pub fn read<R: Read>(
193 inputs: usize,
194 outputs: usize,
195 r: &mut R,
196 ) -> io::Result<Option<(RctType, RctBase)>> {
197 let rct_type = read_byte(r)?;
198 if rct_type == 0 {
199 return Ok(None);
200 }
201 let rct_type =
202 RctType::try_from(rct_type).map_err(|()| io::Error::other("invalid RCT type"))?;
203
204 match rct_type {
205 RctType::AggregateMlsagBorromean | RctType::MlsagBorromean => {}
206 RctType::MlsagBulletproofs |
207 RctType::MlsagBulletproofsCompactAmount |
208 RctType::ClsagBulletproof |
209 RctType::ClsagBulletproofPlus => {
210 if outputs == 0 {
211 Err(io::Error::other("RCT with Bulletproofs(+) had 0 outputs"))?;
217 }
218 }
219 }
220
221 Ok(Some((
222 rct_type,
223 RctBase {
224 fee: read_varint(r)?,
225 pseudo_outs: if rct_type == RctType::MlsagBorromean {
230 read_raw_vec(read_compressed_point, inputs, r)?
231 } else {
232 vec![]
233 },
234 encrypted_amounts: (0 .. outputs)
235 .map(|_| EncryptedAmount::read(rct_type.compact_encrypted_amounts(), r))
236 .collect::<Result<_, _>>()?,
237 commitments: read_raw_vec(read_compressed_point, outputs, r)?,
238 },
239 )))
240 }
241}
242
243#[derive(Clone, PartialEq, Eq, Debug)]
245pub enum RctPrunable {
246 AggregateMlsagBorromean {
248 mlsag: Mlsag,
250 borromean: Vec<BorromeanRange>,
252 },
253 MlsagBorromean {
255 mlsags: Vec<Mlsag>,
257 borromean: Vec<BorromeanRange>,
259 },
260 MlsagBulletproofs {
262 mlsags: Vec<Mlsag>,
264 pseudo_outs: Vec<CompressedEdwardsY>,
266 bulletproof: Bulletproof,
268 },
269 MlsagBulletproofsCompactAmount {
274 mlsags: Vec<Mlsag>,
276 pseudo_outs: Vec<CompressedEdwardsY>,
278 bulletproof: Bulletproof,
280 },
281 Clsag {
283 clsags: Vec<Clsag>,
285 pseudo_outs: Vec<CompressedEdwardsY>,
287 bulletproof: Bulletproof,
289 },
290}
291
292impl RctPrunable {
293 pub fn write<W: Write>(&self, w: &mut W, rct_type: RctType) -> io::Result<()> {
295 match self {
296 RctPrunable::AggregateMlsagBorromean { borromean, mlsag } => {
297 write_raw_vec(BorromeanRange::write, borromean, w)?;
298 mlsag.write(w)
299 }
300 RctPrunable::MlsagBorromean { borromean, mlsags } => {
301 write_raw_vec(BorromeanRange::write, borromean, w)?;
302 write_raw_vec(Mlsag::write, mlsags, w)
303 }
304 RctPrunable::MlsagBulletproofs { bulletproof, mlsags, pseudo_outs } |
305 RctPrunable::MlsagBulletproofsCompactAmount { bulletproof, mlsags, pseudo_outs } => {
306 if rct_type == RctType::MlsagBulletproofs {
307 w.write_all(&1u32.to_le_bytes())?;
308 } else {
309 w.write_all(&[1])?;
310 }
311 bulletproof.write(w)?;
312
313 write_raw_vec(Mlsag::write, mlsags, w)?;
314 write_raw_vec(write_compressed_point, pseudo_outs, w)
315 }
316 RctPrunable::Clsag { bulletproof, clsags, pseudo_outs } => {
317 w.write_all(&[1])?;
318 bulletproof.write(w)?;
319
320 write_raw_vec(Clsag::write, clsags, w)?;
321 write_raw_vec(write_compressed_point, pseudo_outs, w)
322 }
323 }
324 }
325
326 pub fn serialize(&self, rct_type: RctType) -> Vec<u8> {
328 let mut serialized = vec![];
329 self.write(&mut serialized, rct_type).unwrap();
330 serialized
331 }
332
333 pub fn read<R: Read>(
335 rct_type: RctType,
336 ring_length: usize,
337 inputs: usize,
338 outputs: usize,
339 r: &mut R,
340 ) -> io::Result<RctPrunable> {
341 Ok(match rct_type {
342 RctType::AggregateMlsagBorromean => RctPrunable::AggregateMlsagBorromean {
343 borromean: read_raw_vec(BorromeanRange::read, outputs, r)?,
344 mlsag: Mlsag::read(ring_length, inputs + 1, r)?,
345 },
346 RctType::MlsagBorromean => RctPrunable::MlsagBorromean {
347 borromean: read_raw_vec(BorromeanRange::read, outputs, r)?,
348 mlsags: (0 .. inputs).map(|_| Mlsag::read(ring_length, 2, r)).collect::<Result<_, _>>()?,
349 },
350 RctType::MlsagBulletproofs | RctType::MlsagBulletproofsCompactAmount => {
351 let bulletproof = {
352 if (if rct_type == RctType::MlsagBulletproofs {
353 u64::from(read_u32(r)?)
354 } else {
355 read_varint(r)?
356 }) != 1
357 {
358 Err(io::Error::other("n bulletproofs instead of one"))?;
359 }
360 Bulletproof::read(r)?
361 };
362 let mlsags =
363 (0 .. inputs).map(|_| Mlsag::read(ring_length, 2, r)).collect::<Result<_, _>>()?;
364 let pseudo_outs = read_raw_vec(read_compressed_point, inputs, r)?;
365 if rct_type == RctType::MlsagBulletproofs {
366 RctPrunable::MlsagBulletproofs { bulletproof, mlsags, pseudo_outs }
367 } else {
368 debug_assert_eq!(rct_type, RctType::MlsagBulletproofsCompactAmount);
369 RctPrunable::MlsagBulletproofsCompactAmount { bulletproof, mlsags, pseudo_outs }
370 }
371 }
372 RctType::ClsagBulletproof | RctType::ClsagBulletproofPlus => RctPrunable::Clsag {
373 bulletproof: {
374 if read_varint::<_, u64>(r)? != 1 {
375 Err(io::Error::other("n bulletproofs instead of one"))?;
376 }
377 (if rct_type == RctType::ClsagBulletproof {
378 Bulletproof::read
379 } else {
380 Bulletproof::read_plus
381 })(r)?
382 },
383 clsags: (0 .. inputs).map(|_| Clsag::read(ring_length, r)).collect::<Result<_, _>>()?,
384 pseudo_outs: read_raw_vec(read_compressed_point, inputs, r)?,
385 },
386 })
387 }
388
389 pub(crate) fn signature_write<W: Write>(&self, w: &mut W) -> io::Result<()> {
391 match self {
392 RctPrunable::AggregateMlsagBorromean { borromean, .. } |
393 RctPrunable::MlsagBorromean { borromean, .. } => {
394 borromean.iter().try_for_each(|rs| rs.write(w))
395 }
396 RctPrunable::MlsagBulletproofs { bulletproof, .. } |
397 RctPrunable::MlsagBulletproofsCompactAmount { bulletproof, .. } |
398 RctPrunable::Clsag { bulletproof, .. } => bulletproof.signature_write(w),
399 }
400 }
401}
402
403#[derive(Clone, PartialEq, Eq, Debug)]
409pub struct RctProofs {
410 pub base: RctBase,
412 pub prunable: RctPrunable,
414}
415
416impl RctProofs {
417 pub fn rct_type(&self) -> RctType {
419 match &self.prunable {
420 RctPrunable::AggregateMlsagBorromean { .. } => RctType::AggregateMlsagBorromean,
421 RctPrunable::MlsagBorromean { .. } => RctType::MlsagBorromean,
422 RctPrunable::MlsagBulletproofs { .. } => RctType::MlsagBulletproofs,
423 RctPrunable::MlsagBulletproofsCompactAmount { .. } => RctType::MlsagBulletproofsCompactAmount,
424 RctPrunable::Clsag { bulletproof, .. } => {
425 if matches!(bulletproof, Bulletproof::Original { .. }) {
426 RctType::ClsagBulletproof
427 } else {
428 RctType::ClsagBulletproofPlus
429 }
430 }
431 }
432 }
433
434 pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
436 let rct_type = self.rct_type();
437 self.base.write(w, rct_type)?;
438 self.prunable.write(w, rct_type)
439 }
440
441 pub fn serialize(&self) -> Vec<u8> {
443 let mut serialized = vec![];
444 self.write(&mut serialized).unwrap();
445 serialized
446 }
447
448 pub fn read<R: Read>(
450 ring_length: usize,
451 inputs: usize,
452 outputs: usize,
453 r: &mut R,
454 ) -> io::Result<Option<RctProofs>> {
455 let Some((rct_type, base)) = RctBase::read(inputs, outputs, r)? else { return Ok(None) };
456 Ok(Some(RctProofs {
457 base,
458 prunable: RctPrunable::read(rct_type, ring_length, inputs, outputs, r)?,
459 }))
460 }
461}
462
463#[derive(Clone, PartialEq, Eq, Debug)]
465pub struct PrunedRctProofs {
466 pub rct_type: RctType,
468 pub base: RctBase,
470}