mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 12:19:24 +00:00
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:
@@ -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),
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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,45 +99,67 @@ 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((
|
||||||
let tx = Transaction::deserialize(
|
txs.txs.iter().map(|res| {
|
||||||
&mut std::io::Cursor::new(
|
let tx = Transaction::deserialize(
|
||||||
rpc_hex(if res.as_hex.len() != 0 { &res.as_hex } else { &res.pruned_as_hex }).unwrap()
|
&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]))?;
|
)
|
||||||
|
).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 {
|
||||||
match tx.prefix.inputs.get(0) {
|
match tx.prefix.inputs.get(0) {
|
||||||
Some(Input::Gen { .. }) => (),
|
Some(Input::Gen { .. }) => (),
|
||||||
_ => Err(RpcError::PrunedTransaction)?
|
_ => Err(RpcError::PrunedTransaction)?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
Reference in New Issue
Block a user