Skip to content

Commit 4a714e0

Browse files
authored
Merge pull request #178 from joostjager/loopout-routerrpc
loopout: allow multi-loop
2 parents 97403b9 + 6acd76e commit 4a714e0

File tree

7 files changed

+197
-59
lines changed

7 files changed

+197
-59
lines changed

go.mod

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,17 @@ module github.com/lightninglabs/loop
33
require (
44
github.com/btcsuite/btcd v0.20.1-beta
55
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f
6-
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d
6+
github.com/btcsuite/btcutil v1.0.2
77
github.com/coreos/bbolt v1.3.3
88
github.com/fortytw2/leaktest v1.3.0
99
github.com/golang/protobuf v1.3.2
1010
github.com/google/go-cmp v0.3.1 // indirect
1111
github.com/grpc-ecosystem/grpc-gateway v1.12.2
1212
github.com/jessevdk/go-flags v1.4.0
1313
github.com/lightninglabs/protobuf-hex-display v1.3.3-0.20191212020323-b444784ce75d
14-
github.com/lightningnetwork/lnd v0.9.0-beta-rc3.0.20200121213302-a2977c4438b5
15-
github.com/lightningnetwork/lnd/queue v1.0.2
14+
github.com/lightningnetwork/lnd v0.10.0-beta.rc1
15+
github.com/lightningnetwork/lnd/queue v1.0.3
1616
github.com/urfave/cli v1.20.0
17-
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472 // indirect
1817
golang.org/x/net v0.0.0-20191002035440-2ec189313ef0
1918
golang.org/x/text v0.3.2 // indirect
2019
google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c

go.sum

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,22 @@ github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9
2727
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
2828
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d h1:yJzD/yFppdVCf6ApMkVy8cUxV0XrxdP9rVf6D87/Mng=
2929
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
30-
github.com/btcsuite/btcwallet v0.11.0 h1:XhwqdhEchy5a0q6R+y3F82roD2hYycPCHovgNyJS08w=
31-
github.com/btcsuite/btcwallet v0.11.0/go.mod h1:qtPAohN1ioo0pvJt/j7bZM8ANBWlYWVCVFL0kkijs7s=
30+
github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts=
31+
github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts=
32+
github.com/btcsuite/btcutil/psbt v1.0.2 h1:gCVY3KxdoEVU7Q6TjusPO+GANIwVgr9yTLqM+a6CZr8=
33+
github.com/btcsuite/btcutil/psbt v1.0.2/go.mod h1:LVveMu4VaNSkIRTZu2+ut0HDBRuYjqGocxDMNS1KuGQ=
34+
github.com/btcsuite/btcwallet v0.11.1-0.20200403222202-ada7ca077ebb h1:kkq2SSCy+OrC7GVZLIqutoHVR2yW4SJQdX70jtmuLDI=
35+
github.com/btcsuite/btcwallet v0.11.1-0.20200403222202-ada7ca077ebb/go.mod h1:9fJNm1aXi4q9P5Nk23mmqppCy1Le3f2/JMWj9UXKkCc=
3236
github.com/btcsuite/btcwallet/wallet/txauthor v1.0.0 h1:KGHMW5sd7yDdDMkCZ/JpP0KltolFsQcB973brBnfj4c=
3337
github.com/btcsuite/btcwallet/wallet/txauthor v1.0.0/go.mod h1:VufDts7bd/zs3GV13f/lXc/0lXrPnvxD/NvmpG/FEKU=
3438
github.com/btcsuite/btcwallet/wallet/txrules v1.0.0 h1:2VsfS0sBedcM5KmDzRMT3+b6xobqWveZGvjb+jFez5w=
3539
github.com/btcsuite/btcwallet/wallet/txrules v1.0.0/go.mod h1:UwQE78yCerZ313EXZwEiu3jNAtfXj2n2+c8RWiE/WNA=
3640
github.com/btcsuite/btcwallet/wallet/txsizes v1.0.0 h1:6DxkcoMnCPY4E9cUDPB5tbuuf40SmmMkSQkoE8vCT+s=
3741
github.com/btcsuite/btcwallet/wallet/txsizes v1.0.0/go.mod h1:pauEU8UuMFiThe5PB3EO+gO5kx87Me5NvdQDsTuq6cs=
3842
github.com/btcsuite/btcwallet/walletdb v1.0.0/go.mod h1:bZTy9RyYZh9fLnSua+/CD48TJtYJSHjjYcSaszuxCCk=
39-
github.com/btcsuite/btcwallet/walletdb v1.1.0 h1:JHAL7wZ8pX4SULabeAv/wPO9sseRWMGzE80lfVmRw6Y=
40-
github.com/btcsuite/btcwallet/walletdb v1.1.0/go.mod h1:bZTy9RyYZh9fLnSua+/CD48TJtYJSHjjYcSaszuxCCk=
43+
github.com/btcsuite/btcwallet/walletdb v1.2.0/go.mod h1:9cwc1Yyg4uvd4ZdfdoMnALji+V9gfWSMfxEdLdR5Vwc=
44+
github.com/btcsuite/btcwallet/walletdb v1.3.1 h1:lW1Ac3F1jJY4K11P+YQtRNcP5jFk27ASfrV7C6mvRU0=
45+
github.com/btcsuite/btcwallet/walletdb v1.3.1/go.mod h1:9cwc1Yyg4uvd4ZdfdoMnALji+V9gfWSMfxEdLdR5Vwc=
4146
github.com/btcsuite/btcwallet/wtxmgr v1.0.0 h1:aIHgViEmZmZfe0tQQqF1xyd2qBqFWxX5vZXkkbjtbeA=
4247
github.com/btcsuite/btcwallet/wtxmgr v1.0.0/go.mod h1:vc4gBprll6BP0UJ+AIGDaySoc7MdAmZf8kelfNb8CFY=
4348
github.com/btcsuite/fastsha256 v0.0.0-20160815193821-637e65642941 h1:kij1x2aL7VE6gtx8KMIt8PGPgI5GV9LgtHFG5KaEMPY=
@@ -137,18 +142,20 @@ github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf h1:HZKvJUHlcXI
137142
github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf/go.mod h1:vxmQPeIQxPf6Jf9rM8R+B4rKBqLA2AjttNxkFBL2Plk=
138143
github.com/lightninglabs/neutrino v0.11.0 h1:lPpYFCtsfJX2W5zI4pWycPmbbBdr7zU+BafYdLoD6k0=
139144
github.com/lightninglabs/neutrino v0.11.0/go.mod h1:CuhF0iuzg9Sp2HO6ZgXgayviFTn1QHdSTJlMncK80wg=
145+
github.com/lightninglabs/neutrino v0.11.1-0.20200316235139-bffc52e8f200 h1:j4iZ1XlUAPQmW6oSzMcJGILYsRHNs+4O3Gk+2Ms5Dww=
146+
github.com/lightninglabs/neutrino v0.11.1-0.20200316235139-bffc52e8f200/go.mod h1:MlZmoKa7CJP3eR1s5yB7Rm5aSyadpKkxqAwLQmog7N0=
140147
github.com/lightninglabs/protobuf-hex-display v1.3.3-0.20191212020323-b444784ce75d h1:QWD/5MPnaZfUVP7P8wLa4M8Td2DI7XXHXt2vhVtUgGI=
141148
github.com/lightninglabs/protobuf-hex-display v1.3.3-0.20191212020323-b444784ce75d/go.mod h1:KDb67YMzoh4eudnzClmvs2FbiLG9vxISmLApUkCa4uI=
142149
github.com/lightningnetwork/lightning-onion v1.0.1 h1:qChGgS5+aPxFeR6JiUsGvanei1bn6WJpYbvosw/1604=
143150
github.com/lightningnetwork/lightning-onion v1.0.1/go.mod h1:rigfi6Af/KqsF7Za0hOgcyq2PNH4AN70AaMRxcJkff4=
144-
github.com/lightningnetwork/lnd v0.9.0-beta-rc3.0.20200121213302-a2977c4438b5 h1:qLO+I/7EggqegY8uu6k9TuE/5Tc5zV2L8gQGfgEV9LY=
145-
github.com/lightningnetwork/lnd v0.9.0-beta-rc3.0.20200121213302-a2977c4438b5/go.mod h1:sxMH8WLTqgERzBCrTrBCuDkT6SqAjZhnOWiAQSNzJ8A=
146-
github.com/lightningnetwork/lnd/cert v1.0.0 h1:J0gtf2UNQX2U+/j5cXnX2wIMSTuJuwrXv7m9qJr2wtw=
147-
github.com/lightningnetwork/lnd/cert v1.0.0/go.mod h1:fmtemlSMf5t4hsQmcprSoOykypAPp+9c+0d0iqTScMo=
151+
github.com/lightningnetwork/lnd v0.10.0-beta.rc1 h1:eCLOYu+Erw+GIxMJHbkTRn23eBVN3s7u188iyw3747Y=
152+
github.com/lightningnetwork/lnd v0.10.0-beta.rc1/go.mod h1:UNd+jGvZbtHFAR4syN6ab2euqh5DNzbVjjomZZFlCEY=
153+
github.com/lightningnetwork/lnd/cert v1.0.1 h1:D+FOL2J/MzoolaWSEZJZc5Qb7vqy6P8IX1HGzqHWnQM=
154+
github.com/lightningnetwork/lnd/cert v1.0.1/go.mod h1:fmtemlSMf5t4hsQmcprSoOykypAPp+9c+0d0iqTScMo=
148155
github.com/lightningnetwork/lnd/queue v1.0.1 h1:jzJKcTy3Nj5lQrooJ3aaw9Lau3I0IwvQR5sqtjdv2R0=
149156
github.com/lightningnetwork/lnd/queue v1.0.1/go.mod h1:vaQwexir73flPW43Mrm7JOgJHmcEFBWWSl9HlyASoms=
150-
github.com/lightningnetwork/lnd/queue v1.0.2 h1:Hx43fmTz2pDH4fIYDr57P/M5cB+GEMLzN+eif8576Xo=
151-
github.com/lightningnetwork/lnd/queue v1.0.2/go.mod h1:YTkTVZCxz8tAYreH27EO3s8572ODumWrNdYW2E/YKxg=
157+
github.com/lightningnetwork/lnd/queue v1.0.3 h1:5ufYVE7lh9GJnL1wOoeO3bZ3aAHWNnkNFHP7W1+NiJ8=
158+
github.com/lightningnetwork/lnd/queue v1.0.3/go.mod h1:YTkTVZCxz8tAYreH27EO3s8572ODumWrNdYW2E/YKxg=
152159
github.com/lightningnetwork/lnd/ticker v1.0.0 h1:S1b60TEGoTtCe2A0yeB+ecoj/kkS4qpwh6l+AkQEZwU=
153160
github.com/lightningnetwork/lnd/ticker v1.0.0/go.mod h1:iaLXJiVgI1sPANIF2qYYUJXjoksPNvGNYowB8aRbpX0=
154161
github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 h1:sjOGyegMIhvgfq5oaue6Td+hxZuf3tDC8lAPrFldqFw=
@@ -200,8 +207,8 @@ golang.org/x/crypto v0.0.0-20180723164146-c126467f60eb/go.mod h1:6SG95UA2DQfeDnf
200207
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
201208
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
202209
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
203-
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472 h1:Gv7RPwsi3eZ2Fgewe3CBsuOebPwO27PoXzRpJPsvSSM=
204-
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
210+
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d h1:2+ZP7EfsZV7Vvmx3TIqSlSzATMkTAKqM14YGFPoSKjI=
211+
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
205212
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
206213
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
207214
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -225,6 +232,7 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
225232
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
226233
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
227234
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
235+
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
228236
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
229237
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
230238
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=

lndclient/router_client.go

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,22 @@ type RouterClient interface {
3434

3535
// PaymentStatus describe the state of a payment.
3636
type PaymentStatus struct {
37-
State routerrpc.PaymentState
38-
Preimage lntypes.Preimage
39-
Fee lnwire.MilliSatoshi
40-
Route *route.Route
37+
State lnrpc.Payment_PaymentStatus
38+
Preimage lntypes.Preimage
39+
Fee lnwire.MilliSatoshi
40+
Value lnwire.MilliSatoshi
41+
InFlightAmt lnwire.MilliSatoshi
42+
InFlightHtlcs int
43+
}
44+
45+
func (p PaymentStatus) String() string {
46+
text := fmt.Sprintf("state=%v", p.State)
47+
if p.State == lnrpc.Payment_IN_FLIGHT {
48+
text += fmt.Sprintf(", inflight_htlcs=%v, inflight_amt=%v",
49+
p.InFlightHtlcs, p.InFlightAmt)
50+
}
51+
52+
return text
4153
}
4254

4355
// SendPaymentRequest defines the payment parameters for a new payment.
@@ -81,6 +93,10 @@ type SendPaymentRequest struct {
8193
// LastHopPubkey is the pubkey of the last hop of the route taken
8294
// for this payment. If empty, any hop may be used.
8395
LastHopPubkey *route.Vertex
96+
97+
// The maximum number of partial payments that may be used to complete
98+
// the full amount.
99+
MaxShards uint32
84100
}
85101

86102
// routerClient is a wrapper around the generated routerrpc proxy.
@@ -108,6 +124,7 @@ func (r *routerClient) SendPayment(ctx context.Context,
108124
FeeLimitSat: int64(request.MaxFee),
109125
PaymentRequest: request.Invoice,
110126
TimeoutSeconds: int32(request.Timeout.Seconds()),
127+
MaxShards: request.MaxShards,
111128
}
112129
if request.MaxCltv != nil {
113130
rpcReq.CltvLimit = *request.MaxCltv
@@ -170,7 +187,7 @@ func (r *routerClient) trackPayment(ctx context.Context,
170187
errorChan := make(chan error, 1)
171188
go func() {
172189
for {
173-
rpcStatus, err := stream.Recv()
190+
payment, err := stream.Recv()
174191
if err != nil {
175192
switch status.Convert(err).Code() {
176193

@@ -189,7 +206,7 @@ func (r *routerClient) trackPayment(ctx context.Context,
189206
return
190207
}
191208

192-
status, err := unmarshallPaymentStatus(rpcStatus)
209+
status, err := unmarshallPaymentStatus(payment)
193210
if err != nil {
194211
errorChan <- err
195212
return
@@ -208,33 +225,36 @@ func (r *routerClient) trackPayment(ctx context.Context,
208225

209226
// unmarshallPaymentStatus converts an rpc status update to the PaymentStatus
210227
// type that is used throughout the application.
211-
func unmarshallPaymentStatus(rpcStatus *routerrpc.PaymentStatus) (
228+
func unmarshallPaymentStatus(rpcPayment *lnrpc.Payment) (
212229
*PaymentStatus, error) {
213230

214231
status := PaymentStatus{
215-
State: rpcStatus.State,
232+
State: rpcPayment.Status,
216233
}
217234

218-
if status.State == routerrpc.PaymentState_SUCCEEDED {
219-
preimage, err := lntypes.MakePreimage(
220-
rpcStatus.Preimage,
235+
if status.State == lnrpc.Payment_SUCCEEDED {
236+
preimage, err := lntypes.MakePreimageFromStr(
237+
rpcPayment.PaymentPreimage,
221238
)
222239
if err != nil {
223240
return nil, err
224241
}
225242
status.Preimage = preimage
243+
status.Fee = lnwire.MilliSatoshi(rpcPayment.FeeMsat)
244+
status.Value = lnwire.MilliSatoshi(rpcPayment.ValueMsat)
245+
}
226246

227-
status.Fee = lnwire.MilliSatoshi(
228-
rpcStatus.Route.TotalFeesMsat,
229-
)
230-
231-
if rpcStatus.Route != nil {
232-
route, err := unmarshallRoute(rpcStatus.Route)
233-
if err != nil {
234-
return nil, err
235-
}
236-
status.Route = route
247+
for _, htlc := range rpcPayment.Htlcs {
248+
if htlc.Status != lnrpc.HTLCAttempt_IN_FLIGHT {
249+
continue
237250
}
251+
252+
status.InFlightHtlcs++
253+
254+
lastHop := htlc.Route.Hops[len(htlc.Route.Hops)-1]
255+
status.InFlightAmt += lnwire.MilliSatoshi(
256+
lastHop.AmtToForwardMsat,
257+
)
238258
}
239259

240260
return &status, nil

loopout.go

Lines changed: 116 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"crypto/rand"
66
"crypto/sha256"
7+
"errors"
78
"fmt"
89
"time"
910

@@ -14,6 +15,8 @@ import (
1415
"github.com/lightninglabs/loop/swap"
1516
"github.com/lightninglabs/loop/sweep"
1617
"github.com/lightningnetwork/lnd/chainntnfs"
18+
"github.com/lightningnetwork/lnd/channeldb"
19+
"github.com/lightningnetwork/lnd/lnrpc"
1720
"github.com/lightningnetwork/lnd/lntypes"
1821
)
1922

@@ -36,6 +39,16 @@ var (
3639
//
3740
// TODO(wilmer): tune?
3841
DefaultSweepConfTargetDelta = DefaultSweepConfTarget * 2
42+
43+
// paymentTimeout is the timeout for the loop out payment loop as
44+
// communicated to lnd.
45+
paymentTimeout = time.Minute
46+
)
47+
48+
const (
49+
// loopOutMaxShards defines that maximum number of shards that may be
50+
// used for a loop out swap.
51+
loopOutMaxShards = 5
3952
)
4053

4154
// loopOutSwap contains all the in-memory state related to a pending loop out
@@ -388,19 +401,120 @@ func (s *loopOutSwap) persistState(ctx context.Context) error {
388401
func (s *loopOutSwap) payInvoices(ctx context.Context) {
389402
// Pay the swap invoice.
390403
s.log.Infof("Sending swap payment %v", s.SwapInvoice)
391-
s.swapPaymentChan = s.lnd.Client.PayInvoice(
404+
s.swapPaymentChan = s.payInvoice(
392405
ctx, s.SwapInvoice, s.MaxSwapRoutingFee,
393406
s.LoopOutContract.UnchargeChannel,
394407
)
395408

396409
// Pay the prepay invoice.
397410
s.log.Infof("Sending prepayment %v", s.PrepayInvoice)
398-
s.prePaymentChan = s.lnd.Client.PayInvoice(
411+
s.prePaymentChan = s.payInvoice(
399412
ctx, s.PrepayInvoice, s.MaxPrepayRoutingFee,
400413
nil,
401414
)
402415
}
403416

417+
// payInvoice pays a single invoice.
418+
func (s *loopOutSwap) payInvoice(ctx context.Context, invoice string,
419+
maxFee btcutil.Amount,
420+
outgoingChannel *uint64) chan lndclient.PaymentResult {
421+
422+
resultChan := make(chan lndclient.PaymentResult)
423+
424+
go func() {
425+
var result lndclient.PaymentResult
426+
427+
status, err := s.payInvoiceAsync(
428+
ctx, invoice, maxFee, outgoingChannel,
429+
)
430+
if err != nil {
431+
result.Err = err
432+
} else {
433+
result.Preimage = status.Preimage
434+
result.PaidFee = status.Fee.ToSatoshis()
435+
result.PaidAmt = status.Value.ToSatoshis()
436+
}
437+
438+
select {
439+
case resultChan <- result:
440+
case <-ctx.Done():
441+
}
442+
}()
443+
444+
return resultChan
445+
}
446+
447+
// payInvoiceAsync is the asynchronously executed part of paying an invoice.
448+
func (s *loopOutSwap) payInvoiceAsync(ctx context.Context,
449+
invoice string, maxFee btcutil.Amount, outgoingChannel *uint64) (
450+
*lndclient.PaymentStatus, error) {
451+
452+
// Extract hash from payment request. Unfortunately the request
453+
// components aren't available directly.
454+
chainParams := s.lnd.ChainParams
455+
hash, _, err := swap.DecodeInvoice(chainParams, invoice)
456+
if err != nil {
457+
return nil, err
458+
}
459+
460+
req := lndclient.SendPaymentRequest{
461+
MaxFee: maxFee,
462+
Invoice: invoice,
463+
OutgoingChannel: outgoingChannel,
464+
Timeout: paymentTimeout,
465+
MaxShards: loopOutMaxShards,
466+
}
467+
468+
// Lookup state of the swap payment.
469+
paymentStateCtx, cancel := context.WithCancel(ctx)
470+
defer cancel()
471+
472+
payStatusChan, payErrChan, err := s.lnd.Router.SendPayment(
473+
paymentStateCtx, req,
474+
)
475+
if err != nil {
476+
return nil, err
477+
}
478+
479+
for {
480+
select {
481+
// Payment advanced to the next state.
482+
case payState := <-payStatusChan:
483+
s.log.Infof("Payment %v: %v", hash, payState)
484+
485+
switch payState.State {
486+
case lnrpc.Payment_SUCCEEDED:
487+
return &payState, nil
488+
489+
case lnrpc.Payment_FAILED:
490+
return nil, errors.New("payment failed")
491+
492+
case lnrpc.Payment_IN_FLIGHT:
493+
// Continue waiting for final state.
494+
495+
default:
496+
return nil, errors.New("unknown payment state")
497+
}
498+
499+
// Abort the swap in case of an error. An unknown payment error
500+
// from TrackPayment is no longer expected here.
501+
case err := <-payErrChan:
502+
if err != channeldb.ErrAlreadyPaid {
503+
return nil, err
504+
}
505+
506+
payStatusChan, payErrChan, err =
507+
s.lnd.Router.TrackPayment(paymentStateCtx, hash)
508+
if err != nil {
509+
return nil, err
510+
}
511+
512+
case <-ctx.Done():
513+
return nil, ctx.Err()
514+
}
515+
}
516+
}
517+
404518
// waitForConfirmedHtlc waits for a confirmed htlc to appear on the chain. In
405519
// case we haven't revealed the preimage yet, it also monitors block height and
406520
// off-chain payment failure.

0 commit comments

Comments
 (0)