Skip to content

Commit 7e28c61

Browse files
committed
rust: sync: handle removal of unconfirmed transactions
When an unconfirmed transaction is removed from the (server) mempool, the "script status" of the scripts involving such transaction should change. For instance if the script only involves txid, it can change from `h("txid:0:")` to `null`. Unfortunately this does not always happen. Thus we don't have a way to realize that we should update our internal cache and specifically remove such transaction. To handle this case, we explicitly request each unconfirmed transaction to the server, to determine if it should be removed. This comes with a performance penalty, but it seems to be necessary.
1 parent 43e1293 commit 7e28c61

File tree

1 file changed

+29
-2
lines changed
  • subprojects/gdk_rust/gdk_electrum/src

1 file changed

+29
-2
lines changed

subprojects/gdk_rust/gdk_electrum/src/lib.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1508,17 +1508,39 @@ impl Syncer {
15081508
let new_txs = self.download_txs(account.num(), &history_txs_id, &scripts, &client)?;
15091509
let headers = self.download_headers(account.num(), &heights_set, &client)?;
15101510

1511-
let store_last_used = {
1511+
let (store_last_used, unc_txs_cache) = {
15121512
let store_read = self.store.read()?;
15131513
let acc_store = store_read.account_cache(account.num())?;
1514-
acc_store.get_both_last_used()
1514+
let unc_txs_cache: Vec<bitcoin::Txid> = acc_store
1515+
.heights
1516+
.iter()
1517+
.filter(|(_, h)| h.is_none())
1518+
.map(|(t, _)| t.into_bitcoin())
1519+
.collect();
1520+
(acc_store.get_both_last_used(), unc_txs_cache)
15151521
};
15161522

1523+
// Sometimes when an unconfirmed transaction is removed from the mempool, the script status
1524+
// of the scripts involving such transaction is not updated. So we don't have a way to
1525+
// realize that we should update our cache.
1526+
// To handle these scenarios, we explicitly try to get such transactions from the server, to
1527+
// understand whether we should remove them or not.
1528+
// Unfortunately this seems to be necessary.
1529+
let unc_txs_to_remove: Vec<BETxid> = unc_txs_cache
1530+
.iter()
1531+
.filter(|txid| {
1532+
// We can't do a batch request here since one error would make the whole call fail
1533+
client.transaction_get_raw(&txid).is_err()
1534+
})
1535+
.map(BETxidConvert::into_be)
1536+
.collect();
1537+
15171538
let changed = if !new_txs.txs.is_empty()
15181539
|| !headers.is_empty()
15191540
|| store_last_used != last_used
15201541
|| !scripts.is_empty()
15211542
|| !txid_height.is_empty()
1543+
|| !unc_txs_to_remove.is_empty()
15221544
{
15231545
info!(
15241546
"There are changes in the store new_txs:{:?} headers:{:?} txid_height:{:?} scripts:{:?} store_last_used_changed:{}",
@@ -1597,6 +1619,11 @@ impl Syncer {
15971619
}
15981620
}
15991621

1622+
// Remove unconfirmed transaction not returned anymore
1623+
for txid in unc_txs_to_remove {
1624+
acc_store.heights.remove(&txid);
1625+
}
1626+
16001627
acc_store.heights.extend(txid_height.into_iter());
16011628
acc_store.scripts.extend(scripts.clone().into_iter().map(|(a, b)| (b, a)));
16021629
acc_store.paths.extend(scripts.into_iter());

0 commit comments

Comments
 (0)