62c191a3ce2727d9930e06b0ca58425408753dfe
[rapid-gossip-sync-server] / src / verifier.rs
1 use std::convert::TryInto;
2
3 use bitcoin::{BlockHash, TxOut};
4 use bitcoin::blockdata::block::Block;
5 use bitcoin::hashes::Hash;
6 use lightning::chain;
7 use lightning::chain::AccessError;
8 use lightning_block_sync::{BlockData, BlockSource};
9 use lightning_block_sync::http::BinaryResponse;
10 use lightning_block_sync::rest::RestClient;
11
12 use crate::config;
13
14 pub(crate) struct ChainVerifier {
15         rest_client: RestClient,
16 }
17
18 struct RestBinaryResponse(Vec<u8>);
19
20 impl ChainVerifier {
21         pub(crate) fn new() -> Self {
22                 ChainVerifier {
23                         rest_client: RestClient::new(config::bitcoin_rest_endpoint()).unwrap(),
24                 }
25         }
26
27         fn retrieve_block(&self, block_height: u32) -> Result<Block, AccessError> {
28                 tokio::task::block_in_place(move || { tokio::runtime::Handle::current().block_on(async move {
29                         let uri = format!("blockhashbyheight/{}.bin", block_height);
30                         let block_hash_result =
31                                 self.rest_client.request_resource::<BinaryResponse, RestBinaryResponse>(&uri).await;
32                         let block_hash: Vec<u8> = block_hash_result.map_err(|error| {
33                                 eprintln!("Could't find block hash at height {}: {}", block_height, error.to_string());
34                                 AccessError::UnknownChain
35                         })?.0;
36                         let block_hash = BlockHash::from_slice(&block_hash).unwrap();
37
38                         let block_result = self.rest_client.get_block(&block_hash).await;
39                         match block_result {
40                                 Ok(BlockData::FullBlock(block)) => {
41                                         Ok(block)
42                                 },
43                                 Ok(_) => unreachable!(),
44                                 Err(error) => {
45                                         eprintln!("Couldn't retrieve block {}: {:?} ({})", block_height, error, block_hash);
46                                         Err(AccessError::UnknownChain)
47                                 }
48                         }
49                 }) })
50         }
51 }
52
53 impl chain::Access for ChainVerifier {
54         fn get_utxo(&self, _genesis_hash: &BlockHash, short_channel_id: u64) -> Result<TxOut, AccessError> {
55                 let block_height = (short_channel_id >> 5 * 8) as u32; // block height is most significant three bytes
56                 let transaction_index = ((short_channel_id >> 2 * 8) & 0xffffff) as u32;
57                 let output_index = (short_channel_id & 0xffff) as u16;
58
59                 let block = self.retrieve_block(block_height)?;
60                 let transaction = block.txdata.get(transaction_index as usize).ok_or_else(|| {
61                         eprintln!("Transaction index {} out of bounds in block {} ({})", transaction_index, block_height, block.block_hash().to_string());
62                         AccessError::UnknownTx
63                 })?;
64                 let output = transaction.output.get(output_index as usize).ok_or_else(|| {
65                         eprintln!("Output index {} out of bounds in transaction {}", output_index, transaction.txid().to_string());
66                         AccessError::UnknownTx
67                 })?;
68                 Ok(output.clone())
69         }
70 }
71
72 impl TryInto<RestBinaryResponse> for BinaryResponse {
73         type Error = std::io::Error;
74
75         fn try_into(self) -> Result<RestBinaryResponse, Self::Error> {
76                 Ok(RestBinaryResponse(self.0))
77         }
78 }