Merge pull request #7467 from yyforyongyu/rename-lookuphtlc

rpcserver: rename `LookupHtlc` to `LookupHtlcResolution`
This commit is contained in:
Olaoluwa Osuntokun 2023-03-01 12:05:09 -08:00 committed by GitHub
commit 530988cadf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 3056 additions and 2917 deletions

View file

@ -85,7 +85,8 @@ of order is also [fixed](https://github.com/lightningnetwork/lnd/pull/7264).
application considers an HTLC settled, but in reality the HTLC has timed out.
Final resolution data will only be available for htlcs that are resolved
after upgrading lnd.
after upgrading lnd. Once resolved, it's final status can be retrieved via
[`LookupHtlcResolution`](https://github.com/lightningnetwork/lnd/pull/7467).
This feature is [opt-in](https://github.com/lightningnetwork/lnd/pull/7341)
via a config flag. The status of the flag is

View file

@ -511,4 +511,8 @@ var allTestCases = []*lntest.TestCase{
Name: "async bidirectional payments",
TestFunc: testBidirectionalAsyncPayments,
},
{
Name: "lookup htlc resolution",
TestFunc: testLookupHtlcResolution,
},
}

71
itest/lnd_htlc_test.go Normal file
View file

@ -0,0 +1,71 @@
package itest
import (
"github.com/btcsuite/btcd/btcutil"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lntest"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// testLookupHtlcResolution checks that `LookupHtlcResolution` returns the
// correct HTLC information.
func testLookupHtlcResolution(ht *lntest.HarnessTest) {
const chanAmt = btcutil.Amount(1000000)
alice := ht.Alice
carol := ht.NewNode("Carol", []string{
"--store-final-htlc-resolutions",
})
ht.EnsureConnected(alice, carol)
// Open a channel between Alice and Carol.
cp := ht.OpenChannel(
alice, carol, lntest.OpenChannelParams{Amt: chanAmt},
)
// Channel should be ready for payments.
const payAmt = 100
// Create an invoice.
invoice := &lnrpc.Invoice{
Memo: "alice to carol htlc lookup",
RPreimage: ht.Random32Bytes(),
Value: payAmt,
}
// Carol adds the invoice to her database.
resp := carol.RPC.AddInvoice(invoice)
// Subscribe the invoice.
stream := carol.RPC.SubscribeSingleInvoice(resp.RHash)
// Alice pays Carol's invoice.
ht.CompletePaymentRequests(alice, []string{resp.PaymentRequest})
// Carol waits until the invoice is settled.
ht.AssertInvoiceState(stream, lnrpc.Invoice_SETTLED)
// Get the channel using the assert function.
//
// TODO(yy): make `ht.OpenChannel` return lnrpc.Channel instead of
// lnrpc.ChannelPoint.
channel := ht.AssertChannelExists(carol, cp)
// Lookup the HTLC and assert the htlc is settled offchain.
req := &lnrpc.LookupHtlcResolutionRequest{
ChanId: channel.ChanId,
HtlcIndex: 0,
}
// Check that Alice will get an error from LookupHtlcResolution.
err := alice.RPC.LookupHtlcResolutionAssertErr(req)
gErr := status.Convert(err)
require.Equal(ht, codes.Unavailable, gErr.Code())
// Check that Carol can get the final htlc info.
finalHTLC := carol.RPC.LookupHtlcResolution(req)
require.True(ht, finalHTLC.Settled, "htlc should be settled")
require.True(ht, finalHTLC.Offchain, "htlc should be Offchain")
}

File diff suppressed because it is too large Load diff

View file

@ -2460,8 +2460,8 @@ func local_request_Lightning_ListAliases_0(ctx context.Context, marshaler runtim
}
func request_Lightning_LookupHtlc_0(ctx context.Context, marshaler runtime.Marshaler, client LightningClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq LookupHtlcRequest
func request_Lightning_LookupHtlcResolution_0(ctx context.Context, marshaler runtime.Marshaler, client LightningClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq LookupHtlcResolutionRequest
var metadata runtime.ServerMetadata
var (
@ -2491,13 +2491,13 @@ func request_Lightning_LookupHtlc_0(ctx context.Context, marshaler runtime.Marsh
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "htlc_index", err)
}
msg, err := client.LookupHtlc(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
msg, err := client.LookupHtlcResolution(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Lightning_LookupHtlc_0(ctx context.Context, marshaler runtime.Marshaler, server LightningServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq LookupHtlcRequest
func local_request_Lightning_LookupHtlcResolution_0(ctx context.Context, marshaler runtime.Marshaler, server LightningServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq LookupHtlcResolutionRequest
var metadata runtime.ServerMetadata
var (
@ -2527,7 +2527,7 @@ func local_request_Lightning_LookupHtlc_0(ctx context.Context, marshaler runtime
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "htlc_index", err)
}
msg, err := server.LookupHtlc(ctx, &protoReq)
msg, err := server.LookupHtlcResolution(ctx, &protoReq)
return msg, metadata, err
}
@ -3864,18 +3864,18 @@ func RegisterLightningHandlerServer(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("GET", pattern_Lightning_LookupHtlc_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
mux.Handle("GET", pattern_Lightning_LookupHtlcResolution_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/lnrpc.Lightning/LookupHtlc", runtime.WithHTTPPathPattern("/v1/htlc/{chan_id}/{htlc_index}"))
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/lnrpc.Lightning/LookupHtlcResolution", runtime.WithHTTPPathPattern("/v1/htlc-resolution/{chan_id}/{htlc_index}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Lightning_LookupHtlc_0(rctx, inboundMarshaler, server, req, pathParams)
resp, md, err := local_request_Lightning_LookupHtlcResolution_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
@ -3883,7 +3883,7 @@ func RegisterLightningHandlerServer(ctx context.Context, mux *runtime.ServeMux,
return
}
forward_Lightning_LookupHtlc_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
forward_Lightning_LookupHtlcResolution_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
@ -5248,23 +5248,23 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("GET", pattern_Lightning_LookupHtlc_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
mux.Handle("GET", pattern_Lightning_LookupHtlcResolution_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req, "/lnrpc.Lightning/LookupHtlc", runtime.WithHTTPPathPattern("/v1/htlc/{chan_id}/{htlc_index}"))
rctx, err := runtime.AnnotateContext(ctx, mux, req, "/lnrpc.Lightning/LookupHtlcResolution", runtime.WithHTTPPathPattern("/v1/htlc-resolution/{chan_id}/{htlc_index}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Lightning_LookupHtlc_0(rctx, inboundMarshaler, client, req, pathParams)
resp, md, err := request_Lightning_LookupHtlcResolution_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Lightning_LookupHtlc_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
forward_Lightning_LookupHtlcResolution_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
@ -5404,7 +5404,7 @@ var (
pattern_Lightning_ListAliases_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "aliases", "list"}, ""))
pattern_Lightning_LookupHtlc_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "htlc", "chan_id", "htlc_index"}, ""))
pattern_Lightning_LookupHtlcResolution_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "htlc-resolution", "chan_id", "htlc_index"}, ""))
)
var (
@ -5540,5 +5540,5 @@ var (
forward_Lightning_ListAliases_0 = runtime.ForwardResponseMessage
forward_Lightning_LookupHtlc_0 = runtime.ForwardResponseMessage
forward_Lightning_LookupHtlcResolution_0 = runtime.ForwardResponseMessage
)

View file

@ -1724,10 +1724,10 @@ func RegisterLightningJSONCallbacks(registry map[string]func(ctx context.Context
callback(string(respBytes), nil)
}
registry["lnrpc.Lightning.LookupHtlc"] = func(ctx context.Context,
registry["lnrpc.Lightning.LookupHtlcResolution"] = func(ctx context.Context,
conn *grpc.ClientConn, reqJSON string, callback func(string, error)) {
req := &LookupHtlcRequest{}
req := &LookupHtlcResolutionRequest{}
err := marshaler.Unmarshal([]byte(reqJSON), req)
if err != nil {
callback("", err)
@ -1735,7 +1735,7 @@ func RegisterLightningJSONCallbacks(registry map[string]func(ctx context.Context
}
client := NewLightningClient(conn)
resp, err := client.LookupHtlc(ctx, req)
resp, err := client.LookupHtlcResolution(ctx, req)
if err != nil {
callback("", err)
return

View file

@ -582,18 +582,26 @@ service Lightning {
*/
rpc ListAliases (ListAliasesRequest) returns (ListAliasesResponse);
rpc LookupHtlc (LookupHtlcRequest) returns (LookupHtlcResponse);
/*
LookupHtlcResolution retrieves a final htlc resolution from the database.
If the htlc has no final resolution yet, a NotFound grpc status code is
returned.
*/
rpc LookupHtlcResolution (LookupHtlcResolutionRequest)
returns (LookupHtlcResolutionResponse);
}
message LookupHtlcRequest {
message LookupHtlcResolutionRequest {
uint64 chan_id = 1;
uint64 htlc_index = 2;
}
message LookupHtlcResponse {
message LookupHtlcResolutionResponse {
// Settled is true is the htlc was settled. If false, the htlc was failed.
bool settled = 1;
// Offchain indicates whether the htlc was resolved off-chain or on-chain.
bool offchain = 2;
}

View file

@ -1601,14 +1601,15 @@
]
}
},
"/v1/htlc/{chan_id}/{htlc_index}": {
"/v1/htlc-resolution/{chan_id}/{htlc_index}": {
"get": {
"operationId": "Lightning_LookupHtlc",
"summary": "LookupHtlcResolution retrieves a final htlc resolution from the database.\nIf the htlc has no final resolution yet, a NotFound grpc status code is\nreturned.",
"operationId": "Lightning_LookupHtlcResolution",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/lnrpcLookupHtlcResponse"
"$ref": "#/definitions/lnrpcLookupHtlcResolutionResponse"
}
},
"default": {
@ -5436,14 +5437,16 @@
}
}
},
"lnrpcLookupHtlcResponse": {
"lnrpcLookupHtlcResolutionResponse": {
"type": "object",
"properties": {
"settled": {
"type": "boolean"
"type": "boolean",
"description": "Settled is true is the htlc was settled. If false, the htlc was failed."
},
"offchain": {
"type": "boolean"
"type": "boolean",
"description": "Offchain indicates whether the htlc was resolved off-chain or on-chain."
}
}
},

View file

@ -165,5 +165,5 @@ http:
get: "/v1/custommessage/subscribe"
- selector: lnrpc.Lightning.ListAliases
get: "/v1/aliases/list"
- selector: lnrpc.Lightning.LookupHtlc
get: "/v1/htlc/{chan_id}/{htlc_index}"
- selector: lnrpc.Lightning.LookupHtlcResolution
get: "/v1/htlc-resolution/{chan_id}/{htlc_index}"

View file

@ -402,7 +402,10 @@ type LightningClient interface {
// their confirmed SCID (if it exists) and/or the base SCID (in the case of
// zero conf).
ListAliases(ctx context.Context, in *ListAliasesRequest, opts ...grpc.CallOption) (*ListAliasesResponse, error)
LookupHtlc(ctx context.Context, in *LookupHtlcRequest, opts ...grpc.CallOption) (*LookupHtlcResponse, error)
// LookupHtlcResolution retrieves a final htlc resolution from the database.
// If the htlc has no final resolution yet, a NotFound grpc status code is
// returned.
LookupHtlcResolution(ctx context.Context, in *LookupHtlcResolutionRequest, opts ...grpc.CallOption) (*LookupHtlcResolutionResponse, error)
}
type lightningClient struct {
@ -1304,9 +1307,9 @@ func (c *lightningClient) ListAliases(ctx context.Context, in *ListAliasesReques
return out, nil
}
func (c *lightningClient) LookupHtlc(ctx context.Context, in *LookupHtlcRequest, opts ...grpc.CallOption) (*LookupHtlcResponse, error) {
out := new(LookupHtlcResponse)
err := c.cc.Invoke(ctx, "/lnrpc.Lightning/LookupHtlc", in, out, opts...)
func (c *lightningClient) LookupHtlcResolution(ctx context.Context, in *LookupHtlcResolutionRequest, opts ...grpc.CallOption) (*LookupHtlcResolutionResponse, error) {
out := new(LookupHtlcResolutionResponse)
err := c.cc.Invoke(ctx, "/lnrpc.Lightning/LookupHtlcResolution", in, out, opts...)
if err != nil {
return nil, err
}
@ -1701,7 +1704,10 @@ type LightningServer interface {
// their confirmed SCID (if it exists) and/or the base SCID (in the case of
// zero conf).
ListAliases(context.Context, *ListAliasesRequest) (*ListAliasesResponse, error)
LookupHtlc(context.Context, *LookupHtlcRequest) (*LookupHtlcResponse, error)
// LookupHtlcResolution retrieves a final htlc resolution from the database.
// If the htlc has no final resolution yet, a NotFound grpc status code is
// returned.
LookupHtlcResolution(context.Context, *LookupHtlcResolutionRequest) (*LookupHtlcResolutionResponse, error)
mustEmbedUnimplementedLightningServer()
}
@ -1907,8 +1913,8 @@ func (UnimplementedLightningServer) SubscribeCustomMessages(*SubscribeCustomMess
func (UnimplementedLightningServer) ListAliases(context.Context, *ListAliasesRequest) (*ListAliasesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListAliases not implemented")
}
func (UnimplementedLightningServer) LookupHtlc(context.Context, *LookupHtlcRequest) (*LookupHtlcResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method LookupHtlc not implemented")
func (UnimplementedLightningServer) LookupHtlcResolution(context.Context, *LookupHtlcResolutionRequest) (*LookupHtlcResolutionResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method LookupHtlcResolution not implemented")
}
func (UnimplementedLightningServer) mustEmbedUnimplementedLightningServer() {}
@ -3170,20 +3176,20 @@ func _Lightning_ListAliases_Handler(srv interface{}, ctx context.Context, dec fu
return interceptor(ctx, in, info, handler)
}
func _Lightning_LookupHtlc_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(LookupHtlcRequest)
func _Lightning_LookupHtlcResolution_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(LookupHtlcResolutionRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(LightningServer).LookupHtlc(ctx, in)
return srv.(LightningServer).LookupHtlcResolution(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/lnrpc.Lightning/LookupHtlc",
FullMethod: "/lnrpc.Lightning/LookupHtlcResolution",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LightningServer).LookupHtlc(ctx, req.(*LookupHtlcRequest))
return srv.(LightningServer).LookupHtlcResolution(ctx, req.(*LookupHtlcResolutionRequest))
}
return interceptor(ctx, in, info, handler)
}
@ -3408,8 +3414,8 @@ var Lightning_ServiceDesc = grpc.ServiceDesc{
Handler: _Lightning_ListAliases_Handler,
},
{
MethodName: "LookupHtlc",
Handler: _Lightning_LookupHtlc_Handler,
MethodName: "LookupHtlcResolution",
Handler: _Lightning_LookupHtlcResolution_Handler,
},
},
Streams: []grpc.StreamDesc{

View file

@ -412,6 +412,10 @@ func (h *HarnessTest) shutdownNodes(skipStandby bool) {
return h.manager.shutdownNode(node)
}, DefaultTimeout)
if err == nil {
continue
}
// Instead of returning the error, we will log it instead. This
// is needed so other nodes can continue their shutdown
// processes.

View file

@ -696,3 +696,33 @@ func (h *HarnessRPC) GetChanInfo(
return resp
}
// LookupHtlcResolution makes a RPC call to the node's LookupHtlcResolution and
// returns the response.
//
//nolint:lll
func (h *HarnessRPC) LookupHtlcResolution(
req *lnrpc.LookupHtlcResolutionRequest) *lnrpc.LookupHtlcResolutionResponse {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
resp, err := h.LN.LookupHtlcResolution(ctxt, req)
h.NoError(err, "LookupHtlcResolution")
return resp
}
// LookupHtlcResolutionAssertErr makes a RPC call to the node's
// LookupHtlcResolution and asserts an RPC error is returned.
func (h *HarnessRPC) LookupHtlcResolutionAssertErr(
req *lnrpc.LookupHtlcResolutionRequest) error {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
_, err := h.LN.LookupHtlcResolution(ctxt, req)
require.Error(h, err, "expected an error")
return err
}

View file

@ -577,7 +577,7 @@ func MainRPCServerPermissions() map[string][]bakery.Op {
Entity: "offchain",
Action: "read",
}},
"/lnrpc.Lightning/LookupHtlc": {{
"/lnrpc.Lightning/LookupHtlcResolution": {{
Entity: "offchain",
Action: "read",
}},
@ -3937,10 +3937,17 @@ func (r *rpcServer) ClosedChannels(ctx context.Context,
return resp, nil
}
// LookupHtlc retrieves a final htlc resolution from the database. If the htlc
// has no final resolution yet, a NotFound grpc status code is returned.
func (r *rpcServer) LookupHtlc(ctx context.Context,
in *lnrpc.LookupHtlcRequest) (*lnrpc.LookupHtlcResponse, error) {
// LookupHtlcResolution retrieves a final htlc resolution from the database. If
// the htlc has no final resolution yet, a NotFound grpc status code is
// returned.
func (r *rpcServer) LookupHtlcResolution(
ctx context.Context, in *lnrpc.LookupHtlcResolutionRequest) (
*lnrpc.LookupHtlcResolutionResponse, error) {
if !r.cfg.StoreFinalHtlcResolutions {
return nil, status.Error(codes.Unavailable, "cannot lookup "+
"with flag --store-final-htlc-resolutions=false")
}
chanID := lnwire.NewShortChanIDFromInt(in.ChanId)
@ -3953,7 +3960,7 @@ func (r *rpcServer) LookupHtlc(ctx context.Context,
return nil, err
}
return &lnrpc.LookupHtlcResponse{
return &lnrpc.LookupHtlcResolutionResponse{
Settled: info.Settled,
Offchain: info.Offchain,
}, nil