ab_transaction_pool/
lib.rs1use ab_core_primitives::block::{BlockNumber, BlockRoot};
2use ab_core_primitives::transaction::TransactionHash;
3use ab_core_primitives::transaction::owned::OwnedTransaction;
4use std::collections::{HashMap, HashSet, VecDeque};
5use std::num::{NonZeroU8, NonZeroU64, NonZeroUsize};
6
7#[derive(Debug, Copy, Clone)]
9pub struct TransactionPoolLimits {
10 pub count: NonZeroUsize,
12 pub size: NonZeroUsize,
14}
15
16#[derive(Debug)]
17pub struct TransactionAuthorizedDetails {
18 pub block_number: BlockNumber,
20 pub block_root: BlockRoot,
22}
23
24#[derive(Debug)]
25pub enum TransactionState {
26 New,
27 Authorized {
28 at: VecDeque<TransactionAuthorizedDetails>,
29 },
30}
31
32#[derive(Debug)]
33#[non_exhaustive]
34pub struct PoolTransaction {
35 pub tx: OwnedTransaction,
36 pub state: TransactionState,
37 }
39
40#[derive(Debug, thiserror::Error)]
42pub enum TransactionAddError {
43 #[error("Already exists")]
45 AlreadyExists,
46 #[error("The block isn't found, possibly too old")]
48 BlockNotFound,
49 #[error("Too many transactions")]
51 TooManyTransactions,
52 #[error("Total size too large")]
54 TotalSizeTooLarge,
55}
56
57#[derive(Debug)]
58struct BlockRootDetails {
59 txs: HashSet<TransactionHash>,
60}
61
62#[derive(Debug)]
69pub struct TransactionPool {
70 transactions: HashMap<TransactionHash, PoolTransaction>,
71 total_size: usize,
72 by_block_root: HashMap<BlockRoot, BlockRootDetails>,
74 by_block_number: HashMap<BlockNumber, BlockRoot>,
76 pruning_depth: NonZeroU64,
77 authorization_history_depth: NonZeroU8,
78 limits: TransactionPoolLimits,
79}
80
81impl TransactionPool {
82 pub fn new(
92 pruning_depth: NonZeroU64,
93 authorization_history_depth: NonZeroU8,
94 limits: TransactionPoolLimits,
95 ) -> Self {
96 Self {
97 transactions: HashMap::default(),
98 total_size: 0,
99 by_block_root: HashMap::default(),
100 by_block_number: HashMap::default(),
101 pruning_depth,
102 authorization_history_depth,
103 limits,
104 }
105 }
106
107 pub fn add(&mut self, tx: OwnedTransaction) -> Result<(), TransactionAddError> {
109 let tx_hash = tx.hash();
110 if self.contains(&tx_hash) {
111 return Err(TransactionAddError::AlreadyExists);
112 }
113
114 let block_root = tx.transaction().header.block_root;
115 let Some(block_txs) = self.by_block_root.get_mut(&block_root) else {
116 return Err(TransactionAddError::BlockNotFound);
117 };
118
119 if self.transactions.len() == self.limits.count.get() {
120 return Err(TransactionAddError::TooManyTransactions);
121 }
122
123 let tx_size = tx.buffer().len() as usize;
124 if self.limits.size.get() - self.total_size < tx_size {
125 return Err(TransactionAddError::TotalSizeTooLarge);
126 }
127
128 self.total_size += tx_size;
129 self.transactions.insert(
130 tx_hash,
131 PoolTransaction {
132 tx,
133 state: TransactionState::New,
134 },
135 );
136 block_txs.txs.insert(tx_hash);
137
138 Ok(())
139 }
140
141 pub fn mark_authorized(
145 &mut self,
146 tx_hash: &TransactionHash,
147 block_number: BlockNumber,
148 block_root: BlockRoot,
149 ) -> bool {
150 let Some(tx) = self.transactions.get_mut(tx_hash) else {
151 return false;
152 };
153
154 let authorized_details = TransactionAuthorizedDetails {
155 block_number,
156 block_root,
157 };
158
159 match &mut tx.state {
160 TransactionState::New => {
161 tx.state = TransactionState::Authorized {
162 at: VecDeque::from([authorized_details]),
163 };
164 }
165 TransactionState::Authorized { at } => {
166 if at.len() == usize::from(self.authorization_history_depth.get()) {
167 at.pop_back();
168 }
169 at.push_front(authorized_details);
170 }
171 }
172
173 true
174 }
175
176 pub fn contains(&self, tx_hash: &TransactionHash) -> bool {
178 self.transactions.contains_key(tx_hash)
179 }
180
181 pub fn iter(
183 &self,
184 ) -> impl ExactSizeIterator<Item = (&'_ TransactionHash, &'_ PoolTransaction)> + '_ {
185 self.transactions.iter()
186 }
187
188 pub fn remove<'a, Txs>(&mut self, tx_hashes: Txs)
190 where
191 Txs: Iterator<Item = &'a TransactionHash>,
192 {
193 for tx_hash in tx_hashes {
194 self.remove_single_tx(tx_hash);
195 }
196 }
197
198 fn remove_single_tx(&mut self, tx_hash: &TransactionHash) {
199 if let Some(tx) = self.transactions.remove(tx_hash) {
200 self.total_size -= tx.tx.buffer().len() as usize;
201
202 let block_root = &tx.tx.transaction().header.block_root;
203 if let Some(set) = self.by_block_root.get_mut(block_root) {
204 set.txs.remove(tx_hash);
205 if set.txs.is_empty() {
206 self.by_block_root.remove(block_root);
207 }
208 }
209 }
210 }
211
212 pub fn add_best_block(&mut self, block_number: BlockNumber, block_root: BlockRoot) {
220 let allowed_blocks =
222 block_number.saturating_sub(BlockNumber::from(self.pruning_depth.get()))..block_number;
223 self.by_block_number
224 .retain(|existing_block_number, existing_block_root| {
225 if allowed_blocks.contains(existing_block_number) {
226 return true;
227 }
228
229 if let Some(tx_hashes) = self.by_block_root.remove(existing_block_root) {
230 for tx_hash in tx_hashes.txs {
231 if let Some(tx) = self.transactions.remove(&tx_hash) {
232 self.total_size -= tx.tx.buffer().len() as usize;
233 }
234 }
235 }
236 false
237 });
238
239 for tx in self.transactions.values_mut() {
240 if let TransactionState::Authorized { at } = &mut tx.state {
241 while at
243 .pop_front_if(|details| details.block_number >= block_number)
244 .is_some()
245 {}
246 }
247 }
248
249 self.by_block_number.insert(block_number, block_root);
250 self.by_block_root.insert(
251 block_root,
252 BlockRootDetails {
253 txs: HashSet::new(),
254 },
255 );
256 }
257}