use std::{collections::HashSet, sync::Arc};
use bitcoincash::Txid;
use serde_json::Value;
use crate::{errors::rpc_invalid_params, mempool::Tracker};
use super::parseutil::{hex_from_value, str_from_value_or_none};
use anyhow::Result;
pub struct MempoolRPC {
mempool: Arc<Tracker>,
}
const FILTER_SCRIPTSIG: &str = "scriptsig";
const FILTER_SCRIPTPUBKEY: &str = "scriptpubkey";
const FILTER_OPERATION: &str = "operation";
const FILTER_OPERATION_UNION: &str = "union";
const FILTER_OPERATION_EXCEPT: &str = "except";
enum SetOperation {
Union,
Except,
}
impl MempoolRPC {
pub fn new(mempool: Arc<Tracker>) -> Self {
Self { mempool }
}
pub async fn mempool_get(&self, params: &[Value]) -> Result<Value> {
let filter = if let Some(p) = params.first() {
if p.is_null() {
None
} else if let Some(p) = p.as_object() {
Some(p)
} else {
return Err(rpc_invalid_params(
"invalid value for filter; must be null or dict".to_string(),
));
}
} else {
None
};
let txs = if let Some(filter) = filter {
let mut valid_filter = false;
let mut results: Vec<Vec<Txid>> = Vec::default();
let setoperation =
match str_from_value_or_none(filter.get(FILTER_OPERATION), FILTER_OPERATION)? {
Some(operation) => match operation.as_str() {
FILTER_OPERATION_EXCEPT => SetOperation::Except,
FILTER_OPERATION_UNION => SetOperation::Union,
_ => return Err(rpc_invalid_params("invalid operation param".to_string())),
},
None => SetOperation::Except,
};
if let Some(scriptsig) = filter.get(FILTER_SCRIPTSIG) {
let filter = hex_from_value(Some(scriptsig), FILTER_SCRIPTSIG)?;
valid_filter = true;
results.push(
self.mempool
.get_txn_partially_matching_scriptsig(&filter)
.await,
);
}
if let Some(scriptpubkey) = filter.get(FILTER_SCRIPTPUBKEY) {
let filter = hex_from_value(Some(scriptpubkey), FILTER_SCRIPTPUBKEY)?;
valid_filter = true;
results.push(
self.mempool
.get_txn_partially_matching_scriptpubkey(&filter)
.await,
);
}
if !valid_filter {
return Err(rpc_invalid_params("Unknown filter parameters".to_string()));
}
match setoperation {
SetOperation::Union => {
let union: HashSet<Txid> = results.into_iter().flatten().collect();
union.into_iter().collect()
}
SetOperation::Except => {
let mut iter = results.into_iter();
let mut except: HashSet<Txid> =
iter.next().unwrap_or_default().into_iter().collect();
for result in iter {
let current_set: HashSet<_> = result.into_iter().collect();
except = except.intersection(¤t_set).cloned().collect();
}
except.into_iter().collect()
}
}
} else {
self.mempool.get_all_txn().await
};
Ok(json!({
"transactions": txs,
}))
}
pub async fn mempool_count(&self) -> Result<Value> {
Ok(json!({"count": self.mempool.get_count().await}))
}
pub(crate) async fn mempool_get_fee_histogram(&self) -> Value {
json!(self.mempool.fee_histogram().await)
}
}