use crate::component::sort_key::{AncestorsScoreSortKey, EvictKey};
use ckb_systemtime::unix_time_as_millis;
use ckb_types::{
core::{
cell::ResolvedTransaction,
tx_pool::{get_transaction_weight, TxEntryInfo},
Capacity, Cycle, FeeRate, TransactionView,
},
packed::{OutPoint, ProposalShortId},
};
use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
use std::sync::Arc;
#[derive(Debug, Clone, Eq)]
pub struct TxEntry {
pub rtx: Arc<ResolvedTransaction>,
pub cycles: Cycle,
pub size: usize,
pub fee: Capacity,
pub ancestors_size: usize,
pub ancestors_fee: Capacity,
pub ancestors_cycles: Cycle,
pub ancestors_count: usize,
pub descendants_fee: Capacity,
pub descendants_size: usize,
pub descendants_cycles: Cycle,
pub descendants_count: usize,
pub timestamp: u64,
}
impl TxEntry {
pub fn new(rtx: Arc<ResolvedTransaction>, cycles: Cycle, fee: Capacity, size: usize) -> Self {
Self::new_with_timestamp(rtx, cycles, fee, size, unix_time_as_millis())
}
pub fn new_with_timestamp(
rtx: Arc<ResolvedTransaction>,
cycles: Cycle,
fee: Capacity,
size: usize,
timestamp: u64,
) -> Self {
TxEntry {
rtx,
cycles,
size,
fee,
timestamp,
ancestors_size: size,
ancestors_fee: fee,
ancestors_cycles: cycles,
descendants_fee: fee,
descendants_size: size,
descendants_cycles: cycles,
descendants_count: 1,
ancestors_count: 1,
}
}
pub fn dummy_resolve(tx: TransactionView, cycles: Cycle, fee: Capacity, size: usize) -> Self {
let rtx = ResolvedTransaction::dummy_resolve(tx);
TxEntry::new(Arc::new(rtx), cycles, fee, size)
}
pub fn related_dep_out_points(&self) -> impl Iterator<Item = &OutPoint> {
self.rtx.related_dep_out_points()
}
pub fn transaction(&self) -> &TransactionView {
&self.rtx.transaction
}
pub fn into_transaction(self) -> TransactionView {
self.rtx.transaction.clone()
}
pub fn proposal_short_id(&self) -> ProposalShortId {
self.transaction().proposal_short_id()
}
pub fn as_score_key(&self) -> AncestorsScoreSortKey {
AncestorsScoreSortKey::from(self)
}
pub fn as_evict_key(&self) -> EvictKey {
EvictKey::from(self)
}
pub fn fee_rate(&self) -> FeeRate {
let weight = get_transaction_weight(self.size, self.cycles);
FeeRate::calculate(self.fee, weight)
}
pub fn add_descendant_weight(&mut self, entry: &TxEntry) {
self.descendants_count = self.descendants_count.saturating_add(1);
self.descendants_size = self.descendants_size.saturating_add(entry.size);
self.descendants_cycles = self.descendants_cycles.saturating_add(entry.cycles);
self.descendants_fee = Capacity::shannons(
self.descendants_fee
.as_u64()
.saturating_add(entry.fee.as_u64()),
);
}
pub fn sub_descendant_weight(&mut self, entry: &TxEntry) {
self.descendants_count = self.descendants_count.saturating_sub(1);
self.descendants_size = self.descendants_size.saturating_sub(entry.size);
self.descendants_cycles = self.descendants_cycles.saturating_sub(entry.cycles);
self.descendants_fee = Capacity::shannons(
self.descendants_fee
.as_u64()
.saturating_sub(entry.fee.as_u64()),
);
}
pub fn add_ancestor_weight(&mut self, entry: &TxEntry) {
self.ancestors_count = self.ancestors_count.saturating_add(1);
self.ancestors_size = self.ancestors_size.saturating_add(entry.size);
self.ancestors_cycles = self.ancestors_cycles.saturating_add(entry.cycles);
self.ancestors_fee = Capacity::shannons(
self.ancestors_fee
.as_u64()
.saturating_add(entry.fee.as_u64()),
);
}
pub fn sub_ancestor_weight(&mut self, entry: &TxEntry) {
self.ancestors_count = self.ancestors_count.saturating_sub(1);
self.ancestors_size = self.ancestors_size.saturating_sub(entry.size);
self.ancestors_cycles = self.ancestors_cycles.saturating_sub(entry.cycles);
self.ancestors_fee = Capacity::shannons(
self.ancestors_fee
.as_u64()
.saturating_sub(entry.fee.as_u64()),
);
}
pub fn reset_statistic_state(&mut self) {
self.ancestors_count = 1;
self.ancestors_size = self.size;
self.ancestors_cycles = self.cycles;
self.ancestors_fee = self.fee;
self.descendants_count = 1;
self.descendants_size = self.size;
self.descendants_cycles = self.cycles;
self.descendants_fee = self.fee;
}
pub fn to_info(&self) -> TxEntryInfo {
TxEntryInfo {
cycles: self.cycles,
size: self.size as u64,
fee: self.fee,
ancestors_size: self.ancestors_size as u64,
ancestors_cycles: self.ancestors_cycles,
descendants_size: self.descendants_size as u64,
descendants_cycles: self.descendants_cycles,
ancestors_count: self.ancestors_count as u64,
timestamp: self.timestamp,
}
}
}
impl Hash for TxEntry {
fn hash<H: Hasher>(&self, state: &mut H) {
Hash::hash(self.transaction(), state);
}
}
impl PartialEq for TxEntry {
fn eq(&self, other: &TxEntry) -> bool {
self.rtx.transaction == other.rtx.transaction
}
}
impl PartialOrd for TxEntry {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for TxEntry {
fn cmp(&self, other: &Self) -> Ordering {
self.as_score_key().cmp(&other.as_score_key())
}
}
impl From<&TxEntry> for AncestorsScoreSortKey {
fn from(entry: &TxEntry) -> Self {
let weight = get_transaction_weight(entry.size, entry.cycles);
let ancestors_weight = get_transaction_weight(entry.ancestors_size, entry.ancestors_cycles);
AncestorsScoreSortKey {
fee: entry.fee,
weight,
ancestors_fee: entry.ancestors_fee,
ancestors_weight,
}
}
}
impl From<&TxEntry> for EvictKey {
fn from(entry: &TxEntry) -> Self {
let weight = get_transaction_weight(entry.size, entry.cycles);
let descendants_weight =
get_transaction_weight(entry.descendants_size, entry.descendants_cycles);
let descendants_feerate = FeeRate::calculate(entry.descendants_fee, descendants_weight);
let feerate = FeeRate::calculate(entry.fee, weight);
EvictKey {
fee_rate: descendants_feerate.max(feerate),
timestamp: entry.timestamp,
descendants_count: entry.descendants_count,
}
}
}