Skip to content

Commit

Permalink
RBF fee check with replaced txs and their descendants
Browse files Browse the repository at this point in the history
  • Loading branch information
chenyukang committed Jan 13, 2024
1 parent e888ea2 commit 3dec484
Showing 1 changed file with 35 additions and 22 deletions.
57 changes: 35 additions & 22 deletions tx-pool/src/pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use ckb_types::{
packed::{Byte32, ProposalShortId},
};
use lru::LruCache;
use std::collections::HashSet;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;

const COMMITTED_HASH_CACHE_SIZE: usize = 100_000;
Expand Down Expand Up @@ -87,17 +87,28 @@ impl TxPool {
if !self.enable_rbf() {
return None;
}
let entry = vec![self.get_pool_entry(&tx.proposal_short_id()).unwrap()];
self.calculate_min_replace_fee(&entry, tx.size)

let mut conflicts = vec![self.get_pool_entry(&tx.proposal_short_id()).unwrap()];
let descendants = self.pool_map.calc_descendants(&tx.proposal_short_id());
let descendants = descendants
.iter()
.filter_map(|id| self.get_pool_entry(id))
.collect::<Vec<_>>();
conflicts.extend(descendants);
self.calculate_min_replace_fee(&conflicts, tx.size)
}

/// min_replace_fee = sum(replaced_txs.fee) + extra_rbf_fee
fn calculate_min_replace_fee(&self, conflicts: &[&PoolEntry], size: usize) -> Option<Capacity> {
let extra_rbf_fee = self.config.min_rbf_rate.fee(size as u64);
let replaced_sum_fee = conflicts
// don't account for duplicate txs
let replaced_fees: HashMap<_, _> = conflicts
.iter()
.map(|c| c.inner.fee)
.try_fold(Capacity::zero(), |acc, x| acc.safe_add(x));
.map(|c| (c.id.clone(), c.inner.fee))
.collect();
let replaced_sum_fee = replaced_fees
.values()
.try_fold(Capacity::zero(), |acc, x| acc.safe_add(*x));
let res = replaced_sum_fee.map_or(Err(CapacityError::Overflow), |sum| {
sum.safe_add(extra_rbf_fee)
});
Expand Down Expand Up @@ -501,22 +512,6 @@ impl TxPool {
.collect::<Vec<_>>();
assert!(conflicts.len() == conflict_ids.len());

// Rule #4, new tx's fee need to higher than min_rbf_fee computed from the tx_pool configuration
// Rule #3, new tx's fee need to higher than conflicts, here we only check the root tx
let fee = entry.fee;
if let Some(min_replace_fee) = self.calculate_min_replace_fee(&conflicts, entry.size) {
if fee < min_replace_fee {
return Err(Reject::RBFRejected(format!(
"Tx's current fee is {}, expect it to >= {} to replace old txs",
fee, min_replace_fee,
)));
}
} else {
return Err(Reject::RBFRejected(
"calculate_min_replace_fee failed".to_string(),
));
}

// Rule #2, new tx don't contain any new unconfirmed inputs
let mut inputs = HashSet::new();
let mut outputs = HashSet::new();
Expand All @@ -543,6 +538,7 @@ impl TxPool {
// Rule #5, the replaced tx's descendants can not more than 100
// and the ancestor of the new tx don't have common set with the replaced tx's descendants
let mut replace_count: usize = 0;
let mut all_conflicted = conflicts.clone();
let ancestors = self.pool_map.calc_ancestors(&short_id);
for conflict in conflicts.iter() {
let descendants = self.pool_map.calc_descendants(&conflict.id);
Expand Down Expand Up @@ -573,6 +569,23 @@ impl TxPool {
));
}
}
all_conflicted.extend(entries);
}

// Rule #4, new tx's fee need to higher than min_rbf_fee computed from the tx_pool configuration
// Rule #3, new tx's fee need to higher than conflicts, here we only check the all conflicted txs fee
let fee = entry.fee;
if let Some(min_replace_fee) = self.calculate_min_replace_fee(&all_conflicted, entry.size) {
if fee < min_replace_fee {
return Err(Reject::RBFRejected(format!(
"Tx's current fee is {}, expect it to >= {} to replace old txs",
fee, min_replace_fee,
)));
}
} else {
return Err(Reject::RBFRejected(
"calculate_min_replace_fee failed".to_string(),
));
}

Ok(conflict_ids)
Expand Down

0 comments on commit 3dec484

Please sign in to comment.