@@ -21,7 +21,7 @@ import akka.testkit.{TestFSMRef, TestProbe}
21
21
import fr .acinq .bitcoin .scalacompat .{ByteVector32 , ByteVector64 , SatoshiLong , TxId }
22
22
import fr .acinq .eclair .TestUtils .randomTxId
23
23
import fr .acinq .eclair .blockchain .SingleKeyOnChainWallet
24
- import fr .acinq .eclair .blockchain .bitcoind .ZmqWatcher .{WatchFundingConfirmed , WatchPublished }
24
+ import fr .acinq .eclair .blockchain .bitcoind .ZmqWatcher .{WatchFundingConfirmed , WatchPublished , WatchPublishedTriggered }
25
25
import fr .acinq .eclair .blockchain .fee .FeeratePerKw
26
26
import fr .acinq .eclair .channel ._
27
27
import fr .acinq .eclair .channel .fsm .Channel
@@ -415,6 +415,59 @@ class WaitForDualFundingSignedStateSpec extends TestKitBaseClass with FixtureAny
415
415
reconnect(f, fundingTxId, aliceExpectsCommitSig = true , bobExpectsCommitSig = false )
416
416
}
417
417
418
+ test(" recv INPUT_DISCONNECTED (commit_sig received by Bob, zero-conf)" , Tag (ChannelStateTestsTags .DualFunding ), Tag (ChannelStateTestsTags .ZeroConf ), Tag (ChannelStateTestsTags .AnchorOutputsZeroFeeHtlcTxs )) { f =>
419
+ import f ._
420
+
421
+ alice2bob.expectMsgType[CommitSig ]
422
+ alice2bob.forward(bob)
423
+ bob2alice.expectMsgType[CommitSig ] // Alice doesn't receive Bob's commit_sig
424
+ bob2alice.expectMsgType[TxSignatures ] // Alice doesn't receive Bob's tx_signatures
425
+ awaitCond(alice.stateName == WAIT_FOR_DUAL_FUNDING_SIGNED )
426
+ awaitCond(bob.stateName == WAIT_FOR_DUAL_FUNDING_CONFIRMED )
427
+
428
+ // Note that this case can only happen when Bob doesn't need Alice's signatures to publish the transaction (when
429
+ // Bob was the only one to contribute to the funding transaction).
430
+ val fundingTx = bob.stateData.asInstanceOf [DATA_WAIT_FOR_DUAL_FUNDING_CONFIRMED ].latestFundingTx.sharedTx.tx.buildUnsignedTx()
431
+ assert(bob2blockchain.expectMsgType[WatchPublished ].txId == fundingTx.txid)
432
+ bob ! WatchPublishedTriggered (fundingTx)
433
+ assert(bob2blockchain.expectMsgType[WatchFundingConfirmed ].txId == fundingTx.txid)
434
+ bob2alice.expectMsgType[ChannelReady ]
435
+ awaitCond(bob.stateName == WAIT_FOR_DUAL_FUNDING_READY )
436
+
437
+ alice ! INPUT_DISCONNECTED
438
+ awaitCond(alice.stateName == OFFLINE )
439
+ bob ! INPUT_DISCONNECTED
440
+ awaitCond(bob.stateName == OFFLINE )
441
+
442
+ val listener = TestProbe ()
443
+ alice.underlyingActor.context.system.eventStream.subscribe(listener.ref, classOf [TransactionPublished ])
444
+
445
+ val aliceInit = Init (alice.underlyingActor.nodeParams.features.initFeatures())
446
+ val bobInit = Init (bob.underlyingActor.nodeParams.features.initFeatures())
447
+ alice ! INPUT_RECONNECTED (bob, aliceInit, bobInit)
448
+ bob ! INPUT_RECONNECTED (alice, bobInit, aliceInit)
449
+ val channelReestablishAlice = alice2bob.expectMsgType[ChannelReestablish ]
450
+ assert(channelReestablishAlice.nextFundingTxId_opt.contains(fundingTx.txid))
451
+ assert(channelReestablishAlice.nextLocalCommitmentNumber == 0 )
452
+ alice2bob.forward(bob, channelReestablishAlice)
453
+ val channelReestablishBob = bob2alice.expectMsgType[ChannelReestablish ]
454
+ assert(channelReestablishBob.nextFundingTxId_opt.isEmpty)
455
+ assert(channelReestablishBob.nextLocalCommitmentNumber == 1 )
456
+ bob2alice.forward(alice, channelReestablishBob)
457
+
458
+ bob2alice.expectMsgType[CommitSig ]
459
+ bob2alice.forward(alice)
460
+ bob2alice.expectMsgType[TxSignatures ]
461
+ bob2alice.forward(alice)
462
+ alice2bob.expectMsgType[TxSignatures ]
463
+ alice2bob.forward(bob)
464
+
465
+ awaitCond(alice.stateName == WAIT_FOR_DUAL_FUNDING_CONFIRMED )
466
+ awaitCond(bob.stateName == WAIT_FOR_DUAL_FUNDING_READY )
467
+ assert(alice2blockchain.expectMsgType[WatchFundingConfirmed ].txId == fundingTx.txid)
468
+ assert(listener.expectMsgType[TransactionPublished ].tx.txid == fundingTx.txid)
469
+ }
470
+
418
471
test(" recv INPUT_DISCONNECTED (commit_sig received)" , Tag (ChannelStateTestsTags .DualFunding )) { f =>
419
472
import f ._
420
473
@@ -448,7 +501,7 @@ class WaitForDualFundingSignedStateSpec extends TestKitBaseClass with FixtureAny
448
501
bob2alice.forward(alice)
449
502
bob2alice.expectMsgType[TxSignatures ]
450
503
bob2alice.forward(alice)
451
- alice2bob.expectMsgType[TxSignatures ]
504
+ alice2bob.expectMsgType[TxSignatures ] // Bob doesn't receive Alice's tx_signatures
452
505
awaitCond(alice.stateName == WAIT_FOR_DUAL_FUNDING_CONFIRMED )
453
506
awaitCond(bob.stateName == WAIT_FOR_DUAL_FUNDING_CONFIRMED )
454
507
@@ -472,6 +525,51 @@ class WaitForDualFundingSignedStateSpec extends TestKitBaseClass with FixtureAny
472
525
assert(listener.expectMsgType[TransactionPublished ].tx.txid == fundingTxId)
473
526
}
474
527
528
+ test(" recv INPUT_DISCONNECTED (tx_signatures received, zero-conf)" , Tag (ChannelStateTestsTags .DualFunding ), Tag (ChannelStateTestsTags .ZeroConf ), Tag (ChannelStateTestsTags .AnchorOutputsZeroFeeHtlcTxs )) { f =>
529
+ import f ._
530
+
531
+ val listener = TestProbe ()
532
+ bob.underlyingActor.context.system.eventStream.subscribe(listener.ref, classOf [TransactionPublished ])
533
+
534
+ alice2bob.expectMsgType[CommitSig ]
535
+ alice2bob.forward(bob)
536
+ bob2alice.expectMsgType[CommitSig ]
537
+ bob2alice.forward(alice)
538
+ bob2alice.expectMsgType[TxSignatures ]
539
+ bob2alice.forward(alice)
540
+ alice2bob.expectMsgType[TxSignatures ] // Bob doesn't receive Alice's tx_signatures
541
+ awaitCond(alice.stateName == WAIT_FOR_DUAL_FUNDING_CONFIRMED )
542
+ awaitCond(bob.stateName == WAIT_FOR_DUAL_FUNDING_CONFIRMED )
543
+
544
+ val fundingTx = alice.stateData.asInstanceOf [DATA_WAIT_FOR_DUAL_FUNDING_CONFIRMED ].latestFundingTx.signedTx_opt.get
545
+ assert(alice2blockchain.expectMsgType[WatchPublished ].txId == fundingTx.txid)
546
+ alice ! WatchPublishedTriggered (fundingTx)
547
+ assert(alice2blockchain.expectMsgType[WatchFundingConfirmed ].txId == fundingTx.txid)
548
+ alice2bob.expectMsgType[ChannelReady ]
549
+ awaitCond(alice.stateName == WAIT_FOR_DUAL_FUNDING_READY )
550
+
551
+ alice ! INPUT_DISCONNECTED
552
+ awaitCond(alice.stateName == OFFLINE )
553
+ bob ! INPUT_DISCONNECTED
554
+ awaitCond(bob.stateName == OFFLINE )
555
+
556
+ val aliceInit = Init (alice.underlyingActor.nodeParams.features.initFeatures())
557
+ val bobInit = Init (bob.underlyingActor.nodeParams.features.initFeatures())
558
+ alice ! INPUT_RECONNECTED (bob, aliceInit, bobInit)
559
+ bob ! INPUT_RECONNECTED (alice, bobInit, aliceInit)
560
+
561
+ assert(alice2bob.expectMsgType[ChannelReestablish ].nextFundingTxId_opt.isEmpty)
562
+ alice2bob.forward(bob)
563
+ assert(bob2alice.expectMsgType[ChannelReestablish ].nextFundingTxId_opt.contains(fundingTx.txid))
564
+ bob2alice.forward(alice)
565
+ alice2bob.expectMsgType[TxSignatures ]
566
+ alice2bob.forward(bob)
567
+ alice2bob.expectMsgType[ChannelReady ]
568
+ alice2bob.forward(bob)
569
+ assert(bob2blockchain.expectMsgType[WatchPublished ].txId == fundingTx.txid)
570
+ assert(listener.expectMsgType[TransactionPublished ].tx.txid == fundingTx.txid)
571
+ }
572
+
475
573
private def reconnect (f : FixtureParam , fundingTxId : TxId , aliceExpectsCommitSig : Boolean , bobExpectsCommitSig : Boolean ): Unit = {
476
574
import f ._
477
575
0 commit comments