@@ -21,7 +21,7 @@ import akka.testkit.{TestFSMRef, TestProbe}
2121import fr .acinq .bitcoin .scalacompat .{ByteVector32 , ByteVector64 , SatoshiLong , TxId }
2222import fr .acinq .eclair .TestUtils .randomTxId
2323import 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 }
2525import fr .acinq .eclair .blockchain .fee .FeeratePerKw
2626import fr .acinq .eclair .channel ._
2727import fr .acinq .eclair .channel .fsm .Channel
@@ -415,6 +415,59 @@ class WaitForDualFundingSignedStateSpec extends TestKitBaseClass with FixtureAny
415415 reconnect(f, fundingTxId, aliceExpectsCommitSig = true , bobExpectsCommitSig = false )
416416 }
417417
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+
418471 test(" recv INPUT_DISCONNECTED (commit_sig received)" , Tag (ChannelStateTestsTags .DualFunding )) { f =>
419472 import f ._
420473
@@ -448,7 +501,7 @@ class WaitForDualFundingSignedStateSpec extends TestKitBaseClass with FixtureAny
448501 bob2alice.forward(alice)
449502 bob2alice.expectMsgType[TxSignatures ]
450503 bob2alice.forward(alice)
451- alice2bob.expectMsgType[TxSignatures ]
504+ alice2bob.expectMsgType[TxSignatures ] // Bob doesn't receive Alice's tx_signatures
452505 awaitCond(alice.stateName == WAIT_FOR_DUAL_FUNDING_CONFIRMED )
453506 awaitCond(bob.stateName == WAIT_FOR_DUAL_FUNDING_CONFIRMED )
454507
@@ -472,6 +525,51 @@ class WaitForDualFundingSignedStateSpec extends TestKitBaseClass with FixtureAny
472525 assert(listener.expectMsgType[TransactionPublished ].tx.txid == fundingTxId)
473526 }
474527
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+
475573 private def reconnect (f : FixtureParam , fundingTxId : TxId , aliceExpectsCommitSig : Boolean , bobExpectsCommitSig : Boolean ): Unit = {
476574 import f ._
477575
0 commit comments