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 efficiency, 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