mirror of
https://github.com/serai-dex/serai.git
synced 2025-03-24 08:08:51 +00:00
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:
parent
13f48a406e
commit
5d9067b84d
2 changed files with 46 additions and 12 deletions
|
@ -1,11 +1,15 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use curve25519_dalek::edwards::{CompressedEdwardsY, EdwardsPoint};
|
use curve25519_dalek::{
|
||||||
|
scalar::Scalar,
|
||||||
|
edwards::{CompressedEdwardsY, EdwardsPoint},
|
||||||
|
};
|
||||||
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
use monero_serai::{
|
use monero_serai::{
|
||||||
|
Commitment,
|
||||||
ringct::RctPrunable,
|
ringct::RctPrunable,
|
||||||
transaction::{Input, Transaction},
|
transaction::{Input, Transaction},
|
||||||
block::Block,
|
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");
|
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();
|
let sig_hash = tx.signature_hash();
|
||||||
// Verify all proofs we support proving for
|
// Verify all proofs we support proving for
|
||||||
// This is due to having debug_asserts calling verify within their proving, and CLSAG
|
// 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));
|
assert!(bulletproofs.verify(&mut rand_core::OsRng, &tx.rct_signatures.base.commitments));
|
||||||
|
|
||||||
for (i, clsag) in clsags.into_iter().enumerate() {
|
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::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;
|
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);
|
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)]
|
#[derive(Deserialize, Debug)]
|
||||||
struct Out {
|
struct Out {
|
||||||
key: String,
|
key: String,
|
||||||
|
@ -127,7 +141,7 @@ async fn check_block(rpc: Arc<Rpc<HttpRpc>>, block_i: usize) {
|
||||||
Some(json!({
|
Some(json!({
|
||||||
"get_txid": true,
|
"get_txid": true,
|
||||||
"outputs": indexes.iter().map(|o| json!({
|
"outputs": indexes.iter().map(|o| json!({
|
||||||
"amount": 0,
|
"amount": amount,
|
||||||
"index": o
|
"index": o
|
||||||
})).collect::<Vec<_>>()
|
})).collect::<Vec<_>>()
|
||||||
})),
|
})),
|
||||||
|
@ -146,11 +160,26 @@ async fn check_block(rpc: Arc<Rpc<HttpRpc>>, block_i: usize) {
|
||||||
.expect("invalid point for ring member")
|
.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
|
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();
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,12 +53,17 @@ impl Input {
|
||||||
res
|
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)? {
|
Ok(match read_byte(r)? {
|
||||||
255 => Input::Gen(read_varint(r)?),
|
255 => Input::Gen(read_varint(r)?),
|
||||||
2 => {
|
2 => {
|
||||||
let amount = read_varint(r)?;
|
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 {
|
Input::ToKey {
|
||||||
amount,
|
amount,
|
||||||
key_offsets: read_vec(read_varint, r)?,
|
key_offsets: read_vec(read_varint, r)?,
|
||||||
|
@ -101,9 +106,9 @@ impl Output {
|
||||||
res
|
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 = read_varint(r)?;
|
||||||
let amount = if interpret_as_rct {
|
let amount = if rct {
|
||||||
if amount != 0 {
|
if amount != 0 {
|
||||||
Err(io::Error::new(io::ErrorKind::Other, "RCT TX output wasn't 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 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() {
|
if inputs.is_empty() {
|
||||||
Err(io::Error::new(io::ErrorKind::Other, "transaction had no inputs"))?;
|
Err(io::Error::new(io::ErrorKind::Other, "transaction had no inputs"))?;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue