Allow custom logger types.
[rapid-gossip-sync-server] / src / verifier.rs
1 use std::convert::TryInto;
2 use std::sync::Arc;
3 use std::sync::Mutex;
4
5 use bitcoin::{BlockHash, TxOut};
6 use bitcoin::blockdata::block::Block;
7 use bitcoin::hashes::Hash;
8 use lightning::routing::gossip::{NetworkGraph, P2PGossipSync};
9 use lightning::routing::utxo::{UtxoFuture, UtxoLookup, UtxoResult, UtxoLookupError};
10 use lightning::util::logger::Logger;
11 use lightning_block_sync::{BlockData, BlockSource};
12 use lightning_block_sync::http::BinaryResponse;
13 use lightning_block_sync::rest::RestClient;
14
15 use crate::config;
16 use crate::types::GossipPeerManager;
17
18 pub(crate) struct ChainVerifier<L: Logger + Send + Sync + 'static> {
19         rest_client: Arc<RestClient>,
20         graph: Arc<NetworkGraph<Arc<L>>>,
21         outbound_gossiper: Arc<P2PGossipSync<Arc<NetworkGraph<Arc<L>>>, Arc<Self>, Arc<L>>>,
22         peer_handler: Mutex<Option<GossipPeerManager<L>>>,
23 }
24
25 struct RestBinaryResponse(Vec<u8>);
26
27 impl<L: Logger + Send + Sync + 'static> ChainVerifier<L> {
28         pub(crate) fn new(graph: Arc<NetworkGraph<Arc<L>>>, outbound_gossiper: Arc<P2PGossipSync<Arc<NetworkGraph<Arc<L>>>, Arc<Self>, Arc<L>>>) -> Self {
29                 ChainVerifier {
30                         rest_client: Arc::new(RestClient::new(config::bitcoin_rest_endpoint()).unwrap()),
31                         outbound_gossiper,
32                         graph,
33                         peer_handler: Mutex::new(None),
34                 }
35         }
36         pub(crate) fn set_ph(&self, peer_handler: GossipPeerManager<L>) {
37                 *self.peer_handler.lock().unwrap() = Some(peer_handler);
38         }
39
40         async fn retrieve_utxo(client: Arc<RestClient>, short_channel_id: u64) -> Result<TxOut, UtxoLookupError> {
41                 let block_height = (short_channel_id >> 5 * 8) as u32; // block height is most significant three bytes
42                 let transaction_index = ((short_channel_id >> 2 * 8) & 0xffffff) as u32;
43                 let output_index = (short_channel_id & 0xffff) as u16;
44
45                 let mut block = Self::retrieve_block(client, block_height).await?;
46                 if transaction_index as usize >= block.txdata.len() { return Err(UtxoLookupError::UnknownTx); }
47                 let mut transaction = block.txdata.swap_remove(transaction_index as usize);
48                 if output_index as usize >= transaction.output.len() { return Err(UtxoLookupError::UnknownTx); }
49                 Ok(transaction.output.swap_remove(output_index as usize))
50         }
51
52         async fn retrieve_block(client: Arc<RestClient>, block_height: u32) -> Result<Block, UtxoLookupError> {
53                 let uri = format!("blockhashbyheight/{}.bin", block_height);
54                 let block_hash_result =
55                         client.request_resource::<BinaryResponse, RestBinaryResponse>(&uri).await;
56                 let block_hash: Vec<u8> = block_hash_result.map_err(|error| {
57                         eprintln!("Could't find block hash at height {}: {}", block_height, error.to_string());
58                         UtxoLookupError::UnknownChain
59                 })?.0;
60                 let block_hash = BlockHash::from_slice(&block_hash).unwrap();
61
62                 let block_result = client.get_block(&block_hash).await;
63                 match block_result {
64                         Ok(BlockData::FullBlock(block)) => {
65                                 Ok(block)
66                         },
67                         Ok(_) => unreachable!(),
68                         Err(error) => {
69                                 eprintln!("Couldn't retrieve block {}: {:?} ({})", block_height, error, block_hash);
70                                 Err(UtxoLookupError::UnknownChain)
71                         }
72                 }
73         }
74 }
75
76 impl<L: Logger + Send + Sync + 'static> UtxoLookup for ChainVerifier<L> {
77         fn get_utxo(&self, _genesis_hash: &BlockHash, short_channel_id: u64) -> UtxoResult {
78                 let res = UtxoFuture::new();
79                 let fut = res.clone();
80                 let graph_ref = Arc::clone(&self.graph);
81                 let client_ref = Arc::clone(&self.rest_client);
82                 let gossip_ref = Arc::clone(&self.outbound_gossiper);
83                 let pm_ref = self.peer_handler.lock().unwrap().clone();
84                 tokio::spawn(async move {
85                         let res = Self::retrieve_utxo(client_ref, short_channel_id).await;
86                         fut.resolve(&*graph_ref, &*gossip_ref, res);
87                         if let Some(pm) = pm_ref { pm.process_events(); }
88                 });
89                 UtxoResult::Async(res)
90         }
91 }
92
93 impl TryInto<RestBinaryResponse> for BinaryResponse {
94         type Error = std::io::Error;
95
96         fn try_into(self) -> Result<RestBinaryResponse, Self::Error> {
97                 Ok(RestBinaryResponse(self.0))
98         }
99 }