itest: migrate deprecated lnrpc Send* calls to routerrpc V2
What changed, and why it matters
This commit only changes integration tests for the Lightning Network Daemon (LND). It removes old test helper functions that called deprecated SendToRoute RPC methods and updates the tests to use the newer routerrpc.SendToRouteV2 API. No production code, user-facing RPC behavior, or security-sensitive logic is modified. It is a test-maintenance change with no direct security relevance.
No security action required. Treat as ordinary test refactoring. Optionally verify that the migrated tests still exercise the same failure paths (AmountBelowMinimum, UnknownNextPeer) and that SendToRouteV2 coverage is equivalent to the removed sync/stream variants.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff modifies four files under itest/ and lntest/, all part of the integration-test harness. It deletes the SendToRoute and SendToRouteSync RPC helpers, removes the ReceiveSendToRouteUpdate stream helper, and rewrites two test files (lnd_routing_test.go and lnd_channel_policy_test.go) to call routerrpc.SendToRouteV2 instead of the deprecated lnrpc SendToRoute/SendToRouteSync streaming APIs. Assertions are updated to check HTLCAttempt.Failure / Failure.Code rather than PaymentError strings. No changes are made to the actual LND node implementation, RPC server, routing logic, or cryptographic handling.
Changed components
itest/lnd_channel_policy_test.goitest/lnd_routing_test.golntest/harness_assertion.golntest/rpc/lnd.goInspect captured patch +49 / −197
diff --git a/itest/lnd_channel_policy_test.go b/itest/lnd_channel_policy_test.go
index 7def317..67f55d8 100644
--- a/itest/lnd_channel_policy_test.go
+++ b/itest/lnd_channel_policy_test.go
@@ -170,29 +170,28 @@ func testUpdateChannelPolicy(ht *lntest.HarnessTest) {
routes.Routes[0].Hops[1].AmtToForward = amtSat
routes.Routes[0].Hops[1].AmtToForwardMsat = amtMSat
- // Send the payment with the modified value.
- alicePayStream := alice.RPC.SendToRoute()
-
- sendReq := &lnrpc.SendToRouteRequest{
+ // Send the payment with the modified value and expect a failure because
+ // the amount is below the minimum HTLC size.
+ sendReq := &routerrpc.SendToRouteRequest{
PaymentHash: resp.RHash,
Route: routes.Routes[0],
}
- err := alicePayStream.Send(sendReq)
- require.NoError(ht, err, "unable to send payment")
-
- // We expect this payment to fail, and that the min_htlc value is
- // communicated back to us, since the attempted HTLC value was too low.
- sendResp, err := ht.ReceiveSendToRouteUpdate(alicePayStream)
- require.NoError(ht, err, "unable to receive payment stream")
-
- // Expected as part of the error message.
- substrs := []string{
- "AmountBelowMinimum",
- "HtlcMinimumMsat: (lnwire.MilliSatoshi) 5000 mSAT",
- }
- for _, s := range substrs {
- require.Contains(ht, sendResp.PaymentError, s)
- }
+ sendResp := alice.RPC.SendToRouteV2(sendReq)
+ require.NotNil(ht, sendResp.Failure, "expected payment failure")
+ require.Equal(
+ ht, lnrpc.Failure_AMOUNT_BELOW_MINIMUM, sendResp.Failure.Code,
+ )
+
+ // The failure should carry the advertised min HTLC value so that
+ // callers can react to the channel policy.
+ require.NotNil(
+ ht, sendResp.Failure.ChannelUpdate,
+ "expected channel update in failure",
+ )
+ require.Equal(
+ ht, uint64(customMinHtlc),
+ sendResp.Failure.ChannelUpdate.HtlcMinimumMsat,
+ )
// Make sure sending using the original value succeeds.
payAmt = btcutil.Amount(5)
@@ -213,17 +212,12 @@ func testUpdateChannelPolicy(ht *lntest.HarnessTest) {
TotalAmtMsat: amtMSat,
}
- sendReq = &lnrpc.SendToRouteRequest{
+ sendReq = &routerrpc.SendToRouteRequest{
PaymentHash: resp.RHash,
Route: route,
}
-
- err = alicePayStream.Send(sendReq)
- require.NoError(ht, err, "unable to send payment")
-
- sendResp, err = ht.ReceiveSendToRouteUpdate(alicePayStream)
- require.NoError(ht, err, "unable to receive payment stream")
- require.Empty(ht, sendResp.PaymentError, "expected payment to succeed")
+ sendResp = alice.RPC.SendToRouteV2(sendReq)
+ require.Nil(ht, sendResp.Failure, "expected payment to succeed")
// With our little cluster set up, we'll update the outbound fees and
// the max htlc size for the Bob side of the Alice->Bob channel, and
diff --git a/itest/lnd_routing_test.go b/itest/lnd_routing_test.go
index 9679f88..b68f796 100644
--- a/itest/lnd_routing_test.go
+++ b/itest/lnd_routing_test.go
@@ -22,41 +22,20 @@ import (
var sendToRouteTestCases = []*lntest.TestCase{
{
- Name: "single hop with sync",
- TestFunc: func(ht *lntest.HarnessTest) {
- // useStream: false, routerrpc: false.
- testSingleHopSendToRouteCase(ht, false, false)
- },
- },
- {
- Name: "single hop with stream",
- TestFunc: func(ht *lntest.HarnessTest) {
- // useStream: true, routerrpc: false.
- testSingleHopSendToRouteCase(ht, true, false)
- },
- },
- {
- Name: "single hop with v2",
- TestFunc: func(ht *lntest.HarnessTest) {
- // useStream: false, routerrpc: true.
- testSingleHopSendToRouteCase(ht, false, true)
- },
+ Name: "single hop",
+ TestFunc: testSingleHopSendToRoute,
},
}
-// testSingleHopSendToRouteCase tests that payments are properly processed
-// through a provided route with a single hop. We'll create the following
-// network topology:
+// testSingleHopSendToRoute tests that payments are properly processed through
+// a provided route with a single hop. We'll create the following network
+// topology:
//
// Carol --100k--> Dave
//
// We'll query the daemon for routes from Carol to Dave and then send payments
-// by feeding the route back into the various SendToRoute RPC methods. Here we
-// test all three SendToRoute endpoints, forcing each to perform both a regular
-// payment and an MPP payment.
-func testSingleHopSendToRouteCase(ht *lntest.HarnessTest,
- useStream, useRPC bool) {
-
+// by feeding the route back into SendToRouteV2.
+func testSingleHopSendToRoute(ht *lntest.HarnessTest) {
const chanAmt = btcutil.Amount(100000)
const paymentAmtSat = 1000
const numPayments = 5
@@ -97,8 +76,6 @@ func testSingleHopSendToRouteCase(ht *lntest.HarnessTest,
ht.WaitForNodeBlockHeight(carol, minerHeight)
ht.WaitForNodeBlockHeight(dave, minerHeight)
- // Query for routes to pay from Carol to Dave using the default CLTV
- // config.
routesReq := &lnrpc.QueryRoutesRequest{
PubKey: dave.PubKeyStr,
Amt: paymentAmtSat,
@@ -108,82 +85,28 @@ func testSingleHopSendToRouteCase(ht *lntest.HarnessTest,
// There should only be one route to try, so take the first item.
r := routes.Routes[0]
- // Construct a closure that will set MPP fields on the route, which
- // allows us to test MPP payments.
- setMPPFields := func(i int) {
+ for i, rHash := range rHashes {
+ // Set the MPP record on the last hop with the payment addr from
+ // the corresponding invoice so the receiver can accept the
+ // HTLC.
hop := r.Hops[len(r.Hops)-1]
hop.TlvPayload = true
hop.MppRecord = &lnrpc.MPPRecord{
PaymentAddr: payAddrs[i],
TotalAmtMsat: paymentAmtSat * 1000,
}
- }
- // Construct closures for each of the payment types covered:
- // - main rpc server sync
- // - main rpc server streaming
- // - routerrpc server sync
- sendToRouteSync := func() {
- for i, rHash := range rHashes {
- setMPPFields(i)
-
- sendReq := &lnrpc.SendToRouteRequest{
- PaymentHash: rHash,
- Route: r,
- }
- resp := carol.RPC.SendToRouteSync(sendReq)
- require.Emptyf(ht, resp.PaymentError,
- "received payment error from %s: %v",
- carol.Name(), resp.PaymentError)
- }
- }
- sendToRouteStream := func() {
- alicePayStream := carol.RPC.SendToRoute()
-
- for i, rHash := range rHashes {
- setMPPFields(i)
-
- sendReq := &lnrpc.SendToRouteRequest{
- PaymentHash: rHash,
- Route: routes.Routes[0],
- }
- err := alicePayStream.Send(sendReq)
- require.NoError(ht, err, "unable to send payment")
-
- resp, err := ht.ReceiveSendToRouteUpdate(alicePayStream)
- require.NoError(ht, err, "unable to receive stream")
- require.Emptyf(ht, resp.PaymentError,
- "received payment error from %s: %v",
- carol.Name(), resp.PaymentError)
- }
- }
- sendToRouteRouterRPC := func() {
- for i, rHash := range rHashes {
- setMPPFields(i)
-
- sendReq := &routerrpc.SendToRouteRequest{
- PaymentHash: rHash,
- Route: r,
- }
- resp := carol.RPC.SendToRouteV2(sendReq)
- require.Nilf(ht, resp.Failure, "received payment "+
- "error from %s", carol.Name())
+ // Dispatch the payment along the prepared route and assert that
+ // no failure was returned.
+ sendReq := &routerrpc.SendToRouteRequest{
+ PaymentHash: rHash,
+ Route: r,
}
- }
-
- // Using Carol as the node as the source, send the payments
- // synchronously via the routerrpc's SendToRoute, or via the main RPC
- // server's SendToRoute streaming or sync calls.
- switch {
- case !useRPC && useStream:
- sendToRouteStream()
- case !useRPC && !useStream:
- sendToRouteSync()
- case useRPC && !useStream:
- sendToRouteRouterRPC()
- default:
- require.Fail(ht, "routerrpc does not support "+
- "streaming send_to_route")
+ resp := carol.RPC.SendToRouteV2(sendReq)
+ require.Nilf(
+ ht, resp.Failure, "received payment error from %s",
+ carol.Name(),
+ )
}
// Verify that the payment's from Carol's PoV have the correct payment
@@ -431,22 +354,15 @@ func testSendToRouteErrorPropagation(ht *lntest.HarnessTest) {
resp := bob.RPC.AddInvoice(invoice)
rHash := resp.RHash
- // Using Alice as the source, pay to the invoice from Bob.
- alicePayStream := alice.RPC.SendToRoute()
-
- sendReq := &lnrpc.SendToRouteRequest{
+ // Using Alice as the source, send to the invoice from Bob via a fake
+ // route - we expect this to fail with UnknownNextPeer.
+ sendReq := &routerrpc.SendToRouteRequest{
PaymentHash: rHash,
Route: fakeRoute.Routes[0],
}
- err := alicePayStream.Send(sendReq)
- require.NoError(ht, err, "unable to send payment")
-
- // At this place we should get an rpc error with notification
- // that edge is not found on hop(0)
- event, err := ht.ReceiveSendToRouteUpdate(alicePayStream)
- require.NoError(ht, err, "payment stream has been closed but fake "+
- "route has consumed")
- require.Contains(ht, event.PaymentError, "UnknownNextPeer")
+ event := alice.RPC.SendToRouteV2(sendReq)
+ require.NotNil(ht, event.Failure, "expected payment failure")
+ require.Equal(ht, lnrpc.Failure_UNKNOWN_NEXT_PEER, event.Failure.Code)
}
// testPrivateChannels tests that a private channel can be used for
diff --git a/lntest/harness_assertion.go b/lntest/harness_assertion.go
index e2fcdeb..b757980 100644
--- a/lntest/harness_assertion.go
+++ b/lntest/harness_assertion.go
@@ -2552,38 +2552,6 @@ func (h *HarnessTest) AssertNumInvoices(hn *node.HarnessNode,
return invoices
}
-// ReceiveSendToRouteUpdate waits until a message is received on the
-// SendToRoute client stream or the timeout is reached.
-func (h *HarnessTest) ReceiveSendToRouteUpdate(
- stream rpc.SendToRouteClient) (*lnrpc.SendResponse, error) {
-
- chanMsg := make(chan *lnrpc.SendResponse, 1)
- errChan := make(chan error, 1)
- go func() {
- // Consume one message. This will block until the message is
- // received.
- resp, err := stream.Recv()
- if err != nil {
- errChan <- err
-
- return
- }
- chanMsg <- resp
- }()
-
- select {
- case <-time.After(DefaultTimeout):
- require.Fail(h, "timeout", "timeout waiting for send resp")
- return nil, nil
-
- case err := <-errChan:
- return nil, err
-
- case updateMsg := <-chanMsg:
- return updateMsg, nil
- }
-}
-
// AssertInvoiceEqual asserts that two lnrpc.Invoices are equivalent. A custom
// comparison function is defined for these tests, since proto message returned
// from unary and streaming RPCs (as of protobuf 1.23.0 and grpc 1.29.1) aren't
diff --git a/lntest/rpc/lnd.go b/lntest/rpc/lnd.go
index 946b37b..a9dc742 100644
--- a/lntest/rpc/lnd.go
+++ b/lntest/rpc/lnd.go
@@ -560,32 +560,6 @@ func (h *HarnessRPC) QueryRoutes(
return routes
}
-type SendToRouteClient lnrpc.Lightning_SendToRouteClient
-
-// SendToRoute makes a RPC call to SendToRoute and asserts.
-func (h *HarnessRPC) SendToRoute() SendToRouteClient {
- // SendToRoute needs to have the context alive for the entire test case
- // as the returned client will be used for send and receive payment
- // stream. Thus we use runCtx here instead of a timeout context.
- client, err := h.LN.SendToRoute(h.runCtx)
- h.NoError(err, "SendToRoute")
-
- return client
-}
-
-// SendToRouteSync makes a RPC call to SendToRouteSync and asserts.
-func (h *HarnessRPC) SendToRouteSync(
- req *lnrpc.SendToRouteRequest) *lnrpc.SendResponse {
-
- ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
- defer cancel()
-
- resp, err := h.LN.SendToRouteSync(ctxt, req)
- h.NoError(err, "SendToRouteSync")
-
- return resp
-}
-
// UpdateChannelPolicy makes a RPC call to UpdateChannelPolicy and asserts.
func (h *HarnessRPC) UpdateChannelPolicy(
req *lnrpc.PolicyUpdateRequest) *lnrpc.PolicyUpdateResponse {
Why this scored 15/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.