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{
|
||||
Name: "final_cltv_delta",
|
||||
Usage: "(optional) number of blocks the last hop has to reveal " +
|
||||
"the preimage",
|
||||
Usage: "(optional) number of blocks the last hop has " +
|
||||
"to reveal the preimage. Note that this " +
|
||||
"should not be set in the case where the " +
|
||||
"path includes a blinded path since in " +
|
||||
"that case, the receiver will already have " +
|
||||
"accounted for this value in the " +
|
||||
"blinded_cltv value",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "use_mc",
|
||||
@@ -1238,6 +1243,13 @@ func parseBlindedPaymentParameters(ctx *cli.Context) (
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// If a blinded path has been provided, then the final_cltv_delta flag
|
||||
// should not be provided since this value will be ignored.
|
||||
if ctx.IsSet("final_cltv_delta") {
|
||||
return nil, fmt.Errorf("`final_cltv_delta` should not be " +
|
||||
"provided if a blinded path is provided")
|
||||
}
|
||||
|
||||
// If any one of our blinding related flags is set, we expect the
|
||||
// full set to be set and we'll error out accordingly.
|
||||
introNode, err := route.NewVertexFromStr(
|
||||
|
||||
@@ -89,6 +89,9 @@
|
||||
channel. We will still wait for the channel to have at least one confirmation
|
||||
and so the main change here is that we don't error out for such a case.
|
||||
|
||||
* [Groundwork](https://github.com/lightningnetwork/lnd/pull/8752) in preparation
|
||||
for implementing route blinding receives.
|
||||
|
||||
## Testing
|
||||
## Database
|
||||
|
||||
|
||||
@@ -360,6 +360,7 @@ func (b *BlindingKit) DecryptAndValidateFwdInfo(payload *Payload,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Validate the data in the blinded route against our incoming htlc's
|
||||
// information.
|
||||
if err := ValidateBlindedRouteData(
|
||||
@@ -368,9 +369,31 @@ func (b *BlindingKit) DecryptAndValidateFwdInfo(payload *Payload,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Exit early if this onion is for the exit hop of the route since
|
||||
// route blinding receives are not yet supported.
|
||||
if isFinalHop {
|
||||
return nil, fmt.Errorf("being the final hop in a blinded " +
|
||||
"path is not yet supported")
|
||||
}
|
||||
|
||||
// At this point, we know we are a forwarding node for this onion
|
||||
// and so we expect the relay info and next SCID fields to be set.
|
||||
relayInfo, err := routeData.RelayInfo.UnwrapOrErr(
|
||||
fmt.Errorf("relay info not set for non-final blinded hop"),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nextSCID, err := routeData.ShortChannelID.UnwrapOrErr(
|
||||
fmt.Errorf("next SCID not set for non-final blinded hop"),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fwdAmt, err := calculateForwardingAmount(
|
||||
b.IncomingAmount, routeData.RelayInfo.Val.BaseFee,
|
||||
routeData.RelayInfo.Val.FeeRate,
|
||||
b.IncomingAmount, relayInfo.Val.BaseFee, relayInfo.Val.FeeRate,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -400,10 +423,10 @@ func (b *BlindingKit) DecryptAndValidateFwdInfo(payload *Payload,
|
||||
}
|
||||
|
||||
return &ForwardingInfo{
|
||||
NextHop: routeData.ShortChannelID.Val,
|
||||
NextHop: nextSCID.Val,
|
||||
AmountToForward: fwdAmt,
|
||||
OutgoingCTLV: b.IncomingCltv - uint32(
|
||||
routeData.RelayInfo.Val.CltvExpiryDelta,
|
||||
relayInfo.Val.CltvExpiryDelta,
|
||||
),
|
||||
// Remap from blinding override type to blinding point type.
|
||||
NextBlinding: tlv.SomeRecordT(
|
||||
|
||||
@@ -186,7 +186,7 @@ func TestDecryptAndValidateFwdInfo(t *testing.T) {
|
||||
|
||||
// Encode valid blinding data that we'll fake decrypting for our test.
|
||||
maxCltv := 1000
|
||||
blindedData := record.NewBlindedRouteData(
|
||||
blindedData := record.NewNonFinalBlindedRouteData(
|
||||
lnwire.NewShortChanIDFromInt(1500), nil,
|
||||
record.PaymentRelayInfo{
|
||||
CltvExpiryDelta: 10,
|
||||
|
||||
@@ -646,7 +646,7 @@ func TestValidateBlindedRouteData(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "max cltv expired",
|
||||
data: record.NewBlindedRouteData(
|
||||
data: record.NewNonFinalBlindedRouteData(
|
||||
scid,
|
||||
nil,
|
||||
record.PaymentRelayInfo{},
|
||||
@@ -663,7 +663,7 @@ func TestValidateBlindedRouteData(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "zero max cltv",
|
||||
data: record.NewBlindedRouteData(
|
||||
data: record.NewNonFinalBlindedRouteData(
|
||||
scid,
|
||||
nil,
|
||||
record.PaymentRelayInfo{},
|
||||
@@ -682,7 +682,7 @@ func TestValidateBlindedRouteData(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "amount below minimum",
|
||||
data: record.NewBlindedRouteData(
|
||||
data: record.NewNonFinalBlindedRouteData(
|
||||
scid,
|
||||
nil,
|
||||
record.PaymentRelayInfo{},
|
||||
@@ -699,7 +699,7 @@ func TestValidateBlindedRouteData(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "valid, no features",
|
||||
data: record.NewBlindedRouteData(
|
||||
data: record.NewNonFinalBlindedRouteData(
|
||||
scid,
|
||||
nil,
|
||||
record.PaymentRelayInfo{},
|
||||
@@ -714,7 +714,7 @@ func TestValidateBlindedRouteData(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "unknown features",
|
||||
data: record.NewBlindedRouteData(
|
||||
data: record.NewNonFinalBlindedRouteData(
|
||||
scid,
|
||||
nil,
|
||||
record.PaymentRelayInfo{},
|
||||
@@ -738,7 +738,7 @@ func TestValidateBlindedRouteData(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "valid data",
|
||||
data: record.NewBlindedRouteData(
|
||||
data: record.NewNonFinalBlindedRouteData(
|
||||
scid,
|
||||
nil,
|
||||
record.PaymentRelayInfo{
|
||||
|
||||
@@ -676,7 +676,7 @@ func (b *blindedForwardTest) createBlindedRoute(hops []*forwardingEdge,
|
||||
|
||||
// Encode the route's blinded data and include it in the
|
||||
// blinded hop.
|
||||
payload := record.NewBlindedRouteData(
|
||||
payload := record.NewNonFinalBlindedRouteData(
|
||||
scid, nil, *relayInfo, constraints, nil,
|
||||
)
|
||||
payloadBytes, err := record.EncodeBlindedRouteData(payload)
|
||||
@@ -739,7 +739,7 @@ func (b *blindedForwardTest) createBlindedRoute(hops []*forwardingEdge,
|
||||
// node ID here so that it _looks like_ a valid
|
||||
// forwarding hop (though in reality it's the last
|
||||
// hop).
|
||||
record.NewBlindedRouteData(
|
||||
record.NewNonFinalBlindedRouteData(
|
||||
lnwire.NewShortChanIDFromInt(100), nil,
|
||||
record.PaymentRelayInfo{}, nil, nil,
|
||||
),
|
||||
|
||||
@@ -266,6 +266,60 @@ func CreateRPCRouteHints(routeHints [][]zpay32.HopHint) []*lnrpc.RouteHint {
|
||||
return res
|
||||
}
|
||||
|
||||
// CreateRPCBlindedPayments takes a set of zpay32.BlindedPaymentPath and
|
||||
// converts them into a set of lnrpc.BlindedPaymentPaths.
|
||||
func CreateRPCBlindedPayments(blindedPaths []*zpay32.BlindedPaymentPath) (
|
||||
[]*lnrpc.BlindedPaymentPath, error) {
|
||||
|
||||
var res []*lnrpc.BlindedPaymentPath
|
||||
for _, path := range blindedPaths {
|
||||
features := path.Features.Features()
|
||||
var featuresSlice []lnrpc.FeatureBit
|
||||
for feature := range features {
|
||||
featuresSlice = append(
|
||||
featuresSlice, lnrpc.FeatureBit(feature),
|
||||
)
|
||||
}
|
||||
|
||||
if len(path.Hops) == 0 {
|
||||
return nil, fmt.Errorf("each blinded path must " +
|
||||
"contain at least one hop")
|
||||
}
|
||||
|
||||
var hops []*lnrpc.BlindedHop
|
||||
for _, hop := range path.Hops {
|
||||
blindedNodeID := hop.BlindedNodePub.
|
||||
SerializeCompressed()
|
||||
hops = append(hops, &lnrpc.BlindedHop{
|
||||
BlindedNode: blindedNodeID,
|
||||
EncryptedData: hop.CipherText,
|
||||
})
|
||||
}
|
||||
|
||||
introNode := path.Hops[0].BlindedNodePub
|
||||
firstBlindingPoint := path.FirstEphemeralBlindingPoint
|
||||
|
||||
blindedPath := &lnrpc.BlindedPath{
|
||||
IntroductionNode: introNode.SerializeCompressed(),
|
||||
BlindingPoint: firstBlindingPoint.
|
||||
SerializeCompressed(),
|
||||
BlindedHops: hops,
|
||||
}
|
||||
|
||||
res = append(res, &lnrpc.BlindedPaymentPath{
|
||||
BlindedPath: blindedPath,
|
||||
BaseFeeMsat: uint64(path.FeeBaseMsat),
|
||||
ProportionalFeeRate: path.FeeRate,
|
||||
TotalCltvDelta: uint32(path.CltvExpiryDelta),
|
||||
HtlcMinMsat: path.HTLCMinMsat,
|
||||
HtlcMaxMsat: path.HTLCMaxMsat,
|
||||
Features: featuresSlice,
|
||||
})
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// CreateZpay32HopHints takes in the lnrpc form of route hints and converts them
|
||||
// into an invoice decoded form.
|
||||
func CreateZpay32HopHints(routeHints []*lnrpc.RouteHint) ([][]zpay32.HopHint, error) {
|
||||
|
||||
+214
-201
@@ -14304,19 +14304,20 @@ type PayReq struct {
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
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"`
|
||||
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"`
|
||||
Expiry int64 `protobuf:"varint,5,opt,name=expiry,proto3" json:"expiry,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"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
Expiry int64 `protobuf:"varint,5,opt,name=expiry,proto3" json:"expiry,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"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
BlindedPaths []*BlindedPaymentPath `protobuf:"bytes,14,rep,name=blinded_paths,json=blindedPaths,proto3" json:"blinded_paths,omitempty"`
|
||||
}
|
||||
|
||||
func (x *PayReq) Reset() {
|
||||
@@ -14442,6 +14443,13 @@ func (x *PayReq) GetFeatures() map[uint32]*Feature {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *PayReq) GetBlindedPaths() []*BlindedPaymentPath {
|
||||
if x != nil {
|
||||
return x.BlindedPaths
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Feature struct {
|
||||
state protoimpl.MessageState
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
@@ -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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
@@ -21520,192 +21532,193 @@ var file_lightning_proto_depIdxs = []int32{
|
||||
38, // 138: lnrpc.AbandonChannelRequest.channel_point:type_name -> lnrpc.ChannelPoint
|
||||
150, // 139: lnrpc.PayReq.route_hints:type_name -> lnrpc.RouteHint
|
||||
244, // 140: lnrpc.PayReq.features:type_name -> lnrpc.PayReq.FeaturesEntry
|
||||
179, // 141: lnrpc.FeeReportResponse.channel_fees:type_name -> lnrpc.ChannelFeeReport
|
||||
38, // 142: lnrpc.PolicyUpdateRequest.chan_point:type_name -> lnrpc.ChannelPoint
|
||||
181, // 143: lnrpc.PolicyUpdateRequest.inbound_fee:type_name -> lnrpc.InboundFee
|
||||
39, // 144: lnrpc.FailedUpdate.outpoint:type_name -> lnrpc.OutPoint
|
||||
11, // 145: lnrpc.FailedUpdate.reason:type_name -> lnrpc.UpdateFailure
|
||||
183, // 146: lnrpc.PolicyUpdateResponse.failed_updates:type_name -> lnrpc.FailedUpdate
|
||||
186, // 147: lnrpc.ForwardingHistoryResponse.forwarding_events:type_name -> lnrpc.ForwardingEvent
|
||||
38, // 148: lnrpc.ExportChannelBackupRequest.chan_point:type_name -> lnrpc.ChannelPoint
|
||||
38, // 149: lnrpc.ChannelBackup.chan_point:type_name -> lnrpc.ChannelPoint
|
||||
38, // 150: lnrpc.MultiChanBackup.chan_points:type_name -> lnrpc.ChannelPoint
|
||||
193, // 151: lnrpc.ChanBackupSnapshot.single_chan_backups:type_name -> lnrpc.ChannelBackups
|
||||
190, // 152: lnrpc.ChanBackupSnapshot.multi_chan_backup:type_name -> lnrpc.MultiChanBackup
|
||||
189, // 153: lnrpc.ChannelBackups.chan_backups:type_name -> lnrpc.ChannelBackup
|
||||
193, // 154: lnrpc.RestoreChanBackupRequest.chan_backups:type_name -> lnrpc.ChannelBackups
|
||||
198, // 155: lnrpc.BakeMacaroonRequest.permissions:type_name -> lnrpc.MacaroonPermission
|
||||
198, // 156: lnrpc.MacaroonPermissionList.permissions:type_name -> lnrpc.MacaroonPermission
|
||||
245, // 157: lnrpc.ListPermissionsResponse.method_permissions:type_name -> lnrpc.ListPermissionsResponse.MethodPermissionsEntry
|
||||
20, // 158: lnrpc.Failure.code:type_name -> lnrpc.Failure.FailureCode
|
||||
209, // 159: lnrpc.Failure.channel_update:type_name -> lnrpc.ChannelUpdate
|
||||
211, // 160: lnrpc.MacaroonId.ops:type_name -> lnrpc.Op
|
||||
198, // 161: lnrpc.CheckMacPermRequest.permissions:type_name -> lnrpc.MacaroonPermission
|
||||
215, // 162: lnrpc.RPCMiddlewareRequest.stream_auth:type_name -> lnrpc.StreamAuth
|
||||
216, // 163: lnrpc.RPCMiddlewareRequest.request:type_name -> lnrpc.RPCMessage
|
||||
216, // 164: lnrpc.RPCMiddlewareRequest.response:type_name -> lnrpc.RPCMessage
|
||||
218, // 165: lnrpc.RPCMiddlewareResponse.register:type_name -> lnrpc.MiddlewareRegistration
|
||||
219, // 166: lnrpc.RPCMiddlewareResponse.feedback:type_name -> lnrpc.InterceptFeedback
|
||||
177, // 167: lnrpc.Peer.FeaturesEntry.value:type_name -> lnrpc.Feature
|
||||
177, // 168: lnrpc.GetInfoResponse.FeaturesEntry.value:type_name -> lnrpc.Feature
|
||||
4, // 169: lnrpc.PendingChannelsResponse.PendingChannel.initiator:type_name -> lnrpc.Initiator
|
||||
3, // 170: lnrpc.PendingChannelsResponse.PendingChannel.commitment_type:type_name -> lnrpc.CommitmentType
|
||||
226, // 171: lnrpc.PendingChannelsResponse.PendingOpenChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel
|
||||
226, // 172: lnrpc.PendingChannelsResponse.WaitingCloseChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel
|
||||
229, // 173: lnrpc.PendingChannelsResponse.WaitingCloseChannel.commitments:type_name -> lnrpc.PendingChannelsResponse.Commitments
|
||||
226, // 174: lnrpc.PendingChannelsResponse.ClosedChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel
|
||||
226, // 175: lnrpc.PendingChannelsResponse.ForceClosedChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel
|
||||
108, // 176: lnrpc.PendingChannelsResponse.ForceClosedChannel.pending_htlcs:type_name -> lnrpc.PendingHTLC
|
||||
15, // 177: lnrpc.PendingChannelsResponse.ForceClosedChannel.anchor:type_name -> lnrpc.PendingChannelsResponse.ForceClosedChannel.AnchorState
|
||||
113, // 178: lnrpc.WalletBalanceResponse.AccountBalanceEntry.value:type_name -> lnrpc.WalletAccountBalance
|
||||
177, // 179: lnrpc.LightningNode.FeaturesEntry.value:type_name -> lnrpc.Feature
|
||||
137, // 180: lnrpc.NodeMetricsResponse.BetweennessCentralityEntry.value:type_name -> lnrpc.FloatMetric
|
||||
177, // 181: lnrpc.NodeUpdate.FeaturesEntry.value:type_name -> lnrpc.Feature
|
||||
177, // 182: lnrpc.Invoice.FeaturesEntry.value:type_name -> lnrpc.Feature
|
||||
154, // 183: lnrpc.Invoice.AmpInvoiceStateEntry.value:type_name -> lnrpc.AMPInvoiceState
|
||||
177, // 184: lnrpc.PayReq.FeaturesEntry.value:type_name -> lnrpc.Feature
|
||||
205, // 185: lnrpc.ListPermissionsResponse.MethodPermissionsEntry.value:type_name -> lnrpc.MacaroonPermissionList
|
||||
114, // 186: lnrpc.Lightning.WalletBalance:input_type -> lnrpc.WalletBalanceRequest
|
||||
117, // 187: lnrpc.Lightning.ChannelBalance:input_type -> lnrpc.ChannelBalanceRequest
|
||||
30, // 188: lnrpc.Lightning.GetTransactions:input_type -> lnrpc.GetTransactionsRequest
|
||||
42, // 189: lnrpc.Lightning.EstimateFee:input_type -> lnrpc.EstimateFeeRequest
|
||||
46, // 190: lnrpc.Lightning.SendCoins:input_type -> lnrpc.SendCoinsRequest
|
||||
48, // 191: lnrpc.Lightning.ListUnspent:input_type -> lnrpc.ListUnspentRequest
|
||||
30, // 192: lnrpc.Lightning.SubscribeTransactions:input_type -> lnrpc.GetTransactionsRequest
|
||||
44, // 193: lnrpc.Lightning.SendMany:input_type -> lnrpc.SendManyRequest
|
||||
50, // 194: lnrpc.Lightning.NewAddress:input_type -> lnrpc.NewAddressRequest
|
||||
52, // 195: lnrpc.Lightning.SignMessage:input_type -> lnrpc.SignMessageRequest
|
||||
54, // 196: lnrpc.Lightning.VerifyMessage:input_type -> lnrpc.VerifyMessageRequest
|
||||
56, // 197: lnrpc.Lightning.ConnectPeer:input_type -> lnrpc.ConnectPeerRequest
|
||||
58, // 198: lnrpc.Lightning.DisconnectPeer:input_type -> lnrpc.DisconnectPeerRequest
|
||||
74, // 199: lnrpc.Lightning.ListPeers:input_type -> lnrpc.ListPeersRequest
|
||||
76, // 200: lnrpc.Lightning.SubscribePeerEvents:input_type -> lnrpc.PeerEventSubscription
|
||||
78, // 201: lnrpc.Lightning.GetInfo:input_type -> lnrpc.GetInfoRequest
|
||||
80, // 202: lnrpc.Lightning.GetDebugInfo:input_type -> lnrpc.GetDebugInfoRequest
|
||||
82, // 203: lnrpc.Lightning.GetRecoveryInfo:input_type -> lnrpc.GetRecoveryInfoRequest
|
||||
109, // 204: lnrpc.Lightning.PendingChannels:input_type -> lnrpc.PendingChannelsRequest
|
||||
63, // 205: lnrpc.Lightning.ListChannels:input_type -> lnrpc.ListChannelsRequest
|
||||
111, // 206: lnrpc.Lightning.SubscribeChannelEvents:input_type -> lnrpc.ChannelEventSubscription
|
||||
70, // 207: lnrpc.Lightning.ClosedChannels:input_type -> lnrpc.ClosedChannelsRequest
|
||||
96, // 208: lnrpc.Lightning.OpenChannelSync:input_type -> lnrpc.OpenChannelRequest
|
||||
96, // 209: lnrpc.Lightning.OpenChannel:input_type -> lnrpc.OpenChannelRequest
|
||||
93, // 210: lnrpc.Lightning.BatchOpenChannel:input_type -> lnrpc.BatchOpenChannelRequest
|
||||
106, // 211: lnrpc.Lightning.FundingStateStep:input_type -> lnrpc.FundingTransitionMsg
|
||||
37, // 212: lnrpc.Lightning.ChannelAcceptor:input_type -> lnrpc.ChannelAcceptResponse
|
||||
88, // 213: lnrpc.Lightning.CloseChannel:input_type -> lnrpc.CloseChannelRequest
|
||||
171, // 214: lnrpc.Lightning.AbandonChannel:input_type -> lnrpc.AbandonChannelRequest
|
||||
33, // 215: lnrpc.Lightning.SendPayment:input_type -> lnrpc.SendRequest
|
||||
33, // 216: lnrpc.Lightning.SendPaymentSync:input_type -> lnrpc.SendRequest
|
||||
35, // 217: lnrpc.Lightning.SendToRoute:input_type -> lnrpc.SendToRouteRequest
|
||||
35, // 218: lnrpc.Lightning.SendToRouteSync:input_type -> lnrpc.SendToRouteRequest
|
||||
155, // 219: lnrpc.Lightning.AddInvoice:input_type -> lnrpc.Invoice
|
||||
160, // 220: lnrpc.Lightning.ListInvoices:input_type -> lnrpc.ListInvoiceRequest
|
||||
159, // 221: lnrpc.Lightning.LookupInvoice:input_type -> lnrpc.PaymentHash
|
||||
162, // 222: lnrpc.Lightning.SubscribeInvoices:input_type -> lnrpc.InvoiceSubscription
|
||||
175, // 223: lnrpc.Lightning.DecodePayReq:input_type -> lnrpc.PayReqString
|
||||
165, // 224: lnrpc.Lightning.ListPayments:input_type -> lnrpc.ListPaymentsRequest
|
||||
167, // 225: lnrpc.Lightning.DeletePayment:input_type -> lnrpc.DeletePaymentRequest
|
||||
168, // 226: lnrpc.Lightning.DeleteAllPayments:input_type -> lnrpc.DeleteAllPaymentsRequest
|
||||
133, // 227: lnrpc.Lightning.DescribeGraph:input_type -> lnrpc.ChannelGraphRequest
|
||||
135, // 228: lnrpc.Lightning.GetNodeMetrics:input_type -> lnrpc.NodeMetricsRequest
|
||||
138, // 229: lnrpc.Lightning.GetChanInfo:input_type -> lnrpc.ChanInfoRequest
|
||||
127, // 230: lnrpc.Lightning.GetNodeInfo:input_type -> lnrpc.NodeInfoRequest
|
||||
119, // 231: lnrpc.Lightning.QueryRoutes:input_type -> lnrpc.QueryRoutesRequest
|
||||
139, // 232: lnrpc.Lightning.GetNetworkInfo:input_type -> lnrpc.NetworkInfoRequest
|
||||
141, // 233: lnrpc.Lightning.StopDaemon:input_type -> lnrpc.StopRequest
|
||||
143, // 234: lnrpc.Lightning.SubscribeChannelGraph:input_type -> lnrpc.GraphTopologySubscription
|
||||
173, // 235: lnrpc.Lightning.DebugLevel:input_type -> lnrpc.DebugLevelRequest
|
||||
178, // 236: lnrpc.Lightning.FeeReport:input_type -> lnrpc.FeeReportRequest
|
||||
182, // 237: lnrpc.Lightning.UpdateChannelPolicy:input_type -> lnrpc.PolicyUpdateRequest
|
||||
185, // 238: lnrpc.Lightning.ForwardingHistory:input_type -> lnrpc.ForwardingHistoryRequest
|
||||
188, // 239: lnrpc.Lightning.ExportChannelBackup:input_type -> lnrpc.ExportChannelBackupRequest
|
||||
191, // 240: lnrpc.Lightning.ExportAllChannelBackups:input_type -> lnrpc.ChanBackupExportRequest
|
||||
192, // 241: lnrpc.Lightning.VerifyChanBackup:input_type -> lnrpc.ChanBackupSnapshot
|
||||
194, // 242: lnrpc.Lightning.RestoreChannelBackups:input_type -> lnrpc.RestoreChanBackupRequest
|
||||
196, // 243: lnrpc.Lightning.SubscribeChannelBackups:input_type -> lnrpc.ChannelBackupSubscription
|
||||
199, // 244: lnrpc.Lightning.BakeMacaroon:input_type -> lnrpc.BakeMacaroonRequest
|
||||
201, // 245: lnrpc.Lightning.ListMacaroonIDs:input_type -> lnrpc.ListMacaroonIDsRequest
|
||||
203, // 246: lnrpc.Lightning.DeleteMacaroonID:input_type -> lnrpc.DeleteMacaroonIDRequest
|
||||
206, // 247: lnrpc.Lightning.ListPermissions:input_type -> lnrpc.ListPermissionsRequest
|
||||
212, // 248: lnrpc.Lightning.CheckMacaroonPermissions:input_type -> lnrpc.CheckMacPermRequest
|
||||
217, // 249: lnrpc.Lightning.RegisterRPCMiddleware:input_type -> lnrpc.RPCMiddlewareResponse
|
||||
25, // 250: lnrpc.Lightning.SendCustomMessage:input_type -> lnrpc.SendCustomMessageRequest
|
||||
23, // 251: lnrpc.Lightning.SubscribeCustomMessages:input_type -> lnrpc.SubscribeCustomMessagesRequest
|
||||
66, // 252: lnrpc.Lightning.ListAliases:input_type -> lnrpc.ListAliasesRequest
|
||||
21, // 253: lnrpc.Lightning.LookupHtlcResolution:input_type -> lnrpc.LookupHtlcResolutionRequest
|
||||
115, // 254: lnrpc.Lightning.WalletBalance:output_type -> lnrpc.WalletBalanceResponse
|
||||
118, // 255: lnrpc.Lightning.ChannelBalance:output_type -> lnrpc.ChannelBalanceResponse
|
||||
31, // 256: lnrpc.Lightning.GetTransactions:output_type -> lnrpc.TransactionDetails
|
||||
43, // 257: lnrpc.Lightning.EstimateFee:output_type -> lnrpc.EstimateFeeResponse
|
||||
47, // 258: lnrpc.Lightning.SendCoins:output_type -> lnrpc.SendCoinsResponse
|
||||
49, // 259: lnrpc.Lightning.ListUnspent:output_type -> lnrpc.ListUnspentResponse
|
||||
29, // 260: lnrpc.Lightning.SubscribeTransactions:output_type -> lnrpc.Transaction
|
||||
45, // 261: lnrpc.Lightning.SendMany:output_type -> lnrpc.SendManyResponse
|
||||
51, // 262: lnrpc.Lightning.NewAddress:output_type -> lnrpc.NewAddressResponse
|
||||
53, // 263: lnrpc.Lightning.SignMessage:output_type -> lnrpc.SignMessageResponse
|
||||
55, // 264: lnrpc.Lightning.VerifyMessage:output_type -> lnrpc.VerifyMessageResponse
|
||||
57, // 265: lnrpc.Lightning.ConnectPeer:output_type -> lnrpc.ConnectPeerResponse
|
||||
59, // 266: lnrpc.Lightning.DisconnectPeer:output_type -> lnrpc.DisconnectPeerResponse
|
||||
75, // 267: lnrpc.Lightning.ListPeers:output_type -> lnrpc.ListPeersResponse
|
||||
77, // 268: lnrpc.Lightning.SubscribePeerEvents:output_type -> lnrpc.PeerEvent
|
||||
79, // 269: lnrpc.Lightning.GetInfo:output_type -> lnrpc.GetInfoResponse
|
||||
81, // 270: lnrpc.Lightning.GetDebugInfo:output_type -> lnrpc.GetDebugInfoResponse
|
||||
83, // 271: lnrpc.Lightning.GetRecoveryInfo:output_type -> lnrpc.GetRecoveryInfoResponse
|
||||
110, // 272: lnrpc.Lightning.PendingChannels:output_type -> lnrpc.PendingChannelsResponse
|
||||
64, // 273: lnrpc.Lightning.ListChannels:output_type -> lnrpc.ListChannelsResponse
|
||||
112, // 274: lnrpc.Lightning.SubscribeChannelEvents:output_type -> lnrpc.ChannelEventUpdate
|
||||
71, // 275: lnrpc.Lightning.ClosedChannels:output_type -> lnrpc.ClosedChannelsResponse
|
||||
38, // 276: lnrpc.Lightning.OpenChannelSync:output_type -> lnrpc.ChannelPoint
|
||||
97, // 277: lnrpc.Lightning.OpenChannel:output_type -> lnrpc.OpenStatusUpdate
|
||||
95, // 278: lnrpc.Lightning.BatchOpenChannel:output_type -> lnrpc.BatchOpenChannelResponse
|
||||
107, // 279: lnrpc.Lightning.FundingStateStep:output_type -> lnrpc.FundingStateStepResp
|
||||
36, // 280: lnrpc.Lightning.ChannelAcceptor:output_type -> lnrpc.ChannelAcceptRequest
|
||||
89, // 281: lnrpc.Lightning.CloseChannel:output_type -> lnrpc.CloseStatusUpdate
|
||||
172, // 282: lnrpc.Lightning.AbandonChannel:output_type -> lnrpc.AbandonChannelResponse
|
||||
34, // 283: lnrpc.Lightning.SendPayment:output_type -> lnrpc.SendResponse
|
||||
34, // 284: lnrpc.Lightning.SendPaymentSync:output_type -> lnrpc.SendResponse
|
||||
34, // 285: lnrpc.Lightning.SendToRoute:output_type -> lnrpc.SendResponse
|
||||
34, // 286: lnrpc.Lightning.SendToRouteSync:output_type -> lnrpc.SendResponse
|
||||
158, // 287: lnrpc.Lightning.AddInvoice:output_type -> lnrpc.AddInvoiceResponse
|
||||
161, // 288: lnrpc.Lightning.ListInvoices:output_type -> lnrpc.ListInvoiceResponse
|
||||
155, // 289: lnrpc.Lightning.LookupInvoice:output_type -> lnrpc.Invoice
|
||||
155, // 290: lnrpc.Lightning.SubscribeInvoices:output_type -> lnrpc.Invoice
|
||||
176, // 291: lnrpc.Lightning.DecodePayReq:output_type -> lnrpc.PayReq
|
||||
166, // 292: lnrpc.Lightning.ListPayments:output_type -> lnrpc.ListPaymentsResponse
|
||||
169, // 293: lnrpc.Lightning.DeletePayment:output_type -> lnrpc.DeletePaymentResponse
|
||||
170, // 294: lnrpc.Lightning.DeleteAllPayments:output_type -> lnrpc.DeleteAllPaymentsResponse
|
||||
134, // 295: lnrpc.Lightning.DescribeGraph:output_type -> lnrpc.ChannelGraph
|
||||
136, // 296: lnrpc.Lightning.GetNodeMetrics:output_type -> lnrpc.NodeMetricsResponse
|
||||
132, // 297: lnrpc.Lightning.GetChanInfo:output_type -> lnrpc.ChannelEdge
|
||||
128, // 298: lnrpc.Lightning.GetNodeInfo:output_type -> lnrpc.NodeInfo
|
||||
122, // 299: lnrpc.Lightning.QueryRoutes:output_type -> lnrpc.QueryRoutesResponse
|
||||
140, // 300: lnrpc.Lightning.GetNetworkInfo:output_type -> lnrpc.NetworkInfo
|
||||
142, // 301: lnrpc.Lightning.StopDaemon:output_type -> lnrpc.StopResponse
|
||||
144, // 302: lnrpc.Lightning.SubscribeChannelGraph:output_type -> lnrpc.GraphTopologyUpdate
|
||||
174, // 303: lnrpc.Lightning.DebugLevel:output_type -> lnrpc.DebugLevelResponse
|
||||
180, // 304: lnrpc.Lightning.FeeReport:output_type -> lnrpc.FeeReportResponse
|
||||
184, // 305: lnrpc.Lightning.UpdateChannelPolicy:output_type -> lnrpc.PolicyUpdateResponse
|
||||
187, // 306: lnrpc.Lightning.ForwardingHistory:output_type -> lnrpc.ForwardingHistoryResponse
|
||||
189, // 307: lnrpc.Lightning.ExportChannelBackup:output_type -> lnrpc.ChannelBackup
|
||||
192, // 308: lnrpc.Lightning.ExportAllChannelBackups:output_type -> lnrpc.ChanBackupSnapshot
|
||||
197, // 309: lnrpc.Lightning.VerifyChanBackup:output_type -> lnrpc.VerifyChanBackupResponse
|
||||
195, // 310: lnrpc.Lightning.RestoreChannelBackups:output_type -> lnrpc.RestoreBackupResponse
|
||||
192, // 311: lnrpc.Lightning.SubscribeChannelBackups:output_type -> lnrpc.ChanBackupSnapshot
|
||||
200, // 312: lnrpc.Lightning.BakeMacaroon:output_type -> lnrpc.BakeMacaroonResponse
|
||||
202, // 313: lnrpc.Lightning.ListMacaroonIDs:output_type -> lnrpc.ListMacaroonIDsResponse
|
||||
204, // 314: lnrpc.Lightning.DeleteMacaroonID:output_type -> lnrpc.DeleteMacaroonIDResponse
|
||||
207, // 315: lnrpc.Lightning.ListPermissions:output_type -> lnrpc.ListPermissionsResponse
|
||||
213, // 316: lnrpc.Lightning.CheckMacaroonPermissions:output_type -> lnrpc.CheckMacPermResponse
|
||||
214, // 317: lnrpc.Lightning.RegisterRPCMiddleware:output_type -> lnrpc.RPCMiddlewareRequest
|
||||
26, // 318: lnrpc.Lightning.SendCustomMessage:output_type -> lnrpc.SendCustomMessageResponse
|
||||
24, // 319: lnrpc.Lightning.SubscribeCustomMessages:output_type -> lnrpc.CustomMessage
|
||||
67, // 320: lnrpc.Lightning.ListAliases:output_type -> lnrpc.ListAliasesResponse
|
||||
22, // 321: lnrpc.Lightning.LookupHtlcResolution:output_type -> lnrpc.LookupHtlcResolutionResponse
|
||||
254, // [254:322] is the sub-list for method output_type
|
||||
186, // [186:254] is the sub-list for method input_type
|
||||
186, // [186:186] is the sub-list for extension type_name
|
||||
186, // [186:186] is the sub-list for extension extendee
|
||||
0, // [0:186] is the sub-list for field type_name
|
||||
151, // 141: lnrpc.PayReq.blinded_paths:type_name -> lnrpc.BlindedPaymentPath
|
||||
179, // 142: lnrpc.FeeReportResponse.channel_fees:type_name -> lnrpc.ChannelFeeReport
|
||||
38, // 143: lnrpc.PolicyUpdateRequest.chan_point:type_name -> lnrpc.ChannelPoint
|
||||
181, // 144: lnrpc.PolicyUpdateRequest.inbound_fee:type_name -> lnrpc.InboundFee
|
||||
39, // 145: lnrpc.FailedUpdate.outpoint:type_name -> lnrpc.OutPoint
|
||||
11, // 146: lnrpc.FailedUpdate.reason:type_name -> lnrpc.UpdateFailure
|
||||
183, // 147: lnrpc.PolicyUpdateResponse.failed_updates:type_name -> lnrpc.FailedUpdate
|
||||
186, // 148: lnrpc.ForwardingHistoryResponse.forwarding_events:type_name -> lnrpc.ForwardingEvent
|
||||
38, // 149: lnrpc.ExportChannelBackupRequest.chan_point:type_name -> lnrpc.ChannelPoint
|
||||
38, // 150: lnrpc.ChannelBackup.chan_point:type_name -> lnrpc.ChannelPoint
|
||||
38, // 151: lnrpc.MultiChanBackup.chan_points:type_name -> lnrpc.ChannelPoint
|
||||
193, // 152: lnrpc.ChanBackupSnapshot.single_chan_backups:type_name -> lnrpc.ChannelBackups
|
||||
190, // 153: lnrpc.ChanBackupSnapshot.multi_chan_backup:type_name -> lnrpc.MultiChanBackup
|
||||
189, // 154: lnrpc.ChannelBackups.chan_backups:type_name -> lnrpc.ChannelBackup
|
||||
193, // 155: lnrpc.RestoreChanBackupRequest.chan_backups:type_name -> lnrpc.ChannelBackups
|
||||
198, // 156: lnrpc.BakeMacaroonRequest.permissions:type_name -> lnrpc.MacaroonPermission
|
||||
198, // 157: lnrpc.MacaroonPermissionList.permissions:type_name -> lnrpc.MacaroonPermission
|
||||
245, // 158: lnrpc.ListPermissionsResponse.method_permissions:type_name -> lnrpc.ListPermissionsResponse.MethodPermissionsEntry
|
||||
20, // 159: lnrpc.Failure.code:type_name -> lnrpc.Failure.FailureCode
|
||||
209, // 160: lnrpc.Failure.channel_update:type_name -> lnrpc.ChannelUpdate
|
||||
211, // 161: lnrpc.MacaroonId.ops:type_name -> lnrpc.Op
|
||||
198, // 162: lnrpc.CheckMacPermRequest.permissions:type_name -> lnrpc.MacaroonPermission
|
||||
215, // 163: lnrpc.RPCMiddlewareRequest.stream_auth:type_name -> lnrpc.StreamAuth
|
||||
216, // 164: lnrpc.RPCMiddlewareRequest.request:type_name -> lnrpc.RPCMessage
|
||||
216, // 165: lnrpc.RPCMiddlewareRequest.response:type_name -> lnrpc.RPCMessage
|
||||
218, // 166: lnrpc.RPCMiddlewareResponse.register:type_name -> lnrpc.MiddlewareRegistration
|
||||
219, // 167: lnrpc.RPCMiddlewareResponse.feedback:type_name -> lnrpc.InterceptFeedback
|
||||
177, // 168: lnrpc.Peer.FeaturesEntry.value:type_name -> lnrpc.Feature
|
||||
177, // 169: lnrpc.GetInfoResponse.FeaturesEntry.value:type_name -> lnrpc.Feature
|
||||
4, // 170: lnrpc.PendingChannelsResponse.PendingChannel.initiator:type_name -> lnrpc.Initiator
|
||||
3, // 171: lnrpc.PendingChannelsResponse.PendingChannel.commitment_type:type_name -> lnrpc.CommitmentType
|
||||
226, // 172: lnrpc.PendingChannelsResponse.PendingOpenChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel
|
||||
226, // 173: lnrpc.PendingChannelsResponse.WaitingCloseChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel
|
||||
229, // 174: lnrpc.PendingChannelsResponse.WaitingCloseChannel.commitments:type_name -> lnrpc.PendingChannelsResponse.Commitments
|
||||
226, // 175: lnrpc.PendingChannelsResponse.ClosedChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel
|
||||
226, // 176: lnrpc.PendingChannelsResponse.ForceClosedChannel.channel:type_name -> lnrpc.PendingChannelsResponse.PendingChannel
|
||||
108, // 177: lnrpc.PendingChannelsResponse.ForceClosedChannel.pending_htlcs:type_name -> lnrpc.PendingHTLC
|
||||
15, // 178: lnrpc.PendingChannelsResponse.ForceClosedChannel.anchor:type_name -> lnrpc.PendingChannelsResponse.ForceClosedChannel.AnchorState
|
||||
113, // 179: lnrpc.WalletBalanceResponse.AccountBalanceEntry.value:type_name -> lnrpc.WalletAccountBalance
|
||||
177, // 180: lnrpc.LightningNode.FeaturesEntry.value:type_name -> lnrpc.Feature
|
||||
137, // 181: lnrpc.NodeMetricsResponse.BetweennessCentralityEntry.value:type_name -> lnrpc.FloatMetric
|
||||
177, // 182: lnrpc.NodeUpdate.FeaturesEntry.value:type_name -> lnrpc.Feature
|
||||
177, // 183: lnrpc.Invoice.FeaturesEntry.value:type_name -> lnrpc.Feature
|
||||
154, // 184: lnrpc.Invoice.AmpInvoiceStateEntry.value:type_name -> lnrpc.AMPInvoiceState
|
||||
177, // 185: lnrpc.PayReq.FeaturesEntry.value:type_name -> lnrpc.Feature
|
||||
205, // 186: lnrpc.ListPermissionsResponse.MethodPermissionsEntry.value:type_name -> lnrpc.MacaroonPermissionList
|
||||
114, // 187: lnrpc.Lightning.WalletBalance:input_type -> lnrpc.WalletBalanceRequest
|
||||
117, // 188: lnrpc.Lightning.ChannelBalance:input_type -> lnrpc.ChannelBalanceRequest
|
||||
30, // 189: lnrpc.Lightning.GetTransactions:input_type -> lnrpc.GetTransactionsRequest
|
||||
42, // 190: lnrpc.Lightning.EstimateFee:input_type -> lnrpc.EstimateFeeRequest
|
||||
46, // 191: lnrpc.Lightning.SendCoins:input_type -> lnrpc.SendCoinsRequest
|
||||
48, // 192: lnrpc.Lightning.ListUnspent:input_type -> lnrpc.ListUnspentRequest
|
||||
30, // 193: lnrpc.Lightning.SubscribeTransactions:input_type -> lnrpc.GetTransactionsRequest
|
||||
44, // 194: lnrpc.Lightning.SendMany:input_type -> lnrpc.SendManyRequest
|
||||
50, // 195: lnrpc.Lightning.NewAddress:input_type -> lnrpc.NewAddressRequest
|
||||
52, // 196: lnrpc.Lightning.SignMessage:input_type -> lnrpc.SignMessageRequest
|
||||
54, // 197: lnrpc.Lightning.VerifyMessage:input_type -> lnrpc.VerifyMessageRequest
|
||||
56, // 198: lnrpc.Lightning.ConnectPeer:input_type -> lnrpc.ConnectPeerRequest
|
||||
58, // 199: lnrpc.Lightning.DisconnectPeer:input_type -> lnrpc.DisconnectPeerRequest
|
||||
74, // 200: lnrpc.Lightning.ListPeers:input_type -> lnrpc.ListPeersRequest
|
||||
76, // 201: lnrpc.Lightning.SubscribePeerEvents:input_type -> lnrpc.PeerEventSubscription
|
||||
78, // 202: lnrpc.Lightning.GetInfo:input_type -> lnrpc.GetInfoRequest
|
||||
80, // 203: lnrpc.Lightning.GetDebugInfo:input_type -> lnrpc.GetDebugInfoRequest
|
||||
82, // 204: lnrpc.Lightning.GetRecoveryInfo:input_type -> lnrpc.GetRecoveryInfoRequest
|
||||
109, // 205: lnrpc.Lightning.PendingChannels:input_type -> lnrpc.PendingChannelsRequest
|
||||
63, // 206: lnrpc.Lightning.ListChannels:input_type -> lnrpc.ListChannelsRequest
|
||||
111, // 207: lnrpc.Lightning.SubscribeChannelEvents:input_type -> lnrpc.ChannelEventSubscription
|
||||
70, // 208: lnrpc.Lightning.ClosedChannels:input_type -> lnrpc.ClosedChannelsRequest
|
||||
96, // 209: lnrpc.Lightning.OpenChannelSync:input_type -> lnrpc.OpenChannelRequest
|
||||
96, // 210: lnrpc.Lightning.OpenChannel:input_type -> lnrpc.OpenChannelRequest
|
||||
93, // 211: lnrpc.Lightning.BatchOpenChannel:input_type -> lnrpc.BatchOpenChannelRequest
|
||||
106, // 212: lnrpc.Lightning.FundingStateStep:input_type -> lnrpc.FundingTransitionMsg
|
||||
37, // 213: lnrpc.Lightning.ChannelAcceptor:input_type -> lnrpc.ChannelAcceptResponse
|
||||
88, // 214: lnrpc.Lightning.CloseChannel:input_type -> lnrpc.CloseChannelRequest
|
||||
171, // 215: lnrpc.Lightning.AbandonChannel:input_type -> lnrpc.AbandonChannelRequest
|
||||
33, // 216: lnrpc.Lightning.SendPayment:input_type -> lnrpc.SendRequest
|
||||
33, // 217: lnrpc.Lightning.SendPaymentSync:input_type -> lnrpc.SendRequest
|
||||
35, // 218: lnrpc.Lightning.SendToRoute:input_type -> lnrpc.SendToRouteRequest
|
||||
35, // 219: lnrpc.Lightning.SendToRouteSync:input_type -> lnrpc.SendToRouteRequest
|
||||
155, // 220: lnrpc.Lightning.AddInvoice:input_type -> lnrpc.Invoice
|
||||
160, // 221: lnrpc.Lightning.ListInvoices:input_type -> lnrpc.ListInvoiceRequest
|
||||
159, // 222: lnrpc.Lightning.LookupInvoice:input_type -> lnrpc.PaymentHash
|
||||
162, // 223: lnrpc.Lightning.SubscribeInvoices:input_type -> lnrpc.InvoiceSubscription
|
||||
175, // 224: lnrpc.Lightning.DecodePayReq:input_type -> lnrpc.PayReqString
|
||||
165, // 225: lnrpc.Lightning.ListPayments:input_type -> lnrpc.ListPaymentsRequest
|
||||
167, // 226: lnrpc.Lightning.DeletePayment:input_type -> lnrpc.DeletePaymentRequest
|
||||
168, // 227: lnrpc.Lightning.DeleteAllPayments:input_type -> lnrpc.DeleteAllPaymentsRequest
|
||||
133, // 228: lnrpc.Lightning.DescribeGraph:input_type -> lnrpc.ChannelGraphRequest
|
||||
135, // 229: lnrpc.Lightning.GetNodeMetrics:input_type -> lnrpc.NodeMetricsRequest
|
||||
138, // 230: lnrpc.Lightning.GetChanInfo:input_type -> lnrpc.ChanInfoRequest
|
||||
127, // 231: lnrpc.Lightning.GetNodeInfo:input_type -> lnrpc.NodeInfoRequest
|
||||
119, // 232: lnrpc.Lightning.QueryRoutes:input_type -> lnrpc.QueryRoutesRequest
|
||||
139, // 233: lnrpc.Lightning.GetNetworkInfo:input_type -> lnrpc.NetworkInfoRequest
|
||||
141, // 234: lnrpc.Lightning.StopDaemon:input_type -> lnrpc.StopRequest
|
||||
143, // 235: lnrpc.Lightning.SubscribeChannelGraph:input_type -> lnrpc.GraphTopologySubscription
|
||||
173, // 236: lnrpc.Lightning.DebugLevel:input_type -> lnrpc.DebugLevelRequest
|
||||
178, // 237: lnrpc.Lightning.FeeReport:input_type -> lnrpc.FeeReportRequest
|
||||
182, // 238: lnrpc.Lightning.UpdateChannelPolicy:input_type -> lnrpc.PolicyUpdateRequest
|
||||
185, // 239: lnrpc.Lightning.ForwardingHistory:input_type -> lnrpc.ForwardingHistoryRequest
|
||||
188, // 240: lnrpc.Lightning.ExportChannelBackup:input_type -> lnrpc.ExportChannelBackupRequest
|
||||
191, // 241: lnrpc.Lightning.ExportAllChannelBackups:input_type -> lnrpc.ChanBackupExportRequest
|
||||
192, // 242: lnrpc.Lightning.VerifyChanBackup:input_type -> lnrpc.ChanBackupSnapshot
|
||||
194, // 243: lnrpc.Lightning.RestoreChannelBackups:input_type -> lnrpc.RestoreChanBackupRequest
|
||||
196, // 244: lnrpc.Lightning.SubscribeChannelBackups:input_type -> lnrpc.ChannelBackupSubscription
|
||||
199, // 245: lnrpc.Lightning.BakeMacaroon:input_type -> lnrpc.BakeMacaroonRequest
|
||||
201, // 246: lnrpc.Lightning.ListMacaroonIDs:input_type -> lnrpc.ListMacaroonIDsRequest
|
||||
203, // 247: lnrpc.Lightning.DeleteMacaroonID:input_type -> lnrpc.DeleteMacaroonIDRequest
|
||||
206, // 248: lnrpc.Lightning.ListPermissions:input_type -> lnrpc.ListPermissionsRequest
|
||||
212, // 249: lnrpc.Lightning.CheckMacaroonPermissions:input_type -> lnrpc.CheckMacPermRequest
|
||||
217, // 250: lnrpc.Lightning.RegisterRPCMiddleware:input_type -> lnrpc.RPCMiddlewareResponse
|
||||
25, // 251: lnrpc.Lightning.SendCustomMessage:input_type -> lnrpc.SendCustomMessageRequest
|
||||
23, // 252: lnrpc.Lightning.SubscribeCustomMessages:input_type -> lnrpc.SubscribeCustomMessagesRequest
|
||||
66, // 253: lnrpc.Lightning.ListAliases:input_type -> lnrpc.ListAliasesRequest
|
||||
21, // 254: lnrpc.Lightning.LookupHtlcResolution:input_type -> lnrpc.LookupHtlcResolutionRequest
|
||||
115, // 255: lnrpc.Lightning.WalletBalance:output_type -> lnrpc.WalletBalanceResponse
|
||||
118, // 256: lnrpc.Lightning.ChannelBalance:output_type -> lnrpc.ChannelBalanceResponse
|
||||
31, // 257: lnrpc.Lightning.GetTransactions:output_type -> lnrpc.TransactionDetails
|
||||
43, // 258: lnrpc.Lightning.EstimateFee:output_type -> lnrpc.EstimateFeeResponse
|
||||
47, // 259: lnrpc.Lightning.SendCoins:output_type -> lnrpc.SendCoinsResponse
|
||||
49, // 260: lnrpc.Lightning.ListUnspent:output_type -> lnrpc.ListUnspentResponse
|
||||
29, // 261: lnrpc.Lightning.SubscribeTransactions:output_type -> lnrpc.Transaction
|
||||
45, // 262: lnrpc.Lightning.SendMany:output_type -> lnrpc.SendManyResponse
|
||||
51, // 263: lnrpc.Lightning.NewAddress:output_type -> lnrpc.NewAddressResponse
|
||||
53, // 264: lnrpc.Lightning.SignMessage:output_type -> lnrpc.SignMessageResponse
|
||||
55, // 265: lnrpc.Lightning.VerifyMessage:output_type -> lnrpc.VerifyMessageResponse
|
||||
57, // 266: lnrpc.Lightning.ConnectPeer:output_type -> lnrpc.ConnectPeerResponse
|
||||
59, // 267: lnrpc.Lightning.DisconnectPeer:output_type -> lnrpc.DisconnectPeerResponse
|
||||
75, // 268: lnrpc.Lightning.ListPeers:output_type -> lnrpc.ListPeersResponse
|
||||
77, // 269: lnrpc.Lightning.SubscribePeerEvents:output_type -> lnrpc.PeerEvent
|
||||
79, // 270: lnrpc.Lightning.GetInfo:output_type -> lnrpc.GetInfoResponse
|
||||
81, // 271: lnrpc.Lightning.GetDebugInfo:output_type -> lnrpc.GetDebugInfoResponse
|
||||
83, // 272: lnrpc.Lightning.GetRecoveryInfo:output_type -> lnrpc.GetRecoveryInfoResponse
|
||||
110, // 273: lnrpc.Lightning.PendingChannels:output_type -> lnrpc.PendingChannelsResponse
|
||||
64, // 274: lnrpc.Lightning.ListChannels:output_type -> lnrpc.ListChannelsResponse
|
||||
112, // 275: lnrpc.Lightning.SubscribeChannelEvents:output_type -> lnrpc.ChannelEventUpdate
|
||||
71, // 276: lnrpc.Lightning.ClosedChannels:output_type -> lnrpc.ClosedChannelsResponse
|
||||
38, // 277: lnrpc.Lightning.OpenChannelSync:output_type -> lnrpc.ChannelPoint
|
||||
97, // 278: lnrpc.Lightning.OpenChannel:output_type -> lnrpc.OpenStatusUpdate
|
||||
95, // 279: lnrpc.Lightning.BatchOpenChannel:output_type -> lnrpc.BatchOpenChannelResponse
|
||||
107, // 280: lnrpc.Lightning.FundingStateStep:output_type -> lnrpc.FundingStateStepResp
|
||||
36, // 281: lnrpc.Lightning.ChannelAcceptor:output_type -> lnrpc.ChannelAcceptRequest
|
||||
89, // 282: lnrpc.Lightning.CloseChannel:output_type -> lnrpc.CloseStatusUpdate
|
||||
172, // 283: lnrpc.Lightning.AbandonChannel:output_type -> lnrpc.AbandonChannelResponse
|
||||
34, // 284: lnrpc.Lightning.SendPayment:output_type -> lnrpc.SendResponse
|
||||
34, // 285: lnrpc.Lightning.SendPaymentSync:output_type -> lnrpc.SendResponse
|
||||
34, // 286: lnrpc.Lightning.SendToRoute:output_type -> lnrpc.SendResponse
|
||||
34, // 287: lnrpc.Lightning.SendToRouteSync:output_type -> lnrpc.SendResponse
|
||||
158, // 288: lnrpc.Lightning.AddInvoice:output_type -> lnrpc.AddInvoiceResponse
|
||||
161, // 289: lnrpc.Lightning.ListInvoices:output_type -> lnrpc.ListInvoiceResponse
|
||||
155, // 290: lnrpc.Lightning.LookupInvoice:output_type -> lnrpc.Invoice
|
||||
155, // 291: lnrpc.Lightning.SubscribeInvoices:output_type -> lnrpc.Invoice
|
||||
176, // 292: lnrpc.Lightning.DecodePayReq:output_type -> lnrpc.PayReq
|
||||
166, // 293: lnrpc.Lightning.ListPayments:output_type -> lnrpc.ListPaymentsResponse
|
||||
169, // 294: lnrpc.Lightning.DeletePayment:output_type -> lnrpc.DeletePaymentResponse
|
||||
170, // 295: lnrpc.Lightning.DeleteAllPayments:output_type -> lnrpc.DeleteAllPaymentsResponse
|
||||
134, // 296: lnrpc.Lightning.DescribeGraph:output_type -> lnrpc.ChannelGraph
|
||||
136, // 297: lnrpc.Lightning.GetNodeMetrics:output_type -> lnrpc.NodeMetricsResponse
|
||||
132, // 298: lnrpc.Lightning.GetChanInfo:output_type -> lnrpc.ChannelEdge
|
||||
128, // 299: lnrpc.Lightning.GetNodeInfo:output_type -> lnrpc.NodeInfo
|
||||
122, // 300: lnrpc.Lightning.QueryRoutes:output_type -> lnrpc.QueryRoutesResponse
|
||||
140, // 301: lnrpc.Lightning.GetNetworkInfo:output_type -> lnrpc.NetworkInfo
|
||||
142, // 302: lnrpc.Lightning.StopDaemon:output_type -> lnrpc.StopResponse
|
||||
144, // 303: lnrpc.Lightning.SubscribeChannelGraph:output_type -> lnrpc.GraphTopologyUpdate
|
||||
174, // 304: lnrpc.Lightning.DebugLevel:output_type -> lnrpc.DebugLevelResponse
|
||||
180, // 305: lnrpc.Lightning.FeeReport:output_type -> lnrpc.FeeReportResponse
|
||||
184, // 306: lnrpc.Lightning.UpdateChannelPolicy:output_type -> lnrpc.PolicyUpdateResponse
|
||||
187, // 307: lnrpc.Lightning.ForwardingHistory:output_type -> lnrpc.ForwardingHistoryResponse
|
||||
189, // 308: lnrpc.Lightning.ExportChannelBackup:output_type -> lnrpc.ChannelBackup
|
||||
192, // 309: lnrpc.Lightning.ExportAllChannelBackups:output_type -> lnrpc.ChanBackupSnapshot
|
||||
197, // 310: lnrpc.Lightning.VerifyChanBackup:output_type -> lnrpc.VerifyChanBackupResponse
|
||||
195, // 311: lnrpc.Lightning.RestoreChannelBackups:output_type -> lnrpc.RestoreBackupResponse
|
||||
192, // 312: lnrpc.Lightning.SubscribeChannelBackups:output_type -> lnrpc.ChanBackupSnapshot
|
||||
200, // 313: lnrpc.Lightning.BakeMacaroon:output_type -> lnrpc.BakeMacaroonResponse
|
||||
202, // 314: lnrpc.Lightning.ListMacaroonIDs:output_type -> lnrpc.ListMacaroonIDsResponse
|
||||
204, // 315: lnrpc.Lightning.DeleteMacaroonID:output_type -> lnrpc.DeleteMacaroonIDResponse
|
||||
207, // 316: lnrpc.Lightning.ListPermissions:output_type -> lnrpc.ListPermissionsResponse
|
||||
213, // 317: lnrpc.Lightning.CheckMacaroonPermissions:output_type -> lnrpc.CheckMacPermResponse
|
||||
214, // 318: lnrpc.Lightning.RegisterRPCMiddleware:output_type -> lnrpc.RPCMiddlewareRequest
|
||||
26, // 319: lnrpc.Lightning.SendCustomMessage:output_type -> lnrpc.SendCustomMessageResponse
|
||||
24, // 320: lnrpc.Lightning.SubscribeCustomMessages:output_type -> lnrpc.CustomMessage
|
||||
67, // 321: lnrpc.Lightning.ListAliases:output_type -> lnrpc.ListAliasesResponse
|
||||
22, // 322: lnrpc.Lightning.LookupHtlcResolution:output_type -> lnrpc.LookupHtlcResolutionResponse
|
||||
255, // [255:323] is the sub-list for method output_type
|
||||
187, // [187:255] is the sub-list for method input_type
|
||||
187, // [187:187] is the sub-list for extension 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() }
|
||||
|
||||
@@ -4289,6 +4289,7 @@ message PayReq {
|
||||
bytes payment_addr = 11;
|
||||
int64 num_msat = 12;
|
||||
map<uint32, Feature> features = 13;
|
||||
repeated BlindedPaymentPath blinded_paths = 14;
|
||||
}
|
||||
|
||||
enum FeatureBit {
|
||||
|
||||
@@ -6299,6 +6299,12 @@
|
||||
"additionalProperties": {
|
||||
"$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
|
||||
// forwarding information.
|
||||
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 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
|
||||
// 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]
|
||||
|
||||
// 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 tlv.OptionalRecordT[tlv.TlvType12, PaymentConstraints]
|
||||
@@ -33,16 +43,20 @@ type BlindedRouteData struct {
|
||||
Features tlv.OptionalRecordT[tlv.TlvType14, lnwire.FeatureVector]
|
||||
}
|
||||
|
||||
// NewBlindedRouteData creates the data that's provided for hops within a
|
||||
// blinded route.
|
||||
func NewBlindedRouteData(chanID lnwire.ShortChannelID,
|
||||
// NewNonFinalBlindedRouteData creates the data that's provided for hops within
|
||||
// a blinded route.
|
||||
func NewNonFinalBlindedRouteData(chanID lnwire.ShortChannelID,
|
||||
blindingOverride *btcec.PublicKey, relayInfo PaymentRelayInfo,
|
||||
constraints *PaymentConstraints,
|
||||
features *lnwire.FeatureVector) *BlindedRouteData {
|
||||
|
||||
info := &BlindedRouteData{
|
||||
ShortChannelID: tlv.NewRecordT[tlv.TlvType2](chanID),
|
||||
RelayInfo: tlv.NewRecordT[tlv.TlvType10](relayInfo),
|
||||
ShortChannelID: tlv.SomeRecordT(
|
||||
tlv.NewRecordT[tlv.TlvType2](chanID),
|
||||
),
|
||||
RelayInfo: tlv.SomeRecordT(
|
||||
tlv.NewRecordT[tlv.TlvType10](relayInfo),
|
||||
),
|
||||
}
|
||||
|
||||
if blindingOverride != nil {
|
||||
@@ -64,12 +78,36 @@ func NewBlindedRouteData(chanID lnwire.ShortChannelID,
|
||||
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.
|
||||
func DecodeBlindedRouteData(r io.Reader) (*BlindedRouteData, error) {
|
||||
var (
|
||||
d BlindedRouteData
|
||||
|
||||
padding = d.Padding.Zero()
|
||||
scid = d.ShortChannelID.Zero()
|
||||
pathID = d.PathID.Zero()
|
||||
blindingOverride = d.NextBlindingOverride.Zero()
|
||||
relayInfo = d.RelayInfo.Zero()
|
||||
constraints = d.Constraints.Zero()
|
||||
features = d.Features.Zero()
|
||||
)
|
||||
@@ -80,19 +118,35 @@ func DecodeBlindedRouteData(r io.Reader) (*BlindedRouteData, error) {
|
||||
}
|
||||
|
||||
typeMap, err := tlvRecords.ExtractRecords(
|
||||
&d.ShortChannelID,
|
||||
&blindingOverride, &d.RelayInfo.Val, &constraints,
|
||||
&features,
|
||||
&padding, &scid, &pathID, &blindingOverride, &relayInfo,
|
||||
&constraints, &features,
|
||||
)
|
||||
if err != nil {
|
||||
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 {
|
||||
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 {
|
||||
d.Constraints = tlv.SomeRecordT(constraints)
|
||||
}
|
||||
@@ -111,7 +165,19 @@ func EncodeBlindedRouteData(data *BlindedRouteData) ([]byte, error) {
|
||||
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,
|
||||
*btcec.PublicKey]) {
|
||||
@@ -119,7 +185,11 @@ func EncodeBlindedRouteData(data *BlindedRouteData) ([]byte, error) {
|
||||
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,
|
||||
PaymentConstraints]) {
|
||||
@@ -140,6 +210,19 @@ func EncodeBlindedRouteData(data *BlindedRouteData) ([]byte, error) {
|
||||
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.
|
||||
type PaymentRelayInfo struct {
|
||||
// CltvExpiryDelta is the expiry delta for the payment.
|
||||
@@ -153,8 +236,8 @@ type PaymentRelayInfo struct {
|
||||
BaseFee uint32
|
||||
}
|
||||
|
||||
// newPaymentRelayRecord creates a tlv.Record that encodes the payment relay
|
||||
// (type 10) type for an encrypted blob payload.
|
||||
// Record creates a tlv.Record that encodes the payment relay (type 10) type for
|
||||
// an encrypted blob payload.
|
||||
func (i *PaymentRelayInfo) Record() tlv.Record {
|
||||
return tlv.MakeDynamicRecord(
|
||||
10, &i, func() uint64 {
|
||||
|
||||
+134
-4
@@ -101,7 +101,7 @@ func TestBlindedDataEncoding(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
encodedData := NewBlindedRouteData(
|
||||
encodedData := NewNonFinalBlindedRouteData(
|
||||
channelID, pubkey(t), info, constraints,
|
||||
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
|
||||
// blinded route data provided in the specification.
|
||||
//
|
||||
@@ -131,10 +252,11 @@ func TestBlindingSpecTestVectors(t *testing.T) {
|
||||
tests := []struct {
|
||||
encoded string
|
||||
expectedPaymentData *BlindedRouteData
|
||||
expectedPadding int
|
||||
}{
|
||||
{
|
||||
encoded: "011a0000000000000000000000000000000000000000000000000000020800000000000006c10a0800240000009627100c06000b69e505dc0e00fd023103123456",
|
||||
expectedPaymentData: NewBlindedRouteData(
|
||||
expectedPaymentData: NewNonFinalBlindedRouteData(
|
||||
lnwire.ShortChannelID{
|
||||
BlockHeight: 0,
|
||||
TxIndex: 0,
|
||||
@@ -155,10 +277,11 @@ func TestBlindingSpecTestVectors(t *testing.T) {
|
||||
lnwire.Features,
|
||||
),
|
||||
),
|
||||
expectedPadding: 26,
|
||||
},
|
||||
{
|
||||
encoded: "020800000000000004510821031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f0a0800300000006401f40c06000b69c105dc0e00",
|
||||
expectedPaymentData: NewBlindedRouteData(
|
||||
expectedPaymentData: NewNonFinalBlindedRouteData(
|
||||
lnwire.ShortChannelID{
|
||||
TxPosition: 1105,
|
||||
},
|
||||
@@ -175,7 +298,8 @@ func TestBlindingSpecTestVectors(t *testing.T) {
|
||||
lnwire.NewFeatureVector(
|
||||
lnwire.NewRawFeatureVector(),
|
||||
lnwire.Features,
|
||||
)),
|
||||
),
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -189,6 +313,12 @@ func TestBlindingSpecTestVectors(t *testing.T) {
|
||||
decodedRoute, err := DecodeBlindedRouteData(buff)
|
||||
require.NoError(t, err)
|
||||
|
||||
if test.expectedPadding != 0 {
|
||||
test.expectedPaymentData.PadBy(
|
||||
test.expectedPadding,
|
||||
)
|
||||
}
|
||||
|
||||
require.Equal(
|
||||
t, test.expectedPaymentData, decodedRoute,
|
||||
)
|
||||
|
||||
@@ -2,8 +2,8 @@ package routing
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/lightningnetwork/lnd/channeldb/models"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
@@ -29,6 +29,11 @@ type AdditionalEdge interface {
|
||||
|
||||
// EdgePolicy returns the policy of the additional edge.
|
||||
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.
|
||||
@@ -60,12 +65,49 @@ func (p *PrivateEdge) IntermediatePayloadSize(amount lnwire.MilliSatoshi,
|
||||
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
|
||||
// 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 {
|
||||
policy *models.CachedEdgePolicy
|
||||
cipherText []byte
|
||||
blindingPoint *btcec.PublicKey
|
||||
policy *models.CachedEdgePolicy
|
||||
|
||||
// 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.
|
||||
@@ -78,15 +120,23 @@ func (b *BlindedEdge) EdgePolicy() *models.CachedEdgePolicy {
|
||||
func (b *BlindedEdge) IntermediatePayloadSize(_ lnwire.MilliSatoshi, _ uint32,
|
||||
_ uint64) uint64 {
|
||||
|
||||
blindedPath := b.blindedPayment.BlindedPath
|
||||
|
||||
hop := route.Hop{
|
||||
BlindingPoint: b.blindingPoint,
|
||||
EncryptedData: b.cipherText,
|
||||
BlindingPoint: blindedPath.BlindingPoint,
|
||||
EncryptedData: blindedPath.BlindedHops[b.hopIndex].CipherText,
|
||||
}
|
||||
|
||||
// For blinded paths the next chanID is in the encrypted data tlv.
|
||||
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
|
||||
// implement the AdditionalEdge interface.
|
||||
var _ AdditionalEdge = (*PrivateEdge)(nil)
|
||||
|
||||
@@ -42,9 +42,13 @@ func TestIntermediatePayloadSize(t *testing.T) {
|
||||
hop: route.Hop{
|
||||
EncryptedData: []byte{12, 13},
|
||||
},
|
||||
edge: &BlindedEdge{
|
||||
cipherText: []byte{12, 13},
|
||||
},
|
||||
edge: &BlindedEdge{blindedPayment: &BlindedPayment{
|
||||
BlindedPath: &sphinx.BlindedPath{
|
||||
BlindedHops: []*sphinx.BlindedHopInfo{
|
||||
{CipherText: []byte{12, 13}},
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "Blinded edge - introduction point",
|
||||
@@ -52,10 +56,14 @@ func TestIntermediatePayloadSize(t *testing.T) {
|
||||
EncryptedData: []byte{12, 13},
|
||||
BlindingPoint: blindedPoint,
|
||||
},
|
||||
edge: &BlindedEdge{
|
||||
cipherText: []byte{12, 13},
|
||||
blindingPoint: blindedPoint,
|
||||
},
|
||||
edge: &BlindedEdge{blindedPayment: &BlindedPayment{
|
||||
BlindedPath: &sphinx.BlindedPath{
|
||||
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
|
||||
// hints (both for intermediate hops and the final_cltv_delta for the receiving
|
||||
// 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
|
||||
// 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
|
||||
// don't need to add any route hints.
|
||||
if len(b.BlindedPath.BlindedHops) == 1 {
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
hintCount := len(b.BlindedPath.BlindedHops) - 1
|
||||
@@ -136,14 +136,13 @@ func (b *BlindedPayment) toRouteHints() RouteHints {
|
||||
ToNodeFeatures: features,
|
||||
}
|
||||
|
||||
hints[fromNode] = []AdditionalEdge{
|
||||
&BlindedEdge{
|
||||
policy: edgePolicy,
|
||||
cipherText: b.BlindedPath.BlindedHops[0].CipherText,
|
||||
blindingPoint: b.BlindedPath.BlindingPoint,
|
||||
},
|
||||
edge, err := NewBlindedEdge(edgePolicy, b, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hints[fromNode] = []AdditionalEdge{edge}
|
||||
|
||||
// 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
|
||||
// because we're dealing with hops as pairs.
|
||||
@@ -169,14 +168,13 @@ func (b *BlindedPayment) toRouteHints() RouteHints {
|
||||
ToNodeFeatures: features,
|
||||
}
|
||||
|
||||
hints[fromNode] = []AdditionalEdge{
|
||||
&BlindedEdge{
|
||||
policy: edgePolicy,
|
||||
cipherText: b.BlindedPath.BlindedHops[i].
|
||||
CipherText,
|
||||
},
|
||||
edge, err := NewBlindedEdge(edgePolicy, b, i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hints[fromNode] = []AdditionalEdge{edge}
|
||||
}
|
||||
|
||||
return hints
|
||||
return hints, nil
|
||||
}
|
||||
+33
-29
@@ -128,7 +128,9 @@ func TestBlindedPaymentToHints(t *testing.T) {
|
||||
HtlcMaximum: htlcMax,
|
||||
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.
|
||||
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{
|
||||
v1: {
|
||||
//nolint:lll
|
||||
&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,
|
||||
},
|
||||
blindedEdge1,
|
||||
},
|
||||
vb2: {
|
||||
&BlindedEdge{
|
||||
policy: &models.CachedEdgePolicy{
|
||||
ToNodePubKey: func() route.Vertex {
|
||||
return vb3
|
||||
},
|
||||
ToNodeFeatures: features,
|
||||
},
|
||||
cipherText: cipherText,
|
||||
},
|
||||
blindedEdge2,
|
||||
},
|
||||
}
|
||||
|
||||
actual := blindedPayment.toRouteHints()
|
||||
actual, err := blindedPayment.toRouteHints()
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, len(expected), len(actual))
|
||||
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
|
||||
// custom records and payment address.
|
||||
type finalHopParams struct {
|
||||
amt lnwire.MilliSatoshi
|
||||
totalAmt lnwire.MilliSatoshi
|
||||
cltvDelta uint16
|
||||
amt lnwire.MilliSatoshi
|
||||
totalAmt lnwire.MilliSatoshi
|
||||
|
||||
// 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
|
||||
paymentAddr *[32]byte
|
||||
|
||||
@@ -190,10 +198,21 @@ func newRoute(sourceVertex route.Vertex,
|
||||
// reporting through RPC. Set to zero for the final hop.
|
||||
fee = 0
|
||||
|
||||
// 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)
|
||||
// Only include the final hop CLTV delta in the total
|
||||
// time lock value if this is not a route to a blinded
|
||||
// path. For blinded paths, the total time-lock from the
|
||||
// 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
|
||||
|
||||
// Attach any custom records to the final hop.
|
||||
@@ -968,6 +987,7 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
|
||||
inboundFee,
|
||||
fakeHopHintCapacity,
|
||||
reverseEdge.edge.IntermediatePayloadSize,
|
||||
reverseEdge.edge.BlindedPayment(),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -3322,16 +3322,19 @@ func TestBlindedRouteConstruction(t *testing.T) {
|
||||
ToNodeFeatures: tlvFeatures,
|
||||
}
|
||||
|
||||
// Create final hop parameters for payment amount = 110. Note
|
||||
// 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.
|
||||
// Create final hop parameters for payment amount = 110.
|
||||
totalAmt lnwire.MilliSatoshi = 110
|
||||
finalHopParams = finalHopParams{
|
||||
amt: totalAmt,
|
||||
totalAmt: totalAmt,
|
||||
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
|
||||
// map is keyed by source node, so we can retrieve our blinded edges
|
||||
// accordingly.
|
||||
blindedEdges := blindedPayment.toRouteHints()
|
||||
blindedEdges, err := blindedPayment.toRouteHints()
|
||||
require.NoError(t, err)
|
||||
|
||||
carolDaveEdge := blindedEdges[carolVertex][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.
|
||||
requestHints = routeHints
|
||||
requestExpiry = finalExpiry
|
||||
err error
|
||||
)
|
||||
|
||||
if blindedPayment != nil {
|
||||
@@ -2038,7 +2039,10 @@ func NewRouteRequest(source route.Vertex, target *route.Vertex,
|
||||
requestExpiry = blindedPayment.CltvExpiryDelta
|
||||
}
|
||||
|
||||
requestHints = blindedPayment.toRouteHints()
|
||||
requestHints, err = blindedPayment.toRouteHints()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
requestTarget, err := getTargetNode(target, blindedPayment)
|
||||
|
||||
+38
-26
@@ -51,7 +51,8 @@ func newNodeEdgeUnifier(sourceNode, toNode route.Vertex, useInboundFees bool,
|
||||
// incorrectly specified.
|
||||
func (u *nodeEdgeUnifier) addPolicy(fromNode route.Vertex,
|
||||
edge *models.CachedEdgePolicy, inboundFee models.InboundFee,
|
||||
capacity btcutil.Amount, hopPayloadSizeFn PayloadSizeFunc) {
|
||||
capacity btcutil.Amount, hopPayloadSizeFn PayloadSizeFunc,
|
||||
blindedPayment *BlindedPayment) {
|
||||
|
||||
localChan := fromNode == u.sourceNode
|
||||
|
||||
@@ -86,12 +87,9 @@ func (u *nodeEdgeUnifier) addPolicy(fromNode route.Vertex,
|
||||
inboundFee = models.InboundFee{}
|
||||
}
|
||||
|
||||
unifier.edges = append(unifier.edges, &unifiedEdge{
|
||||
policy: edge,
|
||||
capacity: capacity,
|
||||
hopPayloadSizeFn: hopPayloadSizeFn,
|
||||
inboundFees: inboundFee,
|
||||
})
|
||||
unifier.edges = append(unifier.edges, newUnifiedEdge(
|
||||
edge, capacity, inboundFee, hopPayloadSizeFn, blindedPayment,
|
||||
))
|
||||
}
|
||||
|
||||
// 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(
|
||||
channel.OtherNode, channel.InPolicy, inboundFee,
|
||||
channel.Capacity, defaultHopPayloadSize,
|
||||
channel.Capacity, defaultHopPayloadSize, nil,
|
||||
)
|
||||
|
||||
return nil
|
||||
@@ -137,6 +135,24 @@ type unifiedEdge struct {
|
||||
// is needed because hops of a blinded path differ in their payload
|
||||
// structure compared to cleartext hops.
|
||||
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
|
||||
@@ -292,12 +308,10 @@ func (u *edgeUnifier) getEdgeLocal(netAmtReceived lnwire.MilliSatoshi,
|
||||
maxBandwidth = bandwidth
|
||||
|
||||
// Update best edge.
|
||||
bestEdge = &unifiedEdge{
|
||||
policy: edge.policy,
|
||||
capacity: edge.capacity,
|
||||
hopPayloadSizeFn: edge.hopPayloadSizeFn,
|
||||
inboundFees: edge.inboundFees,
|
||||
}
|
||||
bestEdge = newUnifiedEdge(
|
||||
edge.policy, edge.capacity, edge.inboundFees,
|
||||
edge.hopPayloadSizeFn, edge.blindedPayment,
|
||||
)
|
||||
}
|
||||
|
||||
return bestEdge
|
||||
@@ -376,10 +390,10 @@ func (u *edgeUnifier) getEdgeNetwork(netAmtReceived lnwire.MilliSatoshi,
|
||||
}
|
||||
maxFee = fee
|
||||
|
||||
bestPolicy = &unifiedEdge{
|
||||
policy: edge.policy,
|
||||
inboundFees: edge.inboundFees,
|
||||
}
|
||||
bestPolicy = newUnifiedEdge(
|
||||
edge.policy, 0, edge.inboundFees, nil,
|
||||
edge.blindedPayment,
|
||||
)
|
||||
|
||||
// The payload size function for edges to a connected peer is
|
||||
// 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
|
||||
// have distinct policies for channels to the same peer.
|
||||
policyCopy := *bestPolicy.policy
|
||||
modifiedEdge := unifiedEdge{
|
||||
policy: &policyCopy,
|
||||
inboundFees: bestPolicy.inboundFees,
|
||||
}
|
||||
modifiedEdge.policy.TimeLockDelta = maxTimelock
|
||||
modifiedEdge.capacity = maxCapMsat.ToSatoshis()
|
||||
modifiedEdge.hopPayloadSizeFn = hopPayloadSizeFn
|
||||
policyCopy.TimeLockDelta = maxTimelock
|
||||
modifiedEdge := newUnifiedEdge(
|
||||
&policyCopy, maxCapMsat.ToSatoshis(), bestPolicy.inboundFees,
|
||||
hopPayloadSizeFn, bestPolicy.blindedPayment,
|
||||
)
|
||||
|
||||
return &modifiedEdge
|
||||
return modifiedEdge
|
||||
}
|
||||
|
||||
// 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.addPolicy(
|
||||
fromNode, &p1, inboundFee1, c1, defaultHopPayloadSize,
|
||||
fromNode, &p1, inboundFee1, c1, defaultHopPayloadSize, nil,
|
||||
)
|
||||
unifierFilled.addPolicy(
|
||||
fromNode, &p2, inboundFee2, c2, defaultHopPayloadSize,
|
||||
fromNode, &p2, inboundFee2, c2, defaultHopPayloadSize, nil,
|
||||
)
|
||||
|
||||
unifierNoCapacity := newNodeEdgeUnifier(source, toNode, false, nil)
|
||||
unifierNoCapacity.addPolicy(
|
||||
fromNode, &p1, inboundFee1, 0, defaultHopPayloadSize,
|
||||
fromNode, &p1, inboundFee1, 0, defaultHopPayloadSize, nil,
|
||||
)
|
||||
unifierNoCapacity.addPolicy(
|
||||
fromNode, &p2, inboundFee2, 0, defaultHopPayloadSize,
|
||||
fromNode, &p2, inboundFee2, 0, defaultHopPayloadSize, nil,
|
||||
)
|
||||
|
||||
unifierNoInfo := newNodeEdgeUnifier(source, toNode, false, nil)
|
||||
unifierNoInfo.addPolicy(
|
||||
fromNode, &models.CachedEdgePolicy{}, models.InboundFee{},
|
||||
0, defaultHopPayloadSize,
|
||||
0, defaultHopPayloadSize, nil,
|
||||
)
|
||||
|
||||
unifierInboundFee := newNodeEdgeUnifier(source, toNode, true, nil)
|
||||
unifierInboundFee.addPolicy(
|
||||
fromNode, &p1, inboundFee1, c1, defaultHopPayloadSize,
|
||||
fromNode, &p1, inboundFee1, c1, defaultHopPayloadSize, nil,
|
||||
)
|
||||
unifierInboundFee.addPolicy(
|
||||
fromNode, &p2, inboundFee2, c2, defaultHopPayloadSize,
|
||||
fromNode, &p2, inboundFee2, c2, defaultHopPayloadSize, nil,
|
||||
)
|
||||
|
||||
unifierLocal := newNodeEdgeUnifier(fromNode, toNode, true, nil)
|
||||
unifierLocal.addPolicy(
|
||||
fromNode, &p1, inboundFee1, c1, defaultHopPayloadSize,
|
||||
fromNode, &p1, inboundFee1, c1, defaultHopPayloadSize, nil,
|
||||
)
|
||||
|
||||
inboundFeeZero := models.InboundFee{}
|
||||
@@ -98,10 +98,11 @@ func TestNodeEdgeUnifier(t *testing.T) {
|
||||
}
|
||||
unifierNegInboundFee := newNodeEdgeUnifier(source, toNode, true, nil)
|
||||
unifierNegInboundFee.addPolicy(
|
||||
fromNode, &p1, inboundFeeZero, c1, defaultHopPayloadSize,
|
||||
fromNode, &p1, inboundFeeZero, c1, defaultHopPayloadSize, nil,
|
||||
)
|
||||
unifierNegInboundFee.addPolicy(
|
||||
fromNode, &p2, inboundFeeNegative, c2, defaultHopPayloadSize,
|
||||
nil,
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
|
||||
@@ -6971,6 +6971,13 @@ func (r *rpcServer) DecodePayReq(ctx context.Context,
|
||||
// Convert between the `lnrpc` and `routing` types.
|
||||
routeHints := invoicesrpc.CreateRPCRouteHints(payReq.RouteHints)
|
||||
|
||||
blindedPaymentPaths, err := invoicesrpc.CreateRPCBlindedPayments(
|
||||
payReq.BlindedPaymentPaths,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var amtSat, amtMsat int64
|
||||
if payReq.MilliSat != nil {
|
||||
amtSat = int64(payReq.MilliSat.ToSatoshis())
|
||||
@@ -6996,6 +7003,7 @@ func (r *rpcServer) DecodePayReq(ctx context.Context,
|
||||
Expiry: expiry,
|
||||
CltvExpiry: int64(payReq.MinFinalCLTVExpiry()),
|
||||
RouteHints: routeHints,
|
||||
BlindedPaths: blindedPaymentPaths,
|
||||
PaymentAddr: paymentAddr,
|
||||
Features: invoicesrpc.CreateRPCFeatures(payReq.Features),
|
||||
}, 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)
|
||||
|
||||
case fieldTypeS:
|
||||
if invoice.PaymentAddr != nil {
|
||||
// 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)
|
||||
|
||||
case fieldTypeD:
|
||||
if invoice.Description != nil {
|
||||
// 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)
|
||||
|
||||
case fieldTypeM:
|
||||
if invoice.Metadata != nil {
|
||||
// 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)
|
||||
|
||||
case fieldTypeH:
|
||||
if invoice.DescriptionHash != nil {
|
||||
// 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)
|
||||
|
||||
case fieldTypeX:
|
||||
if invoice.expiry != nil {
|
||||
// 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)
|
||||
|
||||
case fieldTypeC:
|
||||
if invoice.minFinalCLTVExpiry != nil {
|
||||
// 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
|
||||
}
|
||||
|
||||
invoice.minFinalCLTVExpiry, err = parseMinFinalCLTVExpiry(base32Data)
|
||||
invoice.minFinalCLTVExpiry, err =
|
||||
parseMinFinalCLTVExpiry(base32Data)
|
||||
|
||||
case fieldTypeF:
|
||||
if invoice.FallbackAddr != nil {
|
||||
// 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
|
||||
}
|
||||
|
||||
invoice.FallbackAddr, err = parseFallbackAddr(base32Data, net)
|
||||
invoice.FallbackAddr, err = parseFallbackAddr(
|
||||
base32Data, net,
|
||||
)
|
||||
|
||||
case fieldTypeR:
|
||||
// An `r` field can be included in an invoice multiple
|
||||
// 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
|
||||
}
|
||||
|
||||
invoice.RouteHints = append(invoice.RouteHints, routeHint)
|
||||
invoice.RouteHints = append(
|
||||
invoice.RouteHints, routeHint,
|
||||
)
|
||||
|
||||
case fieldType9:
|
||||
if invoice.Features != nil {
|
||||
// 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)
|
||||
|
||||
case fieldTypeB:
|
||||
blindedPaymentPath, err := parseBlindedPaymentPath(
|
||||
base32Data,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
invoice.BlindedPaymentPaths = append(
|
||||
invoice.BlindedPaymentPaths, blindedPaymentPath,
|
||||
)
|
||||
|
||||
default:
|
||||
// Ignore unknown type.
|
||||
}
|
||||
@@ -495,6 +522,17 @@ func parseRouteHint(data []byte) ([]HopHint, error) {
|
||||
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
|
||||
// representation.
|
||||
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 {
|
||||
// Convert 33 byte pubkey to 53 5-bit groups.
|
||||
pubKeyBase32, err := bech32.ConvertBits(
|
||||
|
||||
+33
-1
@@ -76,6 +76,10 @@ const (
|
||||
// probing the recipient.
|
||||
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.
|
||||
// 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
|
||||
@@ -152,6 +156,10 @@ type Invoice struct {
|
||||
// This field is un-exported and can only be read by the
|
||||
// MinFinalCLTVExpiry() method. By forcing callers to read via this
|
||||
// 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
|
||||
|
||||
// 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
|
||||
// represent private routes.
|
||||
//
|
||||
// NOTE: This is optional.
|
||||
// NOTE: This is optional and should not be set at the same time as
|
||||
// BlindedPaymentPaths.
|
||||
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
|
||||
// required support for features by the receiver.
|
||||
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
|
||||
// desired feature bits that are advertised on the invoice. If this option is
|
||||
// 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")
|
||||
}
|
||||
|
||||
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.
|
||||
if invoice.Description != nil && invoice.DescriptionHash != nil {
|
||||
return fmt.Errorf("both description and description hash set")
|
||||
|
||||
+147
-113
@@ -7,7 +7,6 @@ import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -17,7 +16,9 @@ import (
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
sphinx "github.com/lightningnetwork/lightning-onion"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -116,6 +117,62 @@ var (
|
||||
|
||||
// Must be initialized in init().
|
||||
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() {
|
||||
@@ -125,6 +182,8 @@ func init() {
|
||||
// TestDecodeEncode tests that an encoded invoice gets decoded into the expected
|
||||
// Invoice object, and that reencoding the decoded invoice gets us back to the
|
||||
// original encoded string.
|
||||
//
|
||||
//nolint:lll
|
||||
func TestDecodeEncode(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -673,52 +732,77 @@ func TestDecodeEncode(t *testing.T) {
|
||||
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 {
|
||||
var decodedInvoice *Invoice
|
||||
net := &chaincfg.MainNetParams
|
||||
if test.decodedInvoice != nil {
|
||||
decodedInvoice = test.decodedInvoice()
|
||||
net = decodedInvoice.Net
|
||||
}
|
||||
test := test
|
||||
|
||||
invoice, err := Decode(test.encodedInvoice, net)
|
||||
if (err == nil) != test.valid {
|
||||
t.Errorf("Decoding test %d failed: %v", i, err)
|
||||
return
|
||||
}
|
||||
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if test.valid {
|
||||
if err := compareInvoices(decodedInvoice, invoice); err != nil {
|
||||
t.Errorf("Invoice decoding result %d not as expected: %v", i, err)
|
||||
var decodedInvoice *Invoice
|
||||
net := &chaincfg.MainNetParams
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
if test.skipEncoding {
|
||||
continue
|
||||
}
|
||||
if test.beforeEncoding != nil {
|
||||
test.beforeEncoding(decodedInvoice)
|
||||
}
|
||||
|
||||
if test.beforeEncoding != nil {
|
||||
test.beforeEncoding(decodedInvoice)
|
||||
}
|
||||
if decodedInvoice == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if decodedInvoice != nil {
|
||||
reencoded, err := decodedInvoice.Encode(
|
||||
testMessageSigner,
|
||||
)
|
||||
if (err == nil) != test.valid {
|
||||
t.Errorf("Encoding test %d failed: %v", i, err)
|
||||
if !test.valid {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
if test.valid && test.encodedInvoice != reencoded {
|
||||
t.Errorf("Encoding %d failed, expected %v, got %v",
|
||||
i, test.encodedInvoice, reencoded)
|
||||
return
|
||||
}
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.encodedInvoice, reencoded)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -805,25 +889,42 @@ func TestNewInvoice(t *testing.T) {
|
||||
valid: true,
|
||||
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 {
|
||||
test := test
|
||||
|
||||
invoice, err := test.newInvoice()
|
||||
if err != nil && !test.valid {
|
||||
continue
|
||||
}
|
||||
encoded, err := invoice.Encode(testMessageSigner)
|
||||
if (err == nil) != test.valid {
|
||||
t.Errorf("NewInvoice test %d failed: %v", i, err)
|
||||
return
|
||||
}
|
||||
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if test.valid && test.encodedInvoice != encoded {
|
||||
t.Errorf("Encoding %d failed, expected %v, got %v",
|
||||
i, test.encodedInvoice, encoded)
|
||||
return
|
||||
}
|
||||
invoice, err := test.newInvoice()
|
||||
if !test.valid {
|
||||
require.Error(t, err)
|
||||
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 {
|
||||
if a == b {
|
||||
return true
|
||||
|
||||
Reference in new issue
Block a user