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