From 83d2112e8b189fb229193d9d3b88f58dcf83c87f Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Fri, 30 Aug 2019 14:05:53 -0700 Subject: [PATCH 1/4] htlcswitch/hop: move NetworkHop to hop.Network --- htlcswitch/hop/network.go | 28 ++++++++++++++++++++++++++++ htlcswitch/iterator.go | 33 +++------------------------------ htlcswitch/link_test.go | 3 ++- htlcswitch/mock.go | 3 ++- htlcswitch/test_utils.go | 3 ++- 5 files changed, 37 insertions(+), 33 deletions(-) create mode 100644 htlcswitch/hop/network.go diff --git a/htlcswitch/hop/network.go b/htlcswitch/hop/network.go new file mode 100644 index 000000000..6f121642e --- /dev/null +++ b/htlcswitch/hop/network.go @@ -0,0 +1,28 @@ +package hop + +// Network indicates the blockchain network that is intended to be the next hop +// for a forwarded HTLC. The existence of this field within the ForwardingInfo +// struct enables the ability for HTLC to cross chain-boundaries at will. +type Network uint8 + +const ( + // BitcoinNetwork denotes that an HTLC is to be forwarded along the + // Bitcoin link with the specified short channel ID. + BitcoinNetwork Network = iota + + // LitecoinNetwork denotes that an HTLC is to be forwarded along the + // Litecoin link with the specified short channel ID. + LitecoinNetwork +) + +// String returns the string representation of the target Network. +func (c Network) String() string { + switch c { + case BitcoinNetwork: + return "Bitcoin" + case LitecoinNetwork: + return "Litecoin" + default: + return "Kekcoin" + } +} diff --git a/htlcswitch/iterator.go b/htlcswitch/iterator.go index f201129ea..66869d524 100644 --- a/htlcswitch/iterator.go +++ b/htlcswitch/iterator.go @@ -8,39 +8,12 @@ import ( "github.com/btcsuite/btcd/btcec" "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" ) -// NetworkHop indicates the blockchain network that is intended to be the next -// hop for a forwarded HTLC. The existence of this field within the -// ForwardingInfo struct enables the ability for HTLC to cross chain-boundaries -// at will. -type NetworkHop uint8 - -const ( - // BitcoinHop denotes that an HTLC is to be forwarded along the Bitcoin - // link with the specified short channel ID. - BitcoinHop NetworkHop = iota - - // LitecoinHop denotes that an HTLC is to be forwarded along the - // Litecoin link with the specified short channel ID. - LitecoinHop -) - -// String returns the string representation of the target NetworkHop. -func (c NetworkHop) String() string { - switch c { - case BitcoinHop: - return "Bitcoin" - case LitecoinHop: - return "Litecoin" - default: - return "Kekcoin" - } -} - var ( // exitHop is a special "hop" which denotes that an incoming HTLC is // meant to pay finally to the receiving node. @@ -59,7 +32,7 @@ var ( type ForwardingInfo struct { // Network is the target blockchain network that the HTLC will travel // over next. - Network NetworkHop + 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 @@ -199,7 +172,7 @@ func (r *sphinxHopIterator) ForwardingInstructions() (ForwardingInfo, error) { } return ForwardingInfo{ - Network: BitcoinHop, + Network: hop.BitcoinNetwork, NextHop: nextHop, AmountToForward: lnwire.MilliSatoshi(amt), OutgoingCTLV: cltv, diff --git a/htlcswitch/link_test.go b/htlcswitch/link_test.go index 3c337c169..d05e47546 100644 --- a/htlcswitch/link_test.go +++ b/htlcswitch/link_test.go @@ -26,6 +26,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/lnpeer" "github.com/lightningnetwork/lnd/lntypes" @@ -4309,7 +4310,7 @@ func generateHtlcAndInvoice(t *testing.T, htlcExpiry := testStartingHeight + testInvoiceCltvExpiry hops := []ForwardingInfo{ { - Network: BitcoinHop, + Network: hop.BitcoinNetwork, NextHop: exitHop, AmountToForward: htlcAmt, OutgoingCTLV: uint32(htlcExpiry), diff --git a/htlcswitch/mock.go b/htlcswitch/mock.go index 34651b5e3..2256ca543 100644 --- a/htlcswitch/mock.go +++ b/htlcswitch/mock.go @@ -23,6 +23,7 @@ import ( "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/contractcourt" + "github.com/lightningnetwork/lnd/htlcswitch/hop" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/invoices" "github.com/lightningnetwork/lnd/lnpeer" @@ -485,7 +486,7 @@ func (f *ForwardingInfo) decode(r io.Reader) error { if _, err := r.Read(net[:]); err != nil { return err } - f.Network = NetworkHop(net[0]) + f.Network = hop.Network(net[0]) if err := binary.Read(r, binary.BigEndian, &f.NextHop); err != nil { return err diff --git a/htlcswitch/test_utils.go b/htlcswitch/test_utils.go index 50a73a234..02ddedbcf 100644 --- a/htlcswitch/test_utils.go +++ b/htlcswitch/test_utils.go @@ -25,6 +25,7 @@ import ( "github.com/go-errors/errors" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/contractcourt" + "github.com/lightningnetwork/lnd/htlcswitch/hop" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnpeer" @@ -680,7 +681,7 @@ func generateHops(payAmt lnwire.MilliSatoshi, startingHeight uint32, } hops[i] = ForwardingInfo{ - Network: BitcoinHop, + Network: hop.BitcoinNetwork, NextHop: nextHop, AmountToForward: amount, OutgoingCTLV: timeLock, From fc0e4be4d89a6caa573d20e744281df78dec5170 Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Fri, 30 Aug 2019 14:11:20 -0700 Subject: [PATCH 2/4] htlcswitch+hop: move ForwardingInfo to hop.ForwaringInfo --- htlcswitch/hop/forwarding_info.go | 29 ++++++++++++++++++++ htlcswitch/iterator.go | 44 +++++++------------------------ htlcswitch/iterator_test.go | 5 ++-- htlcswitch/link.go | 3 ++- htlcswitch/link_test.go | 2 +- htlcswitch/mock.go | 20 +++++++------- htlcswitch/test_utils.go | 17 +++++++----- 7 files changed, 66 insertions(+), 54 deletions(-) create mode 100644 htlcswitch/hop/forwarding_info.go diff --git a/htlcswitch/hop/forwarding_info.go b/htlcswitch/hop/forwarding_info.go new file mode 100644 index 000000000..54b3274c2 --- /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 66869d524..ad01773a9 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 ae6940e5c..08d024392 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 661b8a929..60820970d 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 d05e47546..c2427700f 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 2256ca543..b76ff51f2 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 02ddedbcf..2e168e5df 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 { From 378e0558c57ff48a887c2046647a9f76e5afdb08 Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Fri, 30 Aug 2019 14:11:38 -0700 Subject: [PATCH 3/4] htlcswitch/hop: move hop.Exit and hop.Source to hop pkg --- htlcswitch/circuit_map.go | 7 ++++--- htlcswitch/hop/type.go | 13 +++++++++++++ htlcswitch/iterator.go | 12 +----------- htlcswitch/link.go | 12 ++++++------ htlcswitch/link_test.go | 8 ++++---- htlcswitch/switch.go | 23 ++++++++++++----------- htlcswitch/test_utils.go | 2 +- 7 files changed, 41 insertions(+), 36 deletions(-) create mode 100644 htlcswitch/hop/type.go diff --git a/htlcswitch/circuit_map.go b/htlcswitch/circuit_map.go index 5db36b88d..017e2b781 100644 --- a/htlcswitch/circuit_map.go +++ b/htlcswitch/circuit_map.go @@ -9,6 +9,7 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/go-errors/errors" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/htlcswitch/hop" "github.com/lightningnetwork/lnd/lnwire" ) @@ -311,7 +312,7 @@ func (cm *circuitMap) restoreMemState() error { // documented case of stray keystones emerges for // forwarded payments, this check should be removed, but // with extreme caution. - if strayKeystone.OutKey.ChanID != sourceHop { + if strayKeystone.OutKey.ChanID != hop.Source { continue } @@ -396,9 +397,9 @@ func (cm *circuitMap) trimAllOpenCircuits() error { // First, skip any channels that have not been assigned their // final channel identifier, otherwise we would try to trim - // htlcs belonging to the all-zero, sourceHop ID. + // htlcs belonging to the all-zero, hop.Source ID. chanID := activeChannel.ShortChanID() - if chanID == sourceHop { + if chanID == hop.Source { continue } diff --git a/htlcswitch/hop/type.go b/htlcswitch/hop/type.go new file mode 100644 index 000000000..a167d10e4 --- /dev/null +++ b/htlcswitch/hop/type.go @@ -0,0 +1,13 @@ +package hop + +import "github.com/lightningnetwork/lnd/lnwire" + +var ( + // Exit is a special "hop" denoting that an incoming HTLC is meant to + // pay finally to the receiving node. + Exit lnwire.ShortChannelID + + // Source is a sentinel "hop" denoting that an incoming HTLC is + // initiated by our own switch. + Source lnwire.ShortChannelID +) diff --git a/htlcswitch/iterator.go b/htlcswitch/iterator.go index ad01773a9..4e04508a5 100644 --- a/htlcswitch/iterator.go +++ b/htlcswitch/iterator.go @@ -14,16 +14,6 @@ import ( "github.com/lightningnetwork/lnd/tlv" ) -var ( - // exitHop is a special "hop" which denotes that an incoming HTLC is - // meant to pay finally to the receiving node. - exitHop lnwire.ShortChannelID - - // sourceHop is a sentinel value denoting that an incoming HTLC is - // initiated by our own switch. - sourceHop lnwire.ShortChannelID -) - // 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 @@ -109,7 +99,7 @@ func (r *sphinxHopIterator) ForwardingInstructions() ( switch r.processedPacket.Action { case sphinx.ExitNode: - nextHop = exitHop + nextHop = hop.Exit case sphinx.MoreHops: s := binary.BigEndian.Uint64(fwdInst.NextAddress[:]) nextHop = lnwire.NewShortChanIDFromInt(s) diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 60820970d..f1e1cf249 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -437,8 +437,8 @@ func (l *channelLink) Start() error { // off point, since all indexes below that are committed. This action // is only performed if the link's final short channel ID has been // assigned, otherwise we would try to trim the htlcs belonging to the - // all-zero, sourceHop ID. - if l.ShortChanID() != sourceHop { + // all-zero, hop.Source ID. + if l.ShortChanID() != hop.Source { localHtlcIndex, err := l.channel.NextLocalHtlcIndex() if err != nil { return fmt.Errorf("unable to retrieve next local "+ @@ -536,7 +536,7 @@ func (l *channelLink) WaitForShutdown() { // the all-zero source ID, meaning that the channel has had its ID finalized. func (l *channelLink) EligibleToForward() bool { return l.channel.RemoteNextRevocation() != nil && - l.ShortChanID() != sourceHop + l.ShortChanID() != hop.Source } // sampleNetworkFee samples the current fee rate on the network to get into the @@ -962,7 +962,7 @@ func (l *channelLink) htlcManager() { // only attempt to resolve packages if our short chan id indicates that // the channel is not pending, otherwise we should have no htlcs to // reforward. - if l.ShortChanID() != sourceHop { + if l.ShortChanID() != hop.Source { if err := l.resolveFwdPkgs(); err != nil { l.fail(LinkFailureError{code: ErrInternalError}, "unable to resolve fwd pkgs: %v", err) @@ -2076,7 +2076,7 @@ func (l *channelLink) UpdateShortChanID() (lnwire.ShortChannelID, error) { if err != nil { l.errorf("unable to refresh short_chan_id for chan_id=%v: %v", chanID, err) - return sourceHop, err + return hop.Source, err } sid := l.channel.ShortChanID() @@ -2675,7 +2675,7 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, } switch fwdInfo.NextHop { - case exitHop: + case hop.Exit: updated, err := l.processExitHop( pd, obfuscator, fwdInfo, heightNow, chanIterator.ExtraOnionBlob(), diff --git a/htlcswitch/link_test.go b/htlcswitch/link_test.go index c2427700f..99889a5fb 100644 --- a/htlcswitch/link_test.go +++ b/htlcswitch/link_test.go @@ -1937,7 +1937,7 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) { } addPkt := htlcPacket{ htlc: htlc, - incomingChanID: sourceHop, + incomingChanID: hop.Source, incomingHTLCID: 0, obfuscator: NewMockObfuscator(), } @@ -2017,7 +2017,7 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) { } addPkt = htlcPacket{ htlc: htlc, - incomingChanID: sourceHop, + incomingChanID: hop.Source, incomingHTLCID: 1, obfuscator: NewMockObfuscator(), } @@ -2535,7 +2535,7 @@ func genAddsAndCircuits(numHtlcs int, htlc *lnwire.UpdateAddHTLC) ( for i := 0; i < numHtlcs; i++ { addPkt := htlcPacket{ htlc: htlc, - incomingChanID: sourceHop, + incomingChanID: hop.Source, incomingHTLCID: uint64(i), obfuscator: NewMockObfuscator(), } @@ -4311,7 +4311,7 @@ func generateHtlcAndInvoice(t *testing.T, hops := []hop.ForwardingInfo{ { Network: hop.BitcoinNetwork, - NextHop: exitHop, + NextHop: hop.Exit, AmountToForward: htlcAmt, OutgoingCTLV: uint32(htlcExpiry), }, diff --git a/htlcswitch/switch.go b/htlcswitch/switch.go index bdc50fe5f..286dc72d6 100644 --- a/htlcswitch/switch.go +++ b/htlcswitch/switch.go @@ -16,6 +16,7 @@ import ( "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/contractcourt" + "github.com/lightningnetwork/lnd/htlcswitch/hop" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" @@ -354,7 +355,7 @@ func (s *Switch) GetPaymentResult(paymentID uint64, paymentHash lntypes.Hash, nChan <-chan *networkResult err error outKey = CircuitKey{ - ChanID: sourceHop, + ChanID: hop.Source, HtlcID: paymentID, } ) @@ -428,7 +429,7 @@ func (s *Switch) SendHTLC(firstHop lnwire.ShortChannelID, paymentID uint64, // this stage it means that packet haven't left boundaries of our // system and something wrong happened. packet := &htlcPacket{ - incomingChanID: sourceHop, + incomingChanID: hop.Source, incomingHTLCID: paymentID, outgoingChanID: firstHop, htlc: htlc, @@ -513,7 +514,7 @@ func (s *Switch) forward(packet *htlcPacket) error { return ErrDuplicateAdd case len(actions.Fails) == 1: - if packet.incomingChanID == sourceHop { + if packet.incomingChanID == hop.Source { return err } @@ -1031,14 +1032,14 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error { case *lnwire.UpdateAddHTLC: // Check if the node is set to reject all onward HTLCs and also make // sure that HTLC is not from the source node. - if s.cfg.RejectHTLC && packet.incomingChanID != sourceHop { + if s.cfg.RejectHTLC && packet.incomingChanID != hop.Source { failure := &lnwire.FailChannelDisabled{} addErr := fmt.Errorf("unable to forward any htlcs") return s.failAddPacket(packet, failure, addErr) } - if packet.incomingChanID == sourceHop { + if packet.incomingChanID == hop.Source { // A blank incomingChanID indicates that this is // a pending user-initiated payment. return s.handleLocalDispatch(packet) @@ -1080,7 +1081,7 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error { // If the link doesn't yet have a source chan ID, then // we'll skip it as well. - case link.ShortChanID() == sourceHop: + case link.ShortChanID() == hop.Source: continue } @@ -1222,7 +1223,7 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error { // // TODO(roasbeef): only do this once link actually // fully settles? - localHTLC := packet.incomingChanID == sourceHop + localHTLC := packet.incomingChanID == hop.Source if !localHTLC { s.fwdEventMtx.Lock() s.pendingFwdingEvents = append( @@ -1241,7 +1242,7 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error { // A blank IncomingChanID in a circuit indicates that it is a pending // user-initiated payment. - if packet.incomingChanID == sourceHop { + if packet.incomingChanID == hop.Source { return s.handleLocalDispatch(packet) } @@ -1798,7 +1799,7 @@ func (s *Switch) reforwardResponses() error { shortChanID := openChannel.ShortChanID() // Locally-initiated payments never need reforwarding. - if shortChanID == sourceHop { + if shortChanID == hop.Source { continue } @@ -1994,7 +1995,7 @@ func (s *Switch) AddLink(link ChannelLink) error { } shortChanID := link.ShortChanID() - if shortChanID == sourceHop { + if shortChanID == hop.Source { log.Infof("Adding pending link chan_id=%v, short_chan_id=%v", chanID, shortChanID) @@ -2157,7 +2158,7 @@ func (s *Switch) UpdateShortChanID(chanID lnwire.ChannelID) error { } // Reject any blank short channel ids. - if shortChanID == sourceHop { + if shortChanID == hop.Source { return fmt.Errorf("refusing trivial short_chan_id for chan_id=%v"+ "live link", chanID) } diff --git a/htlcswitch/test_utils.go b/htlcswitch/test_utils.go index 2e168e5df..650170028 100644 --- a/htlcswitch/test_utils.go +++ b/htlcswitch/test_utils.go @@ -648,7 +648,7 @@ func generateHops(payAmt lnwire.MilliSatoshi, startingHeight uint32, 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. - nextHop := exitHop + nextHop := hop.Exit if i != len(path)-1 { nextHop = path[i+1].channel.ShortChanID() } From 138b0798f5b7ea4988dec3c34d18087c2fdddbf1 Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Fri, 30 Aug 2019 14:11:57 -0700 Subject: [PATCH 4/4] htlcswitch/iterator: use hop.Payload constructors --- htlcswitch/hop/payload.go | 75 +++++++++++++++++++++++++++++++++++++++ htlcswitch/iterator.go | 43 +++------------------- 2 files changed, 79 insertions(+), 39 deletions(-) create mode 100644 htlcswitch/hop/payload.go diff --git a/htlcswitch/hop/payload.go b/htlcswitch/hop/payload.go new file mode 100644 index 000000000..1c7e563be --- /dev/null +++ b/htlcswitch/hop/payload.go @@ -0,0 +1,75 @@ +package hop + +import ( + "encoding/binary" + "io" + + "github.com/lightningnetwork/lightning-onion" + "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/record" + "github.com/lightningnetwork/lnd/tlv" +) + +// Payload encapsulates all information delivered to a hop in an onion payload. +// A Hop can represent either a TLV or legacy payload. The primary forwarding +// instruction can be accessed via ForwardingInfo, and additional records can be +// accessed by other member functions. +type Payload struct { + // FwdInfo holds the basic parameters required for HTLC forwarding, e.g. + // amount, cltv, and next hop. + FwdInfo ForwardingInfo +} + +// NewLegacyPayload builds a Payload from the amount, cltv, and next hop +// parameters provided by leegacy onion payloads. +func NewLegacyPayload(f *sphinx.HopData) *Payload { + nextHop := binary.BigEndian.Uint64(f.NextAddress[:]) + + return &Payload{ + FwdInfo: ForwardingInfo{ + Network: BitcoinNetwork, + NextHop: lnwire.NewShortChanIDFromInt(nextHop), + AmountToForward: lnwire.MilliSatoshi(f.ForwardAmount), + OutgoingCTLV: f.OutgoingCltv, + }, + } +} + +// NewPayloadFromReader builds a new Hop from the passed io.Reader. The reader +// should correspond to the bytes encapsulated in a TLV onion payload. +func NewPayloadFromReader(r io.Reader) (*Payload, error) { + var ( + cid uint64 + amt uint64 + cltv uint32 + ) + + tlvStream, err := tlv.NewStream( + record.NewAmtToFwdRecord(&amt), + record.NewLockTimeRecord(&cltv), + record.NewNextHopIDRecord(&cid), + ) + if err != nil { + return nil, err + } + + err = tlvStream.Decode(r) + if err != nil { + return nil, err + } + + return &Payload{ + FwdInfo: ForwardingInfo{ + Network: BitcoinNetwork, + NextHop: lnwire.NewShortChanIDFromInt(cid), + AmountToForward: lnwire.MilliSatoshi(amt), + OutgoingCTLV: cltv, + }, + }, nil +} + +// ForwardingInfo returns the basic parameters required for HTLC forwarding, +// e.g. amount, cltv, and next hop. +func (h *Payload) ForwardingInfo() ForwardingInfo { + return h.FwdInfo +} diff --git a/htlcswitch/iterator.go b/htlcswitch/iterator.go index 4e04508a5..9dfe3a1af 100644 --- a/htlcswitch/iterator.go +++ b/htlcswitch/iterator.go @@ -2,7 +2,6 @@ package htlcswitch import ( "bytes" - "encoding/binary" "fmt" "io" @@ -10,8 +9,6 @@ import ( "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" ) // HopIterator is an interface that abstracts away the routing information @@ -85,64 +82,32 @@ func (r *sphinxHopIterator) EncodeNextHop(w io.Writer) error { func (r *sphinxHopIterator) ForwardingInstructions() ( hop.ForwardingInfo, error) { - var ( - nextHop lnwire.ShortChannelID - amt uint64 - cltv uint32 - ) - switch r.processedPacket.Payload.Type { // If this is the legacy payload, then we'll extract the information // directly from the pre-populated ForwardingInstructions field. case sphinx.PayloadLegacy: fwdInst := r.processedPacket.ForwardingInstructions + p := hop.NewLegacyPayload(fwdInst) - switch r.processedPacket.Action { - case sphinx.ExitNode: - nextHop = hop.Exit - case sphinx.MoreHops: - s := binary.BigEndian.Uint64(fwdInst.NextAddress[:]) - nextHop = lnwire.NewShortChanIDFromInt(s) - } - - amt = fwdInst.ForwardAmount - cltv = fwdInst.OutgoingCltv + return p.ForwardingInfo(), nil // Otherwise, if this is the TLV payload, then we'll make a new stream // to decode only what we need to make routing decisions. case sphinx.PayloadTLV: - var cid uint64 - - tlvStream, err := tlv.NewStream( - record.NewAmtToFwdRecord(&amt), - record.NewLockTimeRecord(&cltv), - record.NewNextHopIDRecord(&cid), - ) - if err != nil { - return hop.ForwardingInfo{}, err - } - - err = tlvStream.Decode(bytes.NewReader( + p, err := hop.NewPayloadFromReader(bytes.NewReader( r.processedPacket.Payload.Payload, )) if err != nil { return hop.ForwardingInfo{}, err } - nextHop = lnwire.NewShortChanIDFromInt(cid) + return p.ForwardingInfo(), nil default: return hop.ForwardingInfo{}, fmt.Errorf("unknown "+ "sphinx payload type: %v", r.processedPacket.Payload.Type) } - - return hop.ForwardingInfo{ - Network: hop.BitcoinNetwork, - NextHop: nextHop, - AmountToForward: lnwire.MilliSatoshi(amt), - OutgoingCTLV: cltv, - }, nil } // ExtraOnionBlob returns the additional EOB data (if available).