Skip to content

Commit 0f250b1

Browse files
t-bastremyers
authored andcommitted
Unwatch obsolete transactions
We unwatch: - channels that have been spliced or closed - RBF candidates of a splice or closing transaction We revert the modification to channel updates to avoid invalidating the signature.
1 parent e79c65b commit 0f250b1

File tree

5 files changed

+103
-46
lines changed

5 files changed

+103
-46
lines changed

eclair-core/src/main/scala/fr/acinq/eclair/blockchain/bitcoind/ZmqWatcher.scala

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,9 @@ object ZmqWatcher {
120120
def spendingTx: Transaction
121121
}
122122

123-
case class WatchExternalChannelSpent(replyTo: ActorRef[WatchExternalChannelSpentTriggered], txId: TxId, outputIndex: Int, shortChannelId: RealShortChannelId) extends WatchSpent[WatchExternalChannelSpentTriggered] {
124-
override def hints: Set[TxId] = Set.empty
125-
}
123+
case class WatchExternalChannelSpent(replyTo: ActorRef[WatchExternalChannelSpentTriggered], txId: TxId, outputIndex: Int, shortChannelId: RealShortChannelId) extends WatchSpent[WatchExternalChannelSpentTriggered] { override def hints: Set[TxId] = Set.empty }
126124
case class WatchExternalChannelSpentTriggered(shortChannelId: RealShortChannelId, spendingTx: Transaction) extends WatchSpentTriggered
125+
case class UnwatchExternalChannelSpent(txId: TxId, outputIndex: Int) extends Command
127126

128127
case class WatchFundingSpent(replyTo: ActorRef[WatchFundingSpentTriggered], txId: TxId, outputIndex: Int, hints: Set[TxId]) extends WatchSpent[WatchFundingSpentTriggered]
129128
case class WatchFundingSpentTriggered(spendingTx: Transaction) extends WatchSpentTriggered
@@ -356,6 +355,11 @@ private class ZmqWatcher(nodeParams: NodeParams, blockHeight: AtomicLong, client
356355
}
357356
watching(watches -- deprecatedWatches, watchedUtxos)
358357

358+
case UnwatchExternalChannelSpent(txId, outputIndex) =>
359+
val deprecatedWatches = watches.keySet.collect { case w: WatchExternalChannelSpent if w.txId == txId && w.outputIndex == outputIndex => w }
360+
val watchedUtxos1 = deprecatedWatches.foldLeft(watchedUtxos) { case (m, w) => removeWatchedUtxos(m, w) }
361+
watching(watches -- deprecatedWatches, watchedUtxos1)
362+
359363
case ValidateRequest(replyTo, ann) =>
360364
client.validate(ann).map(replyTo ! _)
361365
Behaviors.same

eclair-core/src/main/scala/fr/acinq/eclair/router/Router.scala

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,8 @@ import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
2525
import fr.acinq.bitcoin.scalacompat.{BlockHash, ByteVector32, Satoshi, TxId}
2626
import fr.acinq.eclair.Logs.LogCategory
2727
import fr.acinq.eclair._
28-
import fr.acinq.eclair.blockchain.CurrentBlockHeight
2928
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher
30-
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.{ValidateResult, WatchExternalChannelSpent, WatchExternalChannelSpentTriggered, WatchTxConfirmed, WatchTxConfirmedTriggered}
29+
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher._
3130
import fr.acinq.eclair.channel._
3231
import fr.acinq.eclair.channel.fsm.Channel.ANNOUNCEMENTS_MINCONF
3332
import fr.acinq.eclair.crypto.TransportHandler
@@ -263,14 +262,14 @@ class Router(val nodeParams: NodeParams, watcher: typed.ActorRef[ZmqWatcher.Comm
263262
stay() using Validation.handleChannelValidationResponse(d, nodeParams, watcher, r)
264263

265264
case Event(WatchExternalChannelSpentTriggered(shortChannelId, spendingTx), d) if d.channels.contains(shortChannelId) || d.prunedChannels.contains(shortChannelId) =>
266-
val txId = d.channels.getOrElse(shortChannelId, d.prunedChannels(shortChannelId)).fundingTxId
267-
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", txId, shortChannelId, spendingTx.txid)
265+
val fundingTxId = d.channels.get(shortChannelId).orElse(d.prunedChannels.get(shortChannelId)).get.fundingTxId
266+
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)
268267
watcher ! WatchTxConfirmed(self, spendingTx.txid, ANNOUNCEMENTS_MINCONF * 2)
269268
stay() using d.copy(spentChannels = d.spentChannels + (spendingTx.txid -> shortChannelId))
270269

271270
case Event(WatchTxConfirmedTriggered(_, _, spendingTx), d) =>
272271
d.spentChannels.get(spendingTx.txid) match {
273-
case Some(shortChannelId) => stay() using Validation.handleChannelSpent(d, nodeParams.db.network, shortChannelId)
272+
case Some(shortChannelId) => stay() using Validation.handleChannelSpent(d, watcher, nodeParams.db.network, shortChannelId)
274273
case None => stay()
275274
}
276275

@@ -585,7 +584,6 @@ object Router {
585584
def +(ignoreNode: PublicKey): Ignore = copy(nodes = nodes + ignoreNode)
586585
def ++(ignoreNodes: Set[PublicKey]): Ignore = copy(nodes = nodes ++ ignoreNodes)
587586
def +(ignoreChannel: ChannelDesc): Ignore = copy(channels = channels + ignoreChannel)
588-
def emptyNodes(): Ignore = copy(nodes = Set.empty)
589587
def emptyChannels(): Ignore = copy(channels = Set.empty)
590588
// @formatter:on
591589
}
@@ -634,12 +632,6 @@ object Router {
634632
/** Full route including the final hop, if any. */
635633
val fullRoute: Seq[Hop] = hops ++ finalHop_opt.toSeq
636634

637-
/**
638-
* Fee paid for the trampoline hop, if any.
639-
* Note that when using MPP to reach the trampoline node, the trampoline fee must be counted only once.
640-
*/
641-
val trampolineFee: MilliSatoshi = finalHop_opt.collect { case hop: NodeHop => hop.fee(amount) }.getOrElse(0 msat)
642-
643635
/**
644636
* Fee paid for the blinded route, if any.
645637
* Note that when we are the introduction node for the blinded route, we cannot easily compute the fee without the

eclair-core/src/main/scala/fr/acinq/eclair/router/Validation.scala

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import fr.acinq.bitcoin.scalacompat.Script.{pay2wsh, write}
2424
import fr.acinq.bitcoin.scalacompat.{Satoshi, TxId}
2525
import fr.acinq.eclair.ShortChannelId.outputIndex
2626
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher
27-
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher.{UtxoStatus, ValidateRequest, ValidateResult, WatchExternalChannelSpent}
27+
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher._
2828
import fr.acinq.eclair.channel._
2929
import fr.acinq.eclair.crypto.TransportHandler
3030
import fr.acinq.eclair.db.NetworkDb
@@ -33,7 +33,7 @@ import fr.acinq.eclair.router.Monitoring.Metrics
3333
import fr.acinq.eclair.router.Router._
3434
import fr.acinq.eclair.transactions.Scripts
3535
import fr.acinq.eclair.wire.protocol._
36-
import fr.acinq.eclair.{BlockHeight, Logs, MilliSatoshiLong, NodeParams, RealShortChannelId, ShortChannelId, TimestampSecond, TxCoordinates}
36+
import fr.acinq.eclair.{BlockHeight, Logs, MilliSatoshiLong, NodeParams, RealShortChannelId, ShortChannelId, TxCoordinates}
3737

3838
object Validation {
3939

@@ -120,8 +120,10 @@ object Validation {
120120
Some(updateSplicedPublicChannel(d0, nodeParams, watcher, c, tx.txid, capacity, parentChannel))
121121
case None =>
122122
log.error("spent parent channel shortChannelId={} not found for splice shortChannelId={}", parentScid, c.shortChannelId)
123-
val spentChannels1 = d0.spentChannels.filter(_._2 != parentScid)
124-
Some(addPublicChannel(d0, nodeParams, watcher, c, tx.txid, capacity, None).copy(spentChannels = spentChannels1))
123+
val spendingTxs = d0.spentChannels.filter(_._2 == parentScid).keySet
124+
spendingTxs.foreach(txId => watcher ! UnwatchTxConfirmed(txId))
125+
val d1 = d0.copy(spentChannels = d0.spentChannels -- spendingTxs)
126+
Some(addPublicChannel(d1, nodeParams, watcher, c, tx.txid, capacity, None))
125127
}
126128
case None => Some(addPublicChannel(d0, nodeParams, watcher, c, tx.txid, capacity, None))
127129
}
@@ -171,6 +173,7 @@ object Validation {
171173
implicit val sender: ActorRef = ctx.self // necessary to preserve origin when sending messages to other actors
172174
val fundingOutputIndex = outputIndex(ann.shortChannelId)
173175
watcher ! WatchExternalChannelSpent(ctx.self, spliceTxId, fundingOutputIndex, ann.shortChannelId)
176+
watcher ! UnwatchExternalChannelSpent(parentChannel.fundingTxId, outputIndex(parentChannel.ann.shortChannelId))
174177
// we notify front nodes that the channel has been replaced
175178
ctx.system.eventStream.publish(ChannelsDiscovered(SingleChannelDiscovered(ann, capacity, None, None) :: Nil))
176179
ctx.system.eventStream.publish(ChannelLost(parentChannel.shortChannelId))
@@ -180,15 +183,17 @@ object Validation {
180183
ann = ann,
181184
fundingTxId = spliceTxId,
182185
capacity = capacity,
183-
// update the timestamps of the channel updates to ensure the spliced channel is not pruned
184-
update_1_opt = parentChannel.update_1_opt.map(_.copy(shortChannelId = ann.shortChannelId, timestamp = TimestampSecond.now())),
185-
update_2_opt = parentChannel.update_2_opt.map(_.copy(shortChannelId = ann.shortChannelId, timestamp = TimestampSecond.now())),
186+
// we keep the previous channel updates to ensure that the channel is still used until we receive the new ones
187+
update_1_opt = parentChannel.update_1_opt,
188+
update_2_opt = parentChannel.update_2_opt,
186189
)
187190
log.debug("replacing parent channel scid={} with splice channel scid={}; splice channel={}", parentChannel.shortChannelId, ann.shortChannelId, newPubChan)
188191
// we need to update the graph because the edge identifiers and capacity change from the parent scid to the new splice scid
189192
log.debug("updating the graph for shortChannelId={}", newPubChan.shortChannelId)
190193
val graph1 = d.graphWithBalances.updateChannel(ChannelDesc(parentChannel.shortChannelId, parentChannel.nodeId1, parentChannel.nodeId2), ann.shortChannelId, capacity)
191-
val spentChannels1 = d.spentChannels.filter(_._2 != parentChannel.shortChannelId)
194+
val spendingTxs = d.spentChannels.filter(_._2 == parentChannel.shortChannelId).keySet
195+
spendingTxs.foreach(txId => watcher ! UnwatchTxConfirmed(txId))
196+
val spentChannels1 = d.spentChannels -- spendingTxs
192197
d.copy(
193198
// we also add the splice scid -> channelId and remove the parent scid -> channelId mappings
194199
channels = d.channels + (newPubChan.shortChannelId -> newPubChan) - parentChannel.shortChannelId,
@@ -262,9 +267,9 @@ object Validation {
262267
} else d1
263268
}
264269

265-
def handleChannelSpent(d: Data, db: NetworkDb, shortChannelId: RealShortChannelId)(implicit ctx: ActorContext, log: LoggingAdapter): Data = {
270+
def handleChannelSpent(d: Data, watcher: typed.ActorRef[ZmqWatcher.Command], db: NetworkDb, shortChannelId: RealShortChannelId)(implicit ctx: ActorContext, log: LoggingAdapter): Data = {
266271
implicit val sender: ActorRef = ctx.self // necessary to preserve origin when sending messages to other actors
267-
val lostChannel = d.channels.get(shortChannelId).orElse(d.prunedChannels.get(shortChannelId)).get.ann
272+
val lostChannel = d.channels.get(shortChannelId).orElse(d.prunedChannels.get(shortChannelId)).get
268273
log.info("funding tx for channelId={} was spent", shortChannelId)
269274
// we need to remove nodes that aren't tied to any channels anymore
270275
val channels1 = d.channels - shortChannelId
@@ -287,7 +292,10 @@ object Validation {
287292
// we no longer need to track this or alternative transactions that spent the parent channel
288293
// either this channel was really closed, or it was spliced and the announcement was not received in time
289294
// we will re-add a spliced channel as a new channel later when we receive the announcement
290-
val spentChannels1 = d.spentChannels.filter(_._2 != shortChannelId)
295+
watcher ! UnwatchExternalChannelSpent(lostChannel.fundingTxId, outputIndex(lostChannel.ann.shortChannelId))
296+
val spendingTxs = d.spentChannels.filter(_._2 == shortChannelId).keySet
297+
spendingTxs.foreach(txId => watcher ! UnwatchTxConfirmed(txId))
298+
val spentChannels1 = d.spentChannels -- spendingTxs
291299
d.copy(nodes = d.nodes -- lostNodes, channels = channels1, prunedChannels = prunedChannels1, graphWithBalances = graphWithBalances1, spentChannels = spentChannels1)
292300
}
293301

eclair-core/src/test/scala/fr/acinq/eclair/blockchain/bitcoind/ZmqWatcherSpec.scala

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,54 @@ class ZmqWatcherSpec extends TestKitBaseClass with AnyFunSuiteLike with Bitcoind
350350
})
351351
}
352352

353+
test("unwatch external channel") {
354+
withWatcher(f => {
355+
import f._
356+
357+
val (priv, address) = createExternalAddress()
358+
val tx1 = sendToAddress(address, 250_000 sat, probe)
359+
val outputIndex1 = tx1.txOut.indexWhere(_.publicKeyScript == Script.write(Script.pay2wpkh(priv.publicKey)))
360+
val spendingTx1 = createSpendP2WPKH(tx1, priv, priv.publicKey, 500 sat, 0, 0)
361+
val tx2 = sendToAddress(address, 200_000 sat, probe)
362+
val outputIndex2 = tx2.txOut.indexWhere(_.publicKeyScript == Script.write(Script.pay2wpkh(priv.publicKey)))
363+
val spendingTx2 = createSpendP2WPKH(tx2, priv, priv.publicKey, 500 sat, 0, 0)
364+
365+
watcher ! WatchExternalChannelSpent(probe.ref, tx1.txid, outputIndex1, RealShortChannelId(3))
366+
watcher ! WatchExternalChannelSpent(probe.ref, tx2.txid, outputIndex2, RealShortChannelId(5))
367+
watcher ! UnwatchExternalChannelSpent(tx1.txid, outputIndex1 + 1) // ignored
368+
watcher ! UnwatchExternalChannelSpent(randomTxId(), outputIndex1) // ignored
369+
370+
// When publishing the transaction, the watch triggers immediately.
371+
bitcoinClient.publishTransaction(spendingTx1)
372+
probe.expectMsg(WatchExternalChannelSpentTriggered(RealShortChannelId(3), spendingTx1))
373+
probe.expectNoMessage(100 millis)
374+
375+
// If we unwatch the transaction, we will ignore when it's published.
376+
watcher ! UnwatchExternalChannelSpent(tx2.txid, outputIndex2)
377+
bitcoinClient.publishTransaction(spendingTx2)
378+
probe.expectNoMessage(100 millis)
379+
380+
// If we watch again, this will trigger immediately because the transaction is in the mempool.
381+
watcher ! WatchExternalChannelSpent(probe.ref, tx2.txid, outputIndex2, RealShortChannelId(5))
382+
probe.expectMsg(WatchExternalChannelSpentTriggered(RealShortChannelId(5), spendingTx2))
383+
probe.expectNoMessage(100 millis)
384+
385+
// We make the transactions confirm while we're not watching.
386+
watcher ! UnwatchExternalChannelSpent(tx1.txid, outputIndex1)
387+
watcher ! UnwatchExternalChannelSpent(tx2.txid, outputIndex2)
388+
bitcoinClient.getBlockHeight().pipeTo(probe.ref)
389+
val initialBlockHeight = probe.expectMsgType[BlockHeight]
390+
system.eventStream.subscribe(probe.ref, classOf[CurrentBlockHeight])
391+
generateBlocks(1)
392+
awaitCond(probe.expectMsgType[CurrentBlockHeight].blockHeight >= initialBlockHeight + 1)
393+
394+
// If we watch again after confirmation, the watch instantly triggers.
395+
watcher ! WatchExternalChannelSpent(probe.ref, tx1.txid, outputIndex1, RealShortChannelId(3))
396+
probe.expectMsg(WatchExternalChannelSpentTriggered(RealShortChannelId(3), spendingTx1))
397+
probe.expectNoMessage(100 millis)
398+
})
399+
}
400+
353401
test("watch for unknown spent transactions") {
354402
withWatcher(f => {
355403
import f._

eclair-core/src/test/scala/fr/acinq/eclair/router/RouterSpec.scala

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import fr.acinq.eclair.router.Router._
3939
import fr.acinq.eclair.transactions.Scripts
4040
import fr.acinq.eclair.wire.protocol._
4141
import fr.acinq.eclair.{BlockHeight, CltvExpiryDelta, Features, MilliSatoshi, MilliSatoshiLong, RealShortChannelId, ShortChannelId, TestConstants, TimestampSecond, randomBytes32, randomKey}
42+
import org.scalatest.Inside.inside
4243
import scodec.bits._
4344

4445
import scala.concurrent.duration._
@@ -318,9 +319,11 @@ class RouterSpec extends BaseRouterSpec {
318319
probe.expectMsg(PublicNode(node_b, 2, publicChannelCapacity * 2))
319320
}
320321

321-
def spendingTx(node1: PublicKey, node2: PublicKey): Transaction = {
322-
val originalFundingTx = Transaction(version = 0, txIn = Nil, txOut = TxOut(publicChannelCapacity, write(pay2wsh(Scripts.multiSig2of2(node1, node2)))) :: Nil, lockTime = 0)
323-
Transaction(version = 0, txIn = TxIn(outPoint = OutPoint(originalFundingTx,0), signatureScript = write(pay2wsh(Scripts.multiSig2of2(node1, node2))), sequence = 0) :: Nil, txOut = TxOut(publicChannelCapacity, write(pay2wsh(Scripts.multiSig2of2(node1, node1)))) :: Nil, lockTime = 0)
322+
def spendingTx(node1: PublicKey, node2: PublicKey, capacity: Satoshi = publicChannelCapacity): Transaction = {
323+
val fundingScript = write(pay2wsh(Scripts.multiSig2of2(node1, node2)))
324+
val previousFundingTx = Transaction(version = 2, txIn = Nil, txOut = TxOut(capacity, fundingScript) :: Nil, lockTime = 0)
325+
val nextFundingTx = Transaction(version = 0, txIn = TxIn(OutPoint(previousFundingTx, 0), fundingScript, 0) :: Nil, txOut = TxOut(capacity, fundingScript) :: Nil, lockTime = 0)
326+
nextFundingTx
324327
}
325328

326329
test("properly announce lost channels and nodes") { fixture =>
@@ -1098,11 +1101,6 @@ class RouterSpec extends BaseRouterSpec {
10981101
}
10991102
}
11001103

1101-
def spliceTx(node1: PublicKey, node2: PublicKey, newCapacity: Satoshi): Transaction = {
1102-
val originalFundingTx = Transaction(version = 0, txIn = Nil, txOut = TxOut(publicChannelCapacity, write(pay2wsh(Scripts.multiSig2of2(node1, node2)))) :: Nil, lockTime = 0)
1103-
Transaction(version = 0, txIn = TxIn(outPoint = OutPoint(originalFundingTx,0), signatureScript = write(pay2wsh(Scripts.multiSig2of2(node1, node2))), sequence = 0) :: Nil, txOut = TxOut(newCapacity, write(pay2wsh(Scripts.multiSig2of2(node1, node2)))) :: Nil, lockTime = 0)
1104-
}
1105-
11061104
test("update an existing channel after a splice") { fixture =>
11071105
import fixture._
11081106

@@ -1111,15 +1109,23 @@ class RouterSpec extends BaseRouterSpec {
11111109
val peerConnection = TestProbe()
11121110

11131111
// Channel ab is spent by a splice tx.
1114-
val newCapacity = publicChannelCapacity - 100_000.sat
1115-
router ! WatchExternalChannelSpentTriggered(scid_ab, spliceTx(funding_a, funding_b, newCapacity))
1116-
assert(watcher.expectMsgType[WatchTxConfirmed].minDepth == 12)
1112+
val capacity1 = publicChannelCapacity - 100_000.sat
1113+
val spliceTx1 = spendingTx(funding_a, funding_b, capacity1)
1114+
router ! WatchExternalChannelSpentTriggered(scid_ab, spliceTx1)
1115+
inside(watcher.expectMsgType[WatchTxConfirmed]) { w =>
1116+
assert(w.txId == spliceTx1.txid)
1117+
assert(w.minDepth == 12)
1118+
}
11171119
eventListener.expectNoMessage(100 millis)
11181120

11191121
// Channel ab is spent and confirmed by an RBF of splice tx.
1120-
val newCapacity1 = publicChannelCapacity - 100_000.sat - 1000.sat
1121-
router ! WatchExternalChannelSpentTriggered(scid_ab, spliceTx(funding_a, funding_b, newCapacity1))
1122-
assert(watcher.expectMsgType[WatchTxConfirmed].minDepth == 12)
1122+
val capacity2 = publicChannelCapacity - 100_000.sat - 1000.sat
1123+
val spliceTx2 = spendingTx(funding_a, funding_b, capacity2)
1124+
router ! WatchExternalChannelSpentTriggered(scid_ab, spliceTx2)
1125+
inside(watcher.expectMsgType[WatchTxConfirmed]) { w =>
1126+
assert(w.txId == spliceTx2.txid)
1127+
assert(w.minDepth == 12)
1128+
}
11231129
eventListener.expectNoMessage(100 millis)
11241130

11251131
// The splice of channel ab is announced.
@@ -1128,7 +1134,7 @@ class RouterSpec extends BaseRouterSpec {
11281134
peerConnection.send(router, PeerRoutingMessage(peerConnection.ref, remoteNodeId, spliceAnn))
11291135
peerConnection.expectNoMessage(100 millis)
11301136
assert(watcher.expectMsgType[ValidateRequest].ann == spliceAnn)
1131-
watcher.send(router, ValidateResult(spliceAnn, Right(spliceTx(funding_a, funding_b, newCapacity1), UtxoStatus.Unspent)))
1137+
watcher.send(router, ValidateResult(spliceAnn, Right(spliceTx2, UtxoStatus.Unspent)))
11321138
peerConnection.expectMsg(TransportHandler.ReadAck(spliceAnn))
11331139
peerConnection.expectMsg(GossipDecision.Accepted(spliceAnn))
11341140
assert(peerConnection.sender() == router)
@@ -1141,24 +1147,23 @@ class RouterSpec extends BaseRouterSpec {
11411147
val edge_ba = g.getEdge(ChannelDesc(spliceScid, b, a)).get
11421148
assert(g.getEdge(ChannelDesc(scid_ab, a, b)).isEmpty)
11431149
assert(g.getEdge(ChannelDesc(scid_ab, b, a)).isEmpty)
1144-
assert(edge_ab.capacity == newCapacity1 && edge_ba.capacity == newCapacity1)
1150+
assert(edge_ab.capacity == capacity2 && edge_ba.capacity == capacity2)
11451151

11461152
// The channel update for the splice is confirmed and the channel is not removed.
11471153
router ! WatchTxConfirmedTriggered(BlockHeight(0), 0, spendingTx(funding_a, funding_b))
1148-
eventListener.expectMsg(ChannelsDiscovered(SingleChannelDiscovered(spliceAnn, newCapacity1, None, None) :: Nil))
1154+
eventListener.expectMsg(ChannelsDiscovered(SingleChannelDiscovered(spliceAnn, capacity2, None, None) :: Nil))
11491155
eventListener.expectMsg(ChannelLost(scid_ab))
11501156
peerConnection.expectNoMessage(100 millis)
11511157
eventListener.expectNoMessage(100 millis)
11521158

1153-
// The router no longer tracks the parent scid
1159+
// The router no longer tracks the parent scid.
11541160
val probe = TestProbe()
11551161
awaitAssert({
11561162
probe.send(router, GetRouterData)
11571163
val routerData = probe.expectMsgType[Data]
11581164
assert(routerData.spentChannels.isEmpty)
1165+
assert(!routerData.channels.contains(scid_ab))
11591166
})
1160-
11611167
}
11621168

1163-
11641169
}

0 commit comments

Comments
 (0)