Skip to content

Commit

Permalink
Merge pull request #205 from joostjager/channel-set
Browse files Browse the repository at this point in the history
loop out: allow outbound channel set for multi-loops
  • Loading branch information
joostjager committed May 21, 2020
2 parents 3f10407 + 23a1e33 commit 3316c4b
Show file tree
Hide file tree
Showing 19 changed files with 820 additions and 279 deletions.
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 {
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

0 comments on commit 3316c4b

Please sign in to comment.