From 7a52b6ca5e1b18e9fee831d19fd1798dc9cd0de4 Mon Sep 17 00:00:00 2001 From: Torkel Rogstad Date: Thu, 10 Jul 2025 11:12:45 +0200 Subject: [PATCH 1/4] rpcserver: return consistent grpc NotFound error for GetChaninfo So that we can properly convert this NotFound error back to our `graphdb.ErrEdgeNotFound` error if we are making this call to another node via RPC. --- rpcserver.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rpcserver.go b/rpcserver.go index 7b6a5499e..5a8eede0f 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -6989,7 +6989,10 @@ func (r *rpcServer) GetChanInfo(_ context.Context, default: return nil, fmt.Errorf("specify either chan_id or chan_point") } - if err != nil { + switch { + case errors.Is(err, graphdb.ErrEdgeNotFound): + return nil, status.Error(codes.NotFound, err.Error()) + case err != nil: return nil, err } From 23c3aaac6afe1fbd6103b278d65a3d28f507fa2a Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Thu, 10 Jul 2025 11:15:42 +0200 Subject: [PATCH 2/4] rpcserver: catch extra err case in LookupInvoice For a kvdb backed invoices DB, we sometimes will return the ErrNoInviocesCreated error if no invoices have ever been created on the node. In these cases we should still wrap the returned error in the grpc NotFound code. --- rpcserver.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rpcserver.go b/rpcserver.go index 5a8eede0f..e71723e0f 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -6445,7 +6445,9 @@ func (r *rpcServer) LookupInvoice(ctx context.Context, invoice, err := r.server.invoices.LookupInvoice(ctx, payHash) switch { - case errors.Is(err, invoices.ErrInvoiceNotFound): + case errors.Is(err, invoices.ErrInvoiceNotFound) || + errors.Is(err, invoices.ErrNoInvoicesCreated): + return nil, status.Error(codes.NotFound, err.Error()) case err != nil: return nil, err From c8b04d03f53a5df80afcddcee06eda9e66fab311 Mon Sep 17 00:00:00 2001 From: Torkel Rogstad Date: Thu, 10 Jul 2025 11:16:49 +0200 Subject: [PATCH 3/4] itest+lntest: add grpc NotFound error code test --- itest/list_on_test.go | 4 +++ itest/lnd_misc_test.go | 51 +++++++++++++++++++++++++++++++++++ lntest/rpc/lnd.go | 60 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+) diff --git a/itest/list_on_test.go b/itest/list_on_test.go index 9a1d8ebe3..39b9ed22e 100644 --- a/itest/list_on_test.go +++ b/itest/list_on_test.go @@ -715,6 +715,10 @@ var allTestCases = []*lntest.TestCase{ Name: "peer bootstrapping", TestFunc: testPeerBootstrapping, }, + { + Name: "grpc not found", + TestFunc: testGRPCNotFound, + }, } // appendPrefixed is used to add a prefix to each test name in the subtests diff --git a/itest/lnd_misc_test.go b/itest/lnd_misc_test.go index 4756ca742..66b7fadf4 100644 --- a/itest/lnd_misc_test.go +++ b/itest/lnd_misc_test.go @@ -1,6 +1,7 @@ package itest import ( + "crypto/rand" "encoding/hex" "fmt" "os" @@ -21,7 +22,9 @@ import ( "github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/routing/route" "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" ) // testSphinxReplayPersistence verifies that replayed onion packets are @@ -1344,3 +1347,51 @@ func testSendSelectedCoinsChannelReserve(ht *lntest.HarnessTest) { // Alice should have one reserved UTXO now. ht.AssertNumUTXOs(alice, 1) } + +// testGRPCNotFound verifies that the expected grpc NotFound status code is +// returned when querying various rpc endpoints. +func testGRPCNotFound(ht *lntest.HarnessTest) { + var ( + notFoundErr = codes.NotFound.String() + unknownPub = "0286098b97bc843372b4426d4b276cea9aa2f48f0428d6" + + "f5b66ae101befc14f8b4" + rHash = make([]byte, 32) + ) + unknownPubBytes, err := route.NewVertexFromStr(unknownPub) + require.NoError(ht, err) + + _, err = rand.Read(rHash) + require.NoError(ht, err) + + alice := ht.NewNode("Alice", []string{ + // We add this flag so that we can test the + // LookupHTLCResolutionAssertErr endpoint. + "--store-final-htlc-resolutions", + }).RPC + + alice.GetChanInfoAssertErr(&lnrpc.ChanInfoRequest{ + // Use a random channel ID that doesn't exist. + ChanId: 949807622323240961, + }, notFoundErr) + + alice.GetNodeInfoAssertErr(&lnrpc.NodeInfoRequest{ + // Use a random pubkey that doesn't exist. + PubKey: unknownPub, + }, notFoundErr) + + alice.SendCustomMessageAssertErr(&lnrpc.SendCustomMessageRequest{ + // Use a random pubkey that doesn't exist. + Peer: unknownPubBytes[:], + Data: []byte("test message"), + }, notFoundErr) + + alice.LookupHTLCResolutionAssertErr(&lnrpc.LookupHtlcResolutionRequest{ + ChanId: 400000, + HtlcIndex: 300, + }, notFoundErr) + + alice.LookupInvoiceAssertErr(&lnrpc.PaymentHash{ + // Use a random payment hash that doesn't exist. + RHash: rHash, + }, notFoundErr) +} diff --git a/lntest/rpc/lnd.go b/lntest/rpc/lnd.go index 055b3f29b..e8f359be6 100644 --- a/lntest/rpc/lnd.go +++ b/lntest/rpc/lnd.go @@ -303,6 +303,66 @@ func (h *HarnessRPC) AddInvoiceAssertErr(req *lnrpc.Invoice, errStr string) { require.ErrorContains(h, err, errStr) } +// GetChanInfoAssertErr makes an RPC call to GetChanInfo and asserts an error +// has returned with a specific error message. +func (h *HarnessRPC) GetChanInfoAssertErr(req *lnrpc.ChanInfoRequest, + errStr string) { + + ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout) + defer cancel() + + _, err := h.LN.GetChanInfo(ctxt, req) + require.ErrorContains(h, err, errStr) +} + +// GetNodeInfoAssertErr makes an RPC call to GetNodeInfo and asserts an error +// has returned with a specific error message. +func (h *HarnessRPC) GetNodeInfoAssertErr(req *lnrpc.NodeInfoRequest, + errStr string) { + + ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout) + defer cancel() + + _, err := h.LN.GetNodeInfo(ctxt, req) + require.ErrorContains(h, err, errStr) +} + +// SendCustomMessageAssertErr makes an RPC call to SendCustomMessage and asserts +// an error has returned with a specific error message. +func (h *HarnessRPC) SendCustomMessageAssertErr( + req *lnrpc.SendCustomMessageRequest, errStr string) { + + ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout) + defer cancel() + + _, err := h.LN.SendCustomMessage(ctxt, req) + require.ErrorContains(h, err, errStr) +} + +// LookupInvoiceAssertErr makes an RPC call to LookupInvoice and asserts an +// error has returned with a specific error message. +func (h *HarnessRPC) LookupInvoiceAssertErr(req *lnrpc.PaymentHash, + errStr string) { + + ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout) + defer cancel() + + _, err := h.LN.LookupInvoice(ctxt, req) + require.ErrorContains(h, err, errStr) +} + +// LookupHTLCResolutionAssertErr makes an RPC call to LookupHTLCResolution and +// asserts an error has returned with a specific error message. +func (h *HarnessRPC) LookupHTLCResolutionAssertErr( + req *lnrpc.LookupHtlcResolutionRequest, errStr string) { + + ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout) + defer cancel() + + _, err := h.LN.LookupHtlcResolution(ctxt, req) + require.ErrorContains(h, err, errStr) +} + // AbandonChannel makes a RPC call to AbandonChannel and asserts. func (h *HarnessRPC) AbandonChannel( req *lnrpc.AbandonChannelRequest) *lnrpc.AbandonChannelResponse { From ed7b056ced5b636c93ba4c3294295f6d6aa9c37a Mon Sep 17 00:00:00 2001 From: Torkel Rogstad Date: Thu, 10 Jul 2025 11:19:40 +0200 Subject: [PATCH 4/4] docs: update release notes --- docs/release-notes/release-notes-0.20.0.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/release-notes/release-notes-0.20.0.md b/docs/release-notes/release-notes-0.20.0.md index f61d0a4cd..6a9ef262d 100644 --- a/docs/release-notes/release-notes-0.20.0.md +++ b/docs/release-notes/release-notes-0.20.0.md @@ -114,6 +114,11 @@ circuit. The indices are only available for forwarding events saved after v0.20. [fixed](https://github.com/lightningnetwork/lnd/pull/9572), the affected RPCs are `SubscribeChannelGraph`, `GetChanInfo`, `GetNodeInfo` and `DescribeGraph`. +* [Fix a bug](https://github.com/lightningnetwork/lnd/pull/10064) where the + `GetChanInfo` was not returning the correct gRPC status code in the cases + where the channel is unknown to the node. The same is done for `LookupInvoice` + for the case where the DB is kvdb backed and no invoices have yet been added + to the database. ## lncli Updates @@ -188,5 +193,6 @@ reader of a payment request. * Funyug * Mohamed Awnallah * Pins +* Torkel Rogstad * Yong Yu * Ziggie