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#[derive(Debug, Clone, Eq)]
17pub struct TxEntry {
18 pub rtx: Arc<ResolvedTransaction>,
20 pub cycles: Cycle,
22 pub size: usize,
24 pub fee: Capacity,
26 pub ancestors_size: usize,
28 pub ancestors_fee: Capacity,
30 pub ancestors_cycles: Cycle,
32 pub ancestors_count: usize,
34 pub descendants_fee: Capacity,
36 pub descendants_size: usize,
38 pub descendants_cycles: Cycle,
40 pub descendants_count: usize,
42 pub timestamp: u64,
44}
45
46impl TxEntry {
47 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 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 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 pub fn related_dep_out_points(&self) -> impl Iterator<Item = &OutPoint> {
85 self.rtx.related_dep_out_points()
86 }
87
88 pub fn transaction(&self) -> &TransactionView {
90 &self.rtx.transaction
91 }
92
93 pub fn into_transaction(self) -> TransactionView {
96 self.rtx.transaction.clone()
97 }
98
99 pub fn proposal_short_id(&self) -> ProposalShortId {
101 self.transaction().proposal_short_id()
102 }
103
104 pub fn as_score_key(&self) -> AncestorsScoreSortKey {
106 AncestorsScoreSortKey::from(self)
107 }
108
109 pub fn as_evict_key(&self) -> EvictKey {
111 EvictKey::from(self)
112 }
113
114 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 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 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 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 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 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 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}