Skip to content

Commit

Permalink
Handle v1 Monero TXs spending v2 outputs
Browse files Browse the repository at this point in the history
Also tightens read/fixes a few potential panics.
  • Loading branch information
kayabaNerve committed Aug 23, 2023
1 parent f2872a2 commit bfb5401
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 10 deletions.
2 changes: 1 addition & 1 deletion coins/monero/src/bin/reserialize_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ mod binaries {

let blob = hex::decode(res.blob).expect("node returned non-hex block");
let block = Block::read(&mut blob.as_slice())
.unwrap_or_else(|_| panic!("couldn't deserialize block {block_i}"));
.unwrap_or_else(|e| panic!("couldn't deserialize block {block_i}: {e}"));
assert_eq!(block.hash(), hash, "hash differs");
assert_eq!(block.serialize(), blob, "serialization differs");

Expand Down
43 changes: 34 additions & 9 deletions coins/monero/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,15 +315,40 @@ impl Transaction {
})
.collect::<Result<_, _>>()?;

rct_signatures.base.fee = prefix
.inputs
.iter()
.map(|input| match input {
Input::Gen(..) => 0,
Input::ToKey { amount, .. } => amount.unwrap(),
})
.sum::<u64>()
.saturating_sub(prefix.outputs.iter().map(|output| output.amount.unwrap()).sum());
if !matches!(prefix.inputs[0], Input::Gen(..)) {
let in_amount = prefix
.inputs
.iter()
.map(|input| match input {
Input::Gen(..) => {
Err(io::Error::new(io::ErrorKind::Other, "Input::Gen present in non-coinbase v1 TX"))?
}
// v1 TXs can burn v2 outputs
// dcff3fe4f914d6b6bd4a5b800cc4cca8f2fdd1bd73352f0700d463d36812f328 is one such TX
// It includes a pre-RCT signature for a RCT output, yet if you interpret the RCT
// output as being worth 0, it passes a sum check (guaranteed since no outputs are RCT)
Input::ToKey { amount, .. } => Ok(amount.unwrap_or(0)),
})
.collect::<io::Result<Vec<_>>>()?
.into_iter()
.sum::<u64>();

let mut out = 0;
for output in &prefix.outputs {
if output.amount.is_none() {
Err(io::Error::new(io::ErrorKind::Other, "v1 transaction had a 0-amount output"))?;
}
out += output.amount.unwrap();
}

if in_amount < out {
Err(io::Error::new(
io::ErrorKind::Other,
"transaction spent more than it had as inputs",
))?;
}
rct_signatures.base.fee = in_amount - out;
}
} else if prefix.version == 2 {
rct_signatures = RctSignatures::read(
prefix
Expand Down

0 comments on commit bfb5401

Please sign in to comment.