Monero: support for legacy transactions (#308)
* add mlsag
* fix last commit
* fix miner v1 txs
* fix non-miner v1 txs
* add borromean + fix mlsag
* add block hash calculations
* fix for the jokester that added unreduced scalars
to the borromean signature of
2368d846e671bf79a1f84c6d3af9f0bfe296f043f50cf17ae5e485384a53707b
* Add Borromean range proof verifying functionality
* Add MLSAG verifying functionality
* fmt & clippy :)
* update MLSAG, ss2_elements will always be 2
* Add MgSig proving
* Tidy block.rs
* Tidy Borromean, fix bugs in last commit, replace todo! with unreachable!
* Mark legacy EcdhInfo amount decryption as experimental
* Correct comments
* Write a new impl of the merkle algorithm
This one tries to be understandable.
* Only pull in things only needed for experimental when experimental
* Stop caching the Monero block hash now in processor that we have Block::hash
* Corrections for recent processor commit
* Use a clearer algorithm for the merkle
Should also be more efficient due to not shifting as often.
* Tidy Mlsag
* Remove verify_rct_* from Mlsag
Both methods were ports from Monero, overtly specific without clear
documentation. They need to be added back in, with documentation, or included
in a node which provides the necessary further context for them to be naturally
understandable.
* Move mlsag/mod.rs to mlsag.rs
This should only be a folder if it has multiple files.
* Replace EcdhInfo terminology
The ECDH encrypted the amount, yet this struct contained the encrypted amount,
not some ECDH.
Also corrects the types on the original EcdhInfo struct.
* Correct handling of commitment masks when scanning
* Route read_array through read_raw_vec
* Misc lint
* Make a proper RctType enum
No longer caches RctType in the RctSignatures as well.
* Replace Vec<Bulletproofs> with Bulletproofs
Monero uses aggregated range proofs, so there's only ever one Bulletproof. This
is enforced with a consensus rule as well, making this safe.
As for why Monero uses a vec, it's probably due to the lack of variadic typing
used. Its effectively an Option for them, yet we don't need an Option since we
do have variadic typing (enums).
* Add necessary checks to Eventuality re: supported protocols
* Fix for block 202612 and fix merkel root calculations
* MLSAG (de)serialisation fix
ss_2_elements will not always be 2 as rct type 1 transactions are not enforced to have one input
* Revert "MLSAG (de)serialisation fix"
This reverts commit 5e710e0c96658092c6ecfe5e4ea5a9c3dbee3ab3.
here it checks number of MGs == number of inputs:
https://github.com/monero-project/monero/blob/0a1eaf26f9dd6b762c2582ee12603b2a4671c735/src/cryptonote_core/tx_verification_utils.cpp#L60-59
and here it checks for RctTypeFull number of MGs == 1:
https://github.com/monero-project/monero/blob/0a1eaf26f9dd6b762c2582ee12603b2a4671c735/src/ringct/rctSigs.cpp#L1325
so number of inputs == 1
so ss_2_elements == 2
* update `MlsagAggregate` comment
* cargo update
Resolves a yanked crate
* Move location of serai-client in Cargo.toml
---------
Co-authored-by: Luke Parker <lukeparker5132@gmail.com>
2023-07-04 21:18:05 +00:00
|
|
|
use core::fmt::Debug;
|
|
|
|
use std_shims::io::{self, Read, Write};
|
|
|
|
|
|
|
|
use curve25519_dalek::edwards::EdwardsPoint;
|
|
|
|
#[cfg(feature = "experimental")]
|
|
|
|
use curve25519_dalek::{traits::Identity, scalar::Scalar};
|
|
|
|
|
|
|
|
#[cfg(feature = "experimental")]
|
|
|
|
use monero_generators::H_pow_2;
|
|
|
|
#[cfg(feature = "experimental")]
|
|
|
|
use crate::hash_to_scalar;
|
|
|
|
use crate::serialize::*;
|
|
|
|
|
|
|
|
/// 64 Borromean ring signatures.
|
|
|
|
///
|
|
|
|
/// This type keeps the data as raw bytes as Monero has some transactions with unreduced scalars in
|
|
|
|
/// this field. While we could use `from_bytes_mod_order`, we'd then not be able to encode this
|
|
|
|
/// back into it's original form.
|
|
|
|
///
|
|
|
|
/// Those scalars also have a custom reduction algorithm...
|
|
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
|
|
pub struct BorromeanSignatures {
|
|
|
|
pub s0: [[u8; 32]; 64],
|
|
|
|
pub s1: [[u8; 32]; 64],
|
|
|
|
pub ee: [u8; 32],
|
|
|
|
}
|
|
|
|
|
|
|
|
impl BorromeanSignatures {
|
|
|
|
pub fn read<R: Read>(r: &mut R) -> io::Result<BorromeanSignatures> {
|
|
|
|
Ok(BorromeanSignatures {
|
|
|
|
s0: read_array(read_bytes, r)?,
|
|
|
|
s1: read_array(read_bytes, r)?,
|
|
|
|
ee: read_bytes(r)?,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
2023-07-08 15:29:05 +00:00
|
|
|
for s0 in &self.s0 {
|
Monero: support for legacy transactions (#308)
* add mlsag
* fix last commit
* fix miner v1 txs
* fix non-miner v1 txs
* add borromean + fix mlsag
* add block hash calculations
* fix for the jokester that added unreduced scalars
to the borromean signature of
2368d846e671bf79a1f84c6d3af9f0bfe296f043f50cf17ae5e485384a53707b
* Add Borromean range proof verifying functionality
* Add MLSAG verifying functionality
* fmt & clippy :)
* update MLSAG, ss2_elements will always be 2
* Add MgSig proving
* Tidy block.rs
* Tidy Borromean, fix bugs in last commit, replace todo! with unreachable!
* Mark legacy EcdhInfo amount decryption as experimental
* Correct comments
* Write a new impl of the merkle algorithm
This one tries to be understandable.
* Only pull in things only needed for experimental when experimental
* Stop caching the Monero block hash now in processor that we have Block::hash
* Corrections for recent processor commit
* Use a clearer algorithm for the merkle
Should also be more efficient due to not shifting as often.
* Tidy Mlsag
* Remove verify_rct_* from Mlsag
Both methods were ports from Monero, overtly specific without clear
documentation. They need to be added back in, with documentation, or included
in a node which provides the necessary further context for them to be naturally
understandable.
* Move mlsag/mod.rs to mlsag.rs
This should only be a folder if it has multiple files.
* Replace EcdhInfo terminology
The ECDH encrypted the amount, yet this struct contained the encrypted amount,
not some ECDH.
Also corrects the types on the original EcdhInfo struct.
* Correct handling of commitment masks when scanning
* Route read_array through read_raw_vec
* Misc lint
* Make a proper RctType enum
No longer caches RctType in the RctSignatures as well.
* Replace Vec<Bulletproofs> with Bulletproofs
Monero uses aggregated range proofs, so there's only ever one Bulletproof. This
is enforced with a consensus rule as well, making this safe.
As for why Monero uses a vec, it's probably due to the lack of variadic typing
used. Its effectively an Option for them, yet we don't need an Option since we
do have variadic typing (enums).
* Add necessary checks to Eventuality re: supported protocols
* Fix for block 202612 and fix merkel root calculations
* MLSAG (de)serialisation fix
ss_2_elements will not always be 2 as rct type 1 transactions are not enforced to have one input
* Revert "MLSAG (de)serialisation fix"
This reverts commit 5e710e0c96658092c6ecfe5e4ea5a9c3dbee3ab3.
here it checks number of MGs == number of inputs:
https://github.com/monero-project/monero/blob/0a1eaf26f9dd6b762c2582ee12603b2a4671c735/src/cryptonote_core/tx_verification_utils.cpp#L60-59
and here it checks for RctTypeFull number of MGs == 1:
https://github.com/monero-project/monero/blob/0a1eaf26f9dd6b762c2582ee12603b2a4671c735/src/ringct/rctSigs.cpp#L1325
so number of inputs == 1
so ss_2_elements == 2
* update `MlsagAggregate` comment
* cargo update
Resolves a yanked crate
* Move location of serai-client in Cargo.toml
---------
Co-authored-by: Luke Parker <lukeparker5132@gmail.com>
2023-07-04 21:18:05 +00:00
|
|
|
w.write_all(s0)?;
|
|
|
|
}
|
2023-07-08 15:29:05 +00:00
|
|
|
for s1 in &self.s1 {
|
Monero: support for legacy transactions (#308)
* add mlsag
* fix last commit
* fix miner v1 txs
* fix non-miner v1 txs
* add borromean + fix mlsag
* add block hash calculations
* fix for the jokester that added unreduced scalars
to the borromean signature of
2368d846e671bf79a1f84c6d3af9f0bfe296f043f50cf17ae5e485384a53707b
* Add Borromean range proof verifying functionality
* Add MLSAG verifying functionality
* fmt & clippy :)
* update MLSAG, ss2_elements will always be 2
* Add MgSig proving
* Tidy block.rs
* Tidy Borromean, fix bugs in last commit, replace todo! with unreachable!
* Mark legacy EcdhInfo amount decryption as experimental
* Correct comments
* Write a new impl of the merkle algorithm
This one tries to be understandable.
* Only pull in things only needed for experimental when experimental
* Stop caching the Monero block hash now in processor that we have Block::hash
* Corrections for recent processor commit
* Use a clearer algorithm for the merkle
Should also be more efficient due to not shifting as often.
* Tidy Mlsag
* Remove verify_rct_* from Mlsag
Both methods were ports from Monero, overtly specific without clear
documentation. They need to be added back in, with documentation, or included
in a node which provides the necessary further context for them to be naturally
understandable.
* Move mlsag/mod.rs to mlsag.rs
This should only be a folder if it has multiple files.
* Replace EcdhInfo terminology
The ECDH encrypted the amount, yet this struct contained the encrypted amount,
not some ECDH.
Also corrects the types on the original EcdhInfo struct.
* Correct handling of commitment masks when scanning
* Route read_array through read_raw_vec
* Misc lint
* Make a proper RctType enum
No longer caches RctType in the RctSignatures as well.
* Replace Vec<Bulletproofs> with Bulletproofs
Monero uses aggregated range proofs, so there's only ever one Bulletproof. This
is enforced with a consensus rule as well, making this safe.
As for why Monero uses a vec, it's probably due to the lack of variadic typing
used. Its effectively an Option for them, yet we don't need an Option since we
do have variadic typing (enums).
* Add necessary checks to Eventuality re: supported protocols
* Fix for block 202612 and fix merkel root calculations
* MLSAG (de)serialisation fix
ss_2_elements will not always be 2 as rct type 1 transactions are not enforced to have one input
* Revert "MLSAG (de)serialisation fix"
This reverts commit 5e710e0c96658092c6ecfe5e4ea5a9c3dbee3ab3.
here it checks number of MGs == number of inputs:
https://github.com/monero-project/monero/blob/0a1eaf26f9dd6b762c2582ee12603b2a4671c735/src/cryptonote_core/tx_verification_utils.cpp#L60-59
and here it checks for RctTypeFull number of MGs == 1:
https://github.com/monero-project/monero/blob/0a1eaf26f9dd6b762c2582ee12603b2a4671c735/src/ringct/rctSigs.cpp#L1325
so number of inputs == 1
so ss_2_elements == 2
* update `MlsagAggregate` comment
* cargo update
Resolves a yanked crate
* Move location of serai-client in Cargo.toml
---------
Co-authored-by: Luke Parker <lukeparker5132@gmail.com>
2023-07-04 21:18:05 +00:00
|
|
|
w.write_all(s1)?;
|
|
|
|
}
|
|
|
|
w.write_all(&self.ee)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "experimental")]
|
|
|
|
fn verify(&self, keys_a: &[EdwardsPoint], keys_b: &[EdwardsPoint]) -> bool {
|
|
|
|
let mut transcript = [0; 2048];
|
|
|
|
for i in 0 .. 64 {
|
|
|
|
// TODO: These aren't the correct reduction
|
|
|
|
// TODO: Can either of these be tightened?
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
let LL = EdwardsPoint::vartime_double_scalar_mul_basepoint(
|
|
|
|
&Scalar::from_bytes_mod_order(self.ee),
|
|
|
|
&keys_a[i],
|
|
|
|
&Scalar::from_bytes_mod_order(self.s0[i]),
|
|
|
|
);
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
let LV = EdwardsPoint::vartime_double_scalar_mul_basepoint(
|
|
|
|
&hash_to_scalar(LL.compress().as_bytes()),
|
|
|
|
&keys_b[i],
|
|
|
|
&Scalar::from_bytes_mod_order(self.s1[i]),
|
|
|
|
);
|
|
|
|
transcript[i .. ((i + 1) * 32)].copy_from_slice(LV.compress().as_bytes());
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: This isn't the correct reduction
|
|
|
|
// TODO: Can this be tightened to from_canonical_bytes?
|
|
|
|
hash_to_scalar(&transcript) == Scalar::from_bytes_mod_order(self.ee)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A range proof premised on Borromean ring signatures.
|
|
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
|
|
pub struct BorromeanRange {
|
|
|
|
pub sigs: BorromeanSignatures,
|
|
|
|
pub bit_commitments: [EdwardsPoint; 64],
|
|
|
|
}
|
|
|
|
|
|
|
|
impl BorromeanRange {
|
|
|
|
pub fn read<R: Read>(r: &mut R) -> io::Result<BorromeanRange> {
|
|
|
|
Ok(BorromeanRange {
|
|
|
|
sigs: BorromeanSignatures::read(r)?,
|
|
|
|
bit_commitments: read_array(read_point, r)?,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
|
|
|
self.sigs.write(w)?;
|
|
|
|
write_raw_vec(write_point, &self.bit_commitments, w)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "experimental")]
|
|
|
|
pub fn verify(&self, commitment: &EdwardsPoint) -> bool {
|
|
|
|
if &self.bit_commitments.iter().sum::<EdwardsPoint>() != commitment {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
let H_pow_2 = H_pow_2();
|
|
|
|
let mut commitments_sub_one = [EdwardsPoint::identity(); 64];
|
|
|
|
for i in 0 .. 64 {
|
|
|
|
commitments_sub_one[i] = self.bit_commitments[i] - H_pow_2[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
self.sigs.verify(&self.bit_commitments, &commitments_sub_one)
|
|
|
|
}
|
|
|
|
}
|