-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
e79b5d2
commit 156e11e
Showing
9 changed files
with
432 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,90 @@ | ||
use bitcoin::Network; | ||
use lightning::chain::chaininterface::{FeeEstimator, ConfirmationTarget, BroadcasterInterface}; | ||
use lightning_block_sync::BlockSource; | ||
use tokio::runtime::Handle; | ||
|
||
use self::{bitcoind_client::BitcoindClient, remote::{fee_estimator::RemoteFeeEstimator, broadcaster::RemoteBroadcaster, block_source::RemoteBlockSource}}; | ||
use std::sync::Arc; | ||
|
||
pub mod bitcoind_client; | ||
pub mod broadcaster; | ||
pub mod database; | ||
pub mod fee_estimator; | ||
pub mod listener; | ||
pub mod manager; | ||
pub mod remote; | ||
|
||
|
||
pub enum AnyBlockSource { | ||
Local(Arc<BitcoindClient>), | ||
Remote(remote::block_source::RemoteBlockSource) | ||
} | ||
|
||
impl AnyBlockSource { | ||
pub fn new_remote(network: Network, host: String, token: String) -> Self { | ||
AnyBlockSource::Remote(RemoteBlockSource::new(network, host, token)) | ||
} | ||
} | ||
|
||
impl BlockSource for AnyBlockSource { | ||
fn get_header<'a>(&'a self, header_hash: &'a bitcoin::BlockHash, height_hint: Option<u32>) -> lightning_block_sync::AsyncBlockSourceResult<'a, lightning_block_sync::BlockHeaderData> { | ||
match self { | ||
AnyBlockSource::Local(bitcoind_client) => bitcoind_client.get_header(header_hash, height_hint), | ||
AnyBlockSource::Remote(remote) => remote.get_header(header_hash, height_hint) | ||
} | ||
} | ||
|
||
fn get_block<'a>(&'a self, header_hash: &'a bitcoin::BlockHash) -> lightning_block_sync::AsyncBlockSourceResult<'a, bitcoin::Block> { | ||
match self { | ||
AnyBlockSource::Local(bitcoind_client) => bitcoind_client.get_block(header_hash), | ||
AnyBlockSource::Remote(remote) => remote.get_block(header_hash) | ||
} | ||
} | ||
|
||
fn get_best_block<'a>(&'a self) -> lightning_block_sync::AsyncBlockSourceResult<(bitcoin::BlockHash, Option<u32>)> { | ||
match self { | ||
AnyBlockSource::Local(bitcoind_client) => bitcoind_client.get_best_block(), | ||
AnyBlockSource::Remote(remote) => remote.get_best_block() | ||
} | ||
} | ||
} | ||
|
||
pub enum AnyFeeEstimator { | ||
Local(Arc<BitcoindClient>), | ||
Remote(RemoteFeeEstimator) | ||
} | ||
|
||
impl AnyFeeEstimator { | ||
pub fn new_remote(host: String, token: String, handle: Handle) -> Self { | ||
AnyFeeEstimator::Remote(RemoteFeeEstimator::new( host, token, handle)) | ||
} | ||
} | ||
|
||
impl FeeEstimator for AnyFeeEstimator { | ||
fn get_est_sat_per_1000_weight(&self, confirmation_target: ConfirmationTarget) -> u32 { | ||
match self { | ||
AnyFeeEstimator::Local(bitcoind_client) => bitcoind_client.get_est_sat_per_1000_weight(confirmation_target), | ||
AnyFeeEstimator::Remote(remote) => remote.get_est_sat_per_1000_weight(confirmation_target) | ||
} | ||
} | ||
} | ||
|
||
pub enum AnyBroadcaster { | ||
Local(Arc<BitcoindClient>), | ||
Remote(RemoteBroadcaster) | ||
} | ||
|
||
impl AnyBroadcaster { | ||
pub fn new_remote(host: String, token: String, handle: Handle) -> Self { | ||
AnyBroadcaster::Remote(RemoteBroadcaster::new( host, token, handle)) | ||
} | ||
} | ||
|
||
impl BroadcasterInterface for AnyBroadcaster { | ||
fn broadcast_transaction(&self, tx: &bitcoin::Transaction) { | ||
match self { | ||
AnyBroadcaster::Local(bitcoind_client) => bitcoind_client.broadcast_transaction(tx), | ||
AnyBroadcaster::Remote(remote) => remote.broadcast_transaction(tx) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
use lightning::chain::BestBlock; | ||
use lightning_block_sync::{BlockSource, BlockSourceError, BlockHeaderData}; | ||
use bitcoin::{hashes::hex::{ToHex, FromHex}, util::uint::Uint256, consensus::deserialize, BlockHeader, Block, BlockHash, Network}; | ||
use crate::{hex_utils, p2p::router::RemoteSenseiInfo}; | ||
|
||
pub struct RemoteBlockSource { | ||
network: Network, | ||
remote_sensei: RemoteSenseiInfo | ||
} | ||
|
||
impl RemoteBlockSource { | ||
pub fn new(network: Network, host: String, token: String) -> Self { | ||
Self { | ||
network, | ||
remote_sensei: RemoteSenseiInfo { host, token } | ||
} | ||
} | ||
fn get_header_path(&self, header_hash: String) -> String { | ||
format!("{}/v1/ldk/chain/header/{}", self.remote_sensei.host, header_hash) | ||
} | ||
fn get_block_path(&self, header_hash: String) -> String { | ||
format!("{}/v1/ldk/chain/block/{}", self.remote_sensei.host, header_hash) | ||
} | ||
fn get_best_block_hash_path(&self) -> String { | ||
format!("{}/v1/ldk/chain/best-block-hash", self.remote_sensei.host) | ||
} | ||
fn get_best_block_height_path(&self) -> String { | ||
format!("{}/v1/ldk/chain/best-block-height", self.remote_sensei.host) | ||
} | ||
|
||
pub async fn get_best_block_hash(&self) -> Option<BlockHash> { | ||
let client = reqwest::Client::new(); | ||
match client.get(self.get_best_block_hash_path()) | ||
.header("token", self.remote_sensei.token.clone()) | ||
.send() | ||
.await { | ||
Ok(response) => { | ||
match response.bytes().await { | ||
Ok(serialized_hash) => { | ||
Some(deserialize(&serialized_hash).unwrap()) | ||
}, | ||
Err(_) => None | ||
} | ||
}, | ||
Err(_) => None | ||
} | ||
} | ||
|
||
pub async fn get_best_block_height(&self) -> Option<u32> { | ||
let client = reqwest::Client::new(); | ||
match client.get(self.get_best_block_height_path()) | ||
.header("token", self.remote_sensei.token.clone()) | ||
.send() | ||
.await { | ||
Ok(response) => { | ||
match response.text().await { | ||
Ok(height_as_string) => { | ||
Some(height_as_string.parse().unwrap()) | ||
}, | ||
Err(_) => None | ||
} | ||
}, | ||
Err(_) => None | ||
} | ||
} | ||
|
||
pub async fn get_best_block_async(&self) -> BestBlock { | ||
let best_hash = self.get_best_block_hash().await; | ||
let best_height = self.get_best_block_height().await; | ||
if best_hash.is_none() || best_height.is_none() { | ||
BestBlock::from_genesis(self.network) | ||
} else { | ||
BestBlock::new(best_hash.unwrap(), best_height.unwrap()) | ||
} | ||
} | ||
} | ||
|
||
impl BlockSource for RemoteBlockSource { | ||
fn get_header<'a>(&'a self, header_hash: &'a bitcoin::BlockHash, _height_hint: Option<u32>) -> lightning_block_sync::AsyncBlockSourceResult<'a, lightning_block_sync::BlockHeaderData> { | ||
Box::pin(async move { | ||
let client = reqwest::Client::new(); | ||
let res = client.get(self.get_header_path(header_hash.to_hex())) | ||
.header("token", self.remote_sensei.token.clone()) | ||
.send() | ||
.await; | ||
|
||
match res { | ||
Ok(response) => { | ||
match response.text().await { | ||
Ok(header_data_string) => { | ||
let header_parts: Vec<&str> = header_data_string.split(',').collect(); | ||
let header: BlockHeader = | ||
deserialize(&Vec::<u8>::from_hex(header_parts[0]).unwrap()).unwrap(); | ||
let height: u32 = header_parts[1].to_string().parse().unwrap(); | ||
let chainwork: Uint256 = | ||
deserialize(&hex_utils::to_vec(header_parts[2]).unwrap()).unwrap(); | ||
|
||
Ok(BlockHeaderData { | ||
header, | ||
height, | ||
chainwork, | ||
}) | ||
}, | ||
Err(e) => { | ||
Err(BlockSourceError::transient(e)) | ||
} | ||
} | ||
}, | ||
Err(e) => { | ||
Err(BlockSourceError::transient(e)) | ||
} | ||
} | ||
}) | ||
} | ||
|
||
fn get_block<'a>(&'a self, header_hash: &'a bitcoin::BlockHash) -> lightning_block_sync::AsyncBlockSourceResult<'a, bitcoin::Block> { | ||
Box::pin(async move { | ||
let client = reqwest::Client::new(); | ||
let res = client.get(self.get_block_path(header_hash.to_hex())) | ||
.header("token", self.remote_sensei.token.clone()) | ||
.send() | ||
.await; | ||
|
||
match res { | ||
Ok(response) => { | ||
match response.bytes().await { | ||
Ok(serialized_block_data) => { | ||
let block: Block = deserialize(&serialized_block_data).unwrap(); | ||
Ok(block) | ||
}, | ||
Err(e) => { | ||
Err(BlockSourceError::transient(e)) | ||
} | ||
} | ||
}, | ||
Err(e) => { | ||
Err(BlockSourceError::transient(e)) | ||
} | ||
} | ||
}) | ||
} | ||
|
||
fn get_best_block<'a>(&'a self) -> lightning_block_sync::AsyncBlockSourceResult<(bitcoin::BlockHash, Option<u32>)> { | ||
Box::pin(async move { | ||
let best_block = self.get_best_block_async().await; | ||
Ok((best_block.block_hash(), Some(best_block.height()))) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
use lightning::chain::chaininterface::BroadcasterInterface; | ||
use tokio::runtime::Handle; | ||
use lightning::util::ser::Writeable; | ||
use crate::{p2p::router::RemoteSenseiInfo, hex_utils}; | ||
|
||
pub struct RemoteBroadcaster { | ||
remote_sensei: RemoteSenseiInfo, | ||
tokio_handle: Handle, | ||
} | ||
|
||
impl RemoteBroadcaster { | ||
|
||
pub fn new(host: String, token: String, tokio_handle: Handle) -> Self { | ||
Self { | ||
remote_sensei: RemoteSenseiInfo { host, token }, | ||
tokio_handle, | ||
} | ||
} | ||
|
||
fn broadcast_path(&self) -> String { | ||
format!("{}/v1/ldk/chain/broadcast", self.remote_sensei.host) | ||
} | ||
|
||
pub async fn broadcast_transaction_async(&self, tx: &bitcoin::Transaction) { | ||
let client = reqwest::Client::new(); | ||
let _res = client.post(self.broadcast_path()) | ||
.header("token", self.remote_sensei.token.clone()) | ||
.json(&serde_json::json!({ | ||
"tx": hex_utils::hex_str(&tx.encode()) | ||
})) | ||
.send() | ||
.await; | ||
} | ||
} | ||
|
||
impl BroadcasterInterface for RemoteBroadcaster { | ||
fn broadcast_transaction(&self, tx: &bitcoin::Transaction) { | ||
tokio::task::block_in_place(move || { | ||
self.tokio_handle.clone().block_on(async move { | ||
self.broadcast_transaction_async(tx).await | ||
}) | ||
}) | ||
} | ||
} |
Oops, something went wrong.