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

[1/4] Route Blinding Receives: Groundwork #8752

Merged
merged 11 commits into from
Jul 11, 2024
16 changes: 14 additions & 2 deletions cmd/lncli/cmd_payments.go
Original file line number Diff line number Diff line change
Expand Up @@ -1099,8 +1099,13 @@ var queryRoutesCommand = cli.Command{
},
cli.Int64Flag{
Name: "final_cltv_delta",
Usage: "(optional) number of blocks the last hop has to reveal " +
"the preimage",
Usage: "(optional) number of blocks the last hop has " +
"to reveal the preimage. Note that this " +
"should not be set in the case where the " +
"path includes a blinded path since in " +
"that case, the receiver will already have " +
"accounted for this value in the " +
"blinded_cltv value",
},
cli.BoolFlag{
Name: "use_mc",
Expand Down Expand Up @@ -1238,6 +1243,13 @@ func parseBlindedPaymentParameters(ctx *cli.Context) (
return nil, nil
}

// If a blinded path has been provided, then the final_cltv_delta flag
// should not be provided since this value will be ignored.
if ctx.IsSet("final_cltv_delta") {
return nil, fmt.Errorf("`final_cltv_delta` should not be " +
"provided if a blinded path is provided")
}

// If any one of our blinding related flags is set, we expect the
// full set to be set and we'll error out accordingly.
introNode, err := route.NewVertexFromStr(
Expand Down
3 changes: 3 additions & 0 deletions docs/release-notes/release-notes-0.18.3.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@
channel. We will still wait for the channel to have at least one confirmation
and so the main change here is that we don't error out for such a case.

* [Groundwork](https://github.com/lightningnetwork/lnd/pull/8752) in preparation
for implementing route blinding receives.

## Testing
## Database

Expand Down
31 changes: 27 additions & 4 deletions htlcswitch/hop/iterator.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@ func (b *BlindingKit) DecryptAndValidateFwdInfo(payload *Payload,
if err != nil {
return nil, err
}

// Validate the data in the blinded route against our incoming htlc's
// information.
if err := ValidateBlindedRouteData(
Expand All @@ -368,9 +369,31 @@ func (b *BlindingKit) DecryptAndValidateFwdInfo(payload *Payload,
return nil, err
}

// Exit early if this onion is for the exit hop of the route since
// route blinding receives are not yet supported.
if isFinalHop {
return nil, fmt.Errorf("being the final hop in a blinded " +
"path is not yet supported")
}

// At this point, we know we are a forwarding node for this onion
// and so we expect the relay info and next SCID fields to be set.
relayInfo, err := routeData.RelayInfo.UnwrapOrErr(
fmt.Errorf("relay info not set for non-final blinded hop"),
)
if err != nil {
return nil, err
}

nextSCID, err := routeData.ShortChannelID.UnwrapOrErr(
fmt.Errorf("next SCID not set for non-final blinded hop"),
)
if err != nil {
return nil, err
}

fwdAmt, err := calculateForwardingAmount(
b.IncomingAmount, routeData.RelayInfo.Val.BaseFee,
routeData.RelayInfo.Val.FeeRate,
b.IncomingAmount, relayInfo.Val.BaseFee, relayInfo.Val.FeeRate,
)
if err != nil {
return nil, err
Expand Down Expand Up @@ -400,10 +423,10 @@ func (b *BlindingKit) DecryptAndValidateFwdInfo(payload *Payload,
}

return &ForwardingInfo{
NextHop: routeData.ShortChannelID.Val,
NextHop: nextSCID.Val,
AmountToForward: fwdAmt,
OutgoingCTLV: b.IncomingCltv - uint32(
routeData.RelayInfo.Val.CltvExpiryDelta,
relayInfo.Val.CltvExpiryDelta,
),
// Remap from blinding override type to blinding point type.
NextBlinding: tlv.SomeRecordT(
Expand Down
2 changes: 1 addition & 1 deletion htlcswitch/hop/iterator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ func TestDecryptAndValidateFwdInfo(t *testing.T) {

// Encode valid blinding data that we'll fake decrypting for our test.
maxCltv := 1000
blindedData := record.NewBlindedRouteData(
blindedData := record.NewNonFinalBlindedRouteData(
lnwire.NewShortChanIDFromInt(1500), nil,
record.PaymentRelayInfo{
CltvExpiryDelta: 10,
Expand Down
12 changes: 6 additions & 6 deletions htlcswitch/hop/payload_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,7 @@ func TestValidateBlindedRouteData(t *testing.T) {
}{
{
name: "max cltv expired",
data: record.NewBlindedRouteData(
data: record.NewNonFinalBlindedRouteData(
scid,
nil,
record.PaymentRelayInfo{},
Expand All @@ -663,7 +663,7 @@ func TestValidateBlindedRouteData(t *testing.T) {
},
{
name: "zero max cltv",
data: record.NewBlindedRouteData(
data: record.NewNonFinalBlindedRouteData(
scid,
nil,
record.PaymentRelayInfo{},
Expand All @@ -682,7 +682,7 @@ func TestValidateBlindedRouteData(t *testing.T) {
},
{
name: "amount below minimum",
data: record.NewBlindedRouteData(
data: record.NewNonFinalBlindedRouteData(
scid,
nil,
record.PaymentRelayInfo{},
Expand All @@ -699,7 +699,7 @@ func TestValidateBlindedRouteData(t *testing.T) {
},
{
name: "valid, no features",
data: record.NewBlindedRouteData(
data: record.NewNonFinalBlindedRouteData(
scid,
nil,
record.PaymentRelayInfo{},
Expand All @@ -714,7 +714,7 @@ func TestValidateBlindedRouteData(t *testing.T) {
},
{
name: "unknown features",
data: record.NewBlindedRouteData(
data: record.NewNonFinalBlindedRouteData(
scid,
nil,
record.PaymentRelayInfo{},
Expand All @@ -738,7 +738,7 @@ func TestValidateBlindedRouteData(t *testing.T) {
},
{
name: "valid data",
data: record.NewBlindedRouteData(
data: record.NewNonFinalBlindedRouteData(
scid,
nil,
record.PaymentRelayInfo{
Expand Down
4 changes: 2 additions & 2 deletions itest/lnd_route_blinding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -676,7 +676,7 @@ func (b *blindedForwardTest) createBlindedRoute(hops []*forwardingEdge,

// Encode the route's blinded data and include it in the
// blinded hop.
payload := record.NewBlindedRouteData(
payload := record.NewNonFinalBlindedRouteData(
scid, nil, *relayInfo, constraints, nil,
)
payloadBytes, err := record.EncodeBlindedRouteData(payload)
Expand Down Expand Up @@ -739,7 +739,7 @@ func (b *blindedForwardTest) createBlindedRoute(hops []*forwardingEdge,
// node ID here so that it _looks like_ a valid
// forwarding hop (though in reality it's the last
// hop).
record.NewBlindedRouteData(
record.NewNonFinalBlindedRouteData(
lnwire.NewShortChanIDFromInt(100), nil,
record.PaymentRelayInfo{}, nil, nil,
),
Expand Down
54 changes: 54 additions & 0 deletions lnrpc/invoicesrpc/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,60 @@ func CreateRPCRouteHints(routeHints [][]zpay32.HopHint) []*lnrpc.RouteHint {
return res
}

// CreateRPCBlindedPayments takes a set of zpay32.BlindedPaymentPath and
// converts them into a set of lnrpc.BlindedPaymentPaths.
func CreateRPCBlindedPayments(blindedPaths []*zpay32.BlindedPaymentPath) (
[]*lnrpc.BlindedPaymentPath, error) {

var res []*lnrpc.BlindedPaymentPath
for _, path := range blindedPaths {
features := path.Features.Features()
var featuresSlice []lnrpc.FeatureBit
for feature := range features {
featuresSlice = append(
featuresSlice, lnrpc.FeatureBit(feature),
)
}

if len(path.Hops) == 0 {
return nil, fmt.Errorf("each blinded path must " +
"contain at least one hop")
}

var hops []*lnrpc.BlindedHop
for _, hop := range path.Hops {
blindedNodeID := hop.BlindedNodePub.
SerializeCompressed()
hops = append(hops, &lnrpc.BlindedHop{
BlindedNode: blindedNodeID,
EncryptedData: hop.CipherText,
})
}

introNode := path.Hops[0].BlindedNodePub
Copy link
Collaborator

Choose a reason for hiding this comment

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

Isn't the introduction point a unblinded pubkey in the lnrpc.BlindedPath struct, I wonder why we add the blinded nodekey of the first hope ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yeah so this is cause the first hop's key will be the real node key. Did this for space saving in the invoice since we never need the intro node's blinded pub key as the sender.

but can update depending on how that discussion goes :)

firstBlindingPoint := path.FirstEphemeralBlindingPoint

blindedPath := &lnrpc.BlindedPath{
IntroductionNode: introNode.SerializeCompressed(),
BlindingPoint: firstBlindingPoint.
SerializeCompressed(),
BlindedHops: hops,
}

res = append(res, &lnrpc.BlindedPaymentPath{
BlindedPath: blindedPath,
BaseFeeMsat: uint64(path.FeeBaseMsat),
ProportionalFeeRate: path.FeeRate,
TotalCltvDelta: uint32(path.CltvExpiryDelta),
HtlcMinMsat: path.HTLCMinMsat,
HtlcMaxMsat: path.HTLCMaxMsat,
Features: featuresSlice,
})
}

return res, nil
}

// CreateZpay32HopHints takes in the lnrpc form of route hints and converts them
// into an invoice decoded form.
func CreateZpay32HopHints(routeHints []*lnrpc.RouteHint) ([][]zpay32.HopHint, error) {
Expand Down
Loading
Loading