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

WIP: Route Blinding [1/n]: TLV Record Definition and Blind Hop Processing #7195

Draft
wants to merge 16 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
1140229
record: add route blinding TLV records to top level onion payload
calvinrzachman Sep 12, 2022
46751a1
record: add route blinding payload TLV records
calvinrzachman Sep 12, 2022
44e0341
multi: update to lightning-onion with route blinding functionality
calvinrzachman Sep 22, 2022
7c8e24c
htlcswitch/hop: (en/de)code route blinding TLV payload
calvinrzachman Nov 6, 2022
45ac059
hop/iterator: use all-zero 32 byte onion HMAC as indicator of final hop
calvinrzachman Nov 6, 2022
b9d84f3
htlcswitch/hop: add invalid blind hop payload error
calvinrzachman Nov 16, 2022
eb929f4
multi: initialize `ChannelLink` with persistent node ID key descriptor
calvinrzachman Sep 21, 2022
332e7fd
lnwire: add route blinding TLV to `UpdateAddHTLC` message
calvinrzachman Sep 21, 2022
29c8b96
lnwallet: add ephemeral route blinding point to `PaymentDescriptor`
calvinrzachman Sep 21, 2022
04b2f18
lnwire: define `InvalidOnionBlinding` failure code onion error
calvinrzachman Sep 22, 2022
41b1eb9
htlcswitch/link: add onion payload validation for route blinding
calvinrzachman Nov 6, 2022
8007315
htlcswitch/link: add blind hop processing to `ChannelLink`
calvinrzachman Nov 11, 2022
dcfeaa7
htlcswitch/link: handle errors encountered during blind hop processing
calvinrzachman Nov 22, 2022
9e2a863
multi: make blind hop processing configurable
calvinrzachman Oct 18, 2022
61cd629
htlcswitch/mock: allow `mockHopIterator` to handle both normal and bl…
calvinrzachman Nov 6, 2022
f4b505f
htlcswitch: add ChannelLink tests for blind hop processing
calvinrzachman Nov 17, 2022
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
8 changes: 7 additions & 1 deletion feature/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ type Config struct {
// keep option-scid-alias support.
NoZeroConf bool

// RouteBlinding sets bit to signal support for route blinding.
RouteBlinding bool

// NoAnySegwit unsets any bits that signal support for using other
// segwit witness versions for co-op closes.
NoAnySegwit bool
Expand Down Expand Up @@ -89,7 +92,7 @@ func newManager(cfg Config, desc setDesc) (*Manager, error) {
}
}

// Now, remove any features as directed by the config.
// Now, add or remove any features as directed by the config.
for set, raw := range fsets {
if cfg.NoTLVOnion {
raw.Unset(lnwire.TLVOnionPayloadOptional)
Expand Down Expand Up @@ -146,6 +149,9 @@ func newManager(cfg Config, desc setDesc) (*Manager, error) {
raw.Unset(lnwire.ZeroConfOptional)
raw.Unset(lnwire.ZeroConfRequired)
}
if cfg.RouteBlinding {
raw.Set(lnwire.RouteBlindingOptional)
}
if cfg.NoAnySegwit {
raw.Unset(lnwire.ShutdownAnySegwitOptional)
raw.Unset(lnwire.ShutdownAnySegwitRequired)
Expand Down
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,10 @@ replace github.com/ulikunitz/xz => github.com/ulikunitz/xz v0.5.8
// https://deps.dev/advisory/OSV/GO-2021-0053?from=%2Fgo%2Fgithub.com%252Fgogo%252Fprotobuf%2Fv1.3.1
replace github.com/gogo/protobuf => github.com/gogo/protobuf v1.3.2

// Swap to lightning-onion dependency which supports route blinding
// replace github.com/lightningnetwork/lightning-onion => /Users/calvinzachman/Documents/Workspace/src/github.com/lightning-onion //../lightning-onion
replace github.com/lightningnetwork/lightning-onion => github.com/calvinrzachman/lightning-onion v1.2.1-0.20220705121410-91a33c83e24d

// If you change this please also update .github/pull_request_template.md and
// docs/INSTALL.md.
go 1.18
Expand Down
5 changes: 3 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0 h1:J9B4L7e3oqhXOcm+2IuNApwzQec85lE+QaikUcCs+dk=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/calvinrzachman/lightning-onion v1.2.1-0.20220705121410-91a33c83e24d h1:N2DvOgIQLyB/rFkpQUPlA+RhTc0JZJhBFM7XaqyInG0=
github.com/calvinrzachman/lightning-onion v1.2.1-0.20220705121410-91a33c83e24d/go.mod h1:IpXmxKG482HAQC/aCn0Sd+DYmKlkfaNF/iUz298HhC0=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054 h1:uH66TXeswKn5PW5zdZ39xEwfS9an067BirqA+P4QaLI=
Expand Down Expand Up @@ -440,8 +442,6 @@ github.com/lightninglabs/neutrino v0.14.2 h1:yrnZUCYMZ5ECtXhgDrzqPq2oX8awoAN2D/c
github.com/lightninglabs/neutrino v0.14.2/go.mod h1:OICUeTCn+4Tu27YRJIpWvvqySxx4oH4vgdP33Sw9RDc=
github.com/lightninglabs/protobuf-hex-display v1.4.3-hex-display h1:RZJ8H4ueU/aQ9pFtx5wqsuD3B/DezrewJeVwDKKYY8E=
github.com/lightninglabs/protobuf-hex-display v1.4.3-hex-display/go.mod h1:2oKOBU042GKFHrdbgGiKax4xVrFiZu51lhacUZQ9MnE=
github.com/lightningnetwork/lightning-onion v1.0.2-0.20220211021909-bb84a1ccb0c5 h1:TkKwqFcQTGYoI+VEqyxA8rxpCin8qDaYX0AfVRinT3k=
github.com/lightningnetwork/lightning-onion v1.0.2-0.20220211021909-bb84a1ccb0c5/go.mod h1:7dDx73ApjEZA0kcknI799m2O5kkpfg4/gr7N092ojNo=
github.com/lightningnetwork/lnd/cert v1.1.1 h1:Nsav0RlIDRbOnzz2Yu69SQlK939IKya3Q2S0mDviIN8=
github.com/lightningnetwork/lnd/cert v1.1.1/go.mod h1:1P46svkkd73oSoeI4zjkVKgZNwGq8bkGuPR8z+5vQUs=
github.com/lightningnetwork/lnd/clock v1.0.1/go.mod h1:KnQudQ6w0IAMZi1SgvecLZQZ43ra2vpDNj7H/aasemg=
Expand Down Expand Up @@ -620,6 +620,7 @@ github.com/tv42/zbase32 v0.0.0-20160707012821-501572607d02 h1:tcJ6OjwOMvExLlzrAV
github.com/tv42/zbase32 v0.0.0-20160707012821-501572607d02/go.mod h1:tHlrkM198S068ZqfrO6S8HsoJq2bF3ETfTL+kt4tInY=
github.com/ulikunitz/xz v0.5.8 h1:ERv8V6GKqVi23rgu5cj9pVfVzJbOqAY2Ntl88O6c2nQ=
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.9 h1:cv3/KhXGBGjEXLC4bH0sLuJ9BewaAbpk5oyMOveu4pw=
github.com/urfave/cli v1.22.9/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
Expand Down
30 changes: 30 additions & 0 deletions htlcswitch/hop/blind_hop.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package hop

import (
"github.com/btcsuite/btcd/btcec/v2"
sphinx "github.com/lightningnetwork/lightning-onion"
"github.com/lightningnetwork/lnd/keychain"
)

type BlindHopProcessor struct{}

func NewBlindHopProcessor() *BlindHopProcessor {
return &BlindHopProcessor{}
}

func (b *BlindHopProcessor) DecryptBlindedPayload(nodeID keychain.SingleKeyECDH,
blindingPoint *btcec.PublicKey, payload []byte) ([]byte, error) {

return sphinx.DecryptBlindedData(
nodeID, blindingPoint, payload,
)
}

func (b *BlindHopProcessor) NextBlindingPoint(nodeID keychain.SingleKeyECDH,
blindingPoint *btcec.PublicKey) (*btcec.PublicKey, error) {

// NOTE(8/8/22): We have pulled the sphinx dependency
// out of 'htlcswitch' only to depend on it here?
// To what end?
return sphinx.NextEphemeral(nodeID, blindingPoint)
}
41 changes: 35 additions & 6 deletions htlcswitch/hop/iterator.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ type Iterator interface {
// along with a failure code to signal if the decoding was successful.
ExtractErrorEncrypter(ErrorEncrypterExtracter) (ErrorEncrypter,
lnwire.FailCode)

// IsFinalHop returns a boolean indicating whether we are the final hop.
IsFinalHop() bool

// NOTE(9/23/22): Could also use a NextHop() and keep check for hop.Exit?
// NextHop() lnwire.ShortChannelID
}

// sphinxHopIterator is the Sphinx implementation of hop iterator which uses
Expand Down Expand Up @@ -90,16 +96,33 @@ func (r *sphinxHopIterator) HopPayload() (*Payload, error) {
// 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:
return NewPayloadFromReader(bytes.NewReader(
r.processedPacket.Payload.Payload,
))
return NewPayloadFromReader(
bytes.NewReader(r.processedPacket.Payload.Payload),
r.IsFinalHop(),
)

default:
return nil, fmt.Errorf("unknown sphinx payload type: %v",
r.processedPacket.Payload.Type)
}
}

// IsFinalHop leverages the processed sphinx packet's
// 'Action' to distinguish whether we are the final hop.
func (r *sphinxHopIterator) IsFinalHop() bool {
return int(r.processedPacket.Action) == 0
}

// func (r *sphinxHopIterator) NextHop() lnwire.ShortChannelID {
// if r.processedPacket.Action == sphinx.ExitNode {
// return Exit
// }

// // NOTE: iterator doesn't have access to the next hop.
// // in the case we're not the exit hop
// return Exit
// }

// ExtractErrorEncrypter decodes and returns the ErrorEncrypter for this hop,
// along with a failure code to signal if the decoding was successful. The
// ErrorEncrypter is used to encrypt errors back to the sender in the event that
Expand Down Expand Up @@ -170,7 +193,7 @@ func (p *OnionProcessor) DecodeHopIterator(r io.Reader, rHash []byte,
// case of a replay, an attacker is *forced* to use the same payment
// hash twice, thereby losing their money entirely.
sphinxPacket, err := p.router.ProcessOnionPacket(
onionPkt, rHash, incomingCltv,
onionPkt, rHash, incomingCltv, nil,
)
if err != nil {
switch err {
Expand Down Expand Up @@ -205,7 +228,7 @@ func (p *OnionProcessor) ReconstructHopIterator(r io.Reader, rHash []byte) (
// associated data in order to thwart attempts a replay attacks. In the
// case of a replay, an attacker is *forced* to use the same payment
// hash twice, thereby losing their money entirely.
sphinxPacket, err := p.router.ReconstructOnionPacket(onionPkt, rHash)
sphinxPacket, err := p.router.ReconstructOnionPacket(onionPkt, rHash, nil)
if err != nil {
return nil, err
}
Expand All @@ -220,6 +243,10 @@ type DecodeHopIteratorRequest struct {
OnionReader io.Reader
RHash []byte
IncomingCltv uint32

// An ephemeral public key which is used to decrypt the onion
// when forwarding in the blinded portion of a route.
BlindingPoint *btcec.PublicKey
}

// DecodeHopIteratorResponse encapsulates the outcome of a batched sphinx onion
Expand Down Expand Up @@ -276,7 +303,7 @@ func (p *OnionProcessor) DecodeHopIterators(id []byte,
}

err = tx.ProcessOnionPacket(
seqNum, onionPkt, req.RHash, req.IncomingCltv,
seqNum, onionPkt, req.RHash, req.IncomingCltv, req.BlindingPoint,
)
switch err {
case nil:
Expand Down Expand Up @@ -383,6 +410,8 @@ func (p *OnionProcessor) DecodeHopIterators(id []byte,
func (p *OnionProcessor) ExtractErrorEncrypter(ephemeralKey *btcec.PublicKey) (
ErrorEncrypter, lnwire.FailCode) {

// TODO(9/21/22): Figure how this changes when handling
// errors for a blinded hop.
onionObfuscator, err := sphinx.NewOnionErrorEncrypter(
p.router, ephemeralKey,
)
Expand Down
61 changes: 61 additions & 0 deletions htlcswitch/hop/iterator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,20 +62,26 @@ func TestSphinxHopIteratorForwardingInstructions(t *testing.T) {
},
Action: sphinx.MoreHops,
ForwardingInstructions: &hopData,
// NOTE(9/15/22): This field will only be populated iff the above Action is MoreHops.
},
expectedFwdInfo: expectedFwdInfo,
},
// A TLV payload, we can leave off the action as we'll always
// read the cid encoded.
// NOTE(9/15/22): The disconnect between sphinx and htlcswitch
// packages w.r.t determining the final/exit hop happened with
// the move to TLV payload?
{
sphinxPacket: &sphinx.ProcessedPacket{
Payload: sphinx.HopPayload{
Type: sphinx.PayloadTLV,
Payload: b.Bytes(),
},
Action: sphinx.MoreHops,
},
expectedFwdInfo: expectedFwdInfo,
},
// TODO(11/16/22): Add test with TLV payload and Action: sphinx.ExitNode?
}

// Finally, we'll test that we get the same set of
Expand All @@ -98,3 +104,58 @@ func TestSphinxHopIteratorForwardingInstructions(t *testing.T) {
}
}
}

// TestSphinxHopIteratorFinalHop confirms that the iterator
// correctly passed through the signal from the underlying
// sphinx implementation as to whether a hop is the last hop
// in the route.
func TestSphinxHopIteratorFinalHop(t *testing.T) {
t.Parallel()

var testCases = []struct {
sphinxPacket *sphinx.ProcessedPacket
expectedFinalHop bool
}{
// A regular legacy payload that signals more hops.
{
sphinxPacket: &sphinx.ProcessedPacket{
Payload: sphinx.HopPayload{
Type: sphinx.PayloadLegacy,
},
Action: sphinx.MoreHops,
},
expectedFinalHop: false,
},
// A regular legacy payload that signals final hop.
{
sphinxPacket: &sphinx.ProcessedPacket{
Payload: sphinx.HopPayload{
Type: sphinx.PayloadLegacy,
},
Action: sphinx.ExitNode,
},
expectedFinalHop: true,
},
// A TLV payload that signals final hop.
{
sphinxPacket: &sphinx.ProcessedPacket{
Payload: sphinx.HopPayload{
Type: sphinx.PayloadTLV,
},
Action: sphinx.ExitNode,
},
expectedFinalHop: true,
},
}

iterator := sphinxHopIterator{}
for _, testCase := range testCases {
iterator.processedPacket = testCase.sphinxPacket

isFinalHop := iterator.IsFinalHop()
if isFinalHop != testCase.expectedFinalHop {
t.Fatalf("expected final hop: %t, got: %t",
testCase.expectedFinalHop, isFinalHop)
}
}
}
Loading