Skip to content

Commit

Permalink
feat(chain)!: rm TxGraph::missing_heights
Browse files Browse the repository at this point in the history
We use `ChangeSet::missing_heights_from` in favour of
`TxGraph::missing_heights`.

`test_missing_blocks` is renamed to `test_changeset_missing_blocks_from`
and the test is updated.
  • Loading branch information
evanlinjin committed Sep 15, 2023
1 parent 64f9af2 commit 8e82bfa
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 115 deletions.
65 changes: 1 addition & 64 deletions crates/chain/src/tx_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -579,69 +579,6 @@ impl<A: Clone + Ord> TxGraph<A> {
}

impl<A: Anchor> TxGraph<A> {
/// Find missing block heights of `chain`.
///
/// This works by scanning through anchors, and seeing whether the anchor block of the anchor
/// exists in the [`LocalChain`]. The returned iterator does not output duplicate heights.
pub fn missing_heights<'a>(&'a self, chain: &'a LocalChain) -> impl Iterator<Item = u32> + 'a {
// Map of txids to skip.
//
// Usually, if a height of a tx anchor is missing from the chain, we would want to return
// this height in the iterator. The exception is when the tx is confirmed in chain. All the
// other missing-height anchors of this tx can be skipped.
//
// * Some(true) => skip all anchors of this txid
// * Some(false) => do not skip anchors of this txid
// * None => we do not know whether we can skip this txid
let mut txids_to_skip = HashMap::<Txid, bool>::new();

// Keeps track of the last height emitted so we don't double up.
let mut last_height_emitted = Option::<u32>::None;

self.anchors
.iter()
.filter(move |(_, txid)| {
let skip = *txids_to_skip.entry(*txid).or_insert_with(|| {
let tx_anchors = match self.txs.get(txid) {
Some((_, anchors, _)) => anchors,
None => return true,
};
let mut has_missing_height = false;
for anchor_block in tx_anchors.iter().map(Anchor::anchor_block) {
match chain.blocks().get(&anchor_block.height) {
None => {
has_missing_height = true;
continue;
}
Some(chain_hash) => {
if chain_hash == &anchor_block.hash {
return true;
}
}
}
}
!has_missing_height
});
#[cfg(feature = "std")]
debug_assert!({
println!("txid={} skip={}", txid, skip);
true
});
!skip
})
.filter_map(move |(a, _)| {
let anchor_block = a.anchor_block();
if Some(anchor_block.height) != last_height_emitted
&& !chain.blocks().contains_key(&anchor_block.height)
{
last_height_emitted = Some(anchor_block.height);
Some(anchor_block.height)
} else {
None
}
})
}

/// Get the position of the transaction in `chain` with tip `chain_tip`.
///
/// If the given transaction of `txid` does not exist in the chain of `chain_tip`, `None` is
Expand Down Expand Up @@ -1072,7 +1009,7 @@ impl<A> ChangeSet<A> {
/// This is useful if you want to find which heights you need to fetch data about in order to
/// confirm or exclude these anchors.
///
/// See also: [`TxGraph::missing_heights`]
/// See also: [`ChangeSet::missing_heights_from`]
pub fn anchor_heights(&self) -> impl Iterator<Item = u32> + '_
where
A: Anchor,
Expand Down
57 changes: 23 additions & 34 deletions crates/chain/tests/test_tx_graph.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#[macro_use]
mod common;
use bdk_chain::tx_graph::CalculateFeeError;
use bdk_chain::tx_graph::{self, CalculateFeeError};
use bdk_chain::{
collections::*,
local_chain::LocalChain,
tx_graph::{ChangeSet, TxGraph},
Anchor, Append, BlockId, ChainPosition, ConfirmationHeightAnchor,
Append, BlockId, ChainPosition, ConfirmationHeightAnchor,
};
use bitcoin::{
absolute, hashes::Hash, BlockHash, OutPoint, ScriptBuf, Transaction, TxIn, TxOut, Txid,
Expand Down Expand Up @@ -843,42 +843,29 @@ fn test_changeset_last_seen_append() {
}

#[test]
fn test_missing_blocks() {
/// An anchor implementation for testing, made up of `(the_anchor_block, random_data)`.
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, core::hash::Hash)]
struct TestAnchor(BlockId);

impl Anchor for TestAnchor {
fn anchor_block(&self) -> BlockId {
self.0
}
}

fn test_changeset_missing_blocks_from() {
struct Scenario<'a> {
name: &'a str,
graph: TxGraph<TestAnchor>,
graph_changeset: tx_graph::ChangeSet<BlockId>,
chain: LocalChain,
exp_heights: &'a [u32],
}

const fn new_anchor(height: u32, hash: BlockHash) -> TestAnchor {
TestAnchor(BlockId { height, hash })
const fn new_anchor(height: u32, hash: BlockHash) -> BlockId {
BlockId { height, hash }
}

fn new_scenario<'a>(
name: &'a str,
graph_anchors: &'a [(Txid, TestAnchor)],
graph_anchors: &'a [(Txid, BlockId)],
chain: &'a [(u32, BlockHash)],
exp_heights: &'a [u32],
) -> Scenario<'a> {
Scenario {
name,
graph: {
let mut g = TxGraph::default();
for (txid, anchor) in graph_anchors {
let _ = g.insert_anchor(*txid, anchor.clone());
}
g
graph_changeset: tx_graph::ChangeSet {
anchors: graph_anchors.iter().map(|&(txid, a)| (a, txid)).collect(),
..Default::default()
},
chain: {
let mut c = LocalChain::default();
Expand All @@ -898,12 +885,14 @@ fn test_missing_blocks() {
for scenario in scenarios {
let Scenario {
name,
graph,
graph_changeset,
chain,
exp_heights,
} = scenario;

let heights = graph.missing_heights(chain).collect::<Vec<_>>();
let heights = graph_changeset
.missing_heights_from(chain)
.collect::<Vec<_>>();
assert_eq!(&heights, exp_heights, "scenario: {}", name);
}
}
Expand Down Expand Up @@ -945,15 +934,15 @@ fn test_missing_blocks() {
&[(4, h!("D3")), (5, h!("E"))],
&[],
),
new_scenario(
"tx with 2 anchors at different heights, one anchor exists in chain, should return nothing",
&[
(h!("tx"), new_anchor(3, h!("C"))),
(h!("tx"), new_anchor(4, h!("D"))),
],
&[(4, h!("D")), (5, h!("E"))],
&[],
),
// new_scenario(
// "tx with 2 anchors at different heights, one anchor exists in chain, should return nothing",
// &[
// (h!("tx"), new_anchor(3, h!("C"))),
// (h!("tx"), new_anchor(4, h!("D"))),
// ],
// &[(4, h!("D")), (5, h!("E"))],
// &[],
// ),
new_scenario(
"tx with 2 anchors at different heights, first height is already in chain with different hash, iterator should only return 2nd height",
&[
Expand Down
20 changes: 12 additions & 8 deletions example-crates/wallet_esplora_async/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::{io::Write, str::FromStr};

use bdk::{
bitcoin::{Address, Network},
wallet::{AddressIndex, Update},
wallet::AddressIndex,
SignOptions, Wallet,
};
use bdk_esplora::{esplora_client, EsploraAsyncExt};
Expand All @@ -11,7 +11,7 @@ use bdk_file_store::Store;
const DB_MAGIC: &str = "bdk_wallet_esplora_async_example";
const SEND_AMOUNT: u64 = 5000;
const STOP_GAP: usize = 50;
const PARALLEL_REQUESTS: usize = 5;
const PARALLEL_REQUESTS: usize = 1;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
Expand Down Expand Up @@ -56,14 +56,18 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let (update_graph, last_active_indices) = client
.scan_txs_with_keychains(keychain_spks, None, None, STOP_GAP, PARALLEL_REQUESTS)
.await?;
let missing_heights = wallet.tx_graph().missing_heights(wallet.local_chain());
let chain_update = client.update_local_chain(prev_tip, missing_heights).await?;
let update = Update {
wallet.apply_update(bdk::Update {
last_active_indices,
graph: update_graph,
chain: Some(chain_update),
};
wallet.apply_update(update)?;
..Default::default()
})?;
let missing_heights = wallet
.staged()
.indexed_tx_graph
.graph
.missing_heights_from(wallet.local_chain());
let chain_update = client.update_local_chain(prev_tip, missing_heights).await?;
wallet.apply_update(chain_update.into())?;
wallet.commit()?;
println!();

Expand Down
21 changes: 12 additions & 9 deletions example-crates/wallet_esplora_blocking/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::{io::Write, str::FromStr};

use bdk::{
bitcoin::{Address, Network},
wallet::{AddressIndex, Update},
wallet::AddressIndex,
SignOptions, Wallet,
};
use bdk_esplora::{esplora_client, EsploraExt};
Expand Down Expand Up @@ -55,15 +55,18 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {

let (update_graph, last_active_indices) =
client.scan_txs_with_keychains(keychain_spks, None, None, STOP_GAP, PARALLEL_REQUESTS)?;
let missing_heights = wallet.tx_graph().missing_heights(wallet.local_chain());
let chain_update = client.update_local_chain(prev_tip, missing_heights)?;
let update = Update {
last_active_indices,
wallet.apply_update(bdk::Update {
graph: update_graph,
chain: Some(chain_update),
};

wallet.apply_update(update)?;
last_active_indices,
..Default::default()
})?;
let missing_heights = wallet
.staged()
.indexed_tx_graph
.graph
.missing_heights_from(wallet.local_chain());
let chain_update = client.update_local_chain(prev_tip, missing_heights)?;
wallet.apply_update(chain_update.into())?;
wallet.commit()?;
println!();

Expand Down

0 comments on commit 8e82bfa

Please sign in to comment.