Merge pull request #8752 from ellemouton/rb-receives-setup
[1/4] Route Blinding Receives: Groundwork
This commit is contained in:
27 files changed
+1267
-459
No files matched your search
@@ -1099,8 +1099,13 @@ var queryRoutesCommand = cli.Command{
|
|||||||
},
|
},
|
||||||
cli.Int64Flag{
|
cli.Int64Flag{
|
||||||
Name: "final_cltv_delta",
|
Name: "final_cltv_delta",
|
||||||
Usage: "(optional) number of blocks the last hop has to reveal " +
|
Usage: "(optional) number of blocks the last hop has " +
|
||||||
"the preimage",
|
"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{
|
cli.BoolFlag{
|
||||||
Name: "use_mc",
|
Name: "use_mc",
|
||||||
@@ -1238,6 +1243,13 @@ func parseBlindedPaymentParameters(ctx *cli.Context) (
|
|||||||
return nil, nil
|
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
|
// If any one of our blinding related flags is set, we expect the
|
||||||
// full set to be set and we'll error out accordingly.
|
// full set to be set and we'll error out accordingly.
|
||||||
introNode, err := route.NewVertexFromStr(
|
introNode, err := route.NewVertexFromStr(
|
||||||
|
|||||||
@@ -89,6 +89,9 @@
|
|||||||
channel. We will still wait for the channel to have at least one confirmation
|
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.
|
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
|
## Testing
|
||||||
## Database
|
## Database
|
||||||
|
|
||||||
|
|||||||
@@ -360,6 +360,7 @@ func (b *BlindingKit) DecryptAndValidateFwdInfo(payload *Payload,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate the data in the blinded route against our incoming htlc's
|
// Validate the data in the blinded route against our incoming htlc's
|
||||||
// information.
|
// information.
|
||||||
if err := ValidateBlindedRouteData(
|
if err := ValidateBlindedRouteData(
|
||||||
@@ -368,9 +369,31 @@ func (b *BlindingKit) DecryptAndValidateFwdInfo(payload *Payload,
|
|||||||
return nil, err
|
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(
|
fwdAmt, err := calculateForwardingAmount(
|
||||||
b.IncomingAmount, routeData.RelayInfo.Val.BaseFee,
|
b.IncomingAmount, relayInfo.Val.BaseFee, relayInfo.Val.FeeRate,
|
||||||
routeData.RelayInfo.Val.FeeRate,
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -400,10 +423,10 @@ func (b *BlindingKit) DecryptAndValidateFwdInfo(payload *Payload,
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &ForwardingInfo{
|
return &ForwardingInfo{
|
||||||
NextHop: routeData.ShortChannelID.Val,
|
NextHop: nextSCID.Val,
|
||||||
AmountToForward: fwdAmt,
|
AmountToForward: fwdAmt,
|
||||||
OutgoingCTLV: b.IncomingCltv - uint32(
|
OutgoingCTLV: b.IncomingCltv - uint32(
|
||||||
routeData.RelayInfo.Val.CltvExpiryDelta,
|
relayInfo.Val.CltvExpiryDelta,
|
||||||
),
|
),
|
||||||
// Remap from blinding override type to blinding point type.
|
// Remap from blinding override type to blinding point type.
|
||||||
NextBlinding: tlv.SomeRecordT(
|
NextBlinding: tlv.SomeRecordT(
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ func TestDecryptAndValidateFwdInfo(t *testing.T) {
|
|||||||
|
|
||||||
// Encode valid blinding data that we'll fake decrypting for our test.
|
// Encode valid blinding data that we'll fake decrypting for our test.
|
||||||
maxCltv := 1000
|
maxCltv := 1000
|
||||||
blindedData := record.NewBlindedRouteData(
|
blindedData := record.NewNonFinalBlindedRouteData(
|
||||||
lnwire.NewShortChanIDFromInt(1500), nil,
|
lnwire.NewShortChanIDFromInt(1500), nil,
|
||||||
record.PaymentRelayInfo{
|
record.PaymentRelayInfo{
|
||||||
CltvExpiryDelta: 10,
|
CltvExpiryDelta: 10,
|
||||||
|
|||||||
@@ -646,7 +646,7 @@ func TestValidateBlindedRouteData(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "max cltv expired",
|
name: "max cltv expired",
|
||||||
data: record.NewBlindedRouteData(
|
data: record.NewNonFinalBlindedRouteData(
|
||||||
scid,
|
scid,
|
||||||
nil,
|
nil,
|
||||||
record.PaymentRelayInfo{},
|
record.PaymentRelayInfo{},
|
||||||
@@ -663,7 +663,7 @@ func TestValidateBlindedRouteData(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "zero max cltv",
|
name: "zero max cltv",
|
||||||
data: record.NewBlindedRouteData(
|
data: record.NewNonFinalBlindedRouteData(
|
||||||
scid,
|
scid,
|
||||||
nil,
|
nil,
|
||||||
record.PaymentRelayInfo{},
|
record.PaymentRelayInfo{},
|
||||||
@@ -682,7 +682,7 @@ func TestValidateBlindedRouteData(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "amount below minimum",
|
name: "amount below minimum",
|
||||||
data: record.NewBlindedRouteData(
|
data: record.NewNonFinalBlindedRouteData(
|
||||||
scid,
|
scid,
|
||||||
nil,
|
nil,
|
||||||
record.PaymentRelayInfo{},
|
record.PaymentRelayInfo{},
|
||||||
@@ -699,7 +699,7 @@ func TestValidateBlindedRouteData(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "valid, no features",
|
name: "valid, no features",
|
||||||
data: record.NewBlindedRouteData(
|
data: record.NewNonFinalBlindedRouteData(
|
||||||
scid,
|
scid,
|
||||||
nil,
|
nil,
|
||||||
record.PaymentRelayInfo{},
|
record.PaymentRelayInfo{},
|
||||||
@@ -714,7 +714,7 @@ func TestValidateBlindedRouteData(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "unknown features",
|
name: "unknown features",
|
||||||
data: record.NewBlindedRouteData(
|
data: record.NewNonFinalBlindedRouteData(
|
||||||
scid,
|
scid,
|
||||||
nil,
|
nil,
|
||||||
record.PaymentRelayInfo{},
|
record.PaymentRelayInfo{},
|
||||||
@@ -738,7 +738,7 @@ func TestValidateBlindedRouteData(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "valid data",
|
name: "valid data",
|
||||||
data: record.NewBlindedRouteData(
|
data: record.NewNonFinalBlindedRouteData(
|
||||||
scid,
|
scid,
|
||||||
nil,
|
nil,
|
||||||
record.PaymentRelayInfo{
|
record.PaymentRelayInfo{
|
||||||
|
|||||||
@@ -676,7 +676,7 @@ func (b *blindedForwardTest) createBlindedRoute(hops []*forwardingEdge,
|
|||||||
|
|
||||||
// Encode the route's blinded data and include it in the
|
// Encode the route's blinded data and include it in the
|
||||||
// blinded hop.
|
// blinded hop.
|
||||||
payload := record.NewBlindedRouteData(
|
payload := record.NewNonFinalBlindedRouteData(
|
||||||
scid, nil, *relayInfo, constraints, nil,
|
scid, nil, *relayInfo, constraints, nil,
|
||||||
)
|
)
|
||||||
payloadBytes, err := record.EncodeBlindedRouteData(payload)
|
payloadBytes, err := record.EncodeBlindedRouteData(payload)
|
||||||
@@ -739,7 +739,7 @@ func (b *blindedForwardTest) createBlindedRoute(hops []*forwardingEdge,
|
|||||||
// node ID here so that it _looks like_ a valid
|
// node ID here so that it _looks like_ a valid
|
||||||
// forwarding hop (though in reality it's the last
|
// forwarding hop (though in reality it's the last
|
||||||
// hop).
|
// hop).
|
||||||
record.NewBlindedRouteData(
|
record.NewNonFinalBlindedRouteData(
|
||||||
lnwire.NewShortChanIDFromInt(100), nil,
|
lnwire.NewShortChanIDFromInt(100), nil,
|
||||||
record.PaymentRelayInfo{}, nil, nil,
|
record.PaymentRelayInfo{}, nil, nil,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -266,6 +266,60 @@ func CreateRPCRouteHints(routeHints [][]zpay32.HopHint) []*lnrpc.RouteHint {
|
|||||||
return res
|
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
|
||||||
|
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
|
// CreateZpay32HopHints takes in the lnrpc form of route hints and converts them
|
||||||
// into an invoice decoded form.
|
// into an invoice decoded form.
|
||||||
func CreateZpay32HopHints(routeHints []*lnrpc.RouteHint) ([][]zpay32.HopHint, error) {
|
func CreateZpay32HopHints(routeHints []*lnrpc.RouteHint) ([][]zpay32.HopHint, error) {
|
||||||
|
|||||||
+214
-201
@@ -14304,19 +14304,20 @@ type PayReq struct {
|
|||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
Destination string `protobuf:"bytes,1,opt,name=destination,proto3" json:"destination,omitempty"`
|
Destination string `protobuf:"bytes,1,opt,name=destination,proto3" json:"destination,omitempty"`
|
||||||
PaymentHash string `protobuf:"bytes,2,opt,name=payment_hash,json=paymentHash,proto3" json:"payment_hash,omitempty"`
|
PaymentHash string `protobuf:"bytes,2,opt,name=payment_hash,json=paymentHash,proto3" json:"payment_hash,omitempty"`
|
||||||
NumSatoshis int64 `protobuf:"varint,3,opt,name=num_satoshis,json=numSatoshis,proto3" json:"num_satoshis,omitempty"`
|
NumSatoshis int64 `protobuf:"varint,3,opt,name=num_satoshis,json=numSatoshis,proto3" json:"num_satoshis,omitempty"`
|
||||||
Timestamp int64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
|
Timestamp int64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
|
||||||
Expiry int64 `protobuf:"varint,5,opt,name=expiry,proto3" json:"expiry,omitempty"`
|
Expiry int64 `protobuf:"varint,5,opt,name=expiry,proto3" json:"expiry,omitempty"`
|
||||||
Description string `protobuf:"bytes,6,opt,name=description,proto3" json:"description,omitempty"`
|
Description string `protobuf:"bytes,6,opt,name=description,proto3" json:"description,omitempty"`
|
||||||
DescriptionHash string `protobuf:"bytes,7,opt,name=description_hash,json=descriptionHash,proto3" json:"description_hash,omitempty"`
|
DescriptionHash string `protobuf:"bytes,7,opt,name=description_hash,json=descriptionHash,proto3" json:"description_hash,omitempty"`
|
||||||
FallbackAddr string `protobuf:"bytes,8,opt,name=fallback_addr,json=fallbackAddr,proto3" json:"fallback_addr,omitempty"`
|
FallbackAddr string `protobuf:"bytes,8,opt,name=fallback_addr,json=fallbackAddr,proto3" json:"fallback_addr,omitempty"`
|
||||||
CltvExpiry int64 `protobuf:"varint,9,opt,name=cltv_expiry,json=cltvExpiry,proto3" json:"cltv_expiry,omitempty"`
|
CltvExpiry int64 `protobuf:"varint,9,opt,name=cltv_expiry,json=cltvExpiry,proto3" json:"cltv_expiry,omitempty"`
|
||||||
RouteHints []*RouteHint `protobuf:"bytes,10,rep,name=route_hints,json=routeHints,proto3" json:"route_hints,omitempty"`
|
RouteHints []*RouteHint `protobuf:"bytes,10,rep,name=route_hints,json=routeHints,proto3" json:"route_hints,omitempty"`
|
||||||
PaymentAddr []byte `protobuf:"bytes,11,opt,name=payment_addr,json=paymentAddr,proto3" json:"payment_addr,omitempty"`
|
PaymentAddr []byte `protobuf:"bytes,11,opt,name=payment_addr,json=paymentAddr,proto3" json:"payment_addr,omitempty"`
|
||||||
NumMsat int64 `protobuf:"varint,12,opt,name=num_msat,json=numMsat,proto3" json:"num_msat,omitempty"`
|
NumMsat int64 `protobuf:"varint,12,opt,name=num_msat,json=numMsat,proto3" json:"num_msat,omitempty"`
|
||||||
Features map[uint32]*Feature `protobuf:"bytes,13,rep,name=features,proto3" json:"features,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
Features map[uint32]*Feature `protobuf:"bytes,13,rep,name=features,proto3" json:"features,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||||
|
BlindedPaths []*BlindedPaymentPath `protobuf:"bytes,14,rep,name=blinded_paths,json=blindedPaths,proto3" json:"blinded_paths,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *PayReq) Reset() {
|
func (x *PayReq) Reset() {
|
||||||
@@ -14442,6 +14443,13 @@ func (x *PayReq) GetFeatures() map[uint32]*Feature {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *PayReq) GetBlindedPaths() []*BlindedPaymentPath {
|
||||||
|
if x != nil {
|
||||||
|
return x.BlindedPaths
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type Feature struct {
|
type Feature struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
@@ -20202,7 +20210,7 @@ var file_lightning_proto_rawDesc = []byte{
|
|||||||
0x75, 0x62, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x22, 0x27, 0x0a, 0x0c, 0x50, 0x61, 0x79,
|
0x75, 0x62, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x22, 0x27, 0x0a, 0x0c, 0x50, 0x61, 0x79,
|
||||||
0x52, 0x65, 0x71, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x61, 0x79,
|
0x52, 0x65, 0x71, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x61, 0x79,
|
||||||
0x5f, 0x72, 0x65, 0x71, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x61, 0x79, 0x52,
|
0x5f, 0x72, 0x65, 0x71, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x61, 0x79, 0x52,
|
||||||
0x65, 0x71, 0x22, 0xb0, 0x04, 0x0a, 0x06, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, 0x20, 0x0a,
|
0x65, 0x71, 0x22, 0xf0, 0x04, 0x0a, 0x06, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, 0x20, 0x0a,
|
||||||
0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01,
|
0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01,
|
||||||
0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12,
|
0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12,
|
||||||
0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18,
|
0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18,
|
||||||
@@ -20232,7 +20240,11 @@ var file_lightning_proto_rawDesc = []byte{
|
|||||||
0x37, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28,
|
0x37, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28,
|
||||||
0x0b, 0x32, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71,
|
0x0b, 0x32, 0x1b, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x79, 0x52, 0x65, 0x71,
|
||||||
0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08,
|
0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08,
|
||||||
0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x1a, 0x4b, 0x0a, 0x0d, 0x46, 0x65, 0x61, 0x74,
|
0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x3e, 0x0a, 0x0d, 0x62, 0x6c, 0x69, 0x6e,
|
||||||
|
0x64, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||||
|
0x19, 0x2e, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x50,
|
||||||
|
0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x61, 0x74, 0x68, 0x52, 0x0c, 0x62, 0x6c, 0x69, 0x6e,
|
||||||
|
0x64, 0x65, 0x64, 0x50, 0x61, 0x74, 0x68, 0x73, 0x1a, 0x4b, 0x0a, 0x0d, 0x46, 0x65, 0x61, 0x74,
|
||||||
0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
|
0x75, 0x72, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
|
||||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x05, 0x76,
|
0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x05, 0x76,
|
||||||
0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72,
|
0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6c, 0x6e, 0x72,
|
||||||
@@ -21520,192 +21532,193 @@ var file_lightning_proto_depIdxs = []int32{
|
|||||||
38, // 138: lnrpc.AbandonChannelRequest.channel_point:type_name -> lnrpc.ChannelPoint
|
38, // 138: lnrpc.AbandonChannelRequest.channel_point:type_name -> lnrpc.ChannelPoint
|
||||||
150, // 139: lnrpc.PayReq.route_hints:type_name -> lnrpc.RouteHint
|
150, // 139: lnrpc.PayReq.route_hints:type_name -> lnrpc.RouteHint
|
||||||
244, // 140: lnrpc.PayReq.features:type_name -> lnrpc.PayReq.FeaturesEntry
|
244, // 140: lnrpc.PayReq.features:type_name -> lnrpc.PayReq.FeaturesEntry
|
||||||
179, // 141: lnrpc.FeeReportResponse.channel_fees:type_name -> lnrpc.ChannelFeeReport
|
151, // 141: lnrpc.PayReq.blinded_paths:type_name -> lnrpc.BlindedPaymentPath
|
||||||
38, // 142: lnrpc.PolicyUpdateRequest.chan_point:type_name -> lnrpc.ChannelPoint
|
179, // 142: lnrpc.FeeReportResponse.channel_fees:type_name -> lnrpc.ChannelFeeReport
|
||||||
181, // 143: lnrpc.PolicyUpdateRequest.inbound_fee:type_name -> lnrpc.InboundFee
|
38, // 143: lnrpc.PolicyUpdateRequest.chan_point:type_name -> lnrpc.ChannelPoint
|
||||||
39, // 144: lnrpc.FailedUpdate.outpoint:type_name -> lnrpc.OutPoint
|
181, // 144: lnrpc.PolicyUpdateRequest.inbound_fee:type_name -> lnrpc.InboundFee
|
||||||
11, // 145: lnrpc.FailedUpdate.reason:type_name -> lnrpc.UpdateFailure
|
39, // 145: lnrpc.FailedUpdate.outpoint:type_name -> lnrpc.OutPoint
|
||||||
183, // 146: lnrpc.PolicyUpdateResponse.failed_updates:type_name -> lnrpc.FailedUpdate
|
11, // 146: lnrpc.FailedUpdate.reason:type_name -> lnrpc.UpdateFailure
|
||||||
186, // 147: lnrpc.ForwardingHistoryResponse.forwarding_events:type_name -> lnrpc.ForwardingEvent
|
183, // 147: lnrpc.PolicyUpdateResponse.failed_updates:type_name -> lnrpc.FailedUpdate
|
||||||
38, // 148: lnrpc.ExportChannelBackupRequest.chan_point:type_name -> lnrpc.ChannelPoint
|
186, // 148: lnrpc.ForwardingHistoryResponse.forwarding_events:type_name -> lnrpc.ForwardingEvent
|
||||||
38, // 149: lnrpc.ChannelBackup.chan_point:type_name -> lnrpc.ChannelPoint
|
38, // 149: lnrpc.ExportChannelBackupRequest.chan_point:type_name -> lnrpc.ChannelPoint
|
||||||
38, // 150: lnrpc.MultiChanBackup.chan_points:type_name -> lnrpc.ChannelPoint
|
38, // 150: lnrpc.ChannelBackup.chan_point:type_name -> lnrpc.ChannelPoint
|
||||||
193, // 151: lnrpc.ChanBackupSnapshot.single_chan_backups:type_name -> lnrpc.ChannelBackups
|
38, // 151: lnrpc.MultiChanBackup.chan_points:type_name -> lnrpc.ChannelPoint
|
||||||
190, // 152: lnrpc.ChanBackupSnapshot.multi_chan_backup:type_name -> lnrpc.MultiChanBackup
|
193, // 152: lnrpc.ChanBackupSnapshot.single_chan_backups:type_name -> lnrpc.ChannelBackups
|
||||||
189, // 153: lnrpc.ChannelBackups.chan_backups:type_name -> lnrpc.ChannelBackup
|
190, // 153: lnrpc.ChanBackupSnapshot.multi_chan_backup:type_name -> lnrpc.MultiChanBackup
|
||||||
193, // 154: lnrpc.RestoreChanBackupRequest.chan_backups:type_name -> lnrpc.ChannelBackups
|
189, // 154: lnrpc.ChannelBackups.chan_backups:type_name -> lnrpc.ChannelBackup
|
||||||
198, // 155: lnrpc.BakeMacaroonRequest.permissions:type_name -> lnrpc.MacaroonPermission
|
193, // 155: lnrpc.RestoreChanBackupRequest.chan_backups:type_name -> lnrpc.ChannelBackups
|
||||||
198, // 156: lnrpc.MacaroonPermissionList.permissions:type_name -> lnrpc.MacaroonPermission
|
198, // 156: lnrpc.BakeMacaroonRequest.permissions:type_name -> lnrpc.MacaroonPermission
|
||||||
245, // 157: lnrpc.ListPermissionsResponse.method_permissions:type_name -> lnrpc.ListPermissionsResponse.MethodPermissionsEntry
|
198, // 157: lnrpc.MacaroonPermissionList.permissions:type_name -> lnrpc.MacaroonPermission
|
||||||
20, // 158: lnrpc.Failure.code:type_name -> lnrpc.Failure.FailureCode
|
245, // 158: lnrpc.ListPermissionsResponse.method_permissions:type_name -> lnrpc.ListPermissionsResponse.MethodPermissionsEntry
|
||||||
209, // 159: lnrpc.Failure.channel_update:type_name -> lnrpc.ChannelUpdate
|
20, // 159: lnrpc.Failure.code:type_name -> lnrpc.Failure.FailureCode
|
||||||
211, // 160: lnrpc.MacaroonId.ops:type_name -> lnrpc.Op
|
209, // 160: lnrpc.Failure.channel_update:type_name -> lnrpc.ChannelUpdate
|
||||||
198, // 161: lnrpc.CheckMacPermRequest.permissions:type_name -> lnrpc.MacaroonPermission
|
211, // 161: lnrpc.MacaroonId.ops:type_name -> lnrpc.Op
|
||||||
215, // 162: lnrpc.RPCMiddlewareRequest.stream_auth:type_name -> lnrpc.StreamAuth
|
198, // 162: lnrpc.CheckMacPermRequest.permissions:type_name -> lnrpc.MacaroonPermission
|
||||||
216, // 163: lnrpc.RPCMiddlewareRequest.request:type_name -> lnrpc.RPCMessage
|
215, // 163: lnrpc.RPCMiddlewareRequest.stream_auth:type_name -> lnrpc.StreamAuth
|
||||||
216, // 164: lnrpc.RPCMiddlewareRequest.response:type_name -> lnrpc.RPCMessage
|
216, // 164: lnrpc.RPCMiddlewareRequest.request:type_name -> lnrpc.RPCMessage
|
||||||
218, // 165: lnrpc.RPCMiddlewareResponse.register:type_name -> lnrpc.MiddlewareRegistration
|
216, // 165: lnrpc.RPCMiddlewareRequest.response:type_name -> lnrpc.RPCMessage
|
||||||
219, // 166: lnrpc.RPCMiddlewareResponse.feedback:type_name -> lnrpc.InterceptFeedback
|
218, // 166: lnrpc.RPCMiddlewareResponse.register:type_name -> lnrpc.MiddlewareRegistration
|
||||||
177, // 167: lnrpc.Peer.FeaturesEntry.value:type_name -> lnrpc.Feature
|
219, // 167: lnrpc.RPCMiddlewareResponse.feedback:type_name -> lnrpc.InterceptFeedback
|
||||||
177, // 168: lnrpc.GetInfoResponse.FeaturesEntry.value:type_name -> lnrpc.Feature
|
177, // 168: lnrpc.Peer.FeaturesEntry.value:type_name -> lnrpc.Feature
|
||||||
4, // 169: lnrpc.PendingChannelsResponse.PendingChannel.initiator:type_name -> lnrpc.Initiator
|
177, // 169: lnrpc.GetInfoResponse.FeaturesEntry.value:type_name -> lnrpc.Feature
|
||||||
3, // 170: lnrpc.PendingChannelsResponse.PendingChannel.commitment_type:type_name -> lnrpc.CommitmentType
|
4, // 170: lnrpc.PendingChannelsResponse.PendingChannel.initiator:type_name -> lnrpc.Initiator
|
||||||
226, // 171: lnrpc.PendingChannelsResponse.PendingOpenChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel
|
3, // 171: lnrpc.PendingChannelsResponse.PendingChannel.commitment_type:type_name -> lnrpc.CommitmentType
|
||||||
226, // 172: lnrpc.PendingChannelsResponse.WaitingCloseChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel
|
226, // 172: lnrpc.PendingChannelsResponse.PendingOpenChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel
|
||||||
229, // 173: lnrpc.PendingChannelsResponse.WaitingCloseChannel.commitments:type_name -> lnrpc.PendingChannelsResponse.Commitments
|
226, // 173: lnrpc.PendingChannelsResponse.WaitingCloseChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel
|
||||||
226, // 174: lnrpc.PendingChannelsResponse.ClosedChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel
|
229, // 174: lnrpc.PendingChannelsResponse.WaitingCloseChannel.commitments:type_name -> lnrpc.PendingChannelsResponse.Commitments
|
||||||
226, // 175: lnrpc.PendingChannelsResponse.ForceClosedChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel
|
226, // 175: lnrpc.PendingChannelsResponse.ClosedChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel
|
||||||
108, // 176: lnrpc.PendingChannelsResponse.ForceClosedChannel.pending_htlcs:type_name -> lnrpc.PendingHTLC
|
226, // 176: lnrpc.PendingChannelsResponse.ForceClosedChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel
|
||||||
15, // 177: lnrpc.PendingChannelsResponse.ForceClosedChannel.anchor:type_name -> lnrpc.PendingChannelsResponse.ForceClosedChannel.AnchorState
|
108, // 177: lnrpc.PendingChannelsResponse.ForceClosedChannel.pending_htlcs:type_name -> lnrpc.PendingHTLC
|
||||||
113, // 178: lnrpc.WalletBalanceResponse.AccountBalanceEntry.value:type_name -> lnrpc.WalletAccountBalance
|
15, // 178: lnrpc.PendingChannelsResponse.ForceClosedChannel.anchor:type_name -> lnrpc.PendingChannelsResponse.ForceClosedChannel.AnchorState
|
||||||
177, // 179: lnrpc.LightningNode.FeaturesEntry.value:type_name -> lnrpc.Feature
|
113, // 179: lnrpc.WalletBalanceResponse.AccountBalanceEntry.value:type_name -> lnrpc.WalletAccountBalance
|
||||||
137, // 180: lnrpc.NodeMetricsResponse.BetweennessCentralityEntry.value:type_name -> lnrpc.FloatMetric
|
177, // 180: lnrpc.LightningNode.FeaturesEntry.value:type_name -> lnrpc.Feature
|
||||||
177, // 181: lnrpc.NodeUpdate.FeaturesEntry.value:type_name -> lnrpc.Feature
|
137, // 181: lnrpc.NodeMetricsResponse.BetweennessCentralityEntry.value:type_name -> lnrpc.FloatMetric
|
||||||
177, // 182: lnrpc.Invoice.FeaturesEntry.value:type_name -> lnrpc.Feature
|
177, // 182: lnrpc.NodeUpdate.FeaturesEntry.value:type_name -> lnrpc.Feature
|
||||||
154, // 183: lnrpc.Invoice.AmpInvoiceStateEntry.value:type_name -> lnrpc.AMPInvoiceState
|
177, // 183: lnrpc.Invoice.FeaturesEntry.value:type_name -> lnrpc.Feature
|
||||||
177, // 184: lnrpc.PayReq.FeaturesEntry.value:type_name -> lnrpc.Feature
|
154, // 184: lnrpc.Invoice.AmpInvoiceStateEntry.value:type_name -> lnrpc.AMPInvoiceState
|
||||||
205, // 185: lnrpc.ListPermissionsResponse.MethodPermissionsEntry.value:type_name -> lnrpc.MacaroonPermissionList
|
177, // 185: lnrpc.PayReq.FeaturesEntry.value:type_name -> lnrpc.Feature
|
||||||
114, // 186: lnrpc.Lightning.WalletBalance:input_type -> lnrpc.WalletBalanceRequest
|
205, // 186: lnrpc.ListPermissionsResponse.MethodPermissionsEntry.value:type_name -> lnrpc.MacaroonPermissionList
|
||||||
117, // 187: lnrpc.Lightning.ChannelBalance:input_type -> lnrpc.ChannelBalanceRequest
|
114, // 187: lnrpc.Lightning.WalletBalance:input_type -> lnrpc.WalletBalanceRequest
|
||||||
30, // 188: lnrpc.Lightning.GetTransactions:input_type -> lnrpc.GetTransactionsRequest
|
117, // 188: lnrpc.Lightning.ChannelBalance:input_type -> lnrpc.ChannelBalanceRequest
|
||||||
42, // 189: lnrpc.Lightning.EstimateFee:input_type -> lnrpc.EstimateFeeRequest
|
30, // 189: lnrpc.Lightning.GetTransactions:input_type -> lnrpc.GetTransactionsRequest
|
||||||
46, // 190: lnrpc.Lightning.SendCoins:input_type -> lnrpc.SendCoinsRequest
|
42, // 190: lnrpc.Lightning.EstimateFee:input_type -> lnrpc.EstimateFeeRequest
|
||||||
48, // 191: lnrpc.Lightning.ListUnspent:input_type -> lnrpc.ListUnspentRequest
|
46, // 191: lnrpc.Lightning.SendCoins:input_type -> lnrpc.SendCoinsRequest
|
||||||
30, // 192: lnrpc.Lightning.SubscribeTransactions:input_type -> lnrpc.GetTransactionsRequest
|
48, // 192: lnrpc.Lightning.ListUnspent:input_type -> lnrpc.ListUnspentRequest
|
||||||
44, // 193: lnrpc.Lightning.SendMany:input_type -> lnrpc.SendManyRequest
|
30, // 193: lnrpc.Lightning.SubscribeTransactions:input_type -> lnrpc.GetTransactionsRequest
|
||||||
50, // 194: lnrpc.Lightning.NewAddress:input_type -> lnrpc.NewAddressRequest
|
44, // 194: lnrpc.Lightning.SendMany:input_type -> lnrpc.SendManyRequest
|
||||||
52, // 195: lnrpc.Lightning.SignMessage:input_type -> lnrpc.SignMessageRequest
|
50, // 195: lnrpc.Lightning.NewAddress:input_type -> lnrpc.NewAddressRequest
|
||||||
54, // 196: lnrpc.Lightning.VerifyMessage:input_type -> lnrpc.VerifyMessageRequest
|
52, // 196: lnrpc.Lightning.SignMessage:input_type -> lnrpc.SignMessageRequest
|
||||||
56, // 197: lnrpc.Lightning.ConnectPeer:input_type -> lnrpc.ConnectPeerRequest
|
54, // 197: lnrpc.Lightning.VerifyMessage:input_type -> lnrpc.VerifyMessageRequest
|
||||||
58, // 198: lnrpc.Lightning.DisconnectPeer:input_type -> lnrpc.DisconnectPeerRequest
|
56, // 198: lnrpc.Lightning.ConnectPeer:input_type -> lnrpc.ConnectPeerRequest
|
||||||
74, // 199: lnrpc.Lightning.ListPeers:input_type -> lnrpc.ListPeersRequest
|
58, // 199: lnrpc.Lightning.DisconnectPeer:input_type -> lnrpc.DisconnectPeerRequest
|
||||||
76, // 200: lnrpc.Lightning.SubscribePeerEvents:input_type -> lnrpc.PeerEventSubscription
|
74, // 200: lnrpc.Lightning.ListPeers:input_type -> lnrpc.ListPeersRequest
|
||||||
78, // 201: lnrpc.Lightning.GetInfo:input_type -> lnrpc.GetInfoRequest
|
76, // 201: lnrpc.Lightning.SubscribePeerEvents:input_type -> lnrpc.PeerEventSubscription
|
||||||
80, // 202: lnrpc.Lightning.GetDebugInfo:input_type -> lnrpc.GetDebugInfoRequest
|
78, // 202: lnrpc.Lightning.GetInfo:input_type -> lnrpc.GetInfoRequest
|
||||||
82, // 203: lnrpc.Lightning.GetRecoveryInfo:input_type -> lnrpc.GetRecoveryInfoRequest
|
80, // 203: lnrpc.Lightning.GetDebugInfo:input_type -> lnrpc.GetDebugInfoRequest
|
||||||
109, // 204: lnrpc.Lightning.PendingChannels:input_type -> lnrpc.PendingChannelsRequest
|
82, // 204: lnrpc.Lightning.GetRecoveryInfo:input_type -> lnrpc.GetRecoveryInfoRequest
|
||||||
63, // 205: lnrpc.Lightning.ListChannels:input_type -> lnrpc.ListChannelsRequest
|
109, // 205: lnrpc.Lightning.PendingChannels:input_type -> lnrpc.PendingChannelsRequest
|
||||||
111, // 206: lnrpc.Lightning.SubscribeChannelEvents:input_type -> lnrpc.ChannelEventSubscription
|
63, // 206: lnrpc.Lightning.ListChannels:input_type -> lnrpc.ListChannelsRequest
|
||||||
70, // 207: lnrpc.Lightning.ClosedChannels:input_type -> lnrpc.ClosedChannelsRequest
|
111, // 207: lnrpc.Lightning.SubscribeChannelEvents:input_type -> lnrpc.ChannelEventSubscription
|
||||||
96, // 208: lnrpc.Lightning.OpenChannelSync:input_type -> lnrpc.OpenChannelRequest
|
70, // 208: lnrpc.Lightning.ClosedChannels:input_type -> lnrpc.ClosedChannelsRequest
|
||||||
96, // 209: lnrpc.Lightning.OpenChannel:input_type -> lnrpc.OpenChannelRequest
|
96, // 209: lnrpc.Lightning.OpenChannelSync:input_type -> lnrpc.OpenChannelRequest
|
||||||
93, // 210: lnrpc.Lightning.BatchOpenChannel:input_type -> lnrpc.BatchOpenChannelRequest
|
96, // 210: lnrpc.Lightning.OpenChannel:input_type -> lnrpc.OpenChannelRequest
|
||||||
106, // 211: lnrpc.Lightning.FundingStateStep:input_type -> lnrpc.FundingTransitionMsg
|
93, // 211: lnrpc.Lightning.BatchOpenChannel:input_type -> lnrpc.BatchOpenChannelRequest
|
||||||
37, // 212: lnrpc.Lightning.ChannelAcceptor:input_type -> lnrpc.ChannelAcceptResponse
|
106, // 212: lnrpc.Lightning.FundingStateStep:input_type -> lnrpc.FundingTransitionMsg
|
||||||
88, // 213: lnrpc.Lightning.CloseChannel:input_type -> lnrpc.CloseChannelRequest
|
37, // 213: lnrpc.Lightning.ChannelAcceptor:input_type -> lnrpc.ChannelAcceptResponse
|
||||||
171, // 214: lnrpc.Lightning.AbandonChannel:input_type -> lnrpc.AbandonChannelRequest
|
88, // 214: lnrpc.Lightning.CloseChannel:input_type -> lnrpc.CloseChannelRequest
|
||||||
33, // 215: lnrpc.Lightning.SendPayment:input_type -> lnrpc.SendRequest
|
171, // 215: lnrpc.Lightning.AbandonChannel:input_type -> lnrpc.AbandonChannelRequest
|
||||||
33, // 216: lnrpc.Lightning.SendPaymentSync:input_type -> lnrpc.SendRequest
|
33, // 216: lnrpc.Lightning.SendPayment:input_type -> lnrpc.SendRequest
|
||||||
35, // 217: lnrpc.Lightning.SendToRoute:input_type -> lnrpc.SendToRouteRequest
|
33, // 217: lnrpc.Lightning.SendPaymentSync:input_type -> lnrpc.SendRequest
|
||||||
35, // 218: lnrpc.Lightning.SendToRouteSync:input_type -> lnrpc.SendToRouteRequest
|
35, // 218: lnrpc.Lightning.SendToRoute:input_type -> lnrpc.SendToRouteRequest
|
||||||
155, // 219: lnrpc.Lightning.AddInvoice:input_type -> lnrpc.Invoice
|
35, // 219: lnrpc.Lightning.SendToRouteSync:input_type -> lnrpc.SendToRouteRequest
|
||||||
160, // 220: lnrpc.Lightning.ListInvoices:input_type -> lnrpc.ListInvoiceRequest
|
155, // 220: lnrpc.Lightning.AddInvoice:input_type -> lnrpc.Invoice
|
||||||
159, // 221: lnrpc.Lightning.LookupInvoice:input_type -> lnrpc.PaymentHash
|
160, // 221: lnrpc.Lightning.ListInvoices:input_type -> lnrpc.ListInvoiceRequest
|
||||||
162, // 222: lnrpc.Lightning.SubscribeInvoices:input_type -> lnrpc.InvoiceSubscription
|
159, // 222: lnrpc.Lightning.LookupInvoice:input_type -> lnrpc.PaymentHash
|
||||||
175, // 223: lnrpc.Lightning.DecodePayReq:input_type -> lnrpc.PayReqString
|
162, // 223: lnrpc.Lightning.SubscribeInvoices:input_type -> lnrpc.InvoiceSubscription
|
||||||
165, // 224: lnrpc.Lightning.ListPayments:input_type -> lnrpc.ListPaymentsRequest
|
175, // 224: lnrpc.Lightning.DecodePayReq:input_type -> lnrpc.PayReqString
|
||||||
167, // 225: lnrpc.Lightning.DeletePayment:input_type -> lnrpc.DeletePaymentRequest
|
165, // 225: lnrpc.Lightning.ListPayments:input_type -> lnrpc.ListPaymentsRequest
|
||||||
168, // 226: lnrpc.Lightning.DeleteAllPayments:input_type -> lnrpc.DeleteAllPaymentsRequest
|
167, // 226: lnrpc.Lightning.DeletePayment:input_type -> lnrpc.DeletePaymentRequest
|
||||||
133, // 227: lnrpc.Lightning.DescribeGraph:input_type -> lnrpc.ChannelGraphRequest
|
168, // 227: lnrpc.Lightning.DeleteAllPayments:input_type -> lnrpc.DeleteAllPaymentsRequest
|
||||||
135, // 228: lnrpc.Lightning.GetNodeMetrics:input_type -> lnrpc.NodeMetricsRequest
|
133, // 228: lnrpc.Lightning.DescribeGraph:input_type -> lnrpc.ChannelGraphRequest
|
||||||
138, // 229: lnrpc.Lightning.GetChanInfo:input_type -> lnrpc.ChanInfoRequest
|
135, // 229: lnrpc.Lightning.GetNodeMetrics:input_type -> lnrpc.NodeMetricsRequest
|
||||||
127, // 230: lnrpc.Lightning.GetNodeInfo:input_type -> lnrpc.NodeInfoRequest
|
138, // 230: lnrpc.Lightning.GetChanInfo:input_type -> lnrpc.ChanInfoRequest
|
||||||
119, // 231: lnrpc.Lightning.QueryRoutes:input_type -> lnrpc.QueryRoutesRequest
|
127, // 231: lnrpc.Lightning.GetNodeInfo:input_type -> lnrpc.NodeInfoRequest
|
||||||
139, // 232: lnrpc.Lightning.GetNetworkInfo:input_type -> lnrpc.NetworkInfoRequest
|
119, // 232: lnrpc.Lightning.QueryRoutes:input_type -> lnrpc.QueryRoutesRequest
|
||||||
141, // 233: lnrpc.Lightning.StopDaemon:input_type -> lnrpc.StopRequest
|
139, // 233: lnrpc.Lightning.GetNetworkInfo:input_type -> lnrpc.NetworkInfoRequest
|
||||||
143, // 234: lnrpc.Lightning.SubscribeChannelGraph:input_type -> lnrpc.GraphTopologySubscription
|
141, // 234: lnrpc.Lightning.StopDaemon:input_type -> lnrpc.StopRequest
|
||||||
173, // 235: lnrpc.Lightning.DebugLevel:input_type -> lnrpc.DebugLevelRequest
|
143, // 235: lnrpc.Lightning.SubscribeChannelGraph:input_type -> lnrpc.GraphTopologySubscription
|
||||||
178, // 236: lnrpc.Lightning.FeeReport:input_type -> lnrpc.FeeReportRequest
|
173, // 236: lnrpc.Lightning.DebugLevel:input_type -> lnrpc.DebugLevelRequest
|
||||||
182, // 237: lnrpc.Lightning.UpdateChannelPolicy:input_type -> lnrpc.PolicyUpdateRequest
|
178, // 237: lnrpc.Lightning.FeeReport:input_type -> lnrpc.FeeReportRequest
|
||||||
185, // 238: lnrpc.Lightning.ForwardingHistory:input_type -> lnrpc.ForwardingHistoryRequest
|
182, // 238: lnrpc.Lightning.UpdateChannelPolicy:input_type -> lnrpc.PolicyUpdateRequest
|
||||||
188, // 239: lnrpc.Lightning.ExportChannelBackup:input_type -> lnrpc.ExportChannelBackupRequest
|
185, // 239: lnrpc.Lightning.ForwardingHistory:input_type -> lnrpc.ForwardingHistoryRequest
|
||||||
191, // 240: lnrpc.Lightning.ExportAllChannelBackups:input_type -> lnrpc.ChanBackupExportRequest
|
188, // 240: lnrpc.Lightning.ExportChannelBackup:input_type -> lnrpc.ExportChannelBackupRequest
|
||||||
192, // 241: lnrpc.Lightning.VerifyChanBackup:input_type -> lnrpc.ChanBackupSnapshot
|
191, // 241: lnrpc.Lightning.ExportAllChannelBackups:input_type -> lnrpc.ChanBackupExportRequest
|
||||||
194, // 242: lnrpc.Lightning.RestoreChannelBackups:input_type -> lnrpc.RestoreChanBackupRequest
|
192, // 242: lnrpc.Lightning.VerifyChanBackup:input_type -> lnrpc.ChanBackupSnapshot
|
||||||
196, // 243: lnrpc.Lightning.SubscribeChannelBackups:input_type -> lnrpc.ChannelBackupSubscription
|
194, // 243: lnrpc.Lightning.RestoreChannelBackups:input_type -> lnrpc.RestoreChanBackupRequest
|
||||||
199, // 244: lnrpc.Lightning.BakeMacaroon:input_type -> lnrpc.BakeMacaroonRequest
|
196, // 244: lnrpc.Lightning.SubscribeChannelBackups:input_type -> lnrpc.ChannelBackupSubscription
|
||||||
201, // 245: lnrpc.Lightning.ListMacaroonIDs:input_type -> lnrpc.ListMacaroonIDsRequest
|
199, // 245: lnrpc.Lightning.BakeMacaroon:input_type -> lnrpc.BakeMacaroonRequest
|
||||||
203, // 246: lnrpc.Lightning.DeleteMacaroonID:input_type -> lnrpc.DeleteMacaroonIDRequest
|
201, // 246: lnrpc.Lightning.ListMacaroonIDs:input_type -> lnrpc.ListMacaroonIDsRequest
|
||||||
206, // 247: lnrpc.Lightning.ListPermissions:input_type -> lnrpc.ListPermissionsRequest
|
203, // 247: lnrpc.Lightning.DeleteMacaroonID:input_type -> lnrpc.DeleteMacaroonIDRequest
|
||||||
212, // 248: lnrpc.Lightning.CheckMacaroonPermissions:input_type -> lnrpc.CheckMacPermRequest
|
206, // 248: lnrpc.Lightning.ListPermissions:input_type -> lnrpc.ListPermissionsRequest
|
||||||
217, // 249: lnrpc.Lightning.RegisterRPCMiddleware:input_type -> lnrpc.RPCMiddlewareResponse
|
212, // 249: lnrpc.Lightning.CheckMacaroonPermissions:input_type -> lnrpc.CheckMacPermRequest
|
||||||
25, // 250: lnrpc.Lightning.SendCustomMessage:input_type -> lnrpc.SendCustomMessageRequest
|
217, // 250: lnrpc.Lightning.RegisterRPCMiddleware:input_type -> lnrpc.RPCMiddlewareResponse
|
||||||
23, // 251: lnrpc.Lightning.SubscribeCustomMessages:input_type -> lnrpc.SubscribeCustomMessagesRequest
|
25, // 251: lnrpc.Lightning.SendCustomMessage:input_type -> lnrpc.SendCustomMessageRequest
|
||||||
66, // 252: lnrpc.Lightning.ListAliases:input_type -> lnrpc.ListAliasesRequest
|
23, // 252: lnrpc.Lightning.SubscribeCustomMessages:input_type -> lnrpc.SubscribeCustomMessagesRequest
|
||||||
21, // 253: lnrpc.Lightning.LookupHtlcResolution:input_type -> lnrpc.LookupHtlcResolutionRequest
|
66, // 253: lnrpc.Lightning.ListAliases:input_type -> lnrpc.ListAliasesRequest
|
||||||
115, // 254: lnrpc.Lightning.WalletBalance:output_type -> lnrpc.WalletBalanceResponse
|
21, // 254: lnrpc.Lightning.LookupHtlcResolution:input_type -> lnrpc.LookupHtlcResolutionRequest
|
||||||
118, // 255: lnrpc.Lightning.ChannelBalance:output_type -> lnrpc.ChannelBalanceResponse
|
115, // 255: lnrpc.Lightning.WalletBalance:output_type -> lnrpc.WalletBalanceResponse
|
||||||
31, // 256: lnrpc.Lightning.GetTransactions:output_type -> lnrpc.TransactionDetails
|
118, // 256: lnrpc.Lightning.ChannelBalance:output_type -> lnrpc.ChannelBalanceResponse
|
||||||
43, // 257: lnrpc.Lightning.EstimateFee:output_type -> lnrpc.EstimateFeeResponse
|
31, // 257: lnrpc.Lightning.GetTransactions:output_type -> lnrpc.TransactionDetails
|
||||||
47, // 258: lnrpc.Lightning.SendCoins:output_type -> lnrpc.SendCoinsResponse
|
43, // 258: lnrpc.Lightning.EstimateFee:output_type -> lnrpc.EstimateFeeResponse
|
||||||
49, // 259: lnrpc.Lightning.ListUnspent:output_type -> lnrpc.ListUnspentResponse
|
47, // 259: lnrpc.Lightning.SendCoins:output_type -> lnrpc.SendCoinsResponse
|
||||||
29, // 260: lnrpc.Lightning.SubscribeTransactions:output_type -> lnrpc.Transaction
|
49, // 260: lnrpc.Lightning.ListUnspent:output_type -> lnrpc.ListUnspentResponse
|
||||||
45, // 261: lnrpc.Lightning.SendMany:output_type -> lnrpc.SendManyResponse
|
29, // 261: lnrpc.Lightning.SubscribeTransactions:output_type -> lnrpc.Transaction
|
||||||
51, // 262: lnrpc.Lightning.NewAddress:output_type -> lnrpc.NewAddressResponse
|
45, // 262: lnrpc.Lightning.SendMany:output_type -> lnrpc.SendManyResponse
|
||||||
53, // 263: lnrpc.Lightning.SignMessage:output_type -> lnrpc.SignMessageResponse
|
51, // 263: lnrpc.Lightning.NewAddress:output_type -> lnrpc.NewAddressResponse
|
||||||
55, // 264: lnrpc.Lightning.VerifyMessage:output_type -> lnrpc.VerifyMessageResponse
|
53, // 264: lnrpc.Lightning.SignMessage:output_type -> lnrpc.SignMessageResponse
|
||||||
57, // 265: lnrpc.Lightning.ConnectPeer:output_type -> lnrpc.ConnectPeerResponse
|
55, // 265: lnrpc.Lightning.VerifyMessage:output_type -> lnrpc.VerifyMessageResponse
|
||||||
59, // 266: lnrpc.Lightning.DisconnectPeer:output_type -> lnrpc.DisconnectPeerResponse
|
57, // 266: lnrpc.Lightning.ConnectPeer:output_type -> lnrpc.ConnectPeerResponse
|
||||||
75, // 267: lnrpc.Lightning.ListPeers:output_type -> lnrpc.ListPeersResponse
|
59, // 267: lnrpc.Lightning.DisconnectPeer:output_type -> lnrpc.DisconnectPeerResponse
|
||||||
77, // 268: lnrpc.Lightning.SubscribePeerEvents:output_type -> lnrpc.PeerEvent
|
75, // 268: lnrpc.Lightning.ListPeers:output_type -> lnrpc.ListPeersResponse
|
||||||
79, // 269: lnrpc.Lightning.GetInfo:output_type -> lnrpc.GetInfoResponse
|
77, // 269: lnrpc.Lightning.SubscribePeerEvents:output_type -> lnrpc.PeerEvent
|
||||||
81, // 270: lnrpc.Lightning.GetDebugInfo:output_type -> lnrpc.GetDebugInfoResponse
|
79, // 270: lnrpc.Lightning.GetInfo:output_type -> lnrpc.GetInfoResponse
|
||||||
83, // 271: lnrpc.Lightning.GetRecoveryInfo:output_type -> lnrpc.GetRecoveryInfoResponse
|
81, // 271: lnrpc.Lightning.GetDebugInfo:output_type -> lnrpc.GetDebugInfoResponse
|
||||||
110, // 272: lnrpc.Lightning.PendingChannels:output_type -> lnrpc.PendingChannelsResponse
|
83, // 272: lnrpc.Lightning.GetRecoveryInfo:output_type -> lnrpc.GetRecoveryInfoResponse
|
||||||
64, // 273: lnrpc.Lightning.ListChannels:output_type -> lnrpc.ListChannelsResponse
|
110, // 273: lnrpc.Lightning.PendingChannels:output_type -> lnrpc.PendingChannelsResponse
|
||||||
112, // 274: lnrpc.Lightning.SubscribeChannelEvents:output_type -> lnrpc.ChannelEventUpdate
|
64, // 274: lnrpc.Lightning.ListChannels:output_type -> lnrpc.ListChannelsResponse
|
||||||
71, // 275: lnrpc.Lightning.ClosedChannels:output_type -> lnrpc.ClosedChannelsResponse
|
112, // 275: lnrpc.Lightning.SubscribeChannelEvents:output_type -> lnrpc.ChannelEventUpdate
|
||||||
38, // 276: lnrpc.Lightning.OpenChannelSync:output_type -> lnrpc.ChannelPoint
|
71, // 276: lnrpc.Lightning.ClosedChannels:output_type -> lnrpc.ClosedChannelsResponse
|
||||||
97, // 277: lnrpc.Lightning.OpenChannel:output_type -> lnrpc.OpenStatusUpdate
|
38, // 277: lnrpc.Lightning.OpenChannelSync:output_type -> lnrpc.ChannelPoint
|
||||||
95, // 278: lnrpc.Lightning.BatchOpenChannel:output_type -> lnrpc.BatchOpenChannelResponse
|
97, // 278: lnrpc.Lightning.OpenChannel:output_type -> lnrpc.OpenStatusUpdate
|
||||||
107, // 279: lnrpc.Lightning.FundingStateStep:output_type -> lnrpc.FundingStateStepResp
|
95, // 279: lnrpc.Lightning.BatchOpenChannel:output_type -> lnrpc.BatchOpenChannelResponse
|
||||||
36, // 280: lnrpc.Lightning.ChannelAcceptor:output_type -> lnrpc.ChannelAcceptRequest
|
107, // 280: lnrpc.Lightning.FundingStateStep:output_type -> lnrpc.FundingStateStepResp
|
||||||
89, // 281: lnrpc.Lightning.CloseChannel:output_type -> lnrpc.CloseStatusUpdate
|
36, // 281: lnrpc.Lightning.ChannelAcceptor:output_type -> lnrpc.ChannelAcceptRequest
|
||||||
172, // 282: lnrpc.Lightning.AbandonChannel:output_type -> lnrpc.AbandonChannelResponse
|
89, // 282: lnrpc.Lightning.CloseChannel:output_type -> lnrpc.CloseStatusUpdate
|
||||||
34, // 283: lnrpc.Lightning.SendPayment:output_type -> lnrpc.SendResponse
|
172, // 283: lnrpc.Lightning.AbandonChannel:output_type -> lnrpc.AbandonChannelResponse
|
||||||
34, // 284: lnrpc.Lightning.SendPaymentSync:output_type -> lnrpc.SendResponse
|
34, // 284: lnrpc.Lightning.SendPayment:output_type -> lnrpc.SendResponse
|
||||||
34, // 285: lnrpc.Lightning.SendToRoute:output_type -> lnrpc.SendResponse
|
34, // 285: lnrpc.Lightning.SendPaymentSync:output_type -> lnrpc.SendResponse
|
||||||
34, // 286: lnrpc.Lightning.SendToRouteSync:output_type -> lnrpc.SendResponse
|
34, // 286: lnrpc.Lightning.SendToRoute:output_type -> lnrpc.SendResponse
|
||||||
158, // 287: lnrpc.Lightning.AddInvoice:output_type -> lnrpc.AddInvoiceResponse
|
34, // 287: lnrpc.Lightning.SendToRouteSync:output_type -> lnrpc.SendResponse
|
||||||
161, // 288: lnrpc.Lightning.ListInvoices:output_type -> lnrpc.ListInvoiceResponse
|
158, // 288: lnrpc.Lightning.AddInvoice:output_type -> lnrpc.AddInvoiceResponse
|
||||||
155, // 289: lnrpc.Lightning.LookupInvoice:output_type -> lnrpc.Invoice
|
161, // 289: lnrpc.Lightning.ListInvoices:output_type -> lnrpc.ListInvoiceResponse
|
||||||
155, // 290: lnrpc.Lightning.SubscribeInvoices:output_type -> lnrpc.Invoice
|
155, // 290: lnrpc.Lightning.LookupInvoice:output_type -> lnrpc.Invoice
|
||||||
176, // 291: lnrpc.Lightning.DecodePayReq:output_type -> lnrpc.PayReq
|
155, // 291: lnrpc.Lightning.SubscribeInvoices:output_type -> lnrpc.Invoice
|
||||||
166, // 292: lnrpc.Lightning.ListPayments:output_type -> lnrpc.ListPaymentsResponse
|
176, // 292: lnrpc.Lightning.DecodePayReq:output_type -> lnrpc.PayReq
|
||||||
169, // 293: lnrpc.Lightning.DeletePayment:output_type -> lnrpc.DeletePaymentResponse
|
166, // 293: lnrpc.Lightning.ListPayments:output_type -> lnrpc.ListPaymentsResponse
|
||||||
170, // 294: lnrpc.Lightning.DeleteAllPayments:output_type -> lnrpc.DeleteAllPaymentsResponse
|
169, // 294: lnrpc.Lightning.DeletePayment:output_type -> lnrpc.DeletePaymentResponse
|
||||||
134, // 295: lnrpc.Lightning.DescribeGraph:output_type -> lnrpc.ChannelGraph
|
170, // 295: lnrpc.Lightning.DeleteAllPayments:output_type -> lnrpc.DeleteAllPaymentsResponse
|
||||||
136, // 296: lnrpc.Lightning.GetNodeMetrics:output_type -> lnrpc.NodeMetricsResponse
|
134, // 296: lnrpc.Lightning.DescribeGraph:output_type -> lnrpc.ChannelGraph
|
||||||
132, // 297: lnrpc.Lightning.GetChanInfo:output_type -> lnrpc.ChannelEdge
|
136, // 297: lnrpc.Lightning.GetNodeMetrics:output_type -> lnrpc.NodeMetricsResponse
|
||||||
128, // 298: lnrpc.Lightning.GetNodeInfo:output_type -> lnrpc.NodeInfo
|
132, // 298: lnrpc.Lightning.GetChanInfo:output_type -> lnrpc.ChannelEdge
|
||||||
122, // 299: lnrpc.Lightning.QueryRoutes:output_type -> lnrpc.QueryRoutesResponse
|
128, // 299: lnrpc.Lightning.GetNodeInfo:output_type -> lnrpc.NodeInfo
|
||||||
140, // 300: lnrpc.Lightning.GetNetworkInfo:output_type -> lnrpc.NetworkInfo
|
122, // 300: lnrpc.Lightning.QueryRoutes:output_type -> lnrpc.QueryRoutesResponse
|
||||||
142, // 301: lnrpc.Lightning.StopDaemon:output_type -> lnrpc.StopResponse
|
140, // 301: lnrpc.Lightning.GetNetworkInfo:output_type -> lnrpc.NetworkInfo
|
||||||
144, // 302: lnrpc.Lightning.SubscribeChannelGraph:output_type -> lnrpc.GraphTopologyUpdate
|
142, // 302: lnrpc.Lightning.StopDaemon:output_type -> lnrpc.StopResponse
|
||||||
174, // 303: lnrpc.Lightning.DebugLevel:output_type -> lnrpc.DebugLevelResponse
|
144, // 303: lnrpc.Lightning.SubscribeChannelGraph:output_type -> lnrpc.GraphTopologyUpdate
|
||||||
180, // 304: lnrpc.Lightning.FeeReport:output_type -> lnrpc.FeeReportResponse
|
174, // 304: lnrpc.Lightning.DebugLevel:output_type -> lnrpc.DebugLevelResponse
|
||||||
184, // 305: lnrpc.Lightning.UpdateChannelPolicy:output_type -> lnrpc.PolicyUpdateResponse
|
180, // 305: lnrpc.Lightning.FeeReport:output_type -> lnrpc.FeeReportResponse
|
||||||
187, // 306: lnrpc.Lightning.ForwardingHistory:output_type -> lnrpc.ForwardingHistoryResponse
|
184, // 306: lnrpc.Lightning.UpdateChannelPolicy:output_type -> lnrpc.PolicyUpdateResponse
|
||||||
189, // 307: lnrpc.Lightning.ExportChannelBackup:output_type -> lnrpc.ChannelBackup
|
187, // 307: lnrpc.Lightning.ForwardingHistory:output_type -> lnrpc.ForwardingHistoryResponse
|
||||||
192, // 308: lnrpc.Lightning.ExportAllChannelBackups:output_type -> lnrpc.ChanBackupSnapshot
|
189, // 308: lnrpc.Lightning.ExportChannelBackup:output_type -> lnrpc.ChannelBackup
|
||||||
197, // 309: lnrpc.Lightning.VerifyChanBackup:output_type -> lnrpc.VerifyChanBackupResponse
|
192, // 309: lnrpc.Lightning.ExportAllChannelBackups:output_type -> lnrpc.ChanBackupSnapshot
|
||||||
195, // 310: lnrpc.Lightning.RestoreChannelBackups:output_type -> lnrpc.RestoreBackupResponse
|
197, // 310: lnrpc.Lightning.VerifyChanBackup:output_type -> lnrpc.VerifyChanBackupResponse
|
||||||
192, // 311: lnrpc.Lightning.SubscribeChannelBackups:output_type -> lnrpc.ChanBackupSnapshot
|
195, // 311: lnrpc.Lightning.RestoreChannelBackups:output_type -> lnrpc.RestoreBackupResponse
|
||||||
200, // 312: lnrpc.Lightning.BakeMacaroon:output_type -> lnrpc.BakeMacaroonResponse
|
192, // 312: lnrpc.Lightning.SubscribeChannelBackups:output_type -> lnrpc.ChanBackupSnapshot
|
||||||
202, // 313: lnrpc.Lightning.ListMacaroonIDs:output_type -> lnrpc.ListMacaroonIDsResponse
|
200, // 313: lnrpc.Lightning.BakeMacaroon:output_type -> lnrpc.BakeMacaroonResponse
|
||||||
204, // 314: lnrpc.Lightning.DeleteMacaroonID:output_type -> lnrpc.DeleteMacaroonIDResponse
|
202, // 314: lnrpc.Lightning.ListMacaroonIDs:output_type -> lnrpc.ListMacaroonIDsResponse
|
||||||
207, // 315: lnrpc.Lightning.ListPermissions:output_type -> lnrpc.ListPermissionsResponse
|
204, // 315: lnrpc.Lightning.DeleteMacaroonID:output_type -> lnrpc.DeleteMacaroonIDResponse
|
||||||
213, // 316: lnrpc.Lightning.CheckMacaroonPermissions:output_type -> lnrpc.CheckMacPermResponse
|
207, // 316: lnrpc.Lightning.ListPermissions:output_type -> lnrpc.ListPermissionsResponse
|
||||||
214, // 317: lnrpc.Lightning.RegisterRPCMiddleware:output_type -> lnrpc.RPCMiddlewareRequest
|
213, // 317: lnrpc.Lightning.CheckMacaroonPermissions:output_type -> lnrpc.CheckMacPermResponse
|
||||||
26, // 318: lnrpc.Lightning.SendCustomMessage:output_type -> lnrpc.SendCustomMessageResponse
|
214, // 318: lnrpc.Lightning.RegisterRPCMiddleware:output_type -> lnrpc.RPCMiddlewareRequest
|
||||||
24, // 319: lnrpc.Lightning.SubscribeCustomMessages:output_type -> lnrpc.CustomMessage
|
26, // 319: lnrpc.Lightning.SendCustomMessage:output_type -> lnrpc.SendCustomMessageResponse
|
||||||
67, // 320: lnrpc.Lightning.ListAliases:output_type -> lnrpc.ListAliasesResponse
|
24, // 320: lnrpc.Lightning.SubscribeCustomMessages:output_type -> lnrpc.CustomMessage
|
||||||
22, // 321: lnrpc.Lightning.LookupHtlcResolution:output_type -> lnrpc.LookupHtlcResolutionResponse
|
67, // 321: lnrpc.Lightning.ListAliases:output_type -> lnrpc.ListAliasesResponse
|
||||||
254, // [254:322] is the sub-list for method output_type
|
22, // 322: lnrpc.Lightning.LookupHtlcResolution:output_type -> lnrpc.LookupHtlcResolutionResponse
|
||||||
186, // [186:254] is the sub-list for method input_type
|
255, // [255:323] is the sub-list for method output_type
|
||||||
186, // [186:186] is the sub-list for extension type_name
|
187, // [187:255] is the sub-list for method input_type
|
||||||
186, // [186:186] is the sub-list for extension extendee
|
187, // [187:187] is the sub-list for extension type_name
|
||||||
0, // [0:186] is the sub-list for field type_name
|
187, // [187:187] is the sub-list for extension extendee
|
||||||
|
0, // [0:187] is the sub-list for field type_name
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_lightning_proto_init() }
|
func init() { file_lightning_proto_init() }
|
||||||
|
|||||||
@@ -4289,6 +4289,7 @@ message PayReq {
|
|||||||
bytes payment_addr = 11;
|
bytes payment_addr = 11;
|
||||||
int64 num_msat = 12;
|
int64 num_msat = 12;
|
||||||
map<uint32, Feature> features = 13;
|
map<uint32, Feature> features = 13;
|
||||||
|
repeated BlindedPaymentPath blinded_paths = 14;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum FeatureBit {
|
enum FeatureBit {
|
||||||
|
|||||||
@@ -6299,6 +6299,12 @@
|
|||||||
"additionalProperties": {
|
"additionalProperties": {
|
||||||
"$ref": "#/definitions/lnrpcFeature"
|
"$ref": "#/definitions/lnrpcFeature"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"blinded_paths": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/lnrpcBlindedPaymentPath"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
+98
-15
@@ -14,8 +14,18 @@ import (
|
|||||||
// route encrypted data blob that is created by the recipient to provide
|
// route encrypted data blob that is created by the recipient to provide
|
||||||
// forwarding information.
|
// forwarding information.
|
||||||
type BlindedRouteData struct {
|
type BlindedRouteData struct {
|
||||||
|
// Padding is an optional set of bytes that a recipient can use to pad
|
||||||
|
// the data so that the encrypted recipient data blobs are all the same
|
||||||
|
// length.
|
||||||
|
Padding tlv.OptionalRecordT[tlv.TlvType1, []byte]
|
||||||
|
|
||||||
// ShortChannelID is the channel ID of the next hop.
|
// ShortChannelID is the channel ID of the next hop.
|
||||||
ShortChannelID tlv.RecordT[tlv.TlvType2, lnwire.ShortChannelID]
|
ShortChannelID tlv.OptionalRecordT[tlv.TlvType2, lnwire.ShortChannelID]
|
||||||
|
|
||||||
|
// PathID is a secret set of bytes that the blinded path creator will
|
||||||
|
// set so that they can check the value on decryption to ensure that the
|
||||||
|
// path they created was used for the intended purpose.
|
||||||
|
PathID tlv.OptionalRecordT[tlv.TlvType6, []byte]
|
||||||
|
|
||||||
// NextBlindingOverride is a blinding point that should be switched
|
// NextBlindingOverride is a blinding point that should be switched
|
||||||
// in for the next hop. This is used to combine two blinded paths into
|
// in for the next hop. This is used to combine two blinded paths into
|
||||||
@@ -24,7 +34,7 @@ type BlindedRouteData struct {
|
|||||||
NextBlindingOverride tlv.OptionalRecordT[tlv.TlvType8, *btcec.PublicKey]
|
NextBlindingOverride tlv.OptionalRecordT[tlv.TlvType8, *btcec.PublicKey]
|
||||||
|
|
||||||
// RelayInfo provides the relay parameters for the hop.
|
// RelayInfo provides the relay parameters for the hop.
|
||||||
RelayInfo tlv.RecordT[tlv.TlvType10, PaymentRelayInfo]
|
RelayInfo tlv.OptionalRecordT[tlv.TlvType10, PaymentRelayInfo]
|
||||||
|
|
||||||
// Constraints provides the payment relay constraints for the hop.
|
// Constraints provides the payment relay constraints for the hop.
|
||||||
Constraints tlv.OptionalRecordT[tlv.TlvType12, PaymentConstraints]
|
Constraints tlv.OptionalRecordT[tlv.TlvType12, PaymentConstraints]
|
||||||
@@ -33,16 +43,20 @@ type BlindedRouteData struct {
|
|||||||
Features tlv.OptionalRecordT[tlv.TlvType14, lnwire.FeatureVector]
|
Features tlv.OptionalRecordT[tlv.TlvType14, lnwire.FeatureVector]
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBlindedRouteData creates the data that's provided for hops within a
|
// NewNonFinalBlindedRouteData creates the data that's provided for hops within
|
||||||
// blinded route.
|
// a blinded route.
|
||||||
func NewBlindedRouteData(chanID lnwire.ShortChannelID,
|
func NewNonFinalBlindedRouteData(chanID lnwire.ShortChannelID,
|
||||||
blindingOverride *btcec.PublicKey, relayInfo PaymentRelayInfo,
|
blindingOverride *btcec.PublicKey, relayInfo PaymentRelayInfo,
|
||||||
constraints *PaymentConstraints,
|
constraints *PaymentConstraints,
|
||||||
features *lnwire.FeatureVector) *BlindedRouteData {
|
features *lnwire.FeatureVector) *BlindedRouteData {
|
||||||
|
|
||||||
info := &BlindedRouteData{
|
info := &BlindedRouteData{
|
||||||
ShortChannelID: tlv.NewRecordT[tlv.TlvType2](chanID),
|
ShortChannelID: tlv.SomeRecordT(
|
||||||
RelayInfo: tlv.NewRecordT[tlv.TlvType10](relayInfo),
|
tlv.NewRecordT[tlv.TlvType2](chanID),
|
||||||
|
),
|
||||||
|
RelayInfo: tlv.SomeRecordT(
|
||||||
|
tlv.NewRecordT[tlv.TlvType10](relayInfo),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
if blindingOverride != nil {
|
if blindingOverride != nil {
|
||||||
@@ -64,12 +78,36 @@ func NewBlindedRouteData(chanID lnwire.ShortChannelID,
|
|||||||
return info
|
return info
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewFinalHopBlindedRouteData creates the data that's provided for the final
|
||||||
|
// hop in a blinded route.
|
||||||
|
func NewFinalHopBlindedRouteData(constraints *PaymentConstraints,
|
||||||
|
pathID []byte) *BlindedRouteData {
|
||||||
|
|
||||||
|
var data BlindedRouteData
|
||||||
|
if pathID != nil {
|
||||||
|
data.PathID = tlv.SomeRecordT(
|
||||||
|
tlv.NewPrimitiveRecord[tlv.TlvType6](pathID),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if constraints != nil {
|
||||||
|
data.Constraints = tlv.SomeRecordT(
|
||||||
|
tlv.NewRecordT[tlv.TlvType12](*constraints))
|
||||||
|
}
|
||||||
|
|
||||||
|
return &data
|
||||||
|
}
|
||||||
|
|
||||||
// DecodeBlindedRouteData decodes the data provided within a blinded route.
|
// DecodeBlindedRouteData decodes the data provided within a blinded route.
|
||||||
func DecodeBlindedRouteData(r io.Reader) (*BlindedRouteData, error) {
|
func DecodeBlindedRouteData(r io.Reader) (*BlindedRouteData, error) {
|
||||||
var (
|
var (
|
||||||
d BlindedRouteData
|
d BlindedRouteData
|
||||||
|
|
||||||
|
padding = d.Padding.Zero()
|
||||||
|
scid = d.ShortChannelID.Zero()
|
||||||
|
pathID = d.PathID.Zero()
|
||||||
blindingOverride = d.NextBlindingOverride.Zero()
|
blindingOverride = d.NextBlindingOverride.Zero()
|
||||||
|
relayInfo = d.RelayInfo.Zero()
|
||||||
constraints = d.Constraints.Zero()
|
constraints = d.Constraints.Zero()
|
||||||
features = d.Features.Zero()
|
features = d.Features.Zero()
|
||||||
)
|
)
|
||||||
@@ -80,19 +118,35 @@ func DecodeBlindedRouteData(r io.Reader) (*BlindedRouteData, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
typeMap, err := tlvRecords.ExtractRecords(
|
typeMap, err := tlvRecords.ExtractRecords(
|
||||||
&d.ShortChannelID,
|
&padding, &scid, &pathID, &blindingOverride, &relayInfo,
|
||||||
&blindingOverride, &d.RelayInfo.Val, &constraints,
|
&constraints, &features,
|
||||||
&features,
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
val, ok := typeMap[d.NextBlindingOverride.TlvType()]
|
val, ok := typeMap[d.Padding.TlvType()]
|
||||||
|
if ok && val == nil {
|
||||||
|
d.Padding = tlv.SomeRecordT(padding)
|
||||||
|
}
|
||||||
|
|
||||||
|
if val, ok := typeMap[d.ShortChannelID.TlvType()]; ok && val == nil {
|
||||||
|
d.ShortChannelID = tlv.SomeRecordT(scid)
|
||||||
|
}
|
||||||
|
|
||||||
|
if val, ok := typeMap[d.PathID.TlvType()]; ok && val == nil {
|
||||||
|
d.PathID = tlv.SomeRecordT(pathID)
|
||||||
|
}
|
||||||
|
|
||||||
|
val, ok = typeMap[d.NextBlindingOverride.TlvType()]
|
||||||
if ok && val == nil {
|
if ok && val == nil {
|
||||||
d.NextBlindingOverride = tlv.SomeRecordT(blindingOverride)
|
d.NextBlindingOverride = tlv.SomeRecordT(blindingOverride)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if val, ok := typeMap[d.RelayInfo.TlvType()]; ok && val == nil {
|
||||||
|
d.RelayInfo = tlv.SomeRecordT(relayInfo)
|
||||||
|
}
|
||||||
|
|
||||||
if val, ok := typeMap[d.Constraints.TlvType()]; ok && val == nil {
|
if val, ok := typeMap[d.Constraints.TlvType()]; ok && val == nil {
|
||||||
d.Constraints = tlv.SomeRecordT(constraints)
|
d.Constraints = tlv.SomeRecordT(constraints)
|
||||||
}
|
}
|
||||||
@@ -111,7 +165,19 @@ func EncodeBlindedRouteData(data *BlindedRouteData) ([]byte, error) {
|
|||||||
recordProducers = make([]tlv.RecordProducer, 0, 5)
|
recordProducers = make([]tlv.RecordProducer, 0, 5)
|
||||||
)
|
)
|
||||||
|
|
||||||
recordProducers = append(recordProducers, &data.ShortChannelID)
|
data.Padding.WhenSome(func(p tlv.RecordT[tlv.TlvType1, []byte]) {
|
||||||
|
recordProducers = append(recordProducers, &p)
|
||||||
|
})
|
||||||
|
|
||||||
|
data.ShortChannelID.WhenSome(func(scid tlv.RecordT[tlv.TlvType2,
|
||||||
|
lnwire.ShortChannelID]) {
|
||||||
|
|
||||||
|
recordProducers = append(recordProducers, &scid)
|
||||||
|
})
|
||||||
|
|
||||||
|
data.PathID.WhenSome(func(pathID tlv.RecordT[tlv.TlvType6, []byte]) {
|
||||||
|
recordProducers = append(recordProducers, &pathID)
|
||||||
|
})
|
||||||
|
|
||||||
data.NextBlindingOverride.WhenSome(func(pk tlv.RecordT[tlv.TlvType8,
|
data.NextBlindingOverride.WhenSome(func(pk tlv.RecordT[tlv.TlvType8,
|
||||||
*btcec.PublicKey]) {
|
*btcec.PublicKey]) {
|
||||||
@@ -119,7 +185,11 @@ func EncodeBlindedRouteData(data *BlindedRouteData) ([]byte, error) {
|
|||||||
recordProducers = append(recordProducers, &pk)
|
recordProducers = append(recordProducers, &pk)
|
||||||
})
|
})
|
||||||
|
|
||||||
recordProducers = append(recordProducers, &data.RelayInfo.Val)
|
data.RelayInfo.WhenSome(func(r tlv.RecordT[tlv.TlvType10,
|
||||||
|
PaymentRelayInfo]) {
|
||||||
|
|
||||||
|
recordProducers = append(recordProducers, &r)
|
||||||
|
})
|
||||||
|
|
||||||
data.Constraints.WhenSome(func(cs tlv.RecordT[tlv.TlvType12,
|
data.Constraints.WhenSome(func(cs tlv.RecordT[tlv.TlvType12,
|
||||||
PaymentConstraints]) {
|
PaymentConstraints]) {
|
||||||
@@ -140,6 +210,19 @@ func EncodeBlindedRouteData(data *BlindedRouteData) ([]byte, error) {
|
|||||||
return e[:], nil
|
return e[:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PadBy adds "n" padding bytes to the BlindedRouteData using the Padding field.
|
||||||
|
// Callers should be aware that the total payload size will change by more than
|
||||||
|
// "n" since the "n" bytes will be prefixed by BigSize type and length fields.
|
||||||
|
// Callers may need to call PadBy iteratively until each encrypted data packet
|
||||||
|
// is the same size and so each call will overwrite the Padding record.
|
||||||
|
// Note that calling PadBy with an n value of 0 will still result in a zero
|
||||||
|
// length TLV entry being added.
|
||||||
|
func (b *BlindedRouteData) PadBy(n int) {
|
||||||
|
b.Padding = tlv.SomeRecordT(
|
||||||
|
tlv.NewPrimitiveRecord[tlv.TlvType1](make([]byte, n)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// PaymentRelayInfo describes the relay policy for a blinded path.
|
// PaymentRelayInfo describes the relay policy for a blinded path.
|
||||||
type PaymentRelayInfo struct {
|
type PaymentRelayInfo struct {
|
||||||
// CltvExpiryDelta is the expiry delta for the payment.
|
// CltvExpiryDelta is the expiry delta for the payment.
|
||||||
@@ -153,8 +236,8 @@ type PaymentRelayInfo struct {
|
|||||||
BaseFee uint32
|
BaseFee uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
// newPaymentRelayRecord creates a tlv.Record that encodes the payment relay
|
// Record creates a tlv.Record that encodes the payment relay (type 10) type for
|
||||||
// (type 10) type for an encrypted blob payload.
|
// an encrypted blob payload.
|
||||||
func (i *PaymentRelayInfo) Record() tlv.Record {
|
func (i *PaymentRelayInfo) Record() tlv.Record {
|
||||||
return tlv.MakeDynamicRecord(
|
return tlv.MakeDynamicRecord(
|
||||||
10, &i, func() uint64 {
|
10, &i, func() uint64 {
|
||||||
|
|||||||
+134
-4
@@ -101,7 +101,7 @@ func TestBlindedDataEncoding(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
encodedData := NewBlindedRouteData(
|
encodedData := NewNonFinalBlindedRouteData(
|
||||||
channelID, pubkey(t), info, constraints,
|
channelID, pubkey(t), info, constraints,
|
||||||
testCase.features,
|
testCase.features,
|
||||||
)
|
)
|
||||||
@@ -118,6 +118,127 @@ func TestBlindedDataEncoding(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestBlindedDataFinalHopEncoding tests the encoding and decoding of a blinded
|
||||||
|
// data blob intended for the final hop of a blinded path where only the pathID
|
||||||
|
// will potentially be set.
|
||||||
|
func TestBlindedDataFinalHopEncoding(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
pathID []byte
|
||||||
|
constraints bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "with path ID",
|
||||||
|
pathID: []byte{1, 2, 3, 4, 5, 6},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with no path ID",
|
||||||
|
pathID: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with path ID and constraints",
|
||||||
|
pathID: []byte{1, 2, 3, 4, 5, 6},
|
||||||
|
constraints: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
test := test
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var constraints *PaymentConstraints
|
||||||
|
if test.constraints {
|
||||||
|
constraints = &PaymentConstraints{
|
||||||
|
MaxCltvExpiry: 4,
|
||||||
|
HtlcMinimumMsat: 5,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
encodedData := NewFinalHopBlindedRouteData(
|
||||||
|
constraints, test.pathID,
|
||||||
|
)
|
||||||
|
|
||||||
|
encoded, err := EncodeBlindedRouteData(encodedData)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
b := bytes.NewBuffer(encoded)
|
||||||
|
decodedData, err := DecodeBlindedRouteData(b)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, encodedData, decodedData)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestBlindedRouteDataPadding tests the PadBy method of BlindedRouteData.
|
||||||
|
func TestBlindedRouteDataPadding(t *testing.T) {
|
||||||
|
newBlindedRouteData := func() *BlindedRouteData {
|
||||||
|
channelID := lnwire.NewShortChanIDFromInt(1)
|
||||||
|
info := PaymentRelayInfo{
|
||||||
|
FeeRate: 2,
|
||||||
|
CltvExpiryDelta: 3,
|
||||||
|
BaseFee: 30,
|
||||||
|
}
|
||||||
|
|
||||||
|
constraints := &PaymentConstraints{
|
||||||
|
MaxCltvExpiry: 4,
|
||||||
|
HtlcMinimumMsat: 100,
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewNonFinalBlindedRouteData(
|
||||||
|
channelID, pubkey(t), info, constraints, nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
paddingSize int
|
||||||
|
expectedSizeIncrease uint64
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
// Calling PadBy with an n value of 0 in the case where
|
||||||
|
// there is not yet a padding field will result in a
|
||||||
|
// zero length TLV entry being added. This will add 2
|
||||||
|
// bytes for the type and length fields.
|
||||||
|
name: "no extra padding",
|
||||||
|
expectedSizeIncrease: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "small padding (length " +
|
||||||
|
"field of 1 byte)",
|
||||||
|
paddingSize: 200,
|
||||||
|
expectedSizeIncrease: 202,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "medium padding (length field " +
|
||||||
|
"of 3 bytes)",
|
||||||
|
paddingSize: 256,
|
||||||
|
expectedSizeIncrease: 260,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
data := newBlindedRouteData()
|
||||||
|
|
||||||
|
prePaddingEncoding, err := EncodeBlindedRouteData(data)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
data.PadBy(test.paddingSize)
|
||||||
|
|
||||||
|
postPaddingEncoding, err := EncodeBlindedRouteData(data)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.EqualValues(
|
||||||
|
t, test.expectedSizeIncrease,
|
||||||
|
len(postPaddingEncoding)-
|
||||||
|
len(prePaddingEncoding),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestBlindedRouteVectors tests encoding/decoding of the test vectors for
|
// TestBlindedRouteVectors tests encoding/decoding of the test vectors for
|
||||||
// blinded route data provided in the specification.
|
// blinded route data provided in the specification.
|
||||||
//
|
//
|
||||||
@@ -131,10 +252,11 @@ func TestBlindingSpecTestVectors(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
encoded string
|
encoded string
|
||||||
expectedPaymentData *BlindedRouteData
|
expectedPaymentData *BlindedRouteData
|
||||||
|
expectedPadding int
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
encoded: "011a0000000000000000000000000000000000000000000000000000020800000000000006c10a0800240000009627100c06000b69e505dc0e00fd023103123456",
|
encoded: "011a0000000000000000000000000000000000000000000000000000020800000000000006c10a0800240000009627100c06000b69e505dc0e00fd023103123456",
|
||||||
expectedPaymentData: NewBlindedRouteData(
|
expectedPaymentData: NewNonFinalBlindedRouteData(
|
||||||
lnwire.ShortChannelID{
|
lnwire.ShortChannelID{
|
||||||
BlockHeight: 0,
|
BlockHeight: 0,
|
||||||
TxIndex: 0,
|
TxIndex: 0,
|
||||||
@@ -155,10 +277,11 @@ func TestBlindingSpecTestVectors(t *testing.T) {
|
|||||||
lnwire.Features,
|
lnwire.Features,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
expectedPadding: 26,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
encoded: "020800000000000004510821031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f0a0800300000006401f40c06000b69c105dc0e00",
|
encoded: "020800000000000004510821031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f0a0800300000006401f40c06000b69c105dc0e00",
|
||||||
expectedPaymentData: NewBlindedRouteData(
|
expectedPaymentData: NewNonFinalBlindedRouteData(
|
||||||
lnwire.ShortChannelID{
|
lnwire.ShortChannelID{
|
||||||
TxPosition: 1105,
|
TxPosition: 1105,
|
||||||
},
|
},
|
||||||
@@ -175,7 +298,8 @@ func TestBlindingSpecTestVectors(t *testing.T) {
|
|||||||
lnwire.NewFeatureVector(
|
lnwire.NewFeatureVector(
|
||||||
lnwire.NewRawFeatureVector(),
|
lnwire.NewRawFeatureVector(),
|
||||||
lnwire.Features,
|
lnwire.Features,
|
||||||
)),
|
),
|
||||||
|
),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,6 +313,12 @@ func TestBlindingSpecTestVectors(t *testing.T) {
|
|||||||
decodedRoute, err := DecodeBlindedRouteData(buff)
|
decodedRoute, err := DecodeBlindedRouteData(buff)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
if test.expectedPadding != 0 {
|
||||||
|
test.expectedPaymentData.PadBy(
|
||||||
|
test.expectedPadding,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
require.Equal(
|
require.Equal(
|
||||||
t, test.expectedPaymentData, decodedRoute,
|
t, test.expectedPaymentData, decodedRoute,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ package routing
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcec/v2"
|
|
||||||
"github.com/lightningnetwork/lnd/channeldb/models"
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/routing/route"
|
"github.com/lightningnetwork/lnd/routing/route"
|
||||||
@@ -29,6 +29,11 @@ type AdditionalEdge interface {
|
|||||||
|
|
||||||
// EdgePolicy returns the policy of the additional edge.
|
// EdgePolicy returns the policy of the additional edge.
|
||||||
EdgePolicy() *models.CachedEdgePolicy
|
EdgePolicy() *models.CachedEdgePolicy
|
||||||
|
|
||||||
|
// BlindedPayment returns the BlindedPayment that this additional edge
|
||||||
|
// info was derived from. It will return nil if this edge was not
|
||||||
|
// derived from a blinded route.
|
||||||
|
BlindedPayment() *BlindedPayment
|
||||||
}
|
}
|
||||||
|
|
||||||
// PayloadSizeFunc defines the interface for the payload size function.
|
// PayloadSizeFunc defines the interface for the payload size function.
|
||||||
@@ -60,12 +65,49 @@ func (p *PrivateEdge) IntermediatePayloadSize(amount lnwire.MilliSatoshi,
|
|||||||
return hop.PayloadSize(channelID)
|
return hop.PayloadSize(channelID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BlindedPayment is a no-op for a PrivateEdge since it is not associated with
|
||||||
|
// a blinded payment. This will thus return nil.
|
||||||
|
func (p *PrivateEdge) BlindedPayment() *BlindedPayment {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// BlindedEdge implements the AdditionalEdge interface. Blinded hops are viewed
|
// BlindedEdge implements the AdditionalEdge interface. Blinded hops are viewed
|
||||||
// as additional edges because they are appened at the end of a normal route.
|
// as additional edges because they are appended at the end of a normal route.
|
||||||
type BlindedEdge struct {
|
type BlindedEdge struct {
|
||||||
policy *models.CachedEdgePolicy
|
policy *models.CachedEdgePolicy
|
||||||
cipherText []byte
|
|
||||||
blindingPoint *btcec.PublicKey
|
// blindedPayment is the BlindedPayment that this blinded edge was
|
||||||
|
// derived from.
|
||||||
|
blindedPayment *BlindedPayment
|
||||||
|
|
||||||
|
// hopIndex is the index of the hop in the blinded payment path that
|
||||||
|
// this edge is associated with.
|
||||||
|
hopIndex int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBlindedEdge constructs a new BlindedEdge which packages the policy info
|
||||||
|
// for a specific hop within the given blinded payment path. The hop index
|
||||||
|
// should correspond to the hop within the blinded payment that this edge is
|
||||||
|
// associated with.
|
||||||
|
func NewBlindedEdge(policy *models.CachedEdgePolicy, payment *BlindedPayment,
|
||||||
|
hopIndex int) (*BlindedEdge, error) {
|
||||||
|
|
||||||
|
if payment == nil {
|
||||||
|
return nil, fmt.Errorf("blinded payment cannot be nil for " +
|
||||||
|
"blinded edge")
|
||||||
|
}
|
||||||
|
|
||||||
|
if hopIndex < 0 || hopIndex >= len(payment.BlindedPath.BlindedHops) {
|
||||||
|
return nil, fmt.Errorf("the hop index %d is outside the "+
|
||||||
|
"valid range between 0 and %d", hopIndex,
|
||||||
|
len(payment.BlindedPath.BlindedHops)-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &BlindedEdge{
|
||||||
|
policy: policy,
|
||||||
|
hopIndex: hopIndex,
|
||||||
|
blindedPayment: payment,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EdgePolicy return the policy of the BlindedEdge.
|
// EdgePolicy return the policy of the BlindedEdge.
|
||||||
@@ -78,15 +120,23 @@ func (b *BlindedEdge) EdgePolicy() *models.CachedEdgePolicy {
|
|||||||
func (b *BlindedEdge) IntermediatePayloadSize(_ lnwire.MilliSatoshi, _ uint32,
|
func (b *BlindedEdge) IntermediatePayloadSize(_ lnwire.MilliSatoshi, _ uint32,
|
||||||
_ uint64) uint64 {
|
_ uint64) uint64 {
|
||||||
|
|
||||||
|
blindedPath := b.blindedPayment.BlindedPath
|
||||||
|
|
||||||
hop := route.Hop{
|
hop := route.Hop{
|
||||||
BlindingPoint: b.blindingPoint,
|
BlindingPoint: blindedPath.BlindingPoint,
|
||||||
EncryptedData: b.cipherText,
|
EncryptedData: blindedPath.BlindedHops[b.hopIndex].CipherText,
|
||||||
}
|
}
|
||||||
|
|
||||||
// For blinded paths the next chanID is in the encrypted data tlv.
|
// For blinded paths the next chanID is in the encrypted data tlv.
|
||||||
return hop.PayloadSize(0)
|
return hop.PayloadSize(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BlindedPayment returns the blinded payment that this edge is associated
|
||||||
|
// with.
|
||||||
|
func (b *BlindedEdge) BlindedPayment() *BlindedPayment {
|
||||||
|
return b.blindedPayment
|
||||||
|
}
|
||||||
|
|
||||||
// Compile-time constraints to ensure the PrivateEdge and the BlindedEdge
|
// Compile-time constraints to ensure the PrivateEdge and the BlindedEdge
|
||||||
// implement the AdditionalEdge interface.
|
// implement the AdditionalEdge interface.
|
||||||
var _ AdditionalEdge = (*PrivateEdge)(nil)
|
var _ AdditionalEdge = (*PrivateEdge)(nil)
|
||||||
|
|||||||
@@ -42,9 +42,13 @@ func TestIntermediatePayloadSize(t *testing.T) {
|
|||||||
hop: route.Hop{
|
hop: route.Hop{
|
||||||
EncryptedData: []byte{12, 13},
|
EncryptedData: []byte{12, 13},
|
||||||
},
|
},
|
||||||
edge: &BlindedEdge{
|
edge: &BlindedEdge{blindedPayment: &BlindedPayment{
|
||||||
cipherText: []byte{12, 13},
|
BlindedPath: &sphinx.BlindedPath{
|
||||||
},
|
BlindedHops: []*sphinx.BlindedHopInfo{
|
||||||
|
{CipherText: []byte{12, 13}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Blinded edge - introduction point",
|
name: "Blinded edge - introduction point",
|
||||||
@@ -52,10 +56,14 @@ func TestIntermediatePayloadSize(t *testing.T) {
|
|||||||
EncryptedData: []byte{12, 13},
|
EncryptedData: []byte{12, 13},
|
||||||
BlindingPoint: blindedPoint,
|
BlindingPoint: blindedPoint,
|
||||||
},
|
},
|
||||||
edge: &BlindedEdge{
|
edge: &BlindedEdge{blindedPayment: &BlindedPayment{
|
||||||
cipherText: []byte{12, 13},
|
BlindedPath: &sphinx.BlindedPath{
|
||||||
blindingPoint: blindedPoint,
|
BlindingPoint: blindedPoint,
|
||||||
},
|
BlindedHops: []*sphinx.BlindedHopInfo{
|
||||||
|
{CipherText: []byte{12, 13}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+13
-15
@@ -88,13 +88,13 @@ func (b *BlindedPayment) Validate() error {
|
|||||||
// the case of multiple blinded hops, CLTV delta is fully accounted for in the
|
// the case of multiple blinded hops, CLTV delta is fully accounted for in the
|
||||||
// hints (both for intermediate hops and the final_cltv_delta for the receiving
|
// hints (both for intermediate hops and the final_cltv_delta for the receiving
|
||||||
// node).
|
// node).
|
||||||
func (b *BlindedPayment) toRouteHints() RouteHints {
|
func (b *BlindedPayment) toRouteHints() (RouteHints, error) {
|
||||||
// If we just have a single hop in our blinded route, it just contains
|
// If we just have a single hop in our blinded route, it just contains
|
||||||
// an introduction node (this is a valid path according to the spec).
|
// an introduction node (this is a valid path according to the spec).
|
||||||
// Since we have the un-blinded node ID for the introduction node, we
|
// Since we have the un-blinded node ID for the introduction node, we
|
||||||
// don't need to add any route hints.
|
// don't need to add any route hints.
|
||||||
if len(b.BlindedPath.BlindedHops) == 1 {
|
if len(b.BlindedPath.BlindedHops) == 1 {
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
hintCount := len(b.BlindedPath.BlindedHops) - 1
|
hintCount := len(b.BlindedPath.BlindedHops) - 1
|
||||||
@@ -136,14 +136,13 @@ func (b *BlindedPayment) toRouteHints() RouteHints {
|
|||||||
ToNodeFeatures: features,
|
ToNodeFeatures: features,
|
||||||
}
|
}
|
||||||
|
|
||||||
hints[fromNode] = []AdditionalEdge{
|
edge, err := NewBlindedEdge(edgePolicy, b, 0)
|
||||||
&BlindedEdge{
|
if err != nil {
|
||||||
policy: edgePolicy,
|
return nil, err
|
||||||
cipherText: b.BlindedPath.BlindedHops[0].CipherText,
|
|
||||||
blindingPoint: b.BlindedPath.BlindingPoint,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hints[fromNode] = []AdditionalEdge{edge}
|
||||||
|
|
||||||
// Start at an offset of 1 because the first node in our blinded hops
|
// Start at an offset of 1 because the first node in our blinded hops
|
||||||
// is the introduction node and terminate at the second-last node
|
// is the introduction node and terminate at the second-last node
|
||||||
// because we're dealing with hops as pairs.
|
// because we're dealing with hops as pairs.
|
||||||
@@ -169,14 +168,13 @@ func (b *BlindedPayment) toRouteHints() RouteHints {
|
|||||||
ToNodeFeatures: features,
|
ToNodeFeatures: features,
|
||||||
}
|
}
|
||||||
|
|
||||||
hints[fromNode] = []AdditionalEdge{
|
edge, err := NewBlindedEdge(edgePolicy, b, i)
|
||||||
&BlindedEdge{
|
if err != nil {
|
||||||
policy: edgePolicy,
|
return nil, err
|
||||||
cipherText: b.BlindedPath.BlindedHops[i].
|
|
||||||
CipherText,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hints[fromNode] = []AdditionalEdge{edge}
|
||||||
}
|
}
|
||||||
|
|
||||||
return hints
|
return hints, nil
|
||||||
}
|
}
|
||||||
+33
-29
@@ -128,7 +128,9 @@ func TestBlindedPaymentToHints(t *testing.T) {
|
|||||||
HtlcMaximum: htlcMax,
|
HtlcMaximum: htlcMax,
|
||||||
Features: features,
|
Features: features,
|
||||||
}
|
}
|
||||||
require.Nil(t, blindedPayment.toRouteHints())
|
hints, err := blindedPayment.toRouteHints()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Nil(t, hints)
|
||||||
|
|
||||||
// Populate the blinded payment with hops.
|
// Populate the blinded payment with hops.
|
||||||
blindedPayment.BlindedPath.BlindedHops = []*sphinx.BlindedHopInfo{
|
blindedPayment.BlindedPath.BlindedHops = []*sphinx.BlindedHopInfo{
|
||||||
@@ -146,41 +148,43 @@ func TestBlindedPaymentToHints(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
policy1 := &models.CachedEdgePolicy{
|
||||||
|
TimeLockDelta: cltvDelta,
|
||||||
|
MinHTLC: lnwire.MilliSatoshi(htlcMin),
|
||||||
|
MaxHTLC: lnwire.MilliSatoshi(htlcMax),
|
||||||
|
FeeBaseMSat: lnwire.MilliSatoshi(baseFee),
|
||||||
|
FeeProportionalMillionths: lnwire.MilliSatoshi(
|
||||||
|
ppmFee,
|
||||||
|
),
|
||||||
|
ToNodePubKey: func() route.Vertex {
|
||||||
|
return vb2
|
||||||
|
},
|
||||||
|
ToNodeFeatures: features,
|
||||||
|
}
|
||||||
|
policy2 := &models.CachedEdgePolicy{
|
||||||
|
ToNodePubKey: func() route.Vertex {
|
||||||
|
return vb3
|
||||||
|
},
|
||||||
|
ToNodeFeatures: features,
|
||||||
|
}
|
||||||
|
|
||||||
|
blindedEdge1, err := NewBlindedEdge(policy1, blindedPayment, 0)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
blindedEdge2, err := NewBlindedEdge(policy2, blindedPayment, 1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
expected := RouteHints{
|
expected := RouteHints{
|
||||||
v1: {
|
v1: {
|
||||||
//nolint:lll
|
blindedEdge1,
|
||||||
&BlindedEdge{
|
|
||||||
policy: &models.CachedEdgePolicy{
|
|
||||||
TimeLockDelta: cltvDelta,
|
|
||||||
MinHTLC: lnwire.MilliSatoshi(htlcMin),
|
|
||||||
MaxHTLC: lnwire.MilliSatoshi(htlcMax),
|
|
||||||
FeeBaseMSat: lnwire.MilliSatoshi(baseFee),
|
|
||||||
FeeProportionalMillionths: lnwire.MilliSatoshi(
|
|
||||||
ppmFee,
|
|
||||||
),
|
|
||||||
ToNodePubKey: func() route.Vertex {
|
|
||||||
return vb2
|
|
||||||
},
|
|
||||||
ToNodeFeatures: features,
|
|
||||||
},
|
|
||||||
blindingPoint: blindedPoint,
|
|
||||||
cipherText: cipherText,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
vb2: {
|
vb2: {
|
||||||
&BlindedEdge{
|
blindedEdge2,
|
||||||
policy: &models.CachedEdgePolicy{
|
|
||||||
ToNodePubKey: func() route.Vertex {
|
|
||||||
return vb3
|
|
||||||
},
|
|
||||||
ToNodeFeatures: features,
|
|
||||||
},
|
|
||||||
cipherText: cipherText,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
actual := blindedPayment.toRouteHints()
|
actual, err := blindedPayment.toRouteHints()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Equal(t, len(expected), len(actual))
|
require.Equal(t, len(expected), len(actual))
|
||||||
for vertex, expectedHint := range expected {
|
for vertex, expectedHint := range expected {
|
||||||
|
|||||||
+27
-7
@@ -96,9 +96,17 @@ type edgePolicyWithSource struct {
|
|||||||
// such as amounts and cltvs, as well as more complex features like destination
|
// such as amounts and cltvs, as well as more complex features like destination
|
||||||
// custom records and payment address.
|
// custom records and payment address.
|
||||||
type finalHopParams struct {
|
type finalHopParams struct {
|
||||||
amt lnwire.MilliSatoshi
|
amt lnwire.MilliSatoshi
|
||||||
totalAmt lnwire.MilliSatoshi
|
totalAmt lnwire.MilliSatoshi
|
||||||
cltvDelta uint16
|
|
||||||
|
// cltvDelta is the final hop's minimum CLTV expiry delta.
|
||||||
|
//
|
||||||
|
// NOTE that in the case of paying to a blinded path, this value will
|
||||||
|
// be set to a duplicate of the blinded path's accumulated CLTV value.
|
||||||
|
// We would then only need to use this value in the case where the
|
||||||
|
// introduction node of the path is also the destination node.
|
||||||
|
cltvDelta uint16
|
||||||
|
|
||||||
records record.CustomSet
|
records record.CustomSet
|
||||||
paymentAddr *[32]byte
|
paymentAddr *[32]byte
|
||||||
|
|
||||||
@@ -190,10 +198,21 @@ func newRoute(sourceVertex route.Vertex,
|
|||||||
// reporting through RPC. Set to zero for the final hop.
|
// reporting through RPC. Set to zero for the final hop.
|
||||||
fee = 0
|
fee = 0
|
||||||
|
|
||||||
// As this is the last hop, we'll use the specified
|
// Only include the final hop CLTV delta in the total
|
||||||
// final CLTV delta value instead of the value from the
|
// time lock value if this is not a route to a blinded
|
||||||
// last link in the route.
|
// path. For blinded paths, the total time-lock from the
|
||||||
totalTimeLock += uint32(finalHop.cltvDelta)
|
// whole path will be deduced from the introduction
|
||||||
|
// node's CLTV delta. The exception is for the case
|
||||||
|
// where the final hop is the blinded path introduction
|
||||||
|
// node.
|
||||||
|
if blindedPath == nil ||
|
||||||
|
len(blindedPath.BlindedHops) == 1 {
|
||||||
|
|
||||||
|
// As this is the last hop, we'll use the
|
||||||
|
// specified final CLTV delta value instead of
|
||||||
|
// the value from the last link in the route.
|
||||||
|
totalTimeLock += uint32(finalHop.cltvDelta)
|
||||||
|
}
|
||||||
outgoingTimeLock = totalTimeLock
|
outgoingTimeLock = totalTimeLock
|
||||||
|
|
||||||
// Attach any custom records to the final hop.
|
// Attach any custom records to the final hop.
|
||||||
@@ -968,6 +987,7 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
|
|||||||
inboundFee,
|
inboundFee,
|
||||||
fakeHopHintCapacity,
|
fakeHopHintCapacity,
|
||||||
reverseEdge.edge.IntermediatePayloadSize,
|
reverseEdge.edge.IntermediatePayloadSize,
|
||||||
|
reverseEdge.edge.BlindedPayment(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3322,16 +3322,19 @@ func TestBlindedRouteConstruction(t *testing.T) {
|
|||||||
ToNodeFeatures: tlvFeatures,
|
ToNodeFeatures: tlvFeatures,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create final hop parameters for payment amount = 110. Note
|
// Create final hop parameters for payment amount = 110.
|
||||||
// that final cltv delta is not set because blinded paths
|
|
||||||
// include this final delta in their aggregate delta. A
|
|
||||||
// sender-set delta may be added to account for block arrival
|
|
||||||
// during payment, but we do not set it in this test.
|
|
||||||
totalAmt lnwire.MilliSatoshi = 110
|
totalAmt lnwire.MilliSatoshi = 110
|
||||||
finalHopParams = finalHopParams{
|
finalHopParams = finalHopParams{
|
||||||
amt: totalAmt,
|
amt: totalAmt,
|
||||||
totalAmt: totalAmt,
|
totalAmt: totalAmt,
|
||||||
metadata: metadata,
|
metadata: metadata,
|
||||||
|
|
||||||
|
// We set a CLTV delta here just to test that this will
|
||||||
|
// be ignored by newRoute since this is a blinded path
|
||||||
|
// where the accumulated CLTV delta for the route
|
||||||
|
// communicated in the blinded path should be assumed to
|
||||||
|
// include the CLTV delta of the final hop.
|
||||||
|
cltvDelta: MaxCLTVDelta,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -3341,7 +3344,9 @@ func TestBlindedRouteConstruction(t *testing.T) {
|
|||||||
// that make up the graph we'll give to route construction. The hints
|
// that make up the graph we'll give to route construction. The hints
|
||||||
// map is keyed by source node, so we can retrieve our blinded edges
|
// map is keyed by source node, so we can retrieve our blinded edges
|
||||||
// accordingly.
|
// accordingly.
|
||||||
blindedEdges := blindedPayment.toRouteHints()
|
blindedEdges, err := blindedPayment.toRouteHints()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
carolDaveEdge := blindedEdges[carolVertex][0]
|
carolDaveEdge := blindedEdges[carolVertex][0]
|
||||||
daveEveEdge := blindedEdges[daveBlindedVertex][0]
|
daveEveEdge := blindedEdges[daveBlindedVertex][0]
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -2001,6 +2001,7 @@ func NewRouteRequest(source route.Vertex, target *route.Vertex,
|
|||||||
// Assume that we're starting off with a regular payment.
|
// Assume that we're starting off with a regular payment.
|
||||||
requestHints = routeHints
|
requestHints = routeHints
|
||||||
requestExpiry = finalExpiry
|
requestExpiry = finalExpiry
|
||||||
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
if blindedPayment != nil {
|
if blindedPayment != nil {
|
||||||
@@ -2038,7 +2039,10 @@ func NewRouteRequest(source route.Vertex, target *route.Vertex,
|
|||||||
requestExpiry = blindedPayment.CltvExpiryDelta
|
requestExpiry = blindedPayment.CltvExpiryDelta
|
||||||
}
|
}
|
||||||
|
|
||||||
requestHints = blindedPayment.toRouteHints()
|
requestHints, err = blindedPayment.toRouteHints()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
requestTarget, err := getTargetNode(target, blindedPayment)
|
requestTarget, err := getTargetNode(target, blindedPayment)
|
||||||
|
|||||||
+38
-26
@@ -51,7 +51,8 @@ func newNodeEdgeUnifier(sourceNode, toNode route.Vertex, useInboundFees bool,
|
|||||||
// incorrectly specified.
|
// incorrectly specified.
|
||||||
func (u *nodeEdgeUnifier) addPolicy(fromNode route.Vertex,
|
func (u *nodeEdgeUnifier) addPolicy(fromNode route.Vertex,
|
||||||
edge *models.CachedEdgePolicy, inboundFee models.InboundFee,
|
edge *models.CachedEdgePolicy, inboundFee models.InboundFee,
|
||||||
capacity btcutil.Amount, hopPayloadSizeFn PayloadSizeFunc) {
|
capacity btcutil.Amount, hopPayloadSizeFn PayloadSizeFunc,
|
||||||
|
blindedPayment *BlindedPayment) {
|
||||||
|
|
||||||
localChan := fromNode == u.sourceNode
|
localChan := fromNode == u.sourceNode
|
||||||
|
|
||||||
@@ -86,12 +87,9 @@ func (u *nodeEdgeUnifier) addPolicy(fromNode route.Vertex,
|
|||||||
inboundFee = models.InboundFee{}
|
inboundFee = models.InboundFee{}
|
||||||
}
|
}
|
||||||
|
|
||||||
unifier.edges = append(unifier.edges, &unifiedEdge{
|
unifier.edges = append(unifier.edges, newUnifiedEdge(
|
||||||
policy: edge,
|
edge, capacity, inboundFee, hopPayloadSizeFn, blindedPayment,
|
||||||
capacity: capacity,
|
))
|
||||||
hopPayloadSizeFn: hopPayloadSizeFn,
|
|
||||||
inboundFees: inboundFee,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// addGraphPolicies adds all policies that are known for the toNode in the
|
// addGraphPolicies adds all policies that are known for the toNode in the
|
||||||
@@ -115,7 +113,7 @@ func (u *nodeEdgeUnifier) addGraphPolicies(g routingGraph) error {
|
|||||||
|
|
||||||
u.addPolicy(
|
u.addPolicy(
|
||||||
channel.OtherNode, channel.InPolicy, inboundFee,
|
channel.OtherNode, channel.InPolicy, inboundFee,
|
||||||
channel.Capacity, defaultHopPayloadSize,
|
channel.Capacity, defaultHopPayloadSize, nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -137,6 +135,24 @@ type unifiedEdge struct {
|
|||||||
// is needed because hops of a blinded path differ in their payload
|
// is needed because hops of a blinded path differ in their payload
|
||||||
// structure compared to cleartext hops.
|
// structure compared to cleartext hops.
|
||||||
hopPayloadSizeFn PayloadSizeFunc
|
hopPayloadSizeFn PayloadSizeFunc
|
||||||
|
|
||||||
|
// blindedPayment if set, is the BlindedPayment that this edge was
|
||||||
|
// derived from originally.
|
||||||
|
blindedPayment *BlindedPayment
|
||||||
|
}
|
||||||
|
|
||||||
|
// newUnifiedEdge constructs a new unifiedEdge.
|
||||||
|
func newUnifiedEdge(policy *models.CachedEdgePolicy, capacity btcutil.Amount,
|
||||||
|
inboundFees models.InboundFee, hopPayloadSizeFn PayloadSizeFunc,
|
||||||
|
blindedPayment *BlindedPayment) *unifiedEdge {
|
||||||
|
|
||||||
|
return &unifiedEdge{
|
||||||
|
policy: policy,
|
||||||
|
capacity: capacity,
|
||||||
|
inboundFees: inboundFees,
|
||||||
|
hopPayloadSizeFn: hopPayloadSizeFn,
|
||||||
|
blindedPayment: blindedPayment,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// amtInRange checks whether an amount falls within the valid range for a
|
// amtInRange checks whether an amount falls within the valid range for a
|
||||||
@@ -292,12 +308,10 @@ func (u *edgeUnifier) getEdgeLocal(netAmtReceived lnwire.MilliSatoshi,
|
|||||||
maxBandwidth = bandwidth
|
maxBandwidth = bandwidth
|
||||||
|
|
||||||
// Update best edge.
|
// Update best edge.
|
||||||
bestEdge = &unifiedEdge{
|
bestEdge = newUnifiedEdge(
|
||||||
policy: edge.policy,
|
edge.policy, edge.capacity, edge.inboundFees,
|
||||||
capacity: edge.capacity,
|
edge.hopPayloadSizeFn, edge.blindedPayment,
|
||||||
hopPayloadSizeFn: edge.hopPayloadSizeFn,
|
)
|
||||||
inboundFees: edge.inboundFees,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return bestEdge
|
return bestEdge
|
||||||
@@ -376,10 +390,10 @@ func (u *edgeUnifier) getEdgeNetwork(netAmtReceived lnwire.MilliSatoshi,
|
|||||||
}
|
}
|
||||||
maxFee = fee
|
maxFee = fee
|
||||||
|
|
||||||
bestPolicy = &unifiedEdge{
|
bestPolicy = newUnifiedEdge(
|
||||||
policy: edge.policy,
|
edge.policy, 0, edge.inboundFees, nil,
|
||||||
inboundFees: edge.inboundFees,
|
edge.blindedPayment,
|
||||||
}
|
)
|
||||||
|
|
||||||
// The payload size function for edges to a connected peer is
|
// The payload size function for edges to a connected peer is
|
||||||
// always the same hence there is not need to find the maximum.
|
// always the same hence there is not need to find the maximum.
|
||||||
@@ -404,15 +418,13 @@ func (u *edgeUnifier) getEdgeNetwork(netAmtReceived lnwire.MilliSatoshi,
|
|||||||
// chance for this node pair. But this is all only needed for nodes that
|
// chance for this node pair. But this is all only needed for nodes that
|
||||||
// have distinct policies for channels to the same peer.
|
// have distinct policies for channels to the same peer.
|
||||||
policyCopy := *bestPolicy.policy
|
policyCopy := *bestPolicy.policy
|
||||||
modifiedEdge := unifiedEdge{
|
policyCopy.TimeLockDelta = maxTimelock
|
||||||
policy: &policyCopy,
|
modifiedEdge := newUnifiedEdge(
|
||||||
inboundFees: bestPolicy.inboundFees,
|
&policyCopy, maxCapMsat.ToSatoshis(), bestPolicy.inboundFees,
|
||||||
}
|
hopPayloadSizeFn, bestPolicy.blindedPayment,
|
||||||
modifiedEdge.policy.TimeLockDelta = maxTimelock
|
)
|
||||||
modifiedEdge.capacity = maxCapMsat.ToSatoshis()
|
|
||||||
modifiedEdge.hopPayloadSizeFn = hopPayloadSizeFn
|
|
||||||
|
|
||||||
return &modifiedEdge
|
return modifiedEdge
|
||||||
}
|
}
|
||||||
|
|
||||||
// minAmt returns the minimum amount that can be forwarded on this connection.
|
// minAmt returns the minimum amount that can be forwarded on this connection.
|
||||||
|
|||||||
@@ -59,37 +59,37 @@ func TestNodeEdgeUnifier(t *testing.T) {
|
|||||||
unifierFilled := newNodeEdgeUnifier(source, toNode, false, nil)
|
unifierFilled := newNodeEdgeUnifier(source, toNode, false, nil)
|
||||||
|
|
||||||
unifierFilled.addPolicy(
|
unifierFilled.addPolicy(
|
||||||
fromNode, &p1, inboundFee1, c1, defaultHopPayloadSize,
|
fromNode, &p1, inboundFee1, c1, defaultHopPayloadSize, nil,
|
||||||
)
|
)
|
||||||
unifierFilled.addPolicy(
|
unifierFilled.addPolicy(
|
||||||
fromNode, &p2, inboundFee2, c2, defaultHopPayloadSize,
|
fromNode, &p2, inboundFee2, c2, defaultHopPayloadSize, nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
unifierNoCapacity := newNodeEdgeUnifier(source, toNode, false, nil)
|
unifierNoCapacity := newNodeEdgeUnifier(source, toNode, false, nil)
|
||||||
unifierNoCapacity.addPolicy(
|
unifierNoCapacity.addPolicy(
|
||||||
fromNode, &p1, inboundFee1, 0, defaultHopPayloadSize,
|
fromNode, &p1, inboundFee1, 0, defaultHopPayloadSize, nil,
|
||||||
)
|
)
|
||||||
unifierNoCapacity.addPolicy(
|
unifierNoCapacity.addPolicy(
|
||||||
fromNode, &p2, inboundFee2, 0, defaultHopPayloadSize,
|
fromNode, &p2, inboundFee2, 0, defaultHopPayloadSize, nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
unifierNoInfo := newNodeEdgeUnifier(source, toNode, false, nil)
|
unifierNoInfo := newNodeEdgeUnifier(source, toNode, false, nil)
|
||||||
unifierNoInfo.addPolicy(
|
unifierNoInfo.addPolicy(
|
||||||
fromNode, &models.CachedEdgePolicy{}, models.InboundFee{},
|
fromNode, &models.CachedEdgePolicy{}, models.InboundFee{},
|
||||||
0, defaultHopPayloadSize,
|
0, defaultHopPayloadSize, nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
unifierInboundFee := newNodeEdgeUnifier(source, toNode, true, nil)
|
unifierInboundFee := newNodeEdgeUnifier(source, toNode, true, nil)
|
||||||
unifierInboundFee.addPolicy(
|
unifierInboundFee.addPolicy(
|
||||||
fromNode, &p1, inboundFee1, c1, defaultHopPayloadSize,
|
fromNode, &p1, inboundFee1, c1, defaultHopPayloadSize, nil,
|
||||||
)
|
)
|
||||||
unifierInboundFee.addPolicy(
|
unifierInboundFee.addPolicy(
|
||||||
fromNode, &p2, inboundFee2, c2, defaultHopPayloadSize,
|
fromNode, &p2, inboundFee2, c2, defaultHopPayloadSize, nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
unifierLocal := newNodeEdgeUnifier(fromNode, toNode, true, nil)
|
unifierLocal := newNodeEdgeUnifier(fromNode, toNode, true, nil)
|
||||||
unifierLocal.addPolicy(
|
unifierLocal.addPolicy(
|
||||||
fromNode, &p1, inboundFee1, c1, defaultHopPayloadSize,
|
fromNode, &p1, inboundFee1, c1, defaultHopPayloadSize, nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
inboundFeeZero := models.InboundFee{}
|
inboundFeeZero := models.InboundFee{}
|
||||||
@@ -98,10 +98,11 @@ func TestNodeEdgeUnifier(t *testing.T) {
|
|||||||
}
|
}
|
||||||
unifierNegInboundFee := newNodeEdgeUnifier(source, toNode, true, nil)
|
unifierNegInboundFee := newNodeEdgeUnifier(source, toNode, true, nil)
|
||||||
unifierNegInboundFee.addPolicy(
|
unifierNegInboundFee.addPolicy(
|
||||||
fromNode, &p1, inboundFeeZero, c1, defaultHopPayloadSize,
|
fromNode, &p1, inboundFeeZero, c1, defaultHopPayloadSize, nil,
|
||||||
)
|
)
|
||||||
unifierNegInboundFee.addPolicy(
|
unifierNegInboundFee.addPolicy(
|
||||||
fromNode, &p2, inboundFeeNegative, c2, defaultHopPayloadSize,
|
fromNode, &p2, inboundFeeNegative, c2, defaultHopPayloadSize,
|
||||||
|
nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
|
|||||||
@@ -6971,6 +6971,13 @@ func (r *rpcServer) DecodePayReq(ctx context.Context,
|
|||||||
// Convert between the `lnrpc` and `routing` types.
|
// Convert between the `lnrpc` and `routing` types.
|
||||||
routeHints := invoicesrpc.CreateRPCRouteHints(payReq.RouteHints)
|
routeHints := invoicesrpc.CreateRPCRouteHints(payReq.RouteHints)
|
||||||
|
|
||||||
|
blindedPaymentPaths, err := invoicesrpc.CreateRPCBlindedPayments(
|
||||||
|
payReq.BlindedPaymentPaths,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
var amtSat, amtMsat int64
|
var amtSat, amtMsat int64
|
||||||
if payReq.MilliSat != nil {
|
if payReq.MilliSat != nil {
|
||||||
amtSat = int64(payReq.MilliSat.ToSatoshis())
|
amtSat = int64(payReq.MilliSat.ToSatoshis())
|
||||||
@@ -6996,6 +7003,7 @@ func (r *rpcServer) DecodePayReq(ctx context.Context,
|
|||||||
Expiry: expiry,
|
Expiry: expiry,
|
||||||
CltvExpiry: int64(payReq.MinFinalCLTVExpiry()),
|
CltvExpiry: int64(payReq.MinFinalCLTVExpiry()),
|
||||||
RouteHints: routeHints,
|
RouteHints: routeHints,
|
||||||
|
BlindedPaths: blindedPaymentPaths,
|
||||||
PaymentAddr: paymentAddr,
|
PaymentAddr: paymentAddr,
|
||||||
Features: invoicesrpc.CreateRPCFeatures(payReq.Features),
|
Features: invoicesrpc.CreateRPCFeatures(payReq.Features),
|
||||||
}, nil
|
}, nil
|
||||||
|
|||||||
@@ -0,0 +1,246 @@
|
|||||||
|
package zpay32
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/btcec/v2"
|
||||||
|
sphinx "github.com/lightningnetwork/lightning-onion"
|
||||||
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
|
"github.com/lightningnetwork/lnd/tlv"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// relayInfoSize is the number of bytes that the relay info of a blinded
|
||||||
|
// payment will occupy.
|
||||||
|
// base fee: 4 bytes
|
||||||
|
// prop fee: 4 bytes
|
||||||
|
// cltv delta: 2 bytes
|
||||||
|
// min htlc: 8 bytes
|
||||||
|
// max htlc: 8 bytes
|
||||||
|
relayInfoSize = 26
|
||||||
|
|
||||||
|
// maxNumHopsPerPath is the maximum number of blinded path hops that can
|
||||||
|
// be included in a single encoded blinded path. This is calculated
|
||||||
|
// based on the `data_length` limit of 638 bytes for any tagged field in
|
||||||
|
// a BOLT 11 invoice along with the estimated number of bytes required
|
||||||
|
// for encoding the most minimal blinded path hop. See the [bLIP
|
||||||
|
// proposal](https://github.com/lightning/blips/pull/39) for a detailed
|
||||||
|
// calculation.
|
||||||
|
maxNumHopsPerPath = 7
|
||||||
|
)
|
||||||
|
|
||||||
|
// BlindedPaymentPath holds all the information a payer needs to know about a
|
||||||
|
// blinded path to a receiver of a payment.
|
||||||
|
type BlindedPaymentPath struct {
|
||||||
|
// FeeBaseMsat is the total base fee for the path in milli-satoshis.
|
||||||
|
FeeBaseMsat uint32
|
||||||
|
|
||||||
|
// FeeRate is the total fee rate for the path in parts per million.
|
||||||
|
FeeRate uint32
|
||||||
|
|
||||||
|
// CltvExpiryDelta is the total CLTV delta to apply to the path.
|
||||||
|
CltvExpiryDelta uint16
|
||||||
|
|
||||||
|
// HTLCMinMsat is the minimum number of milli-satoshis that any hop in
|
||||||
|
// the path will route.
|
||||||
|
HTLCMinMsat uint64
|
||||||
|
|
||||||
|
// HTLCMaxMsat is the maximum number of milli-satoshis that a hop in the
|
||||||
|
// path will route.
|
||||||
|
HTLCMaxMsat uint64
|
||||||
|
|
||||||
|
// Features is the feature bit vector for the path.
|
||||||
|
Features *lnwire.FeatureVector
|
||||||
|
|
||||||
|
// FirstEphemeralBlindingPoint is the blinding point to send to the
|
||||||
|
// introduction node. It will be used by the introduction node to derive
|
||||||
|
// a shared secret with the receiver which can then be used to decode
|
||||||
|
// the encrypted payload from the receiver.
|
||||||
|
FirstEphemeralBlindingPoint *btcec.PublicKey
|
||||||
|
|
||||||
|
// Hops is the blinded path. The first hop is the introduction node and
|
||||||
|
// so the BlindedNodeID of this hop will be the real node ID.
|
||||||
|
Hops []*sphinx.BlindedHopInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeBlindedPayment attempts to parse a BlindedPaymentPath from the passed
|
||||||
|
// reader.
|
||||||
|
func DecodeBlindedPayment(r io.Reader) (*BlindedPaymentPath, error) {
|
||||||
|
var relayInfo [relayInfoSize]byte
|
||||||
|
n, err := r.Read(relayInfo[:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if n != relayInfoSize {
|
||||||
|
return nil, fmt.Errorf("unable to read %d relay info bytes "+
|
||||||
|
"off of the given stream: %w", relayInfoSize, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var payment BlindedPaymentPath
|
||||||
|
|
||||||
|
// Parse the relay info fields.
|
||||||
|
payment.FeeBaseMsat = binary.BigEndian.Uint32(relayInfo[:4])
|
||||||
|
payment.FeeRate = binary.BigEndian.Uint32(relayInfo[4:8])
|
||||||
|
payment.CltvExpiryDelta = binary.BigEndian.Uint16(relayInfo[8:10])
|
||||||
|
payment.HTLCMinMsat = binary.BigEndian.Uint64(relayInfo[10:18])
|
||||||
|
payment.HTLCMaxMsat = binary.BigEndian.Uint64(relayInfo[18:])
|
||||||
|
|
||||||
|
// Parse the feature bit vector.
|
||||||
|
f := lnwire.EmptyFeatureVector()
|
||||||
|
err = f.Decode(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
payment.Features = f
|
||||||
|
|
||||||
|
// Parse the first ephemeral blinding point.
|
||||||
|
var blindingPointBytes [btcec.PubKeyBytesLenCompressed]byte
|
||||||
|
_, err = r.Read(blindingPointBytes[:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
blinding, err := btcec.ParsePubKey(blindingPointBytes[:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
payment.FirstEphemeralBlindingPoint = blinding
|
||||||
|
|
||||||
|
// Read the one byte hop number.
|
||||||
|
var numHops [1]byte
|
||||||
|
_, err = r.Read(numHops[:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
payment.Hops = make([]*sphinx.BlindedHopInfo, int(numHops[0]))
|
||||||
|
|
||||||
|
// Parse each hop.
|
||||||
|
for i := 0; i < len(payment.Hops); i++ {
|
||||||
|
hop, err := DecodeBlindedHop(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
payment.Hops[i] = hop
|
||||||
|
}
|
||||||
|
|
||||||
|
return &payment, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode serialises the BlindedPaymentPath and writes the bytes to the passed
|
||||||
|
// writer.
|
||||||
|
// 1) The first 26 bytes contain the relay info:
|
||||||
|
// - Base Fee in msat: uint32 (4 bytes).
|
||||||
|
// - Proportional Fee in PPM: uint32 (4 bytes).
|
||||||
|
// - CLTV expiry delta: uint16 (2 bytes).
|
||||||
|
// - HTLC min msat: uint64 (8 bytes).
|
||||||
|
// - HTLC max msat: uint64 (8 bytes).
|
||||||
|
//
|
||||||
|
// 2) Feature bit vector length (2 bytes).
|
||||||
|
// 3) Feature bit vector (can be zero length).
|
||||||
|
// 4) First blinding point: 33 bytes.
|
||||||
|
// 5) Number of hops: 1 byte.
|
||||||
|
// 6) Encoded BlindedHops.
|
||||||
|
func (p *BlindedPaymentPath) Encode(w io.Writer) error {
|
||||||
|
var relayInfo [26]byte
|
||||||
|
binary.BigEndian.PutUint32(relayInfo[:4], p.FeeBaseMsat)
|
||||||
|
binary.BigEndian.PutUint32(relayInfo[4:8], p.FeeRate)
|
||||||
|
binary.BigEndian.PutUint16(relayInfo[8:10], p.CltvExpiryDelta)
|
||||||
|
binary.BigEndian.PutUint64(relayInfo[10:18], p.HTLCMinMsat)
|
||||||
|
binary.BigEndian.PutUint64(relayInfo[18:], p.HTLCMaxMsat)
|
||||||
|
|
||||||
|
_, err := w.Write(relayInfo[:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = p.Features.Encode(w)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = w.Write(p.FirstEphemeralBlindingPoint.SerializeCompressed())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
numHops := len(p.Hops)
|
||||||
|
if numHops > maxNumHopsPerPath {
|
||||||
|
return fmt.Errorf("the number of hops, %d, exceeds the "+
|
||||||
|
"maximum of %d", numHops, maxNumHopsPerPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = w.Write([]byte{byte(numHops)})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, hop := range p.Hops {
|
||||||
|
err = EncodeBlindedHop(w, hop)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeBlindedHop reads a sphinx.BlindedHopInfo from the passed reader.
|
||||||
|
func DecodeBlindedHop(r io.Reader) (*sphinx.BlindedHopInfo, error) {
|
||||||
|
var nodeIDBytes [btcec.PubKeyBytesLenCompressed]byte
|
||||||
|
_, err := r.Read(nodeIDBytes[:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeID, err := btcec.ParsePubKey(nodeIDBytes[:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dataLen, err := tlv.ReadVarInt(r, &[8]byte{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptedData := make([]byte, dataLen)
|
||||||
|
_, err = r.Read(encryptedData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &sphinx.BlindedHopInfo{
|
||||||
|
BlindedNodePub: nodeID,
|
||||||
|
CipherText: encryptedData,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeBlindedHop writes the passed BlindedHopInfo to the given writer.
|
||||||
|
//
|
||||||
|
// 1) Blinded node pub key: 33 bytes
|
||||||
|
// 2) Cipher text length: BigSize
|
||||||
|
// 3) Cipher text.
|
||||||
|
func EncodeBlindedHop(w io.Writer, hop *sphinx.BlindedHopInfo) error {
|
||||||
|
_, err := w.Write(hop.BlindedNodePub.SerializeCompressed())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(hop.CipherText) > math.MaxUint16 {
|
||||||
|
return fmt.Errorf("encrypted recipient data can not exceed a "+
|
||||||
|
"length of %d bytes", math.MaxUint16)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tlv.WriteVarInt(w, uint64(len(hop.CipherText)), &[8]byte{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = w.Write(hop.CipherText)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
+41
-3
@@ -215,6 +215,7 @@ func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) er
|
|||||||
}
|
}
|
||||||
|
|
||||||
invoice.PaymentHash, err = parse32Bytes(base32Data)
|
invoice.PaymentHash, err = parse32Bytes(base32Data)
|
||||||
|
|
||||||
case fieldTypeS:
|
case fieldTypeS:
|
||||||
if invoice.PaymentAddr != nil {
|
if invoice.PaymentAddr != nil {
|
||||||
// We skip the field if we have already seen a
|
// We skip the field if we have already seen a
|
||||||
@@ -223,6 +224,7 @@ func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) er
|
|||||||
}
|
}
|
||||||
|
|
||||||
invoice.PaymentAddr, err = parse32Bytes(base32Data)
|
invoice.PaymentAddr, err = parse32Bytes(base32Data)
|
||||||
|
|
||||||
case fieldTypeD:
|
case fieldTypeD:
|
||||||
if invoice.Description != nil {
|
if invoice.Description != nil {
|
||||||
// We skip the field if we have already seen a
|
// We skip the field if we have already seen a
|
||||||
@@ -231,6 +233,7 @@ func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) er
|
|||||||
}
|
}
|
||||||
|
|
||||||
invoice.Description, err = parseDescription(base32Data)
|
invoice.Description, err = parseDescription(base32Data)
|
||||||
|
|
||||||
case fieldTypeM:
|
case fieldTypeM:
|
||||||
if invoice.Metadata != nil {
|
if invoice.Metadata != nil {
|
||||||
// We skip the field if we have already seen a
|
// We skip the field if we have already seen a
|
||||||
@@ -248,6 +251,7 @@ func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) er
|
|||||||
}
|
}
|
||||||
|
|
||||||
invoice.Destination, err = parseDestination(base32Data)
|
invoice.Destination, err = parseDestination(base32Data)
|
||||||
|
|
||||||
case fieldTypeH:
|
case fieldTypeH:
|
||||||
if invoice.DescriptionHash != nil {
|
if invoice.DescriptionHash != nil {
|
||||||
// We skip the field if we have already seen a
|
// We skip the field if we have already seen a
|
||||||
@@ -256,6 +260,7 @@ func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) er
|
|||||||
}
|
}
|
||||||
|
|
||||||
invoice.DescriptionHash, err = parse32Bytes(base32Data)
|
invoice.DescriptionHash, err = parse32Bytes(base32Data)
|
||||||
|
|
||||||
case fieldTypeX:
|
case fieldTypeX:
|
||||||
if invoice.expiry != nil {
|
if invoice.expiry != nil {
|
||||||
// We skip the field if we have already seen a
|
// We skip the field if we have already seen a
|
||||||
@@ -264,6 +269,7 @@ func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) er
|
|||||||
}
|
}
|
||||||
|
|
||||||
invoice.expiry, err = parseExpiry(base32Data)
|
invoice.expiry, err = parseExpiry(base32Data)
|
||||||
|
|
||||||
case fieldTypeC:
|
case fieldTypeC:
|
||||||
if invoice.minFinalCLTVExpiry != nil {
|
if invoice.minFinalCLTVExpiry != nil {
|
||||||
// We skip the field if we have already seen a
|
// We skip the field if we have already seen a
|
||||||
@@ -271,7 +277,9 @@ func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) er
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
invoice.minFinalCLTVExpiry, err = parseMinFinalCLTVExpiry(base32Data)
|
invoice.minFinalCLTVExpiry, err =
|
||||||
|
parseMinFinalCLTVExpiry(base32Data)
|
||||||
|
|
||||||
case fieldTypeF:
|
case fieldTypeF:
|
||||||
if invoice.FallbackAddr != nil {
|
if invoice.FallbackAddr != nil {
|
||||||
// We skip the field if we have already seen a
|
// We skip the field if we have already seen a
|
||||||
@@ -279,7 +287,10 @@ func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) er
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
invoice.FallbackAddr, err = parseFallbackAddr(base32Data, net)
|
invoice.FallbackAddr, err = parseFallbackAddr(
|
||||||
|
base32Data, net,
|
||||||
|
)
|
||||||
|
|
||||||
case fieldTypeR:
|
case fieldTypeR:
|
||||||
// An `r` field can be included in an invoice multiple
|
// An `r` field can be included in an invoice multiple
|
||||||
// times, so we won't skip it if we have already seen
|
// times, so we won't skip it if we have already seen
|
||||||
@@ -289,7 +300,10 @@ func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) er
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
invoice.RouteHints = append(invoice.RouteHints, routeHint)
|
invoice.RouteHints = append(
|
||||||
|
invoice.RouteHints, routeHint,
|
||||||
|
)
|
||||||
|
|
||||||
case fieldType9:
|
case fieldType9:
|
||||||
if invoice.Features != nil {
|
if invoice.Features != nil {
|
||||||
// We skip the field if we have already seen a
|
// We skip the field if we have already seen a
|
||||||
@@ -298,6 +312,19 @@ func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) er
|
|||||||
}
|
}
|
||||||
|
|
||||||
invoice.Features, err = parseFeatures(base32Data)
|
invoice.Features, err = parseFeatures(base32Data)
|
||||||
|
|
||||||
|
case fieldTypeB:
|
||||||
|
blindedPaymentPath, err := parseBlindedPaymentPath(
|
||||||
|
base32Data,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
invoice.BlindedPaymentPaths = append(
|
||||||
|
invoice.BlindedPaymentPaths, blindedPaymentPath,
|
||||||
|
)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// Ignore unknown type.
|
// Ignore unknown type.
|
||||||
}
|
}
|
||||||
@@ -495,6 +522,17 @@ func parseRouteHint(data []byte) ([]HopHint, error) {
|
|||||||
return routeHint, nil
|
return routeHint, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseBlindedPaymentPath attempts to parse a BlindedPaymentPath from the given
|
||||||
|
// byte slice.
|
||||||
|
func parseBlindedPaymentPath(data []byte) (*BlindedPaymentPath, error) {
|
||||||
|
base256Data, err := bech32.ConvertBits(data, 5, 8, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return DecodeBlindedPayment(bytes.NewReader(base256Data))
|
||||||
|
}
|
||||||
|
|
||||||
// parseFeatures decodes any feature bits directly from the base32
|
// parseFeatures decodes any feature bits directly from the base32
|
||||||
// representation.
|
// representation.
|
||||||
func parseFeatures(data []byte) (*lnwire.FeatureVector, error) {
|
func parseFeatures(data []byte) (*lnwire.FeatureVector, error) {
|
||||||
|
|||||||
@@ -260,6 +260,29 @@ func writeTaggedFields(bufferBase32 *bytes.Buffer, invoice *Invoice) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, path := range invoice.BlindedPaymentPaths {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
err := path.Encode(&buf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
blindedPathBase32, err := bech32.ConvertBits(
|
||||||
|
buf.Bytes(), 8, 5, true,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = writeTaggedField(
|
||||||
|
bufferBase32, fieldTypeB, blindedPathBase32,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if invoice.Destination != nil {
|
if invoice.Destination != nil {
|
||||||
// Convert 33 byte pubkey to 53 5-bit groups.
|
// Convert 33 byte pubkey to 53 5-bit groups.
|
||||||
pubKeyBase32, err := bech32.ConvertBits(
|
pubKeyBase32, err := bech32.ConvertBits(
|
||||||
|
|||||||
+33
-1
@@ -76,6 +76,10 @@ const (
|
|||||||
// probing the recipient.
|
// probing the recipient.
|
||||||
fieldTypeS = 16
|
fieldTypeS = 16
|
||||||
|
|
||||||
|
// fieldTypeB contains blinded payment path information. This field may
|
||||||
|
// be repeated to include multiple blinded payment paths in the invoice.
|
||||||
|
fieldTypeB = 20
|
||||||
|
|
||||||
// maxInvoiceLength is the maximum total length an invoice can have.
|
// maxInvoiceLength is the maximum total length an invoice can have.
|
||||||
// This is chosen to be the maximum number of bytes that can fit into a
|
// This is chosen to be the maximum number of bytes that can fit into a
|
||||||
// single QR code: https://en.wikipedia.org/wiki/QR_code#Storage
|
// single QR code: https://en.wikipedia.org/wiki/QR_code#Storage
|
||||||
@@ -152,6 +156,10 @@ type Invoice struct {
|
|||||||
// This field is un-exported and can only be read by the
|
// This field is un-exported and can only be read by the
|
||||||
// MinFinalCLTVExpiry() method. By forcing callers to read via this
|
// MinFinalCLTVExpiry() method. By forcing callers to read via this
|
||||||
// method, we can easily enforce the default if not specified.
|
// method, we can easily enforce the default if not specified.
|
||||||
|
//
|
||||||
|
// NOTE: this field is ignored in the case that the invoice contains
|
||||||
|
// blinded paths since then the final minimum cltv expiry delta is
|
||||||
|
// expected to be included in the route's accumulated CLTV delta value.
|
||||||
minFinalCLTVExpiry *uint64
|
minFinalCLTVExpiry *uint64
|
||||||
|
|
||||||
// Description is a short description of the purpose of this invoice.
|
// Description is a short description of the purpose of this invoice.
|
||||||
@@ -180,9 +188,17 @@ type Invoice struct {
|
|||||||
// hint can be individually used to reach the destination. These usually
|
// hint can be individually used to reach the destination. These usually
|
||||||
// represent private routes.
|
// represent private routes.
|
||||||
//
|
//
|
||||||
// NOTE: This is optional.
|
// NOTE: This is optional and should not be set at the same time as
|
||||||
|
// BlindedPaymentPaths.
|
||||||
RouteHints [][]HopHint
|
RouteHints [][]HopHint
|
||||||
|
|
||||||
|
// BlindedPaymentPaths is a set of blinded payment paths that can be
|
||||||
|
// used to find the payment receiver.
|
||||||
|
//
|
||||||
|
// NOTE: This is optional and should not be set at the same time as
|
||||||
|
// RouteHints.
|
||||||
|
BlindedPaymentPaths []*BlindedPaymentPath
|
||||||
|
|
||||||
// Features represents an optional field used to signal optional or
|
// Features represents an optional field used to signal optional or
|
||||||
// required support for features by the receiver.
|
// required support for features by the receiver.
|
||||||
Features *lnwire.FeatureVector
|
Features *lnwire.FeatureVector
|
||||||
@@ -263,6 +279,15 @@ func RouteHint(routeHint []HopHint) func(*Invoice) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithBlindedPaymentPath is a functional option that allows a caller of
|
||||||
|
// NewInvoice to attach a blinded payment path to the invoice. The option can
|
||||||
|
// be used multiple times to attach multiple paths.
|
||||||
|
func WithBlindedPaymentPath(p *BlindedPaymentPath) func(*Invoice) {
|
||||||
|
return func(i *Invoice) {
|
||||||
|
i.BlindedPaymentPaths = append(i.BlindedPaymentPaths, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Features is a functional option that allows callers of NewInvoice to set the
|
// Features is a functional option that allows callers of NewInvoice to set the
|
||||||
// desired feature bits that are advertised on the invoice. If this option is
|
// desired feature bits that are advertised on the invoice. If this option is
|
||||||
// not used, an empty feature vector will automatically be populated.
|
// not used, an empty feature vector will automatically be populated.
|
||||||
@@ -355,6 +380,13 @@ func validateInvoice(invoice *Invoice) error {
|
|||||||
return fmt.Errorf("no payment hash found")
|
return fmt.Errorf("no payment hash found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(invoice.RouteHints) != 0 &&
|
||||||
|
len(invoice.BlindedPaymentPaths) != 0 {
|
||||||
|
|
||||||
|
return fmt.Errorf("cannot have both route hints and blinded " +
|
||||||
|
"payment paths")
|
||||||
|
}
|
||||||
|
|
||||||
// Either Description or DescriptionHash must be set, not both.
|
// Either Description or DescriptionHash must be set, not both.
|
||||||
if invoice.Description != nil && invoice.DescriptionHash != nil {
|
if invoice.Description != nil && invoice.DescriptionHash != nil {
|
||||||
return fmt.Errorf("both description and description hash set")
|
return fmt.Errorf("both description and description hash set")
|
||||||
|
|||||||
+147
-113
@@ -7,7 +7,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -17,7 +16,9 @@ import (
|
|||||||
"github.com/btcsuite/btcd/btcutil"
|
"github.com/btcsuite/btcd/btcutil"
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/btcsuite/btcd/chaincfg"
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
sphinx "github.com/lightningnetwork/lightning-onion"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -116,6 +117,62 @@ var (
|
|||||||
|
|
||||||
// Must be initialized in init().
|
// Must be initialized in init().
|
||||||
testDescriptionHash [32]byte
|
testDescriptionHash [32]byte
|
||||||
|
|
||||||
|
testBlindedPK1Bytes, _ = hex.DecodeString("03f3311e948feb5115242c4e39" +
|
||||||
|
"6c81c448ab7ee5fd24c4e24e66c73533cc4f98b8")
|
||||||
|
testBlindedHopPK1, _ = btcec.ParsePubKey(testBlindedPK1Bytes)
|
||||||
|
testBlindedPK2Bytes, _ = hex.DecodeString("03a8c97ed5cd40d474e4ef18c8" +
|
||||||
|
"99854b25e5070106504cb225e6d2c112d61a805e")
|
||||||
|
testBlindedHopPK2, _ = btcec.ParsePubKey(testBlindedPK2Bytes)
|
||||||
|
testBlindedPK3Bytes, _ = hex.DecodeString("0220293926219d8efe733336e2" +
|
||||||
|
"b674570dd96aa763acb3564e6e367b384d861a0a")
|
||||||
|
testBlindedHopPK3, _ = btcec.ParsePubKey(testBlindedPK3Bytes)
|
||||||
|
testBlindedPK4Bytes, _ = hex.DecodeString("02c75eb336a038294eaaf76015" +
|
||||||
|
"8b2e851c3c0937262e35401ae64a1bee71a2e40c")
|
||||||
|
testBlindedHopPK4, _ = btcec.ParsePubKey(testBlindedPK4Bytes)
|
||||||
|
|
||||||
|
blindedPath1 = &BlindedPaymentPath{
|
||||||
|
FeeBaseMsat: 40,
|
||||||
|
FeeRate: 20,
|
||||||
|
CltvExpiryDelta: 130,
|
||||||
|
HTLCMinMsat: 2,
|
||||||
|
HTLCMaxMsat: 100,
|
||||||
|
Features: lnwire.EmptyFeatureVector(),
|
||||||
|
FirstEphemeralBlindingPoint: testBlindedHopPK1,
|
||||||
|
Hops: []*sphinx.BlindedHopInfo{
|
||||||
|
{
|
||||||
|
BlindedNodePub: testBlindedHopPK2,
|
||||||
|
CipherText: []byte{1, 2, 3, 4, 5},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
BlindedNodePub: testBlindedHopPK3,
|
||||||
|
CipherText: []byte{5, 4, 3, 2, 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
BlindedNodePub: testBlindedHopPK4,
|
||||||
|
CipherText: []byte{
|
||||||
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
|
||||||
|
11, 12, 13, 14,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
blindedPath2 = &BlindedPaymentPath{
|
||||||
|
FeeBaseMsat: 4,
|
||||||
|
FeeRate: 2,
|
||||||
|
CltvExpiryDelta: 10,
|
||||||
|
HTLCMinMsat: 0,
|
||||||
|
HTLCMaxMsat: 10,
|
||||||
|
Features: lnwire.EmptyFeatureVector(),
|
||||||
|
FirstEphemeralBlindingPoint: testBlindedHopPK4,
|
||||||
|
Hops: []*sphinx.BlindedHopInfo{
|
||||||
|
{
|
||||||
|
BlindedNodePub: testBlindedHopPK3,
|
||||||
|
CipherText: []byte{1, 2, 3, 4, 5},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -125,6 +182,8 @@ func init() {
|
|||||||
// TestDecodeEncode tests that an encoded invoice gets decoded into the expected
|
// TestDecodeEncode tests that an encoded invoice gets decoded into the expected
|
||||||
// Invoice object, and that reencoding the decoded invoice gets us back to the
|
// Invoice object, and that reencoding the decoded invoice gets us back to the
|
||||||
// original encoded string.
|
// original encoded string.
|
||||||
|
//
|
||||||
|
//nolint:lll
|
||||||
func TestDecodeEncode(t *testing.T) {
|
func TestDecodeEncode(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -673,52 +732,77 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
i.Destination = nil
|
i.Destination = nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// Invoice with blinded payment paths.
|
||||||
|
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4js5fdqqqqq2qqqqqpgqyzqqqqqqqqqqqqyqqqqqqqqqqqvsqqqqlnxy0ffrlt2y2jgtzw89kgr3zg4dlwtlfycn3yuek8x5eucnuchqps82xf0m2u6sx5wnjw7xxgnxz5kf09quqsv5zvkgj7d5kpzttp4qz7q5qsyqcyq5pzq2feycsemrh7wvendc4kw3tsmkt25a36ev6kfehrv7ecfkrp5zs9q5zqxqspqtr4avek5quzjn427asptzews5wrczfhychr2sq6ue9phmn35tjqcrspqgpsgpgxquyqjzstpsxsu59zqqqqqpqqqqqqyqq2qqqqqqqqqqqqqqqqqqqqqqqqpgqqqqk8t6endgpc99824amqzk9japgu8synwf3wx4qp4ej2r0h8rghypsqsygpf8ynzr8vwleenxdhzke69wrwed2nk8t9n2e8xudnm8pxcvxs2q5qsyqcyq5y4rdlhtf84f8rgdj34275juwls2ftxtcfh035863q3p9k6s94hpxhdmzfn5gxpsazdznxs56j4vt3fdhe00g9v2l3szher50hp4xlggqkxf77f",
|
||||||
|
valid: true,
|
||||||
|
decodedInvoice: func() *Invoice {
|
||||||
|
return &Invoice{
|
||||||
|
Net: &chaincfg.MainNetParams,
|
||||||
|
MilliSat: &testMillisat20mBTC,
|
||||||
|
Timestamp: time.Unix(1496314658, 0),
|
||||||
|
PaymentHash: &testPaymentHash,
|
||||||
|
Description: &testCupOfCoffee,
|
||||||
|
Destination: testPubKey,
|
||||||
|
Features: emptyFeatures,
|
||||||
|
BlindedPaymentPaths: []*BlindedPaymentPath{
|
||||||
|
blindedPath1,
|
||||||
|
blindedPath2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeEncoding: func(i *Invoice) {
|
||||||
|
// Since this destination pubkey was recovered
|
||||||
|
// from the signature, we must set it nil before
|
||||||
|
// encoding to get back the same invoice string.
|
||||||
|
i.Destination = nil
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
var decodedInvoice *Invoice
|
test := test
|
||||||
net := &chaincfg.MainNetParams
|
|
||||||
if test.decodedInvoice != nil {
|
|
||||||
decodedInvoice = test.decodedInvoice()
|
|
||||||
net = decodedInvoice.Net
|
|
||||||
}
|
|
||||||
|
|
||||||
invoice, err := Decode(test.encodedInvoice, net)
|
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||||
if (err == nil) != test.valid {
|
t.Parallel()
|
||||||
t.Errorf("Decoding test %d failed: %v", i, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if test.valid {
|
var decodedInvoice *Invoice
|
||||||
if err := compareInvoices(decodedInvoice, invoice); err != nil {
|
net := &chaincfg.MainNetParams
|
||||||
t.Errorf("Invoice decoding result %d not as expected: %v", i, err)
|
if test.decodedInvoice != nil {
|
||||||
|
decodedInvoice = test.decodedInvoice()
|
||||||
|
net = decodedInvoice.Net
|
||||||
|
}
|
||||||
|
|
||||||
|
invoice, err := Decode(test.encodedInvoice, net)
|
||||||
|
if !test.valid {
|
||||||
|
require.Error(t, err)
|
||||||
|
} else {
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, decodedInvoice, invoice)
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.skipEncoding {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if test.skipEncoding {
|
if test.beforeEncoding != nil {
|
||||||
continue
|
test.beforeEncoding(decodedInvoice)
|
||||||
}
|
}
|
||||||
|
|
||||||
if test.beforeEncoding != nil {
|
if decodedInvoice == nil {
|
||||||
test.beforeEncoding(decodedInvoice)
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if decodedInvoice != nil {
|
|
||||||
reencoded, err := decodedInvoice.Encode(
|
reencoded, err := decodedInvoice.Encode(
|
||||||
testMessageSigner,
|
testMessageSigner,
|
||||||
)
|
)
|
||||||
if (err == nil) != test.valid {
|
if !test.valid {
|
||||||
t.Errorf("Encoding test %d failed: %v", i, err)
|
require.Error(t, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
require.NoError(t, err)
|
||||||
if test.valid && test.encodedInvoice != reencoded {
|
require.Equal(t, test.encodedInvoice, reencoded)
|
||||||
t.Errorf("Encoding %d failed, expected %v, got %v",
|
})
|
||||||
i, test.encodedInvoice, reencoded)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -805,25 +889,42 @@ func TestNewInvoice(t *testing.T) {
|
|||||||
valid: true,
|
valid: true,
|
||||||
encodedInvoice: "lnbcrt241pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdqqnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66df5c8pqjjt4z4ymmuaxfx8eh5v7hmzs3wrfas8m2sz5qz56rw2lxy8mmgm4xln0ha26qkw6u3vhu22pss2udugr9g74c3x20slpcqjgq0el4h6",
|
encodedInvoice: "lnbcrt241pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdqqnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66df5c8pqjjt4z4ymmuaxfx8eh5v7hmzs3wrfas8m2sz5qz56rw2lxy8mmgm4xln0ha26qkw6u3vhu22pss2udugr9g74c3x20slpcqjgq0el4h6",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// Mainnet invoice with two blinded paths.
|
||||||
|
newInvoice: func() (*Invoice, error) {
|
||||||
|
return NewInvoice(&chaincfg.MainNetParams,
|
||||||
|
testPaymentHash,
|
||||||
|
time.Unix(1496314658, 0),
|
||||||
|
Amount(testMillisat20mBTC),
|
||||||
|
Description(testCupOfCoffee),
|
||||||
|
WithBlindedPaymentPath(blindedPath1),
|
||||||
|
WithBlindedPaymentPath(blindedPath2),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
valid: true,
|
||||||
|
//nolint:lll
|
||||||
|
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4js5fdqqqqq2qqqqqpgqyzqqqqqqqqqqqqyqqqqqqqqqqqvsqqqqlnxy0ffrlt2y2jgtzw89kgr3zg4dlwtlfycn3yuek8x5eucnuchqps82xf0m2u6sx5wnjw7xxgnxz5kf09quqsv5zvkgj7d5kpzttp4qz7q5qsyqcyq5pzq2feycsemrh7wvendc4kw3tsmkt25a36ev6kfehrv7ecfkrp5zs9q5zqxqspqtr4avek5quzjn427asptzews5wrczfhychr2sq6ue9phmn35tjqcrspqgpsgpgxquyqjzstpsxsu59zqqqqqpqqqqqqyqq2qqqqqqqqqqqqqqqqqqqqqqqqpgqqqqk8t6endgpc99824amqzk9japgu8synwf3wx4qp4ej2r0h8rghypsqsygpf8ynzr8vwleenxdhzke69wrwed2nk8t9n2e8xudnm8pxcvxs2q5qsyqcyq5y4rdlhtf84f8rgdj34275juwls2ftxtcfh035863q3p9k6s94hpxhdmzfn5gxpsazdznxs56j4vt3fdhe00g9v2l3szher50hp4xlggqkxf77f",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
|
test := test
|
||||||
|
|
||||||
invoice, err := test.newInvoice()
|
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||||
if err != nil && !test.valid {
|
t.Parallel()
|
||||||
continue
|
|
||||||
}
|
|
||||||
encoded, err := invoice.Encode(testMessageSigner)
|
|
||||||
if (err == nil) != test.valid {
|
|
||||||
t.Errorf("NewInvoice test %d failed: %v", i, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if test.valid && test.encodedInvoice != encoded {
|
invoice, err := test.newInvoice()
|
||||||
t.Errorf("Encoding %d failed, expected %v, got %v",
|
if !test.valid {
|
||||||
i, test.encodedInvoice, encoded)
|
require.Error(t, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
encoded, err := invoice.Encode(testMessageSigner)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, test.encodedInvoice, encoded)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -909,73 +1010,6 @@ func TestInvoiceChecksumMalleability(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func compareInvoices(expected, actual *Invoice) error {
|
|
||||||
if !reflect.DeepEqual(expected.Net, actual.Net) {
|
|
||||||
return fmt.Errorf("expected net %v, got %v",
|
|
||||||
expected.Net, actual.Net)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(expected.MilliSat, actual.MilliSat) {
|
|
||||||
return fmt.Errorf("expected milli sat %d, got %d",
|
|
||||||
*expected.MilliSat, *actual.MilliSat)
|
|
||||||
}
|
|
||||||
|
|
||||||
if expected.Timestamp != actual.Timestamp {
|
|
||||||
return fmt.Errorf("expected timestamp %v, got %v",
|
|
||||||
expected.Timestamp, actual.Timestamp)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !compareHashes(expected.PaymentHash, actual.PaymentHash) {
|
|
||||||
return fmt.Errorf("expected payment hash %x, got %x",
|
|
||||||
*expected.PaymentHash, *actual.PaymentHash)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(expected.Description, actual.Description) {
|
|
||||||
return fmt.Errorf("expected description \"%s\", got \"%s\"",
|
|
||||||
*expected.Description, *actual.Description)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !comparePubkeys(expected.Destination, actual.Destination) {
|
|
||||||
return fmt.Errorf("expected destination pubkey %x, got %x",
|
|
||||||
expected.Destination.SerializeCompressed(),
|
|
||||||
actual.Destination.SerializeCompressed())
|
|
||||||
}
|
|
||||||
|
|
||||||
if !compareHashes(expected.DescriptionHash, actual.DescriptionHash) {
|
|
||||||
return fmt.Errorf("expected description hash %x, got %x",
|
|
||||||
*expected.DescriptionHash, *actual.DescriptionHash)
|
|
||||||
}
|
|
||||||
|
|
||||||
if expected.Expiry() != actual.Expiry() {
|
|
||||||
return fmt.Errorf("expected expiry %d, got %d",
|
|
||||||
expected.Expiry(), actual.Expiry())
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(expected.FallbackAddr, actual.FallbackAddr) {
|
|
||||||
return fmt.Errorf("expected FallbackAddr %v, got %v",
|
|
||||||
expected.FallbackAddr, actual.FallbackAddr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(expected.RouteHints) != len(actual.RouteHints) {
|
|
||||||
return fmt.Errorf("expected %d RouteHints, got %d",
|
|
||||||
len(expected.RouteHints), len(actual.RouteHints))
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, routeHint := range expected.RouteHints {
|
|
||||||
err := compareRouteHints(routeHint, actual.RouteHints[i])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(expected.Features, actual.Features) {
|
|
||||||
return fmt.Errorf("expected features %v, got %v",
|
|
||||||
expected.Features, actual.Features)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func comparePubkeys(a, b *btcec.PublicKey) bool {
|
func comparePubkeys(a, b *btcec.PublicKey) bool {
|
||||||
if a == b {
|
if a == b {
|
||||||
return true
|
return true
|
||||||
|
|||||||
Reference in new issue
Block a user