You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Don't scan the blockchain for spent external channels (#3226)
When an external channel is spent, we don't immediately remove it from
our network graph in case the spending transaction is a splice (see
lightning/bolts#1270 for more details).
A side-effect of this change, introduced in #2936, is that when we
start watching a channel after receiving its `channel_announcement`,
we will scan the blockchain if it is actually already spent. This can
be expensive if peers send us `channel_announcement`s for channels
that have been spent a long time ago since `bitcoind` doesn't provide
an index for spending transactions. It is also misleading, because if
we give up after scanning X blocks of the blockchain, we will create
a log line saying that funds are at risk: they're never at risk since
those are not our channels.
This commit fixes this issue by only checking whether the channel is
already spent by a confirmed transaction or not when setting the watch
(which is an inexpensive and efficient RPC call to `bitcoind`), without
scanning the blockchain to find the spending transaction. If it is
already spent, we immediately remove it from our network graph, even
if the spending transaction was actually a splice. This is fine, since
that channel will be re-added to our graph whenever we receive the
`channel_announcement` for the splice. In the worst case, we will simply
not route through an actually available channels for a few blocks while
its splice transaction is confirming.
Co-authored-by: pm47 <pm.padiou@gmail.com>
log.info("funding tx txId={} of channelId={} has been spent by txId={}: waiting for the spending tx to have enough confirmations before removing the channel from the graph", fundingTxId, shortChannelId, spendingTx.txid)
stay() using d.copy(spentChannels = d.spentChannels.updated(spendingTx.txid, d.spentChannels.getOrElse(spendingTx.txid, Set.empty) + shortChannelId))
268
+
spendingTx_opt match {
269
+
caseSome(spendingTx) =>
270
+
log.info("funding tx txId={} of channelId={} has been spent by txId={}: waiting for the spending tx to have enough confirmations before removing the channel from the graph", fundingTxId, shortChannelId, spendingTx.txid)
stay() using d.copy(spentChannels = d.spentChannels.updated(spendingTx.txid, d.spentChannels.getOrElse(spendingTx.txid, Set.empty) + shortChannelId))
273
+
caseNone=>
274
+
// If the channel was spent by a transaction that is already confirmed, it would be very inefficient to scan
275
+
// the blockchain for the spending transaction (which could have confirmed a long time ago), just to forget
276
+
// that channel. We skip scanning the blockchain and immediately forget the channel.
277
+
log.info("funding tx txId={} of channelId={} has already been spent by a confirmed transaction: removing the channel from the graph immediately", fundingTxId, shortChannelId)
278
+
stay() using Validation.handleChannelSpent(d, watcher, nodeParams.db.network, None, Set(shortChannelId))
0 commit comments