use crate::TxPoolError;
use std::sync::RwLock;
use subxt::config::substrate::H256;
#[derive(Default)]
pub struct TxPool {
pending: RwLock<Vec<Vec<u8>>>,
}
impl TxPool {
pub fn new() -> Self {
Self { pending: RwLock::new(Vec::new()) }
}
pub fn submit(&self, extrinsic: Vec<u8>) -> Result<H256, TxPoolError> {
let hash = H256::from(sp_core::blake2_256(&extrinsic));
self.pending
.write()
.map_err(|err| TxPoolError::Lock(err.to_string()))?
.push(extrinsic);
Ok(hash)
}
pub fn drain(&self) -> Result<Vec<Vec<u8>>, TxPoolError> {
Ok(std::mem::take(
&mut *self.pending.write().map_err(|err| TxPoolError::Lock(err.to_string()))?,
))
}
pub fn submit_and_drain(
&self,
extrinsic: Vec<u8>,
) -> Result<(H256, Vec<Vec<u8>>), TxPoolError> {
let hash = H256::from(sp_core::blake2_256(&extrinsic));
let mut pending = self.pending.write().map_err(|err| TxPoolError::Lock(err.to_string()))?;
pending.push(extrinsic);
let all = std::mem::take(&mut *pending);
Ok((hash, all))
}
pub fn pending(&self) -> Result<Vec<Vec<u8>>, TxPoolError> {
Ok(self.pending.read().map_err(|err| TxPoolError::Lock(err.to_string()))?.clone())
}
pub fn len(&self) -> Result<usize, TxPoolError> {
Ok(self.pending.read().map_err(|err| TxPoolError::Lock(err.to_string()))?.len())
}
pub fn is_empty(&self) -> Result<bool, TxPoolError> {
Ok(self.len()? == 0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn submit_returns_correct_hash() {
let pool = TxPool::new();
let extrinsic = vec![1, 2, 3, 4];
let expected_hash = H256::from(sp_core::blake2_256(&extrinsic));
let hash = pool.submit(extrinsic).unwrap();
assert_eq!(hash, expected_hash);
}
#[test]
fn drain_returns_all_extrinsics_in_fifo_order() {
let pool = TxPool::new();
pool.submit(vec![1]).unwrap();
pool.submit(vec![2]).unwrap();
pool.submit(vec![3]).unwrap();
let drained = pool.drain().unwrap();
assert_eq!(drained, vec![vec![1], vec![2], vec![3]]);
assert!(pool.is_empty().unwrap());
}
#[test]
fn pending_returns_extrinsics_without_removing() {
let pool = TxPool::new();
pool.submit(vec![1, 2]).unwrap();
pool.submit(vec![3, 4]).unwrap();
let pending = pool.pending().unwrap();
assert_eq!(pending, vec![vec![1, 2], vec![3, 4]]);
assert_eq!(pool.len().unwrap(), 2);
}
#[test]
fn submit_and_drain_returns_hash_and_all_pending() {
let pool = TxPool::new();
pool.submit(vec![1]).unwrap();
pool.submit(vec![2]).unwrap();
let new_extrinsic = vec![3, 4, 5];
let expected_hash = H256::from(sp_core::blake2_256(&new_extrinsic));
let (hash, drained) = pool.submit_and_drain(new_extrinsic).unwrap();
assert_eq!(hash, expected_hash);
assert_eq!(drained, vec![vec![1], vec![2], vec![3, 4, 5]]);
assert!(pool.is_empty().unwrap());
}
}