diff --git a/htlcswitch/circuit.go b/htlcswitch/circuit.go index 278cac1b..1aa67d36 100644 --- a/htlcswitch/circuit.go +++ b/htlcswitch/circuit.go @@ -105,6 +105,7 @@ func (m *circuitMap) remove(key circuitKey) (*paymentCircuit, error) { if circuit.RefCount--; circuit.RefCount == 0 { delete(m.circuits, key) } + return circuit, nil } diff --git a/htlcswitch/switch_test.go b/htlcswitch/switch_test.go index a6c62c51..a1df2dfd 100644 --- a/htlcswitch/switch_test.go +++ b/htlcswitch/switch_test.go @@ -22,6 +22,9 @@ var ( chanID1 = lnwire.NewChanIDFromOutPoint(chanPoint1) chanID2 = lnwire.NewChanIDFromOutPoint(chanPoint2) + + aliceChanID = lnwire.NewShortChanIDFromInt(1) + bobChanID = lnwire.NewShortChanIDFromInt(2) ) // TestSwitchForward checks the ability of htlc switch to forward add/settle @@ -32,8 +35,8 @@ func TestSwitchForward(t *testing.T) { alicePeer := newMockServer(t, "alice") bobPeer := newMockServer(t, "bob") - aliceChannelLink := newMockChannelLink(chanID1, alicePeer) - bobChannelLink := newMockChannelLink(chanID2, bobPeer) + aliceChannelLink := newMockChannelLink(chanID1, aliceChanID, alicePeer) + bobChannelLink := newMockChannelLink(chanID2, bobChanID, bobPeer) s := New(Config{}) s.Start() @@ -44,13 +47,13 @@ func TestSwitchForward(t *testing.T) { t.Fatalf("unable to add bob link: %v", err) } - // Create request which should be forwarder from alice channel - // link to bob channel link. + // Create request which should be forwarded from Alice channel link to + // bob channel link. preimage := [sha256.Size]byte{1} rhash := fastsha256.Sum256(preimage[:]) packet = newAddPacket( - aliceChannelLink.ChanID(), - NewHopID(bobChannelLink.Peer().PubKey()), + aliceChannelLink.ShortChanID(), + bobChannelLink.ShortChanID(), &lnwire.UpdateAddHTLC{ PaymentHash: rhash, Amount: 1, @@ -73,11 +76,11 @@ func TestSwitchForward(t *testing.T) { t.Fatal("wrong amount of circuits") } - // Create settle request pretending that bob link handled - // the add htlc request and sent the htlc settle request back. This - // request should be forwarder back to alice link. + // Create settle request pretending that bob link handled the add htlc + // request and sent the htlc settle request back. This request should + // be forwarder back to Alice link. packet = newSettlePacket( - bobChannelLink.ChanID(), + bobChannelLink.ShortChanID(), &lnwire.UpdateFufillHTLC{ PaymentPreimage: preimage, }, @@ -92,7 +95,7 @@ func TestSwitchForward(t *testing.T) { case <-aliceChannelLink.packets: break case <-time.After(time.Second): - t.Fatal("request was not propogated to channelPoint") + t.Fatal("request was not propagated to channelPoint") } if s.circuits.pending() != 0 { @@ -108,8 +111,8 @@ func TestSwitchCancel(t *testing.T) { alicePeer := newMockServer(t, "alice") bobPeer := newMockServer(t, "bob") - aliceChannelLink := newMockChannelLink(chanID1, alicePeer) - bobChannelLink := newMockChannelLink(chanID2, bobPeer) + aliceChannelLink := newMockChannelLink(chanID1, aliceChanID, alicePeer) + bobChannelLink := newMockChannelLink(chanID2, bobChanID, bobPeer) s := New(Config{}) s.Start() @@ -125,8 +128,8 @@ func TestSwitchCancel(t *testing.T) { preimage := [sha256.Size]byte{1} rhash := fastsha256.Sum256(preimage[:]) request = newAddPacket( - aliceChannelLink.ChanID(), - NewHopID(bobChannelLink.Peer().PubKey()), + aliceChannelLink.ShortChanID(), + bobChannelLink.ShortChanID(), &lnwire.UpdateAddHTLC{ PaymentHash: rhash, Amount: 1, @@ -153,7 +156,7 @@ func TestSwitchCancel(t *testing.T) { // the add htlc request and sent the htlc settle request back. This // request should be forwarder back to alice channel link. request = newFailPacket( - bobChannelLink.ChanID(), + bobChannelLink.ShortChanID(), &lnwire.UpdateFailHTLC{}, rhash, 1) @@ -182,8 +185,8 @@ func TestSwitchAddSamePayment(t *testing.T) { alicePeer := newMockServer(t, "alice") bobPeer := newMockServer(t, "bob") - aliceChannelLink := newMockChannelLink(chanID1, alicePeer) - bobChannelLink := newMockChannelLink(chanID2, bobPeer) + aliceChannelLink := newMockChannelLink(chanID1, aliceChanID, alicePeer) + bobChannelLink := newMockChannelLink(chanID2, bobChanID, bobPeer) s := New(Config{}) s.Start() @@ -199,8 +202,8 @@ func TestSwitchAddSamePayment(t *testing.T) { preimage := [sha256.Size]byte{1} rhash := fastsha256.Sum256(preimage[:]) request = newAddPacket( - aliceChannelLink.ChanID(), - NewHopID(bobChannelLink.Peer().PubKey()), + aliceChannelLink.ShortChanID(), + bobChannelLink.ShortChanID(), &lnwire.UpdateAddHTLC{ PaymentHash: rhash, Amount: 1, @@ -236,7 +239,7 @@ func TestSwitchAddSamePayment(t *testing.T) { // the add htlc request and sent the htlc settle request back. This // request should be forwarder back to alice channel link. request = newFailPacket( - bobChannelLink.ChanID(), + bobChannelLink.ShortChanID(), &lnwire.UpdateFailHTLC{}, rhash, 1) @@ -277,7 +280,7 @@ func TestSwitchAddSamePayment(t *testing.T) { // users when response is came back from channel link. func TestSwitchSendPayment(t *testing.T) { alicePeer := newMockServer(t, "alice") - aliceChannelLink := newMockChannelLink(chanID1, alicePeer) + aliceChannelLink := newMockChannelLink(chanID1, aliceChanID, alicePeer) s := New(Config{}) s.Start() @@ -334,15 +337,18 @@ func TestSwitchSendPayment(t *testing.T) { t.Fatal("wrong amount of circuits") } - // Create fail request pretending that bob channel link handled - // the add htlc request with error and sent the htlc fail request - // back. This request should be forwarder back to alice channel link. - packet := newFailPacket(aliceChannelLink.ChanID(), + // Create fail request pretending that bob channel link handled the add + // htlc request with error and sent the htlc fail request back. This + // request should be forwarder back to alice channel link. + packet := newFailPacket( + aliceChannelLink.ShortChanID(), &lnwire.UpdateFailHTLC{ Reason: []byte{byte(lnwire.IncorrectValue)}, ID: 1, }, - rhash, 1) + rhash, + 1, + ) if err := s.forward(packet); err != nil { t.Fatalf("can't forward htlc packet: %v", err) diff --git a/htlcswitch/test_utils.go b/htlcswitch/test_utils.go index c0aebd53..214550c9 100644 --- a/htlcswitch/test_utils.go +++ b/htlcswitch/test_utils.go @@ -47,7 +47,7 @@ func generateRandomBytes(n int) ([]byte, error) { // createTestChannel creates the channel and returns our and remote channels // representations. func createTestChannel(alicePrivKey, bobPrivKey []byte, - aliceAmount, bobAmount btcutil.Amount) ( + aliceAmount, bobAmount btcutil.Amount, chanID lnwire.ShortChannelID) ( *lnwallet.LightningChannel, *lnwallet.LightningChannel, func(), error) { aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes(btcec.S256(), alicePrivKey) @@ -167,6 +167,7 @@ func createTestChannel(alicePrivKey, bobPrivKey []byte, RevocationStore: shachain.NewRevocationStore(), TheirDustLimit: bobDustLimit, OurDustLimit: aliceDustLimit, + ShortChanID: chanID, Db: dbAlice, } bobChannelState := &channeldb.OpenChannel{ @@ -193,6 +194,7 @@ func createTestChannel(alicePrivKey, bobPrivKey []byte, RevocationStore: shachain.NewRevocationStore(), TheirDustLimit: aliceDustLimit, OurDustLimit: bobDustLimit, + ShortChanID: chanID, Db: dbBob, } @@ -243,8 +245,8 @@ func getChanID(msg lnwire.Message) lnwire.ChannelID { // generatePayment generates the htlc add request by given path blob and // invoice which should be added by destination peer. -func generatePayment(amount btcutil.Amount, blob [lnwire.OnionPacketSize]byte) ( - *channeldb.Invoice, *lnwire.UpdateAddHTLC, error) { +func generatePayment(invoiceAmt, htlcAmt btcutil.Amount, timelock uint32, + blob [lnwire.OnionPacketSize]byte) (*channeldb.Invoice, *lnwire.UpdateAddHTLC, error) { // Initialize random seed with unix time in order to generate random // preimage every time. @@ -256,46 +258,42 @@ func generatePayment(amount btcutil.Amount, blob [lnwire.OnionPacketSize]byte) ( return nil, nil, err } copy(preimage[:], r) + rhash := fastsha256.Sum256(preimage[:]) - // Generate and add the invoice in carol invoice registry as far as - // htlc request should go to the - return &channeldb.Invoice{ - CreationDate: time.Now(), - Terms: channeldb.ContractTerm{ - Value: amount, - PaymentPreimage: preimage, - }, + invoice := &channeldb.Invoice{ + CreationDate: time.Now(), + Terms: channeldb.ContractTerm{ + Value: invoiceAmt, + PaymentPreimage: preimage, }, - &lnwire.UpdateAddHTLC{ - PaymentHash: rhash, - Amount: amount, - OnionBlob: blob, - }, nil + } + + htlc := &lnwire.UpdateAddHTLC{ + PaymentHash: rhash, + Amount: htlcAmt, + Expiry: timelock, + OnionBlob: blob, + } + + return invoice, htlc, nil } // generateRoute generates the path blob by given array of peers. -func generateRoute(peers []Peer) ([]byte, [lnwire.OnionPacketSize]byte, error) { +func generateRoute(hops ...ForwardingInfo) ([lnwire.OnionPacketSize]byte, error) { var blob [lnwire.OnionPacketSize]byte - if len(peers) == 0 { - return nil, blob, errors.New("empty path") + if len(hops) == 0 { + return blob, errors.New("empty path") } - // Create array of hops in order to create onion blob. - hops := make([]HopID, len(peers)-1) - for i, peer := range peers[1:] { - hops[i] = NewHopID(peer.PubKey()) - } - - // Initialize iterator and encode it. - var b bytes.Buffer iterator := newMockHopIterator(hops...) - if err := iterator.Encode(&b); err != nil { - return nil, blob, err - } - copy(blob[:], b.Bytes()) - return peers[0].PubKey(), blob, nil + w := bytes.NewBuffer(blob[0:0]) + if err := iterator.EncodeNextHop(w); err != nil { + return blob, err + } + + return blob, nil } @@ -313,6 +311,60 @@ type threeHopNetwork struct { firstChannelCleanup func() secondChannelCleanup func() + + globalPolicy ForwardingPolicy +} + +// generateHops... +func generateHops(payAmt btcutil.Amount, + path ...*channelLink) (btcutil.Amount, uint32, []ForwardingInfo) { + + lastHop := path[len(path)-1] + + var ( + runningAmt btcutil.Amount = payAmt + totalTimelock uint32 + ) + + hops := make([]ForwardingInfo, len(path)) + for i := len(path) - 1; i >= 0; i-- { + nextHop := exitHop + if i != len(path)-1 { + nextHop = path[i+1].channel.ShortChanID() + } + + timeLock := lastHop.cfg.FwrdingPolicy.TimeLockDelta + totalTimelock += timeLock + + if i != len(path)-1 { + delta := path[i].cfg.FwrdingPolicy.TimeLockDelta + timeLock = totalTimelock - delta + } + + amount := payAmt + if i != len(path)-1 { + prevHop := hops[i+1] + prevAmount := prevHop.AmountToForward + + fee := ExpectedFee(path[i].cfg.FwrdingPolicy, prevAmount) + runningAmt += fee + + if i == 0 { + amount = prevAmount + } else { + amount = prevAmount + fee + } + } + + hops[i] = ForwardingInfo{ + Network: BitcoinHop, + NextHop: nextHop, + AmountToForward: amount, + OutgoingCTLV: timeLock, + } + } + + return runningAmt, totalTimelock, hops } // makePayment takes the destination node and amount as input, sends the @@ -323,36 +375,37 @@ type threeHopNetwork struct { // * from Alice to Bob // * from Alice to Carol through the Bob // * from Alice to some another peer through the Bob -func (n *threeHopNetwork) makePayment(peers []Peer, - amount btcutil.Amount) (*channeldb.Invoice, error) { +func (n *threeHopNetwork) makePayment(sendingPeer, receivingPeer Peer, + firstHopPub [33]byte, hops []ForwardingInfo, + invoiceAmt, htlcAmt btcutil.Amount, + timelock uint32) (*channeldb.Invoice, error) { - // Extract sender peer. - senderPeer := peers[0].(*mockServer) - peers = peers[1:] + sender := sendingPeer.(*mockServer) + receiver := receivingPeer.(*mockServer) // Generate route convert it to blob, and return next destination for // htlc add request. - firstNode, blob, err := generateRoute(peers) + blob, err := generateRoute(hops...) if err != nil { return nil, err } // Generate payment: invoice and htlc. - invoice, htlc, err := generatePayment(amount, blob) + invoice, htlc, err := generatePayment(invoiceAmt, htlcAmt, timelock, + blob) if err != nil { return nil, err } // Check who is last in the route and add invoice to server registry. - receiverPeer := peers[len(peers)-1].(*mockServer) - if err := receiverPeer.registry.AddInvoice(invoice); err != nil { + if err := receiver.registry.AddInvoice(invoice); err != nil { return nil, err } // Send payment and expose err channel. errChan := make(chan error) go func() { - _, err := senderPeer.htlcSwitch.SendHTLC(firstNode, htlc) + _, err := sender.htlcSwitch.SendHTLC(firstHopPub, htlc) errChan <- err }() @@ -360,7 +413,7 @@ func (n *threeHopNetwork) makePayment(peers []Peer, case err := <-errChan: return invoice, err case <-time.After(12 * time.Second): - return invoice, errors.New("htlc was no settled in time") + return invoice, errors.New("htlc was not settled in time") } } @@ -431,63 +484,80 @@ func newThreeHopNetwork(t *testing.T, aliceToBob, // route which htlc should follow. decoder := &mockIteratorDecoder{} + firstChanID := lnwire.NewShortChanIDFromInt(4) + secondChanID := lnwire.NewShortChanIDFromInt(5) + // Create lightning channels between Alice<->Bob and Bob<->Carol aliceChannel, firstBobChannel, fCleanUp, err := createTestChannel( - alicePrivKey, bobPrivKey, aliceToBob, aliceToBob) + alicePrivKey, bobPrivKey, aliceToBob, aliceToBob, firstChanID) if err != nil { t.Fatalf("unable to create alice<->bob channel: %v", err) } secondBobChannel, carolChannel, sCleanUp, err := createTestChannel( - bobPrivKey, carolPrivKey, bobToCarol, bobToCarol) + bobPrivKey, carolPrivKey, bobToCarol, bobToCarol, secondChanID) if err != nil { t.Fatalf("unable to create bob<->carol channel: %v", err) } + globalPolicy := ForwardingPolicy{ + MinHTLC: 5, + BaseFee: btcutil.Amount(1), + TimeLockDelta: 1, + } + aliceChannelLink := NewChannelLink( - &ChannelLinkConfig{ - // htlc responses will be sent to this node - Peer: bobServer, - // htlc will be propagated to this switch - Switch: aliceServer.htlcSwitch, - // route will be generated by this decoder - DecodeOnion: decoder.Decode, - Registry: aliceServer.registry, - }, aliceChannel) + ChannelLinkConfig{ + FwrdingPolicy: globalPolicy, + Peer: bobServer, + Switch: aliceServer.htlcSwitch, + DecodeOnion: decoder.Decode, + Registry: aliceServer.registry, + }, + aliceChannel, + ) if err := aliceServer.htlcSwitch.addLink(aliceChannelLink); err != nil { t.Fatalf("unable to add alice channel link: %v", err) } firstBobChannelLink := NewChannelLink( - &ChannelLinkConfig{ - Peer: aliceServer, - Switch: bobServer.htlcSwitch, - DecodeOnion: decoder.Decode, - Registry: bobServer.registry, - }, firstBobChannel) + ChannelLinkConfig{ + FwrdingPolicy: globalPolicy, + Peer: aliceServer, + Switch: bobServer.htlcSwitch, + DecodeOnion: decoder.Decode, + Registry: bobServer.registry, + }, + firstBobChannel, + ) if err := bobServer.htlcSwitch.addLink(firstBobChannelLink); err != nil { t.Fatalf("unable to add first bob channel link: %v", err) } secondBobChannelLink := NewChannelLink( - &ChannelLinkConfig{ - Peer: carolServer, - Switch: bobServer.htlcSwitch, - DecodeOnion: decoder.Decode, - Registry: bobServer.registry, - }, secondBobChannel) - + ChannelLinkConfig{ + FwrdingPolicy: globalPolicy, + Peer: carolServer, + Switch: bobServer.htlcSwitch, + DecodeOnion: decoder.Decode, + Registry: bobServer.registry, + }, + secondBobChannel, + ) if err := bobServer.htlcSwitch.addLink(secondBobChannelLink); err != nil { t.Fatalf("unable to add second bob channel link: %v", err) } carolChannelLink := NewChannelLink( - &ChannelLinkConfig{ - Peer: bobServer, - Switch: carolServer.htlcSwitch, - DecodeOnion: decoder.Decode, - Registry: carolServer.registry, - }, carolChannel) + ChannelLinkConfig{ + FwrdingPolicy: globalPolicy, + Peer: bobServer, + Switch: carolServer.htlcSwitch, + DecodeOnion: decoder.Decode, + Registry: carolServer.registry, + }, + carolChannel, + ) if err := carolServer.htlcSwitch.addLink(carolChannelLink); err != nil { t.Fatalf("unable to add carol channel link: %v", err) } @@ -503,5 +573,7 @@ func newThreeHopNetwork(t *testing.T, aliceToBob, firstChannelCleanup: fCleanUp, secondChannelCleanup: sCleanUp, + + globalPolicy: globalPolicy, } }