@@ -68,98 +68,103 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
68
68
69
69
private val defaultSpliceOutScriptPubKey = hex " 0020aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa "
70
70
71
- private def useQuiescence (f : FixtureParam ): Boolean = f.alice .stateData.asInstanceOf [ChannelDataWithCommitments ].commitments.params.useQuiescence
71
+ private def useQuiescence (s : TestFSMRef [ ChannelState , ChannelData , Channel ] ): Boolean = s .stateData.asInstanceOf [ChannelDataWithCommitments ].commitments.params.useQuiescence
72
72
73
- private def initiateSpliceWithoutSigs (f : FixtureParam , spliceIn_opt : Option [SpliceIn ] = None , spliceOut_opt : Option [SpliceOut ] = None ): TestProbe = {
74
- import f ._
73
+ private def useQuiescence (f : FixtureParam ): Boolean = useQuiescence(f.alice)
75
74
75
+ private def initiateSpliceWithoutSigs (s : TestFSMRef [ChannelState , ChannelData , Channel ], r : TestFSMRef [ChannelState , ChannelData , Channel ], s2r : TestProbe , r2s : TestProbe , spliceIn_opt : Option [SpliceIn ], spliceOut_opt : Option [SpliceOut ]): TestProbe = {
76
76
val sender = TestProbe ()
77
77
val cmd = CMD_SPLICE (sender.ref, spliceIn_opt, spliceOut_opt)
78
- alice ! cmd
79
- if (useQuiescence(f )) {
80
- exchangeStfu(f )
78
+ s ! cmd
79
+ if (useQuiescence(s )) {
80
+ exchangeStfu(s, r, s2r, r2s )
81
81
}
82
- alice2bob .expectMsgType[SpliceInit ]
83
- alice2bob .forward(bob )
84
- bob2alice .expectMsgType[SpliceAck ]
85
- bob2alice .forward(alice )
86
-
87
- alice2bob .expectMsgType[TxAddInput ]
88
- alice2bob .forward(bob )
89
- bob2alice .expectMsgType[TxComplete ]
90
- bob2alice .forward(alice )
82
+ s2r .expectMsgType[SpliceInit ]
83
+ s2r .forward(r )
84
+ r2s .expectMsgType[SpliceAck ]
85
+ r2s .forward(s )
86
+
87
+ s2r .expectMsgType[TxAddInput ]
88
+ s2r .forward(r )
89
+ r2s .expectMsgType[TxComplete ]
90
+ r2s .forward(s )
91
91
if (spliceIn_opt.isDefined) {
92
- alice2bob .expectMsgType[TxAddInput ]
93
- alice2bob .forward(bob )
94
- bob2alice .expectMsgType[TxComplete ]
95
- bob2alice .forward(alice )
96
- alice2bob .expectMsgType[TxAddOutput ]
97
- alice2bob .forward(bob )
98
- bob2alice .expectMsgType[TxComplete ]
99
- bob2alice .forward(alice )
92
+ s2r .expectMsgType[TxAddInput ]
93
+ s2r .forward(r )
94
+ r2s .expectMsgType[TxComplete ]
95
+ r2s .forward(s )
96
+ s2r .expectMsgType[TxAddOutput ]
97
+ s2r .forward(r )
98
+ r2s .expectMsgType[TxComplete ]
99
+ r2s .forward(s )
100
100
}
101
101
if (spliceOut_opt.isDefined) {
102
- alice2bob .expectMsgType[TxAddOutput ]
103
- alice2bob .forward(bob )
104
- bob2alice .expectMsgType[TxComplete ]
105
- bob2alice .forward(alice )
102
+ s2r .expectMsgType[TxAddOutput ]
103
+ s2r .forward(r )
104
+ r2s .expectMsgType[TxComplete ]
105
+ r2s .forward(s )
106
106
}
107
- alice2bob .expectMsgType[TxAddOutput ]
108
- alice2bob .forward(bob )
109
- bob2alice .expectMsgType[TxComplete ]
110
- bob2alice .forward(alice )
111
- alice2bob .expectMsgType[TxComplete ]
112
- alice2bob .forward(bob )
107
+ s2r .expectMsgType[TxAddOutput ]
108
+ s2r .forward(r )
109
+ r2s .expectMsgType[TxComplete ]
110
+ r2s .forward(s )
111
+ s2r .expectMsgType[TxComplete ]
112
+ s2r .forward(r )
113
113
sender
114
114
}
115
115
116
- private def exchangeSpliceSigs (f : FixtureParam , sender : TestProbe ): Transaction = {
117
- import f ._
116
+ private def initiateSpliceWithoutSigs (f : FixtureParam , spliceIn_opt : Option [SpliceIn ] = None , spliceOut_opt : Option [SpliceOut ] = None ): TestProbe = initiateSpliceWithoutSigs(f.alice, f.bob, f.alice2bob, f.bob2alice, spliceIn_opt, spliceOut_opt)
118
117
119
- val commitSigBob = bob2alice.fishForMessage() {
118
+ private def exchangeSpliceSigs (s : TestFSMRef [ChannelState , ChannelData , Channel ], r : TestFSMRef [ChannelState , ChannelData , Channel ], s2r : TestProbe , r2s : TestProbe , sender : TestProbe ): Transaction = {
119
+ val commitSigR = r2s.fishForMessage() {
120
120
case _ : CommitSig => true
121
121
case _ : ChannelReady => false
122
122
}
123
- bob2alice .forward(alice, commitSigBob )
124
- val commitSigAlice = alice2bob .fishForMessage() {
123
+ r2s .forward(s, commitSigR )
124
+ val commitSigS = s2r .fishForMessage() {
125
125
case _ : CommitSig => true
126
126
case _ : ChannelReady => false
127
127
}
128
- alice2bob .forward(bob, commitSigAlice )
128
+ s2r .forward(r, commitSigS )
129
129
130
- val txSigsBob = bob2alice .fishForMessage() {
130
+ val txSigsR = r2s .fishForMessage() {
131
131
case _ : TxSignatures => true
132
132
case _ : ChannelUpdate => false
133
133
}
134
- bob2alice .forward(alice, txSigsBob )
135
- val txSigsAlice = alice2bob .fishForMessage() {
134
+ r2s .forward(s, txSigsR )
135
+ val txSigsS = s2r .fishForMessage() {
136
136
case _ : TxSignatures => true
137
137
case _ : ChannelUpdate => false
138
138
}
139
- alice2bob .forward(bob, txSigsAlice )
139
+ s2r .forward(r, txSigsS )
140
140
141
141
sender.expectMsgType[RES_SPLICE ]
142
142
143
- awaitCond(alice .stateData.asInstanceOf [DATA_NORMAL ].spliceStatus == SpliceStatus .NoSplice )
144
- awaitCond(bob .stateData.asInstanceOf [DATA_NORMAL ].spliceStatus == SpliceStatus .NoSplice )
145
- awaitCond(alice .stateData.asInstanceOf [DATA_NORMAL ].commitments.latest.localFundingStatus.asInstanceOf [DualFundedUnconfirmedFundingTx ].sharedTx.isInstanceOf [FullySignedSharedTransaction ])
146
- awaitCond(bob .stateData.asInstanceOf [DATA_NORMAL ].commitments.latest.localFundingStatus.asInstanceOf [DualFundedUnconfirmedFundingTx ].sharedTx.isInstanceOf [FullySignedSharedTransaction ])
147
- alice .stateData.asInstanceOf [DATA_NORMAL ].commitments.latest.localFundingStatus.signedTx_opt.get
143
+ awaitCond(s .stateData.asInstanceOf [DATA_NORMAL ].spliceStatus == SpliceStatus .NoSplice )
144
+ awaitCond(r .stateData.asInstanceOf [DATA_NORMAL ].spliceStatus == SpliceStatus .NoSplice )
145
+ awaitCond(s .stateData.asInstanceOf [DATA_NORMAL ].commitments.latest.localFundingStatus.asInstanceOf [DualFundedUnconfirmedFundingTx ].sharedTx.isInstanceOf [FullySignedSharedTransaction ])
146
+ awaitCond(r .stateData.asInstanceOf [DATA_NORMAL ].commitments.latest.localFundingStatus.asInstanceOf [DualFundedUnconfirmedFundingTx ].sharedTx.isInstanceOf [FullySignedSharedTransaction ])
147
+ s .stateData.asInstanceOf [DATA_NORMAL ].commitments.latest.localFundingStatus.signedTx_opt.get
148
148
}
149
149
150
- private def initiateSplice (f : FixtureParam , spliceIn_opt : Option [SpliceIn ] = None , spliceOut_opt : Option [SpliceOut ] = None ): Transaction = {
151
- val sender = initiateSpliceWithoutSigs(f, spliceIn_opt, spliceOut_opt)
152
- exchangeSpliceSigs(f, sender)
150
+ private def exchangeSpliceSigs (f : FixtureParam , sender : TestProbe ): Transaction = exchangeSpliceSigs(f.alice, f.bob, f.alice2bob, f.bob2alice, sender)
151
+
152
+ private def initiateSplice (s : TestFSMRef [ChannelState , ChannelData , Channel ], r : TestFSMRef [ChannelState , ChannelData , Channel ], s2r : TestProbe , r2s : TestProbe , spliceIn_opt : Option [SpliceIn ], spliceOut_opt : Option [SpliceOut ]): Transaction = {
153
+ val sender = initiateSpliceWithoutSigs(s, r, s2r, r2s, spliceIn_opt, spliceOut_opt)
154
+ exchangeSpliceSigs(s, r, s2r, r2s, sender)
153
155
}
154
156
155
- private def exchangeStfu (f : FixtureParam ): Unit = {
156
- import f ._
157
- alice2bob.expectMsgType[Stfu ]
158
- alice2bob.forward(bob)
159
- bob2alice.expectMsgType[Stfu ]
160
- bob2alice.forward(alice)
157
+ private def initiateSplice (f : FixtureParam , spliceIn_opt : Option [SpliceIn ] = None , spliceOut_opt : Option [SpliceOut ] = None ): Transaction = initiateSplice(f.alice, f.bob, f.alice2bob, f.bob2alice, spliceIn_opt, spliceOut_opt)
158
+
159
+ private def exchangeStfu (s : TestFSMRef [ChannelState , ChannelData , Channel ], r : TestFSMRef [ChannelState , ChannelData , Channel ], s2r : TestProbe , r2s : TestProbe ): Unit = {
160
+ s2r.expectMsgType[Stfu ]
161
+ s2r.forward(r)
162
+ r2s.expectMsgType[Stfu ]
163
+ r2s.forward(s)
161
164
}
162
165
166
+ private def exchangeStfu (f : FixtureParam ): Unit = exchangeStfu(f.alice, f.bob, f.alice2bob, f.bob2alice)
167
+
163
168
case class TestHtlcs (aliceToBob : Seq [(ByteVector32 , UpdateAddHtlc )], bobToAlice : Seq [(ByteVector32 , UpdateAddHtlc )])
164
169
165
170
private def setupHtlcs (f : FixtureParam ): TestHtlcs = {
@@ -416,6 +421,37 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
416
421
awaitCond(bob.stateData.asInstanceOf [DATA_NORMAL ].spliceStatus == SpliceStatus .NoSplice )
417
422
}
418
423
424
+ test(" recv CMD_SPLICE (remote splices in which takes us below reserve)" , Tag (ChannelStateTestsTags .NoMaxHtlcValueInFlight )) { f =>
425
+ import f ._
426
+
427
+ assert(alice.stateData.asInstanceOf [DATA_NORMAL ].commitments.latest.localCommit.spec.toLocal == 800_000_000 .msat)
428
+ val (r1, htlc1) = addHtlc(750_000_000 msat, alice, bob, alice2bob, bob2alice)
429
+ crossSign(alice, bob, alice2bob, bob2alice)
430
+ fulfillHtlc(htlc1.id, r1, bob, alice, bob2alice, alice2bob)
431
+ crossSign(bob, alice, bob2alice, alice2bob)
432
+
433
+ // Bob makes a large splice: Alice doesn't meet the new reserve requirements, but she met the previous one, so we allow this.
434
+ initiateSplice(bob, alice, bob2alice, alice2bob, spliceIn_opt = Some (SpliceIn (4_000_000 sat)), spliceOut_opt = None )
435
+ val postSpliceState = alice.stateData.asInstanceOf [DATA_NORMAL ]
436
+ assert(postSpliceState.commitments.latest.localCommit.spec.toLocal < postSpliceState.commitments.latest.localChannelReserve)
437
+
438
+ // Since Alice is below the reserve and most of the funds are on Bob's side, Alice cannot send HTLCs.
439
+ val probe = TestProbe ()
440
+ val (_, cmd) = makeCmdAdd(5_000_000 msat, bob.nodeParams.nodeId, bob.nodeParams.currentBlockHeight)
441
+ alice ! cmd.copy(replyTo = probe.ref)
442
+ probe.expectMsgType[RES_ADD_FAILED [InsufficientFunds ]]
443
+
444
+ // But Bob can send HTLCs to take Alice above the reserve.
445
+ val (r2, htlc2) = addHtlc(50_000_000 msat, bob, alice, bob2alice, alice2bob)
446
+ crossSign(bob, alice, bob2alice, alice2bob)
447
+ fulfillHtlc(htlc2.id, r2, alice, bob, alice2bob, bob2alice)
448
+ crossSign(alice, bob, alice2bob, bob2alice)
449
+
450
+ // Alice can now send HTLCs as well.
451
+ addHtlc(10_000_000 msat, alice, bob, alice2bob, bob2alice)
452
+ crossSign(alice, bob, alice2bob, bob2alice)
453
+ }
454
+
419
455
def testSpliceInAndOutCmd (f : FixtureParam ): Unit = {
420
456
val htlcs = setupHtlcs(f)
421
457
0 commit comments