mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-26 01:33:02 +01:00
rpcserver: add LookupHtlc call
This commit is contained in:
parent
511fb00777
commit
3a89a84744
@ -1473,6 +1473,12 @@ func TestFinalHtlcs(t *testing.T) {
|
||||
TxPosition: 3,
|
||||
}
|
||||
|
||||
// Test unknown htlc lookup.
|
||||
const unknownHtlcID = 999
|
||||
|
||||
_, err = cdb.LookupFinalHtlc(chanID, unknownHtlcID)
|
||||
require.ErrorIs(t, err, ErrHtlcUnknown)
|
||||
|
||||
// Test offchain final htlcs.
|
||||
const offchainHtlcID = 1
|
||||
|
||||
@ -1489,9 +1495,23 @@ func TestFinalHtlcs(t *testing.T) {
|
||||
}, func() {})
|
||||
require.NoError(t, err)
|
||||
|
||||
info, err := cdb.LookupFinalHtlc(chanID, offchainHtlcID)
|
||||
require.NoError(t, err)
|
||||
require.True(t, info.Settled)
|
||||
require.True(t, info.Offchain)
|
||||
|
||||
// Test onchain final htlcs.
|
||||
const onchainHtlcID = 2
|
||||
|
||||
err = cdb.PutOnchainFinalHtlcOutcome(chanID, onchainHtlcID, true)
|
||||
require.NoError(t, err)
|
||||
|
||||
info, err = cdb.LookupFinalHtlc(chanID, onchainHtlcID)
|
||||
require.NoError(t, err)
|
||||
require.True(t, info.Settled)
|
||||
require.False(t, info.Offchain)
|
||||
|
||||
// Test unknown htlc lookup for existing channel.
|
||||
_, err = cdb.LookupFinalHtlc(chanID, unknownHtlcID)
|
||||
require.ErrorIs(t, err, ErrHtlcUnknown)
|
||||
}
|
||||
|
@ -39,6 +39,16 @@ var (
|
||||
// ErrDryRunMigrationOK signals that a migration executed successful,
|
||||
// but we intentionally did not commit the result.
|
||||
ErrDryRunMigrationOK = errors.New("dry run migration successful")
|
||||
|
||||
// ErrFinalHtlcsBucketNotFound signals that the top-level final htlcs
|
||||
// bucket does not exist.
|
||||
ErrFinalHtlcsBucketNotFound = errors.New("final htlcs bucket not " +
|
||||
"found")
|
||||
|
||||
// ErrFinalChannelBucketNotFound signals that the channel bucket for
|
||||
// final htlc outcomes does not exist.
|
||||
ErrFinalChannelBucketNotFound = errors.New("final htlcs channel " +
|
||||
"bucket not found")
|
||||
)
|
||||
|
||||
// migration is a function which takes a prior outdated version of the database
|
||||
@ -1652,6 +1662,80 @@ func (c *ChannelStateDB) FetchHistoricalChannel(outPoint *wire.OutPoint) (
|
||||
return channel, nil
|
||||
}
|
||||
|
||||
func fetchFinalHtlcsBucket(tx kvdb.RTx,
|
||||
chanID lnwire.ShortChannelID) (kvdb.RBucket, error) {
|
||||
|
||||
finalHtlcsBucket := tx.ReadBucket(finalHtlcsBucket)
|
||||
if finalHtlcsBucket == nil {
|
||||
return nil, ErrFinalHtlcsBucketNotFound
|
||||
}
|
||||
|
||||
var chanIDBytes [8]byte
|
||||
byteOrder.PutUint64(chanIDBytes[:], chanID.ToUint64())
|
||||
|
||||
chanBucket := finalHtlcsBucket.NestedReadBucket(chanIDBytes[:])
|
||||
if chanBucket == nil {
|
||||
return nil, ErrFinalChannelBucketNotFound
|
||||
}
|
||||
|
||||
return chanBucket, nil
|
||||
}
|
||||
|
||||
var ErrHtlcUnknown = errors.New("htlc unknown")
|
||||
|
||||
// LookupFinalHtlc retrieves a final htlc resolution from the database. If the
|
||||
// htlc has no final resolution yet, ErrHtlcUnknown is returned.
|
||||
func (c *ChannelStateDB) LookupFinalHtlc(chanID lnwire.ShortChannelID,
|
||||
htlcIndex uint64) (*FinalHtlcInfo, error) {
|
||||
|
||||
var idBytes [8]byte
|
||||
byteOrder.PutUint64(idBytes[:], htlcIndex)
|
||||
|
||||
var settledByte byte
|
||||
|
||||
err := kvdb.View(c.backend, func(tx kvdb.RTx) error {
|
||||
finalHtlcsBucket, err := fetchFinalHtlcsBucket(
|
||||
tx, chanID,
|
||||
)
|
||||
switch {
|
||||
case errors.Is(err, ErrFinalHtlcsBucketNotFound):
|
||||
fallthrough
|
||||
|
||||
case errors.Is(err, ErrFinalChannelBucketNotFound):
|
||||
return ErrHtlcUnknown
|
||||
|
||||
case err != nil:
|
||||
return fmt.Errorf("cannot fetch final htlcs bucket: %w",
|
||||
err)
|
||||
}
|
||||
|
||||
value := finalHtlcsBucket.Get(idBytes[:])
|
||||
if value == nil {
|
||||
return ErrHtlcUnknown
|
||||
}
|
||||
|
||||
if len(value) != 1 {
|
||||
return errors.New("unexpected final htlc value length")
|
||||
}
|
||||
|
||||
settledByte = value[0]
|
||||
|
||||
return nil
|
||||
}, func() {
|
||||
settledByte = 0
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := FinalHtlcInfo{
|
||||
Settled: settledByte&byte(FinalHtlcSettledBit) != 0,
|
||||
Offchain: settledByte&byte(FinalHtlcOffchainBit) != 0,
|
||||
}
|
||||
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
// PutOnchainFinalHtlcOutcome stores the final on-chain outcome of an htlc in
|
||||
// the database.
|
||||
func (c *ChannelStateDB) PutOnchainFinalHtlcOutcome(
|
||||
|
@ -51,6 +51,16 @@
|
||||
[expose tlv data](https://github.com/lightningnetwork/lnd/pull/7085) that is
|
||||
broadcast over the gossip network.
|
||||
|
||||
* [Add new HTLC notifier event and lookup
|
||||
RPC](https://github.com/lightningnetwork/lnd/pull/6517) for the final
|
||||
settlement of incoming HTLCs. This allows applications to wait for the HTLC to
|
||||
actually disappear from all valid commitment transactions, rather than assume
|
||||
that it will. With the new extensions, situations can be avoided where the
|
||||
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.
|
||||
|
||||
## Wallet
|
||||
|
||||
* [Allows Taproot public keys and tap scripts to be imported as watch-only
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2460,6 +2460,78 @@ 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
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
var (
|
||||
val string
|
||||
ok bool
|
||||
err error
|
||||
_ = err
|
||||
)
|
||||
|
||||
val, ok = pathParams["chan_id"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "chan_id")
|
||||
}
|
||||
|
||||
protoReq.ChanId, err = runtime.Uint64(val)
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "chan_id", err)
|
||||
}
|
||||
|
||||
val, ok = pathParams["htlc_index"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "htlc_index")
|
||||
}
|
||||
|
||||
protoReq.HtlcIndex, err = runtime.Uint64(val)
|
||||
if err != nil {
|
||||
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))
|
||||
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
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
var (
|
||||
val string
|
||||
ok bool
|
||||
err error
|
||||
_ = err
|
||||
)
|
||||
|
||||
val, ok = pathParams["chan_id"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "chan_id")
|
||||
}
|
||||
|
||||
protoReq.ChanId, err = runtime.Uint64(val)
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "chan_id", err)
|
||||
}
|
||||
|
||||
val, ok = pathParams["htlc_index"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "htlc_index")
|
||||
}
|
||||
|
||||
protoReq.HtlcIndex, err = runtime.Uint64(val)
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "htlc_index", err)
|
||||
}
|
||||
|
||||
msg, err := server.LookupHtlc(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
// RegisterLightningHandlerServer registers the http handlers for service Lightning to "mux".
|
||||
// UnaryRPC :call LightningServer directly.
|
||||
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
|
||||
@ -3792,6 +3864,29 @@ 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) {
|
||||
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}"))
|
||||
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)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
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()...)
|
||||
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -5153,6 +5248,26 @@ 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) {
|
||||
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}"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_Lightning_LookupHtlc_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()...)
|
||||
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -5288,6 +5403,8 @@ var (
|
||||
pattern_Lightning_SubscribeCustomMessages_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "custommessage", "subscribe"}, ""))
|
||||
|
||||
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"}, ""))
|
||||
)
|
||||
|
||||
var (
|
||||
@ -5422,4 +5539,6 @@ var (
|
||||
forward_Lightning_SubscribeCustomMessages_0 = runtime.ForwardResponseStream
|
||||
|
||||
forward_Lightning_ListAliases_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_Lightning_LookupHtlc_0 = runtime.ForwardResponseMessage
|
||||
)
|
||||
|
@ -1723,4 +1723,29 @@ func RegisterLightningJSONCallbacks(registry map[string]func(ctx context.Context
|
||||
}
|
||||
callback(string(respBytes), nil)
|
||||
}
|
||||
|
||||
registry["lnrpc.Lightning.LookupHtlc"] = func(ctx context.Context,
|
||||
conn *grpc.ClientConn, reqJSON string, callback func(string, error)) {
|
||||
|
||||
req := &LookupHtlcRequest{}
|
||||
err := marshaler.Unmarshal([]byte(reqJSON), req)
|
||||
if err != nil {
|
||||
callback("", err)
|
||||
return
|
||||
}
|
||||
|
||||
client := NewLightningClient(conn)
|
||||
resp, err := client.LookupHtlc(ctx, req)
|
||||
if err != nil {
|
||||
callback("", err)
|
||||
return
|
||||
}
|
||||
|
||||
respBytes, err := marshaler.Marshal(resp)
|
||||
if err != nil {
|
||||
callback("", err)
|
||||
return
|
||||
}
|
||||
callback(string(respBytes), nil)
|
||||
}
|
||||
}
|
||||
|
@ -577,6 +577,20 @@ service Lightning {
|
||||
zero conf).
|
||||
*/
|
||||
rpc ListAliases (ListAliasesRequest) returns (ListAliasesResponse);
|
||||
|
||||
rpc LookupHtlc (LookupHtlcRequest) returns (LookupHtlcResponse);
|
||||
}
|
||||
|
||||
message LookupHtlcRequest {
|
||||
uint64 chan_id = 1;
|
||||
|
||||
uint64 htlc_index = 2;
|
||||
}
|
||||
|
||||
message LookupHtlcResponse {
|
||||
bool settled = 1;
|
||||
|
||||
bool offchain = 2;
|
||||
}
|
||||
|
||||
message SubscribeCustomMessagesRequest {
|
||||
|
@ -1593,6 +1593,44 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/htlc/{chan_id}/{htlc_index}": {
|
||||
"get": {
|
||||
"operationId": "Lightning_LookupHtlc",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/lnrpcLookupHtlcResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "chan_id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"format": "uint64"
|
||||
},
|
||||
{
|
||||
"name": "htlc_index",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"format": "uint64"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Lightning"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/invoice/{r_hash_str}": {
|
||||
"get": {
|
||||
"summary": "lncli: `lookupinvoice`\nLookupInvoice attempts to look up an invoice according to its payment hash.\nThe passed payment hash *must* be exactly 32 bytes, if not, an error is\nreturned.",
|
||||
@ -5351,6 +5389,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"lnrpcLookupHtlcResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"settled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"offchain": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"lnrpcMPPRecord": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -165,3 +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}"
|
||||
|
@ -398,6 +398,7 @@ 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)
|
||||
}
|
||||
|
||||
type lightningClient struct {
|
||||
@ -1299,6 +1300,15 @@ 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...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// LightningServer is the server API for Lightning service.
|
||||
// All implementations must embed UnimplementedLightningServer
|
||||
// for forward compatibility
|
||||
@ -1683,6 +1693,7 @@ 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)
|
||||
mustEmbedUnimplementedLightningServer()
|
||||
}
|
||||
|
||||
@ -1888,6 +1899,9 @@ 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) mustEmbedUnimplementedLightningServer() {}
|
||||
|
||||
// UnsafeLightningServer may be embedded to opt out of forward compatibility for this service.
|
||||
@ -3148,6 +3162,24 @@ 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)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(LightningServer).LookupHtlc(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/lnrpc.Lightning/LookupHtlc",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(LightningServer).LookupHtlc(ctx, req.(*LookupHtlcRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// Lightning_ServiceDesc is the grpc.ServiceDesc for Lightning service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
@ -3367,6 +3399,10 @@ var Lightning_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "ListAliases",
|
||||
Handler: _Lightning_ListAliases_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "LookupHtlc",
|
||||
Handler: _Lightning_LookupHtlc_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
|
26
rpcserver.go
26
rpcserver.go
@ -577,6 +577,10 @@ func MainRPCServerPermissions() map[string][]bakery.Op {
|
||||
Entity: "offchain",
|
||||
Action: "read",
|
||||
}},
|
||||
"/lnrpc.Lightning/LookupHtlc": {{
|
||||
Entity: "offchain",
|
||||
Action: "read",
|
||||
}},
|
||||
"/lnrpc.Lightning/ListAliases": {{
|
||||
Entity: "offchain",
|
||||
Action: "read",
|
||||
@ -3904,6 +3908,28 @@ 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) {
|
||||
|
||||
chanID := lnwire.NewShortChanIDFromInt(in.ChanId)
|
||||
|
||||
info, err := r.server.chanStateDB.LookupFinalHtlc(chanID, in.HtlcIndex)
|
||||
switch {
|
||||
case errors.Is(err, channeldb.ErrHtlcUnknown):
|
||||
return nil, status.Error(codes.NotFound, err.Error())
|
||||
|
||||
case err != nil:
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &lnrpc.LookupHtlcResponse{
|
||||
Settled: info.Settled,
|
||||
Offchain: info.Offchain,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ListChannels returns a description of all the open channels that this node
|
||||
// is a participant in.
|
||||
func (r *rpcServer) ListChannels(ctx context.Context,
|
||||
|
Loading…
x
Reference in New Issue
Block a user