@@ -231,6 +231,53 @@ class OfflineStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
231
231
awaitCond(bob.stateName == NORMAL )
232
232
}
233
233
234
+ test(" resume htlc settlement" ) { f =>
235
+ import f ._
236
+
237
+ // Successfully send a first payment.
238
+ val (r1, htlc1) = addHtlc(15_000_000 msat, bob, alice, bob2alice, alice2bob)
239
+ crossSign(bob, alice, bob2alice, alice2bob)
240
+ fulfillHtlc(htlc1.id, r1, alice, bob, alice2bob, bob2alice)
241
+ crossSign(alice, bob, alice2bob, bob2alice)
242
+
243
+ // Send a second payment and disconnect right after the fulfill was signed.
244
+ val (r2, htlc2) = addHtlc(25_000_000 msat, bob, alice, bob2alice, alice2bob)
245
+ crossSign(bob, alice, bob2alice, alice2bob)
246
+ fulfillHtlc(htlc2.id, r2, alice, bob, alice2bob, bob2alice)
247
+ val sender = TestProbe ()
248
+ alice ! CMD_SIGN (Some (sender.ref))
249
+ sender.expectMsgType[RES_SUCCESS [CMD_SIGN ]]
250
+ alice2bob.expectMsgType[CommitSig ]
251
+ alice2bob.forward(bob)
252
+ val revB = bob2alice.expectMsgType[RevokeAndAck ]
253
+ bob2alice.expectMsgType[CommitSig ]
254
+ disconnect(alice, bob)
255
+
256
+ reconnect(alice, bob, alice2bob, bob2alice)
257
+ val reestablishA = alice2bob.expectMsgType[ChannelReestablish ]
258
+ assert(reestablishA.nextLocalCommitmentNumber === 4 )
259
+ assert(reestablishA.nextRemoteRevocationNumber === 3 )
260
+ val reestablishB = bob2alice.expectMsgType[ChannelReestablish ]
261
+ assert(reestablishB.nextLocalCommitmentNumber === 5 )
262
+ assert(reestablishB.nextRemoteRevocationNumber === 3 )
263
+
264
+ bob2alice.forward(alice, reestablishB)
265
+ // alice does not re-send messages bob already received
266
+ alice2bob.expectNoMessage(100 millis)
267
+
268
+ alice2bob.forward(bob, reestablishA)
269
+ // bob re-sends its revocation and signature, alice then completes the update
270
+ bob2alice.expectMsg(revB)
271
+ bob2alice.forward(alice)
272
+ bob2alice.expectMsgType[CommitSig ]
273
+ bob2alice.forward(alice)
274
+ alice2bob.expectMsgType[RevokeAndAck ]
275
+ alice2bob.forward(bob)
276
+
277
+ assert(alice.stateData.asInstanceOf [DATA_NORMAL ].commitments.localCommit.index === 4 )
278
+ assert(bob.stateData.asInstanceOf [DATA_NORMAL ].commitments.localCommit.index === 4 )
279
+ }
280
+
234
281
test(" discover that we have a revoked commitment" ) { f =>
235
282
import f ._
236
283
@@ -258,20 +305,21 @@ class OfflineStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
258
305
reconnect(alice, bob, alice2bob, bob2alice)
259
306
260
307
// peers exchange channel_reestablish messages
261
- alice2bob.expectMsgType[ChannelReestablish ]
262
- bob2alice.expectMsgType[ChannelReestablish ]
308
+ val reestablishA = alice2bob.expectMsgType[ChannelReestablish ]
309
+ val reestablishB = bob2alice.expectMsgType[ChannelReestablish ]
263
310
264
311
// alice then realizes it has an old state...
265
- bob2alice.forward(alice)
312
+ bob2alice.forward(alice, reestablishB )
266
313
// ... and ask bob to publish its current commitment
267
314
val error = alice2bob.expectMsgType[Error ]
268
315
assert(error === Error (channelId(alice), PleasePublishYourCommitment (channelId(alice)).getMessage))
269
316
270
317
// alice now waits for bob to publish its commitment
271
318
awaitCond(alice.stateName == WAIT_FOR_REMOTE_PUBLISH_FUTURE_COMMITMENT )
272
319
273
- // bob is nice and publishes its commitment
274
- val bobCommitTx = bob.stateData.asInstanceOf [DATA_NORMAL ].commitments.localCommit.commitTxAndRemoteSig.commitTx.tx
320
+ // bob publishes its commitment when it detects that alice has an outdated commitment
321
+ alice2bob.forward(bob, reestablishA)
322
+ val bobCommitTx = bob2blockchain.expectMsgType[PublishRawTx ].tx
275
323
alice ! WatchFundingSpentTriggered (bobCommitTx)
276
324
277
325
// alice is able to claim its main output
0 commit comments