diff --git a/htlcswitch/link_isolated_test.go b/htlcswitch/link_isolated_test.go new file mode 100644 index 00000000..8f059d74 --- /dev/null +++ b/htlcswitch/link_isolated_test.go @@ -0,0 +1,244 @@ +package htlcswitch + +import ( + "crypto/sha256" + "testing" + "time" + + "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwire" +) + +type linkTestContext struct { + t *testing.T + + aliceLink ChannelLink + bobChannel *lnwallet.LightningChannel + aliceMsgs <-chan lnwire.Message +} + +// sendHtlcBobToAlice sends an HTLC from Bob to Alice, that pays to a preimage +// already in Alice's registry. +func (l *linkTestContext) sendHtlcBobToAlice(htlc *lnwire.UpdateAddHTLC) { + l.t.Helper() + + _, err := l.bobChannel.AddHTLC(htlc, nil) + if err != nil { + l.t.Fatalf("bob failed adding htlc: %v", err) + } + + l.aliceLink.HandleChannelUpdate(htlc) +} + +// sendHtlcAliceToBob sends an HTLC from Alice to Bob, by first committing the +// HTLC in the circuit map, then delivering the outgoing packet to Alice's link. +// The HTLC will be sent to Bob via Alice's message stream. +func (l *linkTestContext) sendHtlcAliceToBob(htlcID int, + htlc *lnwire.UpdateAddHTLC) { + + l.t.Helper() + + circuitMap := l.aliceLink.(*channelLink).cfg.Switch.circuits + fwdActions, err := circuitMap.CommitCircuits( + &PaymentCircuit{ + Incoming: CircuitKey{ + HtlcID: uint64(htlcID), + }, + PaymentHash: htlc.PaymentHash, + }, + ) + if err != nil { + l.t.Fatalf("unable to commit circuit: %v", err) + } + + if len(fwdActions.Adds) != 1 { + l.t.Fatalf("expected 1 adds, found %d", len(fwdActions.Adds)) + } + + err = l.aliceLink.HandleSwitchPacket(&htlcPacket{ + incomingHTLCID: uint64(htlcID), + htlc: htlc, + }) + if err != nil { + l.t.Fatal(err) + } +} + +// receiveHtlcAliceToBob pulls the next message from Alice's message stream, +// asserts that it is an UpdateAddHTLC, then applies it to Bob's state machine. +func (l *linkTestContext) receiveHtlcAliceToBob() { + l.t.Helper() + + var msg lnwire.Message + select { + case msg = <-l.aliceMsgs: + case <-time.After(15 * time.Second): + l.t.Fatalf("did not received htlc from alice") + } + + htlcAdd, ok := msg.(*lnwire.UpdateAddHTLC) + if !ok { + l.t.Fatalf("expected UpdateAddHTLC, got %T", msg) + } + + _, err := l.bobChannel.ReceiveHTLC(htlcAdd) + if err != nil { + l.t.Fatalf("bob failed receiving htlc: %v", err) + } +} + +// sendCommitSigBobToAlice makes Bob sign a new commitment and send it to +// Alice, asserting that it signs expHtlcs number of HTLCs. +func (l *linkTestContext) sendCommitSigBobToAlice(expHtlcs int) { + l.t.Helper() + + sig, htlcSigs, _, err := l.bobChannel.SignNextCommitment() + if err != nil { + l.t.Fatalf("error signing commitment: %v", err) + } + + commitSig := &lnwire.CommitSig{ + CommitSig: sig, + HtlcSigs: htlcSigs, + } + + if len(commitSig.HtlcSigs) != expHtlcs { + l.t.Fatalf("Expected %d htlc sigs, got %d", expHtlcs, + len(commitSig.HtlcSigs)) + } + + l.aliceLink.HandleChannelUpdate(commitSig) +} + +// receiveRevAndAckAliceToBob waits for Alice to send a RevAndAck to Bob, then +// hands this to Bob. +func (l *linkTestContext) receiveRevAndAckAliceToBob() { + l.t.Helper() + + var msg lnwire.Message + select { + case msg = <-l.aliceMsgs: + case <-time.After(15 * time.Second): + l.t.Fatalf("did not receive message") + } + + rev, ok := msg.(*lnwire.RevokeAndAck) + if !ok { + l.t.Fatalf("expected RevokeAndAck, got %T", msg) + } + + _, _, _, _, err := l.bobChannel.ReceiveRevocation(rev) + if err != nil { + l.t.Fatalf("bob failed receiving revocation: %v", err) + } +} + +// receiveCommitSigAliceToBob waits for Alice to send a CommitSig to Bob, +// signing expHtlcs numbers of HTLCs, then hands this to Bob. +func (l *linkTestContext) receiveCommitSigAliceToBob(expHtlcs int) { + l.t.Helper() + + var msg lnwire.Message + select { + case msg = <-l.aliceMsgs: + case <-time.After(15 * time.Second): + l.t.Fatalf("did not receive message") + } + + comSig, ok := msg.(*lnwire.CommitSig) + if !ok { + l.t.Fatalf("expected CommitSig, got %T", msg) + } + + if len(comSig.HtlcSigs) != expHtlcs { + l.t.Fatalf("expected %d htlc sigs, got %d", expHtlcs, + len(comSig.HtlcSigs)) + } + err := l.bobChannel.ReceiveNewCommitment(comSig.CommitSig, + comSig.HtlcSigs) + if err != nil { + l.t.Fatalf("bob failed receiving commitment: %v", err) + } +} + +// sendRevAndAckBobToAlice make Bob revoke his current commitment, then hand +// the RevokeAndAck to Alice. +func (l *linkTestContext) sendRevAndAckBobToAlice() { + l.t.Helper() + + rev, _, err := l.bobChannel.RevokeCurrentCommitment() + if err != nil { + l.t.Fatalf("unable to revoke commitment: %v", err) + } + + l.aliceLink.HandleChannelUpdate(rev) +} + +// receiveSettleAliceToBob waits for Alice to send a HTLC settle message to +// Bob, then hands this to Bob. +func (l *linkTestContext) receiveSettleAliceToBob() { + l.t.Helper() + + var msg lnwire.Message + select { + case msg = <-l.aliceMsgs: + case <-time.After(15 * time.Second): + l.t.Fatalf("did not receive message") + } + + settleMsg, ok := msg.(*lnwire.UpdateFulfillHTLC) + if !ok { + l.t.Fatalf("expected UpdateFulfillHTLC, got %T", msg) + } + + err := l.bobChannel.ReceiveHTLCSettle(settleMsg.PaymentPreimage, + settleMsg.ID) + if err != nil { + l.t.Fatalf("failed settling htlc: %v", err) + } +} + +// sendSettleBobToAlice settles an HTLC on Bob's state machine, then sends an +// UpdateFulfillHTLC message to Alice's upstream inbox. +func (l *linkTestContext) sendSettleBobToAlice(htlcID uint64, + preimage lntypes.Preimage) { + + l.t.Helper() + + err := l.bobChannel.SettleHTLC(preimage, htlcID, nil, nil, nil) + if err != nil { + l.t.Fatalf("alice failed settling htlc id=%d hash=%x", + htlcID, sha256.Sum256(preimage[:])) + } + + settle := &lnwire.UpdateFulfillHTLC{ + ID: htlcID, + PaymentPreimage: preimage, + } + + l.aliceLink.HandleChannelUpdate(settle) +} + +// receiveSettleAliceToBob waits for Alice to send a HTLC settle message to +// Bob, then hands this to Bob. +func (l *linkTestContext) receiveFailAliceToBob() { + l.t.Helper() + + var msg lnwire.Message + select { + case msg = <-l.aliceMsgs: + case <-time.After(15 * time.Second): + l.t.Fatalf("did not receive message") + } + + failMsg, ok := msg.(*lnwire.UpdateFailHTLC) + if !ok { + l.t.Fatalf("expected UpdateFailHTLC, got %T", msg) + } + + err := l.bobChannel.ReceiveFailHTLC(failMsg.ID, failMsg.Reason) + if err != nil { + l.t.Fatalf("unable to apply received fail htlc: %v", err) + } +} diff --git a/htlcswitch/link_test.go b/htlcswitch/link_test.go index b21ad5c2..c6bc3be1 100644 --- a/htlcswitch/link_test.go +++ b/htlcswitch/link_test.go @@ -4378,248 +4378,6 @@ func generateHtlcAndInvoice(t *testing.T, return htlc, invoice } -// sendHtlcBobToAlice sends an HTLC from Bob to Alice, that pays to a preimage -// already in Alice's registry. -func sendHtlcBobToAlice(t *testing.T, aliceLink ChannelLink, - bobChannel *lnwallet.LightningChannel, htlc *lnwire.UpdateAddHTLC) { - - t.Helper() - - _, err := bobChannel.AddHTLC(htlc, nil) - if err != nil { - t.Fatalf("bob failed adding htlc: %v", err) - } - - aliceLink.HandleChannelUpdate(htlc) -} - -// sendHtlcAliceToBob sends an HTLC from Alice to Bob, by first committing the -// HTLC in the circuit map, then delivering the outgoing packet to Alice's link. -// The HTLC will be sent to Bob via Alice's message stream. -func sendHtlcAliceToBob(t *testing.T, aliceLink ChannelLink, htlcID int, - htlc *lnwire.UpdateAddHTLC) { - - t.Helper() - - circuitMap := aliceLink.(*channelLink).cfg.Switch.circuits - fwdActions, err := circuitMap.CommitCircuits( - &PaymentCircuit{ - Incoming: CircuitKey{ - HtlcID: uint64(htlcID), - }, - PaymentHash: htlc.PaymentHash, - }, - ) - if err != nil { - t.Fatalf("unable to commit circuit: %v", err) - } - - if len(fwdActions.Adds) != 1 { - t.Fatalf("expected 1 adds, found %d", len(fwdActions.Adds)) - } - - aliceLink.HandleSwitchPacket(&htlcPacket{ - incomingHTLCID: uint64(htlcID), - htlc: htlc, - }) - -} - -// receiveHtlcAliceToBob pulls the next message from Alice's message stream, -// asserts that it is an UpdateAddHTLC, then applies it to Bob's state machine. -func receiveHtlcAliceToBob(t *testing.T, aliceMsgs <-chan lnwire.Message, - bobChannel *lnwallet.LightningChannel) { - - t.Helper() - - var msg lnwire.Message - select { - case msg = <-aliceMsgs: - case <-time.After(15 * time.Second): - t.Fatalf("did not received htlc from alice") - } - - htlcAdd, ok := msg.(*lnwire.UpdateAddHTLC) - if !ok { - t.Fatalf("expected UpdateAddHTLC, got %T", msg) - } - - _, err := bobChannel.ReceiveHTLC(htlcAdd) - if err != nil { - t.Fatalf("bob failed receiving htlc: %v", err) - } -} - -// sendCommitSigBobToAlice makes Bob sign a new commitment and send it to -// Alice, asserting that it signs expHtlcs number of HTLCs. -func sendCommitSigBobToAlice(t *testing.T, aliceLink ChannelLink, - bobChannel *lnwallet.LightningChannel, expHtlcs int) { - - t.Helper() - - sig, htlcSigs, _, err := bobChannel.SignNextCommitment() - if err != nil { - t.Fatalf("error signing commitment: %v", err) - } - - commitSig := &lnwire.CommitSig{ - CommitSig: sig, - HtlcSigs: htlcSigs, - } - - if len(commitSig.HtlcSigs) != expHtlcs { - t.Fatalf("Expected %d htlc sigs, got %d", expHtlcs, - len(commitSig.HtlcSigs)) - } - - aliceLink.HandleChannelUpdate(commitSig) -} - -// receiveRevAndAckAliceToBob waits for Alice to send a RevAndAck to Bob, then -// hands this to Bob. -func receiveRevAndAckAliceToBob(t *testing.T, aliceMsgs chan lnwire.Message, - aliceLink ChannelLink, - bobChannel *lnwallet.LightningChannel) { - - t.Helper() - - var msg lnwire.Message - select { - case msg = <-aliceMsgs: - case <-time.After(15 * time.Second): - t.Fatalf("did not receive message") - } - - rev, ok := msg.(*lnwire.RevokeAndAck) - if !ok { - t.Fatalf("expected RevokeAndAck, got %T", msg) - } - - _, _, _, _, err := bobChannel.ReceiveRevocation(rev) - if err != nil { - t.Fatalf("bob failed receiving revocation: %v", err) - } -} - -// receiveCommitSigAliceToBob waits for Alice to send a CommitSig to Bob, -// signing expHtlcs numbers of HTLCs, then hands this to Bob. -func receiveCommitSigAliceToBob(t *testing.T, aliceMsgs chan lnwire.Message, - aliceLink ChannelLink, bobChannel *lnwallet.LightningChannel, - expHtlcs int) { - - t.Helper() - - var msg lnwire.Message - select { - case msg = <-aliceMsgs: - case <-time.After(15 * time.Second): - t.Fatalf("did not receive message") - } - - comSig, ok := msg.(*lnwire.CommitSig) - if !ok { - t.Fatalf("expected CommitSig, got %T", msg) - } - - if len(comSig.HtlcSigs) != expHtlcs { - t.Fatalf("expected %d htlc sigs, got %d", expHtlcs, - len(comSig.HtlcSigs)) - } - err := bobChannel.ReceiveNewCommitment(comSig.CommitSig, - comSig.HtlcSigs) - if err != nil { - t.Fatalf("bob failed receiving commitment: %v", err) - } -} - -// sendRevAndAckBobToAlice make Bob revoke his current commitment, then hand -// the RevokeAndAck to Alice. -func sendRevAndAckBobToAlice(t *testing.T, aliceLink ChannelLink, - bobChannel *lnwallet.LightningChannel) { - - t.Helper() - - rev, _, err := bobChannel.RevokeCurrentCommitment() - if err != nil { - t.Fatalf("unable to revoke commitment: %v", err) - } - - aliceLink.HandleChannelUpdate(rev) -} - -// receiveSettleAliceToBob waits for Alice to send a HTLC settle message to -// Bob, then hands this to Bob. -func receiveSettleAliceToBob(t *testing.T, aliceMsgs chan lnwire.Message, - aliceLink ChannelLink, bobChannel *lnwallet.LightningChannel) { - - t.Helper() - - var msg lnwire.Message - select { - case msg = <-aliceMsgs: - case <-time.After(15 * time.Second): - t.Fatalf("did not receive message") - } - - settleMsg, ok := msg.(*lnwire.UpdateFulfillHTLC) - if !ok { - t.Fatalf("expected UpdateFulfillHTLC, got %T", msg) - } - - err := bobChannel.ReceiveHTLCSettle(settleMsg.PaymentPreimage, - settleMsg.ID) - if err != nil { - t.Fatalf("failed settling htlc: %v", err) - } -} - -// sendSettleBobToAlice settles an HTLC on Bob's state machine, then sends an -// UpdateFulfillHTLC message to Alice's upstream inbox. -func sendSettleBobToAlice(t *testing.T, aliceLink ChannelLink, - bobChannel *lnwallet.LightningChannel, htlcID uint64, - preimage lntypes.Preimage) { - - t.Helper() - - err := bobChannel.SettleHTLC(preimage, htlcID, nil, nil, nil) - if err != nil { - t.Fatalf("alice failed settling htlc id=%d hash=%x", - htlcID, sha256.Sum256(preimage[:])) - } - - settle := &lnwire.UpdateFulfillHTLC{ - ID: htlcID, - PaymentPreimage: preimage, - } - - aliceLink.HandleChannelUpdate(settle) -} - -// receiveSettleAliceToBob waits for Alice to send a HTLC settle message to -// Bob, then hands this to Bob. -func receiveFailAliceToBob(t *testing.T, aliceMsgs chan lnwire.Message, - aliceLink ChannelLink, bobChannel *lnwallet.LightningChannel) { - - t.Helper() - - var msg lnwire.Message - select { - case msg = <-aliceMsgs: - case <-time.After(15 * time.Second): - t.Fatalf("did not receive message") - } - - failMsg, ok := msg.(*lnwire.UpdateFailHTLC) - if !ok { - t.Fatalf("expected UpdateFailHTLC, got %T", msg) - } - - err := bobChannel.ReceiveFailHTLC(failMsg.ID, failMsg.Reason) - if err != nil { - t.Fatalf("unable to apply received fail htlc: %v", err) - } -} - // TestChannelLinkNoMoreUpdates tests that we won't send a new commitment // when there are no new updates to sign. func TestChannelLinkNoMoreUpdates(t *testing.T) { @@ -4647,6 +4405,13 @@ func TestChannelLinkNoMoreUpdates(t *testing.T) { htlc1 := generateHtlc(t, coreLink, bobChannel, 0) htlc2 := generateHtlc(t, coreLink, bobChannel, 1) + ctx := linkTestContext{ + t: t, + aliceLink: aliceLink, + aliceMsgs: aliceMsgs, + bobChannel: bobChannel, + } + // We now play out the following scanario: // // (1) Alice receives htlc1 from Bob. @@ -4677,29 +4442,29 @@ func TestChannelLinkNoMoreUpdates(t *testing.T) { // Alice sent. // (17) Send a signature for the empty state. No HTLCs are left. // (18) Alice will revoke her previous state. - // Alice Bob - // | | - // | ... | - // | | <--- idle (no htlc on either side) - // | | - sendHtlcBobToAlice(t, aliceLink, bobChannel, htlc1) // |<----- add-1 ------| (1) - sendCommitSigBobToAlice(t, aliceLink, bobChannel, 1) // |<------ sig -------| (2) - sendHtlcBobToAlice(t, aliceLink, bobChannel, htlc2) // |<----- add-2 ------| (3) - receiveRevAndAckAliceToBob(t, aliceMsgs, aliceLink, bobChannel) // |------- rev ------>| (4) <--- Alice acks add-1 - receiveCommitSigAliceToBob(t, aliceMsgs, aliceLink, bobChannel, 1) // |------- sig ------>| (5) <--- Alice signs add-1 - sendCommitSigBobToAlice(t, aliceLink, bobChannel, 2) // |<------ sig -------| (6) - receiveRevAndAckAliceToBob(t, aliceMsgs, aliceLink, bobChannel) // |------- rev ------>| (7) <--- Alice acks add-2 - sendRevAndAckBobToAlice(t, aliceLink, bobChannel) // |<------ rev -------| (8) - receiveSettleAliceToBob(t, aliceMsgs, aliceLink, bobChannel) // |------ ful-1 ----->| (9) - receiveCommitSigAliceToBob(t, aliceMsgs, aliceLink, bobChannel, 1) // |------- sig ------>| (10) <--- Alice signs add-1 + add-2 + ful-1 = add-2 - sendRevAndAckBobToAlice(t, aliceLink, bobChannel) // |<------ rev -------| (11) - sendCommitSigBobToAlice(t, aliceLink, bobChannel, 1) // |<------ sig -------| (12) - receiveSettleAliceToBob(t, aliceMsgs, aliceLink, bobChannel) // |------ ful-2 ----->| (13) - receiveCommitSigAliceToBob(t, aliceMsgs, aliceLink, bobChannel, 0) // |------- sig ------>| (14) <--- Alice signs add-2 + ful-2 = no htlcs - receiveRevAndAckAliceToBob(t, aliceMsgs, aliceLink, bobChannel) // |------- rev ------>| (15) - sendRevAndAckBobToAlice(t, aliceLink, bobChannel) // |<------ rev -------| (16) <--- Bob acks that there are no more htlcs - sendCommitSigBobToAlice(t, aliceLink, bobChannel, 0) // |<------ sig -------| (17) - receiveRevAndAckAliceToBob(t, aliceMsgs, aliceLink, bobChannel) // |------- rev ------>| (18) <--- Alice acks that there are no htlcs on Alice's side + // Alice Bob + // | | + // | ... | + // | | <--- idle (no htlc on either side) + // | | + ctx.sendHtlcBobToAlice(htlc1) // |<----- add-1 ------| (1) + ctx.sendCommitSigBobToAlice(1) // |<------ sig -------| (2) + ctx.sendHtlcBobToAlice(htlc2) // |<----- add-2 ------| (3) + ctx.receiveRevAndAckAliceToBob() // |------- rev ------>| (4) <--- Alice acks add-1 + ctx.receiveCommitSigAliceToBob(1) // |------- sig ------>| (5) <--- Alice signs add-1 + ctx.sendCommitSigBobToAlice(2) // |<------ sig -------| (6) + ctx.receiveRevAndAckAliceToBob() // |------- rev ------>| (7) <--- Alice acks add-2 + ctx.sendRevAndAckBobToAlice() // |<------ rev -------| (8) + ctx.receiveSettleAliceToBob() // |------ ful-1 ----->| (9) + ctx.receiveCommitSigAliceToBob(1) // |------- sig ------>| (10) <--- Alice signs add-1 + add-2 + ful-1 = add-2 + ctx.sendRevAndAckBobToAlice() // |<------ rev -------| (11) + ctx.sendCommitSigBobToAlice(1) // |<------ sig -------| (12) + ctx.receiveSettleAliceToBob() // |------ ful-2 ----->| (13) + ctx.receiveCommitSigAliceToBob(0) // |------- sig ------>| (14) <--- Alice signs add-2 + ful-2 = no htlcs + ctx.receiveRevAndAckAliceToBob() // |------- rev ------>| (15) + ctx.sendRevAndAckBobToAlice() // |<------ rev -------| (16) <--- Bob acks that there are no more htlcs + ctx.sendCommitSigBobToAlice(0) // |<------ sig -------| (17) + ctx.receiveRevAndAckAliceToBob() // |------- rev ------>| (18) <--- Alice acks that there are no htlcs on Alice's side // No there are no more changes to ACK or sign, make sure Alice doesn't // attempt to send any more messages. @@ -4763,6 +4528,21 @@ func TestChannelLinkWaitForRevocation(t *testing.T) { htlcs = append(htlcs, htlc) } + ctx := linkTestContext{ + t: t, + aliceLink: aliceLink, + aliceMsgs: aliceMsgs, + bobChannel: bobChannel, + } + + assertNoMsgFromAlice := func() { + select { + case <-aliceMsgs: + t.Fatalf("did not expect message from Alice") + case <-time.After(50 * time.Millisecond): + } + } + // We play out the following scenario: // // (1) Add the first HTLC. @@ -4801,45 +4581,41 @@ func TestChannelLinkWaitForRevocation(t *testing.T) { // that now. Since Bob ACKed all settles, no HTLCs should be left on // the commitment. // (18) Alice will revoke her previous state. - // Alice Bob - // | | - // | ... | - // | | <--- idle (no htlc on either side) - // | | - sendHtlcBobToAlice(t, aliceLink, bobChannel, htlcs[0]) // |<----- add-1 ------| (1) - sendCommitSigBobToAlice(t, aliceLink, bobChannel, 1) // |<------ sig -------| (2) - receiveRevAndAckAliceToBob(t, aliceMsgs, aliceLink, bobChannel) // |------- rev ------>| (3) <--- Alice acks add-1 - receiveCommitSigAliceToBob(t, aliceMsgs, aliceLink, bobChannel, 1) // |------- sig ------>| (4) <--- Alice signs add-1 - for i := 1; i < numHtlcs; i++ { // | | - sendHtlcBobToAlice(t, aliceLink, bobChannel, htlcs[i]) // |<----- add-i ------| (5.i) - sendCommitSigBobToAlice(t, aliceLink, bobChannel, i+1) // |<------ sig -------| (6.i) - receiveRevAndAckAliceToBob(t, aliceMsgs, aliceLink, bobChannel) // |------- rev ------>| (7.i) <--- Alice acks add-i - select { // | | - case <-aliceMsgs: // | | Alice should not send a sig for - t.Fatalf("unexpectedly received msg from Alice") // | | Bob's last state, since she is - default: // | | still waiting for a revocation - } // | | for the previous one. - } // | | - sendRevAndAckBobToAlice(t, aliceLink, bobChannel) // |<------ rev -------| (8) Finally let Bob send rev - receiveSettleAliceToBob(t, aliceMsgs, aliceLink, bobChannel) // |------ ful-1 ----->| (9) - receiveCommitSigAliceToBob(t, aliceMsgs, aliceLink, bobChannel, numHtlcs-1) // |------- sig ------>| (10) <--- Alice signs add-i - sendRevAndAckBobToAlice(t, aliceLink, bobChannel) // |<------ rev -------| (11) - for i := 1; i < numHtlcs; i++ { // | | - receiveSettleAliceToBob(t, aliceMsgs, aliceLink, bobChannel) // |------ ful-1 ----->| (12.i) - } // | | - sendCommitSigBobToAlice(t, aliceLink, bobChannel, numHtlcs-1) // |<------ sig -------| (13) - receiveCommitSigAliceToBob(t, aliceMsgs, aliceLink, bobChannel, 0) // |------- sig ------>| (14) - sendRevAndAckBobToAlice(t, aliceLink, bobChannel) // |<------ rev -------| (15) - receiveRevAndAckAliceToBob(t, aliceMsgs, aliceLink, bobChannel) // |------- rev ------>| (16) - sendCommitSigBobToAlice(t, aliceLink, bobChannel, 0) // |<------ sig -------| (17) - receiveRevAndAckAliceToBob(t, aliceMsgs, aliceLink, bobChannel) // |------- rev ------>| (18) + // Alice Bob + // | | + // | ... | + // | | <--- idle (no htlc on either side) + // | | + ctx.sendHtlcBobToAlice(htlcs[0]) // |<----- add-1 ------| (1) + ctx.sendCommitSigBobToAlice(1) // |<------ sig -------| (2) + ctx.receiveRevAndAckAliceToBob() // |------- rev ------>| (3) <--- Alice acks add-1 + ctx.receiveCommitSigAliceToBob(1) // |------- sig ------>| (4) <--- Alice signs add-1 + for i := 1; i < numHtlcs; i++ { // | | + ctx.sendHtlcBobToAlice(htlcs[i]) // |<----- add-i ------| (5.i) + ctx.sendCommitSigBobToAlice(i + 1) // |<------ sig -------| (6.i) + ctx.receiveRevAndAckAliceToBob() // |------- rev ------>| (7.i) <--- Alice acks add-i + assertNoMsgFromAlice() // | | + // | | Alice should not send a sig for + // | | Bob's last state, since she is + // | | still waiting for a revocation + // | | for the previous one. + } // | | + ctx.sendRevAndAckBobToAlice() // |<------ rev -------| (8) Finally let Bob send rev + ctx.receiveSettleAliceToBob() // |------ ful-1 ----->| (9) + ctx.receiveCommitSigAliceToBob(numHtlcs - 1) // |------- sig ------>| (10) <--- Alice signs add-i + ctx.sendRevAndAckBobToAlice() // |<------ rev -------| (11) + for i := 1; i < numHtlcs; i++ { // | | + ctx.receiveSettleAliceToBob() // |------ ful-1 ----->| (12.i) + } // | | + ctx.sendCommitSigBobToAlice(numHtlcs - 1) // |<------ sig -------| (13) + ctx.receiveCommitSigAliceToBob(0) // |------- sig ------>| (14) + ctx.sendRevAndAckBobToAlice() // |<------ rev -------| (15) + ctx.receiveRevAndAckAliceToBob() // |------- rev ------>| (16) + ctx.sendCommitSigBobToAlice(0) // |<------ sig -------| (17) + ctx.receiveRevAndAckAliceToBob() // |------- rev ------>| (18) // Both side's state is now updated, no more messages should be sent. - select { - case <-aliceMsgs: - t.Fatalf("did not expect message from Alice") - case <-time.After(50 * time.Millisecond): - } + assertNoMsgFromAlice() } // TestChannelLinkBatchPreimageWrite asserts that a link will batch preimage @@ -4899,10 +4675,17 @@ func testChannelLinkBatchPreimageWrite(t *testing.T, disconnect bool) { invoices = append(invoices, invoice) } + ctx := linkTestContext{ + t: t, + aliceLink: aliceLink, + aliceMsgs: aliceMsgs, + bobChannel: bobChannel, + } + // First, send a batch of Adds from Alice to Bob. for i, htlc := range htlcs { - sendHtlcAliceToBob(t, aliceLink, i, htlc) - receiveHtlcAliceToBob(t, aliceMsgs, bobChannel) + ctx.sendHtlcAliceToBob(i, htlc) + ctx.receiveHtlcAliceToBob() } // Assert that no preimages exist for these htlcs in Alice's cache. @@ -4918,12 +4701,10 @@ func testChannelLinkBatchPreimageWrite(t *testing.T, disconnect bool) { // Do a commitment dance to lock in the Adds, we expect numHtlcs htlcs // to be on each party's commitment transactions. - receiveCommitSigAliceToBob( - t, aliceMsgs, aliceLink, bobChannel, numHtlcs, - ) - sendRevAndAckBobToAlice(t, aliceLink, bobChannel) - sendCommitSigBobToAlice(t, aliceLink, bobChannel, numHtlcs) - receiveRevAndAckAliceToBob(t, aliceMsgs, aliceLink, bobChannel) + ctx.receiveCommitSigAliceToBob(numHtlcs) + ctx.sendRevAndAckBobToAlice() + ctx.sendCommitSigBobToAlice(numHtlcs) + ctx.receiveRevAndAckAliceToBob() // Check again that no preimages exist for these htlcs in Alice's cache. checkHasPreimages(t, coreLink, htlcs, false) @@ -4931,8 +4712,8 @@ func testChannelLinkBatchPreimageWrite(t *testing.T, disconnect bool) { // Now, have Bob settle the HTLCs back to Alice using the preimages in // the invoice corresponding to each of the HTLCs. for i, invoice := range invoices { - sendSettleBobToAlice( - t, aliceLink, bobChannel, uint64(i), + ctx.sendSettleBobToAlice( + uint64(i), invoice.Terms.PaymentPreimage, ) } @@ -4953,7 +4734,7 @@ func testChannelLinkBatchPreimageWrite(t *testing.T, disconnect bool) { // Otherwise, we are testing that Alice commits the preimages after // receiving a CommitSig from Bob. Bob's commitment should now have 0 // HTLCs. - sendCommitSigBobToAlice(t, aliceLink, bobChannel, 0) + ctx.sendCommitSigBobToAlice(0) // Since Alice will process the CommitSig asynchronously, we wait until // she replies with her RevokeAndAck to ensure the tests reliably @@ -5004,6 +4785,13 @@ func TestChannelLinkCleanupSpuriousResponses(t *testing.T) { htlc1 := generateHtlc(t, coreLink, bobChannel, 0) htlc2 := generateHtlc(t, coreLink, bobChannel, 1) + ctx := linkTestContext{ + t: t, + aliceLink: aliceLink, + aliceMsgs: aliceMsgs, + bobChannel: bobChannel, + } + // We start with he following scenario: Bob sends Alice two HTLCs, and a // commitment dance ensures, leaving two HTLCs that Alice can respond // to. Since Alice is in ExitSettle mode, we will then take over and @@ -5017,12 +4805,12 @@ func TestChannelLinkCleanupSpuriousResponses(t *testing.T) { // |<----- rev ------| // |<----- sig ------| commits add-1 + add-2 // |------ rev ----->| - sendHtlcBobToAlice(t, aliceLink, bobChannel, htlc1) - sendHtlcBobToAlice(t, aliceLink, bobChannel, htlc2) - sendCommitSigBobToAlice(t, aliceLink, bobChannel, 2) - receiveRevAndAckAliceToBob(t, aliceMsgs, aliceLink, bobChannel) - receiveCommitSigAliceToBob(t, aliceMsgs, aliceLink, bobChannel, 2) - sendRevAndAckBobToAlice(t, aliceLink, bobChannel) + ctx.sendHtlcBobToAlice(htlc1) + ctx.sendHtlcBobToAlice(htlc2) + ctx.sendCommitSigBobToAlice(2) + ctx.receiveRevAndAckAliceToBob() + ctx.receiveCommitSigAliceToBob(2) + ctx.sendRevAndAckBobToAlice() // Give Alice to time to process the revocation. time.Sleep(time.Second) @@ -5071,8 +4859,8 @@ func TestChannelLinkCleanupSpuriousResponses(t *testing.T) { // Bob Alice // |<----- fal-1 ------| // |<----- sig ------| commits fal-1 - receiveFailAliceToBob(t, aliceMsgs, aliceLink, bobChannel) - receiveCommitSigAliceToBob(t, aliceMsgs, aliceLink, bobChannel, 1) + ctx.receiveFailAliceToBob() + ctx.receiveCommitSigAliceToBob(1) aliceFwdPkgs, err = coreLink.channel.LoadFwdPkgs() if err != nil { @@ -5108,9 +4896,9 @@ func TestChannelLinkCleanupSpuriousResponses(t *testing.T) { // |------ rev ----->| // |------ sig ----->| // |<----- rev ------| - sendRevAndAckBobToAlice(t, aliceLink, bobChannel) - sendCommitSigBobToAlice(t, aliceLink, bobChannel, 1) - receiveRevAndAckAliceToBob(t, aliceMsgs, aliceLink, bobChannel) + ctx.sendRevAndAckBobToAlice() + ctx.sendCommitSigBobToAlice(1) + ctx.receiveRevAndAckAliceToBob() // Next, we'll construct a fail packet for add-2 (index 1), which we'll // send to Bob and lock in. Since the AddRef is set on this instance, we @@ -5131,8 +4919,8 @@ func TestChannelLinkCleanupSpuriousResponses(t *testing.T) { // Bob Alice // |<----- fal-1 ------| // |<----- sig ------| commits fal-1 - receiveFailAliceToBob(t, aliceMsgs, aliceLink, bobChannel) - receiveCommitSigAliceToBob(t, aliceMsgs, aliceLink, bobChannel, 0) + ctx.receiveFailAliceToBob() + ctx.receiveCommitSigAliceToBob(0) aliceFwdPkgs, err = coreLink.channel.LoadFwdPkgs() if err != nil { @@ -5174,9 +4962,9 @@ func TestChannelLinkCleanupSpuriousResponses(t *testing.T) { // |------ rev ----->| // |------ sig ----->| // |<----- rev ------| - sendRevAndAckBobToAlice(t, aliceLink, bobChannel) - sendCommitSigBobToAlice(t, aliceLink, bobChannel, 0) - receiveRevAndAckAliceToBob(t, aliceMsgs, aliceLink, bobChannel) + ctx.sendRevAndAckBobToAlice() + ctx.sendCommitSigBobToAlice(0) + ctx.receiveRevAndAckAliceToBob() // We'll do a quick sanity check, and blindly send the same fail packet // for the first HTLC. Since this HTLC index has already been settled, @@ -5381,7 +5169,12 @@ func TestChannelLinkFail(t *testing.T) { // Generate an HTLC and send to the link. htlc1 := generateHtlc(t, c, remoteChannel, 0) - sendHtlcBobToAlice(t, c, remoteChannel, htlc1) + ctx := linkTestContext{ + t: t, + aliceLink: c, + bobChannel: remoteChannel, + } + ctx.sendHtlcBobToAlice(htlc1) // Sign a commitment that will include // signature for the HTLC just sent. @@ -5413,7 +5206,13 @@ func TestChannelLinkFail(t *testing.T) { // Generate an HTLC and send to the link. htlc1 := generateHtlc(t, c, remoteChannel, 0) - sendHtlcBobToAlice(t, c, remoteChannel, htlc1) + ctx := linkTestContext{ + t: t, + aliceLink: c, + bobChannel: remoteChannel, + } + + ctx.sendHtlcBobToAlice(htlc1) // Sign a commitment that will include // signature for the HTLC just sent. @@ -5956,12 +5755,19 @@ func TestChannelLinkHoldInvoiceRestart(t *testing.T) { t.Fatalf("unable to add invoice to registry: %v", err) } + ctx := linkTestContext{ + t: t, + aliceLink: alice.link, + aliceMsgs: alice.msgs, + bobChannel: bobChannel, + } + // Lock in htlc paying the hodl invoice. - sendHtlcBobToAlice(t, alice.link, bobChannel, htlc) - sendCommitSigBobToAlice(t, alice.link, bobChannel, 1) - receiveRevAndAckAliceToBob(t, alice.msgs, alice.link, bobChannel) - receiveCommitSigAliceToBob(t, alice.msgs, alice.link, bobChannel, 1) - sendRevAndAckBobToAlice(t, alice.link, bobChannel) + ctx.sendHtlcBobToAlice(htlc) + ctx.sendCommitSigBobToAlice(1) + ctx.receiveRevAndAckAliceToBob() + ctx.receiveCommitSigAliceToBob(1) + ctx.sendRevAndAckBobToAlice() // We expect a call to the invoice registry to notify the arrival of the // htlc. @@ -5973,6 +5779,8 @@ func TestChannelLinkHoldInvoiceRestart(t *testing.T) { // Restart link. alice.restart(false) + ctx.aliceLink = alice.link + ctx.aliceMsgs = alice.msgs // Expect htlc to be reprocessed. <-registry.settleChan @@ -5981,8 +5789,8 @@ func TestChannelLinkHoldInvoiceRestart(t *testing.T) { registry.SettleHodlInvoice(preimage) // Expect alice to send a settle and commitsig message to bob. - receiveSettleAliceToBob(t, alice.msgs, alice.link, bobChannel) - receiveCommitSigAliceToBob(t, alice.msgs, alice.link, bobChannel, 0) + ctx.receiveSettleAliceToBob() + ctx.receiveCommitSigAliceToBob(0) // Stop the link alice.link.Stop() @@ -6048,12 +5856,19 @@ func TestChannelLinkRevocationWindowHodl(t *testing.T) { t.Fatalf("unable to add invoice to registry: %v", err) } + ctx := linkTestContext{ + t: t, + aliceLink: aliceLink, + aliceMsgs: aliceMsgs, + bobChannel: bobChannel, + } + // Lock in htlc 1 on both sides. - sendHtlcBobToAlice(t, aliceLink, bobChannel, htlc1) - sendCommitSigBobToAlice(t, aliceLink, bobChannel, 1) - receiveRevAndAckAliceToBob(t, aliceMsgs, aliceLink, bobChannel) - receiveCommitSigAliceToBob(t, aliceMsgs, aliceLink, bobChannel, 1) - sendRevAndAckBobToAlice(t, aliceLink, bobChannel) + ctx.sendHtlcBobToAlice(htlc1) + ctx.sendCommitSigBobToAlice(1) + ctx.receiveRevAndAckAliceToBob() + ctx.receiveCommitSigAliceToBob(1) + ctx.sendRevAndAckBobToAlice() // We expect a call to the invoice registry to notify the arrival of // htlc 1. @@ -6064,11 +5879,11 @@ func TestChannelLinkRevocationWindowHodl(t *testing.T) { } // Lock in htlc 2 on both sides. - sendHtlcBobToAlice(t, aliceLink, bobChannel, htlc2) - sendCommitSigBobToAlice(t, aliceLink, bobChannel, 2) - receiveRevAndAckAliceToBob(t, aliceMsgs, aliceLink, bobChannel) - receiveCommitSigAliceToBob(t, aliceMsgs, aliceLink, bobChannel, 2) - sendRevAndAckBobToAlice(t, aliceLink, bobChannel) + ctx.sendHtlcBobToAlice(htlc2) + ctx.sendCommitSigBobToAlice(2) + ctx.receiveRevAndAckAliceToBob() + ctx.receiveCommitSigAliceToBob(2) + ctx.sendRevAndAckBobToAlice() select { case <-registry.settleChan: @@ -6081,14 +5896,14 @@ func TestChannelLinkRevocationWindowHodl(t *testing.T) { // Expect alice to send a settle and commitsig message to bob. Bob does // not yet send the revocation. - receiveSettleAliceToBob(t, aliceMsgs, aliceLink, bobChannel) - receiveCommitSigAliceToBob(t, aliceMsgs, aliceLink, bobChannel, 1) + ctx.receiveSettleAliceToBob() + ctx.receiveCommitSigAliceToBob(1) // Settle invoice 2 with the preimage. registry.SettleHodlInvoice(preimage2) // Expect alice to send a settle for htlc 2. - receiveSettleAliceToBob(t, aliceMsgs, aliceLink, bobChannel) + ctx.receiveSettleAliceToBob() // At this point, Alice cannot send a new commit sig to bob because the // revocation window is exhausted. @@ -6104,7 +5919,7 @@ func TestChannelLinkRevocationWindowHodl(t *testing.T) { } // Bob sends revocation and signs commit with htlc 1 settled. - sendRevAndAckBobToAlice(t, aliceLink, bobChannel) + ctx.sendRevAndAckBobToAlice() // Allow some time for it to be processed by the link. time.Sleep(time.Second) @@ -6115,12 +5930,12 @@ func TestChannelLinkRevocationWindowHodl(t *testing.T) { // After the revocation, it is again possible for Alice to send a commit // sig no more htlcs. Bob acks the update. - receiveCommitSigAliceToBob(t, aliceMsgs, aliceLink, bobChannel, 0) - sendRevAndAckBobToAlice(t, aliceLink, bobChannel) + ctx.receiveCommitSigAliceToBob(0) + ctx.sendRevAndAckBobToAlice() // Bob updates his remote commit tx. - sendCommitSigBobToAlice(t, aliceLink, bobChannel, 0) - receiveRevAndAckAliceToBob(t, aliceMsgs, aliceLink, bobChannel) + ctx.sendCommitSigBobToAlice(0) + ctx.receiveRevAndAckAliceToBob() // Stop the link aliceLink.Stop()