use std::collections::HashMap;
use stratum_core::bitcoin::{Block, BlockHash, CompactTarget, Transaction, Wtxid};
#[derive(Default)]
pub struct MempoolMirror {
txdata: HashMap<Wtxid, Transaction>,
current_prev_hash: Option<BlockHash>,
current_nbits: Option<CompactTarget>,
current_min_ntime: Option<u32>,
current_bip34_height: Option<u32>,
}
impl MempoolMirror {
pub fn new() -> Self {
Default::default()
}
pub fn update(&mut self, block: &Block) {
let prev_hash = block.header.prev_blockhash;
if self.current_prev_hash != Some(prev_hash) {
self.txdata.clear();
}
self.current_prev_hash = Some(prev_hash);
self.current_nbits = Some(block.header.bits);
self.current_min_ntime = Some(block.header.time);
self.current_bip34_height = block.txdata.first().map(|coinbase| {
coinbase
.input
.first()
.and_then(|input| {
decode_bip34_height_from_coinbase_script_sig(input.script_sig.as_bytes())
})
.unwrap_or_else(|| coinbase.lock_time.to_consensus_u32())
});
for tx in block.txdata.iter().skip(1) {
let wtxid = tx.compute_wtxid();
self.txdata.insert(wtxid, tx.clone());
}
}
pub fn add_transactions(&mut self, transactions: Vec<Transaction>) {
for tx in transactions {
let wtxid = tx.compute_wtxid();
self.txdata.insert(wtxid, tx);
}
}
pub fn get_txdata(&self, wtxids: &[Wtxid]) -> Vec<Transaction> {
wtxids
.iter()
.filter_map(|wtxid| self.txdata.get(wtxid).cloned())
.collect()
}
pub fn verify(&self, wtxids: &[Wtxid]) -> Vec<Wtxid> {
wtxids
.iter()
.filter(|&wtxid| !self.txdata.contains_key(wtxid))
.copied()
.collect()
}
pub fn get_current_prev_hash(&self) -> Option<BlockHash> {
self.current_prev_hash
}
pub fn get_current_nbits(&self) -> Option<CompactTarget> {
self.current_nbits
}
pub fn get_current_min_ntime(&self) -> Option<u32> {
self.current_min_ntime
}
pub fn get_current_bip34_height(&self) -> Option<u32> {
self.current_bip34_height
}
}
pub(crate) fn decode_bip34_height_from_coinbase_script_sig(script_sig: &[u8]) -> Option<u32> {
let first = *script_sig.first()?;
if first == 0x00 {
return Some(0);
}
if (0x51..=0x60).contains(&first) {
return Some((first - 0x50) as u32);
}
let push_len = first as usize;
if push_len == 0 || push_len > 4 || script_sig.len() < 1 + push_len {
return None;
}
let mut height_bytes = [0u8; 4];
height_bytes[..push_len].copy_from_slice(&script_sig[1..1 + push_len]);
Some(u32::from_le_bytes(height_bytes))
}