123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831 |
- package htlcswitch
- import (
- prand "math/rand"
- "reflect"
- "testing"
- "time"
- "github.com/btcsuite/btcd/btcutil"
- "github.com/davecgh/go-spew/spew"
- "github.com/lightningnetwork/lnd/channeldb"
- "github.com/lightningnetwork/lnd/clock"
- "github.com/lightningnetwork/lnd/lnmock"
- "github.com/lightningnetwork/lnd/lnwallet/chainfee"
- "github.com/lightningnetwork/lnd/lnwire"
- "github.com/stretchr/testify/mock"
- "github.com/stretchr/testify/require"
- )
- const testExpiry = time.Minute
- // TestMailBoxCouriers tests that both aspects of the mailBox struct works
- // properly. Both packets and messages should be able to added to each
- // respective mailbox concurrently, and also messages/packets should also be
- // able to be received concurrently.
- func TestMailBoxCouriers(t *testing.T) {
- t.Parallel()
- // First, we'll create new instance of the current default mailbox
- // type.
- ctx := newMailboxContext(t, time.Now(), testExpiry)
- // We'll be adding 10 message of both types to the mailbox.
- const numPackets = 10
- const halfPackets = numPackets / 2
- // We'll add a set of random packets to the mailbox.
- sentPackets := make([]*htlcPacket, numPackets)
- for i := 0; i < numPackets; i++ {
- pkt := &htlcPacket{
- outgoingChanID: lnwire.NewShortChanIDFromInt(uint64(prand.Int63())),
- incomingChanID: lnwire.NewShortChanIDFromInt(uint64(prand.Int63())),
- amount: lnwire.MilliSatoshi(prand.Int63()),
- htlc: &lnwire.UpdateAddHTLC{
- ID: uint64(i),
- },
- }
- sentPackets[i] = pkt
- err := ctx.mailbox.AddPacket(pkt)
- if err != nil {
- t.Fatalf("unable to add packet: %v", err)
- }
- }
- // Next, we'll do the same, but this time adding wire messages.
- sentMessages := make([]lnwire.Message, numPackets)
- for i := 0; i < numPackets; i++ {
- msg := &lnwire.UpdateAddHTLC{
- ID: uint64(prand.Int63()),
- Amount: lnwire.MilliSatoshi(prand.Int63()),
- }
- sentMessages[i] = msg
- err := ctx.mailbox.AddMessage(msg)
- if err != nil {
- t.Fatalf("unable to add message: %v", err)
- }
- }
- // Now we'll attempt to read back the packets/messages we added to the
- // mailbox. We'll alternative reading from the message outbox vs the
- // packet outbox to ensure that they work concurrently properly.
- recvdPackets := make([]*htlcPacket, 0, numPackets)
- recvdMessages := make([]lnwire.Message, 0, numPackets)
- for i := 0; i < numPackets*2; i++ {
- timeout := time.After(time.Second * 5)
- if i%2 == 0 {
- select {
- case <-timeout:
- t.Fatalf("didn't recv pkt after timeout")
- case pkt := <-ctx.mailbox.PacketOutBox():
- recvdPackets = append(recvdPackets, pkt)
- }
- } else {
- select {
- case <-timeout:
- t.Fatalf("didn't recv message after timeout")
- case msg := <-ctx.mailbox.MessageOutBox():
- recvdMessages = append(recvdMessages, msg)
- }
- }
- }
- // The number of messages/packets we sent, and the number we received
- // should match exactly.
- if len(sentPackets) != len(recvdPackets) {
- t.Fatalf("expected %v packets instead got %v", len(sentPackets),
- len(recvdPackets))
- }
- if len(sentMessages) != len(recvdMessages) {
- t.Fatalf("expected %v messages instead got %v", len(sentMessages),
- len(recvdMessages))
- }
- // Additionally, the set of packets should match exactly, as we should
- // have received the packets in the exact same ordering that we added.
- if !reflect.DeepEqual(sentPackets, recvdPackets) {
- t.Fatalf("recvd packets mismatched: expected %v, got %v",
- spew.Sdump(sentPackets), spew.Sdump(recvdPackets))
- }
- if !reflect.DeepEqual(recvdMessages, recvdMessages) {
- t.Fatalf("recvd messages mismatched: expected %v, got %v",
- spew.Sdump(sentMessages), spew.Sdump(recvdMessages))
- }
- // Now that we've received all of the intended msgs/pkts, ack back half
- // of the packets.
- for _, recvdPkt := range recvdPackets[:halfPackets] {
- ctx.mailbox.AckPacket(recvdPkt.inKey())
- }
- // With the packets drained and partially acked, we reset the mailbox,
- // simulating a link shutting down and then coming back up.
- err := ctx.mailbox.ResetMessages()
- require.NoError(t, err, "unable to reset messages")
- err = ctx.mailbox.ResetPackets()
- require.NoError(t, err, "unable to reset packets")
- // Now, we'll use the same alternating strategy to read from our
- // mailbox. All wire messages are dropped on startup, but any unacked
- // packets will be replayed in the same order they were delivered
- // initially.
- recvdPackets2 := make([]*htlcPacket, 0, halfPackets)
- for i := 0; i < 2*halfPackets; i++ {
- timeout := time.After(time.Second * 5)
- if i%2 == 0 {
- select {
- case <-timeout:
- t.Fatalf("didn't recv pkt after timeout")
- case pkt := <-ctx.mailbox.PacketOutBox():
- recvdPackets2 = append(recvdPackets2, pkt)
- }
- } else {
- select {
- case <-ctx.mailbox.MessageOutBox():
- t.Fatalf("should not receive wire msg after reset")
- default:
- }
- }
- }
- // The number of packets we received should match the number of unacked
- // packets left in the mailbox.
- if halfPackets != len(recvdPackets2) {
- t.Fatalf("expected %v packets instead got %v", halfPackets,
- len(recvdPackets))
- }
- // Additionally, the set of packets should match exactly with the
- // unacked packets, and we should have received the packets in the exact
- // same ordering that we added.
- if !reflect.DeepEqual(recvdPackets[halfPackets:], recvdPackets2) {
- t.Fatalf("recvd packets mismatched: expected %v, got %v",
- spew.Sdump(sentPackets), spew.Sdump(recvdPackets))
- }
- }
- // TestMailBoxResetAfterShutdown tests that ResetMessages and ResetPackets
- // return ErrMailBoxShuttingDown after the mailbox has been stopped.
- func TestMailBoxResetAfterShutdown(t *testing.T) {
- t.Parallel()
- ctx := newMailboxContext(t, time.Now(), time.Second)
- // Stop the mailbox, then try to reset the message and packet couriers.
- ctx.mailbox.Stop()
- err := ctx.mailbox.ResetMessages()
- if err != ErrMailBoxShuttingDown {
- t.Fatalf("expected ErrMailBoxShuttingDown, got: %v", err)
- }
- err = ctx.mailbox.ResetPackets()
- if err != ErrMailBoxShuttingDown {
- t.Fatalf("expected ErrMailBoxShuttingDown, got: %v", err)
- }
- }
- type mailboxContext struct {
- t *testing.T
- mailbox MailBox
- clock *clock.TestClock
- forwards chan *htlcPacket
- }
- // newMailboxContextWithClock creates a new mailbox context with the given
- // mocked clock.
- //
- // TODO(yy): replace all usage of `newMailboxContext` with this method.
- func newMailboxContextWithClock(t *testing.T,
- clock clock.Clock) *mailboxContext {
- ctx := &mailboxContext{
- t: t,
- forwards: make(chan *htlcPacket, 1),
- }
- failMailboxUpdate := func(outScid,
- mboxScid lnwire.ShortChannelID) lnwire.FailureMessage {
- return &lnwire.FailTemporaryNodeFailure{}
- }
- ctx.mailbox = newMemoryMailBox(&mailBoxConfig{
- failMailboxUpdate: failMailboxUpdate,
- forwardPackets: ctx.forward,
- clock: clock,
- })
- ctx.mailbox.Start()
- t.Cleanup(ctx.mailbox.Stop)
- return ctx
- }
- func newMailboxContext(t *testing.T, startTime time.Time,
- expiry time.Duration) *mailboxContext {
- ctx := &mailboxContext{
- t: t,
- clock: clock.NewTestClock(startTime),
- forwards: make(chan *htlcPacket, 1),
- }
- failMailboxUpdate := func(outScid,
- mboxScid lnwire.ShortChannelID) lnwire.FailureMessage {
- return &lnwire.FailTemporaryNodeFailure{}
- }
- ctx.mailbox = newMemoryMailBox(&mailBoxConfig{
- failMailboxUpdate: failMailboxUpdate,
- forwardPackets: ctx.forward,
- clock: ctx.clock,
- expiry: expiry,
- })
- ctx.mailbox.Start()
- t.Cleanup(ctx.mailbox.Stop)
- return ctx
- }
- func (c *mailboxContext) forward(_ chan struct{},
- pkts ...*htlcPacket) error {
- for _, pkt := range pkts {
- c.forwards <- pkt
- }
- return nil
- }
- func (c *mailboxContext) sendAdds(start, num int) []*htlcPacket {
- c.t.Helper()
- sentPackets := make([]*htlcPacket, num)
- for i := 0; i < num; i++ {
- pkt := &htlcPacket{
- outgoingChanID: lnwire.NewShortChanIDFromInt(
- uint64(prand.Int63())),
- incomingChanID: lnwire.NewShortChanIDFromInt(
- uint64(prand.Int63())),
- incomingHTLCID: uint64(start + i),
- amount: lnwire.MilliSatoshi(prand.Int63()),
- htlc: &lnwire.UpdateAddHTLC{
- ID: uint64(start + i),
- },
- }
- sentPackets[i] = pkt
- err := c.mailbox.AddPacket(pkt)
- if err != nil {
- c.t.Fatalf("unable to add packet: %v", err)
- }
- }
- return sentPackets
- }
- func (c *mailboxContext) receivePkts(pkts []*htlcPacket) {
- c.t.Helper()
- for i, expPkt := range pkts {
- select {
- case pkt := <-c.mailbox.PacketOutBox():
- if reflect.DeepEqual(expPkt, pkt) {
- continue
- }
- c.t.Fatalf("inkey mismatch #%d, want: %v vs "+
- "got: %v", i, expPkt.inKey(), pkt.inKey())
- case <-time.After(50 * time.Millisecond):
- c.t.Fatalf("did not receive fail for index %d", i)
- }
- }
- }
- func (c *mailboxContext) checkFails(adds []*htlcPacket) {
- c.t.Helper()
- for i, add := range adds {
- select {
- case fail := <-c.forwards:
- if add.inKey() == fail.inKey() {
- continue
- }
- c.t.Fatalf("inkey mismatch #%d, add: %v vs fail: %v",
- i, add.inKey(), fail.inKey())
- case <-time.After(50 * time.Millisecond):
- c.t.Fatalf("did not receive fail for index %d", i)
- }
- }
- select {
- case pkt := <-c.forwards:
- c.t.Fatalf("unexpected forward: %v", pkt.keystone())
- case <-time.After(50 * time.Millisecond):
- }
- }
- // TestMailBoxFailAdd asserts that FailAdd returns a response to the switch
- // under various interleavings with other operations on the mailbox.
- func TestMailBoxFailAdd(t *testing.T) {
- var (
- batchDelay = time.Second
- expiry = time.Minute
- firstBatchStart = time.Now()
- secondBatchStart = time.Now().Add(batchDelay)
- thirdBatchStart = time.Now().Add(2 * batchDelay)
- thirdBatchExpiry = thirdBatchStart.Add(expiry)
- )
- ctx := newMailboxContext(t, firstBatchStart, expiry)
- failAdds := func(adds []*htlcPacket) {
- for _, add := range adds {
- ctx.mailbox.FailAdd(add)
- }
- }
- const numBatchPackets = 5
- // Send 10 adds, and pull them from the mailbox.
- firstBatch := ctx.sendAdds(0, numBatchPackets)
- ctx.receivePkts(firstBatch)
- // Fail all of these adds, simulating an error adding the HTLCs to the
- // commitment. We should see a failure message for each.
- go failAdds(firstBatch)
- ctx.checkFails(firstBatch)
- // As a sanity check, Fail all of them again and assert that no
- // duplicate fails are sent.
- go failAdds(firstBatch)
- ctx.checkFails(nil)
- // Now, send a second batch of adds after a short delay and deliver them
- // to the link.
- ctx.clock.SetTime(secondBatchStart)
- secondBatch := ctx.sendAdds(numBatchPackets, numBatchPackets)
- ctx.receivePkts(secondBatch)
- // Reset the packet queue w/o changing the current time. This simulates
- // the link flapping and coming back up before the second batch's
- // expiries have elapsed. We should see no failures sent back.
- err := ctx.mailbox.ResetPackets()
- require.NoError(t, err, "unable to reset packets")
- ctx.checkFails(nil)
- // Redeliver the second batch to the link and hold them there.
- ctx.receivePkts(secondBatch)
- // Send a third batch of adds shortly after the second batch.
- ctx.clock.SetTime(thirdBatchStart)
- thirdBatch := ctx.sendAdds(2*numBatchPackets, numBatchPackets)
- // Advance the clock so that the third batch expires. We expect to only
- // see fails for the third batch, since the second batch is still being
- // held by the link.
- ctx.clock.SetTime(thirdBatchExpiry)
- ctx.checkFails(thirdBatch)
- // Finally, reset the link which should cause the second batch to be
- // cancelled immediately.
- err = ctx.mailbox.ResetPackets()
- require.NoError(t, err, "unable to reset packets")
- ctx.checkFails(secondBatch)
- }
- // TestMailBoxPacketPrioritization asserts that the mailbox will prioritize
- // delivering Settle and Fail packets over Adds if both are available for
- // delivery at the same time.
- func TestMailBoxPacketPrioritization(t *testing.T) {
- t.Parallel()
- // First, we'll create new instance of the current default mailbox
- // type.
- ctx := newMailboxContext(t, time.Now(), testExpiry)
- const numPackets = 5
- _, _, aliceChanID, bobChanID := genIDs()
- // Next we'll send the following sequence of packets:
- // - Settle1
- // - Add1
- // - Add2
- // - Fail
- // - Settle2
- sentPackets := make([]*htlcPacket, numPackets)
- for i := 0; i < numPackets; i++ {
- pkt := &htlcPacket{
- outgoingChanID: aliceChanID,
- outgoingHTLCID: uint64(i),
- incomingChanID: bobChanID,
- incomingHTLCID: uint64(i),
- amount: lnwire.MilliSatoshi(prand.Int63()),
- }
- switch i {
- case 0, 4:
- // First and last packets are a Settle. A non-Add is
- // sent first to make the test deterministic w/o needing
- // to sleep.
- pkt.htlc = &lnwire.UpdateFulfillHTLC{ID: uint64(i)}
- case 1, 2:
- // Next two packets are Adds.
- pkt.htlc = &lnwire.UpdateAddHTLC{ID: uint64(i)}
- case 3:
- // Last packet is a Fail.
- pkt.htlc = &lnwire.UpdateFailHTLC{ID: uint64(i)}
- }
- sentPackets[i] = pkt
- err := ctx.mailbox.AddPacket(pkt)
- if err != nil {
- t.Fatalf("failed to add packet: %v", err)
- }
- }
- // When dequeueing the packets, we expect the following sequence:
- // - Settle1
- // - Fail
- // - Settle2
- // - Add1
- // - Add2
- //
- // We expect to see Fail and Settle2 to be delivered before either Add1
- // or Add2 due to the prioritization between the split queue.
- for i := 0; i < numPackets; i++ {
- select {
- case pkt := <-ctx.mailbox.PacketOutBox():
- var expPkt *htlcPacket
- switch i {
- case 0:
- // First packet should be Settle1.
- expPkt = sentPackets[0]
- case 1:
- // Second packet should be Fail.
- expPkt = sentPackets[3]
- case 2:
- // Third packet should be Settle2.
- expPkt = sentPackets[4]
- case 3:
- // Fourth packet should be Add1.
- expPkt = sentPackets[1]
- case 4:
- // Last packet should be Add2.
- expPkt = sentPackets[2]
- }
- if !reflect.DeepEqual(expPkt, pkt) {
- t.Fatalf("recvd packet mismatch %d, want: %v, got: %v",
- i, spew.Sdump(expPkt), spew.Sdump(pkt))
- }
- case <-time.After(50 * time.Millisecond):
- t.Fatalf("didn't receive packet %d before timeout", i)
- }
- }
- }
- // TestMailBoxAddExpiry asserts that the mailbox will cancel back Adds that
- // have reached their expiry time.
- func TestMailBoxAddExpiry(t *testing.T) {
- // Each batch will consist of 10 messages.
- const numBatchPackets = 10
- // deadline is the returned value from the `pktWithExpiry.deadline`.
- deadline := make(chan time.Time, numBatchPackets*2)
- // Create a mock clock and mock the methods.
- mockClock := &lnmock.MockClock{}
- mockClock.On("Now").Return(time.Now())
- // Mock TickAfter, which mounts the above `deadline` channel to the
- // returned value from `pktWithExpiry.deadline`.
- mockClock.On("TickAfter", mock.Anything).Return(deadline)
- // Create a test mailbox context.
- ctx := newMailboxContextWithClock(t, mockClock)
- // Send 10 packets and assert no failures are sent back.
- firstBatch := ctx.sendAdds(0, numBatchPackets)
- ctx.checkFails(nil)
- // Send another 10 packets and assert no failures are sent back.
- secondBatch := ctx.sendAdds(numBatchPackets, numBatchPackets)
- ctx.checkFails(nil)
- // Tick 10 times and we should see the first batch expired.
- for i := 0; i < numBatchPackets; i++ {
- deadline <- time.Now()
- }
- ctx.checkFails(firstBatch)
- // Tick another 10 times and we should see the second batch expired.
- for i := 0; i < numBatchPackets; i++ {
- deadline <- time.Now()
- }
- ctx.checkFails(secondBatch)
- }
- // TestMailBoxDuplicateAddPacket asserts that the mailbox returns an
- // ErrPacketAlreadyExists failure when two htlcPackets are added with identical
- // incoming circuit keys.
- func TestMailBoxDuplicateAddPacket(t *testing.T) {
- t.Parallel()
- ctx := newMailboxContext(t, time.Now(), testExpiry)
- ctx.mailbox.Start()
- addTwice := func(t *testing.T, pkt *htlcPacket) {
- // The first add should succeed.
- err := ctx.mailbox.AddPacket(pkt)
- if err != nil {
- t.Fatalf("unable to add packet: %v", err)
- }
- // Adding again with the same incoming circuit key should fail.
- err = ctx.mailbox.AddPacket(pkt)
- if err != ErrPacketAlreadyExists {
- t.Fatalf("expected ErrPacketAlreadyExists, got: %v", err)
- }
- }
- // Assert duplicate AddPacket calls fail for all types of HTLCs.
- addTwice(t, &htlcPacket{
- incomingHTLCID: 0,
- htlc: &lnwire.UpdateAddHTLC{},
- })
- addTwice(t, &htlcPacket{
- incomingHTLCID: 1,
- htlc: &lnwire.UpdateFulfillHTLC{},
- })
- addTwice(t, &htlcPacket{
- incomingHTLCID: 2,
- htlc: &lnwire.UpdateFailHTLC{},
- })
- }
- // TestMailBoxDustHandling tests that DustPackets returns the expected values
- // for the local and remote dust sum after calling SetFeeRate and
- // SetDustClosure.
- func TestMailBoxDustHandling(t *testing.T) {
- t.Run("tweakless mailbox dust", func(t *testing.T) {
- testMailBoxDust(t, channeldb.SingleFunderTweaklessBit)
- })
- t.Run("zero htlc fee anchors mailbox dust", func(t *testing.T) {
- testMailBoxDust(t, channeldb.SingleFunderTweaklessBit|
- channeldb.AnchorOutputsBit|
- channeldb.ZeroHtlcTxFeeBit,
- )
- })
- }
- func testMailBoxDust(t *testing.T, chantype channeldb.ChannelType) {
- t.Parallel()
- ctx := newMailboxContext(t, time.Now(), testExpiry)
- _, _, aliceID, bobID := genIDs()
- // It should not be the case that the MailBox has packets before the
- // feeRate or dustClosure is set. This is because the mailbox is always
- // created *with* its associated link and attached via AttachMailbox,
- // where these parameters will be set. Even though the lifetime is
- // longer than the link, the setting will persist across multiple link
- // creations.
- ctx.mailbox.SetFeeRate(chainfee.SatPerKWeight(253))
- localDustLimit := btcutil.Amount(400)
- remoteDustLimit := btcutil.Amount(500)
- isDust := dustHelper(chantype, localDustLimit, remoteDustLimit)
- ctx.mailbox.SetDustClosure(isDust)
- // The first packet will be dust according to the remote dust limit,
- // but not the local. We set a different amount if this is a zero fee
- // htlc channel type.
- firstAmt := lnwire.MilliSatoshi(600_000)
- if chantype.ZeroHtlcTxFee() {
- firstAmt = lnwire.MilliSatoshi(450_000)
- }
- firstPkt := &htlcPacket{
- outgoingChanID: aliceID,
- outgoingHTLCID: 0,
- incomingChanID: bobID,
- incomingHTLCID: 0,
- amount: firstAmt,
- htlc: &lnwire.UpdateAddHTLC{
- ID: uint64(0),
- },
- }
- err := ctx.mailbox.AddPacket(firstPkt)
- require.NoError(t, err)
- // Assert that the local sum is 0, and the remote sum accounts for this
- // added packet.
- localSum, remoteSum := ctx.mailbox.DustPackets()
- require.Equal(t, lnwire.MilliSatoshi(0), localSum)
- require.Equal(t, firstAmt, remoteSum)
- // The next packet will be dust according to both limits.
- secondAmt := lnwire.MilliSatoshi(300_000)
- secondPkt := &htlcPacket{
- outgoingChanID: aliceID,
- outgoingHTLCID: 1,
- incomingChanID: bobID,
- incomingHTLCID: 1,
- amount: secondAmt,
- htlc: &lnwire.UpdateAddHTLC{
- ID: uint64(1),
- },
- }
- err = ctx.mailbox.AddPacket(secondPkt)
- require.NoError(t, err)
- // Assert that both the local and remote sums have increased by the
- // second amount.
- localSum, remoteSum = ctx.mailbox.DustPackets()
- require.Equal(t, secondAmt, localSum)
- require.Equal(t, firstAmt+secondAmt, remoteSum)
- // Now we pull both packets off of the queue.
- for i := 0; i < 2; i++ {
- select {
- case <-ctx.mailbox.PacketOutBox():
- case <-time.After(50 * time.Millisecond):
- ctx.t.Fatalf("did not receive packet in time")
- }
- }
- // Assert that the sums haven't changed.
- localSum, remoteSum = ctx.mailbox.DustPackets()
- require.Equal(t, secondAmt, localSum)
- require.Equal(t, firstAmt+secondAmt, remoteSum)
- // Remove the first packet from the mailbox.
- removed := ctx.mailbox.AckPacket(firstPkt.inKey())
- require.True(t, removed)
- // Assert that the remote sum does not include the firstAmt.
- localSum, remoteSum = ctx.mailbox.DustPackets()
- require.Equal(t, secondAmt, localSum)
- require.Equal(t, secondAmt, remoteSum)
- // Remove the second packet from the mailbox.
- removed = ctx.mailbox.AckPacket(secondPkt.inKey())
- require.True(t, removed)
- // Assert that both sums are equal to 0.
- localSum, remoteSum = ctx.mailbox.DustPackets()
- require.Equal(t, lnwire.MilliSatoshi(0), localSum)
- require.Equal(t, lnwire.MilliSatoshi(0), remoteSum)
- }
- // TestMailOrchestrator asserts that the orchestrator properly buffers packets
- // for channels that haven't been made live, such that they are delivered
- // immediately after BindLiveShortChanID. It also tests that packets are delivered
- // readily to mailboxes for channels that are already in the live state.
- func TestMailOrchestrator(t *testing.T) {
- t.Parallel()
- failMailboxUpdate := func(outScid,
- mboxScid lnwire.ShortChannelID) lnwire.FailureMessage {
- return &lnwire.FailTemporaryNodeFailure{}
- }
- // First, we'll create a new instance of our orchestrator.
- mo := newMailOrchestrator(&mailOrchConfig{
- failMailboxUpdate: failMailboxUpdate,
- forwardPackets: func(_ chan struct{},
- pkts ...*htlcPacket) error {
- return nil
- },
- clock: clock.NewTestClock(time.Now()),
- expiry: testExpiry,
- })
- defer mo.Stop()
- // We'll be delivering 10 htlc packets via the orchestrator.
- const numPackets = 10
- const halfPackets = numPackets / 2
- // Before any mailbox is created or made live, we will deliver half of
- // the htlcs via the orchestrator.
- chanID1, chanID2, aliceChanID, bobChanID := genIDs()
- sentPackets := make([]*htlcPacket, halfPackets)
- for i := 0; i < halfPackets; i++ {
- pkt := &htlcPacket{
- outgoingChanID: aliceChanID,
- outgoingHTLCID: uint64(i),
- incomingChanID: bobChanID,
- incomingHTLCID: uint64(i),
- amount: lnwire.MilliSatoshi(prand.Int63()),
- htlc: &lnwire.UpdateAddHTLC{
- ID: uint64(i),
- },
- }
- sentPackets[i] = pkt
- mo.Deliver(pkt.outgoingChanID, pkt)
- }
- // Now, initialize a new mailbox for Alice's chanid.
- mailbox := mo.GetOrCreateMailBox(chanID1, aliceChanID)
- // Verify that no messages are received, since Alice's mailbox has not
- // been made live.
- for i := 0; i < halfPackets; i++ {
- timeout := time.After(50 * time.Millisecond)
- select {
- case <-mailbox.MessageOutBox():
- t.Fatalf("should not receive wire msg after reset")
- case <-timeout:
- }
- }
- // Assign a short chan id to the existing mailbox, make it available for
- // capturing incoming HTLCs. The HTLCs added above should be delivered
- // immediately.
- mo.BindLiveShortChanID(mailbox, chanID1, aliceChanID)
- // Verify that all of the packets are queued and delivered to Alice's
- // mailbox.
- recvdPackets := make([]*htlcPacket, 0, len(sentPackets))
- for i := 0; i < halfPackets; i++ {
- timeout := time.After(5 * time.Second)
- select {
- case <-timeout:
- t.Fatalf("didn't recv pkt %d after timeout", i)
- case pkt := <-mailbox.PacketOutBox():
- recvdPackets = append(recvdPackets, pkt)
- }
- }
- // We should have received half of the total number of packets.
- if len(recvdPackets) != halfPackets {
- t.Fatalf("expected %v packets instead got %v",
- halfPackets, len(recvdPackets))
- }
- // Check that the received packets are equal to the sent packets.
- if !reflect.DeepEqual(recvdPackets, sentPackets) {
- t.Fatalf("recvd packets mismatched: expected %v, got %v",
- spew.Sdump(sentPackets), spew.Sdump(recvdPackets))
- }
- // For the second half of the test, create a new mailbox for Bob and
- // immediately make it live with an assigned short chan id.
- mailbox = mo.GetOrCreateMailBox(chanID2, bobChanID)
- mo.BindLiveShortChanID(mailbox, chanID2, bobChanID)
- // Create the second half of our htlcs, and deliver them via the
- // orchestrator. We should be able to receive each of these in order.
- recvdPackets = make([]*htlcPacket, 0, len(sentPackets))
- for i := 0; i < halfPackets; i++ {
- pkt := &htlcPacket{
- outgoingChanID: aliceChanID,
- outgoingHTLCID: uint64(halfPackets + i),
- incomingChanID: bobChanID,
- incomingHTLCID: uint64(halfPackets + i),
- amount: lnwire.MilliSatoshi(prand.Int63()),
- htlc: &lnwire.UpdateAddHTLC{
- ID: uint64(halfPackets + i),
- },
- }
- sentPackets[i] = pkt
- mo.Deliver(pkt.incomingChanID, pkt)
- timeout := time.After(50 * time.Millisecond)
- select {
- case <-timeout:
- t.Fatalf("didn't recv pkt %d after timeout", halfPackets+i)
- case pkt := <-mailbox.PacketOutBox():
- recvdPackets = append(recvdPackets, pkt)
- }
- }
- // Again, we should have received half of the total number of packets.
- if len(recvdPackets) != halfPackets {
- t.Fatalf("expected %v packets instead got %v",
- halfPackets, len(recvdPackets))
- }
- // Check that the received packets are equal to the sent packets.
- if !reflect.DeepEqual(recvdPackets, sentPackets) {
- t.Fatalf("recvd packets mismatched: expected %v, got %v",
- spew.Sdump(sentPackets), spew.Sdump(recvdPackets))
- }
- }
|