Skip to content

loop out: allow outbound channel set for multi-loops #205

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
May 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -359,9 +359,8 @@ func (s *Client) resumeSwaps(ctx context.Context,
func (s *Client) LoopOut(globalCtx context.Context,
request *OutRequest) (*lntypes.Hash, btcutil.Address, error) {

log.Infof("LoopOut %v to %v (channel: %v)",
request.Amount, request.DestAddr,
request.LoopOutChannel,
log.Infof("LoopOut %v to %v (channels: %v)",
request.Amount, request.DestAddr, request.OutgoingChanSet,
)

if err := s.waitForInitialized(globalCtx); err != nil {
Expand Down
26 changes: 17 additions & 9 deletions cmd/loop/loopout.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package main
import (
"context"
"fmt"
"strconv"
"strings"
"time"

"github.com/btcsuite/btcutil"
Expand All @@ -25,10 +27,10 @@ var loopOutCommand = cli.Command{
Optionally a BASE58/bech32 encoded bitcoin destination address may be
specified. If not specified, a new wallet address will be generated.`,
Flags: []cli.Flag{
cli.Uint64Flag{
cli.StringFlag{
Name: "channel",
Usage: "the 8-byte compact channel ID of the channel " +
"to loop out",
Usage: "the comma-separated list of short " +
"channel IDs of the channels to loop out",
},
cli.StringFlag{
Name: "addr",
Expand Down Expand Up @@ -87,6 +89,17 @@ func loopOut(ctx *cli.Context) error {
return err
}

// Parse outgoing channel set.
chanStrings := strings.Split(ctx.String("channel"), ",")
var outgoingChanSet []uint64
for _, chanString := range chanStrings {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any need to check that the set of channels provided are unique? I doubt it, but wanted to check that it won't behave strangely with duplicates on the lnd side.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is checked in loopd

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know how seriously we account for the alternate client case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The alternate client would still connect to loopd?

chanID, err := strconv.ParseUint(chanString, 10, 64)
if err != nil {
return err
}
outgoingChanSet = append(outgoingChanSet, chanID)
}

var destAddr string
switch {
case ctx.IsSet("addr"):
Expand Down Expand Up @@ -145,11 +158,6 @@ func loopOut(ctx *cli.Context) error {
return err
}

var unchargeChannel uint64
if ctx.IsSet("channel") {
unchargeChannel = ctx.Uint64("channel")
}

resp, err := client.LoopOut(context.Background(), &looprpc.LoopOutRequest{
Amt: int64(amt),
Dest: destAddr,
Expand All @@ -158,7 +166,7 @@ func loopOut(ctx *cli.Context) error {
MaxSwapFee: int64(limits.maxSwapFee),
MaxPrepayRoutingFee: int64(*limits.maxPrepayRoutingFee),
MaxSwapRoutingFee: int64(*limits.maxSwapRoutingFee),
LoopOutChannel: unchargeChannel,
OutgoingChanSet: outgoingChanSet,
SweepConfTarget: sweepConfTarget,
SwapPublicationDeadline: uint64(swapDeadline.Unix()),
})
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module github.com/lightninglabs/loop

require (
github.com/btcsuite/btcd v0.20.1-beta
github.com/btcsuite/btcd v0.20.1-beta.0.20200515232429-9f0179fd2c46
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f
github.com/btcsuite/btcutil v1.0.2
github.com/coreos/bbolt v1.3.3
Expand All @@ -11,7 +11,7 @@ require (
github.com/grpc-ecosystem/grpc-gateway v1.12.2
github.com/jessevdk/go-flags v1.4.0
github.com/lightninglabs/protobuf-hex-display v1.3.3-0.20191212020323-b444784ce75d
github.com/lightningnetwork/lnd v0.10.0-beta.rc5
github.com/lightningnetwork/lnd v0.10.1-beta.rc1
github.com/lightningnetwork/lnd/queue v1.0.3
github.com/urfave/cli v1.20.0
golang.org/x/net v0.0.0-20191002035440-2ec189313ef0
Expand Down
11 changes: 11 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ github.com/btcsuite/btcd v0.0.0-20190629003639-c26ffa870fd8/go.mod h1:3J08xEfcug
github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btcd v0.20.1-beta.0.20200513120220-b470eee47728/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btcd v0.20.1-beta.0.20200515232429-9f0179fd2c46 h1:QyTpiR5nQe94vza2qkvf7Ns8XX2Rjh/vdIhO3RzGj4o=
github.com/btcsuite/btcd v0.20.1-beta.0.20200515232429-9f0179fd2c46/go.mod h1:Yktc19YNjh/Iz2//CX0vfRTS4IJKM/RKO5YZ9Fn+Pgo=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d h1:yJzD/yFppdVCf6ApMkVy8cUxV0XrxdP9rVf6D87/Mng=
Expand All @@ -34,6 +37,8 @@ github.com/btcsuite/btcutil/psbt v1.0.2 h1:gCVY3KxdoEVU7Q6TjusPO+GANIwVgr9yTLqM+
github.com/btcsuite/btcutil/psbt v1.0.2/go.mod h1:LVveMu4VaNSkIRTZu2+ut0HDBRuYjqGocxDMNS1KuGQ=
github.com/btcsuite/btcwallet v0.11.1-0.20200403222202-ada7ca077ebb h1:kkq2SSCy+OrC7GVZLIqutoHVR2yW4SJQdX70jtmuLDI=
github.com/btcsuite/btcwallet v0.11.1-0.20200403222202-ada7ca077ebb/go.mod h1:9fJNm1aXi4q9P5Nk23mmqppCy1Le3f2/JMWj9UXKkCc=
github.com/btcsuite/btcwallet v0.11.1-0.20200515224913-e0e62245ecbe h1:0m9uXDcnUc3Fv72635O/MfLbhbW+0hfSVgRiWezpkHU=
github.com/btcsuite/btcwallet v0.11.1-0.20200515224913-e0e62245ecbe/go.mod h1:9+AH3V5mcTtNXTKe+fe63fDLKGOwQbZqmvOVUef+JFE=
github.com/btcsuite/btcwallet/wallet/txauthor v1.0.0 h1:KGHMW5sd7yDdDMkCZ/JpP0KltolFsQcB973brBnfj4c=
github.com/btcsuite/btcwallet/wallet/txauthor v1.0.0/go.mod h1:VufDts7bd/zs3GV13f/lXc/0lXrPnvxD/NvmpG/FEKU=
github.com/btcsuite/btcwallet/wallet/txrules v1.0.0 h1:2VsfS0sBedcM5KmDzRMT3+b6xobqWveZGvjb+jFez5w=
Expand All @@ -46,6 +51,8 @@ github.com/btcsuite/btcwallet/walletdb v1.3.1 h1:lW1Ac3F1jJY4K11P+YQtRNcP5jFk27A
github.com/btcsuite/btcwallet/walletdb v1.3.1/go.mod h1:9cwc1Yyg4uvd4ZdfdoMnALji+V9gfWSMfxEdLdR5Vwc=
github.com/btcsuite/btcwallet/wtxmgr v1.0.0 h1:aIHgViEmZmZfe0tQQqF1xyd2qBqFWxX5vZXkkbjtbeA=
github.com/btcsuite/btcwallet/wtxmgr v1.0.0/go.mod h1:vc4gBprll6BP0UJ+AIGDaySoc7MdAmZf8kelfNb8CFY=
github.com/btcsuite/btcwallet/wtxmgr v1.1.1-0.20200515224913-e0e62245ecbe h1:yQbJVYfsKbdqDQNLxd4hhiLSiMkIygefW5mSHMsdKpc=
github.com/btcsuite/btcwallet/wtxmgr v1.1.1-0.20200515224913-e0e62245ecbe/go.mod h1:OwC0W0HhUszbWdvJvH6xvgabKSJ0lXl11YbmmqF9YXQ=
github.com/btcsuite/fastsha256 v0.0.0-20160815193821-637e65642941 h1:kij1x2aL7VE6gtx8KMIt8PGPgI5GV9LgtHFG5KaEMPY=
github.com/btcsuite/fastsha256 v0.0.0-20160815193821-637e65642941/go.mod h1:QcFA8DZHtuIAdYKCq/BzELOaznRsCvwf4zTPmaYwaig=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw=
Expand Down Expand Up @@ -157,6 +164,8 @@ github.com/lightningnetwork/lightning-onion v1.0.1 h1:qChGgS5+aPxFeR6JiUsGvanei1
github.com/lightningnetwork/lightning-onion v1.0.1/go.mod h1:rigfi6Af/KqsF7Za0hOgcyq2PNH4AN70AaMRxcJkff4=
github.com/lightningnetwork/lnd v0.10.0-beta.rc5 h1:HcX35Djwk+xoNQe/LA7HnQ11jzbq68TAcpBluhNIKqc=
github.com/lightningnetwork/lnd v0.10.0-beta.rc5/go.mod h1:mEnmP+sSgiKUFBozT3I5xEOgRAREMEWd/3lcWDrB+5E=
github.com/lightningnetwork/lnd v0.10.1-beta.rc1 h1:4uBkLHrxeIf6ad5AHlFhGcOhtbvmYwK4iTcDMixmLpw=
github.com/lightningnetwork/lnd v0.10.1-beta.rc1/go.mod h1:mRd+8n/QOlAiolWVnt1RaTzxVvOyplT3J5uYwUb/EDw=
github.com/lightningnetwork/lnd/cert v1.0.2/go.mod h1:fmtemlSMf5t4hsQmcprSoOykypAPp+9c+0d0iqTScMo=
github.com/lightningnetwork/lnd/queue v1.0.1 h1:jzJKcTy3Nj5lQrooJ3aaw9Lau3I0IwvQR5sqtjdv2R0=
github.com/lightningnetwork/lnd/queue v1.0.1/go.mod h1:vaQwexir73flPW43Mrm7JOgJHmcEFBWWSl9HlyASoms=
Expand Down Expand Up @@ -222,6 +231,8 @@ golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d h1:2+ZP7EfsZV7Vvmx3TIqSlSzATMkTAKqM14YGFPoSKjI=
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
Expand Down
6 changes: 3 additions & 3 deletions interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@ type OutRequest struct {
// client sweep tx.
SweepConfTarget int32

// LoopOutChannel optionally specifies the short channel id of the
// channel to loop out.
LoopOutChannel *uint64
// OutgoingChanSet optionally specifies the short channel ids of the
// channels that may be used to loop out.
OutgoingChanSet loopdb.ChannelSet

// SwapPublicationDeadline can be set by the client to allow the server
// delaying publication of the swap HTLC to save on chain fees.
Expand Down
2 changes: 1 addition & 1 deletion lndclient/lnd_services.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ var (
minimalCompatibleVersion = &verrpc.Version{
AppMajor: 0,
AppMinor: 10,
AppPatch: 0,
AppPatch: 1,
BuildTags: []string{
"signrpc", "walletrpc", "chainrpc", "invoicesrpc",
},
Expand Down
31 changes: 20 additions & 11 deletions lndclient/router_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,20 @@ type SendPaymentRequest struct {
// are only processed when the Invoice field is empty.
Invoice string

MaxFee btcutil.Amount
MaxCltv *int32
OutgoingChannel *uint64
Timeout time.Duration
// MaxFee is the fee limit for this payment.
MaxFee btcutil.Amount

// MaxCltv is the maximum timelock for this payment. If nil, there is no
// maximum.
MaxCltv *int32

// OutgoingChanIds is a restriction on the set of possible outgoing
// channels. If nil or empty, there is no restriction.
OutgoingChanIds []uint64

// Timeout is the payment loop timeout. After this time, no new payment
// attempts will be started.
Timeout time.Duration

// Target is the node in which the payment should be routed towards.
Target route.Vertex
Expand Down Expand Up @@ -126,17 +136,16 @@ func (r *routerClient) SendPayment(ctx context.Context,

rpcCtx := r.routerKitMac.WithMacaroonAuth(ctx)
rpcReq := &routerrpc.SendPaymentRequest{
FeeLimitSat: int64(request.MaxFee),
PaymentRequest: request.Invoice,
TimeoutSeconds: int32(request.Timeout.Seconds()),
MaxParts: request.MaxParts,
FeeLimitSat: int64(request.MaxFee),
PaymentRequest: request.Invoice,
TimeoutSeconds: int32(request.Timeout.Seconds()),
MaxParts: request.MaxParts,
OutgoingChanIds: request.OutgoingChanIds,
}
if request.MaxCltv != nil {
rpcReq.CltvLimit = *request.MaxCltv
}
if request.OutgoingChannel != nil {
rpcReq.OutgoingChanId = *request.OutgoingChannel
}

if request.LastHopPubkey != nil {
rpcReq.LastHopPubkey = request.LastHopPubkey[:]
}
Expand Down
2 changes: 1 addition & 1 deletion loopd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ var (
LoopMinRequiredLndVersion = &verrpc.Version{
AppMajor: 0,
AppMinor: 10,
AppPatch: 0,
AppPatch: 1,
BuildTags: []string{
"signrpc", "walletrpc", "chainrpc", "invoicesrpc",
},
Expand Down
14 changes: 12 additions & 2 deletions loopd/swapclient_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,19 @@ func (s *swapClientServer) LoopOut(ctx context.Context,
int64(in.SwapPublicationDeadline), 0,
),
}
if in.LoopOutChannel != 0 {
req.LoopOutChannel = &in.LoopOutChannel

switch {
case in.LoopOutChannel != 0 && len(in.OutgoingChanSet) > 0:
return nil, errors.New("loop_out_channel and outgoing_" +
"chan_ids are mutually exclusive")

case in.LoopOutChannel != 0:
req.OutgoingChanSet = loopdb.ChannelSet{in.LoopOutChannel}

default:
req.OutgoingChanSet = in.OutgoingChanSet
}

hash, htlc, err := s.impl.LoopOut(ctx, req)
if err != nil {
log.Errorf("LoopOut: %v", err)
Expand Down
10 changes: 2 additions & 8 deletions loopd/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package loopd

import (
"fmt"
"strconv"

"github.com/btcsuite/btcd/chaincfg"
"github.com/lightninglabs/loop"
Expand Down Expand Up @@ -64,13 +63,8 @@ func viewOut(swapClient *loop.Client, chainParams *chaincfg.Params) error {
fmt.Printf(" Preimage: %v\n", s.Contract.Preimage)
fmt.Printf(" Htlc address: %v\n", htlc.Address)

unchargeChannel := "any"
if s.Contract.UnchargeChannel != nil {
unchargeChannel = strconv.FormatUint(
*s.Contract.UnchargeChannel, 10,
)
}
fmt.Printf(" Uncharge channel: %v\n", unchargeChannel)
fmt.Printf(" Uncharge channels: %v\n",
s.Contract.OutgoingChanSet)
fmt.Printf(" Dest: %v\n", s.Contract.DestAddr)
fmt.Printf(" Amt: %v, Expiry: %v\n",
s.Contract.AmountRequested, s.Contract.CltvExpiry,
Expand Down
45 changes: 37 additions & 8 deletions loopdb/loopout.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"bytes"
"encoding/binary"
"fmt"
"strconv"
"strings"
"time"

"github.com/btcsuite/btcd/chaincfg"
Expand Down Expand Up @@ -34,9 +36,9 @@ type LoopOutContract struct {
// client sweep tx.
SweepConfTarget int32

// TargetChannel is the channel to loop out. If zero, any channel may
// be used.
UnchargeChannel *uint64
// OutgoingChanSet is the set of short ids of channels that may be used.
// If empty, any channel may be used.
OutgoingChanSet ChannelSet

// PrepayInvoice is the invoice that the client should pay to the
// server that will be returned if the swap is complete.
Expand All @@ -53,6 +55,34 @@ type LoopOutContract struct {
SwapPublicationDeadline time.Time
}

// ChannelSet stores a set of channels.
type ChannelSet []uint64

// String returns the human-readable representation of a channel set.
func (c ChannelSet) String() string {
channelStrings := make([]string, len(c))
for i, chanID := range c {
channelStrings[i] = strconv.FormatUint(chanID, 10)
}
return strings.Join(channelStrings, ",")
}

// NewChannelSet instantiates a new channel set and verifies that there are no
// duplicates present.
func NewChannelSet(set []uint64) (ChannelSet, error) {
// Check channel set for duplicates.
chanSet := make(map[uint64]struct{})
for _, chanID := range set {
if _, exists := chanSet[chanID]; exists {
return nil, fmt.Errorf("duplicate chan in set: id=%v",
chanID)
}
chanSet[chanID] = struct{}{}
}

return ChannelSet(set), nil
}

// LoopOut is a combination of the contract and the updates.
type LoopOut struct {
Loop
Expand Down Expand Up @@ -161,7 +191,7 @@ func deserializeLoopOutContract(value []byte, chainParams *chaincfg.Params) (
return nil, err
}
if unchargeChannel != 0 {
contract.UnchargeChannel = &unchargeChannel
contract.OutgoingChanSet = ChannelSet{unchargeChannel}
}

var deadlineNano int64
Expand Down Expand Up @@ -248,10 +278,9 @@ func serializeLoopOutContract(swap *LoopOutContract) (
return nil, err
}

var unchargeChannel uint64
if swap.UnchargeChannel != nil {
unchargeChannel = *swap.UnchargeChannel
}
// Always write no outgoing channel. This field is replaced by an
// outgoing channel set.
unchargeChannel := uint64(0)
if err := binary.Write(&b, byteOrder, unchargeChannel); err != nil {
return nil, err
}
Expand Down
Loading