Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Public key-based routing #4758

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions htlcswitch/hop/payload.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"fmt"
"io"

"github.com/lightningnetwork/lnd/routing/route"

sphinx "github.com/lightningnetwork/lightning-onion"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/record"
Expand Down Expand Up @@ -89,6 +91,8 @@ type Payload struct {
// customRecords are user-defined records in the custom type range that
// were included in the payload.
customRecords record.CustomSet

NextHopPubKey *route.Vertex
}

// NewLegacyPayload builds a Payload from the amount, cltv, and next hop
Expand All @@ -111,17 +115,19 @@ func NewLegacyPayload(f *sphinx.HopData) *Payload {
// 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
mpp = &record.MPP{}
cid uint64
amt uint64
cltv uint32
mpp = &record.MPP{}
nextHopPubKey [33]byte
)

tlvStream, err := tlv.NewStream(
record.NewAmtToFwdRecord(&amt),
record.NewLockTimeRecord(&cltv),
record.NewNextHopIDRecord(&cid),
mpp.Record(),
record.NewNextHopPubKeyRecord(&nextHopPubKey),
)
if err != nil {
return nil, err
Expand Down Expand Up @@ -159,6 +165,12 @@ func NewPayloadFromReader(r io.Reader) (*Payload, error) {
// Filter out the custom records.
customRecords := NewCustomRecords(parsedTypes)

var pubkey *route.Vertex
if _, ok := parsedTypes[record.NextHopPubKeyType]; ok {
t := route.Vertex(nextHopPubKey)
pubkey = &t
}

return &Payload{
FwdInfo: ForwardingInfo{
Network: BitcoinNetwork,
Expand All @@ -168,6 +180,7 @@ func NewPayloadFromReader(r io.Reader) (*Payload, error) {
},
MPP: mpp,
customRecords: customRecords,
NextHopPubKey: pubkey,
}, nil
}

Expand Down
7 changes: 5 additions & 2 deletions htlcswitch/link.go
Original file line number Diff line number Diff line change
Expand Up @@ -2682,8 +2682,9 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg,

fwdInfo := pld.ForwardingInfo()

switch fwdInfo.NextHop {
case hop.Exit:
isExit := fwdInfo.NextHop == hop.Exit && pld.NextHopPubKey == nil
switch isExit {
case true:
err := l.processExitHop(
pd, obfuscator, fwdInfo, heightNow, pld,
)
Expand Down Expand Up @@ -2748,6 +2749,7 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg,
incomingTimeout: pd.Timeout,
outgoingTimeout: fwdInfo.OutgoingCTLV,
customRecords: pld.CustomRecords(),
nextHopPubKey: pld.NextHopPubKey,
}
switchPackets = append(
switchPackets, updatePacket,
Expand Down Expand Up @@ -2812,6 +2814,7 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg,
incomingTimeout: pd.Timeout,
outgoingTimeout: fwdInfo.OutgoingCTLV,
customRecords: pld.CustomRecords(),
nextHopPubKey: pld.NextHopPubKey,
}

fwdPkg.FwdFilter.Set(idx)
Expand Down
3 changes: 3 additions & 0 deletions htlcswitch/packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"github.com/lightningnetwork/lnd/htlcswitch/hop"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/record"
"github.com/lightningnetwork/lnd/routing/route"
)

// htlcPacket is a wrapper around htlc lnwire update, which adds additional
Expand Down Expand Up @@ -96,6 +97,8 @@ type htlcPacket struct {
// customRecords are user-defined records in the custom type range that
// were included in the payload.
customRecords record.CustomSet

nextHopPubKey *route.Vertex
}

// inKey returns the circuit key used to identify the incoming htlc.
Expand Down
35 changes: 22 additions & 13 deletions htlcswitch/switch.go
Original file line number Diff line number Diff line change
Expand Up @@ -993,23 +993,32 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error {
}

s.indexMtx.RLock()
targetLink, err := s.getLinkByShortID(packet.outgoingChanID)
if err != nil {
s.indexMtx.RUnlock()

log.Debugf("unable to find link with "+
"destination %v", packet.outgoingChanID)
var targetPeerKey [33]byte
if packet.nextHopPubKey == nil {
targetLink, err := s.getLinkByShortID(packet.outgoingChanID)
if err != nil {
s.indexMtx.RUnlock()

log.Debugf("unable to find link with "+
"destination %v", packet.outgoingChanID)

// If packet was forwarded from another channel link
// than we should notify this link that some error
// occurred.
linkError := NewLinkError(
&lnwire.FailUnknownNextPeer{},
)
// If packet was forwarded from another channel link
// than we should notify this link that some error
// occurred.
linkError := NewLinkError(
&lnwire.FailUnknownNextPeer{},
)

return s.failAddPacket(packet, linkError)
}
targetPeerKey = targetLink.Peer().PubKey()
} else {
targetPeerKey = *packet.nextHopPubKey
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EZ 😎

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The system is ready and waiting for this feature 😎


return s.failAddPacket(packet, linkError)
log.Debugf("Using pubkey routing to %x", targetPeerKey)
}
targetPeerKey := targetLink.Peer().PubKey()

interfaceLinks, _ := s.getLinks(targetPeerKey)
s.indexMtx.RUnlock()

Expand Down
5 changes: 3 additions & 2 deletions lnrpc/invoicesrpc/addinvoice.go
Original file line number Diff line number Diff line change
Expand Up @@ -403,8 +403,9 @@ func addHopHint(hopHints *[]func(*zpay32.Invoice),
channel *channeldb.OpenChannel, chanPolicy *channeldb.ChannelEdgePolicy) {

hopHint := zpay32.HopHint{
NodeID: channel.IdentityPub,
ChannelID: channel.ShortChanID().ToUint64(),
NodeID: channel.IdentityPub,
// Zeroed out for privacy reasons.
// ChannelID: channel.ShortChanID().ToUint64(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assume in final form, we'd also need to actually extend the invoices themselves to signal to the sender that they need to be able to support pubkey routing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that is currently signaled implicitly by one or more zero channel ids in the route hints. It would also be nice to reduce the invoice size (and qr codes) by removing the eight zero bytes completely.

FeeBaseMSat: uint32(chanPolicy.FeeBaseMSat),
FeeProportionalMillionths: uint32(
chanPolicy.FeeProportionalMillionths,
Expand Down
6 changes: 6 additions & 0 deletions record/hop.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ const (
// NextHopOnionType is the type used in the onion to reference the ID
// of the next hop.
NextHopOnionType tlv.Type = 6

NextHopPubKeyType tlv.Type = 10
)

// NewAmtToFwdRecord creates a tlv.Record that encodes the amount_to_forward
Expand Down Expand Up @@ -45,3 +47,7 @@ func NewLockTimeRecord(lockTime *uint32) tlv.Record {
func NewNextHopIDRecord(cid *uint64) tlv.Record {
return tlv.MakePrimitiveRecord(NextHopOnionType, cid)
}

func NewNextHopPubKeyRecord(key *[33]byte) tlv.Record {
return tlv.MakePrimitiveRecord(NextHopPubKeyType, key)
}
15 changes: 13 additions & 2 deletions routing/route/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ func (h *Hop) Copy() *Hop {
// references the _outgoing_ channel ID that follows this hop. This field
// follows the same semantics as the NextAddress field in the onion: it should
// be set to zero to indicate the terminal hop.
func (h *Hop) PackHopPayload(w io.Writer, nextChanID uint64) error {
func (h *Hop) PackHopPayload(w io.Writer, nextChanID uint64, nextHopPubKey *Vertex) error {
// If this is a legacy payload, then we'll exit here as this method
// shouldn't be called.
if h.LegacyPayload == true {
Expand All @@ -180,6 +180,13 @@ func (h *Hop) PackHopPayload(w io.Writer, nextChanID uint64) error {
records = append(records,
record.NewNextHopIDRecord(&nextChanID),
)
} else {
if nextHopPubKey != nil {
pubKey := [33]byte(*nextHopPubKey)
records = append(records,
record.NewNextHopPubKeyRecord(&pubKey),
)
}
}

// If an MPP record is destined for this hop, ensure that we only ever
Expand Down Expand Up @@ -449,7 +456,11 @@ func (r *Route) ToSphinxPath() (*sphinx.PaymentPath, error) {
// channel should be forwarded to so we can construct a
// valid payload.
var b bytes.Buffer
err := hop.PackHopPayload(&b, nextHop)
var nextHopPubKey *Vertex
if i < len(r.Hops)-1 {
nextHopPubKey = &r.Hops[i+1].PubKeyBytes
}
err := hop.PackHopPayload(&b, nextHop, nextHopPubKey)
if err != nil {
return nil, err
}
Expand Down