diff --git a/coins/monero/src/rpc.rs b/coins/monero/src/rpc.rs index 36c4e9b9..2c3b8c25 100644 --- a/coins/monero/src/rpc.rs +++ b/coins/monero/src/rpc.rs @@ -28,8 +28,10 @@ pub enum RpcError { TransactionsNotFound(usize, usize), #[error("invalid point ({0})")] InvalidPoint(String), - #[error("invalid transaction")] - InvalidTransaction + #[error("pruned transaction")] + PrunedTransaction, + #[error("invalid transaction ({0:?})")] + InvalidTransaction([u8; 32]) } pub struct Rpc(String); @@ -119,24 +121,23 @@ impl Rpc { Err(RpcError::TransactionsNotFound(txs.txs.len(), hashes.len()))?; } - Ok( - // Ignores transactions we fail to parse - txs.txs.iter().filter_map( - |res| rpc_hex(if res.as_hex.len() != 0 { &res.as_hex } else { &res.pruned_as_hex }).ok() - .and_then(|bytes| Transaction::deserialize(&mut std::io::Cursor::new(bytes)).ok()) - // https://github.com/monero-project/monero/issues/8311 - .filter( - |tx| if res.as_hex.len() == 0 { - match tx.prefix.inputs.get(0) { - Some(Input::Gen { .. }) => true, - _ => false - } - } else { - true - } - ) - ).collect() - ) + txs.txs.iter().enumerate().map(|(i, res)| { + let tx = Transaction::deserialize( + &mut std::io::Cursor::new( + rpc_hex(if res.as_hex.len() != 0 { &res.as_hex } else { &res.pruned_as_hex }).unwrap() + ) + ).map_err(|_| RpcError::InvalidTransaction(hashes[i]))?; + + // https://github.com/monero-project/monero/issues/8311 + if res.as_hex.len() == 0 { + match tx.prefix.inputs.get(0) { + Some(Input::Gen { .. }) => (), + _ => Err(RpcError::PrunedTransaction)? + } + } + + Ok(tx) + }).collect::>() } pub async fn get_block(&self, height: usize) -> Result { @@ -283,7 +284,7 @@ impl Rpc { }))).await?; if res.status != "OK" { - Err(RpcError::InvalidTransaction)?; + Err(RpcError::InvalidTransaction(tx.hash()))?; } Ok(()) diff --git a/coins/monero/src/transaction.rs b/coins/monero/src/transaction.rs index 9decc833..baa9c6e1 100644 --- a/coins/monero/src/transaction.rs +++ b/coins/monero/src/transaction.rs @@ -134,23 +134,33 @@ pub struct RctBase { impl RctBase { pub fn serialize(&self, w: &mut W, rct_type: u8) -> std::io::Result<()> { w.write_all(&[rct_type])?; - write_varint(&self.fee, w)?; - for ecdh in &self.ecdh_info { - w.write_all(ecdh)?; + match rct_type { + 0 => Ok(()), + 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(outputs: usize, r: &mut R) -> std::io::Result<(RctBase, u8)> { let mut rct_type = [0]; r.read_exact(&mut rct_type)?; Ok(( - RctBase { - fee: read_varint(r)?, - ecdh_info: (0 .. outputs).map( - |_| { let mut ecdh = [0; 8]; r.read_exact(&mut ecdh).map(|_| ecdh) } - ).collect::>()?, - commitments: read_raw_vec(read_point, outputs, r)? + if rct_type[0] == 0 { + RctBase { fee: 0, ecdh_info: vec![], commitments: vec![] } + } else { + RctBase { + fee: read_varint(r)?, + ecdh_info: (0 .. outputs).map( + |_| { let mut ecdh = [0; 8]; r.read_exact(&mut ecdh).map(|_| ecdh) } + ).collect::>()?, + commitments: read_raw_vec(read_point, outputs, r)? + } }, rct_type[0] )) @@ -189,7 +199,6 @@ impl RctPrunable { pub fn deserialize( rct_type: u8, decoys: &[usize], - outputs: usize, r: &mut R ) -> std::io::Result { Ok( @@ -199,7 +208,7 @@ impl RctPrunable { // TODO: Can the amount of outputs be calculated from the BPs for any validly formed TX? bulletproofs: read_vec(Bulletproofs::deserialize, r)?, clsags: (0 .. decoys.len()).map(|o| Clsag::deserialize(decoys[o], r)).collect::>()?, - 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"))? } @@ -228,7 +237,7 @@ impl RctSignatures { pub fn deserialize(decoys: Vec, outputs: usize, r: &mut R) -> std::io::Result { 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)); serialized.clear(); - self.rct_signatures.prunable.serialize(&mut serialized).unwrap(); - sig_hash.extend(hash(&serialized)); + match self.rct_signatures.prunable { + 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) }