From 2b56f1a0487742d9a5a3043c8235c3ecca96a487 Mon Sep 17 00:00:00 2001 From: Wei Chen Date: Wed, 25 Jun 2025 22:01:53 +0000 Subject: [PATCH 1/3] refactor(electrum): remove `unwrap()`s and `expect()`s --- crates/electrum/src/bdk_electrum_client.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/crates/electrum/src/bdk_electrum_client.rs b/crates/electrum/src/bdk_electrum_client.rs index b0b30ea68..6bbe2a659 100644 --- a/crates/electrum/src/bdk_electrum_client.rs +++ b/crates/electrum/src/bdk_electrum_client.rs @@ -579,8 +579,17 @@ impl BdkElectrumClient { let outpoint = vin.previous_output; let vout = outpoint.vout; let prev_tx = self.fetch_tx(outpoint.txid)?; - let txout = prev_tx.output[vout as usize].clone(); - let _ = tx_update.txouts.insert(outpoint, txout); + // Ensure server returns the expected txout. + let txout = prev_tx + .output + .get(vout as usize) + .ok_or_else(|| { + electrum_client::Error::Message(format!( + "prevout {outpoint} does not exist" + )) + })? + .clone(); + tx_update.txouts.insert(outpoint, txout); } } } @@ -652,11 +661,11 @@ fn fetch_tip_and_latest_blocks( }) .fold(agreement_cp, |prev_cp, block| { Some(match prev_cp { - Some(cp) => cp.push(block).expect("must extend checkpoint"), + Some(cp) => cp.push(block).ok()?, None => CheckPoint::new(block), }) }) - .expect("must have at least one checkpoint"); + .ok_or_else(|| Error::Message("failed to construct new checkpoint tip".to_string()))?; Ok((new_tip, new_blocks)) } From 0fdbe9d170941218dddd396c7204bfb8de035d43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BF=97=E5=AE=87?= Date: Thu, 10 Jul 2025 03:04:24 +0000 Subject: [PATCH 2/3] fix(electrum): Return error on incorrect network --- crates/electrum/src/bdk_electrum_client.rs | 24 ++++++++-------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/crates/electrum/src/bdk_electrum_client.rs b/crates/electrum/src/bdk_electrum_client.rs index 6bbe2a659..672cf93b5 100644 --- a/crates/electrum/src/bdk_electrum_client.rs +++ b/crates/electrum/src/bdk_electrum_client.rs @@ -647,25 +647,19 @@ fn fetch_tip_and_latest_blocks( } } agreement_cp + .ok_or_else(|| Error::Message("cannot find agreement block with server".to_string()))? }; - let agreement_height = agreement_cp.as_ref().map(CheckPoint::height); - - let new_tip = new_blocks + let extension = new_blocks .iter() - // Prune `new_blocks` to only include blocks that are actually new. - .filter(|(height, _)| Some(*<&u32>::clone(height)) > agreement_height) - .map(|(height, hash)| BlockId { - height: *height, - hash: *hash, - }) - .fold(agreement_cp, |prev_cp, block| { - Some(match prev_cp { - Some(cp) => cp.push(block).ok()?, - None => CheckPoint::new(block), - }) + .filter({ + let agreement_height = agreement_cp.height(); + move |(height, _)| **height > agreement_height }) - .ok_or_else(|| Error::Message("failed to construct new checkpoint tip".to_string()))?; + .map(|(&height, &hash)| BlockId { height, hash }); + let new_tip = agreement_cp + .extend(extension) + .expect("extension heights already checked to be greater than agreement height"); Ok((new_tip, new_blocks)) } From b24ae6d71815e268417034fda44f2d57325f6166 Mon Sep 17 00:00:00 2001 From: Wei Chen Date: Sun, 13 Jul 2025 18:42:42 +0000 Subject: [PATCH 3/3] test(electrum): test sync with incorrect network --- crates/electrum/src/bdk_electrum_client.rs | 38 ++++++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/crates/electrum/src/bdk_electrum_client.rs b/crates/electrum/src/bdk_electrum_client.rs index 672cf93b5..d277c6f73 100644 --- a/crates/electrum/src/bdk_electrum_client.rs +++ b/crates/electrum/src/bdk_electrum_client.rs @@ -690,9 +690,11 @@ fn chain_update( #[cfg(test)] mod test { use crate::{bdk_electrum_client::TxUpdate, BdkElectrumClient}; - use bdk_chain::bitcoin::{OutPoint, Transaction, TxIn}; - use bdk_core::collections::BTreeMap; - use bdk_testenv::{utils::new_tx, TestEnv}; + use bdk_chain::bitcoin::{constants, Network, OutPoint, ScriptBuf, Transaction, TxIn}; + use bdk_chain::{BlockId, CheckPoint}; + use bdk_core::{collections::BTreeMap, spk_client::SyncRequest}; + use bdk_testenv::{anyhow, utils::new_tx, TestEnv}; + use electrum_client::Error as ElectrumError; use std::sync::Arc; #[cfg(feature = "default")] @@ -725,4 +727,34 @@ mod test { // Ensure that the txouts are empty. assert_eq!(tx_update.txouts, BTreeMap::default()); } + + #[cfg(feature = "default")] + #[test] + fn test_sync_wrong_network_error() -> anyhow::Result<()> { + let env = TestEnv::new()?; + let client = electrum_client::Client::new(env.electrsd.electrum_url.as_str()).unwrap(); + let electrum_client = BdkElectrumClient::new(client); + + let _ = env.mine_blocks(1, None).unwrap(); + + let bogus_spks: Vec = Vec::new(); + let bogus_genesis = constants::genesis_block(Network::Testnet).block_hash(); + let bogus_cp = CheckPoint::new(BlockId { + height: 0, + hash: bogus_genesis, + }); + + let req = SyncRequest::builder() + .chain_tip(bogus_cp) + .spks(bogus_spks) + .build(); + let err = electrum_client.sync(req, 1, false).unwrap_err(); + + assert!( + matches!(err, ElectrumError::Message(m) if m.contains("cannot find agreement block with server")), + "expected missing agreement block error" + ); + + Ok(()) + } }