Skip to content

Commit

Permalink
remote chain
Browse files Browse the repository at this point in the history
  • Loading branch information
johncantrell97 committed Aug 8, 2022
1 parent e79b5d2 commit 156e11e
Show file tree
Hide file tree
Showing 9 changed files with 432 additions and 17 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ name = "senseid"
path = "src/main.rs"

[dependencies]
bitcoin = "0.28.1"
bitcoin = { version = "0.28.1" }
bitcoincore-rpc = "0.15"
futures = "0.3"
chrono = "0.4"
Expand Down
2 changes: 1 addition & 1 deletion senseicore/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ lightning-persister = { version = "0.0.110" }
lightning-background-processor = { version = "0.0.110" }
lightning-rapid-gossip-sync = { version = "0.0.110" }
base64 = "0.13.0"
bitcoin = "0.28"
bitcoin = { version = "0.28.1" }
bitcoin-bech32 = "0.12"
bech32 = "0.8"
futures = "0.3"
Expand Down
84 changes: 84 additions & 0 deletions senseicore/src/chain/mod.rs
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)
}
}
}
149 changes: 149 additions & 0 deletions senseicore/src/chain/remote/block_source.rs
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())))
})
}
}
44 changes: 44 additions & 0 deletions senseicore/src/chain/remote/broadcaster.rs
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
})
})
}
}
Loading

0 comments on commit 156e11e

Please sign in to comment.