Skip to content

Commit 47cbd6e

Browse files
committed
Fix: Also update the payment store for mempool transactions
When we intially implemented `bitcoind` syncing polling the mempool was very frequent and rather inefficient so we made a choice not to unnecessarily update the payment store for mempool changes, especially since we only consider transactions `Succeeded` after `ANTI_REORG_DELAY` anyways. However, since then we made quite a few peformance improvements to the mempool syncing, and by now we should just update they payment store as not doing so will lead to rather unexpected behavior, making some tests fail for `TestChainSource::Bitcoind`, e.g., `channel_full_cycle_0conf`, which we fix here. As we recently switched to updating the payment store based on BDK's `WalletEvent`, but they currently don't offer an API returning such events when applying mempool transactions, we copy over the respective method for generating events from `bdk_wallet`, with the intention of dropping it again once they do. Signed-off-by: Elias Rohrer <dev@tnull.de>
1 parent 9c74adf commit 47cbd6e

File tree

1 file changed

+133
-0
lines changed

1 file changed

+133
-0
lines changed

src/wallet/mod.rs

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,16 +152,47 @@ impl Wallet {
152152
pub(crate) fn apply_mempool_txs(
153153
&self, unconfirmed_txs: Vec<(Transaction, u64)>, evicted_txids: Vec<(Txid, u64)>,
154154
) -> Result<(), Error> {
155+
if unconfirmed_txs.is_empty() {
156+
return Ok(());
157+
}
158+
155159
let mut locked_wallet = self.inner.lock().unwrap();
160+
161+
let chain_tip1 = locked_wallet.latest_checkpoint().block_id();
162+
let wallet_txs1 = locked_wallet
163+
.transactions()
164+
.map(|wtx| (wtx.tx_node.txid, (wtx.tx_node.tx.clone(), wtx.chain_position)))
165+
.collect::<std::collections::BTreeMap<
166+
Txid,
167+
(Arc<Transaction>, bdk_chain::ChainPosition<bdk_chain::ConfirmationBlockTime>),
168+
>>();
169+
156170
locked_wallet.apply_unconfirmed_txs(unconfirmed_txs);
157171
locked_wallet.apply_evicted_txs(evicted_txids);
158172

173+
let chain_tip2 = locked_wallet.latest_checkpoint().block_id();
174+
let wallet_txs2 = locked_wallet
175+
.transactions()
176+
.map(|wtx| (wtx.tx_node.txid, (wtx.tx_node.tx.clone(), wtx.chain_position)))
177+
.collect::<std::collections::BTreeMap<
178+
Txid,
179+
(Arc<Transaction>, bdk_chain::ChainPosition<bdk_chain::ConfirmationBlockTime>),
180+
>>();
181+
182+
let events =
183+
wallet_events(&mut *locked_wallet, chain_tip1, chain_tip2, wallet_txs1, wallet_txs2);
184+
159185
let mut locked_persister = self.persister.lock().unwrap();
160186
locked_wallet.persist(&mut locked_persister).map_err(|e| {
161187
log_error!(self.logger, "Failed to persist wallet: {}", e);
162188
Error::PersistenceFailed
163189
})?;
164190

191+
self.update_payment_store(&mut *locked_wallet, events).map_err(|e| {
192+
log_error!(self.logger, "Failed to update payment store: {}", e);
193+
Error::PersistenceFailed
194+
})?;
195+
165196
Ok(())
166197
}
167198

@@ -1212,3 +1243,105 @@ impl ChangeDestinationSource for WalletKeysManager {
12121243
}
12131244
}
12141245
}
1246+
1247+
// FIXME/TODO: This is copied-over from bdk_wallet and only used to generate `WalletEvent`s after
1248+
// applying mempool transactions. We should drop this when BDK offers to generate events for
1249+
// mempool transactions natively.
1250+
pub(crate) fn wallet_events(
1251+
wallet: &mut bdk_wallet::Wallet, chain_tip1: bdk_chain::BlockId,
1252+
chain_tip2: bdk_chain::BlockId,
1253+
wallet_txs1: std::collections::BTreeMap<
1254+
Txid,
1255+
(Arc<Transaction>, bdk_chain::ChainPosition<bdk_chain::ConfirmationBlockTime>),
1256+
>,
1257+
wallet_txs2: std::collections::BTreeMap<
1258+
Txid,
1259+
(Arc<Transaction>, bdk_chain::ChainPosition<bdk_chain::ConfirmationBlockTime>),
1260+
>,
1261+
) -> Vec<WalletEvent> {
1262+
let mut events: Vec<WalletEvent> = Vec::new();
1263+
1264+
if chain_tip1 != chain_tip2 {
1265+
events.push(WalletEvent::ChainTipChanged { old_tip: chain_tip1, new_tip: chain_tip2 });
1266+
}
1267+
1268+
wallet_txs2.iter().for_each(|(txid2, (tx2, cp2))| {
1269+
if let Some((tx1, cp1)) = wallet_txs1.get(txid2) {
1270+
assert_eq!(tx1.compute_txid(), *txid2);
1271+
match (cp1, cp2) {
1272+
(
1273+
bdk_chain::ChainPosition::Unconfirmed { .. },
1274+
bdk_chain::ChainPosition::Confirmed { anchor, .. },
1275+
) => {
1276+
events.push(WalletEvent::TxConfirmed {
1277+
txid: *txid2,
1278+
tx: tx2.clone(),
1279+
block_time: *anchor,
1280+
old_block_time: None,
1281+
});
1282+
},
1283+
(
1284+
bdk_chain::ChainPosition::Confirmed { anchor, .. },
1285+
bdk_chain::ChainPosition::Unconfirmed { .. },
1286+
) => {
1287+
events.push(WalletEvent::TxUnconfirmed {
1288+
txid: *txid2,
1289+
tx: tx2.clone(),
1290+
old_block_time: Some(*anchor),
1291+
});
1292+
},
1293+
(
1294+
bdk_chain::ChainPosition::Confirmed { anchor: anchor1, .. },
1295+
bdk_chain::ChainPosition::Confirmed { anchor: anchor2, .. },
1296+
) => {
1297+
if *anchor1 != *anchor2 {
1298+
events.push(WalletEvent::TxConfirmed {
1299+
txid: *txid2,
1300+
tx: tx2.clone(),
1301+
block_time: *anchor2,
1302+
old_block_time: Some(*anchor1),
1303+
});
1304+
}
1305+
},
1306+
(
1307+
bdk_chain::ChainPosition::Unconfirmed { .. },
1308+
bdk_chain::ChainPosition::Unconfirmed { .. },
1309+
) => {
1310+
// do nothing if still unconfirmed
1311+
},
1312+
}
1313+
} else {
1314+
match cp2 {
1315+
bdk_chain::ChainPosition::Confirmed { anchor, .. } => {
1316+
events.push(WalletEvent::TxConfirmed {
1317+
txid: *txid2,
1318+
tx: tx2.clone(),
1319+
block_time: *anchor,
1320+
old_block_time: None,
1321+
});
1322+
},
1323+
bdk_chain::ChainPosition::Unconfirmed { .. } => {
1324+
events.push(WalletEvent::TxUnconfirmed {
1325+
txid: *txid2,
1326+
tx: tx2.clone(),
1327+
old_block_time: None,
1328+
});
1329+
},
1330+
}
1331+
}
1332+
});
1333+
1334+
// find tx that are no longer canonical
1335+
wallet_txs1.iter().for_each(|(txid1, (tx1, _))| {
1336+
if !wallet_txs2.contains_key(txid1) {
1337+
let conflicts = wallet.tx_graph().direct_conflicts(tx1).collect::<Vec<_>>();
1338+
if !conflicts.is_empty() {
1339+
events.push(WalletEvent::TxReplaced { txid: *txid1, tx: tx1.clone(), conflicts });
1340+
} else {
1341+
events.push(WalletEvent::TxDropped { txid: *txid1, tx: tx1.clone() });
1342+
}
1343+
}
1344+
});
1345+
1346+
events
1347+
}

0 commit comments

Comments
 (0)