2023-06-25 19:04:01 -07:00
|
|
|
use crate::{
|
2023-06-28 16:31:59 -04:00
|
|
|
u32_hasher_types::{u32hashset_new, U32HasherState}, ThreadTransaction,
|
2023-06-25 19:04:01 -07:00
|
|
|
};
|
2023-06-23 20:41:39 -04:00
|
|
|
use std::{
|
|
|
|
cmp::Ordering,
|
|
|
|
collections::HashSet,
|
|
|
|
hash::{Hash, Hasher},
|
|
|
|
};
|
2023-06-23 16:42:58 -04:00
|
|
|
|
2023-06-25 16:08:16 -07:00
|
|
|
#[allow(clippy::struct_excessive_bools)]
|
2023-06-26 19:29:32 -07:00
|
|
|
#[derive(Clone, Debug)]
|
2023-06-23 16:42:58 -04:00
|
|
|
pub struct AuditTransaction {
|
2023-06-23 20:41:39 -04:00
|
|
|
pub uid: u32,
|
|
|
|
pub fee: u64,
|
|
|
|
pub weight: u32,
|
2023-06-27 18:51:18 -04:00
|
|
|
pub sigop_adjusted_vsize: u32,
|
2023-06-23 20:41:39 -04:00
|
|
|
pub sigops: u32,
|
2023-06-29 12:56:54 -04:00
|
|
|
adjusted_fee_per_vsize: f64,
|
2023-06-23 20:41:39 -04:00
|
|
|
pub effective_fee_per_vsize: f64,
|
|
|
|
pub dependency_rate: f64,
|
|
|
|
pub inputs: Vec<u32>,
|
|
|
|
pub relatives_set_flag: bool,
|
2023-06-25 19:04:01 -07:00
|
|
|
pub ancestors: HashSet<u32, U32HasherState>,
|
|
|
|
pub children: HashSet<u32, U32HasherState>,
|
2023-06-26 21:04:10 -04:00
|
|
|
ancestor_fee: u64,
|
|
|
|
ancestor_weight: u32,
|
2023-06-27 18:51:18 -04:00
|
|
|
ancestor_sigop_adjusted_vsize: u32,
|
2023-06-26 21:04:10 -04:00
|
|
|
ancestor_sigops: u32,
|
2023-06-25 11:19:33 -07:00
|
|
|
// Safety: Must be private to prevent NaN breaking Ord impl.
|
|
|
|
score: f64,
|
2023-06-23 20:41:39 -04:00
|
|
|
pub used: bool,
|
2023-06-26 17:35:33 -04:00
|
|
|
/// whether this transaction has been moved to the "modified" priority queue
|
2023-06-23 20:41:39 -04:00
|
|
|
pub modified: bool,
|
|
|
|
pub dirty: bool,
|
2023-06-23 16:42:58 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Hash for AuditTransaction {
|
2023-06-23 20:41:39 -04:00
|
|
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
|
|
self.uid.hash(state);
|
|
|
|
}
|
2023-06-23 16:42:58 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
impl PartialEq for AuditTransaction {
|
2023-06-23 20:41:39 -04:00
|
|
|
fn eq(&self, other: &Self) -> bool {
|
|
|
|
self.uid == other.uid
|
|
|
|
}
|
2023-06-23 16:42:58 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Eq for AuditTransaction {}
|
|
|
|
|
2023-06-27 22:48:22 -07:00
|
|
|
#[inline]
|
|
|
|
pub fn partial_cmp_uid_score(a: (u32, f64), b: (u32, f64)) -> Option<Ordering> {
|
|
|
|
// If either score is NaN, this is false,
|
|
|
|
// and partial_cmp will return None
|
|
|
|
if a.1 == b.1 {
|
|
|
|
Some(a.0.cmp(&b.0))
|
|
|
|
} else {
|
|
|
|
a.1.partial_cmp(&b.1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-23 16:42:58 -04:00
|
|
|
impl PartialOrd for AuditTransaction {
|
2023-06-25 16:08:16 -07:00
|
|
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
2023-06-27 22:48:22 -07:00
|
|
|
partial_cmp_uid_score((self.uid, self.score), (other.uid, other.score))
|
2023-06-23 16:42:58 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Ord for AuditTransaction {
|
2023-06-25 16:08:16 -07:00
|
|
|
fn cmp(&self, other: &Self) -> Ordering {
|
2023-06-25 11:19:33 -07:00
|
|
|
// Safety: The only possible values for score are f64
|
|
|
|
// that are not NaN. This is because outside code can not
|
|
|
|
// freely assign score. Also, calc_new_score guarantees no NaN.
|
2023-06-24 16:41:53 -07:00
|
|
|
self.partial_cmp(other).expect("score will never be NaN")
|
2023-06-23 20:41:39 -04:00
|
|
|
}
|
|
|
|
}
|
2023-06-23 23:06:13 -07:00
|
|
|
|
2023-06-29 12:56:54 -04:00
|
|
|
#[inline]
|
|
|
|
fn calc_fee_rate(
|
|
|
|
fee: f64,
|
|
|
|
vsize: u32,
|
|
|
|
) -> f64 {
|
|
|
|
fee / (if vsize == 0 {
|
|
|
|
1.0
|
|
|
|
} else {
|
|
|
|
f64::from(vsize)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-06-23 23:06:13 -07:00
|
|
|
impl AuditTransaction {
|
|
|
|
pub fn from_thread_transaction(tx: &ThreadTransaction) -> Self {
|
2023-06-27 22:48:22 -07:00
|
|
|
// rounded up to the nearest integer
|
|
|
|
let sigop_adjusted_vsize = ((tx.weight + 3) / 4).max(tx.sigops * 5);
|
2023-06-25 16:08:16 -07:00
|
|
|
Self {
|
2023-06-23 23:06:13 -07:00
|
|
|
uid: tx.uid,
|
2023-06-28 16:31:59 -04:00
|
|
|
fee: tx.fee as u64,
|
2023-06-23 23:06:13 -07:00
|
|
|
weight: tx.weight,
|
2023-06-27 22:48:22 -07:00
|
|
|
sigop_adjusted_vsize,
|
2023-06-23 23:06:13 -07:00
|
|
|
sigops: tx.sigops,
|
2023-06-29 12:56:54 -04:00
|
|
|
adjusted_fee_per_vsize: calc_fee_rate(tx.fee, sigop_adjusted_vsize),
|
2023-06-23 23:06:13 -07:00
|
|
|
effective_fee_per_vsize: tx.effective_fee_per_vsize,
|
|
|
|
dependency_rate: f64::INFINITY,
|
|
|
|
inputs: tx.inputs.clone(),
|
|
|
|
relatives_set_flag: false,
|
2023-06-25 19:04:01 -07:00
|
|
|
ancestors: u32hashset_new(),
|
|
|
|
children: u32hashset_new(),
|
2023-06-28 16:31:59 -04:00
|
|
|
ancestor_fee: tx.fee as u64,
|
2023-06-23 23:06:13 -07:00
|
|
|
ancestor_weight: tx.weight,
|
2023-06-27 22:48:22 -07:00
|
|
|
ancestor_sigop_adjusted_vsize: sigop_adjusted_vsize,
|
2023-06-23 23:06:13 -07:00
|
|
|
ancestor_sigops: tx.sigops,
|
|
|
|
score: 0.0,
|
|
|
|
used: false,
|
|
|
|
modified: false,
|
|
|
|
dirty: false,
|
|
|
|
}
|
|
|
|
}
|
2023-06-25 11:19:33 -07:00
|
|
|
|
|
|
|
#[inline]
|
2023-06-25 16:08:16 -07:00
|
|
|
pub const fn score(&self) -> f64 {
|
2023-06-25 11:19:33 -07:00
|
|
|
self.score
|
|
|
|
}
|
|
|
|
|
2023-06-26 21:04:10 -04:00
|
|
|
#[inline]
|
2023-06-27 18:51:18 -04:00
|
|
|
pub const fn ancestor_sigop_adjusted_vsize(&self) -> u32 {
|
|
|
|
self.ancestor_sigop_adjusted_vsize
|
2023-06-26 21:04:10 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub const fn ancestor_sigops(&self) -> u32 {
|
|
|
|
self.ancestor_sigops
|
|
|
|
}
|
|
|
|
|
2023-06-26 19:29:32 -07:00
|
|
|
#[inline]
|
|
|
|
pub fn cluster_rate(&self) -> f64 {
|
|
|
|
// Safety: self.ancestor_weight can never be 0.
|
|
|
|
// Even if it could, as it approaches 0, the value inside the min() call
|
|
|
|
// grows, so if we think of 0 as "grew infinitely" then dependency_rate would be
|
|
|
|
// the smaller of the two. If either side is NaN, the other side is returned.
|
|
|
|
self.dependency_rate
|
|
|
|
.min(self.ancestor_fee as f64 / (f64::from(self.ancestor_weight) / 4.0))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_dirty_if_different(&mut self, cluster_rate: f64) {
|
|
|
|
if self.effective_fee_per_vsize != cluster_rate {
|
|
|
|
self.effective_fee_per_vsize = cluster_rate;
|
|
|
|
self.dirty = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-25 11:19:33 -07:00
|
|
|
/// Safety: This function must NEVER set score to NaN.
|
|
|
|
#[inline]
|
2023-06-25 16:16:24 -07:00
|
|
|
fn calc_new_score(&mut self) {
|
2023-06-29 12:56:54 -04:00
|
|
|
self.score = self.adjusted_fee_per_vsize.min(calc_fee_rate(self.ancestor_fee as f64, self.ancestor_sigop_adjusted_vsize));
|
2023-06-25 11:19:33 -07:00
|
|
|
}
|
2023-06-25 16:08:16 -07:00
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn set_ancestors(
|
|
|
|
&mut self,
|
2023-06-25 19:04:01 -07:00
|
|
|
ancestors: HashSet<u32, U32HasherState>,
|
2023-06-25 16:08:16 -07:00
|
|
|
total_fee: u64,
|
|
|
|
total_weight: u32,
|
2023-06-27 18:51:18 -04:00
|
|
|
total_sigop_adjusted_vsize: u32,
|
2023-06-25 16:08:16 -07:00
|
|
|
total_sigops: u32,
|
|
|
|
) {
|
|
|
|
self.ancestors = ancestors;
|
|
|
|
self.ancestor_fee = self.fee + total_fee;
|
|
|
|
self.ancestor_weight = self.weight + total_weight;
|
2023-06-27 18:51:18 -04:00
|
|
|
self.ancestor_sigop_adjusted_vsize = self.sigop_adjusted_vsize + total_sigop_adjusted_vsize;
|
2023-06-25 16:08:16 -07:00
|
|
|
self.ancestor_sigops = self.sigops + total_sigops;
|
|
|
|
self.calc_new_score();
|
|
|
|
self.relatives_set_flag = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn remove_root(
|
|
|
|
&mut self,
|
|
|
|
root_txid: u32,
|
|
|
|
root_fee: u64,
|
|
|
|
root_weight: u32,
|
2023-06-27 18:51:18 -04:00
|
|
|
root_sigop_adjusted_vsize: u32,
|
2023-06-25 16:08:16 -07:00
|
|
|
root_sigops: u32,
|
|
|
|
cluster_rate: f64,
|
|
|
|
) -> f64 {
|
|
|
|
let old_score = self.score();
|
|
|
|
self.dependency_rate = self.dependency_rate.min(cluster_rate);
|
2023-06-26 17:35:33 -04:00
|
|
|
if self.ancestors.remove(&root_txid) {
|
|
|
|
self.ancestor_fee -= root_fee;
|
|
|
|
self.ancestor_weight -= root_weight;
|
2023-06-27 18:51:18 -04:00
|
|
|
self.ancestor_sigop_adjusted_vsize -= root_sigop_adjusted_vsize;
|
2023-06-26 17:35:33 -04:00
|
|
|
self.ancestor_sigops -= root_sigops;
|
|
|
|
self.calc_new_score();
|
|
|
|
}
|
2023-06-25 16:08:16 -07:00
|
|
|
old_score
|
|
|
|
}
|
2023-06-23 23:06:13 -07:00
|
|
|
}
|