Skip to content

Commit

Permalink
Move blindbit backend to sp-client behind feature
Browse files Browse the repository at this point in the history
  • Loading branch information
cygnet3 committed Sep 30, 2024
1 parent 621d075 commit 09a70e4
Show file tree
Hide file tree
Showing 12 changed files with 292 additions and 8 deletions.
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,8 @@ rayon = "1.10.0"
futures = "0.3"
log = "0.4"
async-trait = "0.1"
reqwest = { version = "0.12.4", features = ["rustls-tls"], default-features = false, optional = true }
hex = { version = "0.4.3", features = ["serde"], optional = true }

[features]
blindbit-backend = ["reqwest", "hex"]
81 changes: 81 additions & 0 deletions src/backend/blindbit/backend/backend.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
use std::{ops::RangeInclusive, pin::Pin, sync::Arc};

use async_trait::async_trait;
use bitcoin::{absolute::Height, Amount};
use futures::{stream, Stream, StreamExt};
use reqwest::Url;

use anyhow::Result;

use crate::{backend::blindbit::BlindbitClient, BlockData, ChainBackend, SpentIndexData, UtxoData};

const CONCURRENT_FILTER_REQUESTS: usize = 200;

pub struct BlindbitBackend {
client: BlindbitClient,
}

impl BlindbitBackend {
pub fn new(blindbit_url: String) -> Result<Self> {
let host_url = Url::parse(&blindbit_url)?;
Ok(Self {
client: BlindbitClient::new(host_url),
})
}
}

#[async_trait]
impl ChainBackend for BlindbitBackend {
/// High-level function to get block data for a range of blocks.
/// Block data includes all the information needed to determine if a block is relevant for scanning,
/// but does not include utxos, or spent index.
/// These need to be fetched separately afterwards, if it is determined this block is relevant.
fn get_block_data_for_range(
&self,
range: RangeInclusive<u32>,
dust_limit: Amount,
) -> Pin<Box<dyn Stream<Item = Result<BlockData>> + Send>> {
let client = Arc::new(self.client.clone());

let res = stream::iter(range)
.map(move |n| {
let client = client.clone();

async move {
let blkheight = Height::from_consensus(n)?;
let tweaks = client.tweak_index(blkheight, dust_limit).await?;
let new_utxo_filter = client.filter_new_utxos(blkheight).await?;
let spent_filter = client.filter_spent(blkheight).await?;
let blkhash = new_utxo_filter.block_hash;
Ok(BlockData {
blkheight,
blkhash,
tweaks,
new_utxo_filter: new_utxo_filter.into(),
spent_filter: spent_filter.into(),
})
}
})
.buffered(CONCURRENT_FILTER_REQUESTS);

Box::pin(res)
}

async fn spent_index(&self, block_height: Height) -> Result<SpentIndexData> {
self.client.spent_index(block_height).await.map(Into::into)
}

async fn utxos(&self, block_height: Height) -> Result<Vec<UtxoData>> {
Ok(self
.client
.utxos(block_height)
.await?
.into_iter()
.map(Into::into)
.collect())
}

async fn block_height(&self) -> Result<Height> {
self.client.block_height().await
}
}
3 changes: 3 additions & 0 deletions src/backend/blindbit/backend/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod backend;

pub use backend::BlindbitBackend;
106 changes: 106 additions & 0 deletions src/backend/blindbit/client/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
use std::time::Duration;

use bitcoin::{absolute::Height, secp256k1::PublicKey, Amount};
use reqwest::{Client, Url};

use anyhow::Result;

use super::structs::{BlockHeightResponse, FilterResponse, SpentIndexResponse, UtxoResponse};

#[derive(Clone)]
pub struct BlindbitClient {
client: Client,
host_url: Url,
}

impl BlindbitClient {
pub fn new(mut host_url: Url) -> Self {
let client = reqwest::Client::new();

// we need a trailing slash, if not present we append it
if !host_url.path().ends_with('/') {
host_url.set_path(&format!("{}/", host_url.path()));
}

BlindbitClient { client, host_url }
}
pub async fn block_height(&self) -> Result<Height> {
let url = self.host_url.join("block-height")?;

let res = self
.client
.get(url)
.timeout(Duration::from_secs(5))
.send()
.await?;
let blkheight: BlockHeightResponse = serde_json::from_str(&res.text().await?)?;
Ok(blkheight.block_height)
}

#[allow(dead_code)]
pub async fn tweaks(&self, block_height: Height) -> Result<Vec<PublicKey>> {
let url = self.host_url.join(&format!("tweaks/{}", block_height))?;

let res = self.client.get(url).send().await?;
Ok(serde_json::from_str(&res.text().await?)?)
}

pub async fn tweak_index(
&self,
block_height: Height,
dust_limit: Amount,
) -> Result<Vec<PublicKey>> {
let url = self
.host_url
.join(&format!("tweak-index/{}", block_height))?;

let res = self
.client
.get(url)
.query(&[("dustLimit", format!("{}", dust_limit.to_sat()))])
.send()
.await?;
Ok(serde_json::from_str(&res.text().await?)?)
}

pub async fn utxos(&self, block_height: Height) -> Result<Vec<UtxoResponse>> {
let url = self.host_url.join(&format!("utxos/{}", block_height))?;
let res = self.client.get(url).send().await?;

Ok(serde_json::from_str(&res.text().await?)?)
}

pub async fn spent_index(&self, block_height: Height) -> Result<SpentIndexResponse> {
let url = self
.host_url
.join(&format!("spent-index/{}", block_height))?;
let res = self.client.get(url).send().await?;

Ok(serde_json::from_str(&res.text().await?)?)
}

pub async fn filter_new_utxos(&self, block_height: Height) -> Result<FilterResponse> {
let url = self
.host_url
.join(&format!("filter/new-utxos/{}", block_height))?;

let res = self.client.get(url).send().await?;

Ok(serde_json::from_str(&res.text().await?)?)
}

pub async fn filter_spent(&self, block_height: Height) -> Result<FilterResponse> {
let url = self
.host_url
.join(&format!("filter/spent/{}", block_height))?;

let res = self.client.get(url).send().await?;

Ok(serde_json::from_str(&res.text().await?)?)
}

#[allow(dead_code)]
pub async fn forward_tx(&self) {
// not needed
}
}
4 changes: 4 additions & 0 deletions src/backend/blindbit/client/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
mod client;
pub mod structs;

pub use client::BlindbitClient;
71 changes: 71 additions & 0 deletions src/backend/blindbit/client/structs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use bitcoin::{absolute::Height, Amount, BlockHash, ScriptBuf, Txid};
use serde::Deserialize;

use crate::{FilterData, SpentIndexData, UtxoData};

#[derive(Debug, Deserialize)]
pub struct BlockHeightResponse {
pub block_height: Height,
}

#[derive(Debug, Deserialize)]
pub struct UtxoResponse {
pub txid: Txid,
pub vout: u32,
pub value: Amount,
pub scriptpubkey: ScriptBuf,
pub block_height: Height,
pub block_hash: BlockHash,
pub timestamp: i32,
pub spent: bool,
}

impl From<UtxoResponse> for UtxoData {
fn from(value: UtxoResponse) -> Self {
Self {
txid: value.txid,
vout: value.vout,
value: value.value,
scriptpubkey: value.scriptpubkey,
spent: value.spent,
}
}
}

#[derive(Debug, Deserialize)]
pub struct SpentIndexResponse {
pub block_hash: BlockHash,
pub data: Vec<MyHex>,
}

impl From<SpentIndexResponse> for SpentIndexData {
fn from(value: SpentIndexResponse) -> Self {
Self {
data: value.data.into_iter().map(|x| x.hex).collect(),
}
}
}

#[derive(Deserialize, Debug)]
#[serde(transparent)]
pub struct MyHex {
#[serde(with = "hex::serde")]
pub hex: Vec<u8>,
}

#[derive(Debug, Deserialize)]
pub struct FilterResponse {
pub block_hash: BlockHash,
pub block_height: Height,
pub data: MyHex,
pub filter_type: i32,
}

impl From<FilterResponse> for FilterData {
fn from(value: FilterResponse) -> Self {
Self {
block_hash: value.block_hash,
data: value.data.hex,
}
}
}
5 changes: 5 additions & 0 deletions src/backend/blindbit/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mod backend;
mod client;

pub use backend::BlindbitBackend;
pub use client::BlindbitClient;
5 changes: 5 additions & 0 deletions src/backend/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
mod backend;
#[cfg(feature = "blindbit-backend")]
mod blindbit;
mod structs;

pub use backend::ChainBackend;
pub use structs::*;

#[cfg(feature = "blindbit-backend")]
pub use blindbit::BlindbitBackend;
8 changes: 6 additions & 2 deletions src/client/structs.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
use anyhow::Error;
use bitcoin::{absolute::Height, key::Secp256k1, secp256k1::{PublicKey, SecretKey}, Amount, ScriptBuf};
use bitcoin::{
absolute::Height,
key::Secp256k1,
secp256k1::{PublicKey, SecretKey},
Amount, ScriptBuf,
};
use serde::{Deserialize, Serialize};

type SpendingTxId = String;
Expand Down Expand Up @@ -55,4 +60,3 @@ impl Into<PublicKey> for SpendKey {
}
}
}

8 changes: 4 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
pub mod constants;
mod backend;
mod client;
pub mod constants;
mod scanner;
mod backend;
mod updater;

pub use bitcoin;
pub use silentpayments;

pub use backend::ChainBackend;
pub use backend::*;
pub use client::*;
pub use scanner::SpScanner;
pub use updater::Updater;
pub use client::SpClient;
3 changes: 2 additions & 1 deletion src/scanner/scanner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ use silentpayments::receiving::Label;

use crate::{
backend::{BlockData, ChainBackend, FilterData, UtxoData},
client::{OutputSpendStatus, OwnedOutput, SpClient}, updater::Updater,
client::{OutputSpendStatus, OwnedOutput, SpClient},
updater::Updater,
};

pub struct SpScanner {
Expand Down
1 change: 0 additions & 1 deletion src/updater/updater.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use anyhow::Result;

use crate::client::OwnedOutput;


pub trait Updater {
fn update_last_scan(&mut self, height: Height);

Expand Down

0 comments on commit 09a70e4

Please sign in to comment.