1use 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#[derive(Error, Debug, PartialEq, Eq)]
49pub enum Error {
50 #[error("Unknown RingCt type")]
52 UnknownRctType,
53}
54
55#[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 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#[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 #[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#[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 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#[derive(Debug)]
159#[allow(non_snake_case)]
160pub struct MultisigKlrki {
161 pub K: Key,
163 pub L: Key,
165 pub R: Key,
167 pub ki: Key,
169}
170
171impl_consensus_encoding!(MultisigKlrki, K, L, R, ki);
172
173#[derive(Debug)]
176pub struct MultisigOut {
177 pub c: Vec<Key>,
179}
180
181impl_consensus_encoding!(MultisigOut, c);
182
183#[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 {
192 mask: Key,
194 amount: Key,
196 },
197 Bulletproof {
199 amount: hash::Hash8,
201 },
202}
203
204impl EcdhInfo {
205 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 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 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 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#[derive(Debug)]
260pub struct Opening {
261 pub amount: Amount,
263 pub blinding_factor: Scalar,
265 pub commitment: EdwardsPoint,
267}
268
269fn xor_amount(amount: [u8; 8], shared_key: Scalar) -> [u8; 8] {
270 let mut amount_key = b"amount".to_vec();
272 amount_key.extend(shared_key.as_bytes());
273
274 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 (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 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 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#[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 pub s0: Key64,
358 pub s1: Key64,
360 pub ee: Key,
362}
363
364impl_consensus_encoding!(BoroSig, s0, s1, ee);
365
366#[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 pub ss: Vec<Vec<Key>>,
374 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#[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 pub s: Vec<Key>,
399 pub c1: Key,
401 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 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#[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 pub asig: BoroSig,
426 pub Ci: Key64,
428}
429
430impl_consensus_encoding!(RangeSig, asig, Ci);
431
432#[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 pub A: Key,
441 pub S: Key,
443 pub T1: Key,
445 pub T2: Key,
447 pub taux: Key,
449 pub mu: Key,
451 pub L: Vec<Key>,
453 pub R: Vec<Key>,
455 pub a: Key,
457 pub b: Key,
459 pub t: Key,
461}
462
463impl_consensus_encoding!(Bulletproof, A, S, T1, T2, taux, mu, L, R, a, b, t);
464
465#[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 pub A: Key,
474 pub A1: Key,
476 pub B: Key,
478 pub r1: Key,
480 pub s1: Key,
482 pub d1: Key,
484 pub L: Vec<Key>,
486 pub R: Vec<Key>,
488}
489
490impl_consensus_encoding!(BulletproofPlus, A, A1, B, r1, s1, d1, L, R);
491
492#[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 pub rct_type: RctType,
500 #[cfg_attr(feature = "serde", serde(with = "crate::util::amount::serde::as_pico"))]
502 pub txn_fee: Amount,
503 pub pseudo_outs: Vec<Key>,
505 pub ecdh_info: Vec<EcdhInfo>,
507 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 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 let txn_fee: VarInt = Decodable::consensus_decode(r)?;
553 let txn_fee = Amount::from_pico(*txn_fee);
554 if rct_type == RctType::Simple {
556 pseudo_outs = consensus_decode_sized_vec(r, inputs)?;
557 }
558 let mut ecdh_info: Vec<EcdhInfo> = vec![];
560 for _ in 0..outputs {
561 ecdh_info.push(EcdhInfo::consensus_decode(r, rct_type)?);
562 }
563 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#[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,
617 Full,
619 Simple,
621 Bulletproof,
623 Bulletproof2,
625 Clsag,
627 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 pub fn is_rct_bp(self) -> bool {
649 matches!(
650 self,
651 RctType::Bulletproof | RctType::Bulletproof2 | RctType::Clsag
652 )
653 }
654 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#[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 pub range_sigs: Vec<RangeSig>,
700 pub bulletproofs: Vec<Bulletproof>,
702 pub bulletproofplus: Vec<BulletproofPlus>,
704 pub MGs: Vec<MgSig>,
706 pub Clsags: Vec<Clsag>,
708 pub pseudo_outs: Vec<Key>,
710}
711
712impl RctSigPrunable {
713 #[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 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#[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 pub sig: Option<RctSigBase>,
873 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#[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 pub c: Key,
895 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 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 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 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 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}