use std::collections::{HashSet, HashMap}; use ciphersuite::{Ciphersuite, Ristretto}; use crate::{Signed, TransactionKind, Transaction, verify_transaction}; #[derive(Clone, PartialEq, Eq, Debug)] pub struct Mempool { genesis: [u8; 32], txs: HashMap<[u8; 32], T>, next_nonces: HashMap<::G, u32>, } impl Mempool { pub fn new(genesis: [u8; 32]) -> Self { Mempool { genesis, txs: HashMap::new(), next_nonces: HashMap::new() } } /// Returns true if this is a valid, new transaction. pub fn add( &mut self, blockchain_next_nonces: &HashMap<::G, u32>, tx: T, ) -> bool { match tx.kind() { TransactionKind::Signed(Signed { signer, nonce, .. }) => { // If the mempool doesn't have a nonce tracked, grab it from the blockchain if !self.next_nonces.contains_key(signer) { let Some(blockchain_next_nonces) = blockchain_next_nonces.get(signer).cloned() else { // Not a participant return false; }; self.next_nonces.insert(*signer, blockchain_next_nonces); } if verify_transaction(&tx, self.genesis, &mut HashSet::new(), &mut self.next_nonces) .is_err() { return false; } assert_eq!(self.next_nonces[signer], nonce + 1); self.txs.insert(tx.hash(), tx); true } _ => false, } } // Returns None if the mempool doesn't have a nonce tracked. // The nonce to use when signing should be: // max(blockchain.next_nonce().unwrap(), mempool.next_nonce().unwrap_or(0)) pub fn next_nonce(&self, signer: &::G) -> Option { self.next_nonces.get(signer).cloned() } /// Get transactions to include in a block. pub fn block( &mut self, blockchain_next_nonces: &HashMap<::G, u32>, ) -> HashMap<[u8; 32], T> { let mut res = HashMap::new(); for hash in self.txs.keys().cloned().collect::>() { let tx = &self.txs[&hash]; // Verify this hasn't gone stale match tx.kind() { TransactionKind::Signed(Signed { signer, nonce, .. }) => { if blockchain_next_nonces[signer] > *nonce { self.txs.remove(&hash); continue; } } _ => panic!("non-signed transaction entered mempool"), } // Since this TX isn't stale, include it res.insert(hash, tx.clone()); } res } /// Remove a transaction from the mempool. pub fn remove(&mut self, tx: &[u8; 32]) { self.txs.remove(tx); } #[cfg(test)] pub(crate) fn txs(&self) -> &HashMap<[u8; 32], T> { &self.txs } }