diff --git a/htlcswitch/hop/forwarding_info.go b/htlcswitch/hop/forwarding_info.go new file mode 100644 index 00000000..54b3274c --- /dev/null +++ b/htlcswitch/hop/forwarding_info.go @@ -0,0 +1,29 @@ +package hop + +import ( + "github.com/lightningnetwork/lnd/lnwire" +) + +// ForwardingInfo contains all the information that is necessary to forward and +// incoming HTLC to the next hop encoded within a valid HopIterator instance. +// Forwarding links are to use this information to authenticate the information +// received within the incoming HTLC, to ensure that the prior hop didn't +// tamper with the end-to-end routing information at all. +type ForwardingInfo struct { + // Network is the target blockchain network that the HTLC will travel + // over next. + Network Network + + // NextHop is the channel ID of the next hop. The received HTLC should + // be forwarded to this particular channel in order to continue the + // end-to-end route. + NextHop lnwire.ShortChannelID + + // AmountToForward is the amount of milli-satoshis that the receiving + // node should forward to the next hop. + AmountToForward lnwire.MilliSatoshi + + // OutgoingCTLV is the specified value of the CTLV timelock to be used + // in the outgoing HTLC. + OutgoingCTLV uint32 +} diff --git a/htlcswitch/iterator.go b/htlcswitch/iterator.go index 66869d52..ad01773a 100644 --- a/htlcswitch/iterator.go +++ b/htlcswitch/iterator.go @@ -24,33 +24,6 @@ var ( sourceHop lnwire.ShortChannelID ) -// ForwardingInfo contains all the information that is necessary to forward and -// incoming HTLC to the next hop encoded within a valid HopIterator instance. -// Forwarding links are to use this information to authenticate the information -// received within the incoming HTLC, to ensure that the prior hop didn't -// tamper with the end-to-end routing information at all. -type ForwardingInfo struct { - // Network is the target blockchain network that the HTLC will travel - // over next. - Network hop.Network - - // NextHop is the channel ID of the next hop. The received HTLC should - // be forwarded to this particular channel in order to continue the - // end-to-end route. - NextHop lnwire.ShortChannelID - - // AmountToForward is the amount of milli-satoshis that the receiving - // node should forward to the next hop. - AmountToForward lnwire.MilliSatoshi - - // OutgoingCTLV is the specified value of the CTLV timelock to be used - // in the outgoing HTLC. - OutgoingCTLV uint32 - - // TODO(roasbeef): modify sphinx logic to not just discard the - // remaining bytes, instead should include the rest as excess -} - // HopIterator is an interface that abstracts away the routing information // included in HTLC's which includes the entirety of the payment path of an // HTLC. This interface provides two basic method which carry out: how to @@ -62,7 +35,7 @@ type HopIterator interface { // Additionally, the information encoded within the returned // ForwardingInfo is to be used by each hop to authenticate the // information given to it by the prior hop. - ForwardingInstructions() (ForwardingInfo, error) + ForwardingInstructions() (hop.ForwardingInfo, error) // ExtraOnionBlob returns the additional EOB data (if available). ExtraOnionBlob() []byte @@ -119,7 +92,9 @@ func (r *sphinxHopIterator) EncodeNextHop(w io.Writer) error { // hop to authenticate the information given to it by the prior hop. // // NOTE: Part of the HopIterator interface. -func (r *sphinxHopIterator) ForwardingInstructions() (ForwardingInfo, error) { +func (r *sphinxHopIterator) ForwardingInstructions() ( + hop.ForwardingInfo, error) { + var ( nextHop lnwire.ShortChannelID amt uint64 @@ -154,24 +129,25 @@ func (r *sphinxHopIterator) ForwardingInstructions() (ForwardingInfo, error) { record.NewNextHopIDRecord(&cid), ) if err != nil { - return ForwardingInfo{}, err + return hop.ForwardingInfo{}, err } err = tlvStream.Decode(bytes.NewReader( r.processedPacket.Payload.Payload, )) if err != nil { - return ForwardingInfo{}, err + return hop.ForwardingInfo{}, err } nextHop = lnwire.NewShortChanIDFromInt(cid) default: - return ForwardingInfo{}, fmt.Errorf("unknown sphinx payload "+ - "type: %v", r.processedPacket.Payload.Type) + return hop.ForwardingInfo{}, fmt.Errorf("unknown "+ + "sphinx payload type: %v", + r.processedPacket.Payload.Type) } - return ForwardingInfo{ + return hop.ForwardingInfo{ Network: hop.BitcoinNetwork, NextHop: nextHop, AmountToForward: lnwire.MilliSatoshi(amt), diff --git a/htlcswitch/iterator_test.go b/htlcswitch/iterator_test.go index ae6940e5..08d02439 100644 --- a/htlcswitch/iterator_test.go +++ b/htlcswitch/iterator_test.go @@ -7,6 +7,7 @@ import ( "github.com/davecgh/go-spew/spew" sphinx "github.com/lightningnetwork/lightning-onion" + "github.com/lightningnetwork/lnd/htlcswitch/hop" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/record" "github.com/lightningnetwork/lnd/tlv" @@ -29,7 +30,7 @@ func TestSphinxHopIteratorForwardingInstructions(t *testing.T) { // Next, we'll make the hop forwarding information that we should // extract each type, no matter the payload type. nextAddrInt := binary.BigEndian.Uint64(hopData.NextAddress[:]) - expectedFwdInfo := ForwardingInfo{ + expectedFwdInfo := hop.ForwardingInfo{ NextHop: lnwire.NewShortChanIDFromInt(nextAddrInt), AmountToForward: lnwire.MilliSatoshi(hopData.ForwardAmount), OutgoingCTLV: hopData.OutgoingCltv, @@ -53,7 +54,7 @@ func TestSphinxHopIteratorForwardingInstructions(t *testing.T) { var testCases = []struct { sphinxPacket *sphinx.ProcessedPacket - expectedFwdInfo ForwardingInfo + expectedFwdInfo hop.ForwardingInfo }{ // A regular legacy payload that signals more hops. { diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 661b8a92..60820970 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -16,6 +16,7 @@ import ( "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/contractcourt" "github.com/lightningnetwork/lnd/htlcswitch/hodl" + "github.com/lightningnetwork/lnd/htlcswitch/hop" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/invoices" "github.com/lightningnetwork/lnd/lnpeer" @@ -2849,7 +2850,7 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, // processExitHop handles an htlc for which this link is the exit hop. It // returns a boolean indicating whether the commitment tx needs an update. func (l *channelLink) processExitHop(pd *lnwallet.PaymentDescriptor, - obfuscator ErrorEncrypter, fwdInfo ForwardingInfo, + obfuscator ErrorEncrypter, fwdInfo hop.ForwardingInfo, heightNow uint32, eob []byte) (bool, error) { // If hodl.ExitSettle is requested, we will not validate the final hop's diff --git a/htlcswitch/link_test.go b/htlcswitch/link_test.go index d05e4754..c2427700 100644 --- a/htlcswitch/link_test.go +++ b/htlcswitch/link_test.go @@ -4308,7 +4308,7 @@ func generateHtlcAndInvoice(t *testing.T, htlcAmt := lnwire.NewMSatFromSatoshis(10000) htlcExpiry := testStartingHeight + testInvoiceCltvExpiry - hops := []ForwardingInfo{ + hops := []hop.ForwardingInfo{ { Network: hop.BitcoinNetwork, NextHop: exitHop, diff --git a/htlcswitch/mock.go b/htlcswitch/mock.go index 2256ca54..b76ff51f 100644 --- a/htlcswitch/mock.go +++ b/htlcswitch/mock.go @@ -269,14 +269,16 @@ func (s *mockServer) QuitSignal() <-chan struct{} { // 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 []ForwardingInfo + hops []hop.ForwardingInfo } -func newMockHopIterator(hops ...ForwardingInfo) HopIterator { +func newMockHopIterator(hops ...hop.ForwardingInfo) HopIterator { return &mockHopIterator{hops: hops} } -func (r *mockHopIterator) ForwardingInstructions() (ForwardingInfo, error) { +func (r *mockHopIterator) ForwardingInstructions() ( + hop.ForwardingInfo, error) { + h := r.hops[0] r.hops = r.hops[1:] return h, nil @@ -301,7 +303,7 @@ func (r *mockHopIterator) EncodeNextHop(w io.Writer) error { } for _, hop := range r.hops { - if err := hop.encode(w); err != nil { + if err := encodeFwdInfo(w, &hop); err != nil { return err } } @@ -309,7 +311,7 @@ func (r *mockHopIterator) EncodeNextHop(w io.Writer) error { return nil } -func (f *ForwardingInfo) encode(w io.Writer) error { +func encodeFwdInfo(w io.Writer, f *hop.ForwardingInfo) error { if _, err := w.Write([]byte{byte(f.Network)}); err != nil { return err } @@ -430,10 +432,10 @@ func (p *mockIteratorDecoder) DecodeHopIterator(r io.Reader, rHash []byte, } hopLength := binary.BigEndian.Uint32(b[:]) - hops := make([]ForwardingInfo, hopLength) + hops := make([]hop.ForwardingInfo, hopLength) for i := uint32(0); i < hopLength; i++ { - f := &ForwardingInfo{} - if err := f.decode(r); err != nil { + f := &hop.ForwardingInfo{} + if err := decodeFwdInfo(r, f); err != nil { return nil, lnwire.CodeTemporaryChannelFailure } @@ -481,7 +483,7 @@ func (p *mockIteratorDecoder) DecodeHopIterators(id []byte, return resps, nil } -func (f *ForwardingInfo) decode(r io.Reader) error { +func decodeFwdInfo(r io.Reader, f *hop.ForwardingInfo) error { var net [1]byte if _, err := r.Read(net[:]); err != nil { return err diff --git a/htlcswitch/test_utils.go b/htlcswitch/test_utils.go index 02ddedbc..2e168e5d 100644 --- a/htlcswitch/test_utils.go +++ b/htlcswitch/test_utils.go @@ -597,7 +597,9 @@ func generatePayment(invoiceAmt, htlcAmt lnwire.MilliSatoshi, timelock uint32, } // generateRoute generates the path blob by given array of peers. -func generateRoute(hops ...ForwardingInfo) ([lnwire.OnionPacketSize]byte, error) { +func generateRoute(hops ...hop.ForwardingInfo) ( + [lnwire.OnionPacketSize]byte, error) { + var blob [lnwire.OnionPacketSize]byte if len(hops) == 0 { return blob, errors.New("empty path") @@ -636,12 +638,13 @@ type threeHopNetwork struct { // also the time lock value needed to route an HTLC with the target amount over // the specified path. func generateHops(payAmt lnwire.MilliSatoshi, startingHeight uint32, - path ...*channelLink) (lnwire.MilliSatoshi, uint32, []ForwardingInfo) { + path ...*channelLink) (lnwire.MilliSatoshi, uint32, + []hop.ForwardingInfo) { totalTimelock := startingHeight runningAmt := payAmt - hops := make([]ForwardingInfo, len(path)) + hops := make([]hop.ForwardingInfo, len(path)) for i := len(path) - 1; i >= 0; i-- { // If this is the last hop, then the next hop is the special // "exit node". Otherwise, we look to the "prior" hop. @@ -680,7 +683,7 @@ func generateHops(payAmt lnwire.MilliSatoshi, startingHeight uint32, amount = runningAmt - fee } - hops[i] = ForwardingInfo{ + hops[i] = hop.ForwardingInfo{ Network: hop.BitcoinNetwork, NextHop: nextHop, AmountToForward: amount, @@ -732,7 +735,7 @@ func waitForPayFuncResult(payFunc func() error, d time.Duration) error { // * from Alice to Carol through the Bob // * from Alice to some another peer through the Bob func makePayment(sendingPeer, receivingPeer lnpeer.Peer, - firstHop lnwire.ShortChannelID, hops []ForwardingInfo, + firstHop lnwire.ShortChannelID, hops []hop.ForwardingInfo, invoiceAmt, htlcAmt lnwire.MilliSatoshi, timelock uint32) *paymentResponse { @@ -766,7 +769,7 @@ func makePayment(sendingPeer, receivingPeer lnpeer.Peer, // preparePayment creates an invoice at the receivingPeer and returns a function // that, when called, launches the payment from the sendingPeer. func preparePayment(sendingPeer, receivingPeer lnpeer.Peer, - firstHop lnwire.ShortChannelID, hops []ForwardingInfo, + firstHop lnwire.ShortChannelID, hops []hop.ForwardingInfo, invoiceAmt, htlcAmt lnwire.MilliSatoshi, timelock uint32) (*channeldb.Invoice, func() error, error) { @@ -1247,7 +1250,7 @@ func (n *twoHopNetwork) stop() { } func (n *twoHopNetwork) makeHoldPayment(sendingPeer, receivingPeer lnpeer.Peer, - firstHop lnwire.ShortChannelID, hops []ForwardingInfo, + firstHop lnwire.ShortChannelID, hops []hop.ForwardingInfo, invoiceAmt, htlcAmt lnwire.MilliSatoshi, timelock uint32, preimage lntypes.Preimage) chan error {