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