1use crate::TxPoolError;
10use std::sync::RwLock;
11use subxt::config::substrate::H256;
12
13#[derive(Default)]
19pub struct TxPool {
20 pending: RwLock<Vec<Vec<u8>>>,
21}
22
23impl TxPool {
24 pub fn new() -> Self {
26 Self { pending: RwLock::new(Vec::new()) }
27 }
28
29 pub fn submit(&self, extrinsic: Vec<u8>) -> Result<H256, TxPoolError> {
33 let hash = H256::from(sp_core::blake2_256(&extrinsic));
34 self.pending
35 .write()
36 .map_err(|err| TxPoolError::Lock(err.to_string()))?
37 .push(extrinsic);
38 Ok(hash)
39 }
40
41 pub fn drain(&self) -> Result<Vec<Vec<u8>>, TxPoolError> {
46 Ok(std::mem::take(
47 &mut *self.pending.write().map_err(|err| TxPoolError::Lock(err.to_string()))?,
48 ))
49 }
50
51 pub fn submit_and_drain(
59 &self,
60 extrinsic: Vec<u8>,
61 ) -> Result<(H256, Vec<Vec<u8>>), TxPoolError> {
62 let hash = H256::from(sp_core::blake2_256(&extrinsic));
63 let mut pending = self.pending.write().map_err(|err| TxPoolError::Lock(err.to_string()))?;
64 pending.push(extrinsic);
65 let all = std::mem::take(&mut *pending);
66 Ok((hash, all))
67 }
68
69 pub fn pending(&self) -> Result<Vec<Vec<u8>>, TxPoolError> {
71 Ok(self.pending.read().map_err(|err| TxPoolError::Lock(err.to_string()))?.clone())
72 }
73
74 pub fn len(&self) -> Result<usize, TxPoolError> {
76 Ok(self.pending.read().map_err(|err| TxPoolError::Lock(err.to_string()))?.len())
77 }
78
79 pub fn is_empty(&self) -> Result<bool, TxPoolError> {
81 Ok(self.len()? == 0)
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88
89 #[test]
90 fn submit_returns_correct_hash() {
91 let pool = TxPool::new();
92 let extrinsic = vec![1, 2, 3, 4];
93 let expected_hash = H256::from(sp_core::blake2_256(&extrinsic));
94
95 let hash = pool.submit(extrinsic).unwrap();
96
97 assert_eq!(hash, expected_hash);
98 }
99
100 #[test]
101 fn drain_returns_all_extrinsics_in_fifo_order() {
102 let pool = TxPool::new();
103 pool.submit(vec![1]).unwrap();
104 pool.submit(vec![2]).unwrap();
105 pool.submit(vec![3]).unwrap();
106
107 let drained = pool.drain().unwrap();
108
109 assert_eq!(drained, vec![vec![1], vec![2], vec![3]]);
110 assert!(pool.is_empty().unwrap());
111 }
112
113 #[test]
114 fn pending_returns_extrinsics_without_removing() {
115 let pool = TxPool::new();
116 pool.submit(vec![1, 2]).unwrap();
117 pool.submit(vec![3, 4]).unwrap();
118
119 let pending = pool.pending().unwrap();
120
121 assert_eq!(pending, vec![vec![1, 2], vec![3, 4]]);
122 assert_eq!(pool.len().unwrap(), 2);
123 }
124
125 #[test]
126 fn submit_and_drain_returns_hash_and_all_pending() {
127 let pool = TxPool::new();
128 pool.submit(vec![1]).unwrap();
129 pool.submit(vec![2]).unwrap();
130
131 let new_extrinsic = vec![3, 4, 5];
132 let expected_hash = H256::from(sp_core::blake2_256(&new_extrinsic));
133
134 let (hash, drained) = pool.submit_and_drain(new_extrinsic).unwrap();
135
136 assert_eq!(hash, expected_hash);
137 assert_eq!(drained, vec![vec![1], vec![2], vec![3, 4, 5]]);
138 assert!(pool.is_empty().unwrap());
139 }
140}