44
55use crate :: { AsyncBlockSourceResult , BlockData , BlockSource } ;
66
7+ use bitcoin:: blockdata:: block:: Block ;
78use bitcoin:: blockdata:: transaction:: { TxOut , OutPoint } ;
89use bitcoin:: hash_types:: BlockHash ;
910
@@ -17,7 +18,8 @@ use lightning::routing::utxo::{UtxoFuture, UtxoLookup, UtxoResult, UtxoLookupErr
1718
1819use lightning:: util:: logger:: Logger ;
1920
20- use std:: sync:: Arc ;
21+ use std:: sync:: { Arc , Mutex } ;
22+ use std:: collections:: VecDeque ;
2123use std:: future:: Future ;
2224use std:: ops:: Deref ;
2325
@@ -27,9 +29,6 @@ use std::ops::Deref;
2729/// Note that while this is implementable for a [`BlockSource`] which returns filtered block data
2830/// (i.e. [`BlockData::HeaderOnly`] for [`BlockSource::get_block`] requests), such an
2931/// 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.
3332pub trait UtxoSource : BlockSource + ' static {
3433 /// Fetches the block hash of the block at the given height.
3534 ///
@@ -91,8 +90,11 @@ pub struct GossipVerifier<S: FutureSpawner,
9190 peer_manager : Arc < PeerManager < Descriptor , CM , Arc < P2PGossipSync < Arc < NetworkGraph < L > > , Self , L > > , OM , L , CMH , NS > > ,
9291 gossiper : Arc < P2PGossipSync < Arc < NetworkGraph < L > > , Self , L > > ,
9392 spawn : S ,
93+ block_cache : Arc < Mutex < VecDeque < ( u32 , Block ) > > > ,
9494}
9595
96+ const BLOCK_CACHE_SIZE : usize = 5 ;
97+
9698impl < S : FutureSpawner ,
9799 Blocks : Deref + Send + Sync + Clone ,
98100 L : Deref + Send + Sync ,
@@ -114,34 +116,68 @@ impl<S: FutureSpawner,
114116 /// This is expected to be given to a [`P2PGossipSync`] (initially constructed with `None` for
115117 /// the UTXO lookup) via [`P2PGossipSync::add_utxo_lookup`].
116118 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+ }
118123 }
119124
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 > {
121128 let block_height = ( short_channel_id >> 5 * 8 ) as u32 ; // block height is most significant three bytes
122129 let transaction_index = ( ( short_channel_id >> 2 * 8 ) & 0xffffff ) as u32 ;
123130 let output_index = ( short_channel_id & 0xffff ) as u16 ;
124131
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;
132176 } ;
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- }
140177 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 ) ?;
143179 if outpoint_unspent {
144- Ok ( transaction . output . swap_remove ( output_index as usize ) )
180+ Ok ( output)
145181 } else {
146182 Err ( UtxoLookupError :: UnknownTx )
147183 }
@@ -190,9 +226,10 @@ impl<S: FutureSpawner,
190226 let fut = res. clone ( ) ;
191227 let source = self . source . clone ( ) ;
192228 let gossiper = Arc :: clone ( & self . gossiper ) ;
229+ let block_cache = Arc :: clone ( & self . block_cache ) ;
193230 let pm = Arc :: clone ( & self . peer_manager ) ;
194231 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 ;
196233 fut. resolve ( gossiper. network_graph ( ) , & * gossiper, res) ;
197234 pm. process_events ( ) ;
198235 } ) ;
0 commit comments