routerrpc: pass outgoing channels to probe requests
What changed, and why it matters
This commit fixes a small bug in LND's fee-estimation API: when a user asked to probe a payment route through specific outgoing channels, the list of channels was accidentally dropped before the actual probe was sent. The patch simply passes the channel list through to the probe request. It is a functional bug fix rather than a clear security vulnerability, but it could have caused users to receive fee estimates based on routes they did not intend to use.
Treat as a normal bug fix. Review whether the dropped parameter could have caused users to make routing decisions on unintended channels, and consider a release note, but no urgent security response is indicated by the diff alone.
Security signals we found
Functional bug: user-supplied outgoing channel constraint ignored
Potential operational impact: route-fee estimates may not reflect intended channels
No input validation, auth, or crypto changes in diff
No vendor security framing in commit message or title
Evidence from the diff
In routerrpc, probePaymentRequest already accepted outgoingChanIDs but failed to forward them to probePaymentRequestWithSender. The helper signature is updated to accept outgoingChanIDs []uint64 and pass it into the probePaymentSender callback as req.OutgoingChanIds. A test is updated to assert the IDs are preserved. This is a data-flow fix with no cryptographic or authorization changes visible in the diff.
Changed components
lnrpc/routerrpc/router_server.golnrpc/routerrpc/router_server_test.goRoute fee estimation / probe payment RPC pathInspect captured patch +6 / −3
diff --git a/lnrpc/routerrpc/router_server.go b/lnrpc/routerrpc/router_server.go
index f34da3b..9939c3f 100644
--- a/lnrpc/routerrpc/router_server.go
+++ b/lnrpc/routerrpc/router_server.go
@@ -518,7 +518,8 @@ func (s *Server) probePaymentRequest(ctx context.Context, paymentRequest string,
timeout uint32, outgoingChanIDs []uint64) (*RouteFeeResponse, error) {
return s.probePaymentRequestWithSender(
- ctx, paymentRequest, timeout, s.sendProbePayment,
+ ctx, paymentRequest, timeout, outgoingChanIDs,
+ s.sendProbePayment,
)
}
@@ -533,7 +534,7 @@ type probePaymentSender func(context.Context,
// probePaymentRequest. The sender is injected so tests can inspect generated
// probe requests without invoking the full payment lifecycle.
func (s *Server) probePaymentRequestWithSender(ctx context.Context,
- paymentRequest string, timeout uint32,
+ paymentRequest string, timeout uint32, outgoingChanIDs []uint64,
sendProbePayment probePaymentSender) (*RouteFeeResponse, error) {
payReq, err := zpay32.Decode(
diff --git a/lnrpc/routerrpc/router_server_test.go b/lnrpc/routerrpc/router_server_test.go
index 08bdbe2..84c077c 100644
--- a/lnrpc/routerrpc/router_server_test.go
+++ b/lnrpc/routerrpc/router_server_test.go
@@ -853,6 +853,7 @@ func TestProbePaymentRequestUsesUniqueHashPerLSP(t *testing.T) {
seenHashes := make(map[[32]byte]struct{})
probedDests := make(map[route.Vertex]struct{})
+ outgoingChanIDs := []uint64{123, 456}
expectedCltv := map[route.Vertex]int32{
bobVertex: int32(bobHint.CLTVExpiryDelta),
eveVertex: int32(eveHint.CLTVExpiryDelta),
@@ -875,6 +876,7 @@ func TestProbePaymentRequestUsesUniqueHashPerLSP(t *testing.T) {
probedDests[dest] = struct{}{}
require.Equal(t, expectedCltv[dest], req.FinalCltvDelta)
+ require.Equal(t, outgoingChanIDs, req.OutgoingChanIds)
return &RouteFeeResponse{
RoutingFeeMsat: int64(req.FinalCltvDelta),
@@ -887,7 +889,7 @@ func TestProbePaymentRequestUsesUniqueHashPerLSP(t *testing.T) {
// Act: estimate the route fee with a stubbed probe sender that records
// the generated per-LSP probe requests.
_, err = server.probePaymentRequestWithSender(
- t.Context(), payReq, 1, sendProbe,
+ t.Context(), payReq, 1, outgoingChanIDs, sendProbe,
)
// Assert: all LSPs were probed, each probe had a unique payment hash,
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.