bump to ldk 118
[rapid-gossip-sync-server] / src / verifier.rs
index 90f2f3c776fbf6724faec15a839ed2e85f39aadb..5e912446d3a219ad22f8363a3b329a254f45cbb9 100644 (file)
 use std::convert::TryInto;
+use std::ops::Deref;
 use std::sync::Arc;
+use std::sync::Mutex;
 
+use bitcoin::blockdata::constants::ChainHash;
 use bitcoin::{BlockHash, TxOut};
 use bitcoin::blockdata::block::Block;
 use bitcoin::hashes::Hash;
-use lightning::chain;
-use lightning::chain::AccessError;
-use lightning_block_sync::BlockSource;
+use lightning::log_error;
+use lightning::routing::gossip::{NetworkGraph, P2PGossipSync};
+use lightning::routing::utxo::{UtxoFuture, UtxoLookup, UtxoResult, UtxoLookupError};
+use lightning::util::logger::Logger;
+use lightning_block_sync::{BlockData, BlockSource};
 use lightning_block_sync::http::BinaryResponse;
 use lightning_block_sync::rest::RestClient;
 
 use crate::config;
+use crate::types::GossipPeerManager;
 
-pub(crate) struct ChainVerifier {
+pub(crate) struct ChainVerifier<L: Deref + Clone + Send + Sync + 'static> where L::Target: Logger {
        rest_client: Arc<RestClient>,
+       graph: Arc<NetworkGraph<L>>,
+       outbound_gossiper: Arc<P2PGossipSync<Arc<NetworkGraph<L>>, Arc<Self>, L>>,
+       peer_handler: Mutex<Option<GossipPeerManager<L>>>,
+       logger: L
 }
 
 struct RestBinaryResponse(Vec<u8>);
 
-impl ChainVerifier {
-       pub(crate) fn new() -> Self {
-               let rest_client = RestClient::new(config::bitcoin_rest_endpoint()).unwrap();
+impl<L: Deref + Clone + Send + Sync + 'static> ChainVerifier<L> where L::Target: Logger {
+       pub(crate) fn new(graph: Arc<NetworkGraph<L>>, outbound_gossiper: Arc<P2PGossipSync<Arc<NetworkGraph<L>>, Arc<Self>, L>>, logger: L) -> Self {
                ChainVerifier {
-                       rest_client: Arc::new(rest_client),
+                       rest_client: Arc::new(RestClient::new(config::bitcoin_rest_endpoint()).unwrap()),
+                       outbound_gossiper,
+                       graph,
+                       peer_handler: Mutex::new(None),
+                       logger
                }
        }
-
-       fn retrieve_block(&self, block_height: u32) -> Result<Block, AccessError> {
-               let rest_client = self.rest_client.clone();
-               tokio::task::block_in_place(move || { tokio::runtime::Handle::current().block_on(async move {
-                       let block_hash_result = rest_client.request_resource::<BinaryResponse, RestBinaryResponse>(&format!("blockhashbyheight/{}.bin", block_height)).await;
-                       let block_hash: Vec<u8> = block_hash_result.map_err(|error| {
-                               eprintln!("Could't find block hash at height {}: {}", block_height, error.to_string());
-                               AccessError::UnknownChain
-                       })?.0;
-                       let block_hash = BlockHash::from_slice(&block_hash).unwrap();
-
-                       let block_result = rest_client.get_block(&block_hash).await;
-                       let block = block_result.map_err(|error| {
-                               eprintln!("Couldn't retrieve block {}: {:?} ({})", block_height, error, block_hash);
-                               AccessError::UnknownChain
-                       })?;
-                       Ok(block)
-               }) })
+       pub(crate) fn set_ph(&self, peer_handler: GossipPeerManager<L>) {
+               *self.peer_handler.lock().unwrap() = Some(peer_handler);
        }
-}
 
-impl chain::Access for ChainVerifier {
-       fn get_utxo(&self, _genesis_hash: &BlockHash, short_channel_id: u64) -> Result<TxOut, AccessError> {
+       async fn retrieve_utxo(client: Arc<RestClient>, short_channel_id: u64, logger: L) -> Result<TxOut, UtxoLookupError> {
                let block_height = (short_channel_id >> 5 * 8) as u32; // block height is most significant three bytes
                let transaction_index = ((short_channel_id >> 2 * 8) & 0xffffff) as u32;
                let output_index = (short_channel_id & 0xffff) as u16;
 
-               let block = self.retrieve_block(block_height)?;
-               let transaction = block.txdata.get(transaction_index as usize).ok_or_else(|| {
-                       eprintln!("Transaction index {} out of bounds in block {} ({})", transaction_index, block_height, block.block_hash().to_string());
-                       AccessError::UnknownTx
-               })?;
-               let output = transaction.output.get(output_index as usize).ok_or_else(|| {
-                       eprintln!("Output index {} out of bounds in transaction {}", output_index, transaction.txid().to_string());
-                       AccessError::UnknownTx
-               })?;
-               Ok(output.clone())
+               let mut block = Self::retrieve_block(client, block_height, logger.clone()).await?;
+               if transaction_index as usize >= block.txdata.len() {
+                       log_error!(logger, "Could't find transaction {} in block {}", transaction_index, block_height);
+                       return Err(UtxoLookupError::UnknownTx);
+               }
+               let mut transaction = block.txdata.swap_remove(transaction_index as usize);
+               if output_index as usize >= transaction.output.len() {
+                       log_error!(logger, "Could't find output {} in transaction {}", output_index, transaction.txid());
+                       return Err(UtxoLookupError::UnknownTx);
+               }
+               Ok(transaction.output.swap_remove(output_index as usize))
+       }
+
+       async fn retrieve_block(client: Arc<RestClient>, block_height: u32, logger: L) -> Result<Block, UtxoLookupError> {
+               let uri = format!("blockhashbyheight/{}.bin", block_height);
+               let block_hash_result =
+                       client.request_resource::<BinaryResponse, RestBinaryResponse>(&uri).await;
+               let block_hash: Vec<u8> = block_hash_result.map_err(|error| {
+                       log_error!(logger, "Could't find block hash at height {}: {}", block_height, error.to_string());
+                       UtxoLookupError::UnknownChain
+               })?.0;
+               let block_hash = BlockHash::from_slice(&block_hash).unwrap();
+
+               let block_result = client.get_block(&block_hash).await;
+               match block_result {
+                       Ok(BlockData::FullBlock(block)) => {
+                               Ok(block)
+                       },
+                       Ok(_) => unreachable!(),
+                       Err(error) => {
+                               log_error!(logger, "Couldn't retrieve block {}: {:?} ({})", block_height, error, block_hash);
+                               Err(UtxoLookupError::UnknownChain)
+                       }
+               }
+       }
+}
+
+impl<L: Deref + Clone + Send + Sync + 'static> UtxoLookup for ChainVerifier<L> where L::Target: Logger {
+       fn get_utxo(&self, _genesis_hash: &ChainHash, short_channel_id: u64) -> UtxoResult {
+               let res = UtxoFuture::new();
+               let fut = res.clone();
+               let graph_ref = Arc::clone(&self.graph);
+               let client_ref = Arc::clone(&self.rest_client);
+               let gossip_ref = Arc::clone(&self.outbound_gossiper);
+               let pm_ref = self.peer_handler.lock().unwrap().clone();
+               let logger_ref = self.logger.clone();
+               tokio::spawn(async move {
+                       let res = Self::retrieve_utxo(client_ref, short_channel_id, logger_ref).await;
+                       fut.resolve(&*graph_ref, &*gossip_ref, res);
+                       if let Some(pm) = pm_ref { pm.process_events(); }
+               });
+               UtxoResult::Async(res)
        }
 }