use std::{
collections::{HashMap, HashSet},
iter,
};
use elements::OutPoint;
use crate::{
be,
store::{AnyStore, Store},
ScriptHash, TxSeen, V,
};
pub struct Mempool {
txid_hashes: HashMap<crate::be::Txid, HashSet<ScriptHash>>,
hash_txids: HashMap<ScriptHash, Vec<(crate::be::Txid, i32)>>,
outpoints_created: HashMap<OutPoint, ScriptHash>,
}
impl Default for Mempool {
fn default() -> Self {
Self::new()
}
}
impl Mempool {
pub fn new() -> Mempool {
Mempool {
txid_hashes: HashMap::new(),
hash_txids: HashMap::new(),
outpoints_created: HashMap::new(),
}
}
pub fn remove(&mut self, txids: &[crate::be::Txid]) {
for txid in txids {
if let Some(hashes) = self.txid_hashes.remove(txid) {
for hash in hashes {
if let Some(txid_positions) = self.hash_txids.get_mut(&hash) {
txid_positions.retain(|(tx, _)| tx != txid);
if txid_positions.is_empty() {
self.hash_txids.remove(&hash);
}
}
}
}
}
self.outpoints_created
.retain(|k, _| !txids.contains(&k.txid.into()));
}
pub fn add(&mut self, db: &AnyStore, txs: &[(crate::be::Txid, be::Transaction)]) {
let txs_map: HashMap<crate::be::Txid, &be::Transaction> =
txs.iter().map(|(txid, tx)| (*txid, tx)).collect();
let outputs_created = txs_map
.iter()
.flat_map(|(txid, tx)| tx.outputs_iter().enumerate().zip(iter::repeat(txid)))
.map(|((vout, txout), txid)| {
(
OutPoint::new(txid.elements(), vout as u32),
db.hash(txout.script_pubkey_bytes()),
)
});
self.outpoints_created.extend(outputs_created);
let mut txid_hashes: HashMap<crate::be::Txid, HashSet<ScriptHash>> = HashMap::new();
let mut txid_script_positions: HashMap<crate::be::Txid, Vec<(ScriptHash, i32)>> =
HashMap::new();
let prevouts: Vec<OutPoint> = txs
.iter()
.flat_map(|e| e.1.inputs_iter())
.map(|i| i.previous_output())
.collect();
let spending_script_hashes = db.get_utxos(&prevouts).unwrap();
let mut prevouts_index = 0usize;
for (txid, tx) in txs_map {
for (vin, input) in tx.inputs_iter().enumerate() {
let e = match spending_script_hashes[prevouts_index] {
Some(e) => e,
None => {
match self.outpoints_created.get(&input.previous_output()) {
Some(e) => *e,
None => {
prevouts_index += 1;
continue;
}
}
}
};
txid_hashes.entry(txid).or_default().insert(e);
txid_script_positions
.entry(txid)
.or_default()
.push((e, -(vin as i32) - 1));
prevouts_index += 1;
}
for (vout, output) in tx.outputs_iter().enumerate() {
let e = db.hash(output.script_pubkey_bytes());
txid_hashes.entry(txid).or_default().insert(e);
txid_script_positions
.entry(txid)
.or_default()
.push((e, vout as i32 + 1));
}
}
for (k, v) in txid_hashes {
self.txid_hashes.entry(k).or_default().extend(&v);
}
for (txid, script_positions) in txid_script_positions {
for (script_hash, position) in script_positions {
self.hash_txids
.entry(script_hash)
.or_default()
.push((txid, position));
}
}
}
pub fn seen(&self, script_hashes: &[ScriptHash]) -> Vec<Vec<TxSeen>> {
let mut result = vec![];
for h in script_hashes {
let txid_positions = self.hash_txids.get(h).cloned().unwrap_or(vec![]);
let tx_seens: Vec<TxSeen> = txid_positions
.into_iter()
.map(|(txid, position)| TxSeen::mempool(txid, V::from_raw(position)))
.collect();
result.push(tx_seens);
}
result
}
pub(crate) fn txids_iter(&self) -> impl Iterator<Item = crate::be::Txid> + '_ {
self.txid_hashes.keys().cloned()
}
}
#[cfg(test)]
mod test {
#[test]
fn test_mempool() {}
}