1use crate::ledger::Ledger;
2use crate::tx::{verify_transfer_sig, TransferTransaction, TxError};
3use serde::{Deserialize, Serialize};
4use std::collections::{BTreeMap, HashMap, HashSet};
5use std::sync::RwLock;
6use tokio::sync::RwLock as AsyncRwLock;
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
9struct MempoolEntry {
10 tx: TransferTransaction,
11 pending_nonces: HashMap<String, u64>,
12}
13
14pub struct Mempool {
16 txs: RwLock<HashMap<[u8; 32], TransferTransaction>>,
17 address_nonces: RwLock<HashMap<String, u64>>,
18 by_fee: RwLock<BTreeMap<(u64, u64), [u8; 32]>>, max_size: usize,
20 min_fee: u64,
21 path: RwLock<String>,
22 modified: RwLock<bool>,
23}
24
25impl Mempool {
26 pub fn new(max_size: usize, min_fee: u64) -> Self {
27 Mempool {
28 txs: RwLock::new(HashMap::new()),
29 address_nonces: RwLock::new(HashMap::new()),
30 by_fee: RwLock::new(BTreeMap::new()),
31 max_size,
32 min_fee,
33 path: RwLock::new(String::new()),
34 modified: RwLock::new(false),
35 }
36 }
37
38 pub fn set_path(&self, path: &str) {
39 *self.path.write().unwrap() = path.to_string();
40 }
41
42 pub fn load_from_file(&self, path: &str) {
43 let content = std::fs::read_to_string(path).ok();
44 if let Some(ref s) = content {
45 if let Ok(entries) = serde_json::from_str::<Vec<MempoolEntry>>(s) {
46 let mut txs = self.txs.write().unwrap();
47 let mut by_fee = self.by_fee.write().unwrap();
48 let mut nonces = self.address_nonces.write().unwrap();
49 let base_time = std::time::SystemTime::now()
50 .duration_since(std::time::UNIX_EPOCH)
51 .unwrap_or_default()
52 .as_nanos() as u64;
53 for (i, entry) in entries.into_iter().enumerate() {
54 let hash = entry.tx.tx_hash;
55 txs.insert(hash, entry.tx);
56 by_fee.insert((0, base_time + i as u64), hash);
57 for (addr, n) in entry.pending_nonces {
58 nonces.insert(addr, n);
59 }
60 }
61 }
62 }
63 *self.path.write().unwrap() = path.to_string();
64 }
65
66 pub fn save_to_file(&self) -> Result<(), String> {
67 let (path, entries) = {
68 let path = self.path.read().unwrap().clone();
69 if path.is_empty() {
70 return Ok(());
71 }
72 let txs = self.txs.read().unwrap();
73 let nonces = self.address_nonces.read().unwrap();
74 let entries: Vec<MempoolEntry> = txs
75 .values()
76 .map(|tx| MempoolEntry {
77 tx: tx.clone(),
78 pending_nonces: nonces.clone(),
79 })
80 .collect();
81 (path, entries)
82 };
83 let json = serde_json::to_string_pretty(&entries).map_err(|e| e.to_string())?;
84 let tmp_path = format!("{}.tmp", path);
85 std::fs::write(&tmp_path, &json).map_err(|e| e.to_string())?;
86 std::fs::rename(&tmp_path, &path).map_err(|e| e.to_string())?;
87 *self.modified.write().unwrap() = false;
88 Ok(())
89 }
90
91 pub fn is_modified(&self) -> bool {
92 *self.modified.read().unwrap()
93 }
94
95 pub async fn submit(
98 &self,
99 tx: TransferTransaction,
100 ledger: &AsyncRwLock<Ledger>,
101 ) -> Result<[u8; 32], TxError> {
102 if tx.fee < self.min_fee {
104 return Err(TxError::FeeTooLow);
105 }
106
107 if tx.amount == 0 {
109 return Err(TxError::AmountZero);
110 }
111
112 if tx.from == tx.to {
114 return Err(TxError::SelfTransfer);
115 }
116
117 verify_transfer_sig(&tx)?;
119
120 if self.txs.read().unwrap().contains_key(&tx.tx_hash) {
122 return Err(TxError::DuplicateTransaction);
123 }
124
125 let (ledger_nonce, balance) = {
128 let guard = ledger.read().await;
129 (
130 guard.current_nonce(&tx.from),
131 guard.balance_of(&tx.from, &tx.token),
132 )
133 };
134 let pending_count = self
135 .address_nonces
136 .read()
137 .unwrap()
138 .get(&tx.from)
139 .copied()
140 .unwrap_or(0);
141 let expected_nonce = ledger_nonce + pending_count;
142 if tx.nonce != expected_nonce {
143 return Err(TxError::InvalidNonce);
144 }
145
146 let pending_amount: u64 = self
147 .txs
148 .read()
149 .unwrap()
150 .values()
151 .filter(|t| t.from == tx.from && t.token == tx.token)
152 .map(|t| t.amount + t.fee)
153 .sum();
154 if balance < tx.amount + tx.fee + pending_amount {
155 return Err(TxError::InsufficientBalance);
156 }
157
158 let tx_hash = tx.tx_hash;
160 let fee = tx.fee;
161 let from = tx.from.clone();
162 self.txs.write().unwrap().insert(tx_hash, tx);
163 *self
164 .address_nonces
165 .write()
166 .unwrap()
167 .entry(from)
168 .or_insert(0) += 1;
169
170 let ts = std::time::SystemTime::now()
172 .duration_since(std::time::UNIX_EPOCH)
173 .unwrap_or_default()
174 .as_nanos() as u64;
175 self.by_fee.write().unwrap().insert((fee, ts), tx_hash);
176
177 self.evict_over_capacity();
179
180 *self.modified.write().unwrap() = true;
181
182 Ok(tx_hash)
183 }
184
185 pub fn drain_for_block(&self, max_count: usize, min_fee: u64) -> Vec<TransferTransaction> {
187 let mut result = Vec::new();
188 let mut to_remove = Vec::new();
189
190 let by_fee = self.by_fee.read().unwrap();
191 for ((fee, ts), hash) in by_fee.iter().rev() {
192 if *fee < min_fee {
193 break;
194 }
195 if result.len() >= max_count {
196 break;
197 }
198 if let Some(tx) = self.txs.read().unwrap().get(hash) {
199 result.push(tx.clone());
200 to_remove.push((*fee, *ts, *hash));
201 }
202 }
203 drop(by_fee);
204
205 let mut txs = self.txs.write().unwrap();
207 let mut by_fee_map = self.by_fee.write().unwrap();
208 let mut nonces = self.address_nonces.write().unwrap();
209 let remove_set: HashSet<[u8; 32]> = to_remove.iter().map(|(_, _, h)| *h).collect();
210 for hash in &remove_set {
211 if let Some(tx) = txs.remove(hash) {
212 let addr_count = nonces.get(&tx.from).copied().unwrap_or(1).saturating_sub(1);
213 if addr_count == 0 {
214 nonces.remove(&tx.from);
215 } else {
216 nonces.insert(tx.from, addr_count);
217 }
218 }
219 }
220 by_fee_map.retain(|_k, v| !remove_set.contains(v));
221
222 *self.modified.write().unwrap() = true;
223 result
224 }
225
226 pub fn remove(&self, tx_hashes: &[[u8; 32]]) {
228 let mut txs = self.txs.write().unwrap();
229 let mut by_fee_map = self.by_fee.write().unwrap();
230 let mut nonces = self.address_nonces.write().unwrap();
231
232 let remove_set: HashSet<[u8; 32]> = tx_hashes.iter().copied().collect();
233 for hash in &remove_set {
234 if let Some(tx) = txs.remove(hash) {
235 let addr_count = nonces.get(&tx.from).copied().unwrap_or(1).saturating_sub(1);
236 if addr_count == 0 {
237 nonces.remove(&tx.from);
238 } else {
239 nonces.insert(tx.from, addr_count);
240 }
241 }
242 }
243 by_fee_map.retain(|_k, v| !remove_set.contains(v));
244 *self.modified.write().unwrap() = true;
245 }
246
247 pub fn revalidate(&self, ledger: &AsyncRwLock<Ledger>) {
249 let (to_remove, _balance_map) = {
250 let ledger = ledger.blocking_read();
251 let mut to_remove = Vec::new();
252 let mut balance_map = HashMap::new();
253 let txs = self.txs.read().unwrap();
254 for (hash, tx) in txs.iter() {
255 let ledger_nonce = ledger.current_nonce(&tx.from);
256 if tx.nonce < ledger_nonce {
257 to_remove.push(*hash);
258 continue;
259 }
260 let balance = ledger.balance_of(&tx.from, &tx.token);
261 balance_map.insert(tx.from.clone(), balance);
262 if balance < tx.amount + tx.fee {
263 to_remove.push(*hash);
264 }
265 }
266 drop(txs);
267 (to_remove, balance_map)
268 };
269
270 let mut txs = self.txs.write().unwrap();
271 let mut by_fee_map = self.by_fee.write().unwrap();
272 let mut nonces = self.address_nonces.write().unwrap();
273
274 let remove_set: HashSet<[u8; 32]> = to_remove.iter().copied().collect();
275 for hash in &remove_set {
276 if let Some(tx) = txs.remove(hash) {
277 let addr_count = nonces.get(&tx.from).copied().unwrap_or(1).saturating_sub(1);
278 if addr_count == 0 {
279 nonces.remove(&tx.from);
280 } else {
281 nonces.insert(tx.from, addr_count);
282 }
283 }
284 }
285 by_fee_map.retain(|_k, v| !remove_set.contains(v));
286 *self.modified.write().unwrap() = true;
287 }
288
289 pub fn pending(&self) -> Vec<TransferTransaction> {
290 self.txs.read().unwrap().values().cloned().collect()
291 }
292
293 pub fn hashes(&self) -> Vec<[u8; 32]> {
294 self.txs.read().unwrap().keys().copied().collect()
295 }
296
297 pub fn get_tx(&self, hash: &[u8; 32]) -> Option<TransferTransaction> {
298 self.txs.read().unwrap().get(hash).cloned()
299 }
300
301 pub fn len(&self) -> usize {
302 self.txs.read().unwrap().len()
303 }
304
305 pub fn is_empty(&self) -> bool {
306 self.len() == 0
307 }
308
309 fn evict_over_capacity(&self) {
310 let count = self.txs.read().unwrap().len();
311 if count <= self.max_size {
312 return;
313 }
314 let excess = count - self.max_size;
315 let lowest_fee: Vec<[u8; 32]> = self
316 .by_fee
317 .read()
318 .unwrap()
319 .iter()
320 .take(excess)
321 .map(|(_, hash)| *hash)
322 .collect();
323 self.remove(&lowest_fee);
324 }
325}
326
327#[cfg(test)]
328mod tests {
329 use super::*;
330 use crate::ledger::Ledger;
331 use crate::tx::{hash_transfer, TokenType};
332
333 async fn test_mempool_submit_and_drain_impl(ledger: &AsyncRwLock<Ledger>) {
334 use ed25519_dalek::{Signer, SigningKey};
335
336 let signing_key = SigningKey::from_bytes(&[42u8; 32]);
337 let from = bs58::encode(signing_key.verifying_key().to_bytes()).into_string();
338 let to = bs58::encode([99u8; 32]).into_string();
339
340 ledger
341 .write()
342 .await
343 .issue(&from, &TokenType::TribeChain, 1000);
344 let mempool = Mempool::new(1000, 0);
345
346 let nonce = 0u64;
347 let amount = 100u64;
348 let fee = 1u64;
349 let timestamp = 0u64;
350
351 let tx_hash = hash_transfer(
352 &from,
353 nonce,
354 &to,
355 &TokenType::TribeChain,
356 amount,
357 fee,
358 timestamp,
359 );
360 let signature = signing_key.sign(&tx_hash).to_bytes().to_vec();
361
362 let tx = TransferTransaction {
363 tx_hash,
364 nonce,
365 from: from.clone(),
366 to,
367 token: TokenType::TribeChain,
368 amount,
369 fee,
370 signature,
371 timestamp,
372 };
373
374 let result = mempool.submit(tx, ledger).await;
375 assert!(result.is_ok(), "submit should succeed: {:?}", result);
376 assert_eq!(mempool.len(), 1);
377
378 let drained = mempool.drain_for_block(100, 0);
379 assert_eq!(drained.len(), 1);
380 assert_eq!(drained[0].from, from);
381 assert_eq!(mempool.len(), 0);
382 }
383
384 fn test_mempool_revalidate_impl(ledger: &AsyncRwLock<Ledger>) {
385 let mempool = Mempool::new(1000, 0);
386 assert_eq!(mempool.len(), 0);
387 mempool.revalidate(ledger);
388 assert_eq!(mempool.len(), 0);
389 }
390
391 #[tokio::test]
392 async fn test_mempool_submit_and_drain() {
393 let ledger = AsyncRwLock::new(Ledger::new("protocol".to_string()));
394 test_mempool_submit_and_drain_impl(&ledger).await;
395 }
396
397 #[test]
398 fn test_mempool_revalidate() {
399 let ledger = AsyncRwLock::new(Ledger::new("protocol".to_string()));
400 test_mempool_revalidate_impl(&ledger);
401 }
402}