monero/util/
ringct.rs

1// Rust Monero Library
2// Written in 2019-2023 by
3//   Monero Rust Contributors
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files (the "Software"), to deal
7// in the Software without restriction, including without limitation the rights
8// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9// copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11//
12// The above copyright notice and this permission notice shall be included in all
13// copies or substantial portions of the Software.
14//
15
16//! Ring Confidential Transaction primitive types.
17//!
18//! Support for parsing Ring Confidential Transaction signature within [`Transaction`].
19//!
20//! [`Transaction`]: crate::blockdata::transaction::Transaction
21//!
22
23use std::{fmt, io};
24
25use crate::consensus::encode::{
26    self, consensus_decode_sized_vec, serialize, Decodable, Encodable, VarInt,
27};
28use crate::cryptonote::hash;
29use crate::cryptonote::onetime_key::KeyGenerator;
30use crate::util::amount::Amount;
31use crate::util::key::H;
32use crate::{PublicKey, ViewPair};
33
34#[cfg(feature = "serde")]
35use serde_big_array::BigArray;
36#[cfg(feature = "serde")]
37use serde_crate::{Deserialize, Serialize};
38
39use curve25519_dalek::constants::ED25519_BASEPOINT_POINT;
40use curve25519_dalek::edwards::EdwardsPoint;
41use curve25519_dalek::scalar::Scalar;
42use sealed::sealed;
43use thiserror::Error;
44
45use std::convert::TryInto;
46
47/// Ring Confidential Transaction potential errors.
48#[derive(Error, Debug, PartialEq, Eq)]
49pub enum Error {
50    /// Invalid RingCt type.
51    #[error("Unknown RingCt type")]
52    UnknownRctType,
53}
54
55// ====================================================================
56/// Raw 32 bytes key.
57#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
58#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
59#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
60pub struct Key {
61    /// The actual key.
62    pub key: [u8; 32],
63}
64
65impl_hex_display!(Key, key);
66
67impl_consensus_encoding!(Key, key);
68
69impl Key {
70    fn new() -> Key {
71        Key { key: [0; 32] }
72    }
73}
74
75impl From<[u8; 32]> for Key {
76    fn from(key: [u8; 32]) -> Self {
77        Self { key }
78    }
79}
80
81// ====================================================================
82/// Raw 64 bytes key.
83#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
84#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
85#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
86pub struct Key64 {
87    /// The actual key.
88    #[cfg_attr(feature = "serde", serde(with = "BigArray"))]
89    pub keys: [Key; 64],
90}
91
92impl Key64 {
93    fn new() -> Key64 {
94        Key64 {
95            keys: [Key::new(); 64],
96        }
97    }
98}
99
100impl From<[Key; 64]> for Key64 {
101    fn from(keys: [Key; 64]) -> Self {
102        Self { keys }
103    }
104}
105
106impl Decodable for Key64 {
107    fn consensus_decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Key64, encode::Error> {
108        let mut key64 = Key64::new();
109        for i in 0..64 {
110            let key: Key = Decodable::consensus_decode(r)?;
111            key64.keys[i] = key;
112        }
113        Ok(key64)
114    }
115}
116
117#[sealed]
118impl crate::consensus::encode::Encodable for Key64 {
119    fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
120        let mut len = 0;
121        for i in 0..64 {
122            len += self.keys[i].consensus_encode(w)?;
123        }
124        Ok(len)
125    }
126}
127
128impl fmt::Display for Key64 {
129    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
130        for key in self.keys.iter() {
131            writeln!(fmt, "{}", key)?;
132        }
133        Ok(())
134    }
135}
136
137// ====================================================================
138/// Confidential transaction key.
139#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
140#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
141#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
142pub struct CtKey {
143    //pub dest: Key,
144    /// Mask.
145    pub mask: Key,
146}
147
148impl fmt::Display for CtKey {
149    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
150        writeln!(fmt, "Mask: {}", self.mask)
151    }
152}
153
154impl_consensus_encoding!(CtKey, mask);
155
156// ====================================================================
157/// Multisig.
158#[derive(Debug)]
159#[allow(non_snake_case)]
160pub struct MultisigKlrki {
161    /// K value.
162    pub K: Key,
163    /// L value.
164    pub L: Key,
165    /// R value.
166    pub R: Key,
167    /// ki value.
168    pub ki: Key,
169}
170
171impl_consensus_encoding!(MultisigKlrki, K, L, R, ki);
172
173// ====================================================================
174/// Vector of multisig output keys.
175#[derive(Debug)]
176pub struct MultisigOut {
177    /// Vector of keys.
178    pub c: Vec<Key>,
179}
180
181impl_consensus_encoding!(MultisigOut, c);
182
183// ====================================================================
184/// Diffie-Hellman info, mask and amount for transaction before `Bulletproof2` and only 8-bytes
185/// hash for the amount in `Bulletproof2` type.
186#[derive(Debug, Clone, PartialEq, Eq)]
187#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
188#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
189pub enum EcdhInfo {
190    /// Standard format, before `Bulletproof2`.
191    Standard {
192        /// Mask value.
193        mask: Key,
194        /// Amount value.
195        amount: Key,
196    },
197    /// Bulletproof format.
198    Bulletproof {
199        /// Amount value.
200        amount: hash::Hash8,
201    },
202}
203
204impl EcdhInfo {
205    /// Opens the commitment and verifies it against the given one.
206    pub fn open_commitment(
207        &self,
208        view_pair: &ViewPair,
209        tx_pubkey: &PublicKey,
210        index: usize,
211        candidate_commitment: &EdwardsPoint,
212    ) -> Option<Opening> {
213        let shared_key = KeyGenerator::from_key(view_pair, *tx_pubkey).get_rvn_scalar(index);
214
215        let (amount, blinding_factor) = match self {
216            // ecdhDecode in rctOps.cpp else
217            EcdhInfo::Standard { mask, amount } => {
218                let shared_sec1 = hash::Hash::new(shared_key.as_bytes()).to_bytes();
219                let shared_sec2 = hash::Hash::new(shared_sec1).to_bytes();
220                let mask_scalar = Scalar::from_bytes_mod_order(mask.key)
221                    - Scalar::from_bytes_mod_order(shared_sec1);
222
223                let amount_scalar = Scalar::from_bytes_mod_order(amount.key)
224                    - Scalar::from_bytes_mod_order(shared_sec2);
225                // get first 64 bits (d2b in rctTypes.cpp)
226                let amount_significant_bytes = amount_scalar.to_bytes()[0..8]
227                    .try_into()
228                    .expect("Can't fail");
229                let amount = u64::from_le_bytes(amount_significant_bytes);
230                (amount, mask_scalar)
231            }
232            // ecdhDecode in rctOps.cpp if (v2)
233            EcdhInfo::Bulletproof { amount } => {
234                let amount = xor_amount(amount.0, shared_key.scalar);
235                let mask = mask(shared_key.scalar);
236
237                (u64::from_le_bytes(amount), mask)
238            }
239        };
240
241        let amount_scalar = Scalar::from(amount);
242
243        let expected_commitment = ED25519_BASEPOINT_POINT * blinding_factor
244            + H.point.decompress().unwrap() * amount_scalar;
245
246        if &expected_commitment != candidate_commitment {
247            return None;
248        }
249
250        Some(Opening {
251            amount: Amount::from_pico(amount),
252            blinding_factor,
253            commitment: expected_commitment,
254        })
255    }
256}
257
258/// The result of opening the commitment inside the transaction.
259#[derive(Debug)]
260pub struct Opening {
261    /// The original amount of the output.
262    pub amount: Amount,
263    /// The blinding factor used to blind the amount.
264    pub blinding_factor: Scalar,
265    /// The commitment used to verify the blinded amount.
266    pub commitment: EdwardsPoint,
267}
268
269fn xor_amount(amount: [u8; 8], shared_key: Scalar) -> [u8; 8] {
270    // ecdhHash in .cpp
271    let mut amount_key = b"amount".to_vec();
272    amount_key.extend(shared_key.as_bytes());
273
274    // Hn("amount", Hn(rKbv,t))
275    let hash_shared_key = hash::Hash::new(&amount_key).to_fixed_bytes();
276    let hash_shared_key_significant_bytes = hash_shared_key[0..8]
277        .try_into()
278        .expect("hash_shared_key create above has 32 bytes");
279
280    // amount_t = bt XOR Hn("amount", Hn("amount", Hn(rKbv,t)))
281    // xor8(masked.amount, ecdhHash(sharedSec)); in .cpp
282    (u64::from_le_bytes(amount) ^ u64::from_le_bytes(hash_shared_key_significant_bytes))
283        .to_le_bytes()
284}
285
286fn mask(scalar: Scalar) -> Scalar {
287    let mut commitment_key = b"commitment_mask".to_vec();
288    commitment_key.extend(scalar.as_bytes());
289
290    // yt in Z2M p 53
291    hash::Hash::hash_to_scalar(&commitment_key).scalar
292}
293
294impl fmt::Display for EcdhInfo {
295    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
296        match self {
297            EcdhInfo::Standard { mask, amount } => {
298                writeln!(fmt, "Standard")?;
299                writeln!(fmt, "Mask: {}", mask)?;
300                writeln!(fmt, "Amount: {}", amount)?;
301            }
302            EcdhInfo::Bulletproof { amount } => {
303                writeln!(fmt, "Bulletproof2")?;
304                writeln!(fmt, "Amount: {}", amount)?;
305            }
306        };
307        Ok(())
308    }
309}
310
311impl EcdhInfo {
312    /// Decode Diffie-Hellman info given the RingCt type.
313    fn consensus_decode<R: io::Read + ?Sized>(
314        r: &mut R,
315        rct_type: RctType,
316    ) -> Result<EcdhInfo, encode::Error> {
317        match rct_type {
318            RctType::Full | RctType::Simple | RctType::Bulletproof | RctType::Null => {
319                Ok(EcdhInfo::Standard {
320                    mask: Decodable::consensus_decode(r)?,
321                    amount: Decodable::consensus_decode(r)?,
322                })
323            }
324            RctType::Bulletproof2 | RctType::Clsag | RctType::BulletproofPlus => {
325                Ok(EcdhInfo::Bulletproof {
326                    amount: Decodable::consensus_decode(r)?,
327                })
328            }
329        }
330    }
331}
332
333#[sealed]
334impl crate::consensus::encode::Encodable for EcdhInfo {
335    fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
336        let mut len = 0;
337        match self {
338            EcdhInfo::Standard { mask, amount } => {
339                len += mask.consensus_encode(w)?;
340                len += amount.consensus_encode(w)?;
341            }
342            EcdhInfo::Bulletproof { amount } => {
343                len += amount.consensus_encode(w)?;
344            }
345        }
346        Ok(len)
347    }
348}
349
350// ====================================================================
351/// Borromean signature for range commitment.
352#[derive(Debug, Clone, PartialEq, Eq)]
353#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
354#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
355pub struct BoroSig {
356    /// s0 value.
357    pub s0: Key64,
358    /// s1 value.
359    pub s1: Key64,
360    /// ee value.
361    pub ee: Key,
362}
363
364impl_consensus_encoding!(BoroSig, s0, s1, ee);
365
366// ====================================================================
367/// Contains the necessary keys to represent Mlsag signature.
368#[derive(Debug, Clone, PartialEq, Eq)]
369#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
370#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
371pub struct MgSig {
372    /// Matrice of keys.
373    pub ss: Vec<Vec<Key>>,
374    /// cc value.
375    pub cc: Key,
376}
377
378#[sealed]
379impl crate::consensus::encode::Encodable for MgSig {
380    fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
381        let mut len = 0;
382        for ss in self.ss.iter() {
383            len += encode_sized_vec!(ss, w);
384        }
385        len += self.cc.consensus_encode(w)?;
386        Ok(len)
387    }
388}
389
390// ====================================================================
391/// Clsag signature.
392#[derive(Debug, Clone, PartialEq, Eq)]
393#[allow(non_snake_case)]
394#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
395#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
396pub struct Clsag {
397    /// scalars.
398    pub s: Vec<Key>,
399    /// c1 value.
400    pub c1: Key,
401    /// commitment key image.
402    pub D: Key,
403}
404
405#[sealed]
406impl crate::consensus::encode::Encodable for Clsag {
407    fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
408        let mut len = 0;
409        // Encode the vector without prefix lenght
410        len += encode_sized_vec!(self.s, w);
411        len += self.c1.consensus_encode(w)?;
412        len += self.D.consensus_encode(w)?;
413        Ok(len)
414    }
415}
416
417// ====================================================================
418/// Range signature for range commitment.
419#[derive(Debug, Clone, PartialEq, Eq)]
420#[allow(non_snake_case)]
421#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
422#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
423pub struct RangeSig {
424    /// asig value
425    pub asig: BoroSig,
426    /// Ci value
427    pub Ci: Key64,
428}
429
430impl_consensus_encoding!(RangeSig, asig, Ci);
431
432// ====================================================================
433/// Bulletproof format.
434#[derive(Debug, Clone, PartialEq, Eq)]
435#[allow(non_snake_case)]
436#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
437#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
438pub struct Bulletproof {
439    /// A value.
440    pub A: Key,
441    /// S value.
442    pub S: Key,
443    /// T1 value.
444    pub T1: Key,
445    /// T2 value.
446    pub T2: Key,
447    /// taux value.
448    pub taux: Key,
449    /// mu value.
450    pub mu: Key,
451    /// L value.
452    pub L: Vec<Key>,
453    /// R value.
454    pub R: Vec<Key>,
455    /// a value.
456    pub a: Key,
457    /// b value.
458    pub b: Key,
459    /// t value.
460    pub t: Key,
461}
462
463impl_consensus_encoding!(Bulletproof, A, S, T1, T2, taux, mu, L, R, a, b, t);
464
465// ====================================================================
466/// BulletproofPlus format.
467#[derive(Debug, Clone, PartialEq, Eq)]
468#[allow(non_snake_case)]
469#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
470#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
471pub struct BulletproofPlus {
472    /// A value.
473    pub A: Key,
474    /// A1 value.
475    pub A1: Key,
476    /// B value.
477    pub B: Key,
478    /// r1 value.
479    pub r1: Key,
480    /// s1 value.
481    pub s1: Key,
482    /// d1 value.
483    pub d1: Key,
484    /// L value.
485    pub L: Vec<Key>,
486    /// R value.
487    pub R: Vec<Key>,
488}
489
490impl_consensus_encoding!(BulletproofPlus, A, A1, B, r1, s1, d1, L, R);
491
492// ====================================================================
493/// RingCt base signature format.
494#[derive(Debug, Clone, PartialEq, Eq)]
495#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
496#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
497pub struct RctSigBase {
498    /// The RingCt type of signatures.
499    pub rct_type: RctType,
500    /// Transaction fee.
501    #[cfg_attr(feature = "serde", serde(with = "crate::util::amount::serde::as_pico"))]
502    pub txn_fee: Amount,
503    /// Pseudo outs key vector.
504    pub pseudo_outs: Vec<Key>,
505    /// Ecdh info vector.
506    pub ecdh_info: Vec<EcdhInfo>,
507    /// Out pk vector.
508    pub out_pk: Vec<CtKey>,
509}
510
511impl fmt::Display for RctSigBase {
512    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
513        writeln!(fmt, "RCT type: {}", self.rct_type)?;
514        writeln!(fmt, "Tx fee: {}", self.txn_fee)?;
515        for out in &self.pseudo_outs {
516            writeln!(fmt, "Pseudo out: {}", out)?;
517        }
518        for ecdh in &self.ecdh_info {
519            writeln!(fmt, "Ecdh info: {}", ecdh)?;
520        }
521        for out in &self.out_pk {
522            writeln!(fmt, "Out pk: {}", out)?;
523        }
524        Ok(())
525    }
526}
527
528impl RctSigBase {
529    /// Decode a RingCt base signature given the number of inputs and outputs of the transaction.
530    pub fn consensus_decode<R: io::Read + ?Sized>(
531        r: &mut R,
532        inputs: usize,
533        outputs: usize,
534    ) -> Result<Option<RctSigBase>, encode::Error> {
535        let rct_type: RctType = Decodable::consensus_decode(r)?;
536        match rct_type {
537            RctType::Null => Ok(Some(RctSigBase {
538                rct_type: RctType::Null,
539                txn_fee: Default::default(),
540                pseudo_outs: vec![],
541                ecdh_info: vec![],
542                out_pk: vec![],
543            })),
544            RctType::Full
545            | RctType::Simple
546            | RctType::Bulletproof
547            | RctType::Bulletproof2
548            | RctType::Clsag
549            | RctType::BulletproofPlus => {
550                let mut pseudo_outs: Vec<Key> = vec![];
551                // TxnFee
552                let txn_fee: VarInt = Decodable::consensus_decode(r)?;
553                let txn_fee = Amount::from_pico(*txn_fee);
554                // RctType
555                if rct_type == RctType::Simple {
556                    pseudo_outs = consensus_decode_sized_vec(r, inputs)?;
557                }
558                // EcdhInfo
559                let mut ecdh_info: Vec<EcdhInfo> = vec![];
560                for _ in 0..outputs {
561                    ecdh_info.push(EcdhInfo::consensus_decode(r, rct_type)?);
562                }
563                // OutPk
564                let out_pk: Vec<CtKey> = consensus_decode_sized_vec(r, outputs)?;
565                Ok(Some(RctSigBase {
566                    rct_type,
567                    txn_fee,
568                    pseudo_outs,
569                    ecdh_info,
570                    out_pk,
571                }))
572            }
573        }
574    }
575}
576
577#[sealed]
578impl crate::consensus::encode::Encodable for RctSigBase {
579    fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
580        let mut len = 0;
581        len += self.rct_type.consensus_encode(w)?;
582        match self.rct_type {
583            RctType::Null => Ok(len),
584            RctType::Full
585            | RctType::Simple
586            | RctType::Bulletproof
587            | RctType::Bulletproof2
588            | RctType::Clsag
589            | RctType::BulletproofPlus => {
590                let txn_fee: VarInt = VarInt(self.txn_fee.as_pico());
591                len += txn_fee.consensus_encode(w)?;
592                if self.rct_type == RctType::Simple {
593                    len += encode_sized_vec!(self.pseudo_outs, w);
594                }
595                len += encode_sized_vec!(self.ecdh_info, w);
596                len += encode_sized_vec!(self.out_pk, w);
597                Ok(len)
598            }
599        }
600    }
601}
602
603impl hash::Hashable for RctSigBase {
604    fn hash(&self) -> hash::Hash {
605        hash::Hash::new(serialize(self))
606    }
607}
608
609// ====================================================================
610/// Types of Ring Confidential Transaction signatures.
611#[derive(Debug, PartialEq, Eq, Clone, Copy)]
612#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
613#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
614pub enum RctType {
615    /// Null type.
616    Null,
617    /// Full type.
618    Full,
619    /// Simple type.
620    Simple,
621    /// First Bulletproof type.
622    Bulletproof,
623    /// Bulletproof2 type.
624    Bulletproof2,
625    /// Clsag Ring signatures.
626    Clsag,
627    /// Bulletproof+ type, used in the current network.
628    BulletproofPlus,
629}
630
631impl fmt::Display for RctType {
632    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
633        let rct_type = match self {
634            RctType::Null => "Null",
635            RctType::Full => "Full",
636            RctType::Simple => "Simple",
637            RctType::Bulletproof => "Bulletproof",
638            RctType::Bulletproof2 => "Bulletproof2",
639            RctType::Clsag => "Clsag",
640            RctType::BulletproofPlus => "Bulletproof+",
641        };
642        write!(fmt, "{}", rct_type)
643    }
644}
645
646impl RctType {
647    /// Return if the format use one of the bulletproof format.
648    pub fn is_rct_bp(self) -> bool {
649        matches!(
650            self,
651            RctType::Bulletproof | RctType::Bulletproof2 | RctType::Clsag
652        )
653    }
654    /// Return if the format use one of the bulletproofPlus format.
655    pub fn is_rct_bp_plus(self) -> bool {
656        matches!(self, RctType::BulletproofPlus)
657    }
658}
659
660impl Decodable for RctType {
661    fn consensus_decode<R: io::Read + ?Sized>(r: &mut R) -> Result<RctType, encode::Error> {
662        let rct_type: u8 = Decodable::consensus_decode(r)?;
663        match rct_type {
664            0 => Ok(RctType::Null),
665            1 => Ok(RctType::Full),
666            2 => Ok(RctType::Simple),
667            3 => Ok(RctType::Bulletproof),
668            4 => Ok(RctType::Bulletproof2),
669            5 => Ok(RctType::Clsag),
670            6 => Ok(RctType::BulletproofPlus),
671            _ => Err(Error::UnknownRctType.into()),
672        }
673    }
674}
675
676#[sealed]
677impl crate::consensus::encode::Encodable for RctType {
678    fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
679        match self {
680            RctType::Null => 0u8.consensus_encode(w),
681            RctType::Full => 1u8.consensus_encode(w),
682            RctType::Simple => 2u8.consensus_encode(w),
683            RctType::Bulletproof => 3u8.consensus_encode(w),
684            RctType::Bulletproof2 => 4u8.consensus_encode(w),
685            RctType::Clsag => 5u8.consensus_encode(w),
686            RctType::BulletproofPlus => 6u8.consensus_encode(w),
687        }
688    }
689}
690
691// ====================================================================
692/// Prunable part of RingCt signature format.
693#[derive(Debug, Clone, PartialEq, Eq)]
694#[allow(non_snake_case)]
695#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
696#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
697pub struct RctSigPrunable {
698    /// Range signatures.
699    pub range_sigs: Vec<RangeSig>,
700    /// Bulletproofs.
701    pub bulletproofs: Vec<Bulletproof>,
702    /// BulletproofPlus
703    pub bulletproofplus: Vec<BulletproofPlus>,
704    /// MSLAG signatures, simple rct has N, full has 1.
705    pub MGs: Vec<MgSig>,
706    /// CSLAG signatures.
707    pub Clsags: Vec<Clsag>,
708    /// Pseudo out vector.
709    pub pseudo_outs: Vec<Key>,
710}
711
712impl RctSigPrunable {
713    /// Decode a prunable RingCt signature given the number of inputs and outputs in the
714    /// transaction, the RingCt type and the number of mixins.
715    #[allow(non_snake_case)]
716    pub fn consensus_decode<R: io::Read + ?Sized>(
717        r: &mut R,
718        rct_type: RctType,
719        inputs: usize,
720        outputs: usize,
721        mixin: usize,
722    ) -> Result<Option<RctSigPrunable>, encode::Error> {
723        match rct_type {
724            RctType::Null => Ok(None),
725            RctType::Full
726            | RctType::Simple
727            | RctType::Bulletproof
728            | RctType::Bulletproof2
729            | RctType::Clsag
730            | RctType::BulletproofPlus => {
731                let mut bulletproofs: Vec<Bulletproof> = vec![];
732                let mut bulletproofplus: Vec<BulletproofPlus> = vec![];
733                let mut range_sigs: Vec<RangeSig> = vec![];
734                if rct_type.is_rct_bp() {
735                    match rct_type {
736                        RctType::Bulletproof2 | RctType::Clsag => {
737                            bulletproofs = Decodable::consensus_decode(r)?;
738                        }
739                        _ => {
740                            let size: u32 = Decodable::consensus_decode(r)?;
741                            bulletproofs = consensus_decode_sized_vec(r, size as usize)?;
742                        }
743                    }
744                } else if rct_type.is_rct_bp_plus() {
745                    let size: u8 = Decodable::consensus_decode(r)?;
746                    bulletproofplus = consensus_decode_sized_vec(r, size as usize)?;
747                } else {
748                    range_sigs = consensus_decode_sized_vec(r, outputs)?;
749                }
750
751                let mut Clsags: Vec<Clsag> = vec![];
752                let mut MGs: Vec<MgSig> = vec![];
753
754                match rct_type {
755                    RctType::Clsag | RctType::BulletproofPlus => {
756                        for _ in 0..inputs {
757                            let mut s: Vec<Key> = vec![];
758                            for _ in 0..=mixin {
759                                let s_elems: Key = Decodable::consensus_decode(r)?;
760                                s.push(s_elems);
761                            }
762                            let c1 = Decodable::consensus_decode(r)?;
763                            let D = Decodable::consensus_decode(r)?;
764                            Clsags.push(Clsag { s, c1, D });
765                        }
766                    }
767                    _ => {
768                        let is_simple_or_bp = rct_type == RctType::Simple
769                            || rct_type == RctType::Bulletproof
770                            || rct_type == RctType::Bulletproof2;
771                        let mg_elements = if is_simple_or_bp { inputs } else { 1 };
772                        for _ in 0..mg_elements {
773                            let mut ss: Vec<Vec<Key>> = vec![];
774                            for _ in 0..=mixin {
775                                let mg_ss2_elements = if is_simple_or_bp { 2 } else { 1 + inputs };
776                                let ss_elems: Vec<Key> =
777                                    consensus_decode_sized_vec(r, mg_ss2_elements)?;
778                                ss.push(ss_elems);
779                            }
780                            let cc = Decodable::consensus_decode(r)?;
781                            MGs.push(MgSig { ss, cc });
782                        }
783                    }
784                }
785
786                let mut pseudo_outs: Vec<Key> = vec![];
787                match rct_type {
788                    RctType::Bulletproof
789                    | RctType::Bulletproof2
790                    | RctType::Clsag
791                    | RctType::BulletproofPlus => {
792                        pseudo_outs = consensus_decode_sized_vec(r, inputs)?;
793                    }
794                    _ => (),
795                }
796
797                Ok(Some(RctSigPrunable {
798                    range_sigs,
799                    bulletproofs,
800                    bulletproofplus,
801                    MGs,
802                    Clsags,
803                    pseudo_outs,
804                }))
805            }
806        }
807    }
808
809    /// Encode the prunable RingCt signature part given the RingCt type of the transaction.
810    pub fn consensus_encode<W: io::Write + ?Sized>(
811        &self,
812        w: &mut W,
813        rct_type: RctType,
814    ) -> Result<usize, io::Error> {
815        match rct_type {
816            RctType::Null => Ok(0),
817            RctType::Full
818            | RctType::Simple
819            | RctType::Bulletproof
820            | RctType::Bulletproof2
821            | RctType::Clsag
822            | RctType::BulletproofPlus => {
823                let mut len = 0;
824                if rct_type.is_rct_bp() {
825                    match rct_type {
826                        RctType::Bulletproof2 | RctType::Clsag => {
827                            len += self.bulletproofs.consensus_encode(w)?;
828                        }
829                        _ => {
830                            let size: u32 = self.bulletproofs.len() as u32;
831                            len += size.consensus_encode(w)?;
832                            len += encode_sized_vec!(self.bulletproofs, w);
833                        }
834                    }
835                } else if rct_type.is_rct_bp_plus() {
836                    let size: u8 = self.bulletproofplus.len() as u8;
837                    len += size.consensus_encode(w)?;
838                    len += encode_sized_vec!(self.bulletproofplus, w);
839                } else {
840                    len += encode_sized_vec!(self.range_sigs, w);
841                }
842
843                match rct_type {
844                    RctType::Clsag | RctType::BulletproofPlus => {
845                        len += encode_sized_vec!(self.Clsags, w)
846                    }
847                    _ => len += encode_sized_vec!(self.MGs, w),
848                }
849
850                match rct_type {
851                    RctType::Bulletproof
852                    | RctType::Bulletproof2
853                    | RctType::Clsag
854                    | RctType::BulletproofPlus => {
855                        len += encode_sized_vec!(self.pseudo_outs, w);
856                    }
857                    _ => (),
858                }
859                Ok(len)
860            }
861        }
862    }
863}
864
865// ====================================================================
866/// A RingCt signature.
867#[derive(Debug, Clone, Default, PartialEq, Eq)]
868#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
869#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
870pub struct RctSig {
871    /// The base part.
872    pub sig: Option<RctSigBase>,
873    /// The prunable part.
874    pub p: Option<RctSigPrunable>,
875}
876
877impl fmt::Display for RctSig {
878    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
879        match &self.sig {
880            Some(v) => writeln!(fmt, "Signature: {}", v)?,
881            None => writeln!(fmt, "Signature: None")?,
882        };
883        Ok(())
884    }
885}
886
887// ====================================================================
888/// A raw signature.
889#[derive(Debug, Clone, PartialEq, Eq)]
890#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
891#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
892pub struct Signature {
893    /// c value.
894    pub c: Key,
895    /// r value.
896    pub r: Key,
897}
898
899impl fmt::Display for Signature {
900    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
901        writeln!(fmt, "C: {}", self.c)?;
902        writeln!(fmt, "R: {}", self.r)
903    }
904}
905
906impl_consensus_encoding!(Signature, c, r);
907
908#[cfg(test)]
909mod tests {
910    use crate::consensus::Encodable;
911    use crate::util::ringct::{
912        Clsag, CtKey, EcdhInfo, Key, Key64, MgSig, RctSig, RctSigBase, RctSigPrunable, RctType,
913        Signature,
914    };
915    use crate::{Hash, PrivateKey, PublicKey, ViewPair};
916    use curve25519_dalek::traits::Identity;
917    use curve25519_dalek::EdwardsPoint;
918    use std::io::Cursor;
919
920    #[test]
921    fn code_coverage() {
922        assert_eq!(Key64::new(), Key64::from([Key::new(); 64]));
923        assert_eq!(
924            format!("{}", Key64::new()),
925            "0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n0000000000000000000000000000000000000000000000000000000000000000\n"
926        );
927        assert_eq!(
928            format!(
929                "{}",
930                CtKey {
931                    mask: Default::default()
932                }
933            ),
934            "Mask: 0000000000000000000000000000000000000000000000000000000000000000\n"
935        );
936        let ecd_info_a = EcdhInfo::Standard {
937            mask: Key {
938                key: Hash::new("a").to_bytes(),
939            },
940            amount: Key {
941                key: Hash::new("b").to_bytes(),
942            },
943        };
944
945        let opening_a = ecd_info_a.open_commitment(
946            &ViewPair {
947                view: PrivateKey {
948                    scalar: Default::default(),
949                },
950                spend: PublicKey {
951                    point: Default::default(),
952                },
953            },
954            &PublicKey {
955                point: Default::default(),
956            },
957            0,
958            &EdwardsPoint::identity(),
959        );
960        assert_eq!(format!("{:?}", opening_a), "None");
961        assert_eq!(
962            format!("{}", ecd_info_a),
963            "Standard\nMask: 3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb\nAmount: b5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510\n"
964        );
965        let mut encoder = Cursor::new(vec![]);
966        ecd_info_a.consensus_encode(&mut encoder).unwrap();
967        let res_a = EcdhInfo::consensus_decode(&mut encoder, RctType::Full).unwrap_err();
968        // Note: `EcdhInfo::consensus_decode` fails where it should not !!
969        assert_eq!(res_a.to_string(), "IO error: failed to fill whole buffer");
970
971        let ecd_info_b = EcdhInfo::Bulletproof {
972            amount: Default::default(),
973        };
974        let opening_b = ecd_info_b.open_commitment(
975            &ViewPair {
976                view: PrivateKey {
977                    scalar: Default::default(),
978                },
979                spend: PublicKey {
980                    point: Default::default(),
981                },
982            },
983            &PublicKey {
984                point: Default::default(),
985            },
986            0,
987            &EdwardsPoint::identity(),
988        );
989        assert_eq!(format!("{:?}", opening_b), "None");
990        assert_eq!(
991            format!("{}", ecd_info_b),
992            "Bulletproof2\nAmount: 0x0000…0000\n"
993        );
994        let mut encoder = Cursor::new(vec![]);
995        ecd_info_b.consensus_encode(&mut encoder).unwrap();
996        let res_b = EcdhInfo::consensus_decode(&mut encoder, RctType::Bulletproof).unwrap_err();
997        // Note: `EcdhInfo::consensus_decode` fails where it should not !!
998        assert_eq!(res_b.to_string(), "IO error: failed to fill whole buffer");
999
1000        let rct_base_a = RctSigBase {
1001            rct_type: RctType::Null,
1002            txn_fee: Default::default(),
1003            pseudo_outs: vec![],
1004            ecdh_info: vec![],
1005            out_pk: vec![],
1006        };
1007        assert_eq!(
1008            format!("{}", rct_base_a),
1009            "RCT type: Null\nTx fee: 0.000000000000 xmr\n"
1010        );
1011        let mut encoder = Cursor::new(vec![]);
1012        rct_base_a.consensus_encode(&mut encoder).unwrap();
1013        let res = RctSigBase::consensus_decode(&mut encoder, 0, 0).unwrap_err();
1014        // Note: `EcdhInfo::consensus_decode` fails where it should not !!
1015        assert_eq!(res.to_string(), "IO error: failed to fill whole buffer");
1016
1017        let rct_base_b = RctSigBase {
1018            rct_type: RctType::Full,
1019            txn_fee: Default::default(),
1020            pseudo_outs: vec![],
1021            ecdh_info: vec![],
1022            out_pk: vec![],
1023        };
1024        assert_eq!(
1025            format!("{}", rct_base_b),
1026            "RCT type: Full\nTx fee: 0.000000000000 xmr\n"
1027        );
1028        let mut encoder = Cursor::new(vec![]);
1029        rct_base_b.consensus_encode(&mut encoder).unwrap();
1030        let res = RctSigBase::consensus_decode(&mut encoder, 0, 0).unwrap_err();
1031        // Note: `RctSigBase::consensus_decode` fails where it should not !!
1032        assert_eq!(res.to_string(), "IO error: failed to fill whole buffer");
1033
1034        let sig = RctSigPrunable {
1035            range_sigs: vec![],
1036            bulletproofs: vec![],
1037            bulletproofplus: vec![],
1038            MGs: vec![MgSig {
1039                ss: vec![vec![Key::new()]],
1040                cc: Key::new(),
1041            }],
1042            Clsags: vec![Clsag {
1043                s: vec![Key::new()],
1044                c1: Key::new(),
1045                D: Key::new(),
1046            }],
1047            pseudo_outs: vec![Key::new()],
1048        };
1049        assert_eq!(
1050            format!("{:?}", sig),
1051            "RctSigPrunable { range_sigs: [], bulletproofs: [], bulletproofplus: [], MGs: [MgSig { ss: [[0000000000000000000000000000000000000000000000000000000000000000]], cc: 0000000000000000000000000000000000000000000000000000000000000000 }], Clsags: [Clsag { s: [0000000000000000000000000000000000000000000000000000000000000000], c1: 0000000000000000000000000000000000000000000000000000000000000000, D: 0000000000000000000000000000000000000000000000000000000000000000 }], pseudo_outs: [0000000000000000000000000000000000000000000000000000000000000000] }"
1052        );
1053
1054        let mut buffer = Vec::new();
1055        let mut encoder = Cursor::new(&mut buffer);
1056        sig.consensus_encode(&mut encoder, RctType::Null).unwrap();
1057        assert!(buffer.is_empty());
1058        let res =
1059            RctSigPrunable::consensus_decode(&mut Cursor::new(&buffer), RctType::Null, 0, 0, 0)
1060                .unwrap();
1061        assert!(res.is_none());
1062
1063        for rct_type in [
1064            RctType::Full,
1065            RctType::Bulletproof,
1066            RctType::BulletproofPlus,
1067            RctType::Bulletproof2,
1068            RctType::Simple,
1069            RctType::Clsag,
1070        ] {
1071            let mut buffer = Vec::new();
1072            let mut encoder = Cursor::new(&mut buffer);
1073            sig.consensus_encode(&mut encoder, rct_type).unwrap();
1074            assert!(!buffer.is_empty());
1075            let res =
1076                RctSigPrunable::consensus_decode(&mut Cursor::new(&buffer), rct_type, 0, 0, 0);
1077            assert!(res.is_ok());
1078            assert!(res.unwrap().is_some());
1079        }
1080
1081        assert_eq!(
1082            format!("{}", RctSig { sig: None, p: None }),
1083            "Signature: None\n"
1084        );
1085        assert_eq!(
1086            format!("{}", Signature { c: Default::default(), r: Default::default() }),
1087            "C: 0000000000000000000000000000000000000000000000000000000000000000\nR: 0000000000000000000000000000000000000000000000000000000000000000\n"
1088        );
1089    }
1090}