Skip to content

Commit 93e85c5

Browse files
authored
Monero: use only the first input ring length for RCT deserialization. (#504)
* Use only the first input ring length for all RCT input signatures. This is what Monero does: https://github.com/monero-project/monero/blob/ac02af92867590ca80b2779a7bbeafa99ff94dcb/src/ringct/rctTypes.h#L422 https://github.com/monero-project/monero/blob/master/src/cryptonote_basic/cryptonote_basic.h#L308-L309 This isn't an issue for current transactions as from hf 12 Monero requires all inputs to have the same number of decoys but for transactions before that Monero would reject RCT txs with differing ring lengths. Monero would deserialize each inputs signature using the ring length of the first so the signatures for inputs other than the first would have a different (wrong) number of elements for that input meaning the signature is invalid. But as we are using the ring length of each input, which arguably is the *correct* way, we would approve of transactions with inputs differing in ring lengths. * Check that there is more than one ring member for MLSAG signatures. https://github.com/monero-project/monero/blob/ac02af92867590ca80b2779a7bbeafa99ff94dcb/src/ringct/rctSigs.cpp#L462
1 parent 617ec60 commit 93e85c5

File tree

3 files changed

+31
-20
lines changed

3 files changed

+31
-20
lines changed

coins/monero/src/ringct/mlsag.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@ pub struct RingMatrix {
3333

3434
impl RingMatrix {
3535
pub fn new(matrix: Vec<Vec<EdwardsPoint>>) -> Result<Self, MlsagError> {
36-
if matrix.is_empty() {
36+
// Monero requires that there is more than one ring member for MLSAG signatures:
37+
// https://github.com/monero-project/monero/blob/ac02af92867590ca80b2779a7bbeafa99ff94dcb/
38+
// src/ringct/rctSigs.cpp#L462
39+
if matrix.len() < 2 {
3740
Err(MlsagError::InvalidRing)?;
3841
}
3942
for member in &matrix {

coins/monero/src/ringct/mod.rs

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,8 @@ impl RctPrunable {
257257

258258
pub fn read<R: Read>(
259259
rct_type: RctType,
260-
decoys: &[usize],
260+
ring_length: usize,
261+
inputs: usize,
261262
outputs: usize,
262263
r: &mut R,
263264
) -> io::Result<RctPrunable> {
@@ -268,19 +269,19 @@ impl RctPrunable {
268269
// src/ringct/rctSigs.cpp#L609
269270
// And then for RctNull, that's only allowed for miner TXs which require one input of
270271
// Input::Gen
271-
if decoys.is_empty() {
272+
if inputs == 0 {
272273
Err(io::Error::other("transaction had no inputs"))?;
273274
}
274275

275276
Ok(match rct_type {
276277
RctType::Null => RctPrunable::Null,
277278
RctType::MlsagAggregate => RctPrunable::AggregateMlsagBorromean {
278279
borromean: read_raw_vec(BorromeanRange::read, outputs, r)?,
279-
mlsag: Mlsag::read(decoys[0], decoys.len() + 1, r)?,
280+
mlsag: Mlsag::read(ring_length, inputs + 1, r)?,
280281
},
281282
RctType::MlsagIndividual => RctPrunable::MlsagBorromean {
282283
borromean: read_raw_vec(BorromeanRange::read, outputs, r)?,
283-
mlsags: decoys.iter().map(|d| Mlsag::read(*d, 2, r)).collect::<Result<_, _>>()?,
284+
mlsags: (0 .. inputs).map(|_| Mlsag::read(ring_length, 2, r)).collect::<Result<_, _>>()?,
284285
},
285286
RctType::Bulletproofs | RctType::BulletproofsCompactAmount => {
286287
RctPrunable::MlsagBulletproofs {
@@ -295,8 +296,10 @@ impl RctPrunable {
295296
}
296297
Bulletproofs::read(r)?
297298
},
298-
mlsags: decoys.iter().map(|d| Mlsag::read(*d, 2, r)).collect::<Result<_, _>>()?,
299-
pseudo_outs: read_raw_vec(read_point, decoys.len(), r)?,
299+
mlsags: (0 .. inputs)
300+
.map(|_| Mlsag::read(ring_length, 2, r))
301+
.collect::<Result<_, _>>()?,
302+
pseudo_outs: read_raw_vec(read_point, inputs, r)?,
300303
}
301304
}
302305
RctType::Clsag | RctType::BulletproofsPlus => RctPrunable::Clsag {
@@ -308,8 +311,8 @@ impl RctPrunable {
308311
r,
309312
)?
310313
},
311-
clsags: (0 .. decoys.len()).map(|o| Clsag::read(decoys[o], r)).collect::<Result<_, _>>()?,
312-
pseudo_outs: read_raw_vec(read_point, decoys.len(), r)?,
314+
clsags: (0 .. inputs).map(|_| Clsag::read(ring_length, r)).collect::<Result<_, _>>()?,
315+
pseudo_outs: read_raw_vec(read_point, inputs, r)?,
313316
},
314317
})
315318
}
@@ -382,8 +385,16 @@ impl RctSignatures {
382385
serialized
383386
}
384387

385-
pub fn read<R: Read>(decoys: &[usize], outputs: usize, r: &mut R) -> io::Result<RctSignatures> {
386-
let base = RctBase::read(decoys.len(), outputs, r)?;
387-
Ok(RctSignatures { base: base.0, prunable: RctPrunable::read(base.1, decoys, outputs, r)? })
388+
pub fn read<R: Read>(
389+
ring_length: usize,
390+
inputs: usize,
391+
outputs: usize,
392+
r: &mut R,
393+
) -> io::Result<RctSignatures> {
394+
let base = RctBase::read(inputs, outputs, r)?;
395+
Ok(RctSignatures {
396+
base: base.0,
397+
prunable: RctPrunable::read(base.1, ring_length, inputs, outputs, r)?,
398+
})
388399
}
389400
}

coins/monero/src/transaction.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -331,14 +331,11 @@ impl Transaction {
331331
}
332332
} else if prefix.version == 2 {
333333
rct_signatures = RctSignatures::read(
334-
&prefix
335-
.inputs
336-
.iter()
337-
.map(|input| match input {
338-
Input::Gen(_) => 0,
339-
Input::ToKey { key_offsets, .. } => key_offsets.len(),
340-
})
341-
.collect::<Vec<_>>(),
334+
prefix.inputs.first().map_or(0, |input| match input {
335+
Input::Gen(_) => 0,
336+
Input::ToKey { key_offsets, .. } => key_offsets.len(),
337+
}),
338+
prefix.inputs.len(),
342339
prefix.outputs.len(),
343340
r,
344341
)?;

0 commit comments

Comments
 (0)