Correct RctPrunable decoding

This commit is contained in:
Luke Parker 2022-05-21 23:16:06 -04:00
parent 882d67838e
commit 0c01ad69d8
No known key found for this signature in database
GPG key ID: F9F1386DB1E119B6
2 changed files with 52 additions and 36 deletions

View file

@ -28,8 +28,10 @@ pub enum RpcError {
TransactionsNotFound(usize, usize), TransactionsNotFound(usize, usize),
#[error("invalid point ({0})")] #[error("invalid point ({0})")]
InvalidPoint(String), InvalidPoint(String),
#[error("invalid transaction")] #[error("pruned transaction")]
InvalidTransaction PrunedTransaction,
#[error("invalid transaction ({0:?})")]
InvalidTransaction([u8; 32])
} }
pub struct Rpc(String); pub struct Rpc(String);
@ -119,24 +121,23 @@ impl Rpc {
Err(RpcError::TransactionsNotFound(txs.txs.len(), hashes.len()))?; Err(RpcError::TransactionsNotFound(txs.txs.len(), hashes.len()))?;
} }
Ok( txs.txs.iter().enumerate().map(|(i, res)| {
// Ignores transactions we fail to parse let tx = Transaction::deserialize(
txs.txs.iter().filter_map( &mut std::io::Cursor::new(
|res| rpc_hex(if res.as_hex.len() != 0 { &res.as_hex } else { &res.pruned_as_hex }).ok() rpc_hex(if res.as_hex.len() != 0 { &res.as_hex } else { &res.pruned_as_hex }).unwrap()
.and_then(|bytes| Transaction::deserialize(&mut std::io::Cursor::new(bytes)).ok()) )
// https://github.com/monero-project/monero/issues/8311 ).map_err(|_| RpcError::InvalidTransaction(hashes[i]))?;
.filter(
|tx| if res.as_hex.len() == 0 { // https://github.com/monero-project/monero/issues/8311
match tx.prefix.inputs.get(0) { if res.as_hex.len() == 0 {
Some(Input::Gen { .. }) => true, match tx.prefix.inputs.get(0) {
_ => false Some(Input::Gen { .. }) => (),
} _ => Err(RpcError::PrunedTransaction)?
} else { }
true }
}
) Ok(tx)
).collect() }).collect::<Result<_, _>>()
)
} }
pub async fn get_block(&self, height: usize) -> Result<Block, RpcError> { pub async fn get_block(&self, height: usize) -> Result<Block, RpcError> {
@ -283,7 +284,7 @@ impl Rpc {
}))).await?; }))).await?;
if res.status != "OK" { if res.status != "OK" {
Err(RpcError::InvalidTransaction)?; Err(RpcError::InvalidTransaction(tx.hash()))?;
} }
Ok(()) Ok(())

View file

@ -134,23 +134,33 @@ pub struct RctBase {
impl RctBase { impl RctBase {
pub fn serialize<W: std::io::Write>(&self, w: &mut W, rct_type: u8) -> std::io::Result<()> { pub fn serialize<W: std::io::Write>(&self, w: &mut W, rct_type: u8) -> std::io::Result<()> {
w.write_all(&[rct_type])?; w.write_all(&[rct_type])?;
write_varint(&self.fee, w)?; match rct_type {
for ecdh in &self.ecdh_info { 0 => Ok(()),
w.write_all(ecdh)?; 5 => {
write_varint(&self.fee, w)?;
for ecdh in &self.ecdh_info {
w.write_all(ecdh)?;
}
write_raw_vec(write_point, &self.commitments, w)
},
_ => panic!("Serializing unknown RctType's Base")
} }
write_raw_vec(write_point, &self.commitments, w)
} }
pub fn deserialize<R: std::io::Read>(outputs: usize, r: &mut R) -> std::io::Result<(RctBase, u8)> { pub fn deserialize<R: std::io::Read>(outputs: usize, r: &mut R) -> std::io::Result<(RctBase, u8)> {
let mut rct_type = [0]; let mut rct_type = [0];
r.read_exact(&mut rct_type)?; r.read_exact(&mut rct_type)?;
Ok(( Ok((
RctBase { if rct_type[0] == 0 {
fee: read_varint(r)?, RctBase { fee: 0, ecdh_info: vec![], commitments: vec![] }
ecdh_info: (0 .. outputs).map( } else {
|_| { let mut ecdh = [0; 8]; r.read_exact(&mut ecdh).map(|_| ecdh) } RctBase {
).collect::<Result<_, _>>()?, fee: read_varint(r)?,
commitments: read_raw_vec(read_point, outputs, r)? ecdh_info: (0 .. outputs).map(
|_| { let mut ecdh = [0; 8]; r.read_exact(&mut ecdh).map(|_| ecdh) }
).collect::<Result<_, _>>()?,
commitments: read_raw_vec(read_point, outputs, r)?
}
}, },
rct_type[0] rct_type[0]
)) ))
@ -189,7 +199,6 @@ impl RctPrunable {
pub fn deserialize<R: std::io::Read>( pub fn deserialize<R: std::io::Read>(
rct_type: u8, rct_type: u8,
decoys: &[usize], decoys: &[usize],
outputs: usize,
r: &mut R r: &mut R
) -> std::io::Result<RctPrunable> { ) -> std::io::Result<RctPrunable> {
Ok( Ok(
@ -199,7 +208,7 @@ impl RctPrunable {
// TODO: Can the amount of outputs be calculated from the BPs for any validly formed TX? // TODO: Can the amount of outputs be calculated from the BPs for any validly formed TX?
bulletproofs: read_vec(Bulletproofs::deserialize, r)?, bulletproofs: read_vec(Bulletproofs::deserialize, r)?,
clsags: (0 .. decoys.len()).map(|o| Clsag::deserialize(decoys[o], r)).collect::<Result<_, _>>()?, clsags: (0 .. decoys.len()).map(|o| Clsag::deserialize(decoys[o], r)).collect::<Result<_, _>>()?,
pseudo_outs: read_raw_vec(read_point, outputs, r)? pseudo_outs: read_raw_vec(read_point, decoys.len(), r)?
}, },
_ => Err(std::io::Error::new(std::io::ErrorKind::Other, "Tried to deserialize unknown RCT type"))? _ => Err(std::io::Error::new(std::io::ErrorKind::Other, "Tried to deserialize unknown RCT type"))?
} }
@ -228,7 +237,7 @@ impl RctSignatures {
pub fn deserialize<R: std::io::Read>(decoys: Vec<usize>, outputs: usize, r: &mut R) -> std::io::Result<RctSignatures> { pub fn deserialize<R: std::io::Read>(decoys: Vec<usize>, outputs: usize, r: &mut R) -> std::io::Result<RctSignatures> {
let base = RctBase::deserialize(outputs, r)?; let base = RctBase::deserialize(outputs, r)?;
Ok(RctSignatures { base: base.0, prunable: RctPrunable::deserialize(base.1, &decoys, outputs, r)? }) Ok(RctSignatures { base: base.0, prunable: RctPrunable::deserialize(base.1, &decoys, r)? })
} }
} }
@ -280,8 +289,14 @@ impl Transaction {
sig_hash.extend(hash(&serialized)); sig_hash.extend(hash(&serialized));
serialized.clear(); serialized.clear();
self.rct_signatures.prunable.serialize(&mut serialized).unwrap(); match self.rct_signatures.prunable {
sig_hash.extend(hash(&serialized)); RctPrunable::Null => serialized.resize(32, 0),
_ => {
self.rct_signatures.prunable.serialize(&mut serialized).unwrap();
serialized = hash(&serialized).to_vec();
}
}
sig_hash.extend(&serialized);
hash(&sig_hash) hash(&sig_hash)
} }