Skip to content

Commit e5f25a8

Browse files
committed
Merge #1981: refactor(electrum): remove unwrap()s and expect()s
b24ae6d test(electrum): test sync with incorrect network (Wei Chen) 0fdbe9d fix(electrum): Return error on incorrect network (志宇) 2b56f1a refactor(electrum): remove `unwrap()`s and `expect()`s (Wei Chen) Pull request description: Partially resolves bitcoindevkit/bdk_wallet#30. ### Description This PR eliminates all `unwrap()` and `expect()` calls from `bdk_electrum_client`, replacing them with proper error handling. Given that all public methods already return `Result`, we now propagate error messages instead of panicking. ### Changelog notice * Removed all `unwrap()`s and `expect()`s from `bdk_electrum_client.rs`. ### 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 +nightly fmt` and `cargo clippy` before committing ACKs for top commit: evanlinjin: ACK b24ae6d Tree-SHA512: 33c08031fda35ecc12100202fe7cba2f107bc4537e7519e2ed4df9c1ea70f4a644f0fce0d92175af9745d0bf352c61094eee2ff353636ba8342691d12e3b47cc
2 parents edf0279 + b24ae6d commit e5f25a8

File tree

1 file changed

+55
-20
lines changed

1 file changed

+55
-20
lines changed

crates/electrum/src/bdk_electrum_client.rs

Lines changed: 55 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -579,8 +579,17 @@ impl<E: ElectrumApi> BdkElectrumClient<E> {
579579
let outpoint = vin.previous_output;
580580
let vout = outpoint.vout;
581581
let prev_tx = self.fetch_tx(outpoint.txid)?;
582-
let txout = prev_tx.output[vout as usize].clone();
583-
let _ = tx_update.txouts.insert(outpoint, txout);
582+
// Ensure server returns the expected txout.
583+
let txout = prev_tx
584+
.output
585+
.get(vout as usize)
586+
.ok_or_else(|| {
587+
electrum_client::Error::Message(format!(
588+
"prevout {outpoint} does not exist"
589+
))
590+
})?
591+
.clone();
592+
tx_update.txouts.insert(outpoint, txout);
584593
}
585594
}
586595
}
@@ -638,25 +647,19 @@ fn fetch_tip_and_latest_blocks(
638647
}
639648
}
640649
agreement_cp
650+
.ok_or_else(|| Error::Message("cannot find agreement block with server".to_string()))?
641651
};
642652

643-
let agreement_height = agreement_cp.as_ref().map(CheckPoint::height);
644-
645-
let new_tip = new_blocks
653+
let extension = new_blocks
646654
.iter()
647-
// Prune `new_blocks` to only include blocks that are actually new.
648-
.filter(|(height, _)| Some(*<&u32>::clone(height)) > agreement_height)
649-
.map(|(height, hash)| BlockId {
650-
height: *height,
651-
hash: *hash,
655+
.filter({
656+
let agreement_height = agreement_cp.height();
657+
move |(height, _)| **height > agreement_height
652658
})
653-
.fold(agreement_cp, |prev_cp, block| {
654-
Some(match prev_cp {
655-
Some(cp) => cp.push(block).expect("must extend checkpoint"),
656-
None => CheckPoint::new(block),
657-
})
658-
})
659-
.expect("must have at least one checkpoint");
659+
.map(|(&height, &hash)| BlockId { height, hash });
660+
let new_tip = agreement_cp
661+
.extend(extension)
662+
.expect("extension heights already checked to be greater than agreement height");
660663

661664
Ok((new_tip, new_blocks))
662665
}
@@ -687,9 +690,11 @@ fn chain_update(
687690
#[cfg(test)]
688691
mod test {
689692
use crate::{bdk_electrum_client::TxUpdate, BdkElectrumClient};
690-
use bdk_chain::bitcoin::{OutPoint, Transaction, TxIn};
691-
use bdk_core::collections::BTreeMap;
692-
use bdk_testenv::{utils::new_tx, TestEnv};
693+
use bdk_chain::bitcoin::{constants, Network, OutPoint, ScriptBuf, Transaction, TxIn};
694+
use bdk_chain::{BlockId, CheckPoint};
695+
use bdk_core::{collections::BTreeMap, spk_client::SyncRequest};
696+
use bdk_testenv::{anyhow, utils::new_tx, TestEnv};
697+
use electrum_client::Error as ElectrumError;
693698
use std::sync::Arc;
694699

695700
#[cfg(feature = "default")]
@@ -722,4 +727,34 @@ mod test {
722727
// Ensure that the txouts are empty.
723728
assert_eq!(tx_update.txouts, BTreeMap::default());
724729
}
730+
731+
#[cfg(feature = "default")]
732+
#[test]
733+
fn test_sync_wrong_network_error() -> anyhow::Result<()> {
734+
let env = TestEnv::new()?;
735+
let client = electrum_client::Client::new(env.electrsd.electrum_url.as_str()).unwrap();
736+
let electrum_client = BdkElectrumClient::new(client);
737+
738+
let _ = env.mine_blocks(1, None).unwrap();
739+
740+
let bogus_spks: Vec<ScriptBuf> = Vec::new();
741+
let bogus_genesis = constants::genesis_block(Network::Testnet).block_hash();
742+
let bogus_cp = CheckPoint::new(BlockId {
743+
height: 0,
744+
hash: bogus_genesis,
745+
});
746+
747+
let req = SyncRequest::builder()
748+
.chain_tip(bogus_cp)
749+
.spks(bogus_spks)
750+
.build();
751+
let err = electrum_client.sync(req, 1, false).unwrap_err();
752+
753+
assert!(
754+
matches!(err, ElectrumError::Message(m) if m.contains("cannot find agreement block with server")),
755+
"expected missing agreement block error"
756+
);
757+
758+
Ok(())
759+
}
725760
}

0 commit comments

Comments
 (0)