4
4
5
5
use crate :: { AsyncBlockSourceResult , BlockData , BlockSource } ;
6
6
7
+ use bitcoin:: blockdata:: block:: Block ;
7
8
use bitcoin:: blockdata:: transaction:: { TxOut , OutPoint } ;
8
9
use bitcoin:: hash_types:: BlockHash ;
9
10
@@ -17,7 +18,8 @@ use lightning::routing::utxo::{UtxoFuture, UtxoLookup, UtxoResult, UtxoLookupErr
17
18
18
19
use lightning:: util:: logger:: Logger ;
19
20
20
- use std:: sync:: Arc ;
21
+ use std:: sync:: { Arc , Mutex } ;
22
+ use std:: collections:: VecDeque ;
21
23
use std:: future:: Future ;
22
24
use std:: ops:: Deref ;
23
25
@@ -27,9 +29,6 @@ use std::ops::Deref;
27
29
/// Note that while this is implementable for a [`BlockSource`] which returns filtered block data
28
30
/// (i.e. [`BlockData::HeaderOnly`] for [`BlockSource::get_block`] requests), such an
29
31
/// implementation will reject all gossip as it is not fully able to verify the UTXOs referenced.
30
- ///
31
- /// For effeciency, an implementation may consider caching some set of blocks, as many redundant
32
- /// calls may be made.
33
32
pub trait UtxoSource : BlockSource + ' static {
34
33
/// Fetches the block hash of the block at the given height.
35
34
///
@@ -91,8 +90,11 @@ pub struct GossipVerifier<S: FutureSpawner,
91
90
peer_manager : Arc < PeerManager < Descriptor , CM , Arc < P2PGossipSync < Arc < NetworkGraph < L > > , Self , L > > , OM , L , CMH , NS > > ,
92
91
gossiper : Arc < P2PGossipSync < Arc < NetworkGraph < L > > , Self , L > > ,
93
92
spawn : S ,
93
+ block_cache : Arc < Mutex < VecDeque < ( u32 , Block ) > > > ,
94
94
}
95
95
96
+ const BLOCK_CACHE_SIZE : usize = 5 ;
97
+
96
98
impl < S : FutureSpawner ,
97
99
Blocks : Deref + Send + Sync + Clone ,
98
100
L : Deref + Send + Sync ,
@@ -114,34 +116,68 @@ impl<S: FutureSpawner,
114
116
/// This is expected to be given to a [`P2PGossipSync`] (initially constructed with `None` for
115
117
/// the UTXO lookup) via [`P2PGossipSync::add_utxo_lookup`].
116
118
pub fn new ( source : Blocks , spawn : S , gossiper : Arc < P2PGossipSync < Arc < NetworkGraph < L > > , Self , L > > , peer_manager : Arc < PeerManager < Descriptor , CM , Arc < P2PGossipSync < Arc < NetworkGraph < L > > , Self , L > > , OM , L , CMH , NS > > ) -> Self {
117
- Self { source, spawn, gossiper, peer_manager }
119
+ Self {
120
+ source, spawn, gossiper, peer_manager,
121
+ block_cache : Arc :: new ( Mutex :: new ( VecDeque :: with_capacity ( BLOCK_CACHE_SIZE ) ) ) ,
122
+ }
118
123
}
119
124
120
- async fn retrieve_utxo ( source : Blocks , short_channel_id : u64 ) -> Result < TxOut , UtxoLookupError > {
125
+ async fn retrieve_utxo (
126
+ source : Blocks , block_cache : Arc < Mutex < VecDeque < ( u32 , Block ) > > > , short_channel_id : u64
127
+ ) -> Result < TxOut , UtxoLookupError > {
121
128
let block_height = ( short_channel_id >> 5 * 8 ) as u32 ; // block height is most significant three bytes
122
129
let transaction_index = ( ( short_channel_id >> 2 * 8 ) & 0xffffff ) as u32 ;
123
130
let output_index = ( short_channel_id & 0xffff ) as u16 ;
124
131
125
- let block_hash = source. get_block_hash_by_height ( block_height) . await
126
- . map_err ( |_| UtxoLookupError :: UnknownTx ) ?;
127
- let block_data = source. get_block ( & block_hash) . await
128
- . map_err ( |_| UtxoLookupError :: UnknownTx ) ?;
129
- let mut block = match block_data {
130
- BlockData :: HeaderOnly ( _) => return Err ( UtxoLookupError :: UnknownTx ) ,
131
- BlockData :: FullBlock ( block) => block,
132
+ let ( outpoint, output) ;
133
+
134
+ ' tx_found: loop { // Used as a simple goto
135
+ macro_rules! process_block {
136
+ ( $block: expr) => { {
137
+ if transaction_index as usize >= $block. txdata. len( ) {
138
+ return Err ( UtxoLookupError :: UnknownTx ) ;
139
+ }
140
+ let transaction = & $block. txdata[ transaction_index as usize ] ;
141
+ if output_index as usize >= transaction. output. len( ) {
142
+ return Err ( UtxoLookupError :: UnknownTx ) ;
143
+ }
144
+
145
+ outpoint = OutPoint :: new( transaction. txid( ) , output_index. into( ) ) ;
146
+ output = transaction. output[ output_index as usize ] . clone( ) ;
147
+ } }
148
+ }
149
+ {
150
+ let recent_blocks = block_cache. lock ( ) . unwrap ( ) ;
151
+ for ( height, block) in recent_blocks. iter ( ) {
152
+ if * height == block_height {
153
+ process_block ! ( block) ;
154
+ break ' tx_found;
155
+ }
156
+ }
157
+ }
158
+
159
+ let block_hash = source. get_block_hash_by_height ( block_height) . await
160
+ . map_err ( |_| UtxoLookupError :: UnknownTx ) ?;
161
+ let block_data = source. get_block ( & block_hash) . await
162
+ . map_err ( |_| UtxoLookupError :: UnknownTx ) ?;
163
+ let block = match block_data {
164
+ BlockData :: HeaderOnly ( _) => return Err ( UtxoLookupError :: UnknownTx ) ,
165
+ BlockData :: FullBlock ( block) => block,
166
+ } ;
167
+ process_block ! ( block) ;
168
+ {
169
+ let mut recent_blocks = block_cache. lock ( ) . unwrap ( ) ;
170
+ if recent_blocks. len ( ) >= BLOCK_CACHE_SIZE {
171
+ recent_blocks. pop_front ( ) ;
172
+ }
173
+ recent_blocks. push_back ( ( block_height, block) ) ;
174
+ }
175
+ break ' tx_found;
132
176
} ;
133
- if transaction_index as usize >= block. txdata . len ( ) {
134
- return Err ( UtxoLookupError :: UnknownTx ) ;
135
- }
136
- let mut transaction = block. txdata . swap_remove ( transaction_index as usize ) ;
137
- if output_index as usize >= transaction. output . len ( ) {
138
- return Err ( UtxoLookupError :: UnknownTx ) ;
139
- }
140
177
let outpoint_unspent =
141
- source. is_output_unspent ( OutPoint :: new ( transaction. txid ( ) , output_index. into ( ) ) ) . await
142
- . map_err ( |_| UtxoLookupError :: UnknownTx ) ?;
178
+ source. is_output_unspent ( outpoint) . await . map_err ( |_| UtxoLookupError :: UnknownTx ) ?;
143
179
if outpoint_unspent {
144
- Ok ( transaction . output . swap_remove ( output_index as usize ) )
180
+ Ok ( output)
145
181
} else {
146
182
Err ( UtxoLookupError :: UnknownTx )
147
183
}
@@ -190,9 +226,10 @@ impl<S: FutureSpawner,
190
226
let fut = res. clone ( ) ;
191
227
let source = self . source . clone ( ) ;
192
228
let gossiper = Arc :: clone ( & self . gossiper ) ;
229
+ let block_cache = Arc :: clone ( & self . block_cache ) ;
193
230
let pm = Arc :: clone ( & self . peer_manager ) ;
194
231
self . spawn . spawn ( async move {
195
- let res = Self :: retrieve_utxo ( source, short_channel_id) . await ;
232
+ let res = Self :: retrieve_utxo ( source, block_cache , short_channel_id) . await ;
196
233
fut. resolve ( gossiper. network_graph ( ) , & * gossiper, res) ;
197
234
pm. process_events ( ) ;
198
235
} ) ;
0 commit comments