mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-10 13:09:24 +00:00
doc ringct/mod.rs
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
@@ -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)? })
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user