X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=src%2Fverifier.rs;h=c44cb663158e19d7e1c850b22bd57f6f952665c1;hb=81c27c87b10f2e097eef99e0766d1bcef2ebef60;hp=2446e1a938d426bce0f31d0867dea6b9139cf5a5;hpb=6f44a3ae6ebcd5856560ebcc3bae5122b46c0426;p=rapid-gossip-sync-server diff --git a/src/verifier.rs b/src/verifier.rs index 2446e1a..c44cb66 100644 --- a/src/verifier.rs +++ b/src/verifier.rs @@ -1,68 +1,112 @@ -use std::convert::TryInto; +use std::io::ErrorKind; +use std::ops::Deref; use std::sync::Arc; +use std::sync::Mutex; +use bitcoin::blockdata::constants::ChainHash; use bitcoin::{BlockHash, TxOut}; use bitcoin::blockdata::block::Block; use bitcoin::hashes::Hash; -use futures::executor; -use lightning::chain; -use lightning::chain::AccessError; -use lightning_block_sync::BlockSource; +use lightning::log_error; +use lightning::routing::gossip::{NetworkGraph, P2PGossipSync}; +use lightning::routing::utxo::{UtxoFuture, UtxoLookup, UtxoResult, UtxoLookupError}; +use lightning::util::logger::Logger; +use lightning_block_sync::{BlockData, BlockSource}; use lightning_block_sync::http::BinaryResponse; use lightning_block_sync::rest::RestClient; use crate::config; +use crate::types::GossipPeerManager; -pub(crate) struct ChainVerifier { +pub(crate) struct ChainVerifier where L::Target: Logger { rest_client: Arc, + graph: Arc>, + outbound_gossiper: Arc>, Arc, L>>, + peer_handler: Mutex>>, + logger: L } struct RestBinaryResponse(Vec); -impl ChainVerifier { - pub(crate) fn new() -> Self { - let rest_client = RestClient::new(config::bitcoin_rest_endpoint()).unwrap(); +impl ChainVerifier where L::Target: Logger { + pub(crate) fn new(graph: Arc>, outbound_gossiper: Arc>, Arc, L>>, logger: L) -> Self { ChainVerifier { - rest_client: Arc::new(rest_client), + rest_client: Arc::new(RestClient::new(config::bitcoin_rest_endpoint()).unwrap()), + outbound_gossiper, + graph, + peer_handler: Mutex::new(None), + logger } } - - fn retrieve_block(&self, block_height: u32) -> Result { - let rest_client = self.rest_client.clone(); - executor::block_on(async move { - let block_hash_result = rest_client.request_resource::(&format!("blockhashbyheight/{}.bin", block_height)).await; - let block_hash: Vec = block_hash_result.map_err(|error| { - eprintln!("Could't find block hash at height {}: {}", block_height, error.to_string()); - AccessError::UnknownChain - })?.0; - let block_hash = BlockHash::from_slice(&block_hash).unwrap(); - - let block_result = rest_client.get_block(&block_hash).await; - let block = block_result.map_err(|error| { - eprintln!("Couldn't retrieve block {}: {:?} ({})", block_height, error, block_hash); - AccessError::UnknownChain - })?; - Ok(block) - }) + pub(crate) fn set_ph(&self, peer_handler: GossipPeerManager) { + *self.peer_handler.lock().unwrap() = Some(peer_handler); } -} -impl chain::Access for ChainVerifier { - fn get_utxo(&self, _genesis_hash: &BlockHash, short_channel_id: u64) -> Result { + async fn retrieve_utxo(client: Arc, short_channel_id: u64, logger: L) -> Result { let block_height = (short_channel_id >> 5 * 8) as u32; // block height is most significant three bytes let transaction_index = ((short_channel_id >> 2 * 8) & 0xffffff) as u32; let output_index = (short_channel_id & 0xffff) as u16; - let block = self.retrieve_block(block_height)?; - let transaction = block.txdata.get(transaction_index as usize).ok_or_else(|| { - eprintln!("Transaction index {} out of bounds in block {} ({})", transaction_index, block_height, block.block_hash().to_string()); - AccessError::UnknownTx - })?; - let output = transaction.output.get(output_index as usize).ok_or_else(|| { - eprintln!("Output index {} out of bounds in transaction {}", output_index, transaction.txid().to_string()); - AccessError::UnknownTx - })?; - Ok(output.clone()) + let mut block = Self::retrieve_block(client, block_height, logger.clone()).await?; + if transaction_index as usize >= block.txdata.len() { + log_error!(logger, "Could't find transaction {} in block {}", transaction_index, block_height); + return Err(UtxoLookupError::UnknownTx); + } + let mut transaction = block.txdata.swap_remove(transaction_index as usize); + if output_index as usize >= transaction.output.len() { + log_error!(logger, "Could't find output {} in transaction {}", output_index, transaction.txid()); + return Err(UtxoLookupError::UnknownTx); + } + Ok(transaction.output.swap_remove(output_index as usize)) + } + + async fn retrieve_block(client: Arc, block_height: u32, logger: L) -> Result { + let uri = format!("blockhashbyheight/{}.bin", block_height); + let block_hash_result = + client.request_resource::(&uri).await; + let block_hash: Vec = block_hash_result.map_err(|error| { + match error.kind() { + ErrorKind::InvalidData => { + // the response length was likely 0 + log_error!(logger, "Could't find block hash at height {}: Invalid response! Please make sure the `-rest=1` flag is set.", block_height); + } + _ => { + log_error!(logger, "Could't find block hash at height {}: {}", block_height, error.to_string()); + } + } + UtxoLookupError::UnknownChain + })?.0; + let block_hash = BlockHash::from_slice(&block_hash).unwrap(); + + let block_result = client.get_block(&block_hash).await; + match block_result { + Ok(BlockData::FullBlock(block)) => { + Ok(block) + }, + Ok(_) => unreachable!(), + Err(error) => { + log_error!(logger, "Couldn't retrieve block {}: {:?} ({})", block_height, error, block_hash); + Err(UtxoLookupError::UnknownChain) + } + } + } +} + +impl UtxoLookup for ChainVerifier where L::Target: Logger { + fn get_utxo(&self, _genesis_hash: &ChainHash, short_channel_id: u64) -> UtxoResult { + let res = UtxoFuture::new(); + let fut = res.clone(); + let graph_ref = Arc::clone(&self.graph); + let client_ref = Arc::clone(&self.rest_client); + let gossip_ref = Arc::clone(&self.outbound_gossiper); + let pm_ref = self.peer_handler.lock().unwrap().clone(); + let logger_ref = self.logger.clone(); + tokio::spawn(async move { + let res = Self::retrieve_utxo(client_ref, short_channel_id, logger_ref).await; + fut.resolve(&*graph_ref, &*gossip_ref, res); + if let Some(pm) = pm_ref { pm.process_events(); } + }); + UtxoResult::Async(res) } }