mirror of
https://github.com/lightninglabs/loop
synced 2024-11-11 13:11:12 +00:00
Merge pull request #60 from joostjager/reliable-payment
lndclient: add routerrpc
This commit is contained in:
commit
31451f62f8
6
go.mod
6
go.mod
@ -1,15 +1,15 @@
|
||||
module github.com/lightninglabs/loop
|
||||
|
||||
require (
|
||||
github.com/btcsuite/btcd v0.0.0-20190315201642-aa6e0f35703c
|
||||
github.com/btcsuite/btcd v0.0.0-20190605094302-a0d1e3e36d50
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f
|
||||
github.com/btcsuite/btcutil v0.0.0-20190316010144-3ac1210f4b38
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d
|
||||
github.com/coreos/bbolt v1.3.2
|
||||
github.com/fortytw2/leaktest v1.3.0
|
||||
github.com/golang/protobuf v1.3.1
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.8.5
|
||||
github.com/jessevdk/go-flags v1.4.0
|
||||
github.com/lightningnetwork/lnd v0.5.1-beta.0.20190322220528-6ad8be25e1aa
|
||||
github.com/lightningnetwork/lnd v0.6.1-beta.0.20190605130338-880279b266e9
|
||||
github.com/lightningnetwork/lnd/queue v1.0.1
|
||||
github.com/urfave/cli v1.20.0
|
||||
golang.org/x/net v0.0.0-20190313220215-9f648a60d977
|
||||
|
29
go.sum
29
go.sum
@ -15,20 +15,24 @@ github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBA
|
||||
github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
|
||||
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
|
||||
github.com/btcsuite/btcd v0.0.0-20180823030728-d81d8877b8f3/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ=
|
||||
github.com/btcsuite/btcd v0.0.0-20181130015935-7d2daa5bfef2/go.mod h1:Jr9bmNVGZ7TH2Ux1QuP0ec+yGgh0gE9FIlkzQiI5bR0=
|
||||
github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8=
|
||||
github.com/btcsuite/btcd v0.0.0-20190315201642-aa6e0f35703c h1:5N/b57wo2KfeHCGGdcXtOPsHqkPD+veLZhK/bMg2anQ=
|
||||
github.com/btcsuite/btcd v0.0.0-20190315201642-aa6e0f35703c/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8=
|
||||
github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
|
||||
github.com/btcsuite/btcd v0.0.0-20190605094302-a0d1e3e36d50 h1:4i3KsuVA0o0KoBxAC5x+MY7RbteiMK1V7gf/G08NGIQ=
|
||||
github.com/btcsuite/btcd v0.0.0-20190605094302-a0d1e3e36d50/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
|
||||
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-20180706230648-ab6388e0c60a/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803 h1:j3AgPKKZtZStM2nyhrDSLSYgT7YHrZKdSkq1OYeLjvM=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190316010144-3ac1210f4b38 h1:GbQHMJ2u/geMPV1tbN7i7zARSoPAPuXWa44V0KYvJXU=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190316010144-3ac1210f4b38/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d h1:yJzD/yFppdVCf6ApMkVy8cUxV0XrxdP9rVf6D87/Mng=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
github.com/btcsuite/btcwallet v0.0.0-20180904010540-284e2e0e696e33d5be388f7f3d9a26db703e0c06/go.mod h1:/d7QHZsfUAruXuBhyPITqoYOmJ+nq35qPsJjz/aSpCg=
|
||||
github.com/btcsuite/btcwallet v0.0.0-20190313032608-acf3b04b0273/go.mod h1:mkOYY8/psBiL5E+Wb0V7M0o+N7NXi2SZJz6+RKkncIc=
|
||||
github.com/btcsuite/btcwallet v0.0.0-20190319010515-89ab2044f962 h1:/6EtNbubaGgo10bFyYyE6S6yXfxtc3dxKpvLHxMt5rg=
|
||||
github.com/btcsuite/btcwallet v0.0.0-20190319010515-89ab2044f962/go.mod h1:qMi4jGpAO6YRsd81RYDG7o5pBIGqN9faCioJdagLu64=
|
||||
github.com/btcsuite/btcwallet v0.0.0-20190524003533-2c05240dff28 h1:jn9dKmISiudJAihPQ2XRQenffPkhQeoBh3SCiZSfFXE=
|
||||
github.com/btcsuite/btcwallet v0.0.0-20190524003533-2c05240dff28/go.mod h1:GlcKHrCBxtujd/6coLUvczN68EkaBezgyN+JnEGVDUY=
|
||||
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=
|
||||
@ -76,8 +80,7 @@ github.com/grpc-ecosystem/grpc-gateway v1.8.5 h1:2+KSC78XiO6Qy0hIjfc1OD9H+hsaJdJ
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/jackpal/gateway v1.0.4 h1:LS5EHkLuQ6jzaHwULi0vL+JO0mU/n4yUtK8oUjHHOlM=
|
||||
github.com/jackpal/gateway v1.0.4/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA=
|
||||
github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA=
|
||||
github.com/jackpal/go-nat-pmp v0.0.0-20170405195558-28a68d0c24ad h1:heFfj7z0pGsNCekUlsFhO2jstxO4b5iQ665LjwM5mDc=
|
||||
github.com/jackpal/go-nat-pmp v0.0.0-20170405195558-28a68d0c24ad/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
|
||||
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
@ -114,12 +117,12 @@ github.com/lightninglabs/gozmq v0.0.0-20180324010646-462a8a753885/go.mod h1:KUh1
|
||||
github.com/lightninglabs/neutrino v0.0.0-20181017011010-4d6069299130/go.mod h1:KJq43Fu9ceitbJsSXMILcT4mGDNI/crKmPIkDOZXFyM=
|
||||
github.com/lightninglabs/neutrino v0.0.0-20190213031021-ae4583a89cfb/go.mod h1:g6cMQd+hfAU8pQTJAdjm6/EQREhupyd22f+CL0qYFOE=
|
||||
github.com/lightninglabs/neutrino v0.0.0-20190313035638-e1ad4c33fb18/go.mod h1:v6tz6jbuAubTrRpX8ke2KH9sJxml8KlPQTKgo9mAp1Q=
|
||||
github.com/lightninglabs/neutrino v0.0.0-20190321023416-6dac90b98052 h1:YVi5x7xCumfIVtEejy3zBAej+NqY9EONy/fRim2+azQ=
|
||||
github.com/lightninglabs/neutrino v0.0.0-20190321023416-6dac90b98052/go.mod h1:IcX2/lKnXpLkF4XzefhiJOPn5he2+kZyJE32dxX4F4E=
|
||||
github.com/lightningnetwork/lightning-onion v0.0.0-20180605012408-ac4d9da8f1d6 h1:ONLGrYJVQdbtP6CE/ff1KNWZtygRGEh12RzonTiCzPs=
|
||||
github.com/lightningnetwork/lightning-onion v0.0.0-20180605012408-ac4d9da8f1d6/go.mod h1:8EgEt4a/NUOVQd+3kk6n9aZCJ1Ssj96Pb6lCrci+6oc=
|
||||
github.com/lightningnetwork/lnd v0.5.1-beta.0.20190322220528-6ad8be25e1aa h1:nYDuZ0vMAZ2DVIMmc/B8dNrmZ3YGxe7pwD/4DxG4rxQ=
|
||||
github.com/lightningnetwork/lnd v0.5.1-beta.0.20190322220528-6ad8be25e1aa/go.mod h1:Xwk2vsL5uUgfMDQ7oHfltFZIYX5T74Htk0AA8NPzXlE=
|
||||
github.com/lightninglabs/neutrino v0.0.0-20190521105038-03deaa76ddc2 h1:309vLN4dOi1CTqylpb8CsCWo1nT59rMrluZUF2228e0=
|
||||
github.com/lightninglabs/neutrino v0.0.0-20190521105038-03deaa76ddc2/go.mod h1:/XWY/6/btfsknUpLPV8vvIZyhod61zYaUJiE8HxsFUs=
|
||||
github.com/lightningnetwork/lightning-onion v0.0.0-20190430041606-751fb4dd8b72 h1:KgmypyQfJnEf2vhwboKCtTp4mHxIcLeXPBPWDbPuzFQ=
|
||||
github.com/lightningnetwork/lightning-onion v0.0.0-20190430041606-751fb4dd8b72/go.mod h1:Sooe/CoCqa85JxqHV+IBR2HW+6t2Cv+36awSmoccswM=
|
||||
github.com/lightningnetwork/lnd v0.6.1-beta.0.20190605130338-880279b266e9 h1:zuA8x6yj68cNRqVxX4l/jQdE+AhbK4csA6izqzrupsg=
|
||||
github.com/lightningnetwork/lnd v0.6.1-beta.0.20190605130338-880279b266e9/go.mod h1:bzsQRx/1YhdXEk4TMWjKXUafgVrhcbxlDc9LKpY0VG8=
|
||||
github.com/lightningnetwork/lnd/queue v1.0.1 h1:jzJKcTy3Nj5lQrooJ3aaw9Lau3I0IwvQR5sqtjdv2R0=
|
||||
github.com/lightningnetwork/lnd/queue v1.0.1/go.mod h1:vaQwexir73flPW43Mrm7JOgJHmcEFBWWSl9HlyASoms=
|
||||
github.com/lightningnetwork/lnd/ticker v1.0.0 h1:S1b60TEGoTtCe2A0yeB+ecoj/kkS4qpwh6l+AkQEZwU=
|
||||
@ -147,6 +150,7 @@ go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20180723164146-c126467f60eb/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
@ -174,6 +178,7 @@ golang.org/x/sys v0.0.0-20180821140842-3b58ed4ad339/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190102155601-82a175fd1598/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -83,7 +83,7 @@ func (s *invoicesClient) SubscribeSingleInvoice(ctx context.Context,
|
||||
|
||||
invoiceStream, err := s.client.SubscribeSingleInvoice(
|
||||
s.invoiceMac.WithMacaroonAuth(ctx),
|
||||
&lnrpc.PaymentHash{
|
||||
&invoicesrpc.SubscribeSingleInvoiceRequest{
|
||||
RHash: hash[:],
|
||||
},
|
||||
)
|
||||
|
@ -10,11 +10,12 @@ import (
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightningnetwork/lnd/htlcswitch"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
"github.com/lightningnetwork/lnd/zpay32"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
@ -67,12 +68,12 @@ var (
|
||||
// PaymentResultAlreadyPaid is the string result returned by SendPayment
|
||||
// when the payment was already completed in a previous SendPayment
|
||||
// call.
|
||||
PaymentResultAlreadyPaid = htlcswitch.ErrAlreadyPaid.Error()
|
||||
PaymentResultAlreadyPaid = channeldb.ErrAlreadyPaid.Error()
|
||||
|
||||
// PaymentResultInFlight is the string result returned by SendPayment
|
||||
// when the payment was initiated in a previous SendPayment call and
|
||||
// still in flight.
|
||||
PaymentResultInFlight = htlcswitch.ErrPaymentInFlight.Error()
|
||||
PaymentResultInFlight = channeldb.ErrPaymentInFlight.Error()
|
||||
|
||||
paymentPollInterval = 3 * time.Second
|
||||
)
|
||||
|
@ -24,6 +24,7 @@ type LndServices struct {
|
||||
ChainNotifier ChainNotifierClient
|
||||
Signer SignerClient
|
||||
Invoices InvoicesClient
|
||||
Router RouterClient
|
||||
|
||||
ChainParams *chaincfg.Params
|
||||
|
||||
@ -121,6 +122,7 @@ func NewLndServices(lndAddress, application, network, macaroonDir,
|
||||
signerClient := newSignerClient(conn, macaroons.signerMac)
|
||||
walletKitClient := newWalletKitClient(conn, macaroons.walletKitMac)
|
||||
invoicesClient := newInvoicesClient(conn, macaroons.invoiceMac)
|
||||
routerClient := newRouterClient(conn, macaroons.routerMac)
|
||||
|
||||
cleanup := func() {
|
||||
logger.Debugf("Closing lnd connection")
|
||||
@ -145,6 +147,7 @@ func NewLndServices(lndAddress, application, network, macaroonDir,
|
||||
ChainNotifier: notifierClient,
|
||||
Signer: signerClient,
|
||||
Invoices: invoicesClient,
|
||||
Router: routerClient,
|
||||
ChainParams: chainParams,
|
||||
macaroons: macaroons,
|
||||
},
|
||||
@ -178,6 +181,7 @@ var (
|
||||
defaultInvoiceMacaroonFilename = "invoices.macaroon"
|
||||
defaultChainMacaroonFilename = "chainnotifier.macaroon"
|
||||
defaultWalletKitMacaroonFilename = "walletkit.macaroon"
|
||||
defaultRouterMacaroonFilename = "router.macaroon"
|
||||
defaultSignerFilename = "signer.macaroon"
|
||||
|
||||
// maxMsgRecvSize is the largest gRPC message our client will receive.
|
||||
|
@ -48,6 +48,9 @@ type macaroonPouch struct {
|
||||
// walletKitMac is the macaroon for the WalletKit sub-server.
|
||||
walletKitMac serializedMacaroon
|
||||
|
||||
// routerMac is the macaroon for the router sub-server.
|
||||
routerMac serializedMacaroon
|
||||
|
||||
// adminMac is the primary admin macaroon for lnd.
|
||||
adminMac serializedMacaroon
|
||||
}
|
||||
@ -87,6 +90,13 @@ func newMacaroonPouch(macaroonDir string) (*macaroonPouch, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m.routerMac, err = newSerializedMacaroon(
|
||||
filepath.Join(macaroonDir, defaultRouterMacaroonFilename),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m.adminMac, err = newSerializedMacaroon(
|
||||
filepath.Join(macaroonDir, defaultAdminMacaroonFilename),
|
||||
)
|
||||
|
238
lndclient/router_client.go
Normal file
238
lndclient/router_client.go
Normal file
@ -0,0 +1,238 @@
|
||||
package lndclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
"time"
|
||||
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"google.golang.org/grpc/codes"
|
||||
|
||||
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
)
|
||||
|
||||
// RouterClient exposes payment functionality.
|
||||
type RouterClient interface {
|
||||
// SendPayment attempts to route a payment to the final destination. The
|
||||
// call returns a payment update stream and an error stream.
|
||||
SendPayment(ctx context.Context, request SendPaymentRequest) (
|
||||
chan PaymentStatus, chan error, error)
|
||||
|
||||
// TrackPayment picks up a previously started payment and returns a
|
||||
// payment update stream and an error stream.
|
||||
TrackPayment(ctx context.Context, hash lntypes.Hash) (
|
||||
chan PaymentStatus, chan error, error)
|
||||
}
|
||||
|
||||
// PaymentStatus describe the state of a payment.
|
||||
type PaymentStatus struct {
|
||||
State routerrpc.PaymentState
|
||||
Preimage lntypes.Preimage
|
||||
Fee lnwire.MilliSatoshi
|
||||
Route *route.Route
|
||||
}
|
||||
|
||||
// SendPaymentRequest defines the payment parameters for a new payment.
|
||||
type SendPaymentRequest struct {
|
||||
Invoice string
|
||||
MaxFee btcutil.Amount
|
||||
MaxCltv *int32
|
||||
OutgoingChannel *uint64
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
// routerClient is a wrapper around the generated routerrpc proxy.
|
||||
type routerClient struct {
|
||||
client routerrpc.RouterClient
|
||||
routerKitMac serializedMacaroon
|
||||
}
|
||||
|
||||
func newRouterClient(conn *grpc.ClientConn,
|
||||
routerKitMac serializedMacaroon) *routerClient {
|
||||
|
||||
return &routerClient{
|
||||
client: routerrpc.NewRouterClient(conn),
|
||||
routerKitMac: routerKitMac,
|
||||
}
|
||||
}
|
||||
|
||||
// SendPayment attempts to route a payment to the final destination. The call
|
||||
// returns a payment update stream and an error stream.
|
||||
func (r *routerClient) SendPayment(ctx context.Context,
|
||||
request SendPaymentRequest) (chan PaymentStatus, chan error, error) {
|
||||
|
||||
rpcCtx := r.routerKitMac.WithMacaroonAuth(ctx)
|
||||
rpcReq := &routerrpc.SendPaymentRequest{
|
||||
FeeLimitSat: int64(request.MaxFee),
|
||||
PaymentRequest: request.Invoice,
|
||||
TimeoutSeconds: int32(request.Timeout.Seconds()),
|
||||
}
|
||||
if request.MaxCltv != nil {
|
||||
rpcReq.CltvLimit = *request.MaxCltv
|
||||
}
|
||||
if request.OutgoingChannel != nil {
|
||||
rpcReq.OutgoingChanId = *request.OutgoingChannel
|
||||
}
|
||||
|
||||
stream, err := r.client.SendPayment(rpcCtx, rpcReq)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return r.trackPayment(ctx, stream)
|
||||
}
|
||||
|
||||
// TrackPayment picks up a previously started payment and returns a payment
|
||||
// update stream and an error stream.
|
||||
func (r *routerClient) TrackPayment(ctx context.Context,
|
||||
hash lntypes.Hash) (chan PaymentStatus, chan error, error) {
|
||||
|
||||
ctx = r.routerKitMac.WithMacaroonAuth(ctx)
|
||||
stream, err := r.client.TrackPayment(
|
||||
ctx, &routerrpc.TrackPaymentRequest{
|
||||
PaymentHash: hash[:],
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return r.trackPayment(ctx, stream)
|
||||
}
|
||||
|
||||
// trackPayment takes an update stream from either a SendPayment or a
|
||||
// TrackPayment rpc call and converts it into distinct update and error streams.
|
||||
func (r *routerClient) trackPayment(ctx context.Context,
|
||||
stream routerrpc.Router_TrackPaymentClient) (chan PaymentStatus,
|
||||
chan error, error) {
|
||||
|
||||
statusChan := make(chan PaymentStatus)
|
||||
errorChan := make(chan error, 1)
|
||||
go func() {
|
||||
for {
|
||||
rpcStatus, err := stream.Recv()
|
||||
if err != nil {
|
||||
switch status.Convert(err).Code() {
|
||||
|
||||
// NotFound is only expected as a response to
|
||||
// TrackPayment.
|
||||
case codes.NotFound:
|
||||
err = channeldb.ErrPaymentNotInitiated
|
||||
|
||||
// NotFound is only expected as a response to
|
||||
// SendPayment.
|
||||
case codes.AlreadyExists:
|
||||
err = channeldb.ErrAlreadyPaid
|
||||
}
|
||||
|
||||
errorChan <- err
|
||||
return
|
||||
}
|
||||
|
||||
status, err := unmarshallPaymentStatus(rpcStatus)
|
||||
if err != nil {
|
||||
errorChan <- err
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case statusChan <- *status:
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return statusChan, errorChan, nil
|
||||
}
|
||||
|
||||
// unmarshallPaymentStatus converts an rpc status update to the PaymentStatus
|
||||
// type that is used throughout the application.
|
||||
func unmarshallPaymentStatus(rpcStatus *routerrpc.PaymentStatus) (
|
||||
*PaymentStatus, error) {
|
||||
|
||||
status := PaymentStatus{
|
||||
State: rpcStatus.State,
|
||||
}
|
||||
|
||||
if status.State == routerrpc.PaymentState_SUCCEEDED {
|
||||
preimage, err := lntypes.MakePreimage(
|
||||
rpcStatus.Preimage,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
status.Preimage = preimage
|
||||
|
||||
status.Fee = lnwire.MilliSatoshi(
|
||||
rpcStatus.Route.TotalFeesMsat,
|
||||
)
|
||||
|
||||
if rpcStatus.Route != nil {
|
||||
route, err := unmarshallRoute(rpcStatus.Route)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
status.Route = route
|
||||
}
|
||||
}
|
||||
|
||||
return &status, nil
|
||||
}
|
||||
|
||||
// unmarshallRoute unmarshalls an rpc route.
|
||||
func unmarshallRoute(rpcroute *lnrpc.Route) (
|
||||
*route.Route, error) {
|
||||
|
||||
hops := make([]*route.Hop, len(rpcroute.Hops))
|
||||
for i, hop := range rpcroute.Hops {
|
||||
routeHop, err := unmarshallHop(hop)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hops[i] = routeHop
|
||||
}
|
||||
|
||||
// TODO(joostjager): Fetch self node from lnd.
|
||||
selfNode := route.Vertex{}
|
||||
|
||||
route, err := route.NewRouteFromHops(
|
||||
lnwire.MilliSatoshi(rpcroute.TotalAmtMsat),
|
||||
rpcroute.TotalTimeLock,
|
||||
selfNode,
|
||||
hops,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return route, nil
|
||||
}
|
||||
|
||||
// unmarshallKnownPubkeyHop unmarshalls an rpc hop.
|
||||
func unmarshallHop(hop *lnrpc.Hop) (*route.Hop, error) {
|
||||
pubKey, err := hex.DecodeString(hop.PubKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot decode pubkey %s", hop.PubKey)
|
||||
}
|
||||
|
||||
var pubKeyBytes [33]byte
|
||||
copy(pubKeyBytes[:], pubKey)
|
||||
|
||||
return &route.Hop{
|
||||
OutgoingTimeLock: hop.Expiry,
|
||||
AmtToForward: lnwire.MilliSatoshi(hop.AmtToForwardMsat),
|
||||
PubKeyBytes: pubKeyBytes,
|
||||
ChannelID: hop.ChanId,
|
||||
}, nil
|
||||
}
|
@ -24,6 +24,7 @@ func NewMockLnd() *LndMockServices {
|
||||
chainNotifier := &mockChainNotifier{}
|
||||
signer := &mockSigner{}
|
||||
invoices := &mockInvoices{}
|
||||
router := &mockRouter{}
|
||||
|
||||
lnd := LndMockServices{
|
||||
LndServices: lndclient.LndServices{
|
||||
@ -32,6 +33,7 @@ func NewMockLnd() *LndMockServices {
|
||||
ChainNotifier: chainNotifier,
|
||||
Signer: signer,
|
||||
Invoices: invoices,
|
||||
Router: router,
|
||||
ChainParams: &chaincfg.TestNet3Params,
|
||||
},
|
||||
SendPaymentChannel: make(chan PaymentChannelMessage),
|
||||
@ -44,6 +46,9 @@ func NewMockLnd() *LndMockServices {
|
||||
SettleInvoiceChannel: make(chan lntypes.Preimage),
|
||||
SingleInvoiceSubcribeChannel: make(chan *SingleInvoiceSubscription),
|
||||
|
||||
RouterSendPaymentChannel: make(chan RouterPaymentChannelMessage),
|
||||
TrackPaymentChannel: make(chan TrackPaymentMessage),
|
||||
|
||||
FailInvoiceChannel: make(chan lntypes.Hash, 2),
|
||||
epochChannel: make(chan int32),
|
||||
Height: testStartingHeight,
|
||||
@ -53,6 +58,7 @@ func NewMockLnd() *LndMockServices {
|
||||
chainNotifier.lnd = &lnd
|
||||
walletKit.lnd = &lnd
|
||||
invoices.lnd = &lnd
|
||||
router.lnd = &lnd
|
||||
|
||||
lnd.WaitForFinished = func() {
|
||||
chainNotifier.WaitForFinished()
|
||||
@ -69,6 +75,21 @@ type PaymentChannelMessage struct {
|
||||
Done chan lndclient.PaymentResult
|
||||
}
|
||||
|
||||
// TrackPaymentMessage is the data that passed through TrackPaymentChannel.
|
||||
type TrackPaymentMessage struct {
|
||||
Hash lntypes.Hash
|
||||
|
||||
Updates chan lndclient.PaymentStatus
|
||||
Errors chan error
|
||||
}
|
||||
|
||||
// RouterPaymentChannelMessage is the data that passed through RouterSendPaymentChannel.
|
||||
type RouterPaymentChannelMessage struct {
|
||||
lndclient.SendPaymentRequest
|
||||
|
||||
TrackPaymentMessage
|
||||
}
|
||||
|
||||
// SingleInvoiceSubscription contains the single invoice subscribers
|
||||
type SingleInvoiceSubscription struct {
|
||||
Hash lntypes.Hash
|
||||
@ -94,6 +115,9 @@ type LndMockServices struct {
|
||||
|
||||
SingleInvoiceSubcribeChannel chan *SingleInvoiceSubscription
|
||||
|
||||
RouterSendPaymentChannel chan RouterPaymentChannelMessage
|
||||
TrackPaymentChannel chan TrackPaymentMessage
|
||||
|
||||
Height int32
|
||||
|
||||
WaitForFinished func()
|
||||
|
43
test/router_mock.go
Normal file
43
test/router_mock.go
Normal file
@ -0,0 +1,43 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"github.com/lightninglabs/loop/lndclient"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type mockRouter struct {
|
||||
lnd *LndMockServices
|
||||
}
|
||||
|
||||
func (r *mockRouter) SendPayment(ctx context.Context,
|
||||
request lndclient.SendPaymentRequest) (chan lndclient.PaymentStatus,
|
||||
chan error, error) {
|
||||
|
||||
statusChan := make(chan lndclient.PaymentStatus)
|
||||
errorChan := make(chan error)
|
||||
|
||||
r.lnd.RouterSendPaymentChannel <- RouterPaymentChannelMessage{
|
||||
SendPaymentRequest: request,
|
||||
TrackPaymentMessage: TrackPaymentMessage{
|
||||
Updates: statusChan,
|
||||
Errors: errorChan,
|
||||
},
|
||||
}
|
||||
|
||||
return statusChan, errorChan, nil
|
||||
}
|
||||
|
||||
func (r *mockRouter) TrackPayment(ctx context.Context,
|
||||
hash lntypes.Hash) (chan lndclient.PaymentStatus, chan error, error) {
|
||||
|
||||
statusChan := make(chan lndclient.PaymentStatus)
|
||||
errorChan := make(chan error)
|
||||
r.lnd.TrackPaymentChannel <- TrackPaymentMessage{
|
||||
Hash: hash,
|
||||
Updates: statusChan,
|
||||
Errors: errorChan,
|
||||
}
|
||||
|
||||
return statusChan, errorChan, nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user