ckb_tx_pool/component/
entry.rs

1use crate::component::sort_key::{AncestorsScoreSortKey, EvictKey};
2use ckb_systemtime::unix_time_as_millis;
3use ckb_types::{
4    core::{
5        Capacity, Cycle, FeeRate, TransactionView,
6        cell::ResolvedTransaction,
7        tx_pool::{TxEntryInfo, get_transaction_weight},
8    },
9    packed::{OutPoint, ProposalShortId},
10};
11use std::cmp::Ordering;
12use std::hash::{Hash, Hasher};
13use std::sync::Arc;
14
15/// An entry in the transaction pool.
16#[derive(Debug, Clone, Eq)]
17pub struct TxEntry {
18    /// Transaction
19    pub rtx: Arc<ResolvedTransaction>,
20    /// Cycles
21    pub cycles: Cycle,
22    /// tx size
23    pub size: usize,
24    /// fee
25    pub fee: Capacity,
26    /// ancestors txs size
27    pub ancestors_size: usize,
28    /// ancestors txs fee
29    pub ancestors_fee: Capacity,
30    /// ancestors txs cycles
31    pub ancestors_cycles: Cycle,
32    /// ancestors txs count
33    pub ancestors_count: usize,
34    /// descendants txs fee
35    pub descendants_fee: Capacity,
36    /// descendants txs size
37    pub descendants_size: usize,
38    /// descendants txs cycles
39    pub descendants_cycles: Cycle,
40    /// descendants txs count
41    pub descendants_count: usize,
42    /// The unix timestamp when entering the Txpool, unit: Millisecond
43    pub timestamp: u64,
44}
45
46impl TxEntry {
47    /// Create new transaction pool entry
48    pub fn new(rtx: Arc<ResolvedTransaction>, cycles: Cycle, fee: Capacity, size: usize) -> Self {
49        Self::new_with_timestamp(rtx, cycles, fee, size, unix_time_as_millis())
50    }
51
52    /// Create new transaction pool entry with specified timestamp
53    pub fn new_with_timestamp(
54        rtx: Arc<ResolvedTransaction>,
55        cycles: Cycle,
56        fee: Capacity,
57        size: usize,
58        timestamp: u64,
59    ) -> Self {
60        TxEntry {
61            rtx,
62            cycles,
63            size,
64            fee,
65            timestamp,
66            ancestors_size: size,
67            ancestors_fee: fee,
68            ancestors_cycles: cycles,
69            descendants_fee: fee,
70            descendants_size: size,
71            descendants_cycles: cycles,
72            descendants_count: 1,
73            ancestors_count: 1,
74        }
75    }
76
77    /// Create dummy entry from tx, skip resolve
78    pub fn dummy_resolve(tx: TransactionView, cycles: Cycle, fee: Capacity, size: usize) -> Self {
79        let rtx = ResolvedTransaction::dummy_resolve(tx);
80        TxEntry::new(Arc::new(rtx), cycles, fee, size)
81    }
82
83    /// Return related dep out_points
84    pub fn related_dep_out_points(&self) -> impl Iterator<Item = &OutPoint> {
85        self.rtx.related_dep_out_points()
86    }
87
88    /// Return reference of transaction
89    pub fn transaction(&self) -> &TransactionView {
90        &self.rtx.transaction
91    }
92
93    /// Converts a Entry into a TransactionView
94    /// This consumes the Entry
95    pub fn into_transaction(self) -> TransactionView {
96        self.rtx.transaction.clone()
97    }
98
99    /// Return proposal_short_id of transaction
100    pub fn proposal_short_id(&self) -> ProposalShortId {
101        self.transaction().proposal_short_id()
102    }
103
104    /// Returns a sorted_key
105    pub fn as_score_key(&self) -> AncestorsScoreSortKey {
106        AncestorsScoreSortKey::from(self)
107    }
108
109    /// Returns a evict_key
110    pub fn as_evict_key(&self) -> EvictKey {
111        EvictKey::from(self)
112    }
113
114    /// Returns fee rate
115    pub fn fee_rate(&self) -> FeeRate {
116        let weight = get_transaction_weight(self.size, self.cycles);
117        FeeRate::calculate(self.fee, weight)
118    }
119
120    /// Update ancestor state for add an entry
121    pub fn add_descendant_weight(&mut self, entry: &TxEntry) {
122        self.descendants_count = self.descendants_count.saturating_add(1);
123        self.descendants_size = self.descendants_size.saturating_add(entry.size);
124        self.descendants_cycles = self.descendants_cycles.saturating_add(entry.cycles);
125        self.descendants_fee = Capacity::shannons(
126            self.descendants_fee
127                .as_u64()
128                .saturating_add(entry.fee.as_u64()),
129        );
130    }
131
132    /// Update ancestor state for remove an entry
133    pub fn sub_descendant_weight(&mut self, entry: &TxEntry) {
134        self.descendants_count = self.descendants_count.saturating_sub(1);
135        self.descendants_size = self.descendants_size.saturating_sub(entry.size);
136        self.descendants_cycles = self.descendants_cycles.saturating_sub(entry.cycles);
137        self.descendants_fee = Capacity::shannons(
138            self.descendants_fee
139                .as_u64()
140                .saturating_sub(entry.fee.as_u64()),
141        );
142    }
143
144    /// Update ancestor state for add an entry
145    pub fn add_ancestor_weight(&mut self, entry: &TxEntry) {
146        self.ancestors_count = self.ancestors_count.saturating_add(1);
147        self.ancestors_size = self.ancestors_size.saturating_add(entry.size);
148        self.ancestors_cycles = self.ancestors_cycles.saturating_add(entry.cycles);
149        self.ancestors_fee = Capacity::shannons(
150            self.ancestors_fee
151                .as_u64()
152                .saturating_add(entry.fee.as_u64()),
153        );
154    }
155
156    /// Update ancestor state for remove an entry
157    pub fn sub_ancestor_weight(&mut self, entry: &TxEntry) {
158        self.ancestors_count = self.ancestors_count.saturating_sub(1);
159        self.ancestors_size = self.ancestors_size.saturating_sub(entry.size);
160        self.ancestors_cycles = self.ancestors_cycles.saturating_sub(entry.cycles);
161        self.ancestors_fee = Capacity::shannons(
162            self.ancestors_fee
163                .as_u64()
164                .saturating_sub(entry.fee.as_u64()),
165        );
166    }
167
168    /// Reset ancestor state by remove
169    pub fn reset_statistic_state(&mut self) {
170        self.ancestors_count = 1;
171        self.ancestors_size = self.size;
172        self.ancestors_cycles = self.cycles;
173        self.ancestors_fee = self.fee;
174
175        self.descendants_count = 1;
176        self.descendants_size = self.size;
177        self.descendants_cycles = self.cycles;
178        self.descendants_fee = self.fee;
179    }
180
181    /// Converts entry to a `TxEntryInfo`.
182    pub fn to_info(&self) -> TxEntryInfo {
183        TxEntryInfo {
184            cycles: self.cycles,
185            size: self.size as u64,
186            fee: self.fee,
187            ancestors_size: self.ancestors_size as u64,
188            ancestors_cycles: self.ancestors_cycles,
189            descendants_size: self.descendants_size as u64,
190            descendants_cycles: self.descendants_cycles,
191            ancestors_count: self.ancestors_count as u64,
192            timestamp: self.timestamp,
193        }
194    }
195}
196
197impl Hash for TxEntry {
198    fn hash<H: Hasher>(&self, state: &mut H) {
199        Hash::hash(self.transaction(), state);
200    }
201}
202
203impl PartialEq for TxEntry {
204    fn eq(&self, other: &TxEntry) -> bool {
205        self.rtx.transaction == other.rtx.transaction
206    }
207}
208
209impl PartialOrd for TxEntry {
210    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
211        Some(self.cmp(other))
212    }
213}
214
215impl Ord for TxEntry {
216    fn cmp(&self, other: &Self) -> Ordering {
217        self.as_score_key().cmp(&other.as_score_key())
218    }
219}
220
221impl From<&TxEntry> for AncestorsScoreSortKey {
222    fn from(entry: &TxEntry) -> Self {
223        let weight = get_transaction_weight(entry.size, entry.cycles);
224        let ancestors_weight = get_transaction_weight(entry.ancestors_size, entry.ancestors_cycles);
225        AncestorsScoreSortKey {
226            fee: entry.fee,
227            weight,
228            ancestors_fee: entry.ancestors_fee,
229            ancestors_weight,
230        }
231    }
232}
233
234impl From<&TxEntry> for EvictKey {
235    fn from(entry: &TxEntry) -> Self {
236        let weight = get_transaction_weight(entry.size, entry.cycles);
237        let descendants_weight =
238            get_transaction_weight(entry.descendants_size, entry.descendants_cycles);
239
240        let descendants_feerate = FeeRate::calculate(entry.descendants_fee, descendants_weight);
241        let feerate = FeeRate::calculate(entry.fee, weight);
242        EvictKey {
243            fee_rate: descendants_feerate.max(feerate),
244            timestamp: entry.timestamp,
245            descendants_count: entry.descendants_count,
246        }
247    }
248}