From 5f6c99bdba3e9c07ba9f84921347c08bc1d86d33 Mon Sep 17 00:00:00 2001 From: John Cantrell Date: Tue, 5 Dec 2023 10:39:06 -0500 Subject: [PATCH 1/4] use UtxoSource to get blockhashbyheight --- src/verifier.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/verifier.rs b/src/verifier.rs index 6813ff7d..abff551c 100644 --- a/src/verifier.rs +++ b/src/verifier.rs @@ -5,12 +5,12 @@ use std::sync::Mutex; use bitcoin::{BlockHash, TxOut}; use bitcoin::blockdata::block::Block; -use bitcoin::hashes::Hash; 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::gossip::UtxoSource; use lightning_block_sync::http::BinaryResponse; use lightning_block_sync::rest::RestClient; @@ -60,14 +60,10 @@ impl ChainVerifier where L::Target: } 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| { - log_error!(logger, "Could't find block hash at height {}: {}", block_height, error.to_string()); + let block_hash = client.get_block_hash_by_height(block_height).await.map_err(|error| { + log_error!(logger, "Could't find block hash at height {}: {:?}", block_height, error); UtxoLookupError::UnknownChain - })?.0; - let block_hash = BlockHash::from_slice(&block_hash).unwrap(); + })?; let block_result = client.get_block(&block_hash).await; match block_result { From 56a748df104704f08ef1cda18267cb0726f06ff5 Mon Sep 17 00:00:00 2001 From: John Cantrell Date: Tue, 5 Dec 2023 11:11:54 -0500 Subject: [PATCH 2/4] introduce AnyChainSource for ChainVerifier --- src/verifier.rs | 56 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 8 deletions(-) diff --git a/src/verifier.rs b/src/verifier.rs index abff551c..e89b6eb4 100644 --- a/src/verifier.rs +++ b/src/verifier.rs @@ -17,8 +17,46 @@ use lightning_block_sync::rest::RestClient; use crate::config; use crate::types::GossipPeerManager; +enum AnyChainSource { + Rest(RestClient) +} + +impl BlockSource for AnyChainSource { + fn get_header<'a>(&'a self, header_hash: &'a BlockHash, height_hint: Option) -> lightning_block_sync::AsyncBlockSourceResult<'a, lightning_block_sync::BlockHeaderData> { + match self { + AnyChainSource::Rest(client) => client.get_header(header_hash, height_hint) + } + } + + fn get_block<'a>(&'a self, header_hash: &'a BlockHash) -> lightning_block_sync::AsyncBlockSourceResult<'a, BlockData> { + match self { + AnyChainSource::Rest(client) => client.get_block(header_hash) + } + } + + fn get_best_block<'a>(&'a self) -> lightning_block_sync::AsyncBlockSourceResult<(BlockHash, Option)> { + match self { + AnyChainSource::Rest(client) => client.get_best_block() + } + } +} + +impl UtxoSource for AnyChainSource { + fn get_block_hash_by_height<'a>(&'a self, block_height: u32) -> lightning_block_sync::AsyncBlockSourceResult<'a, BlockHash> { + match self { + AnyChainSource::Rest(client) => client.get_block_hash_by_height(block_height) + } + } + + fn is_output_unspent<'a>(&'a self, outpoint: bitcoin::OutPoint) -> lightning_block_sync::AsyncBlockSourceResult<'a, bool> { + match self { + AnyChainSource::Rest(client) => client.is_output_unspent(outpoint) + } + } +} + pub(crate) struct ChainVerifier where L::Target: Logger { - rest_client: Arc, + chain_source: Arc, graph: Arc>, outbound_gossiper: Arc>, Arc, L>>, peer_handler: Mutex>>, @@ -29,8 +67,10 @@ struct RestBinaryResponse(Vec); impl ChainVerifier where L::Target: Logger { pub(crate) fn new(graph: Arc>, outbound_gossiper: Arc>, Arc, L>>, logger: L) -> Self { + let chain_source = Arc::new(AnyChainSource::Rest(RestClient::new(config::bitcoin_rest_endpoint()).unwrap())); + ChainVerifier { - rest_client: Arc::new(RestClient::new(config::bitcoin_rest_endpoint()).unwrap()), + chain_source, outbound_gossiper, graph, peer_handler: Mutex::new(None), @@ -41,12 +81,12 @@ impl ChainVerifier where L::Target: *self.peer_handler.lock().unwrap() = Some(peer_handler); } - async fn retrieve_utxo(client: Arc, short_channel_id: u64, logger: L) -> Result { + async fn retrieve_utxo(chain_source: 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 mut block = Self::retrieve_block(client, block_height, logger.clone()).await?; + let mut block = Self::retrieve_block(chain_source, 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); @@ -59,13 +99,13 @@ impl ChainVerifier where L::Target: Ok(transaction.output.swap_remove(output_index as usize)) } - async fn retrieve_block(client: Arc, block_height: u32, logger: L) -> Result { - let block_hash = client.get_block_hash_by_height(block_height).await.map_err(|error| { + async fn retrieve_block(chain_source: Arc, block_height: u32, logger: L) -> Result { + let block_hash = chain_source.get_block_hash_by_height(block_height).await.map_err(|error| { log_error!(logger, "Could't find block hash at height {}: {:?}", block_height, error); UtxoLookupError::UnknownChain })?; - let block_result = client.get_block(&block_hash).await; + let block_result = chain_source.get_block(&block_hash).await; match block_result { Ok(BlockData::FullBlock(block)) => { Ok(block) @@ -84,7 +124,7 @@ impl UtxoLookup for ChainVerifier w let res = UtxoFuture::new(); let fut = res.clone(); let graph_ref = Arc::clone(&self.graph); - let client_ref = Arc::clone(&self.rest_client); + let client_ref = Arc::clone(&self.chain_source); let gossip_ref = Arc::clone(&self.outbound_gossiper); let pm_ref = self.peer_handler.lock().unwrap().clone(); let logger_ref = self.logger.clone(); From 17da55347e40678984287a5ae580445bb3c1bf2b Mon Sep 17 00:00:00 2001 From: John Cantrell Date: Tue, 5 Dec 2023 11:42:38 -0500 Subject: [PATCH 3/4] add support for bitcoind json-rpc api --- Cargo.toml | 2 +- src/config.rs | 20 ++++++++++++++++++++ src/verifier.rs | 28 +++++++++++++++++++++------- 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f6d05157..9baf7db1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] bitcoin = "0.29" lightning = { version = "0.0.117" } -lightning-block-sync = { version = "0.0.117", features=["rest-client"] } +lightning-block-sync = { version = "0.0.117", features=["rest-client", "rpc-client"] } lightning-net-tokio = { version = "0.0.117" } tokio = { version = "1.25", features = ["full"] } tokio-postgres = { version = "=0.7.5" } diff --git a/src/config.rs b/src/config.rs index 026e09e2..211b8064 100644 --- a/src/config.rs +++ b/src/config.rs @@ -102,6 +102,26 @@ pub(crate) fn bitcoin_rest_endpoint() -> HttpEndpoint { HttpEndpoint::for_host(host).with_port(port).with_path(path) } +pub(crate) fn bitcoin_rpc_endpoint() -> HttpEndpoint { + let host = env::var("BITCOIN_RPC_DOMAIN").unwrap_or("127.0.0.1".to_string()); + let port = env::var("BITCOIN_RPC_PORT") + .unwrap_or("8332".to_string()) + .parse::() + .expect("BITCOIN_RPC_PORT env variable must be a u16."); + let path = env::var("BITCOIN_RPC_PATH").unwrap_or("/".to_string()); + HttpEndpoint::for_host(host).with_port(port).with_path(path) +} + +pub(crate) fn bitcoin_rpc_credentials() -> String { + let user = env::var("BITCOIN_RPC_USER").unwrap_or("user".to_string()); + let password = env::var("BITCOIN_RPC_PASSWORD").unwrap_or("password".to_string()); + format!("{}:{}", user, password) +} + +pub(crate) fn use_bitcoin_rpc_api() -> bool { + env::var("USE_BITCOIN_RPC_API").unwrap_or("false".to_string()) == "true" +} + pub(crate) fn db_config_table_creation_query() -> &'static str { "CREATE TABLE IF NOT EXISTS config ( id SERIAL PRIMARY KEY, diff --git a/src/verifier.rs b/src/verifier.rs index e89b6eb4..b28c44b4 100644 --- a/src/verifier.rs +++ b/src/verifier.rs @@ -13,30 +13,35 @@ use lightning_block_sync::{BlockData, BlockSource}; use lightning_block_sync::gossip::UtxoSource; use lightning_block_sync::http::BinaryResponse; use lightning_block_sync::rest::RestClient; +use lightning_block_sync::rpc::RpcClient; use crate::config; use crate::types::GossipPeerManager; enum AnyChainSource { - Rest(RestClient) + Rest(RestClient), + Rpc(RpcClient), } impl BlockSource for AnyChainSource { fn get_header<'a>(&'a self, header_hash: &'a BlockHash, height_hint: Option) -> lightning_block_sync::AsyncBlockSourceResult<'a, lightning_block_sync::BlockHeaderData> { match self { - AnyChainSource::Rest(client) => client.get_header(header_hash, height_hint) + AnyChainSource::Rest(client) => client.get_header(header_hash, height_hint), + AnyChainSource::Rpc(client) => client.get_header(header_hash, height_hint), } } fn get_block<'a>(&'a self, header_hash: &'a BlockHash) -> lightning_block_sync::AsyncBlockSourceResult<'a, BlockData> { match self { - AnyChainSource::Rest(client) => client.get_block(header_hash) + AnyChainSource::Rest(client) => client.get_block(header_hash), + AnyChainSource::Rpc(client) => client.get_block(header_hash), } } fn get_best_block<'a>(&'a self) -> lightning_block_sync::AsyncBlockSourceResult<(BlockHash, Option)> { match self { - AnyChainSource::Rest(client) => client.get_best_block() + AnyChainSource::Rest(client) => client.get_best_block(), + AnyChainSource::Rpc(client) => client.get_best_block(), } } } @@ -44,13 +49,15 @@ impl BlockSource for AnyChainSource { impl UtxoSource for AnyChainSource { fn get_block_hash_by_height<'a>(&'a self, block_height: u32) -> lightning_block_sync::AsyncBlockSourceResult<'a, BlockHash> { match self { - AnyChainSource::Rest(client) => client.get_block_hash_by_height(block_height) + AnyChainSource::Rest(client) => client.get_block_hash_by_height(block_height), + AnyChainSource::Rpc(client) => client.get_block_hash_by_height(block_height), } } fn is_output_unspent<'a>(&'a self, outpoint: bitcoin::OutPoint) -> lightning_block_sync::AsyncBlockSourceResult<'a, bool> { match self { - AnyChainSource::Rest(client) => client.is_output_unspent(outpoint) + AnyChainSource::Rest(client) => client.is_output_unspent(outpoint), + AnyChainSource::Rpc(client) => client.is_output_unspent(outpoint), } } } @@ -67,7 +74,14 @@ struct RestBinaryResponse(Vec); impl ChainVerifier where L::Target: Logger { pub(crate) fn new(graph: Arc>, outbound_gossiper: Arc>, Arc, L>>, logger: L) -> Self { - let chain_source = Arc::new(AnyChainSource::Rest(RestClient::new(config::bitcoin_rest_endpoint()).unwrap())); + + let chain_source = { + if config::use_bitcoin_rpc_api() { + Arc::new(AnyChainSource::Rpc(RpcClient::new(&config::bitcoin_rpc_credentials(), config::bitcoin_rpc_endpoint()).unwrap())) + } else { + Arc::new(AnyChainSource::Rest(RestClient::new(config::bitcoin_rest_endpoint()).unwrap())) + } + }; ChainVerifier { chain_source, From a0c43ca632f361fd71eeb264210780f6866faf66 Mon Sep 17 00:00:00 2001 From: John Cantrell Date: Tue, 5 Dec 2023 11:54:30 -0500 Subject: [PATCH 4/4] update readme with new vars --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 12104909..924d5dde 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,10 @@ can be made by setting environment variables, whose usage is as follows: | BITCOIN_REST_DOMAIN | 127.0.0.1 | Domain of the [bitcoind REST server](https://github.com/bitcoin/bitcoin/blob/master/doc/REST-interface.md) | | BITCOIN_REST_PORT | 8332 | HTTP port of the bitcoind REST server | | BITCOIN_REST_PATH | /rest/ | Path infix to access the bitcoind REST endpoints | +| USE_BITCOIN_RPC_API | false | Use JSON-RPC api instead of REST | +| BITCOIN_RPC_DOMAIN | 127.0.0.1 | Domain of the [bitcoind JSON-RPC server](https://github.com/bitcoin/bitcoin/blob/master/doc/JSON-RPC-interface.md) | +| BITCOIN_RPC_PORT | 8332 | port of the bitcoind JSON-RPC server | +| BITCOIN_RPC_PATH | / | Path infix to access the bitcoind JSON-RPC endpoints | | LN_PEERS | _Wallet of Satoshi_ | Comma separated list of LN peers to use for retrieving gossip | ### downloader