Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions backend-blindbit-v1/src/api_structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,72 @@ where

Network::from_core_arg(&buf).map_err(serde::de::Error::custom)
}

impl InfoResponse {
/// Validates whether the server supports the requested parameters.
///
/// Returns:
/// - `Ok(use_tweaks_endpoint)` - true means use tweaks(), false means use tweak_index()
/// - `Err(String)` if the request cannot be satisfied
pub fn validate_and_resolve_endpoint(
&self,
dust_limit: Amount,
with_cutthrough: bool,
) -> Result<bool, String> {
let has_dust_limit = dust_limit > Amount::ZERO;

match (with_cutthrough, has_dust_limit) {
// Request: cutthrough with dust filtering
(true, true) => {
if self.tweaks_cut_through_with_dust_filter {
Ok(true)
} else if self.tweaks_full_with_dust_filter {
Ok(false)
} else {
Err(
"Server does not support dust filtering; cannot satisfy request with dust_limit"
.to_string(),
)
}
}

// Request: cutthrough without dust filtering
(true, false) => {
// Can use tweaks endpoint without dust limit
if self.tweaks_only
|| self.tweaks_full_basic
|| self.tweaks_cut_through_with_dust_filter
{
Ok(true)
} else {
Err("Server does not support tweaks endpoint".to_string())
}
}

// Request: full index with dust filtering
(false, true) => {
if self.tweaks_full_with_dust_filter {
Ok(false)
} else if self.tweaks_cut_through_with_dust_filter {
Ok(true)
} else {
Err(
"Server does not support dust filtering; cannot satisfy request with dust_limit"
.to_string(),
)
}
}

// Request: full index without dust filtering
(false, false) => {
if self.tweaks_full_basic {
Ok(false)
} else if self.tweaks_cut_through_with_dust_filter || self.tweaks_only {
Ok(true)
} else {
Err("Server does not support any tweak endpoints".to_string())
}
}
}
}
}
75 changes: 48 additions & 27 deletions backend-blindbit-v1/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ use std::{ops::RangeInclusive, pin::Pin};
use anyhow::Result;
use async_trait::async_trait;
use bitcoin::{Amount, absolute::Height};
use futures::{Stream, StreamExt, stream};
use futures::{Stream, StreamExt, future::Either, stream};

use itertools::Either;
use spdk_core::chain::{BlockData, ChainBackend, SpentIndexData, UtxoData};

use crate::BlindbitClient;
Expand Down Expand Up @@ -37,38 +36,60 @@ impl ChainBackend for BlindbitBackend {
with_cutthrough: bool,
) -> Pin<Box<dyn Stream<Item = Result<BlockData>> + Send>> {
let client = self.client.clone();
let client_for_info = self.client.clone();

// convert range to u32 since Height does not implement Step
let range = range.start().to_consensus_u32()..=range.end().to_consensus_u32();

let range = match reverse {
false => Either::Left(range),
true => Either::Right(range.rev()),
false => itertools::Either::Left(range),
true => itertools::Either::Right(range.rev()),
};

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

async move {
let blkheight = Height::from_consensus(n)?;
let tweaks = match with_cutthrough {
true => client.tweaks(blkheight, dust_limit).await?,
false => 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);
let res = stream::once(async move {
client_for_info
.info()
.await?
.validate_and_resolve_endpoint(dust_limit, with_cutthrough)
.map_err(|e| anyhow::anyhow!(e))
})
.flat_map(move |use_tweaks| {
let client = client.clone();
let heights = range.clone();

// Resolve the Result once here rather than re-checking it per block.
let use_tweaks: bool = match use_tweaks {
Ok(v) => v,
Err(e) => return Either::Left(stream::once(async move { Err(e) })),
};

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

async move {
let blkheight = Height::from_consensus(n)?;
let tweaks = match use_tweaks {
true => client.tweaks(blkheight, dust_limit).await?,
false => 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);

Either::Right(inner)
});

Box::pin(res)
}
Expand Down