mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
loop: add support for the probe API
This commit is contained in:
parent
7d044f58e8
commit
f786aaa016
20 changed files with 2118 additions and 849 deletions
17
client.go
17
client.go
|
|
@ -48,6 +48,9 @@ var (
|
|||
// and pay for an LSAT token.
|
||||
globalCallTimeout = serverRPCTimeout + lsat.PaymentTimeout
|
||||
|
||||
// probeTimeout is the maximum time until a probe is allowed to take.
|
||||
probeTimeout = 3 * time.Minute
|
||||
|
||||
republishDelay = 10 * time.Second
|
||||
|
||||
// MinerFeeEstimationFailed is a magic number that is returned in a
|
||||
|
|
@ -560,7 +563,9 @@ func (s *Client) LoopInQuote(ctx context.Context,
|
|||
return nil, ErrSwapAmountTooHigh
|
||||
}
|
||||
|
||||
quote, err := s.Server.GetLoopInQuote(ctx, request.Amount)
|
||||
quote, err := s.Server.GetLoopInQuote(
|
||||
ctx, request.Amount, s.lndServices.NodePubkey, request.LastHop,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -625,3 +630,13 @@ func wrapGrpcError(message string, err error) error {
|
|||
grpcStatus.Message()),
|
||||
)
|
||||
}
|
||||
|
||||
// Probe asks the server to probe a route to us given a requested amount and
|
||||
// last hop. The server is free to discard frequent request to avoid abuse or if
|
||||
// there's been a recent probe to us for the same amount.
|
||||
func (s *Client) Probe(ctx context.Context, req *ProbeRequest) error {
|
||||
return s.Server.Probe(
|
||||
ctx, req.Amount, s.lndServices.NodePubkey, req.LastHop,
|
||||
req.RouteHints,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
18
interface.go
18
interface.go
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/lightninglabs/loop/swap"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
"github.com/lightningnetwork/lnd/zpay32"
|
||||
)
|
||||
|
||||
// OutRequest contains the required parameters for a loop out swap.
|
||||
|
|
@ -243,6 +244,11 @@ type LoopInQuoteRequest struct {
|
|||
// ExternalHtlc specifies whether the htlc is published by an external
|
||||
// source.
|
||||
ExternalHtlc bool
|
||||
|
||||
// LastHop is an optional last hop to use. This last hop is used when
|
||||
// the client has already requested a server probe for more accurate
|
||||
// routing fee estimation.
|
||||
LastHop *route.Vertex
|
||||
}
|
||||
|
||||
// LoopInQuote contains estimates for the fees making up the total swap cost
|
||||
|
|
@ -340,3 +346,15 @@ func (s *In) LastUpdate() time.Time {
|
|||
func (s *In) SwapHash() lntypes.Hash {
|
||||
return s.Hash
|
||||
}
|
||||
|
||||
// ProbeRequest specifies probe parameters for the server probe.
|
||||
type ProbeRequest struct {
|
||||
// Amount is the amount that will be probed.
|
||||
Amount btcutil.Amount
|
||||
|
||||
// LastHop is the last hop along the route.
|
||||
LastHop *route.Vertex
|
||||
|
||||
// Optional hop hints.
|
||||
RouteHints [][]zpay32.HopHint
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,6 +95,13 @@ var (
|
|||
Entity: "suggestions",
|
||||
Action: "write",
|
||||
}},
|
||||
"/looprpc.SwapClient/Probe": {{
|
||||
Entity: "swap",
|
||||
Action: "execute",
|
||||
}, {
|
||||
Entity: "loop",
|
||||
Action: "in",
|
||||
}},
|
||||
}
|
||||
|
||||
// allPermissions is the list of all existing permissions that exist
|
||||
|
|
|
|||
|
|
@ -2,12 +2,14 @@ package loopd
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightninglabs/lndclient"
|
||||
|
|
@ -22,6 +24,7 @@ import (
|
|||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/queue"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
"github.com/lightningnetwork/lnd/zpay32"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
|
@ -495,6 +498,85 @@ func (s *swapClientServer) GetLoopInQuote(ctx context.Context,
|
|||
}, nil
|
||||
}
|
||||
|
||||
// unmarshallRouteHints unmarshalls a list of route hints.
|
||||
func unmarshallRouteHints(rpcRouteHints []*looprpc.RouteHint) (
|
||||
[][]zpay32.HopHint, error) {
|
||||
|
||||
routeHints := make([][]zpay32.HopHint, 0, len(rpcRouteHints))
|
||||
for _, rpcRouteHint := range rpcRouteHints {
|
||||
routeHint := make(
|
||||
[]zpay32.HopHint, 0, len(rpcRouteHint.HopHints),
|
||||
)
|
||||
for _, rpcHint := range rpcRouteHint.HopHints {
|
||||
hint, err := unmarshallHopHint(rpcHint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
routeHint = append(routeHint, hint)
|
||||
}
|
||||
routeHints = append(routeHints, routeHint)
|
||||
}
|
||||
|
||||
return routeHints, nil
|
||||
}
|
||||
|
||||
// unmarshallHopHint unmarshalls a single hop hint.
|
||||
func unmarshallHopHint(rpcHint *looprpc.HopHint) (zpay32.HopHint, error) {
|
||||
pubBytes, err := hex.DecodeString(rpcHint.NodeId)
|
||||
if err != nil {
|
||||
return zpay32.HopHint{}, err
|
||||
}
|
||||
|
||||
pubkey, err := btcec.ParsePubKey(pubBytes, btcec.S256())
|
||||
if err != nil {
|
||||
return zpay32.HopHint{}, err
|
||||
}
|
||||
|
||||
return zpay32.HopHint{
|
||||
NodeID: pubkey,
|
||||
ChannelID: rpcHint.ChanId,
|
||||
FeeBaseMSat: rpcHint.FeeBaseMsat,
|
||||
FeeProportionalMillionths: rpcHint.FeeProportionalMillionths,
|
||||
CLTVExpiryDelta: uint16(rpcHint.CltvExpiryDelta),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Probe requests the server to probe the client's node to test inbound
|
||||
// liquidity.
|
||||
func (s *swapClientServer) Probe(ctx context.Context,
|
||||
req *looprpc.ProbeRequest) (*looprpc.ProbeResponse, error) {
|
||||
|
||||
log.Infof("Probe request received")
|
||||
|
||||
var lastHop *route.Vertex
|
||||
if req.LastHop != nil {
|
||||
lastHopVertex, err := route.NewVertexFromBytes(req.LastHop)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lastHop = &lastHopVertex
|
||||
}
|
||||
|
||||
routeHints, err := unmarshallRouteHints(req.RouteHints)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = s.impl.Probe(ctx, &loop.ProbeRequest{
|
||||
Amount: btcutil.Amount(req.Amt),
|
||||
LastHop: lastHop,
|
||||
RouteHints: routeHints,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &looprpc.ProbeResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *swapClientServer) LoopIn(ctx context.Context,
|
||||
in *looprpc.LoopInRequest) (
|
||||
*looprpc.SwapResponse, error) {
|
||||
|
|
|
|||
|
|
@ -43,13 +43,17 @@ const (
|
|||
// canceling loop out swaps.
|
||||
ProtocolVersionLoopOutCancel = 7
|
||||
|
||||
// ProtocolVerionProbe indicates that the client is able to request
|
||||
// the server to perform a probe to test inbound liquidty.
|
||||
ProtocolVersionProbe ProtocolVersion = 8
|
||||
|
||||
// ProtocolVersionUnrecorded is set for swaps were created before we
|
||||
// started saving protocol version with swaps.
|
||||
ProtocolVersionUnrecorded ProtocolVersion = math.MaxUint32
|
||||
|
||||
// CurrentRPCProtocolVersion defines the version of the RPC protocol
|
||||
// that is currently supported by the loop client.
|
||||
CurrentRPCProtocolVersion = looprpc.ProtocolVersion_LOOP_OUT_CANCEL
|
||||
CurrentRPCProtocolVersion = looprpc.ProtocolVersion_PROBE
|
||||
|
||||
// CurrentInternalProtocolVersion defines the RPC current protocol in
|
||||
// the internal representation.
|
||||
|
|
@ -88,6 +92,9 @@ func (p ProtocolVersion) String() string {
|
|||
case ProtocolVersionLoopOutCancel:
|
||||
return "Loop Out Cancel"
|
||||
|
||||
case ProtocolVersionProbe:
|
||||
return "Probe"
|
||||
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ func TestProtocolVersionSanity(t *testing.T) {
|
|||
ProtocolVersionHtlcV2,
|
||||
ProtocolVersionMultiLoopIn,
|
||||
ProtocolVersionLoopOutCancel,
|
||||
ProtocolVersionProbe,
|
||||
}
|
||||
|
||||
rpcVersions := [...]looprpc.ProtocolVersion{
|
||||
|
|
@ -33,6 +34,7 @@ func TestProtocolVersionSanity(t *testing.T) {
|
|||
looprpc.ProtocolVersion_HTLC_V2,
|
||||
looprpc.ProtocolVersion_MULTI_LOOP_IN,
|
||||
looprpc.ProtocolVersion_LOOP_OUT_CANCEL,
|
||||
looprpc.ProtocolVersion_PROBE,
|
||||
}
|
||||
|
||||
require.Equal(t, len(versions), len(rpcVersions))
|
||||
|
|
|
|||
|
|
@ -83,7 +83,9 @@ func newLoopInSwap(globalCtx context.Context, cfg *swapConfig,
|
|||
// Request current server loop in terms and use these to calculate the
|
||||
// swap fee that we should subtract from the swap amount in the payment
|
||||
// request that we send to the server.
|
||||
quote, err := cfg.server.GetLoopInQuote(globalCtx, request.Amount)
|
||||
quote, err := cfg.server.GetLoopInQuote(
|
||||
globalCtx, request.Amount, cfg.lnd.NodePubkey, request.LastHop,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, wrapGrpcError("loop in terms", err)
|
||||
}
|
||||
|
|
|
|||
1139
looprpc/client.pb.go
1139
looprpc/client.pb.go
File diff suppressed because it is too large
Load diff
|
|
@ -345,6 +345,76 @@ func local_request_SwapClient_GetLoopInQuote_0(ctx context.Context, marshaler ru
|
|||
|
||||
}
|
||||
|
||||
var (
|
||||
filter_SwapClient_Probe_0 = &utilities.DoubleArray{Encoding: map[string]int{"amt": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}}
|
||||
)
|
||||
|
||||
func request_SwapClient_Probe_0(ctx context.Context, marshaler runtime.Marshaler, client SwapClientClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq ProbeRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
var (
|
||||
val string
|
||||
ok bool
|
||||
err error
|
||||
_ = err
|
||||
)
|
||||
|
||||
val, ok = pathParams["amt"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "amt")
|
||||
}
|
||||
|
||||
protoReq.Amt, err = runtime.Int64(val)
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "amt", err)
|
||||
}
|
||||
|
||||
if err := req.ParseForm(); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_SwapClient_Probe_0); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := client.Probe(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_SwapClient_Probe_0(ctx context.Context, marshaler runtime.Marshaler, server SwapClientServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq ProbeRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
var (
|
||||
val string
|
||||
ok bool
|
||||
err error
|
||||
_ = err
|
||||
)
|
||||
|
||||
val, ok = pathParams["amt"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "amt")
|
||||
}
|
||||
|
||||
protoReq.Amt, err = runtime.Int64(val)
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "amt", err)
|
||||
}
|
||||
|
||||
if err := req.ParseForm(); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_SwapClient_Probe_0); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := server.Probe(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func request_SwapClient_GetLsatTokens_0(ctx context.Context, marshaler runtime.Marshaler, client SwapClientClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq TokensRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
|
@ -623,6 +693,29 @@ func RegisterSwapClientHandlerServer(ctx context.Context, mux *runtime.ServeMux,
|
|||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_SwapClient_Probe_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, "/looprpc.SwapClient/Probe", runtime.WithHTTPPathPattern("/v1/loop/in/probe/{amt}"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_SwapClient_Probe_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_SwapClient_Probe_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_SwapClient_GetLsatTokens_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
|
|
@ -916,6 +1009,26 @@ func RegisterSwapClientHandlerClient(ctx context.Context, mux *runtime.ServeMux,
|
|||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_SwapClient_Probe_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, "/looprpc.SwapClient/Probe", runtime.WithHTTPPathPattern("/v1/loop/in/probe/{amt}"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_SwapClient_Probe_0(rctx, inboundMarshaler, client, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_SwapClient_Probe_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_SwapClient_GetLsatTokens_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
|
|
@ -1016,6 +1129,8 @@ var (
|
|||
|
||||
pattern_SwapClient_GetLoopInQuote_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"v1", "loop", "in", "quote", "amt"}, ""))
|
||||
|
||||
pattern_SwapClient_Probe_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"v1", "loop", "in", "probe", "amt"}, ""))
|
||||
|
||||
pattern_SwapClient_GetLsatTokens_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "lsat", "tokens"}, ""))
|
||||
|
||||
pattern_SwapClient_GetLiquidityParams_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "liquidity", "params"}, ""))
|
||||
|
|
@ -1042,6 +1157,8 @@ var (
|
|||
|
||||
forward_SwapClient_GetLoopInQuote_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_SwapClient_Probe_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_SwapClient_GetLsatTokens_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_SwapClient_GetLiquidityParams_0 = runtime.ForwardResponseMessage
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
syntax = "proto3";
|
||||
|
||||
import "common.proto";
|
||||
|
||||
package looprpc;
|
||||
|
||||
option go_package = "github.com/lightninglabs/loop/looprpc";
|
||||
|
|
@ -62,6 +64,12 @@ service SwapClient {
|
|||
*/
|
||||
rpc GetLoopInQuote (QuoteRequest) returns (InQuoteResponse);
|
||||
|
||||
/*
|
||||
Probe asks he sever to probe the route to us to have a better upfront
|
||||
estimate about routing fees when loopin-in.
|
||||
*/
|
||||
rpc Probe (ProbeRequest) returns (ProbeResponse);
|
||||
|
||||
/* loop: `listauth`
|
||||
GetLsatTokens returns all LSAT tokens the daemon ever paid for.
|
||||
*/
|
||||
|
|
@ -560,6 +568,13 @@ message QuoteRequest {
|
|||
lower total swap fee. This only has an effect on loop out quotes.
|
||||
*/
|
||||
uint64 swap_publication_deadline = 4;
|
||||
|
||||
/*
|
||||
Optionally the client can specify the last hop pubkey when requesting a
|
||||
loop-in quote. This is useful to get better off-chain routing fee from the
|
||||
server.
|
||||
*/
|
||||
bytes loop_in_last_hop = 5;
|
||||
}
|
||||
|
||||
message InQuoteResponse {
|
||||
|
|
@ -625,6 +640,26 @@ message OutQuoteResponse {
|
|||
int32 conf_target = 6;
|
||||
}
|
||||
|
||||
message ProbeRequest {
|
||||
/*
|
||||
The amount to probe.
|
||||
*/
|
||||
int64 amt = 1;
|
||||
|
||||
/*
|
||||
Optional last hop of the route to probe.
|
||||
*/
|
||||
bytes last_hop = 2;
|
||||
|
||||
/*
|
||||
Optional route hints to reach the destination through private channels.
|
||||
*/
|
||||
repeated RouteHint route_hints = 3;
|
||||
}
|
||||
|
||||
message ProbeResponse {
|
||||
}
|
||||
|
||||
message TokensRequest {
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -126,6 +126,47 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"/v1/loop/in/probe/{amt}": {
|
||||
"get": {
|
||||
"summary": "Probe asks he sever to probe the route to us to have a better upfront\nestimate about routing fees when loopin-in.",
|
||||
"operationId": "SwapClient_Probe",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/looprpcProbeResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "amt",
|
||||
"description": "The amount to probe.",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"format": "int64"
|
||||
},
|
||||
{
|
||||
"name": "last_hop",
|
||||
"description": "Optional last hop of the route to probe.",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"type": "string",
|
||||
"format": "byte"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"SwapClient"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/loop/in/quote/{amt}": {
|
||||
"get": {
|
||||
"summary": "loop: `quote`\nGetQuote returns a quote for a swap with the provided parameters.",
|
||||
|
|
@ -175,6 +216,14 @@
|
|||
"required": false,
|
||||
"type": "string",
|
||||
"format": "uint64"
|
||||
},
|
||||
{
|
||||
"name": "loop_in_last_hop",
|
||||
"description": "Optionally the client can specify the last hop pubkey when requesting a\nloop-in quote. This is useful to get better off-chain routing fee from the\nserver.",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"type": "string",
|
||||
"format": "byte"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
|
|
@ -287,6 +336,14 @@
|
|||
"required": false,
|
||||
"type": "string",
|
||||
"format": "uint64"
|
||||
},
|
||||
{
|
||||
"name": "loop_in_last_hop",
|
||||
"description": "Optionally the client can specify the last hop pubkey when requesting a\nloop-in quote. This is useful to get better off-chain routing fee from the\nserver.",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"type": "string",
|
||||
"format": "byte"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
|
|
@ -452,6 +509,35 @@
|
|||
"default": "FAILURE_REASON_NONE",
|
||||
"description": " - FAILURE_REASON_NONE: FAILURE_REASON_NONE is set when the swap did not fail, it is either in\nprogress or succeeded.\n - FAILURE_REASON_OFFCHAIN: FAILURE_REASON_OFFCHAIN indicates that a loop out failed because it wasn't\npossible to find a route for one or both off chain payments that met the fee\nand timelock limits required.\n - FAILURE_REASON_TIMEOUT: FAILURE_REASON_TIMEOUT indicates that the swap failed because on chain htlc\ndid not confirm before its expiry, or it confirmed too late for us to reveal\nour preimage and claim.\n - FAILURE_REASON_SWEEP_TIMEOUT: FAILURE_REASON_SWEEP_TIMEOUT indicates that a loop out permanently failed\nbecause the on chain htlc wasn't swept before the server revoked the\nhtlc.\n - FAILURE_REASON_INSUFFICIENT_VALUE: FAILURE_REASON_INSUFFICIENT_VALUE indicates that a loop out has failed\nbecause the on chain htlc had a lower value than requested.\n - FAILURE_REASON_TEMPORARY: FAILURE_REASON_TEMPORARY indicates that a swap cannot continue due to an\ninternal error. Manual intervention such as a restart is required.\n - FAILURE_REASON_INCORRECT_AMOUNT: FAILURE_REASON_INCORRECT_AMOUNT indicates that a loop in permanently failed\nbecause the amount extended by an external loop in htlc is insufficient."
|
||||
},
|
||||
"looprpcHopHint": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"node_id": {
|
||||
"type": "string",
|
||||
"description": "The public key of the node at the start of the channel."
|
||||
},
|
||||
"chan_id": {
|
||||
"type": "string",
|
||||
"format": "uint64",
|
||||
"description": "The unique identifier of the channel."
|
||||
},
|
||||
"fee_base_msat": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "The base fee of the channel denominated in millisatoshis."
|
||||
},
|
||||
"fee_proportional_millionths": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "The fee rate of the channel for sending one satoshi across it denominated in\nmillionths of a satoshi."
|
||||
},
|
||||
"cltv_expiry_delta": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "The time-lock delta of the channel."
|
||||
}
|
||||
}
|
||||
},
|
||||
"looprpcInQuoteResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
@ -847,6 +933,21 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"looprpcProbeResponse": {
|
||||
"type": "object"
|
||||
},
|
||||
"looprpcRouteHint": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"hop_hints": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/looprpcHopHint"
|
||||
},
|
||||
"description": "A list of hop hints that when chained together can assist in reaching a\nspecific destination."
|
||||
}
|
||||
}
|
||||
},
|
||||
"looprpcSetLiquidityParamsRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ http:
|
|||
get: "/v1/loop/in/terms"
|
||||
- selector: looprpc.SwapClient.GetLoopInQuote
|
||||
get: "/v1/loop/in/quote/{amt}"
|
||||
- selector: looprpc.SwapClient.Probe
|
||||
get: "/v1/loop/in/probe/{amt}"
|
||||
- selector: looprpc.SwapClient.GetLsatTokens
|
||||
get: "/v1/lsat/tokens"
|
||||
- selector: looprpc.SwapClient.GetLiquidityParams
|
||||
|
|
|
|||
|
|
@ -53,6 +53,10 @@ type SwapClientClient interface {
|
|||
// loop: `quote`
|
||||
//GetQuote returns a quote for a swap with the provided parameters.
|
||||
GetLoopInQuote(ctx context.Context, in *QuoteRequest, opts ...grpc.CallOption) (*InQuoteResponse, error)
|
||||
//
|
||||
//Probe asks he sever to probe the route to us to have a better upfront
|
||||
//estimate about routing fees when loopin-in.
|
||||
Probe(ctx context.Context, in *ProbeRequest, opts ...grpc.CallOption) (*ProbeResponse, error)
|
||||
// loop: `listauth`
|
||||
//GetLsatTokens returns all LSAT tokens the daemon ever paid for.
|
||||
GetLsatTokens(ctx context.Context, in *TokensRequest, opts ...grpc.CallOption) (*TokensResponse, error)
|
||||
|
|
@ -187,6 +191,15 @@ func (c *swapClientClient) GetLoopInQuote(ctx context.Context, in *QuoteRequest,
|
|||
return out, nil
|
||||
}
|
||||
|
||||
func (c *swapClientClient) Probe(ctx context.Context, in *ProbeRequest, opts ...grpc.CallOption) (*ProbeResponse, error) {
|
||||
out := new(ProbeResponse)
|
||||
err := c.cc.Invoke(ctx, "/looprpc.SwapClient/Probe", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *swapClientClient) GetLsatTokens(ctx context.Context, in *TokensRequest, opts ...grpc.CallOption) (*TokensResponse, error) {
|
||||
out := new(TokensResponse)
|
||||
err := c.cc.Invoke(ctx, "/looprpc.SwapClient/GetLsatTokens", in, out, opts...)
|
||||
|
|
@ -262,6 +275,10 @@ type SwapClientServer interface {
|
|||
// loop: `quote`
|
||||
//GetQuote returns a quote for a swap with the provided parameters.
|
||||
GetLoopInQuote(context.Context, *QuoteRequest) (*InQuoteResponse, error)
|
||||
//
|
||||
//Probe asks he sever to probe the route to us to have a better upfront
|
||||
//estimate about routing fees when loopin-in.
|
||||
Probe(context.Context, *ProbeRequest) (*ProbeResponse, error)
|
||||
// loop: `listauth`
|
||||
//GetLsatTokens returns all LSAT tokens the daemon ever paid for.
|
||||
GetLsatTokens(context.Context, *TokensRequest) (*TokensResponse, error)
|
||||
|
|
@ -316,6 +333,9 @@ func (UnimplementedSwapClientServer) GetLoopInTerms(context.Context, *TermsReque
|
|||
func (UnimplementedSwapClientServer) GetLoopInQuote(context.Context, *QuoteRequest) (*InQuoteResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetLoopInQuote not implemented")
|
||||
}
|
||||
func (UnimplementedSwapClientServer) Probe(context.Context, *ProbeRequest) (*ProbeResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Probe not implemented")
|
||||
}
|
||||
func (UnimplementedSwapClientServer) GetLsatTokens(context.Context, *TokensRequest) (*TokensResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetLsatTokens not implemented")
|
||||
}
|
||||
|
|
@ -506,6 +526,24 @@ func _SwapClient_GetLoopInQuote_Handler(srv interface{}, ctx context.Context, de
|
|||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SwapClient_Probe_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ProbeRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SwapClientServer).Probe(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/looprpc.SwapClient/Probe",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SwapClientServer).Probe(ctx, req.(*ProbeRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SwapClient_GetLsatTokens_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(TokensRequest)
|
||||
if err := dec(in); err != nil {
|
||||
|
|
@ -617,6 +655,10 @@ var SwapClient_ServiceDesc = grpc.ServiceDesc{
|
|||
MethodName: "GetLoopInQuote",
|
||||
Handler: _SwapClient_GetLoopInQuote_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Probe",
|
||||
Handler: _SwapClient_Probe_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetLsatTokens",
|
||||
Handler: _SwapClient_GetLsatTokens_Handler,
|
||||
|
|
|
|||
261
looprpc/common.pb.go
Normal file
261
looprpc/common.pb.go
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.26.0
|
||||
// protoc v3.6.1
|
||||
// source: common.proto
|
||||
|
||||
package looprpc
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type HopHint struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// The public key of the node at the start of the channel.
|
||||
NodeId string `protobuf:"bytes,1,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"`
|
||||
// The unique identifier of the channel.
|
||||
ChanId uint64 `protobuf:"varint,2,opt,name=chan_id,json=chanId,proto3" json:"chan_id,omitempty"`
|
||||
// The base fee of the channel denominated in millisatoshis.
|
||||
FeeBaseMsat uint32 `protobuf:"varint,3,opt,name=fee_base_msat,json=feeBaseMsat,proto3" json:"fee_base_msat,omitempty"`
|
||||
//
|
||||
//The fee rate of the channel for sending one satoshi across it denominated in
|
||||
//millionths of a satoshi.
|
||||
FeeProportionalMillionths uint32 `protobuf:"varint,4,opt,name=fee_proportional_millionths,json=feeProportionalMillionths,proto3" json:"fee_proportional_millionths,omitempty"`
|
||||
// The time-lock delta of the channel.
|
||||
CltvExpiryDelta uint32 `protobuf:"varint,5,opt,name=cltv_expiry_delta,json=cltvExpiryDelta,proto3" json:"cltv_expiry_delta,omitempty"`
|
||||
}
|
||||
|
||||
func (x *HopHint) Reset() {
|
||||
*x = HopHint{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_common_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *HopHint) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*HopHint) ProtoMessage() {}
|
||||
|
||||
func (x *HopHint) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_common_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use HopHint.ProtoReflect.Descriptor instead.
|
||||
func (*HopHint) Descriptor() ([]byte, []int) {
|
||||
return file_common_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *HopHint) GetNodeId() string {
|
||||
if x != nil {
|
||||
return x.NodeId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *HopHint) GetChanId() uint64 {
|
||||
if x != nil {
|
||||
return x.ChanId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *HopHint) GetFeeBaseMsat() uint32 {
|
||||
if x != nil {
|
||||
return x.FeeBaseMsat
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *HopHint) GetFeeProportionalMillionths() uint32 {
|
||||
if x != nil {
|
||||
return x.FeeProportionalMillionths
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *HopHint) GetCltvExpiryDelta() uint32 {
|
||||
if x != nil {
|
||||
return x.CltvExpiryDelta
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type RouteHint struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
//
|
||||
//A list of hop hints that when chained together can assist in reaching a
|
||||
//specific destination.
|
||||
HopHints []*HopHint `protobuf:"bytes,1,rep,name=hop_hints,json=hopHints,proto3" json:"hop_hints,omitempty"`
|
||||
}
|
||||
|
||||
func (x *RouteHint) Reset() {
|
||||
*x = RouteHint{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_common_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *RouteHint) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*RouteHint) ProtoMessage() {}
|
||||
|
||||
func (x *RouteHint) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_common_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use RouteHint.ProtoReflect.Descriptor instead.
|
||||
func (*RouteHint) Descriptor() ([]byte, []int) {
|
||||
return file_common_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *RouteHint) GetHopHints() []*HopHint {
|
||||
if x != nil {
|
||||
return x.HopHints
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_common_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_common_proto_rawDesc = []byte{
|
||||
0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07,
|
||||
0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x22, 0xcb, 0x01, 0x0a, 0x07, 0x48, 0x6f, 0x70, 0x48,
|
||||
0x69, 0x6e, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07,
|
||||
0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x63,
|
||||
0x68, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x66, 0x65, 0x65, 0x5f, 0x62, 0x61, 0x73,
|
||||
0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x66, 0x65,
|
||||
0x65, 0x42, 0x61, 0x73, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x3e, 0x0a, 0x1b, 0x66, 0x65, 0x65,
|
||||
0x5f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x6d, 0x69,
|
||||
0x6c, 0x6c, 0x69, 0x6f, 0x6e, 0x74, 0x68, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x19,
|
||||
0x66, 0x65, 0x65, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x4d,
|
||||
0x69, 0x6c, 0x6c, 0x69, 0x6f, 0x6e, 0x74, 0x68, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x6c, 0x74,
|
||||
0x76, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x05,
|
||||
0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x63, 0x6c, 0x74, 0x76, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79,
|
||||
0x44, 0x65, 0x6c, 0x74, 0x61, 0x22, 0x3a, 0x0a, 0x09, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x48, 0x69,
|
||||
0x6e, 0x74, 0x12, 0x2d, 0x0a, 0x09, 0x68, 0x6f, 0x70, 0x5f, 0x68, 0x69, 0x6e, 0x74, 0x73, 0x18,
|
||||
0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e,
|
||||
0x48, 0x6f, 0x70, 0x48, 0x69, 0x6e, 0x74, 0x52, 0x08, 0x68, 0x6f, 0x70, 0x48, 0x69, 0x6e, 0x74,
|
||||
0x73, 0x42, 0x27, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
|
||||
0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x6c, 0x6f,
|
||||
0x6f, 0x70, 0x2f, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_common_proto_rawDescOnce sync.Once
|
||||
file_common_proto_rawDescData = file_common_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_common_proto_rawDescGZIP() []byte {
|
||||
file_common_proto_rawDescOnce.Do(func() {
|
||||
file_common_proto_rawDescData = protoimpl.X.CompressGZIP(file_common_proto_rawDescData)
|
||||
})
|
||||
return file_common_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_common_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_common_proto_goTypes = []interface{}{
|
||||
(*HopHint)(nil), // 0: looprpc.HopHint
|
||||
(*RouteHint)(nil), // 1: looprpc.RouteHint
|
||||
}
|
||||
var file_common_proto_depIdxs = []int32{
|
||||
0, // 0: looprpc.RouteHint.hop_hints:type_name -> looprpc.HopHint
|
||||
1, // [1:1] is the sub-list for method output_type
|
||||
1, // [1:1] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_common_proto_init() }
|
||||
func file_common_proto_init() {
|
||||
if File_common_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_common_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*HopHint); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_common_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*RouteHint); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_common_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 2,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_common_proto_goTypes,
|
||||
DependencyIndexes: file_common_proto_depIdxs,
|
||||
MessageInfos: file_common_proto_msgTypes,
|
||||
}.Build()
|
||||
File_common_proto = out.File
|
||||
file_common_proto_rawDesc = nil
|
||||
file_common_proto_goTypes = nil
|
||||
file_common_proto_depIdxs = nil
|
||||
}
|
||||
33
looprpc/common.proto
Normal file
33
looprpc/common.proto
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package looprpc;
|
||||
|
||||
option go_package = "github.com/lightninglabs/loop/looprpc";
|
||||
|
||||
message HopHint {
|
||||
// The public key of the node at the start of the channel.
|
||||
string node_id = 1;
|
||||
|
||||
// The unique identifier of the channel.
|
||||
uint64 chan_id = 2;
|
||||
|
||||
// The base fee of the channel denominated in millisatoshis.
|
||||
uint32 fee_base_msat = 3;
|
||||
|
||||
/*
|
||||
The fee rate of the channel for sending one satoshi across it denominated in
|
||||
millionths of a satoshi.
|
||||
*/
|
||||
uint32 fee_proportional_millionths = 4;
|
||||
|
||||
// The time-lock delta of the channel.
|
||||
uint32 cltv_expiry_delta = 5;
|
||||
}
|
||||
|
||||
message RouteHint {
|
||||
/*
|
||||
A list of hop hints that when chained together can assist in reaching a
|
||||
specific destination.
|
||||
*/
|
||||
repeated HopHint hop_hints = 1;
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,5 +1,7 @@
|
|||
syntax = "proto3";
|
||||
|
||||
import "common.proto";
|
||||
|
||||
package looprpc;
|
||||
|
||||
option go_package = "github.com/lightninglabs/loop/looprpc";
|
||||
|
|
@ -29,6 +31,8 @@ service SwapServer {
|
|||
|
||||
rpc CancelLoopOutSwap (CancelLoopOutSwapRequest)
|
||||
returns (CancelLoopOutSwapResponse);
|
||||
|
||||
rpc Probe (ServerProbeRequest) returns (ServerProbeResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -70,6 +74,10 @@ enum ProtocolVersion {
|
|||
|
||||
// The client supports loop out swap cancelation.
|
||||
LOOP_OUT_CANCEL = 7;
|
||||
|
||||
// The client is able to ask the server to probe to test inbound
|
||||
// liquidity.
|
||||
PROBE = 8;
|
||||
}
|
||||
|
||||
message ServerLoopOutRequest {
|
||||
|
|
@ -201,6 +209,16 @@ message ServerLoopInQuoteRequest {
|
|||
/// The swap amount. If zero, a quote for a maximum amt swap will be given.
|
||||
uint64 amt = 1;
|
||||
|
||||
// The destination pubkey. Will be used to retrieve cached probed routing
|
||||
// fee.
|
||||
bytes pubkey = 3;
|
||||
|
||||
// The last hop to use. Will be used to retrieve cached probed routing fee.
|
||||
bytes last_hop = 4;
|
||||
|
||||
// Optional route hints to reach the destination through private channels.
|
||||
repeated RouteHint route_hints = 5;
|
||||
|
||||
/// The protocol version that the client adheres to.
|
||||
ProtocolVersion protocol_version = 2;
|
||||
}
|
||||
|
|
@ -414,3 +432,25 @@ message CancelLoopOutSwapRequest {
|
|||
|
||||
message CancelLoopOutSwapResponse {
|
||||
}
|
||||
|
||||
message ServerProbeRequest {
|
||||
/// The protocol version that the client adheres to.
|
||||
ProtocolVersion protocol_version = 1;
|
||||
|
||||
// The probe amount.
|
||||
uint64 amt = 2;
|
||||
|
||||
// The target node for the probe.
|
||||
bytes target = 3;
|
||||
|
||||
// Optional last hop to use when probing the client.
|
||||
bytes last_hop = 4;
|
||||
|
||||
/*
|
||||
Optional route hints to reach the destination through private channels.
|
||||
*/
|
||||
repeated RouteHint route_hints = 5;
|
||||
}
|
||||
|
||||
message ServerProbeResponse {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ type SwapServerClient interface {
|
|||
SubscribeLoopOutUpdates(ctx context.Context, in *SubscribeUpdatesRequest, opts ...grpc.CallOption) (SwapServer_SubscribeLoopOutUpdatesClient, error)
|
||||
SubscribeLoopInUpdates(ctx context.Context, in *SubscribeUpdatesRequest, opts ...grpc.CallOption) (SwapServer_SubscribeLoopInUpdatesClient, error)
|
||||
CancelLoopOutSwap(ctx context.Context, in *CancelLoopOutSwapRequest, opts ...grpc.CallOption) (*CancelLoopOutSwapResponse, error)
|
||||
Probe(ctx context.Context, in *ServerProbeRequest, opts ...grpc.CallOption) (*ServerProbeResponse, error)
|
||||
}
|
||||
|
||||
type swapServerClient struct {
|
||||
|
|
@ -174,6 +175,15 @@ func (c *swapServerClient) CancelLoopOutSwap(ctx context.Context, in *CancelLoop
|
|||
return out, nil
|
||||
}
|
||||
|
||||
func (c *swapServerClient) Probe(ctx context.Context, in *ServerProbeRequest, opts ...grpc.CallOption) (*ServerProbeResponse, error) {
|
||||
out := new(ServerProbeResponse)
|
||||
err := c.cc.Invoke(ctx, "/looprpc.SwapServer/Probe", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// SwapServerServer is the server API for SwapServer service.
|
||||
// All implementations must embed UnimplementedSwapServerServer
|
||||
// for forward compatibility
|
||||
|
|
@ -188,6 +198,7 @@ type SwapServerServer interface {
|
|||
SubscribeLoopOutUpdates(*SubscribeUpdatesRequest, SwapServer_SubscribeLoopOutUpdatesServer) error
|
||||
SubscribeLoopInUpdates(*SubscribeUpdatesRequest, SwapServer_SubscribeLoopInUpdatesServer) error
|
||||
CancelLoopOutSwap(context.Context, *CancelLoopOutSwapRequest) (*CancelLoopOutSwapResponse, error)
|
||||
Probe(context.Context, *ServerProbeRequest) (*ServerProbeResponse, error)
|
||||
mustEmbedUnimplementedSwapServerServer()
|
||||
}
|
||||
|
||||
|
|
@ -225,6 +236,9 @@ func (UnimplementedSwapServerServer) SubscribeLoopInUpdates(*SubscribeUpdatesReq
|
|||
func (UnimplementedSwapServerServer) CancelLoopOutSwap(context.Context, *CancelLoopOutSwapRequest) (*CancelLoopOutSwapResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CancelLoopOutSwap not implemented")
|
||||
}
|
||||
func (UnimplementedSwapServerServer) Probe(context.Context, *ServerProbeRequest) (*ServerProbeResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Probe not implemented")
|
||||
}
|
||||
func (UnimplementedSwapServerServer) mustEmbedUnimplementedSwapServerServer() {}
|
||||
|
||||
// UnsafeSwapServerServer may be embedded to opt out of forward compatibility for this service.
|
||||
|
|
@ -424,6 +438,24 @@ func _SwapServer_CancelLoopOutSwap_Handler(srv interface{}, ctx context.Context,
|
|||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SwapServer_Probe_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ServerProbeRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SwapServerServer).Probe(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/looprpc.SwapServer/Probe",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SwapServerServer).Probe(ctx, req.(*ServerProbeRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// SwapServer_ServiceDesc is the grpc.ServiceDesc for SwapServer service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
|
|
@ -463,6 +495,10 @@ var SwapServer_ServiceDesc = grpc.ServiceDesc{
|
|||
MethodName: "CancelLoopOutSwap",
|
||||
Handler: _SwapServer_CancelLoopOutSwap_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Probe",
|
||||
Handler: _SwapServer_Probe_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
|
|
|
|||
|
|
@ -212,8 +212,8 @@ func (s *serverMock) GetLoopInTerms(ctx context.Context) (
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (s *serverMock) GetLoopInQuote(ctx context.Context, amt btcutil.Amount) (
|
||||
*LoopInQuote, error) {
|
||||
func (s *serverMock) GetLoopInQuote(context.Context, btcutil.Amount,
|
||||
route.Vertex, *route.Vertex) (*LoopInQuote, error) {
|
||||
|
||||
return &LoopInQuote{
|
||||
SwapFee: testSwapFee,
|
||||
|
|
@ -235,3 +235,10 @@ func (s *serverMock) SubscribeLoopInUpdates(_ context.Context,
|
|||
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func (s *serverMock) Probe(ctx context.Context, amt btcutil.Amount,
|
||||
pubKey route.Vertex, lastHop *route.Vertex,
|
||||
routeHints [][]zpay32.HopHint) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import (
|
|||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
"github.com/lightningnetwork/lnd/tor"
|
||||
"github.com/lightningnetwork/lnd/zpay32"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
)
|
||||
|
|
@ -51,8 +52,11 @@ type swapServerClient interface {
|
|||
GetLoopInTerms(ctx context.Context) (
|
||||
*LoopInTerms, error)
|
||||
|
||||
GetLoopInQuote(ctx context.Context, amt btcutil.Amount) (
|
||||
*LoopInQuote, error)
|
||||
GetLoopInQuote(ctx context.Context, amt btcutil.Amount,
|
||||
pubKey route.Vertex, lastHop *route.Vertex) (*LoopInQuote, error)
|
||||
|
||||
Probe(ctx context.Context, amt btcutil.Amount, target route.Vertex,
|
||||
lastHop *route.Vertex, routeHints [][]zpay32.HopHint) error
|
||||
|
||||
NewLoopOutSwap(ctx context.Context,
|
||||
swapHash lntypes.Hash, amount btcutil.Amount, expiry int32,
|
||||
|
|
@ -203,16 +207,23 @@ func (s *grpcSwapServerClient) GetLoopInTerms(ctx context.Context) (
|
|||
}
|
||||
|
||||
func (s *grpcSwapServerClient) GetLoopInQuote(ctx context.Context,
|
||||
amt btcutil.Amount) (*LoopInQuote, error) {
|
||||
amt btcutil.Amount, pubKey route.Vertex, lastHop *route.Vertex) (
|
||||
*LoopInQuote, error) {
|
||||
|
||||
rpcCtx, rpcCancel := context.WithTimeout(ctx, globalCallTimeout)
|
||||
defer rpcCancel()
|
||||
quoteResp, err := s.server.LoopInQuote(rpcCtx,
|
||||
&looprpc.ServerLoopInQuoteRequest{
|
||||
Amt: uint64(amt),
|
||||
ProtocolVersion: loopdb.CurrentRPCProtocolVersion,
|
||||
},
|
||||
)
|
||||
|
||||
req := &looprpc.ServerLoopInQuoteRequest{
|
||||
Amt: uint64(amt),
|
||||
ProtocolVersion: loopdb.CurrentRPCProtocolVersion,
|
||||
Pubkey: pubKey[:],
|
||||
}
|
||||
|
||||
if lastHop != nil {
|
||||
req.LastHop = lastHop[:]
|
||||
}
|
||||
|
||||
quoteResp, err := s.server.LoopInQuote(rpcCtx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -223,6 +234,76 @@ func (s *grpcSwapServerClient) GetLoopInQuote(ctx context.Context,
|
|||
}, nil
|
||||
}
|
||||
|
||||
// marshallRouteHints marshalls a list of route hints.
|
||||
func marshallRouteHints(routeHints [][]zpay32.HopHint) (
|
||||
[]*looprpc.RouteHint, error) {
|
||||
|
||||
rpcRouteHints := make([]*looprpc.RouteHint, 0, len(routeHints))
|
||||
for _, routeHint := range routeHints {
|
||||
rpcRouteHint := make(
|
||||
[]*looprpc.HopHint, 0, len(routeHint),
|
||||
)
|
||||
for _, hint := range routeHint {
|
||||
rpcHint, err := marshallHopHint(hint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rpcRouteHint = append(rpcRouteHint, rpcHint)
|
||||
}
|
||||
rpcRouteHints = append(rpcRouteHints, &looprpc.RouteHint{
|
||||
HopHints: rpcRouteHint,
|
||||
})
|
||||
}
|
||||
|
||||
return rpcRouteHints, nil
|
||||
}
|
||||
|
||||
// marshallHopHint marshalls a single hop hint.
|
||||
func marshallHopHint(hint zpay32.HopHint) (*looprpc.HopHint, error) {
|
||||
nodeID, err := route.NewVertexFromBytes(
|
||||
hint.NodeID.SerializeCompressed(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &looprpc.HopHint{
|
||||
ChanId: hint.ChannelID,
|
||||
CltvExpiryDelta: uint32(hint.CLTVExpiryDelta),
|
||||
FeeBaseMsat: hint.FeeBaseMSat,
|
||||
FeeProportionalMillionths: hint.FeeProportionalMillionths,
|
||||
NodeId: nodeID.String(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *grpcSwapServerClient) Probe(ctx context.Context, amt btcutil.Amount,
|
||||
target route.Vertex, lastHop *route.Vertex,
|
||||
routeHints [][]zpay32.HopHint) error {
|
||||
|
||||
rpcCtx, rpcCancel := context.WithTimeout(ctx, probeTimeout)
|
||||
defer rpcCancel()
|
||||
|
||||
rpcRouteHints, err := marshallRouteHints(routeHints)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req := &looprpc.ServerProbeRequest{
|
||||
Amt: uint64(amt),
|
||||
Target: target[:],
|
||||
ProtocolVersion: loopdb.CurrentRPCProtocolVersion,
|
||||
RouteHints: rpcRouteHints,
|
||||
}
|
||||
|
||||
if lastHop != nil {
|
||||
req.LastHop = lastHop[:]
|
||||
}
|
||||
|
||||
_, err = s.server.Probe(rpcCtx, req)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *grpcSwapServerClient) NewLoopOutSwap(ctx context.Context,
|
||||
swapHash lntypes.Hash, amount btcutil.Amount, expiry int32,
|
||||
receiverKey [33]byte, swapPublicationDeadline time.Time,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue