Clean the Monero lib for auditing (#577)
* Remove unsafe creation of dalek_ff_group::EdwardsPoint in BP+
* Rename Bulletproofs to Bulletproof, since they are a single Bulletproof
Also bifurcates prove with prove_plus, and adds a few documentation items.
* Make CLSAG signing private
Also adds a bit more documentation and does a bit more tidying.
* Remove the distribution cache
It's a notable bandwidth/performance improvement, yet it's not ready. We need a
dedicated Distribution struct which is managed by the wallet and passed in.
While we can do that now, it's not currently worth the effort.
* Tidy Borromean/MLSAG a tad
* Remove experimental feature from monero-serai
* Move amount_decryption into EncryptedAmount::decrypt
* Various RingCT doc comments
* Begin crate smashing
* Further documentation, start shoring up API boundaries of existing crates
* Document and clean clsag
* Add a dedicated send/recv CLSAG mask struct
Abstracts the types used internally.
Also moves the tests from monero-serai to monero-clsag.
* Smash out monero-bulletproofs
Removes usage of dalek-ff-group/multiexp for curve25519-dalek.
Makes compiling in the generators an optional feature.
Adds a structured batch verifier which should be notably more performant.
Documentation and clean up still necessary.
* Correct no-std builds for monero-clsag and monero-bulletproofs
* Tidy and document monero-bulletproofs
I still don't like the impl of the original Bulletproofs...
* Error if missing documentation
* Smash out MLSAG
* Smash out Borromean
* Tidy up monero-serai as a meta crate
* Smash out RPC, wallet
* Document the RPC
* Improve docs a bit
* Move Protocol to monero-wallet
* Incomplete work on using Option to remove panic cases
* Finish documenting monero-serai
* Remove TODO on reading pseudo_outs for AggregateMlsagBorromean
* Only read transactions with one Input::Gen or all Input::ToKey
Also adds a helper to fetch a transaction's prefix.
* Smash out polyseed
* Smash out seed
* Get the repo to compile again
* Smash out Monero addresses
* Document cargo features
Credit to @hinto-janai for adding such sections to their work on documenting
monero-serai in #568.
* Fix deserializing v2 miner transactions
* Rewrite monero-wallet's send code
I have yet to redo the multisig code and the builder. This should be much
cleaner, albeit slower due to redoing work.
This compiles with clippy --all-features. I have to finish the multisig/builder
for --all-targets to work (and start updating the rest of Serai).
* Add SignableTransaction Read/Write
* Restore Monero multisig TX code
* Correct invalid RPC type def in monero-rpc
* Update monero-wallet tests to compile
Some are _consistently_ failing due to the inputs we attempt to spend being too
young. I'm unsure what's up with that. Most seem to pass _consistently_,
implying it's not a random issue yet some configuration/env aspect.
* Clean and document monero-address
* Sync rest of repo with monero-serai changes
* Represent height/block number as a u32
* Diversify ViewPair/Scanner into ViewPair/GuaranteedViewPair and Scanner/GuaranteedScanner
Also cleans the Scanner impl.
* Remove non-small-order view key bound
Guaranteed addresses are in fact guaranteed even with this due to prefixing key
images causing zeroing the ECDH to not zero the shared key.
* Finish documenting monero-serai
* Correct imports for no-std
* Remove possible panic in monero-serai on systems < 32 bits
This was done by requiring the system's usize can represent a certain number.
* Restore the reserialize chain binary
* fmt, machete, GH CI
* Correct misc TODOs in monero-serai
* Have Monero test runner evaluate an Eventuality for all signed TXs
* Fix a pair of bugs in the decoy tests
Unfortunately, this test is still failing.
* Fix remaining bugs in monero-wallet tests
* Reject torsioned spend keys to ensure we can spend the outputs we scan
* Tidy inlined epee code in the RPC
* Correct the accidental swap of stagenet/testnet address bytes
* Remove unused dep from processor
* Handle Monero fee logic properly in the processor
* Document v2 TX/RCT output relation assumed when scanning
* Adjust how we mine the initial blocks due to some CI test failures
* Fix weight estimation for RctType::ClsagBulletproof TXs
* Again increase the amount of blocks we mine prior to running tests
* Correct the if check about when to mine blocks on start
Finally fixes the lack of decoy candidates failures in CI.
* Run Monero on Debian, even for internal testnets
Change made due to a segfault incurred when locally testing.
https://github.com/monero-project/monero/issues/9141 for the upstream.
* Don't attempt running tests on the verify-chain binary
Adds a minimum XMR fee to the processor and runs fmt.
* Increase minimum Monero fee in processor
I'm truly unsure why this is required right now.
* Distinguish fee from necessary_fee in monero-wallet
If there's no change, the fee is difference of the inputs to the outputs. The
prior code wouldn't check that amount is greater than or equal to the necessary
fee, and returning the would-be change amount as the fee isn't necessarily
helpful.
Now the fee is validated in such cases and the necessary fee is returned,
enabling operating off of that.
* Restore minimum Monero fee from develop
2024-07-07 03:57:18 -07:00
|
|
|
use std_shims::{
|
|
|
|
|
vec,
|
|
|
|
|
vec::Vec,
|
|
|
|
|
io::{self, Read, Write},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
use zeroize::Zeroize;
|
|
|
|
|
|
|
|
|
|
use curve25519_dalek::edwards::EdwardsPoint;
|
|
|
|
|
|
|
|
|
|
pub use monero_mlsag as mlsag;
|
|
|
|
|
pub use monero_clsag as clsag;
|
|
|
|
|
pub use monero_borromean as borromean;
|
|
|
|
|
pub use monero_bulletproofs as bulletproofs;
|
|
|
|
|
|
|
|
|
|
use crate::{
|
|
|
|
|
io::*,
|
|
|
|
|
ringct::{mlsag::Mlsag, clsag::Clsag, borromean::BorromeanRange, bulletproofs::Bulletproof},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// An encrypted amount.
|
|
|
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
|
|
|
pub enum EncryptedAmount {
|
|
|
|
|
/// The original format for encrypted amounts.
|
|
|
|
|
Original {
|
|
|
|
|
/// A mask used with a mask derived from the shared secret to encrypt the amount.
|
|
|
|
|
mask: [u8; 32],
|
|
|
|
|
/// The amount, as a scalar, encrypted.
|
|
|
|
|
amount: [u8; 32],
|
|
|
|
|
},
|
|
|
|
|
/// The "compact" format for encrypted amounts.
|
|
|
|
|
Compact {
|
|
|
|
|
/// The amount, as a u64, encrypted.
|
|
|
|
|
amount: [u8; 8],
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl EncryptedAmount {
|
|
|
|
|
/// Read an EncryptedAmount from a reader.
|
|
|
|
|
pub fn read<R: Read>(compact: bool, r: &mut R) -> io::Result<EncryptedAmount> {
|
|
|
|
|
Ok(if !compact {
|
|
|
|
|
EncryptedAmount::Original { mask: read_bytes(r)?, amount: read_bytes(r)? }
|
|
|
|
|
} else {
|
|
|
|
|
EncryptedAmount::Compact { amount: read_bytes(r)? }
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Write the EncryptedAmount to a writer.
|
|
|
|
|
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
|
|
|
|
match self {
|
|
|
|
|
EncryptedAmount::Original { mask, amount } => {
|
|
|
|
|
w.write_all(mask)?;
|
|
|
|
|
w.write_all(amount)
|
|
|
|
|
}
|
|
|
|
|
EncryptedAmount::Compact { amount } => w.write_all(amount),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// The type of the RingCT data.
|
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
|
|
|
|
pub enum RctType {
|
|
|
|
|
/// One MLSAG for multiple inputs and Borromean range proofs.
|
|
|
|
|
///
|
|
|
|
|
/// This aligns with RCTTypeFull.
|
|
|
|
|
AggregateMlsagBorromean,
|
|
|
|
|
// One MLSAG for each input and a Borromean range proof.
|
|
|
|
|
///
|
|
|
|
|
/// This aligns with RCTTypeSimple.
|
|
|
|
|
MlsagBorromean,
|
|
|
|
|
// One MLSAG for each input and a Bulletproof.
|
|
|
|
|
///
|
|
|
|
|
/// This aligns with RCTTypeBulletproof.
|
|
|
|
|
MlsagBulletproofs,
|
|
|
|
|
/// One MLSAG for each input and a Bulletproof, yet using EncryptedAmount::Compact.
|
|
|
|
|
///
|
|
|
|
|
/// This aligns with RCTTypeBulletproof2.
|
|
|
|
|
MlsagBulletproofsCompactAmount,
|
|
|
|
|
/// One CLSAG for each input and a Bulletproof.
|
|
|
|
|
///
|
|
|
|
|
/// This aligns with RCTTypeCLSAG.
|
|
|
|
|
ClsagBulletproof,
|
|
|
|
|
/// One CLSAG for each input and a Bulletproof+.
|
|
|
|
|
///
|
|
|
|
|
/// This aligns with RCTTypeBulletproofPlus.
|
|
|
|
|
ClsagBulletproofPlus,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<RctType> for u8 {
|
2024-07-13 00:29:02 -04:00
|
|
|
fn from(rct_type: RctType) -> u8 {
|
|
|
|
|
match rct_type {
|
Clean the Monero lib for auditing (#577)
* Remove unsafe creation of dalek_ff_group::EdwardsPoint in BP+
* Rename Bulletproofs to Bulletproof, since they are a single Bulletproof
Also bifurcates prove with prove_plus, and adds a few documentation items.
* Make CLSAG signing private
Also adds a bit more documentation and does a bit more tidying.
* Remove the distribution cache
It's a notable bandwidth/performance improvement, yet it's not ready. We need a
dedicated Distribution struct which is managed by the wallet and passed in.
While we can do that now, it's not currently worth the effort.
* Tidy Borromean/MLSAG a tad
* Remove experimental feature from monero-serai
* Move amount_decryption into EncryptedAmount::decrypt
* Various RingCT doc comments
* Begin crate smashing
* Further documentation, start shoring up API boundaries of existing crates
* Document and clean clsag
* Add a dedicated send/recv CLSAG mask struct
Abstracts the types used internally.
Also moves the tests from monero-serai to monero-clsag.
* Smash out monero-bulletproofs
Removes usage of dalek-ff-group/multiexp for curve25519-dalek.
Makes compiling in the generators an optional feature.
Adds a structured batch verifier which should be notably more performant.
Documentation and clean up still necessary.
* Correct no-std builds for monero-clsag and monero-bulletproofs
* Tidy and document monero-bulletproofs
I still don't like the impl of the original Bulletproofs...
* Error if missing documentation
* Smash out MLSAG
* Smash out Borromean
* Tidy up monero-serai as a meta crate
* Smash out RPC, wallet
* Document the RPC
* Improve docs a bit
* Move Protocol to monero-wallet
* Incomplete work on using Option to remove panic cases
* Finish documenting monero-serai
* Remove TODO on reading pseudo_outs for AggregateMlsagBorromean
* Only read transactions with one Input::Gen or all Input::ToKey
Also adds a helper to fetch a transaction's prefix.
* Smash out polyseed
* Smash out seed
* Get the repo to compile again
* Smash out Monero addresses
* Document cargo features
Credit to @hinto-janai for adding such sections to their work on documenting
monero-serai in #568.
* Fix deserializing v2 miner transactions
* Rewrite monero-wallet's send code
I have yet to redo the multisig code and the builder. This should be much
cleaner, albeit slower due to redoing work.
This compiles with clippy --all-features. I have to finish the multisig/builder
for --all-targets to work (and start updating the rest of Serai).
* Add SignableTransaction Read/Write
* Restore Monero multisig TX code
* Correct invalid RPC type def in monero-rpc
* Update monero-wallet tests to compile
Some are _consistently_ failing due to the inputs we attempt to spend being too
young. I'm unsure what's up with that. Most seem to pass _consistently_,
implying it's not a random issue yet some configuration/env aspect.
* Clean and document monero-address
* Sync rest of repo with monero-serai changes
* Represent height/block number as a u32
* Diversify ViewPair/Scanner into ViewPair/GuaranteedViewPair and Scanner/GuaranteedScanner
Also cleans the Scanner impl.
* Remove non-small-order view key bound
Guaranteed addresses are in fact guaranteed even with this due to prefixing key
images causing zeroing the ECDH to not zero the shared key.
* Finish documenting monero-serai
* Correct imports for no-std
* Remove possible panic in monero-serai on systems < 32 bits
This was done by requiring the system's usize can represent a certain number.
* Restore the reserialize chain binary
* fmt, machete, GH CI
* Correct misc TODOs in monero-serai
* Have Monero test runner evaluate an Eventuality for all signed TXs
* Fix a pair of bugs in the decoy tests
Unfortunately, this test is still failing.
* Fix remaining bugs in monero-wallet tests
* Reject torsioned spend keys to ensure we can spend the outputs we scan
* Tidy inlined epee code in the RPC
* Correct the accidental swap of stagenet/testnet address bytes
* Remove unused dep from processor
* Handle Monero fee logic properly in the processor
* Document v2 TX/RCT output relation assumed when scanning
* Adjust how we mine the initial blocks due to some CI test failures
* Fix weight estimation for RctType::ClsagBulletproof TXs
* Again increase the amount of blocks we mine prior to running tests
* Correct the if check about when to mine blocks on start
Finally fixes the lack of decoy candidates failures in CI.
* Run Monero on Debian, even for internal testnets
Change made due to a segfault incurred when locally testing.
https://github.com/monero-project/monero/issues/9141 for the upstream.
* Don't attempt running tests on the verify-chain binary
Adds a minimum XMR fee to the processor and runs fmt.
* Increase minimum Monero fee in processor
I'm truly unsure why this is required right now.
* Distinguish fee from necessary_fee in monero-wallet
If there's no change, the fee is difference of the inputs to the outputs. The
prior code wouldn't check that amount is greater than or equal to the necessary
fee, and returning the would-be change amount as the fee isn't necessarily
helpful.
Now the fee is validated in such cases and the necessary fee is returned,
enabling operating off of that.
* Restore minimum Monero fee from develop
2024-07-07 03:57:18 -07:00
|
|
|
RctType::AggregateMlsagBorromean => 1,
|
|
|
|
|
RctType::MlsagBorromean => 2,
|
|
|
|
|
RctType::MlsagBulletproofs => 3,
|
|
|
|
|
RctType::MlsagBulletproofsCompactAmount => 4,
|
|
|
|
|
RctType::ClsagBulletproof => 5,
|
|
|
|
|
RctType::ClsagBulletproofPlus => 6,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TryFrom<u8> for RctType {
|
|
|
|
|
type Error = ();
|
|
|
|
|
fn try_from(byte: u8) -> Result<Self, ()> {
|
|
|
|
|
Ok(match byte {
|
|
|
|
|
1 => RctType::AggregateMlsagBorromean,
|
|
|
|
|
2 => RctType::MlsagBorromean,
|
|
|
|
|
3 => RctType::MlsagBulletproofs,
|
|
|
|
|
4 => RctType::MlsagBulletproofsCompactAmount,
|
|
|
|
|
5 => RctType::ClsagBulletproof,
|
|
|
|
|
6 => RctType::ClsagBulletproofPlus,
|
|
|
|
|
_ => Err(())?,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl RctType {
|
|
|
|
|
/// True if this RctType uses compact encrypted amounts, false otherwise.
|
|
|
|
|
pub fn compact_encrypted_amounts(&self) -> bool {
|
|
|
|
|
match self {
|
|
|
|
|
RctType::AggregateMlsagBorromean | RctType::MlsagBorromean | RctType::MlsagBulletproofs => {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
RctType::MlsagBulletproofsCompactAmount |
|
|
|
|
|
RctType::ClsagBulletproof |
|
|
|
|
|
RctType::ClsagBulletproofPlus => true,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// True if this RctType uses a Bulletproof, false otherwise.
|
|
|
|
|
pub(crate) fn bulletproof(&self) -> bool {
|
|
|
|
|
match self {
|
|
|
|
|
RctType::MlsagBulletproofs |
|
|
|
|
|
RctType::MlsagBulletproofsCompactAmount |
|
|
|
|
|
RctType::ClsagBulletproof => true,
|
|
|
|
|
RctType::AggregateMlsagBorromean |
|
|
|
|
|
RctType::MlsagBorromean |
|
|
|
|
|
RctType::ClsagBulletproofPlus => false,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// True if this RctType uses a Bulletproof+, false otherwise.
|
|
|
|
|
pub(crate) fn bulletproof_plus(&self) -> bool {
|
|
|
|
|
match self {
|
|
|
|
|
RctType::ClsagBulletproofPlus => true,
|
|
|
|
|
RctType::AggregateMlsagBorromean |
|
|
|
|
|
RctType::MlsagBorromean |
|
|
|
|
|
RctType::MlsagBulletproofs |
|
|
|
|
|
RctType::MlsagBulletproofsCompactAmount |
|
|
|
|
|
RctType::ClsagBulletproof => false,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// The base of the RingCT data.
|
|
|
|
|
///
|
|
|
|
|
/// This excludes all proofs (which once initially verified do not need to be kept around) and
|
|
|
|
|
/// solely keeps data which either impacts the effects of the transactions or is needed to scan it.
|
|
|
|
|
///
|
|
|
|
|
/// The one exception for this is `pseudo_outs`, which was originally present here yet moved to
|
|
|
|
|
/// RctPrunable in a later hard fork (causing it to be present in both).
|
|
|
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
|
|
|
pub struct RctBase {
|
|
|
|
|
/// The fee used by this transaction.
|
|
|
|
|
pub fee: u64,
|
|
|
|
|
/// The re-randomized amount commitments used within inputs.
|
|
|
|
|
///
|
|
|
|
|
/// This field was deprecated and is empty for modern RctTypes.
|
|
|
|
|
pub pseudo_outs: Vec<EdwardsPoint>,
|
|
|
|
|
/// The encrypted amounts for the recipients to decrypt.
|
|
|
|
|
pub encrypted_amounts: Vec<EncryptedAmount>,
|
|
|
|
|
/// The output commitments.
|
|
|
|
|
pub commitments: Vec<EdwardsPoint>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl RctBase {
|
|
|
|
|
/// Write the RctBase.
|
|
|
|
|
pub fn write<W: Write>(&self, w: &mut W, rct_type: RctType) -> io::Result<()> {
|
|
|
|
|
w.write_all(&[u8::from(rct_type)])?;
|
|
|
|
|
|
|
|
|
|
write_varint(&self.fee, w)?;
|
|
|
|
|
if rct_type == RctType::MlsagBorromean {
|
|
|
|
|
write_raw_vec(write_point, &self.pseudo_outs, w)?;
|
|
|
|
|
}
|
|
|
|
|
for encrypted_amount in &self.encrypted_amounts {
|
|
|
|
|
encrypted_amount.write(w)?;
|
|
|
|
|
}
|
|
|
|
|
write_raw_vec(write_point, &self.commitments, w)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Read a RctBase.
|
|
|
|
|
pub fn read<R: Read>(
|
|
|
|
|
inputs: usize,
|
|
|
|
|
outputs: usize,
|
|
|
|
|
r: &mut R,
|
|
|
|
|
) -> io::Result<Option<(RctType, RctBase)>> {
|
|
|
|
|
let rct_type = read_byte(r)?;
|
|
|
|
|
if rct_type == 0 {
|
|
|
|
|
return Ok(None);
|
|
|
|
|
}
|
|
|
|
|
let rct_type =
|
|
|
|
|
RctType::try_from(rct_type).map_err(|()| io::Error::other("invalid RCT type"))?;
|
|
|
|
|
|
|
|
|
|
match rct_type {
|
|
|
|
|
RctType::AggregateMlsagBorromean | RctType::MlsagBorromean => {}
|
|
|
|
|
RctType::MlsagBulletproofs |
|
|
|
|
|
RctType::MlsagBulletproofsCompactAmount |
|
|
|
|
|
RctType::ClsagBulletproof |
|
|
|
|
|
RctType::ClsagBulletproofPlus => {
|
|
|
|
|
if outputs == 0 {
|
|
|
|
|
// Because the Bulletproofs(+) layout must be canonical, there must be 1 Bulletproof if
|
|
|
|
|
// Bulletproofs are in use
|
|
|
|
|
// If there are Bulletproofs, there must be a matching amount of outputs, implicitly
|
|
|
|
|
// banning 0 outputs
|
|
|
|
|
// Since HF 12 (CLSAG being 13), a 2-output minimum has also been enforced
|
|
|
|
|
Err(io::Error::other("RCT with Bulletproofs(+) had 0 outputs"))?;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(Some((
|
|
|
|
|
rct_type,
|
|
|
|
|
RctBase {
|
|
|
|
|
fee: read_varint(r)?,
|
|
|
|
|
// Only read pseudo_outs if they have yet to be moved to RctPrunable
|
|
|
|
|
// This would apply to AggregateMlsagBorromean and MlsagBorromean, except
|
|
|
|
|
// AggregateMlsagBorromean doesn't use pseudo_outs due to using the sum of the output
|
|
|
|
|
// commitments directly as the effective singular pseudo-out
|
|
|
|
|
pseudo_outs: if rct_type == RctType::MlsagBorromean {
|
|
|
|
|
read_raw_vec(read_point, inputs, r)?
|
|
|
|
|
} else {
|
|
|
|
|
vec![]
|
|
|
|
|
},
|
|
|
|
|
encrypted_amounts: (0 .. outputs)
|
|
|
|
|
.map(|_| EncryptedAmount::read(rct_type.compact_encrypted_amounts(), r))
|
|
|
|
|
.collect::<Result<_, _>>()?,
|
|
|
|
|
commitments: read_raw_vec(read_point, outputs, r)?,
|
|
|
|
|
},
|
|
|
|
|
)))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// The prunable part of the RingCT data.
|
|
|
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
|
|
|
pub enum RctPrunable {
|
|
|
|
|
/// An aggregate MLSAG with Borromean range proofs.
|
|
|
|
|
AggregateMlsagBorromean {
|
|
|
|
|
/// The aggregate MLSAG ring signature.
|
|
|
|
|
mlsag: Mlsag,
|
|
|
|
|
/// The Borromean range proofs for each output.
|
|
|
|
|
borromean: Vec<BorromeanRange>,
|
|
|
|
|
},
|
|
|
|
|
/// MLSAGs with Borromean range proofs.
|
|
|
|
|
MlsagBorromean {
|
|
|
|
|
/// The MLSAG ring signatures for each input.
|
|
|
|
|
mlsags: Vec<Mlsag>,
|
|
|
|
|
/// The Borromean range proofs for each output.
|
|
|
|
|
borromean: Vec<BorromeanRange>,
|
|
|
|
|
},
|
|
|
|
|
/// MLSAGs with Bulletproofs.
|
|
|
|
|
MlsagBulletproofs {
|
|
|
|
|
/// The MLSAG ring signatures for each input.
|
|
|
|
|
mlsags: Vec<Mlsag>,
|
|
|
|
|
/// The re-blinded commitments for the outputs being spent.
|
|
|
|
|
pseudo_outs: Vec<EdwardsPoint>,
|
|
|
|
|
/// The aggregate Bulletproof, proving the outputs are within range.
|
|
|
|
|
bulletproof: Bulletproof,
|
|
|
|
|
},
|
|
|
|
|
/// MLSAGs with Bulletproofs and compact encrypted amounts.
|
|
|
|
|
///
|
|
|
|
|
/// This has an identical layout to MlsagBulletproofs and is interpreted the exact same way. It's
|
|
|
|
|
/// only differentiated to ensure discovery of the correct RctType.
|
|
|
|
|
MlsagBulletproofsCompactAmount {
|
|
|
|
|
/// The MLSAG ring signatures for each input.
|
|
|
|
|
mlsags: Vec<Mlsag>,
|
|
|
|
|
/// The re-blinded commitments for the outputs being spent.
|
|
|
|
|
pseudo_outs: Vec<EdwardsPoint>,
|
|
|
|
|
/// The aggregate Bulletproof, proving the outputs are within range.
|
|
|
|
|
bulletproof: Bulletproof,
|
|
|
|
|
},
|
|
|
|
|
/// CLSAGs with Bulletproofs(+).
|
|
|
|
|
Clsag {
|
|
|
|
|
/// The CLSAGs for each input.
|
|
|
|
|
clsags: Vec<Clsag>,
|
|
|
|
|
/// The re-blinded commitments for the outputs being spent.
|
|
|
|
|
pseudo_outs: Vec<EdwardsPoint>,
|
|
|
|
|
/// The aggregate Bulletproof(+), proving the outputs are within range.
|
|
|
|
|
bulletproof: Bulletproof,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl RctPrunable {
|
|
|
|
|
/// Write the RctPrunable.
|
|
|
|
|
pub fn write<W: Write>(&self, w: &mut W, rct_type: RctType) -> io::Result<()> {
|
|
|
|
|
match self {
|
|
|
|
|
RctPrunable::AggregateMlsagBorromean { borromean, mlsag } => {
|
|
|
|
|
write_raw_vec(BorromeanRange::write, borromean, w)?;
|
|
|
|
|
mlsag.write(w)
|
|
|
|
|
}
|
|
|
|
|
RctPrunable::MlsagBorromean { borromean, mlsags } => {
|
|
|
|
|
write_raw_vec(BorromeanRange::write, borromean, w)?;
|
|
|
|
|
write_raw_vec(Mlsag::write, mlsags, w)
|
|
|
|
|
}
|
|
|
|
|
RctPrunable::MlsagBulletproofs { bulletproof, mlsags, pseudo_outs } |
|
|
|
|
|
RctPrunable::MlsagBulletproofsCompactAmount { bulletproof, mlsags, pseudo_outs } => {
|
|
|
|
|
if rct_type == RctType::MlsagBulletproofs {
|
|
|
|
|
w.write_all(&1u32.to_le_bytes())?;
|
|
|
|
|
} else {
|
|
|
|
|
w.write_all(&[1])?;
|
|
|
|
|
}
|
|
|
|
|
bulletproof.write(w)?;
|
|
|
|
|
|
|
|
|
|
write_raw_vec(Mlsag::write, mlsags, w)?;
|
|
|
|
|
write_raw_vec(write_point, pseudo_outs, w)
|
|
|
|
|
}
|
|
|
|
|
RctPrunable::Clsag { bulletproof, clsags, pseudo_outs } => {
|
|
|
|
|
w.write_all(&[1])?;
|
|
|
|
|
bulletproof.write(w)?;
|
|
|
|
|
|
|
|
|
|
write_raw_vec(Clsag::write, clsags, w)?;
|
|
|
|
|
write_raw_vec(write_point, pseudo_outs, w)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Serialize the RctPrunable to a `Vec<u8>`.
|
|
|
|
|
pub fn serialize(&self, rct_type: RctType) -> Vec<u8> {
|
|
|
|
|
let mut serialized = vec![];
|
2025-08-08 21:28:47 -04:00
|
|
|
self
|
|
|
|
|
.write(&mut serialized, rct_type)
|
|
|
|
|
.expect("write failed but <Vec as io::Write> doesn't fail");
|
Clean the Monero lib for auditing (#577)
* Remove unsafe creation of dalek_ff_group::EdwardsPoint in BP+
* Rename Bulletproofs to Bulletproof, since they are a single Bulletproof
Also bifurcates prove with prove_plus, and adds a few documentation items.
* Make CLSAG signing private
Also adds a bit more documentation and does a bit more tidying.
* Remove the distribution cache
It's a notable bandwidth/performance improvement, yet it's not ready. We need a
dedicated Distribution struct which is managed by the wallet and passed in.
While we can do that now, it's not currently worth the effort.
* Tidy Borromean/MLSAG a tad
* Remove experimental feature from monero-serai
* Move amount_decryption into EncryptedAmount::decrypt
* Various RingCT doc comments
* Begin crate smashing
* Further documentation, start shoring up API boundaries of existing crates
* Document and clean clsag
* Add a dedicated send/recv CLSAG mask struct
Abstracts the types used internally.
Also moves the tests from monero-serai to monero-clsag.
* Smash out monero-bulletproofs
Removes usage of dalek-ff-group/multiexp for curve25519-dalek.
Makes compiling in the generators an optional feature.
Adds a structured batch verifier which should be notably more performant.
Documentation and clean up still necessary.
* Correct no-std builds for monero-clsag and monero-bulletproofs
* Tidy and document monero-bulletproofs
I still don't like the impl of the original Bulletproofs...
* Error if missing documentation
* Smash out MLSAG
* Smash out Borromean
* Tidy up monero-serai as a meta crate
* Smash out RPC, wallet
* Document the RPC
* Improve docs a bit
* Move Protocol to monero-wallet
* Incomplete work on using Option to remove panic cases
* Finish documenting monero-serai
* Remove TODO on reading pseudo_outs for AggregateMlsagBorromean
* Only read transactions with one Input::Gen or all Input::ToKey
Also adds a helper to fetch a transaction's prefix.
* Smash out polyseed
* Smash out seed
* Get the repo to compile again
* Smash out Monero addresses
* Document cargo features
Credit to @hinto-janai for adding such sections to their work on documenting
monero-serai in #568.
* Fix deserializing v2 miner transactions
* Rewrite monero-wallet's send code
I have yet to redo the multisig code and the builder. This should be much
cleaner, albeit slower due to redoing work.
This compiles with clippy --all-features. I have to finish the multisig/builder
for --all-targets to work (and start updating the rest of Serai).
* Add SignableTransaction Read/Write
* Restore Monero multisig TX code
* Correct invalid RPC type def in monero-rpc
* Update monero-wallet tests to compile
Some are _consistently_ failing due to the inputs we attempt to spend being too
young. I'm unsure what's up with that. Most seem to pass _consistently_,
implying it's not a random issue yet some configuration/env aspect.
* Clean and document monero-address
* Sync rest of repo with monero-serai changes
* Represent height/block number as a u32
* Diversify ViewPair/Scanner into ViewPair/GuaranteedViewPair and Scanner/GuaranteedScanner
Also cleans the Scanner impl.
* Remove non-small-order view key bound
Guaranteed addresses are in fact guaranteed even with this due to prefixing key
images causing zeroing the ECDH to not zero the shared key.
* Finish documenting monero-serai
* Correct imports for no-std
* Remove possible panic in monero-serai on systems < 32 bits
This was done by requiring the system's usize can represent a certain number.
* Restore the reserialize chain binary
* fmt, machete, GH CI
* Correct misc TODOs in monero-serai
* Have Monero test runner evaluate an Eventuality for all signed TXs
* Fix a pair of bugs in the decoy tests
Unfortunately, this test is still failing.
* Fix remaining bugs in monero-wallet tests
* Reject torsioned spend keys to ensure we can spend the outputs we scan
* Tidy inlined epee code in the RPC
* Correct the accidental swap of stagenet/testnet address bytes
* Remove unused dep from processor
* Handle Monero fee logic properly in the processor
* Document v2 TX/RCT output relation assumed when scanning
* Adjust how we mine the initial blocks due to some CI test failures
* Fix weight estimation for RctType::ClsagBulletproof TXs
* Again increase the amount of blocks we mine prior to running tests
* Correct the if check about when to mine blocks on start
Finally fixes the lack of decoy candidates failures in CI.
* Run Monero on Debian, even for internal testnets
Change made due to a segfault incurred when locally testing.
https://github.com/monero-project/monero/issues/9141 for the upstream.
* Don't attempt running tests on the verify-chain binary
Adds a minimum XMR fee to the processor and runs fmt.
* Increase minimum Monero fee in processor
I'm truly unsure why this is required right now.
* Distinguish fee from necessary_fee in monero-wallet
If there's no change, the fee is difference of the inputs to the outputs. The
prior code wouldn't check that amount is greater than or equal to the necessary
fee, and returning the would-be change amount as the fee isn't necessarily
helpful.
Now the fee is validated in such cases and the necessary fee is returned,
enabling operating off of that.
* Restore minimum Monero fee from develop
2024-07-07 03:57:18 -07:00
|
|
|
serialized
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Read a RctPrunable.
|
|
|
|
|
pub fn read<R: Read>(
|
|
|
|
|
rct_type: RctType,
|
|
|
|
|
ring_length: usize,
|
|
|
|
|
inputs: usize,
|
|
|
|
|
outputs: usize,
|
|
|
|
|
r: &mut R,
|
|
|
|
|
) -> io::Result<RctPrunable> {
|
|
|
|
|
Ok(match rct_type {
|
|
|
|
|
RctType::AggregateMlsagBorromean => RctPrunable::AggregateMlsagBorromean {
|
|
|
|
|
borromean: read_raw_vec(BorromeanRange::read, outputs, r)?,
|
|
|
|
|
mlsag: Mlsag::read(ring_length, inputs + 1, r)?,
|
|
|
|
|
},
|
|
|
|
|
RctType::MlsagBorromean => RctPrunable::MlsagBorromean {
|
|
|
|
|
borromean: read_raw_vec(BorromeanRange::read, outputs, r)?,
|
|
|
|
|
mlsags: (0 .. inputs).map(|_| Mlsag::read(ring_length, 2, r)).collect::<Result<_, _>>()?,
|
|
|
|
|
},
|
|
|
|
|
RctType::MlsagBulletproofs | RctType::MlsagBulletproofsCompactAmount => {
|
|
|
|
|
let bulletproof = {
|
|
|
|
|
if (if rct_type == RctType::MlsagBulletproofs {
|
|
|
|
|
u64::from(read_u32(r)?)
|
|
|
|
|
} else {
|
|
|
|
|
read_varint(r)?
|
|
|
|
|
}) != 1
|
|
|
|
|
{
|
|
|
|
|
Err(io::Error::other("n bulletproofs instead of one"))?;
|
|
|
|
|
}
|
|
|
|
|
Bulletproof::read(r)?
|
|
|
|
|
};
|
|
|
|
|
let mlsags =
|
|
|
|
|
(0 .. inputs).map(|_| Mlsag::read(ring_length, 2, r)).collect::<Result<_, _>>()?;
|
|
|
|
|
let pseudo_outs = read_raw_vec(read_point, inputs, r)?;
|
|
|
|
|
if rct_type == RctType::MlsagBulletproofs {
|
|
|
|
|
RctPrunable::MlsagBulletproofs { bulletproof, mlsags, pseudo_outs }
|
|
|
|
|
} else {
|
|
|
|
|
debug_assert_eq!(rct_type, RctType::MlsagBulletproofsCompactAmount);
|
|
|
|
|
RctPrunable::MlsagBulletproofsCompactAmount { bulletproof, mlsags, pseudo_outs }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
RctType::ClsagBulletproof | RctType::ClsagBulletproofPlus => RctPrunable::Clsag {
|
|
|
|
|
bulletproof: {
|
|
|
|
|
if read_varint::<_, u64>(r)? != 1 {
|
|
|
|
|
Err(io::Error::other("n bulletproofs instead of one"))?;
|
|
|
|
|
}
|
|
|
|
|
(if rct_type == RctType::ClsagBulletproof {
|
|
|
|
|
Bulletproof::read
|
|
|
|
|
} else {
|
|
|
|
|
Bulletproof::read_plus
|
|
|
|
|
})(r)?
|
|
|
|
|
},
|
|
|
|
|
clsags: (0 .. inputs).map(|_| Clsag::read(ring_length, r)).collect::<Result<_, _>>()?,
|
|
|
|
|
pseudo_outs: read_raw_vec(read_point, inputs, r)?,
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Write the RctPrunable as necessary for signing the signature.
|
|
|
|
|
pub(crate) fn signature_write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
|
|
|
|
match self {
|
|
|
|
|
RctPrunable::AggregateMlsagBorromean { borromean, .. } |
|
|
|
|
|
RctPrunable::MlsagBorromean { borromean, .. } => {
|
|
|
|
|
borromean.iter().try_for_each(|rs| rs.write(w))
|
|
|
|
|
}
|
|
|
|
|
RctPrunable::MlsagBulletproofs { bulletproof, .. } |
|
|
|
|
|
RctPrunable::MlsagBulletproofsCompactAmount { bulletproof, .. } |
|
|
|
|
|
RctPrunable::Clsag { bulletproof, .. } => bulletproof.signature_write(w),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// The RingCT proofs.
|
|
|
|
|
///
|
|
|
|
|
/// This contains both the RctBase and RctPrunable structs.
|
|
|
|
|
///
|
|
|
|
|
/// The C++ codebase refers to this as rct_signatures.
|
|
|
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
|
|
|
pub struct RctProofs {
|
|
|
|
|
/// The data necessary for handling this transaction.
|
|
|
|
|
pub base: RctBase,
|
|
|
|
|
/// The data necessary for verifying this transaction.
|
|
|
|
|
pub prunable: RctPrunable,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl RctProofs {
|
|
|
|
|
/// RctType for a given RctProofs struct.
|
|
|
|
|
pub fn rct_type(&self) -> RctType {
|
|
|
|
|
match &self.prunable {
|
|
|
|
|
RctPrunable::AggregateMlsagBorromean { .. } => RctType::AggregateMlsagBorromean,
|
|
|
|
|
RctPrunable::MlsagBorromean { .. } => RctType::MlsagBorromean,
|
|
|
|
|
RctPrunable::MlsagBulletproofs { .. } => RctType::MlsagBulletproofs,
|
|
|
|
|
RctPrunable::MlsagBulletproofsCompactAmount { .. } => RctType::MlsagBulletproofsCompactAmount,
|
|
|
|
|
RctPrunable::Clsag { bulletproof, .. } => {
|
|
|
|
|
if matches!(bulletproof, Bulletproof::Original { .. }) {
|
|
|
|
|
RctType::ClsagBulletproof
|
|
|
|
|
} else {
|
|
|
|
|
RctType::ClsagBulletproofPlus
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Write the RctProofs.
|
|
|
|
|
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 the RctProofs to a `Vec<u8>`.
|
|
|
|
|
pub fn serialize(&self) -> Vec<u8> {
|
|
|
|
|
let mut serialized = vec![];
|
2025-08-08 21:28:47 -04:00
|
|
|
self.write(&mut serialized).expect("write failed but <Vec as io::Write> doesn't fail");
|
Clean the Monero lib for auditing (#577)
* Remove unsafe creation of dalek_ff_group::EdwardsPoint in BP+
* Rename Bulletproofs to Bulletproof, since they are a single Bulletproof
Also bifurcates prove with prove_plus, and adds a few documentation items.
* Make CLSAG signing private
Also adds a bit more documentation and does a bit more tidying.
* Remove the distribution cache
It's a notable bandwidth/performance improvement, yet it's not ready. We need a
dedicated Distribution struct which is managed by the wallet and passed in.
While we can do that now, it's not currently worth the effort.
* Tidy Borromean/MLSAG a tad
* Remove experimental feature from monero-serai
* Move amount_decryption into EncryptedAmount::decrypt
* Various RingCT doc comments
* Begin crate smashing
* Further documentation, start shoring up API boundaries of existing crates
* Document and clean clsag
* Add a dedicated send/recv CLSAG mask struct
Abstracts the types used internally.
Also moves the tests from monero-serai to monero-clsag.
* Smash out monero-bulletproofs
Removes usage of dalek-ff-group/multiexp for curve25519-dalek.
Makes compiling in the generators an optional feature.
Adds a structured batch verifier which should be notably more performant.
Documentation and clean up still necessary.
* Correct no-std builds for monero-clsag and monero-bulletproofs
* Tidy and document monero-bulletproofs
I still don't like the impl of the original Bulletproofs...
* Error if missing documentation
* Smash out MLSAG
* Smash out Borromean
* Tidy up monero-serai as a meta crate
* Smash out RPC, wallet
* Document the RPC
* Improve docs a bit
* Move Protocol to monero-wallet
* Incomplete work on using Option to remove panic cases
* Finish documenting monero-serai
* Remove TODO on reading pseudo_outs for AggregateMlsagBorromean
* Only read transactions with one Input::Gen or all Input::ToKey
Also adds a helper to fetch a transaction's prefix.
* Smash out polyseed
* Smash out seed
* Get the repo to compile again
* Smash out Monero addresses
* Document cargo features
Credit to @hinto-janai for adding such sections to their work on documenting
monero-serai in #568.
* Fix deserializing v2 miner transactions
* Rewrite monero-wallet's send code
I have yet to redo the multisig code and the builder. This should be much
cleaner, albeit slower due to redoing work.
This compiles with clippy --all-features. I have to finish the multisig/builder
for --all-targets to work (and start updating the rest of Serai).
* Add SignableTransaction Read/Write
* Restore Monero multisig TX code
* Correct invalid RPC type def in monero-rpc
* Update monero-wallet tests to compile
Some are _consistently_ failing due to the inputs we attempt to spend being too
young. I'm unsure what's up with that. Most seem to pass _consistently_,
implying it's not a random issue yet some configuration/env aspect.
* Clean and document monero-address
* Sync rest of repo with monero-serai changes
* Represent height/block number as a u32
* Diversify ViewPair/Scanner into ViewPair/GuaranteedViewPair and Scanner/GuaranteedScanner
Also cleans the Scanner impl.
* Remove non-small-order view key bound
Guaranteed addresses are in fact guaranteed even with this due to prefixing key
images causing zeroing the ECDH to not zero the shared key.
* Finish documenting monero-serai
* Correct imports for no-std
* Remove possible panic in monero-serai on systems < 32 bits
This was done by requiring the system's usize can represent a certain number.
* Restore the reserialize chain binary
* fmt, machete, GH CI
* Correct misc TODOs in monero-serai
* Have Monero test runner evaluate an Eventuality for all signed TXs
* Fix a pair of bugs in the decoy tests
Unfortunately, this test is still failing.
* Fix remaining bugs in monero-wallet tests
* Reject torsioned spend keys to ensure we can spend the outputs we scan
* Tidy inlined epee code in the RPC
* Correct the accidental swap of stagenet/testnet address bytes
* Remove unused dep from processor
* Handle Monero fee logic properly in the processor
* Document v2 TX/RCT output relation assumed when scanning
* Adjust how we mine the initial blocks due to some CI test failures
* Fix weight estimation for RctType::ClsagBulletproof TXs
* Again increase the amount of blocks we mine prior to running tests
* Correct the if check about when to mine blocks on start
Finally fixes the lack of decoy candidates failures in CI.
* Run Monero on Debian, even for internal testnets
Change made due to a segfault incurred when locally testing.
https://github.com/monero-project/monero/issues/9141 for the upstream.
* Don't attempt running tests on the verify-chain binary
Adds a minimum XMR fee to the processor and runs fmt.
* Increase minimum Monero fee in processor
I'm truly unsure why this is required right now.
* Distinguish fee from necessary_fee in monero-wallet
If there's no change, the fee is difference of the inputs to the outputs. The
prior code wouldn't check that amount is greater than or equal to the necessary
fee, and returning the would-be change amount as the fee isn't necessarily
helpful.
Now the fee is validated in such cases and the necessary fee is returned,
enabling operating off of that.
* Restore minimum Monero fee from develop
2024-07-07 03:57:18 -07:00
|
|
|
serialized
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Read a RctProofs.
|
|
|
|
|
pub fn read<R: Read>(
|
|
|
|
|
ring_length: usize,
|
|
|
|
|
inputs: usize,
|
|
|
|
|
outputs: usize,
|
|
|
|
|
r: &mut R,
|
|
|
|
|
) -> io::Result<Option<RctProofs>> {
|
|
|
|
|
let Some((rct_type, base)) = RctBase::read(inputs, outputs, r)? else { return Ok(None) };
|
|
|
|
|
Ok(Some(RctProofs {
|
|
|
|
|
base,
|
|
|
|
|
prunable: RctPrunable::read(rct_type, ring_length, inputs, outputs, r)?,
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-13 00:29:02 -04:00
|
|
|
|
|
|
|
|
/// A pruned set of RingCT proofs.
|
|
|
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
|
|
|
pub struct PrunedRctProofs {
|
|
|
|
|
/// The type of RctProofs this used to be.
|
|
|
|
|
pub rct_type: RctType,
|
|
|
|
|
/// The data necessary for handling this transaction.
|
|
|
|
|
pub base: RctBase,
|
|
|
|
|
}
|