diff --git a/htlcswitch/link_test.go b/htlcswitch/link_test.go index 819ae747..9db53720 100644 --- a/htlcswitch/link_test.go +++ b/htlcswitch/link_test.go @@ -92,22 +92,24 @@ func TestChannelLinkSingleHopPayment(t *testing.T) { n.firstBobChannelLink.ChanID())) } + var amount btcutil.Amount = btcutil.SatoshiPerBitcoin + htlcAmt, totalTimelock, hops := generateHops(amount, n.firstBobChannelLink) + // Wait for: // * HTLC add request to be sent to bob. // * alice<->bob commitment state to be updated. // * settle request to be sent back from bob to alice. // * alice<->bob commitment state to be updated. // * user notification to be sent. - var amount btcutil.Amount = btcutil.SatoshiPerBitcoin - invoice, err := n.makePayment([]Peer{ - n.aliceServer, - n.bobServer, - }, amount) + invoice, err := n.makePayment(n.aliceServer, n.bobServer, + n.bobServer.PubKey(), hops, amount, htlcAmt, totalTimelock) if err != nil { t.Fatalf("unable to make the payment: %v", err) } // Wait for Bob to receive the revocation. + // + // TODO(roasbef); replace with select over returned err chan time.Sleep(100 * time.Millisecond) // Check that alice invoice was settled and bandwidth of HTLC @@ -153,26 +155,30 @@ func TestChannelLinkBidirectionalOneHopPayments(t *testing.T) { n.firstBobChannelLink.ChanID())) } + const amt btcutil.Amount = 10 + + htlcAmt, totalTimelock, hopsForwards := generateHops(amt, + n.firstBobChannelLink) + _, _, hopsBackwards := generateHops(amt, n.aliceChannelLink) + // Send max available payment number in both sides, thereby testing // the property of channel link to cope with overflowing. errChan := make(chan error) count := 2 * lnwallet.MaxHTLCNumber for i := 0; i < count/2; i++ { go func() { - _, err := n.makePayment([]Peer{ - n.aliceServer, - n.bobServer, - }, 10) + _, err := n.makePayment(n.aliceServer, n.bobServer, + n.bobServer.PubKey(), hopsForwards, amt, htlcAmt, + totalTimelock) errChan <- err }() } for i := 0; i < count/2; i++ { go func() { - _, err := n.makePayment([]Peer{ - n.bobServer, - n.aliceServer, - }, 10) + _, err := n.makePayment(n.bobServer, n.aliceServer, + n.aliceServer.PubKey(), hopsBackwards, amt, htlcAmt, + totalTimelock) errChan <- err }() } @@ -240,6 +246,10 @@ func TestChannelLinkMultiHopPayment(t *testing.T) { n.carolChannelLink.ChanID())) } + var amount btcutil.Amount = btcutil.SatoshiPerBitcoin + htlcAmt, totalTimelock, hops := generateHops(amount, + n.firstBobChannelLink, n.carolChannelLink) + // Wait for: // * HTLC add request to be sent from Alice to Bob. // * Alice<->Bob commitment states to be updated. @@ -250,12 +260,9 @@ func TestChannelLinkMultiHopPayment(t *testing.T) { // * settle request to be sent back from Bob to Alice. // * Alice<->Bob commitment states to be updated. // * user notification to be sent. - var amount btcutil.Amount = btcutil.SatoshiPerBitcoin - invoice, err := n.makePayment([]Peer{ - n.aliceServer, - n.bobServer, - n.carolServer, - }, amount) + invoice, err := n.makePayment(n.aliceServer, n.carolServer, + n.bobServer.PubKey(), hops, amount, htlcAmt, + totalTimelock) if err != nil { t.Fatalf("unable to send payment: %v", err) } @@ -269,24 +276,31 @@ func TestChannelLinkMultiHopPayment(t *testing.T) { t.Fatal("alice invoice wasn't settled") } - if aliceBandwidthBefore-amount != n.aliceChannelLink.Bandwidth() { - t.Fatal("the bandwidth of alice channel link which handles " + - "alice->bob channel wasn't decreased on htlc amount") + expectedAliceBandwidth := aliceBandwidthBefore - htlcAmt + if expectedAliceBandwidth != n.aliceChannelLink.Bandwidth() { + t.Fatalf("channel bandwidth incorrect: expected %v, got %v", + expectedAliceBandwidth, n.aliceChannelLink.Bandwidth()) } - if firstBobBandwidthBefore+amount != n.firstBobChannelLink.Bandwidth() { - t.Fatal("the bandwidth of bob channel link which handles " + - "alice->bob channel wasn't increased on htlc amount") + expectedBobBandwidth1 := firstBobBandwidthBefore + htlcAmt + if expectedBobBandwidth1 != n.firstBobChannelLink.Bandwidth() { + t.Fatalf("channel bandwidth incorrect: expected %v, got %v", + expectedBobBandwidth1, n.firstBobChannelLink.Bandwidth()) } - if secondBobBandwidthBefore-amount != n.secondBobChannelLink.Bandwidth() { - t.Fatal("the bandwidth of bob channel link which handles " + - "bob->carol channel wasn't decreased on htlc amount") + expectedBobBandwidth2 := secondBobBandwidthBefore - amount + if expectedBobBandwidth2 != n.secondBobChannelLink.Bandwidth() { + t.Fatalf("channel bandwidth incorrect: expected %v, got %v", + expectedBobBandwidth2, n.secondBobChannelLink.Bandwidth()) } - if carolBandwidthBefore+amount != n.carolChannelLink.Bandwidth() { - t.Fatal("the bandwidth of carol channel link which handles " + - "carol->bob channel wasn't decreased on htlc amount") + expectedCarolBandwidth := carolBandwidthBefore + amount + if expectedCarolBandwidth != n.carolChannelLink.Bandwidth() { + t.Fatalf("channel bandwidth incorrect: expected %v, got %v", + expectedCarolBandwidth, n.carolChannelLink.Bandwidth()) + } +} + } } @@ -308,18 +322,18 @@ func TestChannelLinkMultiHopInsufficientPayment(t *testing.T) { secondBobBandwidthBefore := n.secondBobChannelLink.Bandwidth() aliceBandwidthBefore := n.aliceChannelLink.Bandwidth() + var amount btcutil.Amount = 4 * btcutil.SatoshiPerBitcoin + htlcAmt, totalTimelock, hops := generateHops(amount, + n.firstBobChannelLink, n.carolChannelLink) + // Wait for: // * HTLC add request to be sent to from Alice to Bob. // * Alice<->Bob commitment states to be updated. // * Bob trying to add HTLC add request in Bob<->Carol channel. // * Cancel HTLC request to be sent back from Bob to Alice. // * user notification to be sent. - var amount btcutil.Amount = 4 * btcutil.SatoshiPerBitcoin - invoice, err := n.makePayment([]Peer{ - n.aliceServer, - n.bobServer, - n.carolServer, - }, amount) + invoice, err := n.makePayment(n.aliceServer, n.bobServer, + n.bobServer.PubKey(), hops, amount, htlcAmt, totalTimelock) if err == nil { t.Fatal("error haven't been received") } else if err.Error() != errors.New(lnwire.InsufficientCapacity).Error() { @@ -354,7 +368,6 @@ func TestChannelLinkMultiHopInsufficientPayment(t *testing.T) { t.Fatal("the bandwidth of carol channel link which handles " + "bob->carol channel should be the same") } - } // TestChannelLinkMultiHopUnknownPaymentHash checks that we receive remote error @@ -376,19 +389,16 @@ func TestChannelLinkMultiHopUnknownPaymentHash(t *testing.T) { var amount btcutil.Amount = btcutil.SatoshiPerBitcoin - // Generate route convert it to blob, and return next destination for - // htlc add request. - peers := []Peer{ - n.bobServer, - n.carolServer, - } - firstNode, blob, err := generateRoute(peers) + htlcAmt, totalTimelock, hops := generateHops(amount, + n.firstBobChannelLink, n.carolChannelLink) + blob, err := generateRoute(hops...) if err != nil { t.Fatal(err) } // Generate payment: invoice and htlc. - invoice, htlc, err := generatePayment(amount, blob) + invoice, htlc, err := generatePayment(amount, htlcAmt, totalTimelock, + blob) if err != nil { t.Fatal(err) } @@ -403,8 +413,8 @@ func TestChannelLinkMultiHopUnknownPaymentHash(t *testing.T) { } // Send payment and expose err channel. - if _, err := n.aliceServer.htlcSwitch.SendHTLC(firstNode, - htlc); err == nil { + _, err = n.aliceServer.htlcSwitch.SendHTLC(n.bobServer.PubKey(), htlc) + if err == nil { t.Fatal("error wasn't received") } @@ -441,7 +451,7 @@ func TestChannelLinkMultiHopUnknownPaymentHash(t *testing.T) { // TestChannelLinkMultiHopUnknownNextHop construct the chain of hops // Carol<->Bob<->Alice and checks that we receive remote error from Bob if he // has no idea about next hop (hop might goes down and routing info not updated -// yet) +// yet). func TestChannelLinkMultiHopUnknownNextHop(t *testing.T) { n := newThreeHopNetwork(t, btcutil.SatoshiPerBitcoin*3, @@ -458,13 +468,12 @@ func TestChannelLinkMultiHopUnknownNextHop(t *testing.T) { aliceBandwidthBefore := n.aliceChannelLink.Bandwidth() var amount btcutil.Amount = btcutil.SatoshiPerBitcoin + htlcAmt, totalTimelock, hops := generateHops(amount, + n.firstBobChannelLink, n.carolChannelLink) - dave := newMockServer(t, "save") - invoice, err := n.makePayment([]Peer{ - n.aliceServer, - n.bobServer, - dave, - }, amount) + davePub := newMockServer(t, "save").PubKey() + invoice, err := n.makePayment(n.aliceServer, n.bobServer, davePub, hops, + amount, htlcAmt, totalTimelock) if err == nil { t.Fatal("error haven't been received") } else if err.Error() != errors.New(lnwire.UnknownDestination).Error() { @@ -472,6 +481,8 @@ func TestChannelLinkMultiHopUnknownNextHop(t *testing.T) { } // Wait for Alice to receive the revocation. + // + // TODO(roasbeef): add in ntfn hook for state transition completion time.Sleep(100 * time.Millisecond) // Check that alice invoice wasn't settled and bandwidth of htlc @@ -525,11 +536,11 @@ func TestChannelLinkMultiHopDecodeError(t *testing.T) { aliceBandwidthBefore := n.aliceChannelLink.Bandwidth() var amount btcutil.Amount = btcutil.SatoshiPerBitcoin - invoice, err := n.makePayment([]Peer{ - n.aliceServer, - n.bobServer, - n.carolServer, - }, amount) + htlcAmt, totalTimelock, hops := generateHops(amount, + n.firstBobChannelLink, n.carolChannelLink) + + invoice, err := n.makePayment(n.aliceServer, n.carolServer, + n.bobServer.PubKey(), hops, amount, htlcAmt, totalTimelock) if err == nil { t.Fatal("error haven't been received") } else if err.Error() != errors.New(lnwire.SphinxParseError).Error() { @@ -624,7 +635,7 @@ func TestChannelLinkSingleHopMessageOrdering(t *testing.T) { n.aliceServer.record(func(m lnwire.Message) { if getChanID(m) == chanPoint { if len(aliceOrder) == 0 { - t.Fatal("redudant messages") + t.Fatal("redundant messages") } if reflect.TypeOf(aliceOrder[0]) != reflect.TypeOf(m) { @@ -640,7 +651,7 @@ func TestChannelLinkSingleHopMessageOrdering(t *testing.T) { n.bobServer.record(func(m lnwire.Message) { if getChanID(m) == chanPoint { if len(bobOrder) == 0 { - t.Fatal("redudant messages") + t.Fatal("redundant messages") } if reflect.TypeOf(bobOrder[0]) != reflect.TypeOf(m) { @@ -657,16 +668,17 @@ func TestChannelLinkSingleHopMessageOrdering(t *testing.T) { } defer n.stop() + var amount btcutil.Amount = btcutil.SatoshiPerBitcoin + htlcAmt, totalTimelock, hops := generateHops(amount, n.firstBobChannelLink) + // Wait for: // * htlc add htlc request to be sent to alice // * alice<->bob commitment state to be updated // * settle request to be sent back from alice to bob // * alice<->bob commitment state to be updated - var amount btcutil.Amount = btcutil.SatoshiPerBitcoin - if _, err := n.makePayment([]Peer{ - n.aliceServer, - n.bobServer, - }, amount); err != nil { + _, err := n.makePayment(n.aliceServer, n.bobServer, + n.bobServer.PubKey(), hops, amount, htlcAmt, totalTimelock) + if err != nil { t.Fatalf("unable to make the payment: %v", err) } } diff --git a/htlcswitch/mock.go b/htlcswitch/mock.go index f51dff49..48bf3d8f 100644 --- a/htlcswitch/mock.go +++ b/htlcswitch/mock.go @@ -34,7 +34,7 @@ type mockServer struct { name string messages chan lnwire.Message - id []byte + id [33]byte htlcSwitch *Switch registry *mockInvoiceRegistry @@ -44,9 +44,13 @@ type mockServer struct { var _ Peer = (*mockServer)(nil) func newMockServer(t *testing.T, name string) *mockServer { + var id [33]byte + h := sha256.Sum256([]byte(name)) + copy(id[:], h[:]) + return &mockServer{ t: t, - id: []byte(name), + id: id, name: name, messages: make(chan lnwire.Message, 3000), quit: make(chan bool), @@ -91,24 +95,20 @@ func (s *mockServer) Start() error { // mockHopIterator represents the test version of hop iterator which instead // of encrypting the path in onion blob just stores the path as a list of hops. type mockHopIterator struct { - hops []HopID + hops []ForwardingInfo } -func newMockHopIterator(hops ...HopID) HopIterator { +func newMockHopIterator(hops ...ForwardingInfo) HopIterator { return &mockHopIterator{hops: hops} } -func (r *mockHopIterator) Next() *HopID { - if len(r.hops) != 0 { - next := r.hops[0] - r.hops = r.hops[1:] - return &next - } - - return nil +func (r *mockHopIterator) ForwardingInstructions() ForwardingInfo { + h := r.hops[0] + r.hops = r.hops[1:] + return h } -func (r *mockHopIterator) Encode(w io.Writer) error { +func (r *mockHopIterator) EncodeNextHop(w io.Writer) error { var hopLength [4]byte binary.BigEndian.PutUint32(hopLength[:], uint32(len(r.hops))) @@ -117,7 +117,7 @@ func (r *mockHopIterator) Encode(w io.Writer) error { } for _, hop := range r.hops { - if _, err := w.Write(hop[:]); err != nil { + if err := hop.encode(w); err != nil { return err } } @@ -125,15 +125,33 @@ func (r *mockHopIterator) Encode(w io.Writer) error { return nil } +func (f *ForwardingInfo) encode(w io.Writer) error { + if _, err := w.Write([]byte{byte(f.Network)}); err != nil { + return err + } + + if err := binary.Write(w, binary.BigEndian, f.NextHop); err != nil { + return err + } + + if err := binary.Write(w, binary.BigEndian, f.AmountToForward); err != nil { + return err + } + + if err := binary.Write(w, binary.BigEndian, f.OutgoingCTLV); err != nil { + return err + } + + return nil +} + var _ HopIterator = (*mockHopIterator)(nil) // mockIteratorDecoder test version of hop iterator decoder which decodes the // encoded array of hops. type mockIteratorDecoder struct{} -func (p *mockIteratorDecoder) Decode(r io.Reader, meta []byte) ( - HopIterator, error) { - +func (p *mockIteratorDecoder) Decode(r io.Reader, meta []byte) (HopIterator, error) { var b [4]byte _, err := r.Read(b[:]) if err != nil { @@ -141,21 +159,41 @@ func (p *mockIteratorDecoder) Decode(r io.Reader, meta []byte) ( } hopLength := binary.BigEndian.Uint32(b[:]) - hops := make([]HopID, hopLength) + hops := make([]ForwardingInfo, hopLength) for i := uint32(0); i < hopLength; i++ { - var hop HopID - - _, err := r.Read(hop[:]) - if err != nil { + f := &ForwardingInfo{} + if err := f.decode(r); err != nil { return nil, err } - hops[i] = hop + hops[i] = *f } return newMockHopIterator(hops...), nil } +func (f *ForwardingInfo) decode(r io.Reader) error { + var net [1]byte + if _, err := r.Read(net[:]); err != nil { + return err + } + f.Network = NetworkHop(net[0]) + + if err := binary.Read(r, binary.BigEndian, &f.NextHop); err != nil { + return err + } + + if err := binary.Read(r, binary.BigEndian, &f.AmountToForward); err != nil { + return err + } + + if err := binary.Read(r, binary.BigEndian, &f.OutgoingCTLV); err != nil { + return err + } + + return nil +} + // messageInterceptor is function that handles the incoming peer messages and // may decide should we handle it or not. type messageInterceptor func(m lnwire.Message) @@ -218,11 +256,7 @@ func (s *mockServer) readHandler(message lnwire.Message) error { return nil } -func (s *mockServer) ID() [sha256.Size]byte { - return [sha256.Size]byte{} -} - -func (s *mockServer) PubKey() []byte { +func (s *mockServer) PubKey() [33]byte { return s.id } @@ -247,21 +281,27 @@ func (s *mockServer) Stop() { } func (s *mockServer) String() string { - return string(s.id) + return s.name } type mockChannelLink struct { - chanID lnwire.ChannelID - peer Peer + shortChanID lnwire.ShortChannelID + + chanID lnwire.ChannelID + + peer Peer + packets chan *htlcPacket } -func newMockChannelLink(chanID lnwire.ChannelID, +func newMockChannelLink(chanID lnwire.ChannelID, shortChanID lnwire.ShortChannelID, peer Peer) *mockChannelLink { + return &mockChannelLink{ - chanID: chanID, - packets: make(chan *htlcPacket, 1), - peer: peer, + chanID: chanID, + shortChanID: shortChanID, + packets: make(chan *htlcPacket, 1), + peer: peer, } } @@ -272,15 +312,19 @@ func (f *mockChannelLink) HandleSwitchPacket(packet *htlcPacket) { func (f *mockChannelLink) HandleChannelUpdate(lnwire.Message) { } +func (f *mockChannelLink) UpdateForwardingPolicy(_ ForwardingPolicy) { +} + func (f *mockChannelLink) Stats() (uint64, btcutil.Amount, btcutil.Amount) { return 0, 0, 0 } -func (f *mockChannelLink) ChanID() lnwire.ChannelID { return f.chanID } -func (f *mockChannelLink) Bandwidth() btcutil.Amount { return 99999999 } -func (f *mockChannelLink) Peer() Peer { return f.peer } -func (f *mockChannelLink) Start() error { return nil } -func (f *mockChannelLink) Stop() {} +func (f *mockChannelLink) ChanID() lnwire.ChannelID { return f.chanID } +func (f *mockChannelLink) ShortChanID() lnwire.ShortChannelID { return f.shortChanID } +func (f *mockChannelLink) Bandwidth() btcutil.Amount { return 99999999 } +func (f *mockChannelLink) Peer() Peer { return f.peer } +func (f *mockChannelLink) Start() error { return nil } +func (f *mockChannelLink) Stop() {} var _ ChannelLink = (*mockChannelLink)(nil)