From 0c9fcd790e235c601de35a18690e835d8463c74c Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Wed, 17 Jun 2020 22:25:57 +0200 Subject: [PATCH] lndclient: move to github.com/lightninglabs/lndclient --- client.go | 2 +- client_test.go | 2 +- config.go | 2 +- executor.go | 2 +- go.mod | 3 +- go.sum | 9 +- lndclient/basic_client.go | 136 ---- lndclient/chainnotifier_client.go | 239 ------ lndclient/invoices_client.go | 166 ---- lndclient/lightning_client.go | 1221 ----------------------------- lndclient/lnd_services.go | 463 ----------- lndclient/lnd_services_test.go | 158 ---- lndclient/log.go | 23 - lndclient/macaroon_pouch.go | 118 --- lndclient/router_client.go | 416 ---------- lndclient/signer_client.go | 254 ------ lndclient/versioner_client.go | 68 -- lndclient/walletkit_client.go | 349 --------- loopd/daemon.go | 9 +- loopd/log.go | 2 +- loopd/run.go | 4 +- loopd/swapclient_server.go | 2 +- loopd/utils.go | 2 +- loopd/view.go | 13 +- loopin.go | 2 +- loopin_test.go | 2 +- loopout.go | 2 +- loopout_test.go | 2 +- lsat/interceptor.go | 2 +- lsat/interceptor_test.go | 2 +- swap.go | 2 +- swap/net.go | 27 - swap/tx.go | 24 - sweep/sweeper.go | 2 +- test/context.go | 2 +- test/invoices_mock.go | 2 +- test/lightning_client_mock.go | 2 +- test/lnd_services_mock.go | 2 +- test/router_mock.go | 2 +- test/versioner_mock.go | 2 +- test/walletkit_mock.go | 2 +- testcontext_test.go | 2 +- 42 files changed, 44 insertions(+), 3702 deletions(-) delete mode 100644 lndclient/basic_client.go delete mode 100644 lndclient/chainnotifier_client.go delete mode 100644 lndclient/invoices_client.go delete mode 100644 lndclient/lightning_client.go delete mode 100644 lndclient/lnd_services.go delete mode 100644 lndclient/lnd_services_test.go delete mode 100644 lndclient/log.go delete mode 100644 lndclient/macaroon_pouch.go delete mode 100644 lndclient/router_client.go delete mode 100644 lndclient/signer_client.go delete mode 100644 lndclient/versioner_client.go delete mode 100644 lndclient/walletkit_client.go delete mode 100644 swap/net.go diff --git a/client.go b/client.go index 3c9fb7d..fe73cf5 100644 --- a/client.go +++ b/client.go @@ -9,7 +9,7 @@ import ( "time" "github.com/btcsuite/btcutil" - "github.com/lightninglabs/loop/lndclient" + "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop/loopdb" "github.com/lightninglabs/loop/lsat" "github.com/lightninglabs/loop/swap" diff --git a/client_test.go b/client_test.go index b381a50..911732a 100644 --- a/client_test.go +++ b/client_test.go @@ -9,7 +9,7 @@ import ( "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcutil" - "github.com/lightninglabs/loop/lndclient" + "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop/loopdb" "github.com/lightninglabs/loop/test" "github.com/lightningnetwork/lnd/lnrpc" diff --git a/config.go b/config.go index 350b40d..cc69839 100644 --- a/config.go +++ b/config.go @@ -3,7 +3,7 @@ package loop import ( "time" - "github.com/lightninglabs/loop/lndclient" + "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop/loopdb" "github.com/lightninglabs/loop/lsat" ) diff --git a/executor.go b/executor.go index a71a6b2..b0ace93 100644 --- a/executor.go +++ b/executor.go @@ -7,7 +7,7 @@ import ( "sync/atomic" "time" - "github.com/lightninglabs/loop/lndclient" + "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop/loopdb" "github.com/lightninglabs/loop/sweep" "github.com/lightningnetwork/lnd/queue" diff --git a/go.mod b/go.mod index 9fb092c..f340745 100644 --- a/go.mod +++ b/go.mod @@ -8,16 +8,15 @@ require ( github.com/coreos/bbolt v1.3.3 github.com/fortytw2/leaktest v1.3.0 github.com/golang/protobuf v1.3.2 - github.com/google/go-cmp v0.3.1 // indirect github.com/grpc-ecosystem/grpc-gateway v1.14.3 github.com/jessevdk/go-flags v1.4.0 + github.com/lightninglabs/lndclient v0.0.0-20200618122423-5d815058a719 github.com/lightninglabs/protobuf-hex-display v1.3.3-0.20191212020323-b444784ce75d github.com/lightningnetwork/lnd v0.10.0-beta.rc6.0.20200615174244-103c59a4889f github.com/lightningnetwork/lnd/queue v1.0.4 github.com/stretchr/testify v1.5.1 github.com/urfave/cli v1.20.0 golang.org/x/net v0.0.0-20191002035440-2ec189313ef0 - golang.org/x/text v0.3.2 // indirect google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c google.golang.org/grpc v1.24.0 gopkg.in/macaroon.v2 v2.1.0 diff --git a/go.sum b/go.sum index 76ce44c..fa8cd9f 100644 --- a/go.sum +++ b/go.sum @@ -110,9 +110,8 @@ github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.1-0.20190312032427-6f77996f0c42/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= @@ -168,6 +167,8 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf h1:HZKvJUHlcXI/f/O0Avg7t8sqkPo78HFzjmeYFl6DPnc= github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf/go.mod h1:vxmQPeIQxPf6Jf9rM8R+B4rKBqLA2AjttNxkFBL2Plk= +github.com/lightninglabs/lndclient v0.0.0-20200618122423-5d815058a719 h1:RzyO65pK78xxSaekERq2V3g32GMBPc0MfxmM8MDMVmQ= +github.com/lightninglabs/lndclient v0.0.0-20200618122423-5d815058a719/go.mod h1:WQU2oE0eJIp7jtBOUnL7CO5/YoWrAqyH23A6M1r31i8= github.com/lightninglabs/neutrino v0.11.0/go.mod h1:CuhF0iuzg9Sp2HO6ZgXgayviFTn1QHdSTJlMncK80wg= github.com/lightninglabs/neutrino v0.11.1-0.20200316235139-bffc52e8f200 h1:j4iZ1XlUAPQmW6oSzMcJGILYsRHNs+4O3Gk+2Ms5Dww= github.com/lightninglabs/neutrino v0.11.1-0.20200316235139-bffc52e8f200/go.mod h1:MlZmoKa7CJP3eR1s5yB7Rm5aSyadpKkxqAwLQmog7N0= @@ -311,13 +312,11 @@ golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 h1:+DCIGbF/swA92ohVg0//6X2IVY3KZs6p9mix0ziNYJM= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= diff --git a/lndclient/basic_client.go b/lndclient/basic_client.go deleted file mode 100644 index ad70c65..0000000 --- a/lndclient/basic_client.go +++ /dev/null @@ -1,136 +0,0 @@ -package lndclient - -import ( - "fmt" - "io/ioutil" - "path/filepath" - - "github.com/lightningnetwork/lnd/lncfg" - "github.com/lightningnetwork/lnd/lnrpc" - "github.com/lightningnetwork/lnd/macaroons" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - macaroon "gopkg.in/macaroon.v2" -) - -// BasicClientOption is a functional option argument that allows adding arbitrary -// lnd basic client configuration overrides, without forcing existing users of -// NewBasicClient to update their invocation. These are always processed in -// order, with later options overriding earlier ones. -type BasicClientOption func(*basicClientOptions) - -// basicClientOptions is a set of options that can configure the lnd client -// returned by NewBasicClient. -type basicClientOptions struct { - macFilename string -} - -// defaultBasicClientOptions returns a basicClientOptions set to lnd basic client -// defaults. -func defaultBasicClientOptions() *basicClientOptions { - return &basicClientOptions{ - macFilename: defaultAdminMacaroonFilename, - } -} - -// MacFilename is a basic client option that sets the name of the macaroon file -// to use. -func MacFilename(macFilename string) BasicClientOption { - return func(bc *basicClientOptions) { - bc.macFilename = macFilename - } -} - -// applyBasicClientOptions updates a basicClientOptions set with functional -// options. -func (bc *basicClientOptions) applyBasicClientOptions(options ...BasicClientOption) { - for _, option := range options { - option(bc) - } -} - -// NewBasicClient creates a new basic gRPC client to lnd. We call this client -// "basic" as it falls back to expected defaults if the arguments aren't -// provided. -func NewBasicClient(lndHost, tlsPath, macDir, network string, - basicOptions ...BasicClientOption) ( - - lnrpc.LightningClient, error) { - - conn, err := NewBasicConn( - lndHost, tlsPath, macDir, network, basicOptions..., - ) - if err != nil { - return nil, err - } - - return lnrpc.NewLightningClient(conn), nil -} - -// NewBasicConn creates a new basic gRPC connection to lnd. We call this -// connection "basic" as it falls back to expected defaults if the arguments -// aren't provided. -func NewBasicConn(lndHost, tlsPath, macDir, network string, - basicOptions ...BasicClientOption) ( - - *grpc.ClientConn, error) { - - if tlsPath == "" { - tlsPath = defaultTLSCertPath - } - - // Load the specified TLS certificate and build transport credentials - creds, err := credentials.NewClientTLSFromFile(tlsPath, "") - if err != nil { - return nil, err - } - - // Create a dial options array. - opts := []grpc.DialOption{ - grpc.WithTransportCredentials(creds), - } - - if macDir == "" { - macDir = filepath.Join( - defaultLndDir, defaultDataDir, defaultChainSubDir, - "bitcoin", network, - ) - } - - // Starting with the set of default options, we'll apply any specified - // functional options to the basic client. - bco := defaultBasicClientOptions() - bco.applyBasicClientOptions(basicOptions...) - - macPath := filepath.Join(macDir, bco.macFilename) - - // Load the specified macaroon file. - macBytes, err := ioutil.ReadFile(macPath) - if err == nil { - // Only if file is found - mac := &macaroon.Macaroon{} - if err = mac.UnmarshalBinary(macBytes); err != nil { - return nil, fmt.Errorf("unable to decode macaroon: %v", - err) - } - - // Now we append the macaroon credentials to the dial options. - cred := macaroons.NewMacaroonCredential(mac) - opts = append(opts, grpc.WithPerRPCCredentials(cred)) - opts = append(opts, grpc.WithDefaultCallOptions(maxMsgRecvSize)) - } - - // We need to use a custom dialer so we can also connect to unix sockets - // and not just TCP addresses. - opts = append( - opts, grpc.WithContextDialer( - lncfg.ClientAddressDialer(defaultRPCPort), - ), - ) - conn, err := grpc.Dial(lndHost, opts...) - if err != nil { - return nil, fmt.Errorf("unable to connect to RPC server: %v", err) - } - - return conn, nil -} diff --git a/lndclient/chainnotifier_client.go b/lndclient/chainnotifier_client.go deleted file mode 100644 index c48a2ed..0000000 --- a/lndclient/chainnotifier_client.go +++ /dev/null @@ -1,239 +0,0 @@ -package lndclient - -import ( - "context" - "fmt" - "sync" - - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/wire" - "github.com/lightninglabs/loop/swap" - "github.com/lightningnetwork/lnd/chainntnfs" - "github.com/lightningnetwork/lnd/lnrpc/chainrpc" - "google.golang.org/grpc" -) - -// ChainNotifierClient exposes base lightning functionality. -type ChainNotifierClient interface { - RegisterBlockEpochNtfn(ctx context.Context) ( - chan int32, chan error, error) - - RegisterConfirmationsNtfn(ctx context.Context, txid *chainhash.Hash, - pkScript []byte, numConfs, heightHint int32) ( - chan *chainntnfs.TxConfirmation, chan error, error) - - RegisterSpendNtfn(ctx context.Context, - outpoint *wire.OutPoint, pkScript []byte, heightHint int32) ( - chan *chainntnfs.SpendDetail, chan error, error) -} - -type chainNotifierClient struct { - client chainrpc.ChainNotifierClient - chainMac serializedMacaroon - - wg sync.WaitGroup -} - -func newChainNotifierClient(conn *grpc.ClientConn, chainMac serializedMacaroon) *chainNotifierClient { - return &chainNotifierClient{ - client: chainrpc.NewChainNotifierClient(conn), - chainMac: chainMac, - } -} - -func (s *chainNotifierClient) WaitForFinished() { - s.wg.Wait() -} - -func (s *chainNotifierClient) RegisterSpendNtfn(ctx context.Context, - outpoint *wire.OutPoint, pkScript []byte, heightHint int32) ( - chan *chainntnfs.SpendDetail, chan error, error) { - - var rpcOutpoint *chainrpc.Outpoint - if outpoint != nil { - rpcOutpoint = &chainrpc.Outpoint{ - Hash: outpoint.Hash[:], - Index: outpoint.Index, - } - } - - macaroonAuth := s.chainMac.WithMacaroonAuth(ctx) - resp, err := s.client.RegisterSpendNtfn(macaroonAuth, &chainrpc.SpendRequest{ - HeightHint: uint32(heightHint), - Outpoint: rpcOutpoint, - Script: pkScript, - }) - if err != nil { - return nil, nil, err - } - - spendChan := make(chan *chainntnfs.SpendDetail, 1) - errChan := make(chan error, 1) - - processSpendDetail := func(d *chainrpc.SpendDetails) error { - outpointHash, err := chainhash.NewHash(d.SpendingOutpoint.Hash) - if err != nil { - return err - } - txHash, err := chainhash.NewHash(d.SpendingTxHash) - if err != nil { - return err - } - tx, err := swap.DecodeTx(d.RawSpendingTx) - if err != nil { - return err - } - spendChan <- &chainntnfs.SpendDetail{ - SpentOutPoint: &wire.OutPoint{ - Hash: *outpointHash, - Index: d.SpendingOutpoint.Index, - }, - SpenderTxHash: txHash, - SpenderInputIndex: d.SpendingInputIndex, - SpendingTx: tx, - SpendingHeight: int32(d.SpendingHeight), - } - - return nil - } - - s.wg.Add(1) - go func() { - defer s.wg.Done() - for { - spendEvent, err := resp.Recv() - if err != nil { - errChan <- err - return - } - - switch c := spendEvent.Event.(type) { - case *chainrpc.SpendEvent_Spend: - err := processSpendDetail(c.Spend) - if err != nil { - errChan <- err - } - return - } - } - }() - - return spendChan, errChan, nil -} - -func (s *chainNotifierClient) RegisterConfirmationsNtfn(ctx context.Context, - txid *chainhash.Hash, pkScript []byte, numConfs, heightHint int32) ( - chan *chainntnfs.TxConfirmation, chan error, error) { - - var txidSlice []byte - if txid != nil { - txidSlice = txid[:] - } - confStream, err := s.client.RegisterConfirmationsNtfn( - s.chainMac.WithMacaroonAuth(ctx), - &chainrpc.ConfRequest{ - Script: pkScript, - NumConfs: uint32(numConfs), - HeightHint: uint32(heightHint), - Txid: txidSlice, - }, - ) - if err != nil { - return nil, nil, err - } - - confChan := make(chan *chainntnfs.TxConfirmation, 1) - errChan := make(chan error, 1) - - s.wg.Add(1) - go func() { - defer s.wg.Done() - - for { - var confEvent *chainrpc.ConfEvent - confEvent, err := confStream.Recv() - if err != nil { - errChan <- err - return - } - - switch c := confEvent.Event.(type) { - - // Script confirmed - case *chainrpc.ConfEvent_Conf: - tx, err := swap.DecodeTx(c.Conf.RawTx) - if err != nil { - errChan <- err - return - } - blockHash, err := chainhash.NewHash( - c.Conf.BlockHash, - ) - if err != nil { - errChan <- err - return - } - confChan <- &chainntnfs.TxConfirmation{ - BlockHeight: c.Conf.BlockHeight, - BlockHash: blockHash, - Tx: tx, - TxIndex: c.Conf.TxIndex, - } - return - - // Ignore reorg events, not supported. - case *chainrpc.ConfEvent_Reorg: - continue - - // Nil event, should never happen. - case nil: - errChan <- fmt.Errorf("conf event empty") - return - - // Unexpected type. - default: - errChan <- fmt.Errorf( - "conf event has unexpected type", - ) - return - } - } - }() - - return confChan, errChan, nil -} - -func (s *chainNotifierClient) RegisterBlockEpochNtfn(ctx context.Context) ( - chan int32, chan error, error) { - - blockEpochClient, err := s.client.RegisterBlockEpochNtfn( - s.chainMac.WithMacaroonAuth(ctx), &chainrpc.BlockEpoch{}, - ) - if err != nil { - return nil, nil, err - } - - blockErrorChan := make(chan error, 1) - blockEpochChan := make(chan int32) - - // Start block epoch goroutine. - s.wg.Add(1) - go func() { - defer s.wg.Done() - for { - epoch, err := blockEpochClient.Recv() - if err != nil { - blockErrorChan <- err - return - } - - select { - case blockEpochChan <- int32(epoch.Height): - case <-ctx.Done(): - return - } - } - }() - - return blockEpochChan, blockErrorChan, nil -} diff --git a/lndclient/invoices_client.go b/lndclient/invoices_client.go deleted file mode 100644 index 1e9f73d..0000000 --- a/lndclient/invoices_client.go +++ /dev/null @@ -1,166 +0,0 @@ -package lndclient - -import ( - "context" - "errors" - "sync" - - "github.com/btcsuite/btcutil" - "github.com/lightningnetwork/lnd/channeldb" - "github.com/lightningnetwork/lnd/lnrpc" - "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" - "github.com/lightningnetwork/lnd/lntypes" - "google.golang.org/grpc" -) - -// InvoicesClient exposes invoice functionality. -type InvoicesClient interface { - SubscribeSingleInvoice(ctx context.Context, hash lntypes.Hash) ( - <-chan InvoiceUpdate, <-chan error, error) - - SettleInvoice(ctx context.Context, preimage lntypes.Preimage) error - - CancelInvoice(ctx context.Context, hash lntypes.Hash) error - - AddHoldInvoice(ctx context.Context, in *invoicesrpc.AddInvoiceData) ( - string, error) -} - -// InvoiceUpdate contains a state update for an invoice. -type InvoiceUpdate struct { - State channeldb.ContractState - AmtPaid btcutil.Amount -} - -type invoicesClient struct { - client invoicesrpc.InvoicesClient - invoiceMac serializedMacaroon - wg sync.WaitGroup -} - -func newInvoicesClient(conn *grpc.ClientConn, invoiceMac serializedMacaroon) *invoicesClient { - return &invoicesClient{ - client: invoicesrpc.NewInvoicesClient(conn), - invoiceMac: invoiceMac, - } -} - -func (s *invoicesClient) WaitForFinished() { - s.wg.Wait() -} - -func (s *invoicesClient) SettleInvoice(ctx context.Context, - preimage lntypes.Preimage) error { - - timeoutCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - rpcCtx := s.invoiceMac.WithMacaroonAuth(timeoutCtx) - _, err := s.client.SettleInvoice(rpcCtx, &invoicesrpc.SettleInvoiceMsg{ - Preimage: preimage[:], - }) - - return err -} - -func (s *invoicesClient) CancelInvoice(ctx context.Context, - hash lntypes.Hash) error { - - rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - rpcCtx = s.invoiceMac.WithMacaroonAuth(rpcCtx) - _, err := s.client.CancelInvoice(rpcCtx, &invoicesrpc.CancelInvoiceMsg{ - PaymentHash: hash[:], - }) - - return err -} - -func (s *invoicesClient) SubscribeSingleInvoice(ctx context.Context, - hash lntypes.Hash) (<-chan InvoiceUpdate, - <-chan error, error) { - - invoiceStream, err := s.client.SubscribeSingleInvoice( - s.invoiceMac.WithMacaroonAuth(ctx), - &invoicesrpc.SubscribeSingleInvoiceRequest{ - RHash: hash[:], - }, - ) - if err != nil { - return nil, nil, err - } - - updateChan := make(chan InvoiceUpdate) - errChan := make(chan error, 1) - - // Invoice updates goroutine. - s.wg.Add(1) - go func() { - defer s.wg.Done() - for { - invoice, err := invoiceStream.Recv() - if err != nil { - errChan <- err - return - } - - state, err := fromRPCInvoiceState(invoice.State) - if err != nil { - errChan <- err - return - } - - select { - case updateChan <- InvoiceUpdate{ - State: state, - AmtPaid: btcutil.Amount(invoice.AmtPaidSat), - }: - case <-ctx.Done(): - return - } - } - }() - - return updateChan, errChan, nil -} - -func (s *invoicesClient) AddHoldInvoice(ctx context.Context, - in *invoicesrpc.AddInvoiceData) (string, error) { - - rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - rpcIn := &invoicesrpc.AddHoldInvoiceRequest{ - Memo: in.Memo, - Hash: in.Hash[:], - Value: int64(in.Value.ToSatoshis()), - Expiry: in.Expiry, - CltvExpiry: in.CltvExpiry, - Private: true, - } - - rpcCtx = s.invoiceMac.WithMacaroonAuth(rpcCtx) - resp, err := s.client.AddHoldInvoice(rpcCtx, rpcIn) - if err != nil { - return "", err - } - return resp.PaymentRequest, nil -} - -func fromRPCInvoiceState(state lnrpc.Invoice_InvoiceState) ( - channeldb.ContractState, error) { - - switch state { - case lnrpc.Invoice_OPEN: - return channeldb.ContractOpen, nil - case lnrpc.Invoice_ACCEPTED: - return channeldb.ContractAccepted, nil - case lnrpc.Invoice_SETTLED: - return channeldb.ContractSettled, nil - case lnrpc.Invoice_CANCELED: - return channeldb.ContractCanceled, nil - } - - return 0, errors.New("unknown state") -} diff --git a/lndclient/lightning_client.go b/lndclient/lightning_client.go deleted file mode 100644 index 61093f3..0000000 --- a/lndclient/lightning_client.go +++ /dev/null @@ -1,1221 +0,0 @@ -package lndclient - -import ( - "bytes" - "context" - "encoding/hex" - "errors" - "fmt" - "sync" - "time" - - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" - "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/lnwire" - "github.com/lightningnetwork/lnd/routing/route" - "github.com/lightningnetwork/lnd/zpay32" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -// LightningClient exposes base lightning functionality. -type LightningClient interface { - PayInvoice(ctx context.Context, invoice string, - maxFee btcutil.Amount, - outgoingChannel *uint64) chan PaymentResult - - GetInfo(ctx context.Context) (*Info, error) - - EstimateFeeToP2WSH(ctx context.Context, amt btcutil.Amount, - confTarget int32) (btcutil.Amount, error) - - ConfirmedWalletBalance(ctx context.Context) (btcutil.Amount, error) - - AddInvoice(ctx context.Context, in *invoicesrpc.AddInvoiceData) ( - lntypes.Hash, string, error) - - // LookupInvoice looks up an invoice by hash. - LookupInvoice(ctx context.Context, hash lntypes.Hash) (*Invoice, error) - - // ListTransactions returns all known transactions of the backing lnd - // node. - ListTransactions(ctx context.Context) ([]Transaction, error) - - // ListChannels retrieves all channels of the backing lnd node. - ListChannels(ctx context.Context) ([]ChannelInfo, error) - - // ClosedChannels returns all closed channels of the backing lnd node. - ClosedChannels(ctx context.Context) ([]ClosedChannel, error) - - // ForwardingHistory makes a paginated call to our forwarding history - // endpoint. - ForwardingHistory(ctx context.Context, - req ForwardingHistoryRequest) (*ForwardingHistoryResponse, error) - - // ListInvoices makes a paginated call to our list invoices endpoint. - ListInvoices(ctx context.Context, req ListInvoicesRequest) ( - *ListInvoicesResponse, error) - - // ListPayments makes a paginated call to our list payments endpoint. - ListPayments(ctx context.Context, - req ListPaymentsRequest) (*ListPaymentsResponse, error) - - // ChannelBackup retrieves the backup for a particular channel. The - // backup is returned as an encrypted chanbackup.Single payload. - ChannelBackup(context.Context, wire.OutPoint) ([]byte, error) - - // ChannelBackups retrieves backups for all existing pending open and - // open channels. The backups are returned as an encrypted - // chanbackup.Multi payload. - ChannelBackups(ctx context.Context) ([]byte, error) -} - -// Info contains info about the connected lnd node. -type Info struct { - BlockHeight uint32 - IdentityPubkey [33]byte - Alias string - Network string - Uris []string -} - -// ChannelInfo stores unpacked per-channel info. -type ChannelInfo struct { - // ChannelPoint is the funding outpoint of the channel. - ChannelPoint string - - // Active indicates whether the channel is active. - Active bool - - // ChannelID holds the unique channel ID for the channel. The first 3 bytes - // are the block height, the next 3 the index within the block, and the last - // 2 bytes are the /output index for the channel. - ChannelID uint64 - - // PubKeyBytes is the raw bytes of the public key of the remote node. - PubKeyBytes route.Vertex - - // Capacity is the total amount of funds held in this channel. - Capacity btcutil.Amount - - // LocalBalance is the current balance of this node in this channel. - LocalBalance btcutil.Amount - - // RemoteBalance is the counterparty's current balance in this channel. - RemoteBalance btcutil.Amount - - // Initiator indicates whether we opened the channel or not. - Initiator bool - - // Private indicates that the channel is private. - Private bool - - // LifeTime is the total amount of time we have monitored the peer's - // online status for. - LifeTime time.Duration - - // Uptime is the total amount of time the peer has been observed as - // online over its lifetime. - Uptime time.Duration -} - -// ClosedChannel represents a channel that has been closed. -type ClosedChannel struct { - // ChannelPoint is the funding outpoint of the channel. - ChannelPoint string - - // ChannelID holds the unique channel ID for the channel. The first 3 - // bytes are the block height, the next 3 the index within the block, - // and the last 2 bytes are the output index for the channel. - ChannelID uint64 - - // ClosingTxHash is the tx hash of the close transaction for the channel. - ClosingTxHash string - - // CloseType is the type of channel closure. - CloseType CloseType - - // OpenInitiator is true if we opened the channel. This value is not - // always available (older channels do not have it). - OpenInitiator Initiator - - // Initiator indicates which party initiated the channel close. Since - // this value is not always set in the rpc response, we also make a best - // effort attempt to set it based on CloseType. - CloseInitiator Initiator - - // PubKeyBytes is the raw bytes of the public key of the remote node. - PubKeyBytes route.Vertex - - // Capacity is the total amount of funds held in this channel. - Capacity btcutil.Amount - - // SettledBalance is the amount we were paid out directly in this - // channel close. Note that this does not include cases where we need to - // sweep our commitment or htlcs. - SettledBalance btcutil.Amount -} - -// CloseType is an enum which represents the types of closes our channels may -// have. This type maps to the rpc value. -type CloseType uint8 - -const ( - // CloseTypeCooperative represents cooperative closes. - CloseTypeCooperative CloseType = iota - - // CloseTypeLocalForce represents force closes that we initiated. - CloseTypeLocalForce - - // CloseTypeRemoteForce represents force closes that our peer initiated. - CloseTypeRemoteForce - - // CloseTypeBreach represents breach closes from our peer. - CloseTypeBreach - - // CloseTypeFundingCancelled represents channels which were never - // created because their funding transaction was cancelled. - CloseTypeFundingCancelled - - // CloseTypeAbandoned represents a channel that was abandoned. - CloseTypeAbandoned -) - -// String returns the string representation of a close type. -func (c CloseType) String() string { - switch c { - case CloseTypeCooperative: - return "Cooperative" - - case CloseTypeLocalForce: - return "Local Force" - - case CloseTypeRemoteForce: - return "Remote Force" - - case CloseTypeBreach: - return "Breach" - - case CloseTypeFundingCancelled: - return "Funding Cancelled" - - case CloseTypeAbandoned: - return "Abandoned" - - default: - return "Unknown" - } -} - -// Initiator indicates the party that opened or closed a channel. This enum is -// used for cases where we may not have a full set of initiator information -// available over rpc (this is the case for older channels). -type Initiator uint8 - -const ( - // InitiatorUnrecorded is set when we do not know the open/close - // initiator for a channel, this is the case when the channel was - // closed before lnd started tracking initiators. - InitiatorUnrecorded Initiator = iota - - // InitiatorLocal is set when we initiated a channel open or close. - InitiatorLocal - - // InitiatorRemote is set when the remote party initiated a chanel open - // or close. - InitiatorRemote - - // InitiatorBoth is set in the case where both parties initiated a - // cooperative close (this is possible with multiple rounds of - // negotiation). - InitiatorBoth -) - -// String provides the string represenetation of a close initiator. -func (c Initiator) String() string { - switch c { - case InitiatorUnrecorded: - return "Unrecorded" - - case InitiatorLocal: - return "Local" - - case InitiatorRemote: - return "Remote" - - case InitiatorBoth: - return "Both" - - default: - return fmt.Sprintf("unknown initiator: %d", c) - } -} - -// Transaction represents an on chain transaction. -type Transaction struct { - // Tx is the on chain transaction. - Tx *wire.MsgTx - - // TxHash is the transaction hash string. - TxHash string - - // Timestamp is the timestamp our wallet has for the transaction. - Timestamp time.Time - - // Amount is the balance change that this transaction had on addresses - // controlled by our wallet. - Amount btcutil.Amount - - // Fee is the amount of fees our wallet committed to this transaction. - // Note that this field is not exhaustive, as it does not account for - // fees taken from inputs that that wallet doesn't know it owns (for - // example, the fees taken from our channel balance when we close a - // channel). - Fee btcutil.Amount - - // Confirmations is the number of confirmations the transaction has. - Confirmations int32 - - // Label is an optional label set for on chain transactions. - Label string -} - -var ( - // ErrMalformedServerResponse is returned when the swap and/or prepay - // invoice is malformed. - ErrMalformedServerResponse = errors.New( - "one or more invoices are malformed", - ) - - // ErrNoRouteToServer is returned if no quote can returned because there - // is no route to the server. - ErrNoRouteToServer = errors.New("no off-chain route to server") - - // PaymentResultUnknownPaymentHash is the string result returned by - // SendPayment when the final node indicates the hash is unknown. - PaymentResultUnknownPaymentHash = "UnknownPaymentHash" - - // PaymentResultSuccess is the string result returned by SendPayment - // when the payment was successful. - PaymentResultSuccess = "" - - // PaymentResultAlreadyPaid is the string result returned by SendPayment - // when the payment was already completed in a previous SendPayment - // call. - 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 = channeldb.ErrPaymentInFlight.Error() - - paymentPollInterval = 3 * time.Second -) - -type lightningClient struct { - client lnrpc.LightningClient - wg sync.WaitGroup - params *chaincfg.Params - adminMac serializedMacaroon -} - -func newLightningClient(conn *grpc.ClientConn, - params *chaincfg.Params, adminMac serializedMacaroon) *lightningClient { - - return &lightningClient{ - client: lnrpc.NewLightningClient(conn), - params: params, - adminMac: adminMac, - } -} - -// PaymentResult signals the result of a payment. -type PaymentResult struct { - Err error - Preimage lntypes.Preimage - PaidFee btcutil.Amount - PaidAmt btcutil.Amount -} - -func (s *lightningClient) WaitForFinished() { - s.wg.Wait() -} - -func (s *lightningClient) ConfirmedWalletBalance(ctx context.Context) ( - btcutil.Amount, error) { - - rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - rpcCtx = s.adminMac.WithMacaroonAuth(rpcCtx) - resp, err := s.client.WalletBalance(rpcCtx, &lnrpc.WalletBalanceRequest{}) - if err != nil { - return 0, err - } - - return btcutil.Amount(resp.ConfirmedBalance), nil -} - -func (s *lightningClient) GetInfo(ctx context.Context) (*Info, error) { - rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - rpcCtx = s.adminMac.WithMacaroonAuth(rpcCtx) - resp, err := s.client.GetInfo(rpcCtx, &lnrpc.GetInfoRequest{}) - if err != nil { - return nil, err - } - - pubKey, err := hex.DecodeString(resp.IdentityPubkey) - if err != nil { - return nil, err - } - - var pubKeyArray [33]byte - copy(pubKeyArray[:], pubKey) - - return &Info{ - BlockHeight: resp.BlockHeight, - IdentityPubkey: pubKeyArray, - Alias: resp.Alias, - Network: resp.Chains[0].Network, - Uris: resp.Uris, - }, nil -} - -func (s *lightningClient) EstimateFeeToP2WSH(ctx context.Context, - amt btcutil.Amount, confTarget int32) (btcutil.Amount, - error) { - - rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - // Generate dummy p2wsh address for fee estimation. - wsh := [32]byte{} - p2wshAddress, err := btcutil.NewAddressWitnessScriptHash( - wsh[:], s.params, - ) - if err != nil { - return 0, err - } - - rpcCtx = s.adminMac.WithMacaroonAuth(rpcCtx) - resp, err := s.client.EstimateFee( - rpcCtx, - &lnrpc.EstimateFeeRequest{ - TargetConf: confTarget, - AddrToAmount: map[string]int64{ - p2wshAddress.String(): int64(amt), - }, - }, - ) - if err != nil { - return 0, err - } - return btcutil.Amount(resp.FeeSat), nil -} - -// PayInvoice pays an invoice. -func (s *lightningClient) PayInvoice(ctx context.Context, invoice string, - maxFee btcutil.Amount, outgoingChannel *uint64) chan PaymentResult { - - // Use buffer to prevent blocking. - paymentChan := make(chan PaymentResult, 1) - - // Execute payment in parallel, because it will block until server - // discovers preimage. - s.wg.Add(1) - go func() { - defer s.wg.Done() - - result := s.payInvoice(ctx, invoice, maxFee, outgoingChannel) - if result != nil { - paymentChan <- *result - } - }() - - return paymentChan -} - -// payInvoice tries to send a payment and returns the final result. If -// necessary, it will poll lnd for the payment result. -func (s *lightningClient) payInvoice(ctx context.Context, invoice string, - maxFee btcutil.Amount, outgoingChannel *uint64) *PaymentResult { - - payReq, err := zpay32.Decode(invoice, s.params) - if err != nil { - return &PaymentResult{ - Err: fmt.Errorf("invoice decode: %v", err), - } - } - - if payReq.MilliSat == nil { - return &PaymentResult{ - Err: errors.New("no amount in invoice"), - } - } - - hash := lntypes.Hash(*payReq.PaymentHash) - - ctx = s.adminMac.WithMacaroonAuth(ctx) - for { - // Create no timeout context as this call can block for a long - // time. - - req := &lnrpc.SendRequest{ - FeeLimit: &lnrpc.FeeLimit{ - Limit: &lnrpc.FeeLimit_Fixed{ - Fixed: int64(maxFee), - }, - }, - PaymentRequest: invoice, - } - - if outgoingChannel != nil { - req.OutgoingChanId = *outgoingChannel - } - - payResp, err := s.client.SendPaymentSync(ctx, req) - - if status.Code(err) == codes.Canceled { - return nil - } - - if err == nil { - // TODO: Use structured payment error when available, - // instead of this britle string matching. - switch payResp.PaymentError { - - // Paid successfully. - case PaymentResultSuccess: - log.Infof( - "Payment %v completed", hash, - ) - - r := payResp.PaymentRoute - preimage, err := lntypes.MakePreimage( - payResp.PaymentPreimage, - ) - if err != nil { - return &PaymentResult{Err: err} - } - return &PaymentResult{ - PaidFee: btcutil.Amount(r.TotalFees), - PaidAmt: btcutil.Amount( - r.TotalAmt - r.TotalFees, - ), - Preimage: preimage, - } - - // Invoice was already paid on a previous run. - case PaymentResultAlreadyPaid: - log.Infof( - "Payment %v already completed", hash, - ) - - // Unfortunately lnd doesn't return the route if - // the payment was successful in a previous - // call. Assume paid fees 0 and take paid amount - // from invoice. - - return &PaymentResult{ - PaidFee: 0, - PaidAmt: payReq.MilliSat.ToSatoshis(), - } - - // If the payment is already in flight, we will poll - // again later for an outcome. - // - // TODO: Improve this when lnd expose more API to - // tracking existing payments. - case PaymentResultInFlight: - log.Infof( - "Payment %v already in flight", hash, - ) - - time.Sleep(paymentPollInterval) - - // Other errors are transformed into an error struct. - default: - log.Warnf( - "Payment %v failed: %v", hash, - payResp.PaymentError, - ) - - return &PaymentResult{ - Err: errors.New(payResp.PaymentError), - } - } - } - } -} - -func (s *lightningClient) AddInvoice(ctx context.Context, - in *invoicesrpc.AddInvoiceData) (lntypes.Hash, string, error) { - - rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - rpcIn := &lnrpc.Invoice{ - Memo: in.Memo, - Value: int64(in.Value.ToSatoshis()), - Expiry: in.Expiry, - CltvExpiry: in.CltvExpiry, - Private: true, - } - - if in.Preimage != nil { - rpcIn.RPreimage = in.Preimage[:] - } - if in.Hash != nil { - rpcIn.RHash = in.Hash[:] - } - - rpcCtx = s.adminMac.WithMacaroonAuth(rpcCtx) - resp, err := s.client.AddInvoice(rpcCtx, rpcIn) - if err != nil { - return lntypes.Hash{}, "", err - } - hash, err := lntypes.MakeHash(resp.RHash) - if err != nil { - return lntypes.Hash{}, "", err - } - - return hash, resp.PaymentRequest, nil -} - -// Invoice represents an invoice in lnd. -type Invoice struct { - // Preimage is the invoice's preimage, which is set if the invoice - // is settled. - Preimage *lntypes.Preimage - - // Hash is the invoice hash. - Hash lntypes.Hash - - // Memo is an optional memo field for hte invoice. - Memo string - - // PaymentRequest is the invoice's payment request. - PaymentRequest string - - // Amount is the amount of the invoice in millisatoshis. - Amount lnwire.MilliSatoshi - - // AmountPaid is the amount that was paid for the invoice. This field - // will only be set if the invoice is settled. - AmountPaid lnwire.MilliSatoshi - - // CreationDate is the time the invoice was created. - CreationDate time.Time - - // SettleDate is the time the invoice was settled. - SettleDate time.Time - - // State is the invoice's current state. - State channeldb.ContractState - - // IsKeysend indicates whether the invoice was a spontaneous payment. - IsKeysend bool -} - -// LookupInvoice looks up an invoice in lnd, it will error if the invoice is -// not known to lnd. -func (s *lightningClient) LookupInvoice(ctx context.Context, - hash lntypes.Hash) (*Invoice, error) { - - rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - rpcIn := &lnrpc.PaymentHash{ - RHash: hash[:], - } - - rpcCtx = s.adminMac.WithMacaroonAuth(rpcCtx) - resp, err := s.client.LookupInvoice(rpcCtx, rpcIn) - if err != nil { - return nil, err - } - - invoice, err := unmarshalInvoice(resp) - if err != nil { - return nil, err - } - - return invoice, nil -} - -// unmarshalInvoice creates an invoice from the rpc response provided. -func unmarshalInvoice(resp *lnrpc.Invoice) (*Invoice, error) { - hash, err := lntypes.MakeHash(resp.RHash) - if err != nil { - return nil, err - } - - invoice := &Invoice{ - Preimage: nil, - Hash: hash, - Memo: resp.Memo, - PaymentRequest: resp.PaymentRequest, - Amount: lnwire.MilliSatoshi(resp.ValueMsat), - AmountPaid: lnwire.MilliSatoshi(resp.AmtPaidMsat), - CreationDate: time.Unix(resp.CreationDate, 0), - IsKeysend: resp.IsKeysend, - } - - switch resp.State { - case lnrpc.Invoice_OPEN: - invoice.State = channeldb.ContractOpen - - case lnrpc.Invoice_ACCEPTED: - invoice.State = channeldb.ContractAccepted - - // If the invoice is settled, it also has a non-nil preimage, which we - // can set on our invoice. - case lnrpc.Invoice_SETTLED: - invoice.State = channeldb.ContractSettled - preimage, err := lntypes.MakePreimage(resp.RPreimage) - if err != nil { - return nil, err - } - invoice.Preimage = &preimage - - case lnrpc.Invoice_CANCELED: - invoice.State = channeldb.ContractCanceled - - default: - return nil, fmt.Errorf("unknown invoice state: %v", - resp.State) - } - - // Only set settle date if it is non-zero, because 0 unix time is - // not the same as a zero time struct. - if resp.SettleDate != 0 { - invoice.SettleDate = time.Unix(resp.SettleDate, 0) - } - - return invoice, nil -} - -// ListTransactions returns all known transactions of the backing lnd node. -func (s *lightningClient) ListTransactions(ctx context.Context) ([]Transaction, error) { - rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - rpcCtx = s.adminMac.WithMacaroonAuth(rpcCtx) - rpcIn := &lnrpc.GetTransactionsRequest{} - resp, err := s.client.GetTransactions(rpcCtx, rpcIn) - if err != nil { - return nil, err - } - - txs := make([]Transaction, len(resp.Transactions)) - for i, respTx := range resp.Transactions { - rawTx, err := hex.DecodeString(respTx.RawTxHex) - if err != nil { - return nil, err - } - - var tx wire.MsgTx - if err := tx.Deserialize(bytes.NewReader(rawTx)); err != nil { - return nil, err - } - - txs[i] = Transaction{ - Tx: &tx, - TxHash: tx.TxHash().String(), - Timestamp: time.Unix(respTx.TimeStamp, 0), - Amount: btcutil.Amount(respTx.Amount), - Fee: btcutil.Amount(respTx.TotalFees), - Confirmations: respTx.NumConfirmations, - Label: respTx.Label, - } - } - - return txs, nil -} - -// ListChannels retrieves all channels of the backing lnd node. -func (s *lightningClient) ListChannels(ctx context.Context) ( - []ChannelInfo, error) { - - rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - response, err := s.client.ListChannels( - s.adminMac.WithMacaroonAuth(rpcCtx), - &lnrpc.ListChannelsRequest{}, - ) - if err != nil { - return nil, err - } - - result := make([]ChannelInfo, len(response.Channels)) - for i, channel := range response.Channels { - remoteVertex, err := route.NewVertexFromStr(channel.RemotePubkey) - if err != nil { - return nil, err - } - - result[i] = ChannelInfo{ - ChannelPoint: channel.ChannelPoint, - Active: channel.Active, - ChannelID: channel.ChanId, - PubKeyBytes: remoteVertex, - Capacity: btcutil.Amount(channel.Capacity), - LocalBalance: btcutil.Amount(channel.LocalBalance), - RemoteBalance: btcutil.Amount(channel.RemoteBalance), - Initiator: channel.Initiator, - Private: channel.Private, - LifeTime: time.Second * time.Duration( - channel.Lifetime, - ), - Uptime: time.Second * time.Duration( - channel.Uptime, - ), - } - } - - return result, nil -} - -// ClosedChannels returns a list of our closed channels. -func (s *lightningClient) ClosedChannels(ctx context.Context) ([]ClosedChannel, - error) { - - rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - response, err := s.client.ClosedChannels( - s.adminMac.WithMacaroonAuth(rpcCtx), - &lnrpc.ClosedChannelsRequest{}, - ) - if err != nil { - return nil, err - } - - channels := make([]ClosedChannel, len(response.Channels)) - for i, channel := range response.Channels { - remote, err := route.NewVertexFromStr(channel.RemotePubkey) - if err != nil { - return nil, err - } - - closeType, err := rpcCloseType(channel.CloseType) - if err != nil { - return nil, err - } - - openInitiator, err := getInitiator(channel.OpenInitiator) - if err != nil { - return nil, err - } - - closeInitiator, err := rpcCloseInitiator( - channel.CloseInitiator, closeType, - ) - if err != nil { - return nil, err - } - - channels[i] = ClosedChannel{ - ChannelPoint: channel.ChannelPoint, - ChannelID: channel.ChanId, - ClosingTxHash: channel.ClosingTxHash, - CloseType: closeType, - OpenInitiator: openInitiator, - CloseInitiator: closeInitiator, - PubKeyBytes: remote, - Capacity: btcutil.Amount(channel.Capacity), - SettledBalance: btcutil.Amount(channel.SettledBalance), - } - } - - return channels, nil -} - -// rpcCloseType maps a rpc close type to our local enum. -func rpcCloseType(t lnrpc.ChannelCloseSummary_ClosureType) (CloseType, error) { - switch t { - case lnrpc.ChannelCloseSummary_COOPERATIVE_CLOSE: - return CloseTypeCooperative, nil - - case lnrpc.ChannelCloseSummary_LOCAL_FORCE_CLOSE: - return CloseTypeLocalForce, nil - - case lnrpc.ChannelCloseSummary_REMOTE_FORCE_CLOSE: - return CloseTypeRemoteForce, nil - - case lnrpc.ChannelCloseSummary_BREACH_CLOSE: - return CloseTypeBreach, nil - - case lnrpc.ChannelCloseSummary_FUNDING_CANCELED: - return CloseTypeFundingCancelled, nil - - case lnrpc.ChannelCloseSummary_ABANDONED: - return CloseTypeAbandoned, nil - - default: - return 0, fmt.Errorf("unknown close type: %v", t) - } -} - -// rpcCloseInitiator maps a close initiator to our local type. Since this field -// is not always set in lnd for older channels, also use our close type to infer -// who initiated the close when we have force closes. -func rpcCloseInitiator(initiator lnrpc.Initiator, - closeType CloseType) (Initiator, error) { - - // Since our close type is always set on the rpc, we first check whether - // we can figure out the close initiator from this value. This is only - // possible for force closes/breaches. - switch closeType { - case CloseTypeLocalForce: - return InitiatorLocal, nil - - case CloseTypeRemoteForce, CloseTypeBreach: - return InitiatorRemote, nil - } - - // Otherwise, we check whether our initiator field is set, and fail only - // if we have an unknown type. - return getInitiator(initiator) -} - -// getInitiator maps a rpc initiator value to our initiator enum. -func getInitiator(initiator lnrpc.Initiator) (Initiator, error) { - switch initiator { - case lnrpc.Initiator_INITIATOR_LOCAL: - return InitiatorLocal, nil - - case lnrpc.Initiator_INITIATOR_REMOTE: - return InitiatorRemote, nil - - case lnrpc.Initiator_INITIATOR_BOTH: - return InitiatorBoth, nil - - case lnrpc.Initiator_INITIATOR_UNKNOWN: - return InitiatorUnrecorded, nil - - default: - return InitiatorUnrecorded, fmt.Errorf("unknown "+ - "initiator: %v", initiator) - } -} - -// ForwardingHistoryRequest contains the request parameters for a paginated -// forwarding history call. -type ForwardingHistoryRequest struct { - // StartTime is the beginning of the query period. - StartTime time.Time - - // EndTime is the end of the query period. - EndTime time.Time - - // MaxEvents is the maximum number of events to return. - MaxEvents uint32 - - // Offset is the index from which to start querying. - Offset uint32 -} - -// ForwardingHistoryResponse contains the response to a forwarding history -// query, including last index offset required for paginated queries. -type ForwardingHistoryResponse struct { - // LastIndexOffset is the index offset of the last item in our set. - LastIndexOffset uint32 - - // Events is the set of events that were found in the interval queried. - Events []ForwardingEvent -} - -// ForwardingEvent represents a htlc that was forwarded through our node. -type ForwardingEvent struct { - // Timestamp is the time that we processed the forwarding event. - Timestamp time.Time - - // ChannelIn is the id of the channel the htlc arrived at our node on. - ChannelIn uint64 - - // ChannelOut is the id of the channel the htlc left our node on. - ChannelOut uint64 - - // AmountMsatIn is the amount that was forwarded into our node in - // millisatoshis. - AmountMsatIn lnwire.MilliSatoshi - - // AmountMsatOut is the amount that was forwarded out of our node in - // millisatoshis. - AmountMsatOut lnwire.MilliSatoshi - - // FeeMsat is the amount of fees earned in millisatoshis, - FeeMsat lnwire.MilliSatoshi -} - -// ForwardingHistory returns a set of forwarding events for the period queried. -// Note that this call is paginated, and the information required to make -// subsequent calls is provided in the response. -func (s *lightningClient) ForwardingHistory(ctx context.Context, - req ForwardingHistoryRequest) (*ForwardingHistoryResponse, error) { - - rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - response, err := s.client.ForwardingHistory( - s.adminMac.WithMacaroonAuth(rpcCtx), - &lnrpc.ForwardingHistoryRequest{ - StartTime: uint64(req.StartTime.Unix()), - EndTime: uint64(req.EndTime.Unix()), - IndexOffset: req.Offset, - NumMaxEvents: req.MaxEvents, - }, - ) - if err != nil { - return nil, err - } - - events := make([]ForwardingEvent, len(response.ForwardingEvents)) - for i, event := range response.ForwardingEvents { - events[i] = ForwardingEvent{ - Timestamp: time.Unix(int64(event.Timestamp), 0), - ChannelIn: event.ChanIdIn, - ChannelOut: event.ChanIdOut, - AmountMsatIn: lnwire.MilliSatoshi(event.AmtIn), - AmountMsatOut: lnwire.MilliSatoshi(event.AmtOut), - FeeMsat: lnwire.MilliSatoshi(event.FeeMsat), - } - } - - return &ForwardingHistoryResponse{ - LastIndexOffset: response.LastOffsetIndex, - Events: events, - }, nil -} - -// ListInvoicesRequest contains the request parameters for a paginated -// list invoices call. -type ListInvoicesRequest struct { - // MaxInvoices is the maximum number of invoices to return. - MaxInvoices uint64 - - // Offset is the index from which to start querying. - Offset uint64 - - // Reversed is set to query our invoices backwards. - Reversed bool - - // PendingOnly is set if we only want pending invoices. - PendingOnly bool -} - -// ListInvoicesResponse contains the response to a list invoices query, -// including the index offsets required for paginated queries. -type ListInvoicesResponse struct { - // FirstIndexOffset is the index offset of the first item in our set. - FirstIndexOffset uint64 - - // LastIndexOffset is the index offset of the last item in our set. - LastIndexOffset uint64 - - // Invoices is the set of invoices that were returned. - Invoices []Invoice -} - -// ListInvoices returns a list of invoices from our node. -func (s *lightningClient) ListInvoices(ctx context.Context, - req ListInvoicesRequest) (*ListInvoicesResponse, error) { - - rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - resp, err := s.client.ListInvoices( - s.adminMac.WithMacaroonAuth(rpcCtx), - &lnrpc.ListInvoiceRequest{ - PendingOnly: false, - IndexOffset: req.Offset, - NumMaxInvoices: req.MaxInvoices, - Reversed: req.Reversed, - }, - ) - if err != nil { - return nil, err - } - - invoices := make([]Invoice, len(resp.Invoices)) - for i, invoice := range resp.Invoices { - inv, err := unmarshalInvoice(invoice) - if err != nil { - return nil, err - } - - invoices[i] = *inv - } - - return &ListInvoicesResponse{ - FirstIndexOffset: resp.FirstIndexOffset, - LastIndexOffset: resp.LastIndexOffset, - Invoices: invoices, - }, nil -} - -// Payment represents a payment made by our node. -type Payment struct { - // Hash is the payment hash used. - Hash lntypes.Hash - - // Preimage is the preimage of the payment. It will have a non-nil value - // if the payment is settled. - Preimage *lntypes.Preimage - - // Amount is the amount in millisatoshis of the payment. - Amount lnwire.MilliSatoshi - - // Fee is the amount in millisatoshis that was paid in fees. - Fee lnwire.MilliSatoshi - - // Status describes the state of a payment. - Status *PaymentStatus - - // Htlcs is the set of htlc attempts made by the payment. - Htlcs []*lnrpc.HTLCAttempt - - // SequenceNumber is a unique id for each payment. - SequenceNumber uint64 -} - -// ListPaymentsRequest contains the request parameters for a paginated -// list payments call. -type ListPaymentsRequest struct { - // MaxPayments is the maximum number of payments to return. - MaxPayments uint64 - - // Offset is the index from which to start querying. - Offset uint64 - - // Reversed is set to query our payments backwards. - Reversed bool - - // IncludeIncomplete is set if we want to include incomplete payments. - IncludeIncomplete bool -} - -// ListPaymentsResponse contains the response to a list payments query, -// including the index offsets required for paginated queries. -type ListPaymentsResponse struct { - // FirstIndexOffset is the index offset of the first item in our set. - FirstIndexOffset uint64 - - // LastIndexOffset is the index offset of the last item in our set. - LastIndexOffset uint64 - - // Payments is the set of invoices that were returned. - Payments []Payment -} - -// ListPayments makes a paginated call to our listpayments endpoint. -func (s *lightningClient) ListPayments(ctx context.Context, - req ListPaymentsRequest) (*ListPaymentsResponse, error) { - - rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - resp, err := s.client.ListPayments( - s.adminMac.WithMacaroonAuth(rpcCtx), - &lnrpc.ListPaymentsRequest{ - IncludeIncomplete: req.IncludeIncomplete, - IndexOffset: req.Offset, - MaxPayments: req.MaxPayments, - Reversed: req.Reversed, - }) - if err != nil { - return nil, err - } - - payments := make([]Payment, len(resp.Payments)) - for i, payment := range resp.Payments { - hash, err := lntypes.MakeHashFromStr(payment.PaymentHash) - if err != nil { - return nil, err - } - - status, err := unmarshallPaymentStatus(payment) - if err != nil { - return nil, err - } - - pmt := Payment{ - Hash: hash, - Status: status, - Htlcs: payment.Htlcs, - Amount: lnwire.MilliSatoshi(payment.ValueMsat), - Fee: lnwire.MilliSatoshi(payment.FeeMsat), - SequenceNumber: payment.PaymentIndex, - } - - // Add our preimage if it is known. - if payment.PaymentPreimage != "" { - preimage, err := lntypes.MakePreimageFromStr( - payment.PaymentPreimage, - ) - if err != nil { - return nil, err - } - pmt.Preimage = &preimage - } - - payments[i] = pmt - } - - return &ListPaymentsResponse{ - FirstIndexOffset: resp.FirstIndexOffset, - LastIndexOffset: resp.LastIndexOffset, - Payments: payments, - }, nil -} - -// ChannelBackup retrieves the backup for a particular channel. The backup is -// returned as an encrypted chanbackup.Single payload. -func (s *lightningClient) ChannelBackup(ctx context.Context, - channelPoint wire.OutPoint) ([]byte, error) { - - rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - rpcCtx = s.adminMac.WithMacaroonAuth(rpcCtx) - req := &lnrpc.ExportChannelBackupRequest{ - ChanPoint: &lnrpc.ChannelPoint{ - FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{ - FundingTxidBytes: channelPoint.Hash[:], - }, - OutputIndex: channelPoint.Index, - }, - } - resp, err := s.client.ExportChannelBackup(rpcCtx, req) - if err != nil { - return nil, err - } - - return resp.ChanBackup, nil -} - -// ChannelBackups retrieves backups for all existing pending open and open -// channels. The backups are returned as an encrypted chanbackup.Multi payload. -func (s *lightningClient) ChannelBackups(ctx context.Context) ([]byte, error) { - rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - rpcCtx = s.adminMac.WithMacaroonAuth(rpcCtx) - req := &lnrpc.ChanBackupExportRequest{} - resp, err := s.client.ExportAllChannelBackups(rpcCtx, req) - if err != nil { - return nil, err - } - - return resp.MultiChanBackup.MultiChanBackup, nil -} diff --git a/lndclient/lnd_services.go b/lndclient/lnd_services.go deleted file mode 100644 index ac8aec0..0000000 --- a/lndclient/lnd_services.go +++ /dev/null @@ -1,463 +0,0 @@ -package lndclient - -import ( - "context" - "errors" - "fmt" - "net" - "path/filepath" - "time" - - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcutil" - "github.com/lightninglabs/loop/swap" - "github.com/lightningnetwork/lnd/lncfg" - "github.com/lightningnetwork/lnd/lnrpc/verrpc" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/status" -) - -var ( - rpcTimeout = 30 * time.Second - - // minimalCompatibleVersion is the minimum version and build tags - // required in lnd to get all functionality implemented in lndclient. - // Users can provide their own, specific version if needed. If only a - // subset of the lndclient functionality is needed, the required build - // tags can be adjusted accordingly. This default will be used as a fall - // back version if none is specified in the configuration. - minimalCompatibleVersion = &verrpc.Version{ - AppMajor: 0, - AppMinor: 10, - AppPatch: 1, - BuildTags: []string{ - "signrpc", "walletrpc", "chainrpc", "invoicesrpc", - }, - } - - // ErrVersionCheckNotImplemented is the error that is returned if the - // version RPC is not implemented in lnd. This means the version of lnd - // is lower than v0.10.0-beta. - ErrVersionCheckNotImplemented = errors.New("version check not " + - "implemented, need minimum lnd version of v0.10.0-beta") - - // ErrVersionIncompatible is the error that is returned if the connected - // lnd instance is not supported. - ErrVersionIncompatible = errors.New("version incompatible") - - // ErrBuildTagsMissing is the error that is returned if the - // connected lnd instance does not have all built tags activated that - // are required. - ErrBuildTagsMissing = errors.New("build tags missing") -) - -// LndServicesConfig holds all configuration settings that are needed to connect -// to an lnd node. -type LndServicesConfig struct { - // LndAddress is the network address (host:port) of the lnd node to - // connect to. - LndAddress string - - // Network is the bitcoin network we expect the lnd node to operate on. - Network string - - // MacaroonDir is the directory where all lnd macaroons can be found. - MacaroonDir string - - // TLSPath is the path to lnd's TLS certificate file. - TLSPath string - - // CheckVersion is the minimum version the connected lnd node needs to - // be in order to be compatible. The node will be checked against this - // when connecting. If no version is supplied, the default minimum - // version will be used. - CheckVersion *verrpc.Version - - // Dialer is an optional dial function that can be passed in if the - // default lncfg.ClientAddressDialer should not be used. - Dialer DialerFunc -} - -// DialerFunc is a function that is used as grpc.WithContextDialer(). -type DialerFunc func(context.Context, string) (net.Conn, error) - -// LndServices constitutes a set of required services. -type LndServices struct { - Client LightningClient - WalletKit WalletKitClient - ChainNotifier ChainNotifierClient - Signer SignerClient - Invoices InvoicesClient - Router RouterClient - Versioner VersionerClient - - ChainParams *chaincfg.Params - NodeAlias string - NodePubkey [33]byte - Version *verrpc.Version - - macaroons *macaroonPouch -} - -// GrpcLndServices constitutes a set of required RPC services. -type GrpcLndServices struct { - LndServices - - cleanup func() -} - -// NewLndServices creates creates a connection to the given lnd instance and -// creates a set of required RPC services. -func NewLndServices(cfg *LndServicesConfig) (*GrpcLndServices, error) { - // We need to use a custom dialer so we can also connect to unix - // sockets and not just TCP addresses. - if cfg.Dialer == nil { - cfg.Dialer = lncfg.ClientAddressDialer(defaultRPCPort) - } - - // Fall back to minimal compatible version if none if specified. - if cfg.CheckVersion == nil { - cfg.CheckVersion = minimalCompatibleVersion - } - - // Based on the network, if the macaroon directory isn't set, then - // we'll use the expected default locations. - macaroonDir := cfg.MacaroonDir - if macaroonDir == "" { - switch cfg.Network { - case "testnet": - macaroonDir = filepath.Join( - defaultLndDir, defaultDataDir, - defaultChainSubDir, "bitcoin", "testnet", - ) - - case "mainnet": - macaroonDir = filepath.Join( - defaultLndDir, defaultDataDir, - defaultChainSubDir, "bitcoin", "mainnet", - ) - - case "simnet": - macaroonDir = filepath.Join( - defaultLndDir, defaultDataDir, - defaultChainSubDir, "bitcoin", "simnet", - ) - - case "regtest": - macaroonDir = filepath.Join( - defaultLndDir, defaultDataDir, - defaultChainSubDir, "bitcoin", "regtest", - ) - - default: - return nil, fmt.Errorf("unsupported network: %v", - cfg.Network) - } - } - - // Setup connection with lnd - log.Infof("Creating lnd connection to %v", cfg.LndAddress) - conn, err := getClientConn(cfg) - if err != nil { - return nil, err - } - - log.Infof("Connected to lnd") - - chainParams, err := swap.ChainParamsFromNetwork(cfg.Network) - if err != nil { - return nil, err - } - - // We are going to check that the connected lnd is on the same network - // and is a compatible version with all the required subservers enabled. - // For this, we make two calls, both of which only need the readonly - // macaroon. We don't use the pouch yet because if not all subservers - // are enabled, then not all macaroons might be there and the user would - // get a more cryptic error message. - readonlyMac, err := newSerializedMacaroon( - filepath.Join(macaroonDir, defaultReadonlyFilename), - ) - if err != nil { - return nil, err - } - nodeAlias, nodeKey, version, err := checkLndCompatibility( - conn, chainParams, readonlyMac, cfg.Network, cfg.CheckVersion, - ) - if err != nil { - return nil, err - } - - // Now that we've ensured our macaroon directory is set properly, we - // can retrieve our full macaroon pouch from the directory. - macaroons, err := newMacaroonPouch(macaroonDir) - if err != nil { - return nil, fmt.Errorf("unable to obtain macaroons: %v", err) - } - - // With the macaroons loaded and the version checked, we can now create - // the real lightning client which uses the admin macaroon. - lightningClient := newLightningClient( - conn, chainParams, macaroons.adminMac, - ) - - // With the network check passed, we'll now initialize the rest of the - // sub-server connections, giving each of them their specific macaroon. - notifierClient := newChainNotifierClient(conn, macaroons.chainMac) - signerClient := newSignerClient(conn, macaroons.signerMac) - walletKitClient := newWalletKitClient(conn, macaroons.walletKitMac) - invoicesClient := newInvoicesClient(conn, macaroons.invoiceMac) - routerClient := newRouterClient(conn, macaroons.routerMac) - versionerClient := newVersionerClient(conn, macaroons.readonlyMac) - - cleanup := func() { - log.Debugf("Closing lnd connection") - err := conn.Close() - if err != nil { - log.Errorf("Error closing client connection: %v", err) - } - - log.Debugf("Wait for client to finish") - lightningClient.WaitForFinished() - - log.Debugf("Wait for chain notifier to finish") - notifierClient.WaitForFinished() - - log.Debugf("Wait for invoices to finish") - invoicesClient.WaitForFinished() - - log.Debugf("Lnd services finished") - } - - services := &GrpcLndServices{ - LndServices: LndServices{ - Client: lightningClient, - WalletKit: walletKitClient, - ChainNotifier: notifierClient, - Signer: signerClient, - Invoices: invoicesClient, - Router: routerClient, - Versioner: versionerClient, - ChainParams: chainParams, - NodeAlias: nodeAlias, - NodePubkey: nodeKey, - Version: version, - macaroons: macaroons, - }, - cleanup: cleanup, - } - - log.Infof("Using network %v", cfg.Network) - - return services, nil -} - -// Close closes the lnd connection and waits for all sub server clients to -// finish their goroutines. -func (s *GrpcLndServices) Close() { - s.cleanup() - - log.Debugf("Lnd services finished") -} - -// checkLndCompatibility makes sure the connected lnd instance is running on the -// correct network, has the version RPC implemented, is the correct minimal -// version and supports all required build tags/subservers. -func checkLndCompatibility(conn *grpc.ClientConn, chainParams *chaincfg.Params, - readonlyMac serializedMacaroon, network string, - minVersion *verrpc.Version) (string, [33]byte, *verrpc.Version, error) { - - // onErr is a closure that simplifies returning multiple values in the - // error case. - onErr := func(err error) (string, [33]byte, *verrpc.Version, error) { - closeErr := conn.Close() - if closeErr != nil { - log.Errorf("Error closing lnd connection: %v", closeErr) - } - - // Make static error messages a bit less cryptic by adding the - // version or build tag that we expect. - newErr := fmt.Errorf("lnd compatibility check failed: %v", err) - if err == ErrVersionIncompatible || err == ErrBuildTagsMissing { - newErr = fmt.Errorf("error checking connected lnd "+ - "version. at least version \"%s\" is "+ - "required", VersionString(minVersion)) - } - - return "", [33]byte{}, nil, newErr - } - - // We use our own clients with a readonly macaroon here, because we know - // that's all we need for the checks. - lightningClient := newLightningClient(conn, chainParams, readonlyMac) - versionerClient := newVersionerClient(conn, readonlyMac) - - // With our readonly macaroon obtained, we'll ensure that the network - // for lnd matches our expected network. - info, err := lightningClient.GetInfo(context.Background()) - if err != nil { - err := fmt.Errorf("unable to get info for lnd node: %v", err) - return onErr(err) - } - if network != info.Network { - err := fmt.Errorf("network mismatch with connected lnd node, "+ - "wanted '%s', got '%s'", network, info.Network) - return onErr(err) - } - - // Now let's also check the version of the connected lnd node. - version, err := checkVersionCompatibility(versionerClient, minVersion) - if err != nil { - return onErr(err) - } - - // Return the static part of the info we just queried from the node so - // it can be cached for later use. - return info.Alias, info.IdentityPubkey, version, nil -} - -// checkVersionCompatibility makes sure the connected lnd node has the correct -// version and required build tags enabled. -// -// NOTE: This check will **never** return a non-nil error for a version of -// lnd < 0.10.0 because any version previous to 0.10.0 doesn't have the version -// endpoint implemented! -func checkVersionCompatibility(client VersionerClient, - expected *verrpc.Version) (*verrpc.Version, error) { - - // First, test that the version RPC is even implemented. - version, err := client.GetVersion(context.Background()) - if err != nil { - // The version service has only been added in lnd v0.10.0. If - // we get an unimplemented error, it means the lnd version is - // definitely older than that. - s, ok := status.FromError(err) - if ok && s.Code() == codes.Unimplemented { - return nil, ErrVersionCheckNotImplemented - } - return nil, fmt.Errorf("GetVersion error: %v", err) - } - - log.Infof("lnd version: %v", VersionString(version)) - - // Now check the version and make sure all required build tags are set. - err = assertVersionCompatible(version, expected) - if err != nil { - return nil, err - } - err = assertBuildTagsEnabled(version, expected.BuildTags) - if err != nil { - return nil, err - } - - // All check positive, version is fully compatible. - return version, nil -} - -// assertVersionCompatible makes sure the detected lnd version is compatible -// with our current version requirements. -func assertVersionCompatible(actual *verrpc.Version, - expected *verrpc.Version) error { - - // We need to check the versions parts sequentially as they are - // hierarchical. - if actual.AppMajor != expected.AppMajor { - if actual.AppMajor > expected.AppMajor { - return nil - } - return ErrVersionIncompatible - } - - if actual.AppMinor != expected.AppMinor { - if actual.AppMinor > expected.AppMinor { - return nil - } - return ErrVersionIncompatible - } - - if actual.AppPatch != expected.AppPatch { - if actual.AppPatch > expected.AppPatch { - return nil - } - return ErrVersionIncompatible - } - - // The actual version and expected version are identical. - return nil -} - -// assertBuildTagsEnabled makes sure all required build tags are set. -func assertBuildTagsEnabled(actual *verrpc.Version, - requiredTags []string) error { - - tagMap := make(map[string]struct{}) - for _, tag := range actual.BuildTags { - tagMap[tag] = struct{}{} - } - for _, required := range requiredTags { - if _, ok := tagMap[required]; !ok { - return ErrBuildTagsMissing - } - } - - // All tags found. - return nil -} - -var ( - defaultRPCPort = "10009" - defaultLndDir = btcutil.AppDataDir("lnd", false) - defaultTLSCertFilename = "tls.cert" - defaultTLSCertPath = filepath.Join( - defaultLndDir, defaultTLSCertFilename, - ) - defaultDataDir = "data" - defaultChainSubDir = "chain" - - defaultAdminMacaroonFilename = "admin.macaroon" - defaultInvoiceMacaroonFilename = "invoices.macaroon" - defaultChainMacaroonFilename = "chainnotifier.macaroon" - defaultWalletKitMacaroonFilename = "walletkit.macaroon" - defaultRouterMacaroonFilename = "router.macaroon" - defaultSignerFilename = "signer.macaroon" - defaultReadonlyFilename = "readonly.macaroon" - - // maxMsgRecvSize is the largest gRPC message our client will receive. - // We set this to 200MiB. - maxMsgRecvSize = grpc.MaxCallRecvMsgSize(1 * 1024 * 1024 * 200) -) - -func getClientConn(cfg *LndServicesConfig) (*grpc.ClientConn, error) { - - // Load the specified TLS certificate and build transport credentials - // with it. - tlsPath := cfg.TLSPath - if tlsPath == "" { - tlsPath = defaultTLSCertPath - } - - creds, err := credentials.NewClientTLSFromFile(tlsPath, "") - if err != nil { - return nil, err - } - - // Create a dial options array. - opts := []grpc.DialOption{ - grpc.WithTransportCredentials(creds), - - // Use a custom dialer, to allow connections to unix sockets, - // in-memory listeners etc, and not just TCP addresses. - grpc.WithContextDialer(cfg.Dialer), - grpc.WithDefaultCallOptions(maxMsgRecvSize), - } - - conn, err := grpc.Dial(cfg.LndAddress, opts...) - if err != nil { - return nil, fmt.Errorf("unable to connect to RPC server: %v", - err) - } - - return conn, nil -} diff --git a/lndclient/lnd_services_test.go b/lndclient/lnd_services_test.go deleted file mode 100644 index 30bc8be..0000000 --- a/lndclient/lnd_services_test.go +++ /dev/null @@ -1,158 +0,0 @@ -package lndclient - -import ( - "context" - "testing" - - "github.com/lightningnetwork/lnd/lnrpc/verrpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -type mockVersioner struct { - version *verrpc.Version - err error -} - -func (m *mockVersioner) GetVersion(_ context.Context) (*verrpc.Version, error) { - return m.version, m.err -} - -// TestCheckVersionCompatibility makes sure the correct error is returned if an -// old lnd is connected that doesn't implement the version RPC, has an older -// version or if an lnd with not all subservers enabled is connected. -func TestCheckVersionCompatibility(t *testing.T) { - // Make sure a version check against a node that doesn't implement the - // version RPC always fails. - unimplemented := &mockVersioner{ - err: status.Error(codes.Unimplemented, "missing"), - } - _, err := checkVersionCompatibility(unimplemented, &verrpc.Version{ - AppMajor: 0, - AppMinor: 10, - AppPatch: 0, - }) - if err != ErrVersionCheckNotImplemented { - t.Fatalf("unexpected error. got '%v' wanted '%v'", err, - ErrVersionCheckNotImplemented) - } - - // Next, make sure an older version than what we want is rejected. - oldVersion := &mockVersioner{ - version: &verrpc.Version{ - AppMajor: 0, - AppMinor: 10, - AppPatch: 0, - }, - } - _, err = checkVersionCompatibility(oldVersion, &verrpc.Version{ - AppMajor: 0, - AppMinor: 11, - AppPatch: 0, - }) - if err != ErrVersionIncompatible { - t.Fatalf("unexpected error. got '%v' wanted '%v'", err, - ErrVersionIncompatible) - } - - // Finally, make sure we also get the correct error when trying to run - // against an lnd that doesn't have all required build tags enabled. - buildTagsMissing := &mockVersioner{ - version: &verrpc.Version{ - AppMajor: 0, - AppMinor: 10, - AppPatch: 0, - BuildTags: []string{"dev", "lntest", "btcd", "signrpc"}, - }, - } - _, err = checkVersionCompatibility(buildTagsMissing, &verrpc.Version{ - AppMajor: 0, - AppMinor: 10, - AppPatch: 0, - BuildTags: []string{"signrpc", "walletrpc"}, - }) - if err != ErrBuildTagsMissing { - t.Fatalf("unexpected error. got '%v' wanted '%v'", err, - ErrVersionIncompatible) - } -} - -// TestLndVersionCheckComparison makes sure the version check comparison works -// correctly and considers all three version levels. -func TestLndVersionCheckComparison(t *testing.T) { - actual := &verrpc.Version{ - AppMajor: 1, - AppMinor: 2, - AppPatch: 3, - } - testCases := []struct { - name string - expectMajor uint32 - expectMinor uint32 - expectPatch uint32 - actual *verrpc.Version - expectedErr error - }{ - { - name: "no expectation", - expectMajor: 0, - expectMinor: 0, - expectPatch: 0, - actual: actual, - expectedErr: nil, - }, - { - name: "expect exact same version", - expectMajor: 1, - expectMinor: 2, - expectPatch: 3, - actual: actual, - expectedErr: nil, - }, - { - name: "ignore patch if minor is bigger", - expectMajor: 12, - expectMinor: 9, - expectPatch: 14, - actual: &verrpc.Version{ - AppMajor: 12, - AppMinor: 22, - AppPatch: 0, - }, - expectedErr: nil, - }, - { - name: "all fields different", - expectMajor: 3, - expectMinor: 2, - expectPatch: 1, - actual: actual, - expectedErr: ErrVersionIncompatible, - }, - { - name: "patch version different", - expectMajor: 1, - expectMinor: 2, - expectPatch: 4, - actual: actual, - expectedErr: ErrVersionIncompatible, - }, - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.name, func(t *testing.T) { - err := assertVersionCompatible( - tc.actual, &verrpc.Version{ - AppMajor: tc.expectMajor, - AppMinor: tc.expectMinor, - AppPatch: tc.expectPatch, - }, - ) - if err != tc.expectedErr { - t.Fatalf("unexpected error, got '%v' wanted "+ - "'%v'", err, tc.expectedErr) - } - }) - } -} diff --git a/lndclient/log.go b/lndclient/log.go deleted file mode 100644 index db4dd42..0000000 --- a/lndclient/log.go +++ /dev/null @@ -1,23 +0,0 @@ -package lndclient - -import ( - "github.com/btcsuite/btclog" - "github.com/lightningnetwork/lnd/build" -) - -// log is a logger that is initialized with no output filters. This -// means the package will not perform any logging by default until the -// caller requests it. -var log btclog.Logger - -// The default amount of logging is none. -func init() { - UseLogger(build.NewSubLogger("LNDC", nil)) -} - -// UseLogger uses a specified Logger to output package logging info. -// This should be used in preference to SetLogWriter if the caller is also -// using btclog. -func UseLogger(logger btclog.Logger) { - log = logger -} diff --git a/lndclient/macaroon_pouch.go b/lndclient/macaroon_pouch.go deleted file mode 100644 index e0a2ae9..0000000 --- a/lndclient/macaroon_pouch.go +++ /dev/null @@ -1,118 +0,0 @@ -package lndclient - -import ( - "context" - "encoding/hex" - "io/ioutil" - "path/filepath" - - "google.golang.org/grpc/metadata" -) - -// serializedMacaroon is a type that represents a hex-encoded macaroon. We'll -// use this primarily vs the raw binary format as the gRPC metadata feature -// requires that all keys and values be strings. -type serializedMacaroon string - -// newSerializedMacaroon reads a new serializedMacaroon from that target -// macaroon path. If the file can't be found, then an error is returned. -func newSerializedMacaroon(macaroonPath string) (serializedMacaroon, error) { - macBytes, err := ioutil.ReadFile(macaroonPath) - if err != nil { - return "", err - } - - return serializedMacaroon(hex.EncodeToString(macBytes)), nil -} - -// WithMacaroonAuth modifies the passed context to include the macaroon KV -// metadata of the target macaroon. This method can be used to add the macaroon -// at call time, rather than when the connection to the gRPC server is created. -func (s serializedMacaroon) WithMacaroonAuth(ctx context.Context) context.Context { - return metadata.AppendToOutgoingContext(ctx, "macaroon", string(s)) -} - -// macaroonPouch holds the set of macaroons we need to interact with lnd for -// Loop. Each sub-server has its own macaroon, and for the remaining temporary -// calls that directly hit lnd, we'll use the admin macaroon. -type macaroonPouch struct { - // invoiceMac is the macaroon for the invoices sub-server. - invoiceMac serializedMacaroon - - // chainMac is the macaroon for the ChainNotifier sub-server. - chainMac serializedMacaroon - - // signerMac is the macaroon for the Signer sub-server. - signerMac serializedMacaroon - - // 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 - - // readonlyMac is the primary read-only macaroon for lnd. - readonlyMac serializedMacaroon -} - -// newMacaroonPouch returns a new instance of a fully populated macaroonPouch -// given the directory where all the macaroons are stored. -func newMacaroonPouch(macaroonDir string) (*macaroonPouch, error) { - m := &macaroonPouch{} - - var err error - - m.invoiceMac, err = newSerializedMacaroon( - filepath.Join(macaroonDir, defaultInvoiceMacaroonFilename), - ) - if err != nil { - return nil, err - } - - m.chainMac, err = newSerializedMacaroon( - filepath.Join(macaroonDir, defaultChainMacaroonFilename), - ) - if err != nil { - return nil, err - } - - m.signerMac, err = newSerializedMacaroon( - filepath.Join(macaroonDir, defaultSignerFilename), - ) - if err != nil { - return nil, err - } - - m.walletKitMac, err = newSerializedMacaroon( - filepath.Join(macaroonDir, defaultWalletKitMacaroonFilename), - ) - if err != nil { - 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), - ) - if err != nil { - return nil, err - } - - m.readonlyMac, err = newSerializedMacaroon( - filepath.Join(macaroonDir, defaultReadonlyFilename), - ) - if err != nil { - return nil, err - } - - return m, nil -} diff --git a/lndclient/router_client.go b/lndclient/router_client.go deleted file mode 100644 index 32ea08b..0000000 --- a/lndclient/router_client.go +++ /dev/null @@ -1,416 +0,0 @@ -package lndclient - -import ( - "context" - "crypto/rand" - "encoding/hex" - "fmt" - "io" - "time" - - "github.com/btcsuite/btcutil" - "github.com/lightningnetwork/lnd/channeldb" - "github.com/lightningnetwork/lnd/lnrpc" - "github.com/lightningnetwork/lnd/lnrpc/routerrpc" - "github.com/lightningnetwork/lnd/lntypes" - "github.com/lightningnetwork/lnd/lnwire" - "github.com/lightningnetwork/lnd/record" - "github.com/lightningnetwork/lnd/routing/route" - "github.com/lightningnetwork/lnd/zpay32" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -// 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 lnrpc.Payment_PaymentStatus - - // FailureReason is the reason why the payment failed. Only set when - // State is Failed. - FailureReason lnrpc.PaymentFailureReason - - Preimage lntypes.Preimage - Fee lnwire.MilliSatoshi - Value lnwire.MilliSatoshi - InFlightAmt lnwire.MilliSatoshi - InFlightHtlcs int -} - -func (p PaymentStatus) String() string { - text := fmt.Sprintf("state=%v", p.State) - if p.State == lnrpc.Payment_IN_FLIGHT { - text += fmt.Sprintf(", inflight_htlcs=%v, inflight_amt=%v", - p.InFlightHtlcs, p.InFlightAmt) - } - - return text -} - -// SendPaymentRequest defines the payment parameters for a new payment. -type SendPaymentRequest struct { - // Invoice is an encoded payment request. The individual payment - // parameters Target, Amount, PaymentHash, FinalCLTVDelta and RouteHints - // are only processed when the Invoice field is empty. - Invoice string - - // 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 - - // Amount is the value of the payment to send through the network in - // satoshis. - Amount btcutil.Amount - - // PaymentHash is the r-hash value to use within the HTLC extended to - // the first hop. - PaymentHash *lntypes.Hash - - // FinalCLTVDelta is the CTLV expiry delta to use for the _final_ hop - // in the route. This means that the final hop will have a CLTV delta - // of at least: currentHeight + FinalCLTVDelta. - FinalCLTVDelta uint16 - - // RouteHints represents the different routing hints that can be used to - // assist a payment in reaching its destination successfully. These - // hints will act as intermediate hops along the route. - // - // NOTE: This is optional unless required by the payment. When providing - // multiple routes, ensure the hop hints within each route are chained - // together and sorted in forward order in order to reach the - // destination successfully. - RouteHints [][]zpay32.HopHint - - // LastHopPubkey is the pubkey of the last hop of the route taken - // for this payment. If empty, any hop may be used. - LastHopPubkey *route.Vertex - - // MaxParts is the maximum number of partial payments that may be used - // to complete the full amount. - MaxParts uint32 - - // KeySend is set to true if the tlv payload will include the preimage. - KeySend bool - - // CustomRecords holds the custom TLV records that will be added to the - // payment. - CustomRecords map[uint64][]byte -} - -// 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()), - MaxParts: request.MaxParts, - OutgoingChanIds: request.OutgoingChanIds, - } - if request.MaxCltv != nil { - rpcReq.CltvLimit = *request.MaxCltv - } - - if request.LastHopPubkey != nil { - rpcReq.LastHopPubkey = request.LastHopPubkey[:] - } - - rpcReq.DestCustomRecords = request.CustomRecords - - if request.KeySend { - if request.PaymentHash != nil { - return nil, nil, fmt.Errorf( - "keysend payment must not include a preset payment hash") - } - - var preimage lntypes.Preimage - if _, err := rand.Read(preimage[:]); err != nil { - return nil, nil, err - } - - if rpcReq.DestCustomRecords == nil { - rpcReq.DestCustomRecords = make(map[uint64][]byte) - } - - // Override the payment hash. - rpcReq.DestCustomRecords[record.KeySendType] = preimage[:] - hash := preimage.Hash() - request.PaymentHash = &hash - } - - // Only if there is no payment request set, we will parse the individual - // payment parameters. - if request.Invoice == "" { - rpcReq.Dest = request.Target[:] - rpcReq.Amt = int64(request.Amount) - rpcReq.PaymentHash = request.PaymentHash[:] - rpcReq.FinalCltvDelta = int32(request.FinalCLTVDelta) - - routeHints, err := marshallRouteHints(request.RouteHints) - if err != nil { - return nil, nil, err - } - rpcReq.RouteHints = routeHints - } - - stream, err := r.client.SendPaymentV2(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.TrackPaymentV2( - 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. -// Once the payment reaches a final state, the status and error channels will -// be closed to signal that we are finished sending into them. -func (r *routerClient) trackPayment(ctx context.Context, - stream routerrpc.Router_TrackPaymentV2Client) (chan PaymentStatus, - chan error, error) { - - statusChan := make(chan PaymentStatus) - errorChan := make(chan error, 1) - go func() { - for { - payment, err := stream.Recv() - if err != nil { - // If we get an EOF error, the payment has - // reached a final state and the server is - // finished sending us updates. We close both - // channels to signal that we are done sending - // values on them and return. - if err == io.EOF { - close(statusChan) - close(errorChan) - return - } - - 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(payment) - 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(rpcPayment *lnrpc.Payment) ( - *PaymentStatus, error) { - - status := PaymentStatus{ - State: rpcPayment.Status, - } - - switch status.State { - case lnrpc.Payment_SUCCEEDED: - preimage, err := lntypes.MakePreimageFromStr( - rpcPayment.PaymentPreimage, - ) - if err != nil { - return nil, err - } - status.Preimage = preimage - status.Fee = lnwire.MilliSatoshi(rpcPayment.FeeMsat) - status.Value = lnwire.MilliSatoshi(rpcPayment.ValueMsat) - - case lnrpc.Payment_FAILED: - status.FailureReason = rpcPayment.FailureReason - } - - for _, htlc := range rpcPayment.Htlcs { - if htlc.Status != lnrpc.HTLCAttempt_IN_FLIGHT { - continue - } - - status.InFlightHtlcs++ - - lastHop := htlc.Route.Hops[len(htlc.Route.Hops)-1] - status.InFlightAmt += lnwire.MilliSatoshi( - lastHop.AmtToForwardMsat, - ) - } - - 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 -} - -// marshallRouteHints marshalls a list of route hints. -func marshallRouteHints(routeHints [][]zpay32.HopHint) ( - []*lnrpc.RouteHint, error) { - - rpcRouteHints := make([]*lnrpc.RouteHint, 0, len(routeHints)) - for _, routeHint := range routeHints { - rpcRouteHint := make( - []*lnrpc.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, &lnrpc.RouteHint{ - HopHints: rpcRouteHint, - }) - } - - return rpcRouteHints, nil -} - -// marshallHopHint marshalls a single hop hint. -func marshallHopHint(hint zpay32.HopHint) (*lnrpc.HopHint, error) { - nodeID, err := route.NewVertexFromBytes( - hint.NodeID.SerializeCompressed(), - ) - if err != nil { - return nil, err - } - - return &lnrpc.HopHint{ - ChanId: hint.ChannelID, - CltvExpiryDelta: uint32(hint.CLTVExpiryDelta), - FeeBaseMsat: hint.FeeBaseMSat, - FeeProportionalMillionths: hint.FeeProportionalMillionths, - NodeId: nodeID.String(), - }, nil -} diff --git a/lndclient/signer_client.go b/lndclient/signer_client.go deleted file mode 100644 index eec0621..0000000 --- a/lndclient/signer_client.go +++ /dev/null @@ -1,254 +0,0 @@ -package lndclient - -import ( - "context" - - "github.com/btcsuite/btcd/btcec" - "github.com/btcsuite/btcd/wire" - "github.com/lightninglabs/loop/swap" - "github.com/lightningnetwork/lnd/input" - "github.com/lightningnetwork/lnd/keychain" - "github.com/lightningnetwork/lnd/lnrpc/signrpc" - "google.golang.org/grpc" -) - -// SignerClient exposes sign functionality. -type SignerClient interface { - SignOutputRaw(ctx context.Context, tx *wire.MsgTx, - signDescriptors []*input.SignDescriptor) ([][]byte, error) - - // ComputeInputScript generates the proper input script for P2WPKH - // output and NP2WPKH outputs. This method only requires that the - // `Output`, `HashType`, `SigHashes` and `InputIndex` fields are - // populated within the sign descriptors. - ComputeInputScript(ctx context.Context, tx *wire.MsgTx, - signDescriptors []*input.SignDescriptor) ([]*input.Script, error) - - // SignMessage signs a message with the key specified in the key - // locator. The returned signature is fixed-size LN wire format encoded. - SignMessage(ctx context.Context, msg []byte, - locator keychain.KeyLocator) ([]byte, error) - - // VerifyMessage verifies a signature over a message using the public - // key provided. The signature must be fixed-size LN wire format - // encoded. - VerifyMessage(ctx context.Context, msg, sig []byte, pubkey [33]byte) ( - bool, error) - - // DeriveSharedKey returns a shared secret key by performing - // Diffie-Hellman key derivation between the ephemeral public key and - // the key specified by the key locator (or the node's identity private - // key if no key locator is specified): - // - // P_shared = privKeyNode * ephemeralPubkey - // - // The resulting shared public key is serialized in the compressed - // format and hashed with SHA256, resulting in a final key length of 256 - // bits. - DeriveSharedKey(ctx context.Context, ephemeralPubKey *btcec.PublicKey, - keyLocator *keychain.KeyLocator) ([32]byte, error) -} - -type signerClient struct { - client signrpc.SignerClient - signerMac serializedMacaroon -} - -func newSignerClient(conn *grpc.ClientConn, - signerMac serializedMacaroon) *signerClient { - - return &signerClient{ - client: signrpc.NewSignerClient(conn), - signerMac: signerMac, - } -} - -func marshallSignDescriptors(signDescriptors []*input.SignDescriptor, -) []*signrpc.SignDescriptor { - - rpcSignDescs := make([]*signrpc.SignDescriptor, len(signDescriptors)) - for i, signDesc := range signDescriptors { - var keyBytes []byte - var keyLocator *signrpc.KeyLocator - if signDesc.KeyDesc.PubKey != nil { - keyBytes = signDesc.KeyDesc.PubKey.SerializeCompressed() - } else { - keyLocator = &signrpc.KeyLocator{ - KeyFamily: int32( - signDesc.KeyDesc.KeyLocator.Family, - ), - KeyIndex: int32( - signDesc.KeyDesc.KeyLocator.Index, - ), - } - } - - var doubleTweak []byte - if signDesc.DoubleTweak != nil { - doubleTweak = signDesc.DoubleTweak.Serialize() - } - - rpcSignDescs[i] = &signrpc.SignDescriptor{ - WitnessScript: signDesc.WitnessScript, - Output: &signrpc.TxOut{ - PkScript: signDesc.Output.PkScript, - Value: signDesc.Output.Value, - }, - Sighash: uint32(signDesc.HashType), - InputIndex: int32(signDesc.InputIndex), - KeyDesc: &signrpc.KeyDescriptor{ - RawKeyBytes: keyBytes, - KeyLoc: keyLocator, - }, - SingleTweak: signDesc.SingleTweak, - DoubleTweak: doubleTweak, - } - } - - return rpcSignDescs -} - -func (s *signerClient) SignOutputRaw(ctx context.Context, tx *wire.MsgTx, - signDescriptors []*input.SignDescriptor) ([][]byte, error) { - - txRaw, err := swap.EncodeTx(tx) - if err != nil { - return nil, err - } - rpcSignDescs := marshallSignDescriptors(signDescriptors) - - rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - rpcCtx = s.signerMac.WithMacaroonAuth(rpcCtx) - resp, err := s.client.SignOutputRaw(rpcCtx, - &signrpc.SignReq{ - RawTxBytes: txRaw, - SignDescs: rpcSignDescs, - }, - ) - if err != nil { - return nil, err - } - - return resp.RawSigs, nil -} - -// ComputeInputScript generates the proper input script for P2WPKH output and -// NP2WPKH outputs. This method only requires that the `Output`, `HashType`, -// `SigHashes` and `InputIndex` fields are populated within the sign -// descriptors. -func (s *signerClient) ComputeInputScript(ctx context.Context, tx *wire.MsgTx, - signDescriptors []*input.SignDescriptor) ([]*input.Script, error) { - - txRaw, err := swap.EncodeTx(tx) - if err != nil { - return nil, err - } - rpcSignDescs := marshallSignDescriptors(signDescriptors) - - rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - rpcCtx = s.signerMac.WithMacaroonAuth(rpcCtx) - resp, err := s.client.ComputeInputScript( - rpcCtx, &signrpc.SignReq{ - RawTxBytes: txRaw, - SignDescs: rpcSignDescs, - }, - ) - if err != nil { - return nil, err - } - - inputScripts := make([]*input.Script, 0, len(resp.InputScripts)) - for _, inputScript := range resp.InputScripts { - inputScripts = append(inputScripts, &input.Script{ - SigScript: inputScript.SigScript, - Witness: inputScript.Witness, - }) - } - - return inputScripts, nil -} - -// SignMessage signs a message with the key specified in the key locator. The -// returned signature is fixed-size LN wire format encoded. -func (s *signerClient) SignMessage(ctx context.Context, msg []byte, - locator keychain.KeyLocator) ([]byte, error) { - - rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - rpcIn := &signrpc.SignMessageReq{ - Msg: msg, - KeyLoc: &signrpc.KeyLocator{ - KeyFamily: int32(locator.Family), - KeyIndex: int32(locator.Index), - }, - } - - rpcCtx = s.signerMac.WithMacaroonAuth(rpcCtx) - resp, err := s.client.SignMessage(rpcCtx, rpcIn) - if err != nil { - return nil, err - } - - return resp.Signature, nil -} - -// VerifyMessage verifies a signature over a message using the public key -// provided. The signature must be fixed-size LN wire format encoded. -func (s *signerClient) VerifyMessage(ctx context.Context, msg, sig []byte, - pubkey [33]byte) (bool, error) { - - rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - rpcIn := &signrpc.VerifyMessageReq{ - Msg: msg, - Signature: sig, - Pubkey: pubkey[:], - } - - rpcCtx = s.signerMac.WithMacaroonAuth(rpcCtx) - resp, err := s.client.VerifyMessage(rpcCtx, rpcIn) - if err != nil { - return false, err - } - return resp.Valid, nil -} - -// DeriveSharedKey returns a shared secret key by performing Diffie-Hellman key -// derivation between the ephemeral public key and the key specified by the key -// locator (or the node's identity private key if no key locator is specified): -// -// P_shared = privKeyNode * ephemeralPubkey -// -// The resulting shared public key is serialized in the compressed format and -// hashed with SHA256, resulting in a final key length of 256 bits. -func (s *signerClient) DeriveSharedKey(ctx context.Context, - ephemeralPubKey *btcec.PublicKey, - keyLocator *keychain.KeyLocator) ([32]byte, error) { - - rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - rpcIn := &signrpc.SharedKeyRequest{ - EphemeralPubkey: ephemeralPubKey.SerializeCompressed(), - KeyLoc: &signrpc.KeyLocator{ - KeyFamily: int32(keyLocator.Family), - KeyIndex: int32(keyLocator.Index), - }, - } - - rpcCtx = s.signerMac.WithMacaroonAuth(rpcCtx) - resp, err := s.client.DeriveSharedKey(rpcCtx, rpcIn) - if err != nil { - return [32]byte{}, err - } - - var sharedKey [32]byte - copy(sharedKey[:], resp.SharedKey) - return sharedKey, nil -} diff --git a/lndclient/versioner_client.go b/lndclient/versioner_client.go deleted file mode 100644 index fd36812..0000000 --- a/lndclient/versioner_client.go +++ /dev/null @@ -1,68 +0,0 @@ -package lndclient - -import ( - "context" - "fmt" - "strings" - - "github.com/lightningnetwork/lnd/lnrpc/verrpc" - "google.golang.org/grpc" -) - -// VersionerClient exposes the version of lnd. -type VersionerClient interface { - // GetVersion returns the version and build information of the lnd - // daemon. - GetVersion(ctx context.Context) (*verrpc.Version, error) -} - -type versionerClient struct { - client verrpc.VersionerClient - readonlyMac serializedMacaroon -} - -func newVersionerClient(conn *grpc.ClientConn, - readonlyMac serializedMacaroon) *versionerClient { - - return &versionerClient{ - client: verrpc.NewVersionerClient(conn), - readonlyMac: readonlyMac, - } -} - -// GetVersion returns the version and build information of the lnd -// daemon. -// -// NOTE: This method is part of the VersionerClient interface. -func (v *versionerClient) GetVersion(ctx context.Context) (*verrpc.Version, - error) { - - rpcCtx, cancel := context.WithTimeout( - v.readonlyMac.WithMacaroonAuth(ctx), rpcTimeout, - ) - defer cancel() - return v.client.GetVersion(rpcCtx, &verrpc.VersionRequest{}) -} - -// VersionString returns a nice, human readable string of a version returned by -// the VersionerClient, including all build tags. -func VersionString(version *verrpc.Version) string { - short := VersionStringShort(version) - enabledTags := strings.Join(version.BuildTags, ",") - return fmt.Sprintf("%s, build tags '%s'", short, enabledTags) -} - -// VersionStringShort returns a nice, human readable string of a version -// returned by the VersionerClient. -func VersionStringShort(version *verrpc.Version) string { - versionStr := fmt.Sprintf( - "v%d.%d.%d", version.AppMajor, version.AppMinor, - version.AppPatch, - ) - if version.AppPreRelease != "" { - versionStr = fmt.Sprintf( - "%s-%s", versionStr, version.AppPreRelease, - ) - } - return versionStr -} diff --git a/lndclient/walletkit_client.go b/lndclient/walletkit_client.go deleted file mode 100644 index 99ff405..0000000 --- a/lndclient/walletkit_client.go +++ /dev/null @@ -1,349 +0,0 @@ -package lndclient - -import ( - "context" - "encoding/hex" - "fmt" - "time" - - "github.com/btcsuite/btcd/btcec" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcwallet/wtxmgr" - "github.com/lightninglabs/loop/swap" - "github.com/lightningnetwork/lnd/keychain" - "github.com/lightningnetwork/lnd/lnrpc" - "github.com/lightningnetwork/lnd/lnrpc/signrpc" - "github.com/lightningnetwork/lnd/lnrpc/walletrpc" - "github.com/lightningnetwork/lnd/lnwallet" - "github.com/lightningnetwork/lnd/lnwallet/chainfee" - "google.golang.org/grpc" -) - -// WalletKitClient exposes wallet functionality. -type WalletKitClient interface { - // ListUnspent returns a list of all utxos spendable by the wallet with - // a number of confirmations between the specified minimum and maximum. - ListUnspent(ctx context.Context, minConfs, maxConfs int32) ( - []*lnwallet.Utxo, error) - - // LeaseOutput locks an output to the given ID, preventing it from being - // available for any future coin selection attempts. The absolute time - // of the lock's expiration is returned. The expiration of the lock can - // be extended by successive invocations of this call. Outputs can be - // unlocked before their expiration through `ReleaseOutput`. - LeaseOutput(ctx context.Context, lockID wtxmgr.LockID, - op wire.OutPoint) (time.Time, error) - - // ReleaseOutput unlocks an output, allowing it to be available for coin - // selection if it remains unspent. The ID should match the one used to - // originally lock the output. - ReleaseOutput(ctx context.Context, lockID wtxmgr.LockID, - op wire.OutPoint) error - - DeriveNextKey(ctx context.Context, family int32) ( - *keychain.KeyDescriptor, error) - - DeriveKey(ctx context.Context, locator *keychain.KeyLocator) ( - *keychain.KeyDescriptor, error) - - NextAddr(ctx context.Context) (btcutil.Address, error) - - PublishTransaction(ctx context.Context, tx *wire.MsgTx) error - - SendOutputs(ctx context.Context, outputs []*wire.TxOut, - feeRate chainfee.SatPerKWeight) (*wire.MsgTx, error) - - EstimateFee(ctx context.Context, confTarget int32) (chainfee.SatPerKWeight, - error) - - // ListSweeps returns a list of sweep transaction ids known to our node. - // Note that this function only looks up transaction ids, and does not - // query our wallet for the full set of transactions. - ListSweeps(ctx context.Context) ([]string, error) -} - -type walletKitClient struct { - client walletrpc.WalletKitClient - walletKitMac serializedMacaroon -} - -// A compile-time constraint to ensure walletKitclient satisfies the -// WalletKitClient interface. -var _ WalletKitClient = (*walletKitClient)(nil) - -func newWalletKitClient(conn *grpc.ClientConn, - walletKitMac serializedMacaroon) *walletKitClient { - - return &walletKitClient{ - client: walletrpc.NewWalletKitClient(conn), - walletKitMac: walletKitMac, - } -} - -// ListUnspent returns a list of all utxos spendable by the wallet with a number -// of confirmations between the specified minimum and maximum. -func (m *walletKitClient) ListUnspent(ctx context.Context, minConfs, - maxConfs int32) ([]*lnwallet.Utxo, error) { - - rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - rpcCtx = m.walletKitMac.WithMacaroonAuth(rpcCtx) - resp, err := m.client.ListUnspent(rpcCtx, &walletrpc.ListUnspentRequest{ - MinConfs: minConfs, - MaxConfs: maxConfs, - }) - if err != nil { - return nil, err - } - - utxos := make([]*lnwallet.Utxo, 0, len(resp.Utxos)) - for _, utxo := range resp.Utxos { - var addrType lnwallet.AddressType - switch utxo.AddressType { - case lnrpc.AddressType_WITNESS_PUBKEY_HASH: - addrType = lnwallet.WitnessPubKey - case lnrpc.AddressType_NESTED_PUBKEY_HASH: - addrType = lnwallet.NestedWitnessPubKey - default: - return nil, fmt.Errorf("invalid utxo address type %v", - utxo.AddressType) - } - - pkScript, err := hex.DecodeString(utxo.PkScript) - if err != nil { - return nil, err - } - - opHash, err := chainhash.NewHash(utxo.Outpoint.TxidBytes) - if err != nil { - return nil, err - } - - utxos = append(utxos, &lnwallet.Utxo{ - AddressType: addrType, - Value: btcutil.Amount(utxo.AmountSat), - Confirmations: utxo.Confirmations, - PkScript: pkScript, - OutPoint: wire.OutPoint{ - Hash: *opHash, - Index: utxo.Outpoint.OutputIndex, - }, - }) - } - - return utxos, nil -} - -// LeaseOutput locks an output to the given ID, preventing it from being -// available for any future coin selection attempts. The absolute time of the -// lock's expiration is returned. The expiration of the lock can be extended by -// successive invocations of this call. Outputs can be unlocked before their -// expiration through `ReleaseOutput`. -func (m *walletKitClient) LeaseOutput(ctx context.Context, lockID wtxmgr.LockID, - op wire.OutPoint) (time.Time, error) { - - rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - rpcCtx = m.walletKitMac.WithMacaroonAuth(rpcCtx) - resp, err := m.client.LeaseOutput(rpcCtx, &walletrpc.LeaseOutputRequest{ - Id: lockID[:], - Outpoint: &lnrpc.OutPoint{ - TxidBytes: op.Hash[:], - OutputIndex: op.Index, - }, - }) - if err != nil { - return time.Time{}, err - } - - return time.Unix(int64(resp.Expiration), 0), nil -} - -// ReleaseOutput unlocks an output, allowing it to be available for coin -// selection if it remains unspent. The ID should match the one used to -// originally lock the output. -func (m *walletKitClient) ReleaseOutput(ctx context.Context, - lockID wtxmgr.LockID, op wire.OutPoint) error { - - rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - rpcCtx = m.walletKitMac.WithMacaroonAuth(rpcCtx) - _, err := m.client.ReleaseOutput(rpcCtx, &walletrpc.ReleaseOutputRequest{ - Id: lockID[:], - Outpoint: &lnrpc.OutPoint{ - TxidBytes: op.Hash[:], - OutputIndex: op.Index, - }, - }) - return err -} - -func (m *walletKitClient) DeriveNextKey(ctx context.Context, family int32) ( - *keychain.KeyDescriptor, error) { - - rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - rpcCtx = m.walletKitMac.WithMacaroonAuth(rpcCtx) - resp, err := m.client.DeriveNextKey(rpcCtx, &walletrpc.KeyReq{ - KeyFamily: family, - }) - if err != nil { - return nil, err - } - - key, err := btcec.ParsePubKey(resp.RawKeyBytes, btcec.S256()) - if err != nil { - return nil, err - } - - return &keychain.KeyDescriptor{ - KeyLocator: keychain.KeyLocator{ - Family: keychain.KeyFamily(resp.KeyLoc.KeyFamily), - Index: uint32(resp.KeyLoc.KeyIndex), - }, - PubKey: key, - }, nil -} - -func (m *walletKitClient) DeriveKey(ctx context.Context, in *keychain.KeyLocator) ( - *keychain.KeyDescriptor, error) { - - rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - rpcCtx = m.walletKitMac.WithMacaroonAuth(rpcCtx) - resp, err := m.client.DeriveKey(rpcCtx, &signrpc.KeyLocator{ - KeyFamily: int32(in.Family), - KeyIndex: int32(in.Index), - }) - if err != nil { - return nil, err - } - - key, err := btcec.ParsePubKey(resp.RawKeyBytes, btcec.S256()) - if err != nil { - return nil, err - } - - return &keychain.KeyDescriptor{ - KeyLocator: *in, - PubKey: key, - }, nil -} - -func (m *walletKitClient) NextAddr(ctx context.Context) ( - btcutil.Address, error) { - - rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - rpcCtx = m.walletKitMac.WithMacaroonAuth(rpcCtx) - resp, err := m.client.NextAddr(rpcCtx, &walletrpc.AddrRequest{}) - if err != nil { - return nil, err - } - - addr, err := btcutil.DecodeAddress(resp.Addr, nil) - if err != nil { - return nil, err - } - - return addr, nil -} - -func (m *walletKitClient) PublishTransaction(ctx context.Context, - tx *wire.MsgTx) error { - - txHex, err := swap.EncodeTx(tx) - if err != nil { - return err - } - - rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - rpcCtx = m.walletKitMac.WithMacaroonAuth(rpcCtx) - _, err = m.client.PublishTransaction(rpcCtx, &walletrpc.Transaction{ - TxHex: txHex, - }) - - return err -} - -func (m *walletKitClient) SendOutputs(ctx context.Context, - outputs []*wire.TxOut, feeRate chainfee.SatPerKWeight) ( - *wire.MsgTx, error) { - - rpcOutputs := make([]*signrpc.TxOut, len(outputs)) - for i, output := range outputs { - rpcOutputs[i] = &signrpc.TxOut{ - PkScript: output.PkScript, - Value: output.Value, - } - } - - rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - rpcCtx = m.walletKitMac.WithMacaroonAuth(rpcCtx) - resp, err := m.client.SendOutputs(rpcCtx, &walletrpc.SendOutputsRequest{ - Outputs: rpcOutputs, - SatPerKw: int64(feeRate), - }) - if err != nil { - return nil, err - } - - tx, err := swap.DecodeTx(resp.RawTx) - if err != nil { - return nil, err - } - - return tx, nil -} - -func (m *walletKitClient) EstimateFee(ctx context.Context, confTarget int32) ( - chainfee.SatPerKWeight, error) { - - rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - rpcCtx = m.walletKitMac.WithMacaroonAuth(rpcCtx) - resp, err := m.client.EstimateFee(rpcCtx, &walletrpc.EstimateFeeRequest{ - ConfTarget: int32(confTarget), - }) - if err != nil { - return 0, err - } - - return chainfee.SatPerKWeight(resp.SatPerKw), nil -} - -// ListSweeps returns a list of sweep transaction ids known to our node. -// Note that this function only looks up transaction ids (Verbose=false), and -// does not query our wallet for the full set of transactions. -func (m *walletKitClient) ListSweeps(ctx context.Context) ([]string, error) { - rpcCtx, cancel := context.WithTimeout(ctx, rpcTimeout) - defer cancel() - - resp, err := m.client.ListSweeps( - m.walletKitMac.WithMacaroonAuth(rpcCtx), - &walletrpc.ListSweepsRequest{ - Verbose: false, - }, - ) - if err != nil { - return nil, err - } - - // Since we have requested the abbreviated response from lnd, we can - // just get our response to a list of sweeps and return it. - sweeps := resp.GetTransactionIds() - return sweeps.TransactionIds, nil -} diff --git a/loopd/daemon.go b/loopd/daemon.go index 6b23078..224148c 100644 --- a/loopd/daemon.go +++ b/loopd/daemon.go @@ -10,8 +10,8 @@ import ( "sync/atomic" proxy "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop" - "github.com/lightninglabs/loop/lndclient" "github.com/lightninglabs/loop/looprpc" "github.com/lightningnetwork/lnd/lntypes" "google.golang.org/grpc" @@ -36,7 +36,8 @@ type listenerCfg struct { restListener func() (net.Listener, error) // getLnd returns a grpc connection to an lnd instance. - getLnd func(string, *lndConfig) (*lndclient.GrpcLndServices, error) + getLnd func(lndclient.Network, *lndConfig) (*lndclient.GrpcLndServices, + error) } // Daemon is the struct that holds one instance of the loop client daemon. @@ -106,8 +107,10 @@ func (d *Daemon) Start() error { return errOnlyStartOnce } + network := lndclient.Network(d.cfg.Network) + var err error - d.lnd, err = d.listenerCfg.getLnd(d.cfg.Network, d.cfg.Lnd) + d.lnd, err = d.listenerCfg.getLnd(network, d.cfg.Lnd) if err != nil { return err } diff --git a/loopd/log.go b/loopd/log.go index dd1811a..2abc58e 100644 --- a/loopd/log.go +++ b/loopd/log.go @@ -2,8 +2,8 @@ package loopd import ( "github.com/btcsuite/btclog" + "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop" - "github.com/lightninglabs/loop/lndclient" "github.com/lightninglabs/loop/loopdb" "github.com/lightninglabs/loop/lsat" "github.com/lightningnetwork/lnd/build" diff --git a/loopd/run.go b/loopd/run.go index 650bdaa..35647a6 100644 --- a/loopd/run.go +++ b/loopd/run.go @@ -9,8 +9,8 @@ import ( "strings" "github.com/jessevdk/go-flags" + "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop" - "github.com/lightninglabs/loop/lndclient" "github.com/lightningnetwork/lnd/build" "github.com/lightningnetwork/lnd/lnrpc/verrpc" "github.com/lightningnetwork/lnd/signal" @@ -67,7 +67,7 @@ func newListenerCfg(config *Config, rpcCfg RPCConfig) *listenerCfg { return net.Listen("tcp", config.RESTListen) }, - getLnd: func(network string, cfg *lndConfig) ( + getLnd: func(network lndclient.Network, cfg *lndConfig) ( *lndclient.GrpcLndServices, error) { svcCfg := &lndclient.LndServicesConfig{ diff --git a/loopd/swapclient_server.go b/loopd/swapclient_server.go index 542843b..2973c6f 100644 --- a/loopd/swapclient_server.go +++ b/loopd/swapclient_server.go @@ -12,8 +12,8 @@ import ( "github.com/lightningnetwork/lnd/queue" "github.com/lightningnetwork/lnd/routing/route" + "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop" - "github.com/lightninglabs/loop/lndclient" "github.com/lightninglabs/loop/loopdb" "github.com/lightninglabs/loop/swap" diff --git a/loopd/utils.go b/loopd/utils.go index a518a17..2df499e 100644 --- a/loopd/utils.go +++ b/loopd/utils.go @@ -5,8 +5,8 @@ import ( "path/filepath" "github.com/btcsuite/btcutil" + "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop" - "github.com/lightninglabs/loop/lndclient" ) // getClient returns an instance of the swap client. diff --git a/loopd/view.go b/loopd/view.go index ff5084e..a0e7a4f 100644 --- a/loopd/view.go +++ b/loopd/view.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/btcsuite/btcd/chaincfg" + "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop" "github.com/lightninglabs/loop/loopdb" "github.com/lightninglabs/loop/swap" @@ -11,12 +12,9 @@ import ( // view prints all swaps currently in the database. func view(config *Config, lisCfg *listenerCfg) error { - chainParams, err := swap.ChainParamsFromNetwork(config.Network) - if err != nil { - return err - } + network := lndclient.Network(config.Network) - lnd, err := lisCfg.getLnd(config.Network, config.Lnd) + lnd, err := lisCfg.getLnd(network, config.Lnd) if err != nil { return err } @@ -28,6 +26,11 @@ func view(config *Config, lisCfg *listenerCfg) error { } defer cleanup() + chainParams, err := network.ChainParams() + if err != nil { + return err + } + if err := viewOut(swapClient, chainParams); err != nil { return err } diff --git a/loopin.go b/loopin.go index b72359c..ab82c47 100644 --- a/loopin.go +++ b/loopin.go @@ -16,7 +16,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" - "github.com/lightninglabs/loop/lndclient" + "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop/loopdb" "github.com/lightninglabs/loop/swap" "github.com/lightningnetwork/lnd/lntypes" diff --git a/loopin_test.go b/loopin_test.go index 0af35fb..9765d76 100644 --- a/loopin_test.go +++ b/loopin_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/lightninglabs/loop/lndclient" + "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop/loopdb" "github.com/lightninglabs/loop/swap" "github.com/lightninglabs/loop/test" diff --git a/loopout.go b/loopout.go index 4531778..4418f28 100644 --- a/loopout.go +++ b/loopout.go @@ -10,7 +10,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" - "github.com/lightninglabs/loop/lndclient" + "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop/loopdb" "github.com/lightninglabs/loop/swap" "github.com/lightninglabs/loop/sweep" diff --git a/loopout_test.go b/loopout_test.go index 67a0da4..fbbddfe 100644 --- a/loopout_test.go +++ b/loopout_test.go @@ -10,7 +10,7 @@ import ( "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" - "github.com/lightninglabs/loop/lndclient" + "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop/loopdb" "github.com/lightninglabs/loop/sweep" "github.com/lightninglabs/loop/test" diff --git a/lsat/interceptor.go b/lsat/interceptor.go index c121211..7912738 100644 --- a/lsat/interceptor.go +++ b/lsat/interceptor.go @@ -9,7 +9,7 @@ import ( "time" "github.com/btcsuite/btcutil" - "github.com/lightninglabs/loop/lndclient" + "github.com/lightninglabs/lndclient" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/macaroons" diff --git a/lsat/interceptor_test.go b/lsat/interceptor_test.go index 24764d8..0e28f41 100644 --- a/lsat/interceptor_test.go +++ b/lsat/interceptor_test.go @@ -9,7 +9,7 @@ import ( "testing" "time" - "github.com/lightninglabs/loop/lndclient" + "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop/test" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lntypes" diff --git a/swap.go b/swap.go index 4ed5339..fd5dc54 100644 --- a/swap.go +++ b/swap.go @@ -4,7 +4,7 @@ import ( "context" "time" - "github.com/lightninglabs/loop/lndclient" + "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop/loopdb" "github.com/lightninglabs/loop/swap" "github.com/lightningnetwork/lnd/lntypes" diff --git a/swap/net.go b/swap/net.go deleted file mode 100644 index 581c76d..0000000 --- a/swap/net.go +++ /dev/null @@ -1,27 +0,0 @@ -package swap - -import ( - "errors" - - "github.com/btcsuite/btcd/chaincfg" -) - -// ChainParamsFromNetwork returns chain parameters based on a network name. -func ChainParamsFromNetwork(network string) (*chaincfg.Params, error) { - switch network { - case "mainnet": - return &chaincfg.MainNetParams, nil - - case "testnet": - return &chaincfg.TestNet3Params, nil - - case "regtest": - return &chaincfg.RegressionNetParams, nil - - case "simnet": - return &chaincfg.SimNetParams, nil - - default: - return nil, errors.New("unknown network") - } -} diff --git a/swap/tx.go b/swap/tx.go index 2212c05..653fcdf 100644 --- a/swap/tx.go +++ b/swap/tx.go @@ -9,30 +9,6 @@ import ( "github.com/btcsuite/btcutil" ) -// EncodeTx encodes a tx to raw bytes. -func EncodeTx(tx *wire.MsgTx) ([]byte, error) { - var buffer bytes.Buffer - err := tx.BtcEncode(&buffer, 0, wire.WitnessEncoding) - if err != nil { - return nil, err - } - rawTx := buffer.Bytes() - - return rawTx, nil -} - -// DecodeTx decodes raw tx bytes. -func DecodeTx(rawTx []byte) (*wire.MsgTx, error) { - tx := wire.MsgTx{} - r := bytes.NewReader(rawTx) - err := tx.BtcDecode(r, 0, wire.WitnessEncoding) - if err != nil { - return nil, err - } - - return &tx, nil -} - // GetScriptOutput locates the given script in the outputs of a transaction and // returns its outpoint and value. func GetScriptOutput(htlcTx *wire.MsgTx, scriptHash []byte) ( diff --git a/sweep/sweeper.go b/sweep/sweeper.go index 038ba9f..44e4b12 100644 --- a/sweep/sweeper.go +++ b/sweep/sweeper.go @@ -8,7 +8,7 @@ import ( "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" - "github.com/lightninglabs/loop/lndclient" + "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop/swap" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" diff --git a/test/context.go b/test/context.go index fbb8850..81740c4 100644 --- a/test/context.go +++ b/test/context.go @@ -7,7 +7,7 @@ import ( "time" "github.com/btcsuite/btcd/wire" - "github.com/lightninglabs/loop/lndclient" + "github.com/lightninglabs/lndclient" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lntypes" diff --git a/test/invoices_mock.go b/test/invoices_mock.go index d8cf41f..fc00c9b 100644 --- a/test/invoices_mock.go +++ b/test/invoices_mock.go @@ -7,7 +7,7 @@ import ( "time" "github.com/btcsuite/btcd/btcec" - "github.com/lightninglabs/loop/lndclient" + "github.com/lightninglabs/lndclient" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/zpay32" diff --git a/test/lightning_client_mock.go b/test/lightning_client_mock.go index a6702e7..36ff92b 100644 --- a/test/lightning_client_mock.go +++ b/test/lightning_client_mock.go @@ -10,7 +10,7 @@ import ( "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" - "github.com/lightninglabs/loop/lndclient" + "github.com/lightninglabs/lndclient" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" "github.com/lightningnetwork/lnd/lntypes" diff --git a/test/lnd_services_mock.go b/test/lnd_services_mock.go index 1e313dd..acda454 100644 --- a/test/lnd_services_mock.go +++ b/test/lnd_services_mock.go @@ -8,7 +8,7 @@ import ( "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/wire" - "github.com/lightninglabs/loop/lndclient" + "github.com/lightninglabs/lndclient" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lntypes" diff --git a/test/router_mock.go b/test/router_mock.go index 706e0af..f7fbbaa 100644 --- a/test/router_mock.go +++ b/test/router_mock.go @@ -1,7 +1,7 @@ package test import ( - "github.com/lightninglabs/loop/lndclient" + "github.com/lightninglabs/lndclient" "github.com/lightningnetwork/lnd/lntypes" "golang.org/x/net/context" ) diff --git a/test/versioner_mock.go b/test/versioner_mock.go index 12ef6ae..7722e03 100644 --- a/test/versioner_mock.go +++ b/test/versioner_mock.go @@ -3,7 +3,7 @@ package test import ( "context" - "github.com/lightninglabs/loop/lndclient" + "github.com/lightninglabs/lndclient" "github.com/lightningnetwork/lnd/lnrpc/verrpc" ) diff --git a/test/walletkit_mock.go b/test/walletkit_mock.go index fcd59c9..45f2e09 100644 --- a/test/walletkit_mock.go +++ b/test/walletkit_mock.go @@ -10,7 +10,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" "github.com/btcsuite/btcwallet/wtxmgr" - "github.com/lightninglabs/loop/lndclient" + "github.com/lightninglabs/lndclient" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee" diff --git a/testcontext_test.go b/testcontext_test.go index 5bdaf2f..7bec18c 100644 --- a/testcontext_test.go +++ b/testcontext_test.go @@ -7,7 +7,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" - "github.com/lightninglabs/loop/lndclient" + "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop/loopdb" "github.com/lightninglabs/loop/swap" "github.com/lightninglabs/loop/sweep"