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