Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update bdk_electrum crate to use sync/full-scan structs #1403

Merged
merged 13 commits into from
May 10, 2024
Merged
Changes from 1 commit
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
Prev Previous commit
feat(electrum): update docs and simplify logic of ElectrumExt
Helper method docs are updated to explain what they are updating. Logic
is simplified as we do not need to check whether a tx exists already in
`update_graph` before inserting it.
  • Loading branch information
evanlinjin committed May 10, 2024

Verified

This commit was signed with the committer’s verified signature.
evanlinjin 志宇
commit b45897e6fe2f7e67f5d75ec6f983757b28c5ec19
69 changes: 39 additions & 30 deletions crates/electrum/src/electrum_ext.rs
Original file line number Diff line number Diff line change
@@ -395,6 +395,12 @@ fn determine_tx_anchor(
}
}

/// Populate the `graph_update` with associated transactions/anchors of `outpoints`.
///
/// Transactions in which the outpoint resides, and transactions that spend from the outpoint are
/// included. Anchors of the aforementioned transactions are included.
///
/// Checkpoints (in `cps`) are used to create anchors. The `tx_cache` is self-explanatory.
fn populate_with_outpoints(
client: &impl ElectrumApi,
cps: &BTreeMap<u32, CheckPoint>,
@@ -403,59 +409,52 @@ fn populate_with_outpoints(
outpoints: impl IntoIterator<Item = OutPoint>,
) -> Result<(), Error> {
for outpoint in outpoints {
let txid = outpoint.txid;
let tx = client.transaction_get(&txid)?;
debug_assert_eq!(tx.txid(), txid);
let txout = match tx.output.get(outpoint.vout as usize) {
let op_txid = outpoint.txid;
let op_tx = fetch_tx(client, tx_cache, op_txid)?;
let op_txout = match op_tx.output.get(outpoint.vout as usize) {
Some(txout) => txout,
None => continue,
};
debug_assert_eq!(op_tx.txid(), op_txid);

// attempt to find the following transactions (alongside their chain positions), and
// add to our sparsechain `update`:
let mut has_residing = false; // tx in which the outpoint resides
let mut has_spending = false; // tx that spends the outpoint
for res in client.script_get_history(&txout.script_pubkey)? {
for res in client.script_get_history(&op_txout.script_pubkey)? {
if has_residing && has_spending {
break;
}

if res.tx_hash == txid {
if has_residing {
continue;
}
if !has_residing && res.tx_hash == op_txid {
has_residing = true;
if graph_update.get_tx(res.tx_hash).is_none() {
let _ = graph_update.insert_tx(tx.clone());
let _ = graph_update.insert_tx(Arc::clone(&op_tx));
if let Some(anchor) = determine_tx_anchor(cps, res.height, res.tx_hash) {
let _ = graph_update.insert_anchor(res.tx_hash, anchor);
}
} else {
if has_spending {
continue;
}
let res_tx = match graph_update.get_tx(res.tx_hash) {
Some(tx) => tx,
None => {
let res_tx = fetch_tx(client, tx_cache, res.tx_hash)?;
let _ = graph_update.insert_tx(Arc::clone(&res_tx));
res_tx
}
};
}

if !has_spending && res.tx_hash != op_txid {
let res_tx = fetch_tx(client, tx_cache, res.tx_hash)?;
// we exclude txs/anchors that do not spend our specified outpoint(s)
has_spending = res_tx
.input
.iter()
.any(|txin| txin.previous_output == outpoint);
if !has_spending {
continue;
}
};

if let Some(anchor) = determine_tx_anchor(cps, res.height, res.tx_hash) {
let _ = graph_update.insert_anchor(res.tx_hash, anchor);
let _ = graph_update.insert_tx(Arc::clone(&res_tx));
if let Some(anchor) = determine_tx_anchor(cps, res.height, res.tx_hash) {
let _ = graph_update.insert_anchor(res.tx_hash, anchor);
}
}
}
}
Ok(())
}

/// Populate the `graph_update` with transactions/anchors of the provided `txids`.
fn populate_with_txids(
client: &impl ElectrumApi,
cps: &BTreeMap<u32, CheckPoint>,
@@ -476,6 +475,8 @@ fn populate_with_txids(
.map(|txo| &txo.script_pubkey)
.expect("tx must have an output");

// because of restrictions of the Electrum API, we have to use the `script_get_history`
// call to get confirmation status of our transaction
let anchor = match client
.script_get_history(spk)?
.into_iter()
@@ -485,16 +486,17 @@ fn populate_with_txids(
None => continue,
};

if graph_update.get_tx(txid).is_none() {
let _ = graph_update.insert_tx(tx);
}
let _ = graph_update.insert_tx(tx);
if let Some(anchor) = anchor {
let _ = graph_update.insert_anchor(txid, anchor);
}
}
Ok(())
}

/// Fetch transaction of given `txid`.
///
/// We maintain a `tx_cache` so that we won't need to fetch from Electrum with every call.
fn fetch_tx<C: ElectrumApi>(
client: &C,
tx_cache: &mut TxCache,
@@ -530,6 +532,13 @@ fn fetch_prev_txout<C: ElectrumApi>(
Ok(())
}

/// Populate the `graph_update` with transactions/anchors associated with the given `spks`.
///
/// Transactions that contains an output with requested spk, or spends form an output with
/// requested spk will be added to `graph_update`. Anchors of the aforementioned transactions are
/// also included.
///
/// Checkpoints (in `cps`) are used to create anchors. The `tx_cache` is self-explanatory.
fn populate_with_spks<I: Ord + Clone>(
client: &impl ElectrumApi,
cps: &BTreeMap<u32, CheckPoint>,