add borromean + fix mlsag

This commit is contained in:
Boog900
2023-05-31 19:32:47 +01:00
parent d8b8ce9837
commit f81f5c386d
8 changed files with 167 additions and 26 deletions

View File

@@ -0,0 +1,54 @@
#![allow(non_snake_case)]
use std::fmt::Debug;
use std::io::{self, Read, Write};
use curve25519_dalek::edwards::EdwardsPoint;
use curve25519_dalek::scalar::Scalar;
use crate::serialize::*;
fn read_64_array<R: Read, T: Debug, F: Fn(&mut R) -> io::Result<T>>(
f: F,
r: &mut R,
) -> io::Result<[T; 64]> {
(0 .. 64).map(|_| f(r)).collect::<io::Result<Vec<T>>>().map(|vec| vec.try_into().unwrap())
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct BorroSig {
pub s0: [Scalar; 64],
pub s1: [Scalar; 64],
pub ee: Scalar,
}
impl BorroSig {
pub fn read<R: Read>(r: &mut R) -> io::Result<BorroSig> {
Ok(BorroSig {
s0: read_64_array(read_scalar, r)?,
s1: read_64_array(read_scalar, r)?,
ee: read_scalar(r)?,
})
}
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
write_raw_vec(write_scalar, &self.s0, w)?;
write_raw_vec(write_scalar, &self.s1, w)?;
write_scalar(&self.ee, w)
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct RangeSig {
pub asig: BorroSig,
pub Ci: [EdwardsPoint; 64],
}
impl RangeSig {
pub fn read<R: Read>(r: &mut R) -> io::Result<RangeSig> {
Ok(RangeSig { asig: BorroSig::read(r)?, Ci: read_64_array(read_point, r)? })
}
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
self.asig.write(w)?;
write_raw_vec(write_point, &self.Ci, w)
}
}

View File

@@ -13,7 +13,7 @@ use crate::{
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
pub struct Mlsag { pub struct Mlsag {
pub ss: Vec<Vec<Scalar>>, pub ss: Vec<Vec<Scalar>>,
pub cc: EdwardsPoint, pub cc: Scalar,
} }
impl Mlsag { impl Mlsag {
@@ -21,15 +21,15 @@ impl Mlsag {
for ss in self.ss.iter() { for ss in self.ss.iter() {
write_raw_vec(write_scalar, ss, w)?; write_raw_vec(write_scalar, ss, w)?;
} }
write_point(&self.cc, w) write_scalar(&self.cc, w)
} }
pub fn read<R: Read>(decoys: usize, elements: usize, r: &mut R) -> io::Result<Mlsag> { pub fn read<R: Read>(mixins: usize, ss2_elements: usize, r: &mut R) -> io::Result<Mlsag> {
Ok(Mlsag { Ok(Mlsag {
ss: (0 .. decoys) ss: (0 .. mixins)
.map(|_| read_raw_vec(read_scalar, elements, r)) .map(|_| read_raw_vec(read_scalar, ss2_elements, r))
.collect::<Result<_, _>>()?, .collect::<Result<_, _>>()?,
cc: read_point(r)?, cc: read_scalar(r)?,
}) })
} }
} }

View File

@@ -10,15 +10,17 @@ pub use hash_to_point::{raw_hash_to_point, hash_to_point};
/// CLSAG struct, along with signing and verifying functionality. /// CLSAG struct, along with signing and verifying functionality.
pub mod clsag; pub mod clsag;
/// MLSAG struct, along with verifying functionality. /// MLSAG struct.
pub mod mlsag; pub mod mlsag;
/// RangeSig struct.
pub mod borromean;
/// Bulletproofs(+) structs, along with proving and verifying functionality. /// Bulletproofs(+) structs, along with proving and verifying functionality.
pub mod bulletproofs; pub mod bulletproofs;
use crate::{ use crate::{
Protocol, Protocol,
serialize::*, serialize::*,
ringct::{clsag::Clsag, mlsag::Mlsag, bulletproofs::Bulletproofs}, ringct::{clsag::Clsag, mlsag::Mlsag, bulletproofs::Bulletproofs, borromean::RangeSig},
}; };
/// Generate a key image for a given key. Defined as `x * hash_to_point(xG)`. /// Generate a key image for a given key. Defined as `x * hash_to_point(xG)`.
@@ -26,10 +28,36 @@ pub fn generate_key_image(secret: &Zeroizing<Scalar>) -> EdwardsPoint {
hash_to_point(&ED25519_BASEPOINT_TABLE * secret.deref()) * secret.deref() hash_to_point(&ED25519_BASEPOINT_TABLE * secret.deref()) * secret.deref()
} }
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum EcdhInfo {
Standard { mask: Scalar, amount: Scalar },
Bulletproof { amount: [u8; 8] },
}
impl EcdhInfo {
pub fn read<R: Read>(rct_type: u8, r: &mut R) -> io::Result<(EcdhInfo)> {
Ok(match rct_type {
0 ..= 3 => EcdhInfo::Standard { mask: read_scalar(r)?, amount: read_scalar(r)? },
_ => EcdhInfo::Bulletproof { amount: read_bytes(r)? },
})
}
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
match self {
EcdhInfo::Standard { mask, amount } => {
write_scalar(mask, w)?;
write_scalar(amount, w)
}
EcdhInfo::Bulletproof { amount } => w.write_all(amount),
}
}
}
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
pub struct RctBase { pub struct RctBase {
pub fee: u64, pub fee: u64,
pub ecdh_info: Vec<[u8; 8]>, pub ecdh_info: Vec<EcdhInfo>,
pub pseudo_outs: Vec<EdwardsPoint>,
pub commitments: Vec<EdwardsPoint>, pub commitments: Vec<EdwardsPoint>,
} }
@@ -42,10 +70,13 @@ impl RctBase {
w.write_all(&[rct_type])?; w.write_all(&[rct_type])?;
match rct_type { match rct_type {
0 => Ok(()), 0 => Ok(()),
5 | 6 => { _ => {
write_varint(&self.fee, w)?; write_varint(&self.fee, w)?;
if rct_type == 2 {
write_raw_vec(write_point, &self.pseudo_outs, w)?;
}
for ecdh in &self.ecdh_info { for ecdh in &self.ecdh_info {
w.write_all(ecdh)?; ecdh.write(w)?;
} }
write_raw_vec(write_point, &self.commitments, w) write_raw_vec(write_point, &self.commitments, w)
} }
@@ -53,15 +84,18 @@ impl RctBase {
} }
} }
pub fn read<R: Read>(outputs: usize, r: &mut R) -> io::Result<(RctBase, u8)> { pub fn read<R: Read>(inputs: usize, outputs: usize, r: &mut R) -> io::Result<(RctBase, u8)> {
let rct_type = read_byte(r)?; let rct_type = read_byte(r)?;
Ok(( Ok((
if rct_type == 0 { if rct_type == 0 {
RctBase { fee: 0, ecdh_info: vec![], commitments: vec![] } RctBase { fee: 0, ecdh_info: vec![], pseudo_outs: vec![], commitments: vec![] }
} else { } else {
RctBase { RctBase {
fee: read_varint(r)?, fee: read_varint(r)?,
ecdh_info: (0 .. outputs).map(|_| read_bytes(r)).collect::<Result<_, _>>()?, pseudo_outs: if rct_type == 2 { read_raw_vec(read_point, inputs, r)? } else { vec![] },
ecdh_info: (0 .. outputs)
.map(|_| EcdhInfo::read(rct_type, r))
.collect::<Result<_, _>>()?,
commitments: read_raw_vec(read_point, outputs, r)?, commitments: read_raw_vec(read_point, outputs, r)?,
} }
}, },
@@ -73,6 +107,11 @@ impl RctBase {
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
pub enum RctPrunable { pub enum RctPrunable {
Null, Null,
Borromean {
range_sigs: Vec<RangeSig>,
mlsags: Vec<Mlsag>,
simple: bool,
},
BulletProof { BulletProof {
bulletproofs: Vec<Bulletproofs>, bulletproofs: Vec<Bulletproofs>,
mlsags: Vec<Mlsag>, mlsags: Vec<Mlsag>,
@@ -91,6 +130,13 @@ impl RctPrunable {
pub fn rct_type(&self) -> u8 { pub fn rct_type(&self) -> u8 {
match self { match self {
RctPrunable::Null => 0, RctPrunable::Null => 0,
RctPrunable::Borromean { simple, .. } => {
if !simple {
1
} else {
2
}
}
RctPrunable::BulletProof { v2, .. } => { RctPrunable::BulletProof { v2, .. } => {
if !v2 { if !v2 {
3 3
@@ -116,6 +162,10 @@ impl RctPrunable {
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> { pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
match self { match self {
RctPrunable::Null => Ok(()), RctPrunable::Null => Ok(()),
RctPrunable::Borromean { range_sigs, mlsags, simple: _ } => {
write_raw_vec(RangeSig::write, range_sigs, w)?;
write_raw_vec(Mlsag::write, mlsags, w)
}
RctPrunable::BulletProof { bulletproofs, mlsags, pseudo_outs, v2 } => { RctPrunable::BulletProof { bulletproofs, mlsags, pseudo_outs, v2 } => {
if !v2 { if !v2 {
w.write_all(&u32::try_from(bulletproofs.len()).unwrap().to_le_bytes())?; w.write_all(&u32::try_from(bulletproofs.len()).unwrap().to_le_bytes())?;
@@ -140,9 +190,26 @@ impl RctPrunable {
serialized serialized
} }
pub fn read<R: Read>(rct_type: u8, decoys: &[usize], r: &mut R) -> io::Result<RctPrunable> { pub fn read<R: Read>(
rct_type: u8,
decoys: &[usize],
outputs: usize,
r: &mut R,
) -> io::Result<RctPrunable> {
Ok(match rct_type { Ok(match rct_type {
0 => RctPrunable::Null, 0 => RctPrunable::Null,
1 => RctPrunable::Borromean {
range_sigs: read_raw_vec(RangeSig::read, outputs, r)?,
mlsags: vec![Mlsag::read(decoys[0], 1 + decoys.len(), r)?],
simple: false,
},
2 => RctPrunable::Borromean {
range_sigs: read_raw_vec(RangeSig::read, outputs, r)?,
mlsags: (0 .. decoys.len())
.map(|o| Mlsag::read(decoys[o], 2, r))
.collect::<Result<_, _>>()?,
simple: true,
},
3 | 4 => RctPrunable::BulletProof { 3 | 4 => RctPrunable::BulletProof {
bulletproofs: read_raw_vec( bulletproofs: read_raw_vec(
Bulletproofs::read, Bulletproofs::read,
@@ -172,10 +239,10 @@ impl RctPrunable {
pub(crate) fn signature_write<W: Write>(&self, w: &mut W) -> io::Result<()> { pub(crate) fn signature_write<W: Write>(&self, w: &mut W) -> io::Result<()> {
match self { match self {
RctPrunable::Null => panic!("Serializing RctPrunable::Null for a signature"), RctPrunable::Null => panic!("Serializing RctPrunable::Null for a signature"),
RctPrunable::BulletProof { .. } => todo!(),
RctPrunable::Clsag { bulletproofs, .. } => { RctPrunable::Clsag { bulletproofs, .. } => {
bulletproofs.iter().try_for_each(|bp| bp.signature_write(w)) bulletproofs.iter().try_for_each(|bp| bp.signature_write(w))
} }
_ => todo!(),
} }
} }
} }
@@ -203,7 +270,7 @@ impl RctSignatures {
} }
pub fn read<R: Read>(decoys: Vec<usize>, outputs: usize, r: &mut R) -> io::Result<RctSignatures> { pub fn read<R: Read>(decoys: Vec<usize>, outputs: usize, r: &mut R) -> io::Result<RctSignatures> {
let base = RctBase::read(outputs, r)?; let base = RctBase::read(decoys.len(), outputs, r)?;
Ok(RctSignatures { base: base.0, prunable: RctPrunable::read(base.1, &decoys, r)? }) Ok(RctSignatures { base: base.0, prunable: RctPrunable::read(base.1, &decoys, outputs, r)? })
} }
} }

View File

@@ -249,7 +249,7 @@ impl Transaction {
let prefix = TransactionPrefix::read(r)?; let prefix = TransactionPrefix::read(r)?;
let mut signatures = vec![]; let mut signatures = vec![];
let mut rct_signatures = RctSignatures { let mut rct_signatures = RctSignatures {
base: RctBase { fee: 0, ecdh_info: vec![], commitments: vec![] }, base: RctBase { fee: 0, ecdh_info: vec![], pseudo_outs: vec![], commitments: vec![] },
prunable: RctPrunable::Null, prunable: RctPrunable::Null,
}; };

View File

@@ -35,6 +35,7 @@ pub use send::{
pub(crate) use send::InternalPayment; pub(crate) use send::InternalPayment;
#[cfg(feature = "multisig")] #[cfg(feature = "multisig")]
pub use send::TransactionMachine; pub use send::TransactionMachine;
use crate::ringct::EcdhInfo;
fn key_image_sort(x: &EdwardsPoint, y: &EdwardsPoint) -> std::cmp::Ordering { fn key_image_sort(x: &EdwardsPoint, y: &EdwardsPoint) -> std::cmp::Ordering {
x.compress().to_bytes().cmp(&y.compress().to_bytes()).reverse() x.compress().to_bytes().cmp(&y.compress().to_bytes()).reverse()
@@ -92,8 +93,24 @@ pub(crate) fn amount_encryption(amount: u64, key: Scalar) -> [u8; 8] {
(amount ^ u64::from_le_bytes(hash(&amount_mask)[.. 8].try_into().unwrap())).to_le_bytes() (amount ^ u64::from_le_bytes(hash(&amount_mask)[.. 8].try_into().unwrap())).to_le_bytes()
} }
fn amount_decryption(amount: [u8; 8], key: Scalar) -> u64 { fn amount_decryption(amount: &EcdhInfo, key: Scalar) -> u64 {
u64::from_le_bytes(amount_encryption(u64::from_le_bytes(amount), key)) match amount {
EcdhInfo::Standard { mask, amount } => {
let shared_sec1 = hash(key.as_bytes());
let shared_sec2 = hash(&shared_sec1);
let mask_scalar = mask - Scalar::from_bytes_mod_order(shared_sec1);
let amount_scalar = amount - Scalar::from_bytes_mod_order(shared_sec2);
// get first 64 bits (d2b in rctTypes.cpp)
let amount_significant_bytes =
amount_scalar.to_bytes()[0 .. 8].try_into().expect("Can't fail");
let amount = u64::from_le_bytes(amount_significant_bytes);
amount
}
EcdhInfo::Bulletproof { amount } => {
u64::from_le_bytes(amount_encryption(u64::from_le_bytes(*amount), key))
}
}
} }
pub(crate) fn commitment_mask(shared_key: Scalar) -> Scalar { pub(crate) fn commitment_mask(shared_key: Scalar) -> Scalar {

View File

@@ -372,7 +372,7 @@ impl Scanner {
// Regular transaction // Regular transaction
} else { } else {
let amount = match tx.rct_signatures.base.ecdh_info.get(o) { let amount = match tx.rct_signatures.base.ecdh_info.get(o) {
Some(amount) => amount_decryption(*amount, shared_key), Some(amount) => amount_decryption(amount, shared_key),
// This should never happen, yet it may be possible with miner transactions? // This should never happen, yet it may be possible with miner transactions?
// Using get just decreases the possibility of a panic and lets us move on in that case // Using get just decreases the possibility of a panic and lets us move on in that case
None => break, None => break,

View File

@@ -49,6 +49,7 @@ pub use builder::SignableTransactionBuilder;
mod multisig; mod multisig;
#[cfg(feature = "multisig")] #[cfg(feature = "multisig")]
pub use multisig::TransactionMachine; pub use multisig::TransactionMachine;
use crate::ringct::EcdhInfo;
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)] #[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)]
@@ -632,7 +633,7 @@ impl SignableTransaction {
key: output.dest.compress(), key: output.dest.compress(),
view_tag: Some(output.view_tag).filter(|_| matches!(self.protocol, Protocol::v16)), view_tag: Some(output.view_tag).filter(|_| matches!(self.protocol, Protocol::v16)),
}); });
ecdh_info.push(output.amount); ecdh_info.push(EcdhInfo::Bulletproof { amount: output.amount });
} }
( (
@@ -649,6 +650,7 @@ impl SignableTransaction {
base: RctBase { base: RctBase {
fee, fee,
ecdh_info, ecdh_info,
pseudo_outs: vec![],
commitments: commitments.iter().map(|commitment| commitment.calculate()).collect(), commitments: commitments.iter().map(|commitment| commitment.calculate()).collect(),
}, },
prunable: RctPrunable::Clsag { prunable: RctPrunable::Clsag {
@@ -701,7 +703,7 @@ impl SignableTransaction {
clsags.append(&mut clsag_pairs.iter().map(|clsag| clsag.0.clone()).collect::<Vec<_>>()); clsags.append(&mut clsag_pairs.iter().map(|clsag| clsag.0.clone()).collect::<Vec<_>>());
pseudo_outs.append(&mut clsag_pairs.iter().map(|clsag| clsag.1).collect::<Vec<_>>()); pseudo_outs.append(&mut clsag_pairs.iter().map(|clsag| clsag.1).collect::<Vec<_>>());
} }
RctPrunable::BulletProof { .. } => { _ => {
todo!() todo!()
} }
} }
@@ -753,7 +755,8 @@ impl Eventuality {
view_tag: Some(expected.view_tag).filter(|_| matches!(self.protocol, Protocol::v16)), view_tag: Some(expected.view_tag).filter(|_| matches!(self.protocol, Protocol::v16)),
} != actual) || } != actual) ||
(Some(&expected.commitment.calculate()) != tx.rct_signatures.base.commitments.get(o)) || (Some(&expected.commitment.calculate()) != tx.rct_signatures.base.commitments.get(o)) ||
(Some(&expected.amount) != tx.rct_signatures.base.ecdh_info.get(o)) (Some(&EcdhInfo::Bulletproof { amount: expected.amount }) !=
tx.rct_signatures.base.ecdh_info.get(o))
{ {
return false; return false;
} }

View File

@@ -429,7 +429,7 @@ impl SignatureMachine<Transaction> for TransactionSignatureMachine {
pseudo_outs.push(pseudo_out); pseudo_outs.push(pseudo_out);
} }
} }
RctPrunable::BulletProof { .. } => todo!(""), _ => todo!(""),
} }
Ok(tx) Ok(tx)
} }