1use crate::eth::{error::PoolError, util::hex_fmt_many};
2use alloy_consensus::{
3 Transaction, Typed2718,
4 crypto::RecoveryError,
5 transaction::{SignerRecoverable, TxHashRef},
6};
7use alloy_network::AnyRpcTransaction;
8use alloy_primitives::{
9 Address, TxHash,
10 map::{HashMap, HashSet},
11};
12use alloy_rlp::Encodable;
13use anvil_core::eth::transaction::PendingTransaction;
14use parking_lot::RwLock;
15use std::{
16 cmp::{Ordering, Reverse},
17 collections::{BTreeMap, BTreeSet},
18 fmt,
19 str::FromStr,
20 sync::Arc,
21 time::Instant,
22};
23
24pub type TxMarker = Vec<u8>;
26
27type ReplacedTransactions<T> = (Vec<Arc<PoolTransaction<T>>>, Vec<TxHash>);
30
31#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
35pub enum TransactionOrder {
36 Fifo,
41 #[default]
46 Fees,
47}
48
49impl TransactionOrder {
50 pub fn priority<T: Transaction>(&self, tx: &T) -> TransactionPriority {
52 match self {
53 Self::Fifo => TransactionPriority::default(),
54 Self::Fees => TransactionPriority {
55 max_fee_per_gas: tx.max_fee_per_gas(),
56 max_priority_fee_per_gas: tx.max_priority_fee_per_gas(),
57 },
58 }
59 }
60}
61
62impl FromStr for TransactionOrder {
63 type Err = String;
64
65 fn from_str(s: &str) -> Result<Self, Self::Err> {
66 let s = s.to_lowercase();
67 let order = match s.as_str() {
68 "fees" => Self::Fees,
69 "fifo" => Self::Fifo,
70 _ => return Err(format!("Unknown TransactionOrder: `{s}`")),
71 };
72 Ok(order)
73 }
74}
75
76#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
81pub struct TransactionPriority {
82 max_fee_per_gas: u128,
83 max_priority_fee_per_gas: Option<u128>,
84}
85
86impl TransactionPriority {
87 fn effective_tip(&self, base_fee: u64) -> u128 {
90 let fee = self.max_fee_per_gas.saturating_sub(base_fee as u128);
91 self.max_priority_fee_per_gas.map_or(fee, |tip| fee.min(tip))
92 }
93}
94
95#[derive(Clone, PartialEq, Eq)]
97pub struct PoolTransaction<T> {
98 pub pending_transaction: PendingTransaction<T>,
100 pub requires: Vec<TxMarker>,
102 pub provides: Vec<TxMarker>,
104 pub priority: TransactionPriority,
106 pub is_replay: bool,
108}
109
110impl<T> PoolTransaction<T> {
113 pub const fn new(transaction: PendingTransaction<T>) -> Self {
114 Self {
115 pending_transaction: transaction,
116 requires: vec![],
117 provides: vec![],
118 priority: TransactionPriority { max_fee_per_gas: 0, max_priority_fee_per_gas: None },
119 is_replay: false,
120 }
121 }
122
123 pub const fn with_replay(mut self) -> Self {
125 self.is_replay = true;
126 self
127 }
128
129 pub const fn hash(&self) -> TxHash {
131 *self.pending_transaction.hash()
132 }
133}
134
135impl<T: Transaction> PoolTransaction<T> {
136 pub fn max_fee_per_gas(&self) -> u128 {
138 self.pending_transaction.transaction.max_fee_per_gas()
139 }
140}
141
142impl<T: Typed2718> PoolTransaction<T> {
143 pub fn tx_type(&self) -> u8 {
145 self.pending_transaction.transaction.ty()
146 }
147}
148
149impl<T: fmt::Debug> fmt::Debug for PoolTransaction<T> {
150 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
151 write!(fmt, "Transaction {{ ")?;
152 write!(fmt, "hash: {:?}, ", self.pending_transaction.hash())?;
153 write!(fmt, "requires: [{}], ", hex_fmt_many(self.requires.iter()))?;
154 write!(fmt, "provides: [{}], ", hex_fmt_many(self.provides.iter()))?;
155 write!(fmt, "raw tx: {:?}", self.pending_transaction)?;
156 write!(fmt, "}}")?;
157 Ok(())
158 }
159}
160
161impl<T> TryFrom<AnyRpcTransaction> for PoolTransaction<T>
162where
163 T: SignerRecoverable + TxHashRef + Encodable + TryFrom<AnyRpcTransaction>,
164 <T as TryFrom<AnyRpcTransaction>>::Error: Into<eyre::Error>,
165 RecoveryError: Into<eyre::Error>,
166{
167 type Error = eyre::Error;
168 fn try_from(value: AnyRpcTransaction) -> Result<Self, Self::Error> {
169 let typed_transaction = T::try_from(value).map_err(Into::into)?;
170 let pending_transaction = PendingTransaction::new(typed_transaction)?;
171 Ok(Self {
172 pending_transaction,
173 requires: vec![],
174 provides: vec![],
175 priority: TransactionPriority::default(),
176 is_replay: false,
177 })
178 }
179}
180
181#[derive(Clone, Debug)]
185pub struct PendingTransactions<T> {
186 required_markers: HashMap<TxMarker, HashSet<TxHash>>,
188 waiting_markers: HashMap<Vec<TxMarker>, TxHash>,
190 waiting_queue: HashMap<TxHash, PendingPoolTransaction<T>>,
192}
193
194impl<T> Default for PendingTransactions<T> {
195 fn default() -> Self {
196 Self {
197 required_markers: Default::default(),
198 waiting_markers: Default::default(),
199 waiting_queue: Default::default(),
200 }
201 }
202}
203
204impl<T> PendingTransactions<T> {
205 pub(super) fn snapshot(&self) -> Self {
207 Self {
208 required_markers: self.required_markers.clone(),
209 waiting_markers: self.waiting_markers.clone(),
210 waiting_queue: self.waiting_queue.clone(),
211 }
212 }
213
214 pub fn len(&self) -> usize {
216 self.waiting_queue.len()
217 }
218
219 pub fn is_empty(&self) -> bool {
220 self.waiting_queue.is_empty()
221 }
222
223 pub fn clear(&mut self) {
225 self.required_markers.clear();
226 self.waiting_markers.clear();
227 self.waiting_queue.clear();
228 }
229
230 pub fn transactions(&self) -> impl Iterator<Item = Arc<PoolTransaction<T>>> + '_ {
232 self.waiting_queue.values().map(|tx| tx.transaction.clone())
233 }
234
235 pub fn contains(&self, hash: &TxHash) -> bool {
237 self.waiting_queue.contains_key(hash)
238 }
239
240 pub fn get(&self, hash: &TxHash) -> Option<&PendingPoolTransaction<T>> {
242 self.waiting_queue.get(hash)
243 }
244
245 pub fn mark_and_unlock(
250 &mut self,
251 markers: impl IntoIterator<Item = impl AsRef<TxMarker>>,
252 ) -> Vec<PendingPoolTransaction<T>> {
253 let mut unlocked_ready = Vec::new();
254 for mark in markers {
255 let mark = mark.as_ref();
256 if let Some(tx_hashes) = self.required_markers.remove(mark) {
257 for hash in tx_hashes {
258 let tx = self.waiting_queue.get_mut(&hash).expect("tx is included;");
259 tx.mark(mark);
260
261 if tx.is_ready() {
262 let tx = self.waiting_queue.remove(&hash).expect("tx is included;");
263 self.waiting_markers.remove(&tx.transaction.provides);
264
265 unlocked_ready.push(tx);
266 }
267 }
268 }
269 }
270
271 unlocked_ready
272 }
273
274 pub fn remove(&mut self, hashes: Vec<TxHash>) -> Vec<Arc<PoolTransaction<T>>> {
278 let mut removed = vec![];
279 for hash in hashes {
280 if let Some(waiting_tx) = self.waiting_queue.remove(&hash) {
281 self.waiting_markers.remove(&waiting_tx.transaction.provides);
282 for marker in waiting_tx.missing_markers {
283 let remove = if let Some(required) = self.required_markers.get_mut(&marker) {
284 required.remove(&hash);
285 required.is_empty()
286 } else {
287 false
288 };
289 if remove {
290 self.required_markers.remove(&marker);
291 }
292 }
293 removed.push(waiting_tx.transaction)
294 }
295 }
296 removed
297 }
298
299 pub fn remove_with_dependents(
301 &mut self,
302 hashes: Vec<TxHash>,
303 invalidated: impl IntoIterator<Item = TxMarker>,
304 ) -> Vec<Arc<PoolTransaction<T>>> {
305 let mut required_by = HashMap::<TxMarker, Vec<TxHash>>::default();
306 for (hash, tx) in &self.waiting_queue {
307 for marker in &tx.transaction.requires {
308 required_by.entry(marker.clone()).or_default().push(*hash);
309 }
310 }
311
312 let mut to_remove = HashSet::<TxHash>::default();
313 let mut markers = invalidated.into_iter().collect::<Vec<_>>();
314 for hash in hashes {
315 if to_remove.insert(hash)
316 && let Some(tx) = self.waiting_queue.get(&hash)
317 {
318 markers.extend(tx.transaction.provides.iter().cloned());
319 }
320 }
321
322 while let Some(marker) = markers.pop() {
323 if let Some(dependents) = required_by.remove(&marker) {
324 for hash in dependents {
325 if to_remove.insert(hash)
326 && let Some(tx) = self.waiting_queue.get(&hash)
327 {
328 markers.extend(tx.transaction.provides.iter().cloned());
329 }
330 }
331 }
332 }
333
334 self.remove(to_remove.into_iter().collect())
335 }
336}
337
338impl<T: Transaction> PendingTransactions<T> {
339 pub fn add_transaction(&mut self, tx: PendingPoolTransaction<T>) -> Result<(), PoolError> {
341 assert!(!tx.is_ready(), "transaction must not be ready");
342 assert!(
343 !self.waiting_queue.contains_key(&tx.transaction.hash()),
344 "transaction is already added"
345 );
346
347 let replaced_hash = if let Some(replace) = self
348 .waiting_markers
349 .get(&tx.transaction.provides)
350 .and_then(|hash| self.waiting_queue.get(hash))
351 {
352 if tx.transaction.max_fee_per_gas() <= replace.transaction.max_fee_per_gas() {
354 warn!(target: "txpool", "pending replacement transaction underpriced [{:?}]", tx.transaction.hash());
355 return Err(PoolError::ReplacementUnderpriced(tx.transaction.hash()));
356 }
357 Some(replace.transaction.hash())
358 } else {
359 None
360 };
361 if let Some(replaced_hash) = replaced_hash {
363 self.remove(vec![replaced_hash]);
364 }
365
366 for marker in &tx.missing_markers {
368 self.required_markers.entry(marker.clone()).or_default().insert(tx.transaction.hash());
369 }
370
371 self.waiting_markers.insert(tx.transaction.provides.clone(), tx.transaction.hash());
373 self.waiting_queue.insert(tx.transaction.hash(), tx);
375
376 Ok(())
377 }
378}
379
380pub struct PendingPoolTransaction<T> {
382 pub transaction: Arc<PoolTransaction<T>>,
383 pub missing_markers: HashSet<TxMarker>,
385 pub added_at: Instant,
387}
388
389impl<T> Clone for PendingPoolTransaction<T> {
390 fn clone(&self) -> Self {
391 Self {
392 transaction: Arc::clone(&self.transaction),
393 missing_markers: self.missing_markers.clone(),
394 added_at: self.added_at,
395 }
396 }
397}
398
399impl<T> PendingPoolTransaction<T> {
400 pub fn new(transaction: PoolTransaction<T>, provided: &HashMap<TxMarker, TxHash>) -> Self {
405 let missing_markers = transaction
406 .requires
407 .iter()
408 .filter(|marker| {
409 !provided.contains_key(&**marker)
411 })
412 .cloned()
413 .collect();
414
415 Self { transaction: Arc::new(transaction), missing_markers, added_at: Instant::now() }
416 }
417
418 pub fn mark(&mut self, marker: &TxMarker) {
420 self.missing_markers.remove(marker);
421 }
422
423 pub fn is_ready(&self) -> bool {
425 self.missing_markers.is_empty()
426 }
427}
428
429impl<T: fmt::Debug> fmt::Debug for PendingPoolTransaction<T> {
430 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
431 write!(fmt, "PendingTransaction {{ ")?;
432 write!(fmt, "added_at: {:?}, ", self.added_at)?;
433 write!(fmt, "tx: {:?}, ", self.transaction)?;
434 write!(fmt, "missing_markers: {{{}}}", hex_fmt_many(self.missing_markers.iter()))?;
435 write!(fmt, "}}")
436 }
437}
438
439pub struct TransactionsIterator<T> {
440 all: HashMap<TxHash, ReadyTransaction<T>>,
441 awaiting: HashMap<TxHash, (usize, PoolTransactionRef<T>)>,
442 independent: BTreeMap<(u128, Reverse<u64>), PoolTransactionRef<T>>,
444 base_fee: u64,
445 _invalid: HashSet<TxHash>,
446}
447
448impl<T> TransactionsIterator<T> {
449 fn insert_independent(&mut self, tx_ref: PoolTransactionRef<T>) {
451 let tip = tx_ref.transaction.priority.effective_tip(self.base_fee);
452 self.independent.insert((tip, Reverse(tx_ref.id)), tx_ref);
453 }
454
455 fn independent_or_awaiting(&mut self, satisfied: usize, tx_ref: PoolTransactionRef<T>) {
458 if satisfied >= tx_ref.transaction.requires.len() {
459 self.insert_independent(tx_ref);
461 } else {
462 self.awaiting.insert(tx_ref.transaction.hash(), (satisfied, tx_ref));
464 }
465 }
466}
467
468impl<T> Iterator for TransactionsIterator<T> {
469 type Item = Arc<PoolTransaction<T>>;
470
471 fn next(&mut self) -> Option<Self::Item> {
472 loop {
473 let (_, best) = self.independent.pop_last()?;
474 let hash = best.transaction.hash();
475
476 let ready =
477 if let Some(ready) = self.all.get(&hash).cloned() { ready } else { continue };
478
479 for hash in &ready.unlocks {
481 let res = if let Some((mut satisfied, tx_ref)) = self.awaiting.remove(hash) {
483 satisfied += 1;
484 Some((satisfied, tx_ref))
485 } else {
487 self.all
488 .get(hash)
489 .map(|next| (next.requires_offset + 1, next.transaction.clone()))
490 };
491 if let Some((satisfied, tx_ref)) = res {
492 self.independent_or_awaiting(satisfied, tx_ref)
493 }
494 }
495
496 return Some(best.transaction);
497 }
498 }
499}
500
501#[derive(Clone, Debug)]
503pub struct ReadyTransactions<T> {
504 id: u64,
508 provided_markers: HashMap<TxMarker, TxHash>,
510 ready_tx: Arc<RwLock<HashMap<TxHash, ReadyTransaction<T>>>>,
512 independent_transactions: BTreeSet<PoolTransactionRef<T>>,
515}
516
517impl<T> Default for ReadyTransactions<T> {
518 fn default() -> Self {
519 Self {
520 id: 0,
521 provided_markers: Default::default(),
522 ready_tx: Default::default(),
523 independent_transactions: Default::default(),
524 }
525 }
526}
527
528impl<T> ReadyTransactions<T> {
529 pub(super) fn snapshot(&self) -> Self {
531 Self {
532 id: self.id,
533 provided_markers: self.provided_markers.clone(),
534 ready_tx: Arc::new(RwLock::new(self.ready_tx.read().clone())),
535 independent_transactions: self.independent_transactions.clone(),
536 }
537 }
538
539 pub fn get_transactions(&self, base_fee: u64) -> TransactionsIterator<T> {
541 let mut iter = TransactionsIterator {
542 all: self.ready_tx.read().clone(),
543 independent: Default::default(),
544 base_fee,
545 awaiting: Default::default(),
546 _invalid: Default::default(),
547 };
548 for tx_ref in &self.independent_transactions {
549 iter.insert_independent(tx_ref.clone());
550 }
551 iter
552 }
553
554 pub fn clear(&mut self) {
556 self.provided_markers.clear();
557 self.ready_tx.write().clear();
558 self.independent_transactions.clear();
559 }
560
561 pub fn contains(&self, hash: &TxHash) -> bool {
563 self.ready_tx.read().contains_key(hash)
564 }
565
566 pub fn len(&self) -> usize {
568 self.ready_tx.read().len()
569 }
570
571 pub fn is_empty(&self) -> bool {
573 self.ready_tx.read().is_empty()
574 }
575
576 pub fn get(&self, hash: &TxHash) -> Option<ReadyTransaction<T>> {
578 self.ready_tx.read().get(hash).cloned()
579 }
580
581 pub const fn provided_markers(&self) -> &HashMap<TxMarker, TxHash> {
582 &self.provided_markers
583 }
584
585 const fn next_id(&mut self) -> u64 {
586 let id = self.id;
587 self.id = self.id.wrapping_add(1);
588 id
589 }
590
591 pub fn clear_transactions(&mut self, tx_hashes: &[TxHash]) -> Vec<Arc<PoolTransaction<T>>> {
594 self.remove_with_markers(tx_hashes.to_vec(), None)
595 }
596
597 pub fn prune_tags(&mut self, marker: TxMarker) -> Vec<Arc<PoolTransaction<T>>> {
602 let mut removed_tx = vec![];
603
604 let mut remove = vec![marker];
606
607 while let Some(marker) = remove.pop() {
608 let res = self
609 .provided_markers
610 .remove(&marker)
611 .and_then(|hash| self.ready_tx.write().remove(&hash));
612
613 if let Some(tx) = res {
614 let unlocks = tx.unlocks;
615 self.independent_transactions.remove(&tx.transaction);
616 let tx = tx.transaction.transaction;
617
618 {
620 let hash = tx.hash();
621 let mut ready = self.ready_tx.write();
622
623 let mut previous_markers = |marker| -> Option<Vec<TxMarker>> {
624 let prev_hash = self.provided_markers.get(marker)?;
625 let tx2 = ready.get_mut(prev_hash)?;
626 if let Some(idx) = tx2.unlocks.iter().position(|i| i == &hash) {
628 tx2.unlocks.swap_remove(idx);
629 }
630 tx2.unlocks.is_empty().then(|| tx2.transaction.transaction.provides.clone())
631 };
632
633 for marker in &tx.requires {
635 if let Some(mut tags_to_remove) = previous_markers(marker) {
636 remove.append(&mut tags_to_remove);
637 }
638 }
639 }
640
641 for hash in unlocks {
643 if let Some(tx) = self.ready_tx.write().get_mut(&hash) {
644 tx.requires_offset += 1;
645 if tx.requires_offset == tx.transaction.transaction.requires.len() {
646 self.independent_transactions.insert(tx.transaction.clone());
647 }
648 }
649 }
650 let current_marker = ▮
652 for marker in &tx.provides {
653 let removed = self.provided_markers.remove(marker);
654 assert_eq!(
655 removed,
656 if current_marker == marker { None } else { Some(tx.hash()) },
657 "The pool contains exactly one transaction providing given tag; the removed transaction
658 claims to provide that tag, so it has to be mapped to it's hash; qed"
659 );
660 }
661 removed_tx.push(tx);
662 }
663 }
664
665 removed_tx
666 }
667
668 pub fn remove_with_markers(
671 &mut self,
672 mut tx_hashes: Vec<TxHash>,
673 marker_filter: Option<HashSet<TxMarker>>,
674 ) -> Vec<Arc<PoolTransaction<T>>> {
675 let mut removed = Vec::new();
676 let mut ready = self.ready_tx.write();
677
678 while let Some(hash) = tx_hashes.pop() {
679 if let Some(mut tx) = ready.remove(&hash) {
680 let invalidated = tx.transaction.transaction.provides.iter().filter(|mark| {
681 marker_filter.as_ref().is_none_or(|filter| !filter.contains(&**mark))
682 });
683
684 let mut removed_some_marks = false;
685 for mark in invalidated {
687 removed_some_marks = true;
688 self.provided_markers.remove(mark);
689 }
690
691 for mark in &tx.transaction.transaction.requires {
693 if let Some(provider_hash) = self.provided_markers.get(mark)
694 && let Some(provider_tx) = ready.get_mut(provider_hash)
695 && let Some(idx) = provider_tx.unlocks.iter().position(|i| i == &hash)
696 {
697 provider_tx.unlocks.swap_remove(idx);
698 }
699 }
700
701 self.independent_transactions.remove(&tx.transaction);
703
704 if removed_some_marks {
705 tx_hashes.append(&mut tx.unlocks);
707 }
708
709 removed.push(tx.transaction.transaction);
711 }
712 }
713
714 removed
715 }
716}
717
718impl<T: Transaction> ReadyTransactions<T> {
719 pub fn add_transaction(
726 &mut self,
727 tx: PendingPoolTransaction<T>,
728 ) -> Result<Vec<Arc<PoolTransaction<T>>>, PoolError> {
729 assert!(tx.is_ready(), "transaction must be ready",);
730 assert!(
731 !self.ready_tx.read().contains_key(&tx.transaction.hash()),
732 "transaction already included"
733 );
734
735 let (replaced_tx, unlocks) = self.replaced_transactions(&tx.transaction)?;
736
737 let id = self.next_id();
738 let hash = tx.transaction.hash();
739
740 let mut independent = true;
741 let mut requires_offset = 0;
742 let mut ready = self.ready_tx.write();
743 for mark in &tx.transaction.requires {
745 if let Some(other) = self.provided_markers.get(mark) {
747 let tx = ready.get_mut(other).expect("hash included;");
748 tx.unlocks.push(hash);
749 independent = false;
751 } else {
752 requires_offset += 1;
753 }
754 }
755
756 for mark in tx.transaction.provides.iter().cloned() {
758 self.provided_markers.insert(mark, hash);
759 }
760
761 let transaction = PoolTransactionRef { id, transaction: tx.transaction };
762
763 if independent {
765 self.independent_transactions.insert(transaction.clone());
766 }
767
768 ready.insert(hash, ReadyTransaction { transaction, unlocks, requires_offset });
770
771 Ok(replaced_tx)
772 }
773
774 fn replaced_transactions(
776 &mut self,
777 tx: &PoolTransaction<T>,
778 ) -> Result<ReplacedTransactions<T>, PoolError> {
779 let remove_hashes: HashSet<_> =
781 tx.provides.iter().filter_map(|mark| self.provided_markers.get(mark)).collect();
782
783 if remove_hashes.is_empty() {
785 return Ok((Vec::new(), Vec::new()));
786 }
787
788 let mut unlocked_tx = Vec::new();
790 {
791 let ready = self.ready_tx.read();
794 for to_remove in remove_hashes.iter().filter_map(|hash| ready.get(*hash)) {
795 if to_remove.provides() == tx.provides {
798 if tx.pending_transaction.transaction.max_fee_per_gas()
800 <= to_remove.max_fee_per_gas()
801 {
802 warn!(target: "txpool", "ready replacement transaction underpriced [{:?}]", tx.hash());
803 return Err(PoolError::ReplacementUnderpriced(tx.hash()));
804 }
805 trace!(target: "txpool", "replacing ready transaction [{:?}] with higher gas price [{:?}]", to_remove.transaction.transaction.hash(), tx.hash());
806 }
807
808 unlocked_tx.extend(to_remove.unlocks.iter().copied())
809 }
810 }
811
812 let remove_hashes = remove_hashes.into_iter().copied().collect::<Vec<_>>();
813
814 let new_provides = tx.provides.iter().cloned().collect::<HashSet<_>>();
815 let removed_tx = self.remove_with_markers(remove_hashes, Some(new_provides));
816
817 Ok((removed_tx, unlocked_tx))
818 }
819}
820
821#[derive(Debug)]
823pub struct PoolTransactionRef<T> {
824 pub transaction: Arc<PoolTransaction<T>>,
826 pub id: u64,
828}
829
830impl<T> Clone for PoolTransactionRef<T> {
831 fn clone(&self) -> Self {
832 Self { transaction: Arc::clone(&self.transaction), id: self.id }
833 }
834}
835
836impl<T> Eq for PoolTransactionRef<T> {}
837
838impl<T> PartialEq<Self> for PoolTransactionRef<T> {
839 fn eq(&self, other: &Self) -> bool {
840 self.cmp(other) == Ordering::Equal
841 }
842}
843
844impl<T> PartialOrd<Self> for PoolTransactionRef<T> {
845 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
846 Some(self.cmp(other))
847 }
848}
849
850impl<T> Ord for PoolTransactionRef<T> {
851 fn cmp(&self, other: &Self) -> Ordering {
852 self.transaction
853 .priority
854 .cmp(&other.transaction.priority)
855 .then_with(|| other.id.cmp(&self.id))
856 }
857}
858
859#[derive(Debug)]
860pub struct ReadyTransaction<T> {
861 pub transaction: PoolTransactionRef<T>,
863 pub unlocks: Vec<TxHash>,
865 pub requires_offset: usize,
867}
868
869impl<T> Clone for ReadyTransaction<T> {
870 fn clone(&self) -> Self {
871 Self {
872 transaction: self.transaction.clone(),
873 unlocks: self.unlocks.clone(),
874 requires_offset: self.requires_offset,
875 }
876 }
877}
878
879impl<T> ReadyTransaction<T> {
880 pub fn provides(&self) -> &[TxMarker] {
881 &self.transaction.transaction.provides
882 }
883}
884
885impl<T: Transaction> ReadyTransaction<T> {
886 pub fn max_fee_per_gas(&self) -> u128 {
887 self.transaction.transaction.max_fee_per_gas()
888 }
889}
890
891pub fn to_marker(nonce: u64, from: Address) -> TxMarker {
893 let mut data = [0u8; 28];
894 data[..8].copy_from_slice(&nonce.to_le_bytes()[..]);
895 data[8..].copy_from_slice(&from.0[..]);
896 data.to_vec()
897}
898
899#[cfg(test)]
900mod tests {
901 use super::*;
902
903 #[test]
904 fn can_id_txs() {
905 let addr = Address::random();
906 assert_eq!(to_marker(1, addr), to_marker(1, addr));
907 assert_ne!(to_marker(2, addr), to_marker(1, addr));
908 }
909}