mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-26 01:33:02 +01:00
lnrpc: add wire records fields to payment+interceptor RPCs
This commit is contained in:
parent
878f964a33
commit
42e358e3d3
@ -92,6 +92,7 @@ func (r *forwardInterceptor) onIntercept(
|
||||
CustomRecords: htlc.InOnionCustomRecords,
|
||||
OnionBlob: htlc.OnionBlob[:],
|
||||
AutoFailHeight: htlc.AutoFailHeight,
|
||||
InWireCustomRecords: htlc.InWireCustomRecords,
|
||||
}
|
||||
|
||||
return r.stream.Send(interceptionRequest)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -358,6 +358,15 @@ message SendPaymentRequest {
|
||||
being sent.
|
||||
*/
|
||||
bool cancelable = 24;
|
||||
|
||||
/*
|
||||
An optional field that can be used to pass an arbitrary set of TLV records
|
||||
to the first hop peer of this payment. This can be used to pass application
|
||||
specific data during the payment attempt. Record types are required to be in
|
||||
the custom range >= 65536. When using REST, the values must be encoded as
|
||||
base64.
|
||||
*/
|
||||
map<uint64, bytes> first_hop_custom_records = 25;
|
||||
}
|
||||
|
||||
message TrackPaymentRequest {
|
||||
@ -451,6 +460,15 @@ message SendToRouteRequest {
|
||||
routes, incorrect payment details, or insufficient funds.
|
||||
*/
|
||||
bool skip_temp_err = 3;
|
||||
|
||||
/*
|
||||
An optional field that can be used to pass an arbitrary set of TLV records
|
||||
to the first hop peer of this payment. This can be used to pass application
|
||||
specific data during the payment attempt. Record types are required to be in
|
||||
the custom range >= 65536. When using REST, the values must be encoded as
|
||||
base64.
|
||||
*/
|
||||
map<uint64, bytes> first_hop_custom_records = 4;
|
||||
}
|
||||
|
||||
message SendToRouteResponse {
|
||||
@ -726,6 +744,15 @@ message BuildRouteRequest {
|
||||
This is also called payment secret in specifications (e.g. BOLT 11).
|
||||
*/
|
||||
bytes payment_addr = 5;
|
||||
|
||||
/*
|
||||
An optional field that can be used to pass an arbitrary set of TLV records
|
||||
to the first hop peer of this payment. This can be used to pass application
|
||||
specific data during the payment attempt. Record types are required to be in
|
||||
the custom range >= 65536. When using REST, the values must be encoded as
|
||||
base64.
|
||||
*/
|
||||
map<uint64, bytes> first_hop_custom_records = 6;
|
||||
}
|
||||
|
||||
message BuildRouteResponse {
|
||||
@ -982,6 +1009,9 @@ message ForwardHtlcInterceptRequest {
|
||||
// The block height at which this htlc will be auto-failed to prevent the
|
||||
// channel from force-closing.
|
||||
int32 auto_fail_height = 10;
|
||||
|
||||
// The custom records of the peer's incoming p2p wire message.
|
||||
map<uint64, bytes> in_wire_custom_records = 11;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1283,6 +1283,14 @@
|
||||
"type": "string",
|
||||
"format": "byte",
|
||||
"description": "An optional payment addr to be included within the last hop of the route.\nThis is also called payment secret in specifications (e.g. BOLT 11)."
|
||||
},
|
||||
"first_hop_custom_records": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string",
|
||||
"format": "byte"
|
||||
},
|
||||
"description": "An optional field that can be used to pass an arbitrary set of TLV records\nto the first hop peer of this payment. This can be used to pass application\nspecific data during the payment attempt. Record types are required to be in\nthe custom range \u003e= 65536. When using REST, the values must be encoded as\nbase64."
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -1447,6 +1455,14 @@
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"description": "The block height at which this htlc will be auto-failed to prevent the\nchannel from force-closing."
|
||||
},
|
||||
"in_wire_custom_records": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string",
|
||||
"format": "byte"
|
||||
},
|
||||
"description": "The custom records of the peer's incoming p2p wire message."
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -1957,6 +1973,14 @@
|
||||
"cancelable": {
|
||||
"type": "boolean",
|
||||
"description": "If set, the payment loop can be interrupted by manually canceling the\npayment context, even before the payment timeout is reached. Note that the\npayment may still succeed after cancellation, as in-flight attempts can\nstill settle afterwards. Canceling will only prevent further attempts from\nbeing sent."
|
||||
},
|
||||
"first_hop_custom_records": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string",
|
||||
"format": "byte"
|
||||
},
|
||||
"description": "An optional field that can be used to pass an arbitrary set of TLV records\nto the first hop peer of this payment. This can be used to pass application\nspecific data during the payment attempt. Record types are required to be in\nthe custom range \u003e= 65536. When using REST, the values must be encoded as\nbase64."
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -1975,6 +1999,14 @@
|
||||
"skip_temp_err": {
|
||||
"type": "boolean",
|
||||
"description": "Whether the payment should be marked as failed when a temporary error is\nreturned from the given route. Set it to true so the payment won't be\nfailed unless a terminal error is occurred, such as payment timeout, no\nroutes, incorrect payment details, or insufficient funds."
|
||||
},
|
||||
"first_hop_custom_records": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string",
|
||||
"format": "byte"
|
||||
},
|
||||
"description": "An optional field that can be used to pass an arbitrary set of TLV records\nto the first hop peer of this payment. This can be used to pass application\nspecific data during the payment attempt. Record types are required to be in\nthe custom range \u003e= 65536. When using REST, the values must be encoded as\nbase64."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -858,6 +858,12 @@ func (r *RouterBackend) extractIntentFromSendRequest(
|
||||
}
|
||||
payIntent.DestCustomRecords = customRecords
|
||||
|
||||
firstHopRecords := lnwire.CustomRecords(rpcPayReq.FirstHopCustomRecords)
|
||||
if err := firstHopRecords.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
payIntent.FirstHopCustomRecords = firstHopRecords
|
||||
|
||||
payIntent.PayAttemptTimeout = time.Second *
|
||||
time.Duration(rpcPayReq.TimeoutSeconds)
|
||||
|
||||
|
@ -864,6 +864,11 @@ func (s *Server) SendToRouteV2(ctx context.Context,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
firstHopRecords := lnwire.CustomRecords(req.FirstHopCustomRecords)
|
||||
if err := firstHopRecords.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var attempt *channeldb.HTLCAttempt
|
||||
|
||||
// Pass route to the router. This call returns the full htlc attempt
|
||||
@ -873,9 +878,13 @@ func (s *Server) SendToRouteV2(ctx context.Context,
|
||||
// case, we give precedence to the attempt information as stored in the
|
||||
// db.
|
||||
if req.SkipTempErr {
|
||||
attempt, err = s.cfg.Router.SendToRouteSkipTempErr(hash, route)
|
||||
attempt, err = s.cfg.Router.SendToRouteSkipTempErr(
|
||||
hash, route, firstHopRecords,
|
||||
)
|
||||
} else {
|
||||
attempt, err = s.cfg.Router.SendToRoute(hash, route)
|
||||
attempt, err = s.cfg.Router.SendToRoute(
|
||||
hash, route, firstHopRecords,
|
||||
)
|
||||
}
|
||||
if attempt != nil {
|
||||
rpcAttempt, err := s.cfg.RouterBackend.MarshalHTLCAttempt(
|
||||
@ -1458,9 +1467,26 @@ func (s *Server) BuildRoute(_ context.Context,
|
||||
)
|
||||
}
|
||||
|
||||
var firstHopBlob fn.Option[[]byte]
|
||||
if len(req.FirstHopCustomRecords) > 0 {
|
||||
firstHopRecords := lnwire.CustomRecords(
|
||||
req.FirstHopCustomRecords,
|
||||
)
|
||||
if err := firstHopRecords.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
firstHopData, err := firstHopRecords.Serialize()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
firstHopBlob = fn.Some(firstHopData)
|
||||
}
|
||||
|
||||
// Build the route and return it to the caller.
|
||||
route, err := s.cfg.Router.BuildRoute(
|
||||
amt, hops, outgoingChan, req.FinalCltvDelta, payAddr,
|
||||
firstHopBlob,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -1057,18 +1057,21 @@ func (r *ChannelRouter) PreparePayment(payment *LightningPayment) (
|
||||
|
||||
// SendToRoute sends a payment using the provided route and fails the payment
|
||||
// when an error is returned from the attempt.
|
||||
func (r *ChannelRouter) SendToRoute(htlcHash lntypes.Hash,
|
||||
rt *route.Route) (*channeldb.HTLCAttempt, error) {
|
||||
func (r *ChannelRouter) SendToRoute(htlcHash lntypes.Hash, rt *route.Route,
|
||||
firstHopCustomRecords lnwire.CustomRecords) (*channeldb.HTLCAttempt,
|
||||
error) {
|
||||
|
||||
return r.sendToRoute(htlcHash, rt, false)
|
||||
return r.sendToRoute(htlcHash, rt, false, firstHopCustomRecords)
|
||||
}
|
||||
|
||||
// SendToRouteSkipTempErr sends a payment using the provided route and fails
|
||||
// the payment ONLY when a terminal error is returned from the attempt.
|
||||
func (r *ChannelRouter) SendToRouteSkipTempErr(htlcHash lntypes.Hash,
|
||||
rt *route.Route) (*channeldb.HTLCAttempt, error) {
|
||||
rt *route.Route,
|
||||
firstHopCustomRecords lnwire.CustomRecords) (*channeldb.HTLCAttempt,
|
||||
error) {
|
||||
|
||||
return r.sendToRoute(htlcHash, rt, true)
|
||||
return r.sendToRoute(htlcHash, rt, true, firstHopCustomRecords)
|
||||
}
|
||||
|
||||
// sendToRoute attempts to send a payment with the given hash through the
|
||||
@ -1078,7 +1081,9 @@ func (r *ChannelRouter) SendToRouteSkipTempErr(htlcHash lntypes.Hash,
|
||||
// was initiated, both return values will be non-nil. If skipTempErr is true,
|
||||
// the payment won't be failed unless a terminal error has occurred.
|
||||
func (r *ChannelRouter) sendToRoute(htlcHash lntypes.Hash, rt *route.Route,
|
||||
skipTempErr bool) (*channeldb.HTLCAttempt, error) {
|
||||
skipTempErr bool,
|
||||
firstHopCustomRecords lnwire.CustomRecords) (*channeldb.HTLCAttempt,
|
||||
error) {
|
||||
|
||||
log.Debugf("SendToRoute for payment %v with skipTempErr=%v",
|
||||
htlcHash, skipTempErr)
|
||||
@ -1149,7 +1154,8 @@ func (r *ChannelRouter) sendToRoute(htlcHash lntypes.Hash, rt *route.Route,
|
||||
// - no payment timeout.
|
||||
// - no current block height.
|
||||
p := newPaymentLifecycle(
|
||||
r, 0, paymentIdentifier, nil, shardTracker, 0, nil,
|
||||
r, 0, paymentIdentifier, nil, shardTracker, 0,
|
||||
firstHopCustomRecords,
|
||||
)
|
||||
|
||||
// We found a route to try, create a new HTLC attempt to try.
|
||||
@ -1318,8 +1324,9 @@ func (e ErrNoChannel) Error() string {
|
||||
// amount is nil, the minimum routable amount is used. To force a specific
|
||||
// outgoing channel, use the outgoingChan parameter.
|
||||
func (r *ChannelRouter) BuildRoute(amt fn.Option[lnwire.MilliSatoshi],
|
||||
hops []route.Vertex, outgoingChan *uint64,
|
||||
finalCltvDelta int32, payAddr *[32]byte) (*route.Route, error) {
|
||||
hops []route.Vertex, outgoingChan *uint64, finalCltvDelta int32,
|
||||
payAddr *[32]byte, _ fn.Option[[]byte]) (*route.Route,
|
||||
error) {
|
||||
|
||||
log.Tracef("BuildRoute called: hopsCount=%v, amt=%v", len(hops), amt)
|
||||
|
||||
|
@ -519,7 +519,7 @@ func TestChannelUpdateValidation(t *testing.T) {
|
||||
// Send off the payment request to the router. The specified route
|
||||
// should be attempted and the channel update should be received by
|
||||
// graph and ignored because it is missing a valid signature.
|
||||
_, err = ctx.router.SendToRoute(payment, rt)
|
||||
_, err = ctx.router.SendToRoute(payment, rt, nil)
|
||||
require.Error(t, err, "expected route to fail with channel update")
|
||||
|
||||
_, e1, e2, err = ctx.graph.FetchChannelEdgesByID(
|
||||
@ -539,7 +539,7 @@ func TestChannelUpdateValidation(t *testing.T) {
|
||||
ctx.graphBuilder.setNextReject(false)
|
||||
|
||||
// Retry the payment using the same route as before.
|
||||
_, err = ctx.router.SendToRoute(payment, rt)
|
||||
_, err = ctx.router.SendToRoute(payment, rt, nil)
|
||||
require.Error(t, err, "expected route to fail with channel update")
|
||||
|
||||
// This time a valid signature was supplied and the policy change should
|
||||
@ -1428,7 +1428,7 @@ func TestSendToRouteStructuredError(t *testing.T) {
|
||||
// update should be received by router and ignored
|
||||
// because it is missing a valid
|
||||
// signature.
|
||||
_, err = ctx.router.SendToRoute(payment, rt)
|
||||
_, err = ctx.router.SendToRoute(payment, rt, nil)
|
||||
|
||||
fErr, ok := err.(*htlcswitch.ForwardingError)
|
||||
require.True(
|
||||
@ -1507,7 +1507,7 @@ func TestSendToRouteMaxHops(t *testing.T) {
|
||||
// Send off the payment request to the router. We expect an error back
|
||||
// indicating that the route is too long.
|
||||
var payHash lntypes.Hash
|
||||
_, err = ctx.router.SendToRoute(payHash, rt)
|
||||
_, err = ctx.router.SendToRoute(payHash, rt, nil)
|
||||
if err != route.ErrMaxRouteHopsExceeded {
|
||||
t.Fatalf("expected ErrMaxRouteHopsExceeded, but got %v", err)
|
||||
}
|
||||
@ -1649,12 +1649,16 @@ func TestBuildRoute(t *testing.T) {
|
||||
|
||||
// Test that we can't build a route when no hops are given.
|
||||
hops = []route.Vertex{}
|
||||
_, err = ctx.router.BuildRoute(noAmt, hops, nil, 40, nil)
|
||||
_, err = ctx.router.BuildRoute(
|
||||
noAmt, hops, nil, 40, nil, fn.None[[]byte](),
|
||||
)
|
||||
require.Error(t, err)
|
||||
|
||||
// Create hop list for an unknown destination.
|
||||
hops := []route.Vertex{ctx.aliases["b"], ctx.aliases["y"]}
|
||||
_, err = ctx.router.BuildRoute(noAmt, hops, nil, 40, &payAddr)
|
||||
_, err = ctx.router.BuildRoute(
|
||||
noAmt, hops, nil, 40, &payAddr, fn.None[[]byte](),
|
||||
)
|
||||
noChanErr := ErrNoChannel{}
|
||||
require.ErrorAs(t, err, &noChanErr)
|
||||
require.Equal(t, 1, noChanErr.position)
|
||||
@ -1664,7 +1668,9 @@ func TestBuildRoute(t *testing.T) {
|
||||
amt := lnwire.NewMSatFromSatoshis(100)
|
||||
|
||||
// Build the route for the given amount.
|
||||
rt, err := ctx.router.BuildRoute(fn.Some(amt), hops, nil, 40, &payAddr)
|
||||
rt, err := ctx.router.BuildRoute(
|
||||
fn.Some(amt), hops, nil, 40, &payAddr, fn.None[[]byte](),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check that we get the expected route back. The total amount should be
|
||||
@ -1674,7 +1680,9 @@ func TestBuildRoute(t *testing.T) {
|
||||
require.Equal(t, lnwire.MilliSatoshi(106000), rt.TotalAmount)
|
||||
|
||||
// Build the route for the minimum amount.
|
||||
rt, err = ctx.router.BuildRoute(noAmt, hops, nil, 40, &payAddr)
|
||||
rt, err = ctx.router.BuildRoute(
|
||||
noAmt, hops, nil, 40, &payAddr, fn.None[[]byte](),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check that we get the expected route back. The minimum that we can
|
||||
@ -1690,7 +1698,9 @@ func TestBuildRoute(t *testing.T) {
|
||||
// Test a route that contains incompatible channel htlc constraints.
|
||||
// There is no amount that can pass through both channel 5 and 4.
|
||||
hops = []route.Vertex{ctx.aliases["e"], ctx.aliases["c"]}
|
||||
_, err = ctx.router.BuildRoute(noAmt, hops, nil, 40, nil)
|
||||
_, err = ctx.router.BuildRoute(
|
||||
noAmt, hops, nil, 40, nil, fn.None[[]byte](),
|
||||
)
|
||||
require.Error(t, err)
|
||||
noChanErr = ErrNoChannel{}
|
||||
require.ErrorAs(t, err, &noChanErr)
|
||||
@ -1708,7 +1718,9 @@ func TestBuildRoute(t *testing.T) {
|
||||
// amount that could be delivered to the receiver of 21819 msat, using
|
||||
// policy of channel 3.
|
||||
hops = []route.Vertex{ctx.aliases["b"], ctx.aliases["z"]}
|
||||
rt, err = ctx.router.BuildRoute(noAmt, hops, nil, 40, &payAddr)
|
||||
rt, err = ctx.router.BuildRoute(
|
||||
noAmt, hops, nil, 40, &payAddr, fn.None[[]byte](),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
checkHops(rt, []uint64{1, 8}, payAddr)
|
||||
require.Equal(t, lnwire.MilliSatoshi(21200), rt.TotalAmount)
|
||||
@ -1720,7 +1732,9 @@ func TestBuildRoute(t *testing.T) {
|
||||
// We get 106000 - 1000 (base in) - 0.001 * 106000 (rate in) = 104894.
|
||||
hops = []route.Vertex{ctx.aliases["d"], ctx.aliases["f"]}
|
||||
amt = lnwire.NewMSatFromSatoshis(100)
|
||||
rt, err = ctx.router.BuildRoute(fn.Some(amt), hops, nil, 40, &payAddr)
|
||||
rt, err = ctx.router.BuildRoute(
|
||||
fn.Some(amt), hops, nil, 40, &payAddr, fn.None[[]byte](),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
checkHops(rt, []uint64{9, 10}, payAddr)
|
||||
require.EqualValues(t, 104894, rt.TotalAmount)
|
||||
@ -1734,7 +1748,9 @@ func TestBuildRoute(t *testing.T) {
|
||||
// of 20179 msat, which results in underpayment of 1 msat in fee. There
|
||||
// is a third pass through newRoute in which this gets corrected to end
|
||||
hops = []route.Vertex{ctx.aliases["d"], ctx.aliases["f"]}
|
||||
rt, err = ctx.router.BuildRoute(noAmt, hops, nil, 40, &payAddr)
|
||||
rt, err = ctx.router.BuildRoute(
|
||||
noAmt, hops, nil, 40, &payAddr, fn.None[[]byte](),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
checkHops(rt, []uint64{9, 10}, payAddr)
|
||||
require.EqualValues(t, 20180, rt.TotalAmount, "%v", rt.TotalAmount)
|
||||
@ -2208,7 +2224,7 @@ func TestSendToRouteSkipTempErrSuccess(t *testing.T) {
|
||||
payment.On("TerminalInfo").Return(nil, nil)
|
||||
|
||||
// Expect a successful send to route.
|
||||
attempt, err := router.SendToRouteSkipTempErr(payHash, rt)
|
||||
attempt, err := router.SendToRouteSkipTempErr(payHash, rt, nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, testAttempt, attempt)
|
||||
|
||||
@ -2261,7 +2277,7 @@ func TestSendToRouteSkipTempErrNonMPP(t *testing.T) {
|
||||
}}
|
||||
|
||||
// Expect an error to be returned.
|
||||
attempt, err := router.SendToRouteSkipTempErr(payHash, rt)
|
||||
attempt, err := router.SendToRouteSkipTempErr(payHash, rt, nil)
|
||||
require.ErrorIs(t, ErrSkipTempErr, err)
|
||||
require.Nil(t, attempt)
|
||||
|
||||
@ -2345,7 +2361,7 @@ func TestSendToRouteSkipTempErrTempFailure(t *testing.T) {
|
||||
payment.On("TerminalInfo").Return(nil, nil)
|
||||
|
||||
// Expect a failed send to route.
|
||||
attempt, err := router.SendToRouteSkipTempErr(payHash, rt)
|
||||
attempt, err := router.SendToRouteSkipTempErr(payHash, rt, nil)
|
||||
require.Equal(t, tempErr, err)
|
||||
require.Equal(t, testAttempt, attempt)
|
||||
|
||||
@ -2432,7 +2448,7 @@ func TestSendToRouteSkipTempErrPermanentFailure(t *testing.T) {
|
||||
payment.On("TerminalInfo").Return(nil, &failureReason)
|
||||
|
||||
// Expect a failed send to route.
|
||||
attempt, err := router.SendToRouteSkipTempErr(payHash, rt)
|
||||
attempt, err := router.SendToRouteSkipTempErr(payHash, rt, nil)
|
||||
require.Equal(t, permErr, err)
|
||||
require.Equal(t, testAttempt, attempt)
|
||||
|
||||
@ -2517,7 +2533,7 @@ func TestSendToRouteTempFailure(t *testing.T) {
|
||||
).Return(nil, nil)
|
||||
|
||||
// Expect a failed send to route.
|
||||
attempt, err := router.SendToRoute(payHash, rt)
|
||||
attempt, err := router.SendToRoute(payHash, rt, nil)
|
||||
require.Equal(t, tempErr, err)
|
||||
require.Equal(t, testAttempt, attempt)
|
||||
|
||||
|
@ -5445,7 +5445,7 @@ func (r *rpcServer) dispatchPaymentIntent(
|
||||
} else {
|
||||
var attempt *channeldb.HTLCAttempt
|
||||
attempt, routerErr = r.server.chanRouter.SendToRoute(
|
||||
payIntent.rHash, payIntent.route,
|
||||
payIntent.rHash, payIntent.route, nil,
|
||||
)
|
||||
|
||||
if routerErr == nil {
|
||||
|
Loading…
x
Reference in New Issue
Block a user