1
1
//! Unbroadcasted transaction queue.
2
2
3
- use std:: vec:: Vec ;
3
+ use alloc:: vec:: Vec ;
4
+ use chain:: tx_graph;
5
+ use chain:: Anchor ;
6
+ use chain:: TxGraph ;
4
7
5
- use crate :: collections:: BTreeMap ;
6
- use crate :: collections:: HashMap ;
8
+ use crate :: collections:: HashSet ;
7
9
use crate :: collections:: VecDeque ;
8
10
9
11
use bitcoin:: Txid ;
10
12
use chain:: Merge ;
11
- use chain:: TxUpdate ;
12
13
13
14
/// An ordered unbroadcasted list.
14
15
///
@@ -17,46 +18,34 @@ use chain::TxUpdate;
17
18
pub struct BroadcastQueue {
18
19
queue : VecDeque < Txid > ,
19
20
20
- /// Enforces that we only change the tx entry if the change has a larger `seq`.
21
- latest_seq : HashMap < Txid , ( u64 , bool ) > ,
22
- next_seq : u64 ,
21
+ /// Enforces that we do not have duplicates in `queue`.
22
+ dedup : HashSet < Txid > ,
23
23
}
24
24
25
- /// Represents changes made to [`BroadcastQueue`].
25
+ /// Represents a single mutation to [`BroadcastQueue`].
26
+ #[ derive( Debug , Clone , PartialEq , serde:: Deserialize , serde:: Serialize ) ]
27
+ pub enum Mutation {
28
+ /// A push to the back of the queue.
29
+ Push ( Txid ) ,
30
+ ///
31
+ Remove ( Txid ) ,
32
+ }
33
+
34
+ /// A list of mutations made to [`BroadcastQueue`].
26
35
#[ must_use]
27
36
#[ derive( Debug , Clone , Default , PartialEq , serde:: Deserialize , serde:: Serialize ) ]
28
37
pub struct ChangeSet {
29
- pub insert_tx : BTreeMap < Txid , u64 > ,
30
- pub remove_tx : BTreeMap < Txid , u64 > ,
38
+ /// Mutations.
39
+ pub mutations : Vec < Mutation > ,
31
40
}
32
41
33
42
impl Merge for ChangeSet {
34
43
fn merge ( & mut self , other : Self ) {
35
- self . insert_tx . extend (
36
- other
37
- . insert_tx
38
- . into_iter ( )
39
- // Only replace `seq`uences which are larger.
40
- . filter ( |( txid, other_seq) | match self . insert_tx . get ( txid) {
41
- Some ( seq) => seq < other_seq,
42
- None => true ,
43
- } )
44
- . collect :: < Vec < _ > > ( ) ,
45
- ) ;
46
- self . remove_tx . extend (
47
- other
48
- . remove_tx
49
- . into_iter ( )
50
- . filter ( |( txid, other_seq) | match self . remove_tx . get ( txid) {
51
- Some ( seq) => seq < other_seq,
52
- None => true ,
53
- } )
54
- . collect :: < Vec < _ > > ( ) ,
55
- ) ;
44
+ self . mutations . extend ( other. mutations ) ;
56
45
}
57
46
58
47
fn is_empty ( & self ) -> bool {
59
- self . insert_tx . is_empty ( ) && self . remove_tx . is_empty ( )
48
+ self . mutations . is_empty ( )
60
49
}
61
50
}
62
51
@@ -70,119 +59,175 @@ impl BroadcastQueue {
70
59
71
60
/// Apply the given `changeset`.
72
61
pub fn apply_changeset ( & mut self , changeset : ChangeSet ) {
73
- for ( txid, seq) in changeset. insert_tx {
74
- self . _push ( txid, seq) ;
75
- }
76
- for ( txid, seq) in changeset. remove_tx {
77
- self . _remove ( txid, seq) ;
78
- }
79
- self . _sort_queue ( ) ;
80
- }
81
-
82
- fn _sort_queue ( & mut self ) {
83
- self . queue
84
- . make_contiguous ( )
85
- . sort_unstable_by_key ( |txid| self . latest_seq . get ( txid) ) ;
86
- }
87
-
88
- fn _push ( & mut self , txid : Txid , seq : u64 ) -> bool {
89
- let modified = & mut false ;
90
- self . latest_seq
91
- . entry ( txid)
92
- . and_modify ( |( v, in_queue) | {
93
- if * v < seq {
94
- * v = seq;
95
- * modified = true ;
96
- if !* in_queue {
97
- * in_queue = true ;
98
- self . queue . push_back ( txid) ;
99
- }
100
- }
101
- } )
102
- . or_insert_with ( || {
103
- * modified = true ;
104
- self . queue . push_back ( txid) ;
105
- ( seq, true )
106
- } ) ;
107
- self . next_seq = self . next_seq . max ( seq. saturating_add ( 1 ) ) ;
108
- * modified
109
- }
110
-
111
- fn _contains ( latest_seq : & mut HashMap < Txid , ( u64 , bool ) > , txid : Txid ) -> bool {
112
- match latest_seq. get ( & txid) {
113
- Some ( & ( _, contains) ) => contains,
114
- None => false ,
62
+ for mutation in changeset. mutations {
63
+ match mutation {
64
+ Mutation :: Push ( txid) => self . _push ( txid) ,
65
+ Mutation :: Remove ( txid) => self . _remove ( txid) ,
66
+ } ;
115
67
}
116
68
}
117
69
118
- fn _remove ( & mut self , txid : Txid , seq : u64 ) -> bool {
119
- let modified = & mut false ;
120
- self . latest_seq
121
- . entry ( txid)
122
- . and_modify ( |( v, in_queue) | {
123
- if * v <= seq {
124
- * v = seq;
125
- * modified = true ;
126
- if * in_queue {
127
- * in_queue = false ;
128
- // Most of the time, we are popping from the front of the queue.
129
- let index = self
130
- . queue
131
- . iter ( )
132
- . enumerate ( )
133
- . find_map ( |( i, & q_txid) | if q_txid == txid { Some ( i) } else { None } )
134
- . expect ( "must be in queue" ) ;
135
- self . queue . swap_remove_front ( index) ;
136
- }
137
- }
138
- } )
139
- . or_insert_with ( || {
140
- * modified = true ;
141
- ( seq, false )
142
- } ) ;
143
- self . next_seq = self . next_seq . max ( seq. saturating_add ( 1 ) ) ;
144
- * modified
70
+ /// Whether the `txid` exists in the queue.
71
+ pub fn contains ( & self , txid : Txid ) -> bool {
72
+ self . dedup . contains ( & txid)
145
73
}
146
74
147
- /// Reinserting will bump the tx's seq to `next_seq`.
75
+ /// Push a `txid` to the queue if it does not already exist.
76
+ ///
77
+ /// # Warning
78
+ ///
79
+ /// This does not get rid of conflicting transactions already in the queue.
148
80
pub fn push ( & mut self , txid : Txid ) -> ChangeSet {
149
- let seq = self . next_seq ;
81
+ let mut changeset = ChangeSet :: default ( ) ;
82
+ if self . _push ( txid) {
83
+ changeset. mutations . push ( Mutation :: Push ( txid) ) ;
84
+ }
85
+ changeset
86
+ }
87
+ fn _push ( & mut self , txid : Txid ) -> bool {
88
+ if self . dedup . insert ( txid) {
89
+ self . queue . push_back ( txid) ;
90
+ return true ;
91
+ }
92
+ false
93
+ }
150
94
95
+ /// Push a `txid` to the broadcast queue (if it does not exist already) and displaces all
96
+ /// coflicting txids in the queue.
97
+ pub fn push_and_displace_conflicts < A > ( & mut self , tx_graph : & TxGraph < A > , txid : Txid ) -> ChangeSet
98
+ where
99
+ A : Anchor ,
100
+ {
151
101
let mut changeset = ChangeSet :: default ( ) ;
152
- if self . _push ( txid, seq) {
153
- changeset. insert_tx . insert ( txid, seq) ;
154
- self . next_seq += 1 ;
102
+
103
+ let tx = match tx_graph. get_tx ( txid) {
104
+ Some ( tx) => tx,
105
+ None => {
106
+ debug_assert ! (
107
+ !self . dedup. contains( & txid) ,
108
+ "Cannot have txid in queue which has no corresponding tx in graph"
109
+ ) ;
110
+ return changeset;
111
+ }
112
+ } ;
113
+
114
+ if self . _push ( txid) {
115
+ changeset. mutations . push ( Mutation :: Push ( txid) ) ;
116
+
117
+ for txid in tx_graph. walk_conflicts ( & tx, |_, conflict_txid| Some ( conflict_txid) ) {
118
+ if self . _remove ( txid) {
119
+ changeset. mutations . push ( Mutation :: Remove ( txid) ) ;
120
+ }
121
+ }
155
122
}
123
+
156
124
changeset
157
125
}
158
126
159
- /// Returns the next `txid` of the transaction to broadcast.
160
- pub fn next ( & self ) -> Option < Txid > {
161
- // TODO: Implement.
162
- // NOTE: Prioritize txs that have no dependencies.
163
- None
127
+ /// Returns the next `txid` of the queue to broadcast which has no dependencies to other
128
+ /// transactions in the queue.
129
+ pub fn next_to_broadcast < A > ( & self , tx_graph : & TxGraph < A > ) -> Option < Txid >
130
+ where
131
+ A : Anchor ,
132
+ {
133
+ self . queue . iter ( ) . copied ( ) . find ( |& txid| {
134
+ let tx = match tx_graph. get_tx ( txid) {
135
+ Some ( tx) => tx,
136
+ None => return false ,
137
+ } ;
138
+ if tx
139
+ . input
140
+ . iter ( )
141
+ . any ( |txin| self . dedup . contains ( & txin. previous_output . txid ) )
142
+ {
143
+ return false ;
144
+ }
145
+ true
146
+ } )
164
147
}
165
148
166
- /// Untrack the `txid`.
167
- pub fn remove ( & mut self , txid : Txid ) -> ChangeSet {
168
- let seq = self . next_seq ;
149
+ /// Returns unbroadcasted dependencies of the given `txid`.
150
+ ///
151
+ /// The returned `Vec` is in broadcast order.
152
+ pub fn unbroadcasted_dependencies < A > ( & self , tx_graph : & TxGraph < A > , txid : Txid ) -> Vec < Txid >
153
+ where
154
+ A : Anchor ,
155
+ {
156
+ let tx = match tx_graph. get_tx ( txid) {
157
+ Some ( tx) => tx,
158
+ None => return Vec :: new ( ) ,
159
+ } ;
160
+ let mut txs = tx_graph
161
+ . walk_ancestors ( tx, |_depth, ancestor_tx| {
162
+ let ancestor_txid = ancestor_tx. compute_txid ( ) ;
163
+ if self . dedup . contains ( & ancestor_txid) {
164
+ Some ( ancestor_txid)
165
+ } else {
166
+ None
167
+ }
168
+ } )
169
+ . collect :: < Vec < _ > > ( ) ;
170
+ txs. reverse ( ) ;
171
+ txs
172
+ }
169
173
174
+ /// Untracks and removes a transaction from the broadcast queue.
175
+ ///
176
+ /// Transactions are automatically removed from the queue upon successful broadcast, so calling
177
+ /// this method directly is typically not required.
178
+ pub fn remove ( & mut self , txid : Txid ) -> ChangeSet {
170
179
let mut changeset = ChangeSet :: default ( ) ;
171
- if self . _remove ( txid, seq) {
172
- changeset. remove_tx . insert ( txid, seq) ;
173
- self . next_seq += 1 ;
180
+ if self . _remove ( txid) {
181
+ changeset. mutations . push ( Mutation :: Remove ( txid) ) ;
174
182
}
175
183
changeset
176
184
}
185
+ fn _remove ( & mut self , txid : Txid ) -> bool {
186
+ if self . dedup . remove ( & txid) {
187
+ let i = ( 0 ..self . queue . len ( ) )
188
+ . zip ( self . queue . iter ( ) . copied ( ) )
189
+ . find_map ( |( i, queue_txid) | if queue_txid == txid { Some ( i) } else { None } )
190
+ . expect ( "must exist in queue to exist in `queue`" ) ;
191
+ let _removed = self . queue . remove ( i) ;
192
+ debug_assert_eq ! ( _removed, Some ( txid) ) ;
193
+ return true ;
194
+ }
195
+ false
196
+ }
177
197
178
- /// Untrack transactions that are given anchors and seen-at timestamps.
179
- pub fn update < A > ( & mut self , tx_update : & TxUpdate < A > ) -> ChangeSet {
198
+ /// Untracks and removes a transaction and it's descendants from the broadcast queue.
199
+ pub fn remove_and_displace_dependants < A > (
200
+ & mut self ,
201
+ tx_graph : & TxGraph < A > ,
202
+ txid : Txid ,
203
+ ) -> ChangeSet
204
+ where
205
+ A : Anchor ,
206
+ {
180
207
let mut changeset = ChangeSet :: default ( ) ;
181
- for ( _, txid) in & tx_update. anchors {
182
- changeset. merge ( self . remove ( * txid) ) ;
208
+
209
+ if self . _remove ( txid) {
210
+ changeset. mutations . push ( Mutation :: Remove ( txid) ) ;
211
+ for txid in tx_graph. walk_descendants ( txid, |_depth, txid| Some ( txid) ) {
212
+ if self . _remove ( txid) {
213
+ changeset. mutations . push ( Mutation :: Remove ( txid) ) ;
214
+ }
215
+ }
183
216
}
184
- for ( txid, _) in & tx_update. seen_ats {
185
- changeset. merge ( self . remove ( * txid) ) ;
217
+ changeset
218
+ }
219
+
220
+ /// Untrack transactions that are given anchors and/or mempool timestamps.
221
+ pub fn filter_from_graph_changeset < A > (
222
+ & mut self ,
223
+ graph_changeset : & tx_graph:: ChangeSet < A > ,
224
+ ) -> ChangeSet {
225
+ let mut changeset = ChangeSet :: default ( ) ;
226
+ let s_txids = graph_changeset. last_seen . iter ( ) . map ( |( txid, _) | * txid) ;
227
+ let a_txids = graph_changeset. anchors . iter ( ) . map ( |( _, txid) | * txid) ;
228
+ let e_txids = graph_changeset. last_evicted . iter ( ) . map ( |( txid, _) | * txid) ;
229
+ for txid in s_txids. chain ( a_txids) . chain ( e_txids) {
230
+ changeset. merge ( self . remove ( txid) ) ;
186
231
}
187
232
changeset
188
233
}
@@ -193,4 +238,11 @@ impl BroadcastQueue {
193
238
pub fn txids ( & self ) -> impl ExactSizeIterator < Item = Txid > + ' _ {
194
239
self . queue . iter ( ) . copied ( )
195
240
}
241
+
242
+ /// Initial changeset.
243
+ pub fn initial_changeset ( & self ) -> ChangeSet {
244
+ ChangeSet {
245
+ mutations : self . queue . iter ( ) . copied ( ) . map ( Mutation :: Push ) . collect ( ) ,
246
+ }
247
+ }
196
248
}
0 commit comments