use ckb_types::{
core::tx_pool::Reject,
packed::{Byte32, OutPoint, ProposalShortId},
};
use std::collections::{HashMap, HashSet, hash_map::Entry};
#[derive(Default, Debug, Clone)]
pub(crate) struct Edges {
pub(crate) inputs: HashMap<OutPoint, ProposalShortId>,
pub(crate) deps: HashMap<OutPoint, HashSet<ProposalShortId>>,
pub(crate) header_deps: HashMap<ProposalShortId, Vec<Byte32>>,
}
impl Edges {
#[cfg(test)]
pub(crate) fn inputs_len(&self) -> usize {
self.inputs.len()
}
#[cfg(test)]
pub(crate) fn header_deps_len(&self) -> usize {
self.header_deps.len()
}
#[cfg(test)]
pub(crate) fn deps_len(&self) -> usize {
self.deps.len()
}
pub(crate) fn insert_input(
&mut self,
out_point: OutPoint,
txid: ProposalShortId,
) -> Result<(), Reject> {
match self.inputs.entry(out_point.clone()) {
Entry::Occupied(occupied) => {
let msg = format!(
"txpool unexpected double-spending out_point: {:?} old_tx: {:?} new_tx: {:?}",
out_point,
occupied.get(),
txid
);
Err(Reject::RBFRejected(msg))
}
Entry::Vacant(vacant) => {
vacant.insert(txid);
Ok(())
}
}
}
pub(crate) fn remove_input(&mut self, out_point: &OutPoint) -> Option<ProposalShortId> {
self.inputs.remove(out_point)
}
pub(crate) fn get_input_ref(&self, out_point: &OutPoint) -> Option<&ProposalShortId> {
self.inputs.get(out_point)
}
pub(crate) fn get_deps_ref(&self, out_point: &OutPoint) -> Option<&HashSet<ProposalShortId>> {
self.deps.get(out_point)
}
pub(crate) fn remove_deps(&mut self, out_point: &OutPoint) -> Option<HashSet<ProposalShortId>> {
self.deps.remove(out_point)
}
pub(crate) fn insert_deps(&mut self, out_point: OutPoint, txid: ProposalShortId) {
self.deps.entry(out_point).or_default().insert(txid);
}
pub(crate) fn delete_txid_by_dep(&mut self, out_point: OutPoint, txid: &ProposalShortId) {
if let Entry::Occupied(mut occupied) = self.deps.entry(out_point) {
let ids = occupied.get_mut();
ids.remove(txid);
if ids.is_empty() {
occupied.remove();
}
}
}
pub(crate) fn clear(&mut self) {
self.inputs.clear();
self.deps.clear();
self.header_deps.clear();
}
}