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)]
pub struct Mlsag {
pub ss: Vec<Vec<Scalar>>,
pub cc: EdwardsPoint,
pub cc: Scalar,
}
impl Mlsag {
@@ -21,15 +21,15 @@ impl Mlsag {
for ss in self.ss.iter() {
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 {
ss: (0 .. decoys)
.map(|_| read_raw_vec(read_scalar, elements, r))
ss: (0 .. mixins)
.map(|_| read_raw_vec(read_scalar, ss2_elements, r))
.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.
pub mod clsag;
/// MLSAG struct, along with verifying functionality.
/// MLSAG struct.
pub mod mlsag;
/// RangeSig struct.
pub mod borromean;
/// Bulletproofs(+) structs, along with proving and verifying functionality.
pub mod bulletproofs;
use crate::{
Protocol,
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)`.
@@ -26,10 +28,36 @@ pub fn generate_key_image(secret: &Zeroizing<Scalar>) -> EdwardsPoint {
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)]
pub struct RctBase {
pub fee: u64,
pub ecdh_info: Vec<[u8; 8]>,
pub ecdh_info: Vec<EcdhInfo>,
pub pseudo_outs: Vec<EdwardsPoint>,
pub commitments: Vec<EdwardsPoint>,
}
@@ -42,10 +70,13 @@ impl RctBase {
w.write_all(&[rct_type])?;
match rct_type {
0 => Ok(()),
5 | 6 => {
_ => {
write_varint(&self.fee, w)?;
if rct_type == 2 {
write_raw_vec(write_point, &self.pseudo_outs, w)?;
}
for ecdh in &self.ecdh_info {
w.write_all(ecdh)?;
ecdh.write(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)?;
Ok((
if rct_type == 0 {
RctBase { fee: 0, ecdh_info: vec![], commitments: vec![] }
RctBase { fee: 0, ecdh_info: vec![], pseudo_outs: vec![], commitments: vec![] }
} else {
RctBase {
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)?,
}
},
@@ -73,6 +107,11 @@ impl RctBase {
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum RctPrunable {
Null,
Borromean {
range_sigs: Vec<RangeSig>,
mlsags: Vec<Mlsag>,
simple: bool,
},
BulletProof {
bulletproofs: Vec<Bulletproofs>,
mlsags: Vec<Mlsag>,
@@ -91,6 +130,13 @@ impl RctPrunable {
pub fn rct_type(&self) -> u8 {
match self {
RctPrunable::Null => 0,
RctPrunable::Borromean { simple, .. } => {
if !simple {
1
} else {
2
}
}
RctPrunable::BulletProof { v2, .. } => {
if !v2 {
3
@@ -116,6 +162,10 @@ impl RctPrunable {
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
match self {
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 } => {
if !v2 {
w.write_all(&u32::try_from(bulletproofs.len()).unwrap().to_le_bytes())?;
@@ -140,9 +190,26 @@ impl RctPrunable {
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 {
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 {
bulletproofs: read_raw_vec(
Bulletproofs::read,
@@ -172,10 +239,10 @@ 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::BulletProof { .. } => todo!(),
RctPrunable::Clsag { bulletproofs, .. } => {
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> {
let base = RctBase::read(outputs, r)?;
Ok(RctSignatures { base: base.0, prunable: RctPrunable::read(base.1, &decoys, r)? })
let base = RctBase::read(decoys.len(), outputs, 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 mut signatures = vec![];
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,
};

View File

@@ -35,6 +35,7 @@ pub use send::{
pub(crate) use send::InternalPayment;
#[cfg(feature = "multisig")]
pub use send::TransactionMachine;
use crate::ringct::EcdhInfo;
fn key_image_sort(x: &EdwardsPoint, y: &EdwardsPoint) -> std::cmp::Ordering {
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()
}
fn amount_decryption(amount: [u8; 8], key: Scalar) -> u64 {
u64::from_le_bytes(amount_encryption(u64::from_le_bytes(amount), key))
fn amount_decryption(amount: &EcdhInfo, key: Scalar) -> u64 {
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 {

View File

@@ -372,7 +372,7 @@ impl Scanner {
// Regular transaction
} else {
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?
// Using get just decreases the possibility of a panic and lets us move on in that case
None => break,

View File

@@ -49,6 +49,7 @@ pub use builder::SignableTransactionBuilder;
mod multisig;
#[cfg(feature = "multisig")]
pub use multisig::TransactionMachine;
use crate::ringct::EcdhInfo;
#[allow(non_snake_case)]
#[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)]
@@ -632,7 +633,7 @@ impl SignableTransaction {
key: output.dest.compress(),
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 {
fee,
ecdh_info,
pseudo_outs: vec![],
commitments: commitments.iter().map(|commitment| commitment.calculate()).collect(),
},
prunable: RctPrunable::Clsag {
@@ -701,7 +703,7 @@ impl SignableTransaction {
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<_>>());
}
RctPrunable::BulletProof { .. } => {
_ => {
todo!()
}
}
@@ -753,7 +755,8 @@ impl Eventuality {
view_tag: Some(expected.view_tag).filter(|_| matches!(self.protocol, Protocol::v16)),
} != actual) ||
(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;
}

View File

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