Add get_block_transactions_possible which automatically filters invalid TXs

Adds Clone to the various error types, which they already should've had.
This commit is contained in:
Luke Parker
2022-05-28 05:08:37 -04:00
parent ba032cca4a
commit e950b9682b
5 changed files with 70 additions and 30 deletions

View File

@@ -22,7 +22,7 @@ use crate::random_scalar;
pub type Transcript = DigestTranscript::<blake2::Blake2b512>; pub type Transcript = DigestTranscript::<blake2::Blake2b512>;
#[derive(Error, Debug)] #[derive(Clone, Error, Debug)]
pub enum MultisigError { pub enum MultisigError {
#[error("internal error ({0})")] #[error("internal error ({0})")]
InternalError(String), InternalError(String),

View File

@@ -27,7 +27,7 @@ lazy_static! {
static ref INV_EIGHT: Scalar = Scalar::from(8 as u8).invert(); static ref INV_EIGHT: Scalar = Scalar::from(8 as u8).invert();
} }
#[derive(Error, Debug)] #[derive(Clone, Error, Debug)]
pub enum ClsagError { pub enum ClsagError {
#[error("internal error ({0})")] #[error("internal error ({0})")]
InternalError(String), InternalError(String),

View File

@@ -18,14 +18,14 @@ pub struct JsonRpcResponse<T> {
result: T result: T
} }
#[derive(Error, Debug)] #[derive(Clone, Error, Debug)]
pub enum RpcError { pub enum RpcError {
#[error("internal error ({0})")] #[error("internal error ({0})")]
InternalError(String), InternalError(String),
#[error("connection error")] #[error("connection error")]
ConnectionError, ConnectionError,
#[error("transaction not found (expected {1}, got {0})")] #[error("transactions not found")]
TransactionsNotFound(usize, usize), TransactionsNotFound(Vec<[u8; 32]>),
#[error("invalid point ({0})")] #[error("invalid point ({0})")]
InvalidPoint(String), InvalidPoint(String),
#[error("pruned transaction")] #[error("pruned transaction")]
@@ -99,34 +99,38 @@ impl Rpc {
Ok(self.rpc_call::<Option<()>, HeightResponse>("get_height", None).await?.height) Ok(self.rpc_call::<Option<()>, HeightResponse>("get_height", None).await?.height)
} }
pub async fn get_transactions(&self, hashes: &[[u8; 32]]) -> Result<Vec<Transaction>, RpcError> { async fn get_transactions_core(
&self,
hashes: &[[u8; 32]]
) -> Result<(Vec<Result<Transaction, RpcError>>, Vec<[u8; 32]>), RpcError> {
if hashes.len() == 0 { if hashes.len() == 0 {
return Ok(vec![]); return Ok((vec![], vec![]));
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
struct TransactionResponse { struct TransactionResponse {
tx_hash: String,
as_hex: String, as_hex: String,
pruned_as_hex: String pruned_as_hex: String
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
struct TransactionsResponse { struct TransactionsResponse {
#[serde(default)]
missed_tx: Vec<String>,
txs: Vec<TransactionResponse> txs: Vec<TransactionResponse>
} }
let txs: TransactionsResponse = self.rpc_call("get_transactions", Some(json!({ let txs: TransactionsResponse = self.rpc_call("get_transactions", Some(json!({
"txs_hashes": hashes.iter().map(|hash| hex::encode(&hash)).collect::<Vec<String>>() "txs_hashes": hashes.iter().map(|hash| hex::encode(&hash)).collect::<Vec<_>>()
}))).await?; }))).await?;
if txs.txs.len() != hashes.len() {
Err(RpcError::TransactionsNotFound(txs.txs.len(), hashes.len()))?;
}
txs.txs.iter().enumerate().map(|(i, res)| { Ok((
txs.txs.iter().map(|res| {
let tx = Transaction::deserialize( let tx = Transaction::deserialize(
&mut std::io::Cursor::new( &mut std::io::Cursor::new(
rpc_hex(if res.as_hex.len() != 0 { &res.as_hex } else { &res.pruned_as_hex }).unwrap() rpc_hex(if res.as_hex.len() != 0 { &res.as_hex } else { &res.pruned_as_hex }).unwrap()
) )
).map_err(|_| RpcError::InvalidTransaction(hashes[i]))?; ).map_err(|_| RpcError::InvalidTransaction(hex::decode(&res.tx_hash).unwrap().try_into().unwrap()))?;
// https://github.com/monero-project/monero/issues/8311 // https://github.com/monero-project/monero/issues/8311
if res.as_hex.len() == 0 { if res.as_hex.len() == 0 {
@@ -137,7 +141,25 @@ impl Rpc {
} }
Ok(tx) Ok(tx)
}).collect::<Result<_, _>>() }).collect(),
txs.missed_tx.iter().map(|hash| hex::decode(&hash).unwrap().try_into().unwrap()).collect()
))
}
pub async fn get_transactions(&self, hashes: &[[u8; 32]]) -> Result<Vec<Transaction>, RpcError> {
let (txs, missed) = self.get_transactions_core(hashes).await?;
if missed.len() != 0 {
Err(RpcError::TransactionsNotFound(missed))?;
}
// This will clone several KB and is accordingly inefficient
// TODO: Optimize
txs.iter().cloned().collect::<Result<_, _>>()
}
pub async fn get_transactions_possible(&self, hashes: &[[u8; 32]]) -> Result<Vec<Transaction>, RpcError> {
let (txs, _) = self.get_transactions_core(hashes).await?;
Ok(txs.iter().cloned().filter_map(|tx| tx.ok()).collect())
} }
pub async fn get_block(&self, height: usize) -> Result<Block, RpcError> { pub async fn get_block(&self, height: usize) -> Result<Block, RpcError> {
@@ -160,13 +182,31 @@ impl Rpc {
) )
} }
pub async fn get_block_transactions(&self, height: usize) -> Result<Vec<Transaction>, RpcError> { async fn get_block_transactions_core(
&self,
height: usize,
possible: bool
) -> Result<Vec<Transaction>, RpcError> {
let block = self.get_block(height).await?; let block = self.get_block(height).await?;
let mut res = vec![block.miner_tx]; let mut res = vec![block.miner_tx];
res.extend(self.get_transactions(&block.txs).await?); res.extend(
if possible {
self.get_transactions_possible(&block.txs).await?
} else {
self.get_transactions(&block.txs).await?
}
);
Ok(res) Ok(res)
} }
pub async fn get_block_transactions(&self, height: usize) -> Result<Vec<Transaction>, RpcError> {
self.get_block_transactions_core(height, false).await
}
pub async fn get_block_transactions_possible(&self, height: usize) -> Result<Vec<Transaction>, RpcError> {
self.get_block_transactions_core(height, true).await
}
pub async fn get_o_indexes(&self, hash: [u8; 32]) -> Result<Vec<u64>, RpcError> { pub async fn get_o_indexes(&self, hash: [u8; 32]) -> Result<Vec<u64>, RpcError> {
#[derive(Serialize, Debug)] #[derive(Serialize, Debug)]
struct Request { struct Request {

View File

@@ -76,7 +76,7 @@ impl SendOutput {
} }
#[derive(Error, Debug)] #[derive(Clone, Error, Debug)]
pub enum TransactionError { pub enum TransactionError {
#[error("no inputs")] #[error("no inputs")]
NoInputs, NoInputs,

View File

@@ -15,7 +15,7 @@ pub mod sign;
pub mod tests; pub mod tests;
/// Set of errors for curve-related operations, namely encoding and decoding /// Set of errors for curve-related operations, namely encoding and decoding
#[derive(Error, Debug)] #[derive(Clone, Error, Debug)]
pub enum CurveError { pub enum CurveError {
#[error("invalid length for data (expected {0}, got {0})")] #[error("invalid length for data (expected {0}, got {0})")]
InvalidLength(usize, usize), InvalidLength(usize, usize),
@@ -150,7 +150,7 @@ impl MultisigParams {
pub fn i(&self) -> u16 { self.i } pub fn i(&self) -> u16 { self.i }
} }
#[derive(Error, Debug)] #[derive(Clone, Error, Debug)]
pub enum FrostError { pub enum FrostError {
#[error("a parameter was 0 (required {0}, participants {1})")] #[error("a parameter was 0 (required {0}, participants {1})")]
ZeroParameter(u16, u16), ZeroParameter(u16, u16),