Update reserialize_chain for v1 and migration TXs

Also always marks 0-amount inputs as RCT due to impossibility of non-RCT
0-amount outputs.
This commit is contained in:
Luke Parker 2023-07-08 21:09:22 -04:00
parent 13f48a406e
commit 5d9067b84d
No known key found for this signature in database
2 changed files with 46 additions and 12 deletions

View file

@ -1,11 +1,15 @@
use std::sync::Arc;
use curve25519_dalek::edwards::{CompressedEdwardsY, EdwardsPoint};
use curve25519_dalek::{
scalar::Scalar,
edwards::{CompressedEdwardsY, EdwardsPoint},
};
use serde::Deserialize;
use serde_json::json;
use monero_serai::{
Commitment,
ringct::RctPrunable,
transaction::{Input, Transaction},
block::Block,
@ -82,6 +86,12 @@ async fn check_block(rpc: Arc<Rpc<HttpRpc>>, block_i: usize) {
);
assert_eq!(tx.hash(), tx_hash, "Transaction hash was different");
if matches!(tx.rct_signatures.prunable, RctPrunable::Null) {
assert_eq!(tx.prefix.version, 1);
assert!(!tx.signatures.is_empty());
continue;
}
let sig_hash = tx.signature_hash();
// Verify all proofs we support proving for
// This is due to having debug_asserts calling verify within their proving, and CLSAG
@ -97,9 +107,9 @@ async fn check_block(rpc: Arc<Rpc<HttpRpc>>, block_i: usize) {
assert!(bulletproofs.verify(&mut rand_core::OsRng, &tx.rct_signatures.base.commitments));
for (i, clsag) in clsags.into_iter().enumerate() {
let (image, key_offsets) = match &tx.prefix.inputs[i] {
let (amount, key_offsets, image) = match &tx.prefix.inputs[i] {
Input::Gen(_) => panic!("Input::Gen"),
Input::ToKey { key_image, key_offsets, .. } => (key_image, key_offsets),
Input::ToKey { amount, key_offsets, key_image } => (amount, key_offsets, key_image),
};
let mut running_sum = 0;
@ -109,7 +119,11 @@ async fn check_block(rpc: Arc<Rpc<HttpRpc>>, block_i: usize) {
actual_indexes.push(running_sum);
}
async fn get_outs(rpc: &Rpc<HttpRpc>, indexes: &[u64]) -> Vec<[EdwardsPoint; 2]> {
async fn get_outs(
rpc: &Rpc<HttpRpc>,
amount: u64,
indexes: &[u64],
) -> Vec<[EdwardsPoint; 2]> {
#[derive(Deserialize, Debug)]
struct Out {
key: String,
@ -127,7 +141,7 @@ async fn check_block(rpc: Arc<Rpc<HttpRpc>>, block_i: usize) {
Some(json!({
"get_txid": true,
"outputs": indexes.iter().map(|o| json!({
"amount": 0,
"amount": amount,
"index": o
})).collect::<Vec<_>>()
})),
@ -146,11 +160,26 @@ async fn check_block(rpc: Arc<Rpc<HttpRpc>>, block_i: usize) {
.expect("invalid point for ring member")
};
outs.outs.iter().map(|out| [rpc_point(&out.key), rpc_point(&out.mask)]).collect()
outs
.outs
.iter()
.map(|out| {
let mask = rpc_point(&out.mask);
if amount != 0 {
assert_eq!(mask, Commitment::new(Scalar::from(1u8), amount).calculate());
}
[rpc_point(&out.key), mask]
})
.collect()
}
clsag
.verify(&get_outs(&rpc, &actual_indexes).await, image, &pseudo_outs[i], &sig_hash)
.verify(
&get_outs(&rpc, amount.unwrap_or(0), &actual_indexes).await,
image,
&pseudo_outs[i],
&sig_hash,
)
.unwrap();
}
}

View file

@ -53,12 +53,17 @@ impl Input {
res
}
pub fn read<R: Read>(interpret_as_rct: bool, r: &mut R) -> io::Result<Input> {
pub fn read<R: Read>(r: &mut R) -> io::Result<Input> {
Ok(match read_byte(r)? {
255 => Input::Gen(read_varint(r)?),
2 => {
let amount = read_varint(r)?;
let amount = if (amount == 0) && interpret_as_rct { None } else { Some(amount) };
// https://github.com/monero-project/monero/
// blob/00fd416a99686f0956361d1cd0337fe56e58d4a7/
// src/cryptonote_basic/cryptonote_format_utils.cpp#L860-L863
// A non-RCT 0-amount input can't exist because only RCT TXs can have a 0-amount output
// That's why collapsing to None if the amount is 0 is safe, even without knowing if RCT
let amount = if amount == 0 { None } else { Some(amount) };
Input::ToKey {
amount,
key_offsets: read_vec(read_varint, r)?,
@ -101,9 +106,9 @@ impl Output {
res
}
pub fn read<R: Read>(interpret_as_rct: bool, r: &mut R) -> io::Result<Output> {
pub fn read<R: Read>(rct: bool, r: &mut R) -> io::Result<Output> {
let amount = read_varint(r)?;
let amount = if interpret_as_rct {
let amount = if rct {
if amount != 0 {
Err(io::Error::new(io::ErrorKind::Other, "RCT TX output wasn't 0"))?;
}
@ -215,7 +220,7 @@ impl TransactionPrefix {
let timelock = Timelock::from_raw(read_varint(r)?);
let inputs = read_vec(|r| Input::read(version == 2, r), r)?;
let inputs = read_vec(|r| Input::read(r), r)?;
if inputs.is_empty() {
Err(io::Error::new(io::ErrorKind::Other, "transaction had no inputs"))?;
}