doc ringct/mod.rs

This commit is contained in:
hinto.janai
2024-05-16 19:06:59 -04:00
parent 58fe79da10
commit d4b22e5136
4 changed files with 158 additions and 31 deletions

View File

@@ -15,8 +15,8 @@ const CORRECT_BLOCK_HASH_202612: [u8; 32] =
const EXISTING_BLOCK_HASH_202612: [u8; 32] =
hex_literal::hex!("bbd604d2ba11ba27935e006ed39c9bfdd99b76bf4a50654bc1e1e61217962698");
#[derive(Clone, PartialEq, Eq, Debug)]
/// The header of a [`Block`].
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct BlockHeader {
/// This represents the hardfork number of the block.
pub major_version: u8,
@@ -60,9 +60,7 @@ impl BlockHeader {
w.write_all(&self.nonce.to_le_bytes())
}
/// Serialize [`Self`].
///
/// This allocates and returns a new buffer containing the serialized bytes.
/// Serialize [`Self`] into a new byte buffer.
///
/// # Example
/// ```rust
@@ -125,8 +123,8 @@ impl BlockHeader {
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
/// Block on the Monero blockchain.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Block {
/// The header of this block.
pub header: BlockHeader,
@@ -197,9 +195,7 @@ impl Block {
hash
}
/// Serialize [`Self`].
///
/// This allocates and returns a new buffer containing the serialized bytes.
/// Serialize [`Self`] into a new byte buffer.
pub fn serialize(&self) -> Vec<u8> {
let mut serialized = vec![];
self.write(&mut serialized).unwrap();

View File

@@ -93,19 +93,34 @@ pub(crate) fn BASEPOINT_PRECOMP() -> &'static VartimeEdwardsPrecomputation {
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
#[allow(non_camel_case_types)]
pub enum Protocol {
/// Version 14.
v14,
/// Version 16.
v16,
/// A custom version with customized properties.
Custom {
/// See [`Self::ring_len`].
ring_len: usize,
/// See [`Self::bp_plus`].
bp_plus: bool,
/// See [`Self::optimal_rct_type`].
optimal_rct_type: RctType,
/// See [`Self::view_tags`].
view_tags: bool,
/// See [`Self::v16_fee`].
v16_fee: bool,
},
}
impl Protocol {
/// Amount of ring members under this protocol version.
///
/// # Example
/// ```rust
/// # use monero_serai::*;
/// assert_eq!(Protocol::v14.ring_len(), 11);
/// assert_eq!(Protocol::v16.ring_len(), 16);
/// ```
pub fn ring_len(&self) -> usize {
match self {
Protocol::v14 => 11,
@@ -117,6 +132,13 @@ impl Protocol {
/// Whether or not the specified version uses Bulletproofs or Bulletproofs+.
///
/// This method will likely be reworked when versions not using Bulletproofs at all are added.
///
/// # Example
/// ```rust
/// # use monero_serai::*;
/// assert_eq!(Protocol::v14.bp_plus(), false);
/// assert_eq!(Protocol::v16.bp_plus(), true);
/// ```
pub fn bp_plus(&self) -> bool {
match self {
Protocol::v14 => false,
@@ -125,6 +147,14 @@ impl Protocol {
}
}
/// The optimal RingCT type for this version.
///
/// # Example
/// ```rust
/// # use monero_serai::{*, ringct::*};
/// assert_eq!(Protocol::v14.optimal_rct_type(), RctType::Clsag);
/// assert_eq!(Protocol::v16.optimal_rct_type(), RctType::BulletproofsPlus);
/// ```
// TODO: Make this an Option when we support pre-RCT protocols
pub fn optimal_rct_type(&self) -> RctType {
match self {
@@ -135,6 +165,13 @@ impl Protocol {
}
/// Whether or not the specified version uses view tags.
///
/// # Example
/// ```rust
/// # use monero_serai::{*, ringct::*};
/// assert_eq!(Protocol::v14.view_tags(), false);
/// assert_eq!(Protocol::v16.view_tags(), true);
/// ```
pub fn view_tags(&self) -> bool {
match self {
Protocol::v14 => false,
@@ -145,6 +182,13 @@ impl Protocol {
/// Whether or not the specified version uses the fee algorithm from Monero
/// hard fork version 16 (released in v18 binaries).
///
/// # Example
/// ```rust
/// # use monero_serai::{*, ringct::*};
/// assert_eq!(Protocol::v14.v16_fee(), false);
/// assert_eq!(Protocol::v16.v16_fee(), true);
/// ```
pub fn v16_fee(&self) -> bool {
match self {
Protocol::v14 => false,
@@ -206,11 +250,15 @@ impl Protocol {
}
}
/// Transparent structure representing a Pedersen commitment's contents.
/// Transparent structure representing a [https://web.getmonero.org/resources/moneropedia/pedersen-commitment.html]()'s contents.
#[allow(non_snake_case)]
#[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
pub struct Commitment {
/// The value used to mask the `amount`.
pub mask: Scalar,
/// The value being masked.
///
/// In Monero's case, this is the amount of XMR in atomic units.
pub amount: u64,
}
@@ -226,6 +274,7 @@ impl Commitment {
Commitment { mask: Scalar::ONE, amount: 0 }
}
/// Create a new [`Self`].
pub fn new(mask: Scalar, amount: u64) -> Commitment {
Commitment { mask, amount }
}

View File

@@ -11,6 +11,7 @@ use monero_generators::hash_to_point;
use crate::{serialize::*, hash_to_scalar};
/// A signature within a [`RingSignature`].
#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
pub struct Signature {
c: Scalar,
@@ -18,23 +19,37 @@ pub struct Signature {
}
impl Signature {
/// Serialize [`Self`] into the writer `w`.
///
/// # Errors
/// This function returns any errors from the writer itself.
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
write_scalar(&self.c, w)?;
write_scalar(&self.r, w)?;
Ok(())
}
/// Create [`Self`] from the reader `r`.
///
/// # Errors
/// This function returns an error if either the reader failed,
/// or if the data could not be deserialized into a [`Self`].
pub fn read<R: Read>(r: &mut R) -> io::Result<Signature> {
Ok(Signature { c: read_scalar(r)?, r: read_scalar(r)? })
}
}
/// A [ring signature](https://en.wikipedia.org/wiki/Ring_signature).
#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
pub struct RingSignature {
sigs: Vec<Signature>,
}
impl RingSignature {
/// Serialize [`Self`] into the writer `w`.
///
/// # Errors
/// This function returns any errors from the writer itself.
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
for sig in &self.sigs {
sig.write(w)?;
@@ -42,6 +57,11 @@ impl RingSignature {
Ok(())
}
/// Create [`Self`] from the reader `r`.
///
/// # Errors
/// This function returns an error if either the reader failed,
/// or if the data could not be deserialized into a [`Self`].
pub fn read<R: Read>(members: usize, r: &mut R) -> io::Result<RingSignature> {
Ok(RingSignature { sigs: read_raw_vec(Signature::read, members, r)? })
}

View File

@@ -81,7 +81,18 @@ pub enum RctType {
}
impl RctType {
/// Convert the RctType to its byte representation.
/// Convert [`self`] to its byte representation.
///
/// ```rust
/// # use monero_serai::ringct::*;
/// assert_eq!(RctType::Null.to_bytes(), 0);
/// assert_eq!(RctType::MlsagAggregate.to_bytes(), 1);
/// assert_eq!(RctType::MlsagIndividual.to_bytes(), 2);
/// assert_eq!(RctType::Bulletproofs.to_bytes(), 3);
/// assert_eq!(RctType::BulletproofsCompactAmount.to_bytes(), 4);
/// assert_eq!(RctType::Clsag.to_bytes(), 5);
/// assert_eq!(RctType::BulletproofsPlus.to_bytes(), 6);
/// ```
pub fn to_byte(self) -> u8 {
match self {
RctType::Null => 0,
@@ -94,7 +105,25 @@ impl RctType {
}
}
/// Convert the RctType from its byte representation.
/// Create [`Self`] from a byte representation.
///
/// ```rust
/// # use monero_serai::ringct::*;
/// assert_eq!(RctType::from_bytes(0).unwrap(), RctType::Null);
/// assert_eq!(RctType::from_bytes(1).unwrap(), RctType::MlsagAggregate);
/// assert_eq!(RctType::from_bytes(2).unwrap(), RctType::MlsagIndividual);
/// assert_eq!(RctType::from_bytes(3).unwrap(), RctType::Bulletproofs);
/// assert_eq!(RctType::from_bytes(4).unwrap(), RctType::BulletproofsCompactAmount);
/// assert_eq!(RctType::from_bytes(5).unwrap(), RctType::Clsag);
/// assert_eq!(RctType::from_bytes(6).unwrap(), RctType::BulletproofsPlus);
/// ```
///
/// # Errors
/// This function returns [`None`] if the byte representation is invalid.
/// ```rust
/// # use monero_serai::ringct::*;
/// assert_eq!(RctType::from_bytes(7), None);
/// ```
pub fn from_byte(byte: u8) -> Option<Self> {
Some(match byte {
0 => RctType::Null,
@@ -109,12 +138,23 @@ impl RctType {
}
/// Returns true if this RctType uses compact encrypted amounts, false otherwise.
///
/// ```rust
/// # use monero_serai::ringct::*;
/// assert_eq!(RctType::Null.compact_encrypted_amounts(), false);
/// assert_eq!(RctType::MlsagAggregate.compact_encrypted_amounts(), false);
/// assert_eq!(RctType::MlsagIndividual.compact_encrypted_amounts(), false);
/// assert_eq!(RctType::Bulletproofs.compact_encrypted_amounts(), false);
/// assert_eq!(RctType::BulletproofsCompactAmount.compact_encrypted_amounts(), true);
/// assert_eq!(RctType::Clsag.compact_encrypted_amounts(), true);
/// assert_eq!(RctType::BulletproofsPlus.compact_encrypted_amounts(), true);
/// ```
pub fn compact_encrypted_amounts(&self) -> bool {
match self {
RctType::Null |
RctType::MlsagAggregate |
RctType::MlsagIndividual |
RctType::Bulletproofs => false,
RctType::Null
| RctType::MlsagAggregate
| RctType::MlsagIndividual
| RctType::Bulletproofs => false,
RctType::BulletproofsCompactAmount | RctType::Clsag | RctType::BulletproofsPlus => true,
}
}
@@ -170,10 +210,10 @@ impl RctBase {
match rct_type {
RctType::Null | RctType::MlsagAggregate | RctType::MlsagIndividual => {}
RctType::Bulletproofs |
RctType::BulletproofsCompactAmount |
RctType::Clsag |
RctType::BulletproofsPlus => {
RctType::Bulletproofs
| RctType::BulletproofsCompactAmount
| RctType::Clsag
| RctType::BulletproofsPlus => {
if outputs == 0 {
// Because the Bulletproofs(+) layout must be canonical, there must be 1 Bulletproof if
// Bulletproofs are in use
@@ -198,7 +238,7 @@ impl RctBase {
} else {
vec![]
},
encrypted_amounts: (0 .. outputs)
encrypted_amounts: (0..outputs)
.map(|_| EncryptedAmount::read(rct_type.compact_encrypted_amounts(), r))
.collect::<Result<_, _>>()?,
commitments: read_raw_vec(read_point, outputs, r)?,
@@ -209,6 +249,7 @@ impl RctBase {
}
}
/// The prunable portion of the RingCT data.
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum RctPrunable {
Null,
@@ -235,10 +276,14 @@ pub enum RctPrunable {
impl RctPrunable {
pub(crate) fn fee_weight(protocol: Protocol, inputs: usize, outputs: usize) -> usize {
// 1 byte for number of BPs (technically a VarInt, yet there's always just zero or one)
1 + Bulletproof::fee_weight(protocol.bp_plus(), outputs) +
(inputs * (Clsag::fee_weight(protocol.ring_len()) + 32))
1 + Bulletproof::fee_weight(protocol.bp_plus(), outputs)
+ (inputs * (Clsag::fee_weight(protocol.ring_len()) + 32))
}
/// Serialize [`Self`] into the writer `w`.
///
/// # Errors
/// This function returns any errors from the writer itself.
pub fn write<W: Write>(&self, w: &mut W, rct_type: RctType) -> io::Result<()> {
match self {
RctPrunable::Null => Ok(()),
@@ -271,12 +316,18 @@ impl RctPrunable {
}
}
/// Serialize [`Self`] into a new byte buffer.
pub fn serialize(&self, rct_type: RctType) -> Vec<u8> {
let mut serialized = vec![];
self.write(&mut serialized, rct_type).unwrap();
serialized
}
/// Create [`Self`] from the reader `r`.
///
/// # Errors
/// This function returns an error if either the reader failed,
/// or if the data could not be deserialized into a [`Self`].
pub fn read<R: Read>(
rct_type: RctType,
ring_length: usize,
@@ -303,7 +354,7 @@ impl RctPrunable {
},
RctType::MlsagIndividual => RctPrunable::MlsagBorromean {
borromean: read_raw_vec(BorromeanRange::read, outputs, r)?,
mlsags: (0 .. inputs).map(|_| Mlsag::read(ring_length, 2, r)).collect::<Result<_, _>>()?,
mlsags: (0..inputs).map(|_| Mlsag::read(ring_length, 2, r)).collect::<Result<_, _>>()?,
},
RctType::Bulletproofs | RctType::BulletproofsCompactAmount => {
RctPrunable::MlsagBulletproofs {
@@ -318,9 +369,7 @@ impl RctPrunable {
}
Bulletproof::read(r)?
},
mlsags: (0 .. inputs)
.map(|_| Mlsag::read(ring_length, 2, r))
.collect::<Result<_, _>>()?,
mlsags: (0..inputs).map(|_| Mlsag::read(ring_length, 2, r)).collect::<Result<_, _>>()?,
pseudo_outs: read_raw_vec(read_point, inputs, r)?,
}
}
@@ -331,7 +380,7 @@ impl RctPrunable {
}
(if rct_type == RctType::Clsag { Bulletproof::read } else { Bulletproof::read_plus })(r)?
},
clsags: (0 .. inputs).map(|_| Clsag::read(ring_length, r)).collect::<Result<_, _>>()?,
clsags: (0..inputs).map(|_| Clsag::read(ring_length, r)).collect::<Result<_, _>>()?,
pseudo_outs: read_raw_vec(read_point, inputs, r)?,
},
})
@@ -340,19 +389,22 @@ impl RctPrunable {
pub(crate) fn signature_write<W: Write>(&self, w: &mut W) -> io::Result<()> {
match self {
RctPrunable::Null => panic!("Serializing RctPrunable::Null for a signature"),
RctPrunable::AggregateMlsagBorromean { borromean, .. } |
RctPrunable::MlsagBorromean { borromean, .. } => {
RctPrunable::AggregateMlsagBorromean { borromean, .. }
| RctPrunable::MlsagBorromean { borromean, .. } => {
borromean.iter().try_for_each(|rs| rs.write(w))
}
RctPrunable::MlsagBulletproofs { bulletproofs, .. } |
RctPrunable::Clsag { bulletproofs, .. } => bulletproofs.signature_write(w),
RctPrunable::MlsagBulletproofs { bulletproofs, .. }
| RctPrunable::Clsag { bulletproofs, .. } => bulletproofs.signature_write(w),
}
}
}
/// RingCT signature data.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct RctSignatures {
/// The base of the RingCT data.
pub base: RctBase,
/// The prunable portion of the RingCT data.
pub prunable: RctPrunable,
}
@@ -393,18 +445,28 @@ impl RctSignatures {
RctBase::fee_weight(outputs, fee) + RctPrunable::fee_weight(protocol, inputs, outputs)
}
/// Serialize [`Self`] into the writer `w`.
///
/// # Errors
/// This function returns any errors from the writer itself.
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
let rct_type = self.rct_type();
self.base.write(w, rct_type)?;
self.prunable.write(w, rct_type)
}
/// Serialize [`Self`] into a new byte buffer.
pub fn serialize(&self) -> Vec<u8> {
let mut serialized = vec![];
self.write(&mut serialized).unwrap();
serialized
}
/// Create [`Self`] from the reader `r` and other data.
///
/// # Errors
/// This function returns an error if either the reader failed,
/// or if the data could not be deserialized into a [`Self`].
pub fn read<R: Read>(
ring_length: usize,
inputs: usize,