Skip to content

Commit bcff89d

Browse files
committed
Merge #1756: fix(electrum): prevent fetch_prev_txout from querying coinbase transactions
d4ef266 test(electrum): `fetch_prev_txout` does not process coinbase transactions (Wei Chen) 0944b35 fix(electrum): prevent `fetch_prev_txout` from querying coinbase transactions (Wei Chen) Pull request description: Fixes #1698. ### Description `fetch_prev_txout` should not try to query the prevouts of coinbase transactions, as it may result in the Electrum server returning an error which would cause the overall `sync`/`full_scan` to fail. Without this critical bug fix, `bdk_electrum` will crash when someone tracks an address being mined to. ### Notes to the reviewers ### Changelog notice * `fetch_prev_txout` no longer queries coinbase transactions. ### Checklists #### All Submissions: * [x] I've signed all my commits * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md) * [x] I ran `cargo fmt` and `cargo clippy` before committing #### Bugfixes: * [ ] This pull request breaks the existing API * [x] I've added tests to reproduce the issue which are now passing * [x] I'm linking the issue being fixed by this PR ACKs for top commit: evanlinjin: ACK d4ef266 Tree-SHA512: 6423f9486e84f204cf756117cabff35ce79ee169efa43a059c1669ad0f7193b58299eee7c5672af35ab070ed8011637b0a1904866ce2f2fa4580ddc3f9f2d982
2 parents 2a1787b + d4ef266 commit bcff89d

File tree

2 files changed

+77
-1
lines changed

2 files changed

+77
-1
lines changed

crates/electrum/src/bdk_electrum_client.rs

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ impl<E: ElectrumApi> BdkElectrumClient<E> {
422422
) -> Result<(), Error> {
423423
let mut no_dup = HashSet::<Txid>::new();
424424
for tx in &tx_update.txs {
425-
if no_dup.insert(tx.compute_txid()) {
425+
if !tx.is_coinbase() && no_dup.insert(tx.compute_txid()) {
426426
for vin in &tx.input {
427427
let outpoint = vin.previous_output;
428428
let vout = outpoint.vout;
@@ -531,3 +531,45 @@ fn chain_update(
531531
}
532532
Ok(tip)
533533
}
534+
535+
#[cfg(test)]
536+
mod test {
537+
use crate::{bdk_electrum_client::TxUpdate, BdkElectrumClient};
538+
use bdk_chain::bitcoin::{OutPoint, Transaction, TxIn};
539+
use bdk_core::collections::BTreeMap;
540+
use bdk_testenv::{utils::new_tx, TestEnv};
541+
use std::sync::Arc;
542+
543+
#[cfg(feature = "default")]
544+
#[test]
545+
fn test_fetch_prev_txout_with_coinbase() {
546+
let env = TestEnv::new().unwrap();
547+
let electrum_client =
548+
electrum_client::Client::new(env.electrsd.electrum_url.as_str()).unwrap();
549+
let client = BdkElectrumClient::new(electrum_client);
550+
551+
// Create a coinbase transaction.
552+
let coinbase_tx = Transaction {
553+
input: vec![TxIn {
554+
previous_output: OutPoint::null(),
555+
..Default::default()
556+
}],
557+
..new_tx(0)
558+
};
559+
560+
assert!(coinbase_tx.is_coinbase());
561+
562+
// Test that `fetch_prev_txout` does not process coinbase transactions. Calling
563+
// `fetch_prev_txout` on a coinbase transaction will trigger a `fetch_tx` on a transaction
564+
// with a txid of all zeros. If `fetch_prev_txout` attempts to fetch this transaction, this
565+
// assertion will fail.
566+
let mut tx_update = TxUpdate {
567+
txs: vec![Arc::new(coinbase_tx)],
568+
..Default::default()
569+
};
570+
assert!(client.fetch_prev_txout(&mut tx_update).is_ok());
571+
572+
// Ensure that the txouts are empty.
573+
assert_eq!(tx_update.txouts, BTreeMap::default());
574+
}
575+
}

crates/electrum/tests/test_electrum.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,3 +525,37 @@ fn tx_can_become_unconfirmed_after_reorg() -> anyhow::Result<()> {
525525

526526
Ok(())
527527
}
528+
529+
#[test]
530+
fn test_sync_with_coinbase() -> anyhow::Result<()> {
531+
let env = TestEnv::new()?;
532+
let electrum_client = electrum_client::Client::new(env.electrsd.electrum_url.as_str())?;
533+
let client = BdkElectrumClient::new(electrum_client);
534+
535+
// Setup address.
536+
let spk_to_track = ScriptBuf::new_p2wsh(&WScriptHash::all_zeros());
537+
let addr_to_track = Address::from_script(&spk_to_track, bdk_chain::bitcoin::Network::Regtest)?;
538+
539+
// Setup receiver.
540+
let (mut recv_chain, _) = LocalChain::from_genesis_hash(env.bitcoind.client.get_block_hash(0)?);
541+
let mut recv_graph = IndexedTxGraph::<ConfirmationBlockTime, _>::new({
542+
let mut recv_index = SpkTxOutIndex::default();
543+
recv_index.insert_spk((), spk_to_track.clone());
544+
recv_index
545+
});
546+
547+
// Mine some blocks.
548+
env.mine_blocks(101, Some(addr_to_track))?;
549+
env.wait_until_electrum_sees_block(Duration::from_secs(6))?;
550+
551+
// Check to see if electrum syncs properly.
552+
assert!(sync_with_electrum(
553+
&client,
554+
[spk_to_track.clone()],
555+
&mut recv_chain,
556+
&mut recv_graph,
557+
)
558+
.is_ok());
559+
560+
Ok(())
561+
}

0 commit comments

Comments
 (0)