2019-01-24 14:28:25 +01:00
|
|
|
package lnd
|
2015-12-30 00:09:38 +01:00
|
|
|
|
|
|
|
import (
|
2018-03-28 06:51:04 +02:00
|
|
|
"bytes"
|
2019-09-29 00:46:21 +02:00
|
|
|
"context"
|
2016-06-21 20:52:09 +02:00
|
|
|
"encoding/hex"
|
2017-01-25 02:12:51 +01:00
|
|
|
"errors"
|
2015-12-31 06:40:41 +01:00
|
|
|
"fmt"
|
2016-07-13 02:46:25 +02:00
|
|
|
"io"
|
2016-12-27 06:51:47 +01:00
|
|
|
"math"
|
2021-12-06 05:24:06 +01:00
|
|
|
"net"
|
lnd+rpc: modify rpcServer to fully manaage listeners and gRPC, handle sub-servers
In this commit, we modify the existing rpcServer to fully manage the
macaroons, gRPC server, and also seek out and create all sub-servers.
With this change, the RPC server gains more responsibility, as it
becomes the "root" server in the hierarchy of gRPC sub-servers.
In addition to creating each sub-server, it will also merge the set of
macaroon permissions for each sub-server, with the permissions of the
rest of the RPC infra. As a result, each sub-server is able to
independently specify what it needs w.r.t macaroon permissions and have
that taken care of by the RPC server. In order to achieve this, we need
to unify the creation of the RPC interceptors, and also fully manage the
gRPC server ourselves.
Some examples with various build configs:
```
⛰i make build
Building debug lnd and lncli.
go build -v -tags="dev" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
unknown flag `signrpc.signermacaroonpath'
unknown flag `signrpc.signermacaroonpath'
⛰i make build tags=signerrpc
Building debug lnd and lncli.
go build -v -tags="dev signerrpc" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev signerrpc" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
2018-10-22 17:31:01.132 [INF] LTND: Version: 0.5.0-beta commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty, build=development, logging=default
2018-10-22 17:31:01.133 [INF] LTND: Active chain: Bitcoin (network=simnet)
2018-10-22 17:31:01.140 [INF] CHDB: Checking for schema update: latest_version=6, db_version=6
2018-10-22 17:31:01.236 [INF] LTND: Primary chain is set to: bitcoin
2018-10-22 17:31:02.391 [INF] LNWL: Opened wallet
2018-10-22 17:31:03.315 [INF] LNWL: The wallet has been unlocked without a time limit
2018-10-22 17:31:03.315 [INF] LTND: LightningWallet opened
2018-10-22 17:31:03.319 [INF] LNWL: Catching up block hashes to height 3060, this will take a while...
2018-10-22 17:31:03.320 [INF] HSWC: Restoring in-memory circuit state from disk
2018-10-22 17:31:03.320 [INF] LNWL: Done catching up block hashes
2018-10-22 17:31:03.320 [INF] HSWC: Payment circuits loaded: num_pending=0, num_open=0
2018-10-22 17:31:03.322 [DBG] LTND: Populating dependencies for sub RPC server: Signrpc
```
As for the config, an example is:
```
[signrpc]
signrpc.signermacaroonpath=~/signer.macaroon
```
2018-10-23 03:03:07 +02:00
|
|
|
"net/http"
|
2023-11-16 04:19:24 +01:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2020-03-24 17:28:55 +01:00
|
|
|
"runtime"
|
2018-06-15 02:14:31 +02:00
|
|
|
"sort"
|
2020-07-23 18:36:42 +02:00
|
|
|
"strconv"
|
2017-01-15 03:12:20 +01:00
|
|
|
"strings"
|
2018-07-01 00:13:14 +02:00
|
|
|
"sync"
|
2016-01-17 04:12:36 +01:00
|
|
|
"sync/atomic"
|
2018-07-28 03:39:38 +02:00
|
|
|
"time"
|
2015-12-30 00:09:38 +01:00
|
|
|
|
2018-07-31 09:17:17 +02:00
|
|
|
"github.com/btcsuite/btcd/blockchain"
|
2022-02-23 14:48:00 +01:00
|
|
|
"github.com/btcsuite/btcd/btcec/v2"
|
|
|
|
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
|
|
|
|
"github.com/btcsuite/btcd/btcutil"
|
|
|
|
"github.com/btcsuite/btcd/btcutil/psbt"
|
2020-03-31 09:13:17 +02:00
|
|
|
"github.com/btcsuite/btcd/chaincfg"
|
2018-07-31 09:17:17 +02:00
|
|
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
|
|
"github.com/btcsuite/btcd/txscript"
|
|
|
|
"github.com/btcsuite/btcd/wire"
|
2021-02-20 02:42:16 +01:00
|
|
|
"github.com/btcsuite/btcwallet/waddrmgr"
|
2024-03-26 18:08:27 +01:00
|
|
|
"github.com/btcsuite/btcwallet/wallet"
|
2019-03-05 14:22:30 +01:00
|
|
|
"github.com/btcsuite/btcwallet/wallet/txauthor"
|
2016-09-21 02:15:26 +02:00
|
|
|
"github.com/davecgh/go-spew/spew"
|
2021-07-27 12:59:59 +02:00
|
|
|
proxy "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
2018-12-13 12:26:30 +01:00
|
|
|
"github.com/lightningnetwork/lnd/autopilot"
|
2018-09-20 12:26:58 +02:00
|
|
|
"github.com/lightningnetwork/lnd/build"
|
2020-09-29 17:56:18 +02:00
|
|
|
"github.com/lightningnetwork/lnd/chainreg"
|
2019-10-10 15:51:09 +02:00
|
|
|
"github.com/lightningnetwork/lnd/chanacceptor"
|
2018-12-10 05:12:24 +01:00
|
|
|
"github.com/lightningnetwork/lnd/chanbackup"
|
2019-12-17 16:36:27 +01:00
|
|
|
"github.com/lightningnetwork/lnd/chanfitness"
|
2016-09-19 21:04:56 +02:00
|
|
|
"github.com/lightningnetwork/lnd/channeldb"
|
2024-06-26 04:58:57 +02:00
|
|
|
"github.com/lightningnetwork/lnd/channeldb/graphsession"
|
2023-11-08 10:18:45 +01:00
|
|
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
2019-01-23 03:28:35 +01:00
|
|
|
"github.com/lightningnetwork/lnd/channelnotifier"
|
2019-10-30 10:31:01 +01:00
|
|
|
"github.com/lightningnetwork/lnd/contractcourt"
|
2019-03-23 03:57:03 +01:00
|
|
|
"github.com/lightningnetwork/lnd/discovery"
|
2019-12-10 22:09:52 +01:00
|
|
|
"github.com/lightningnetwork/lnd/feature"
|
2024-05-14 00:21:26 +02:00
|
|
|
"github.com/lightningnetwork/lnd/fn"
|
2020-11-17 00:28:56 +01:00
|
|
|
"github.com/lightningnetwork/lnd/funding"
|
2024-06-17 01:30:01 +02:00
|
|
|
"github.com/lightningnetwork/lnd/graph"
|
2017-05-02 22:04:58 +02:00
|
|
|
"github.com/lightningnetwork/lnd/htlcswitch"
|
2020-10-14 17:44:18 +02:00
|
|
|
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
2019-01-16 15:47:43 +01:00
|
|
|
"github.com/lightningnetwork/lnd/input"
|
2018-12-20 11:57:44 +01:00
|
|
|
"github.com/lightningnetwork/lnd/invoices"
|
2019-11-14 05:58:54 +01:00
|
|
|
"github.com/lightningnetwork/lnd/keychain"
|
2021-04-26 19:08:11 +02:00
|
|
|
"github.com/lightningnetwork/lnd/kvdb"
|
2020-05-18 14:13:24 +02:00
|
|
|
"github.com/lightningnetwork/lnd/labels"
|
lnd+rpc: modify rpcServer to fully manaage listeners and gRPC, handle sub-servers
In this commit, we modify the existing rpcServer to fully manage the
macaroons, gRPC server, and also seek out and create all sub-servers.
With this change, the RPC server gains more responsibility, as it
becomes the "root" server in the hierarchy of gRPC sub-servers.
In addition to creating each sub-server, it will also merge the set of
macaroon permissions for each sub-server, with the permissions of the
rest of the RPC infra. As a result, each sub-server is able to
independently specify what it needs w.r.t macaroon permissions and have
that taken care of by the RPC server. In order to achieve this, we need
to unify the creation of the RPC interceptors, and also fully manage the
gRPC server ourselves.
Some examples with various build configs:
```
⛰i make build
Building debug lnd and lncli.
go build -v -tags="dev" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
unknown flag `signrpc.signermacaroonpath'
unknown flag `signrpc.signermacaroonpath'
⛰i make build tags=signerrpc
Building debug lnd and lncli.
go build -v -tags="dev signerrpc" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev signerrpc" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
2018-10-22 17:31:01.132 [INF] LTND: Version: 0.5.0-beta commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty, build=development, logging=default
2018-10-22 17:31:01.133 [INF] LTND: Active chain: Bitcoin (network=simnet)
2018-10-22 17:31:01.140 [INF] CHDB: Checking for schema update: latest_version=6, db_version=6
2018-10-22 17:31:01.236 [INF] LTND: Primary chain is set to: bitcoin
2018-10-22 17:31:02.391 [INF] LNWL: Opened wallet
2018-10-22 17:31:03.315 [INF] LNWL: The wallet has been unlocked without a time limit
2018-10-22 17:31:03.315 [INF] LTND: LightningWallet opened
2018-10-22 17:31:03.319 [INF] LNWL: Catching up block hashes to height 3060, this will take a while...
2018-10-22 17:31:03.320 [INF] HSWC: Restoring in-memory circuit state from disk
2018-10-22 17:31:03.320 [INF] LNWL: Done catching up block hashes
2018-10-22 17:31:03.320 [INF] HSWC: Payment circuits loaded: num_pending=0, num_open=0
2018-10-22 17:31:03.322 [DBG] LTND: Populating dependencies for sub RPC server: Signrpc
```
As for the config, an example is:
```
[signrpc]
signrpc.signermacaroonpath=~/signer.macaroon
```
2018-10-23 03:03:07 +02:00
|
|
|
"github.com/lightningnetwork/lnd/lncfg"
|
2016-01-16 19:38:48 +01:00
|
|
|
"github.com/lightningnetwork/lnd/lnrpc"
|
2019-01-03 19:13:08 +01:00
|
|
|
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
|
2019-10-10 15:51:09 +02:00
|
|
|
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
2021-08-24 11:21:55 +02:00
|
|
|
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
|
2019-01-15 10:06:48 +01:00
|
|
|
"github.com/lightningnetwork/lnd/lntypes"
|
2024-07-24 13:31:21 +02:00
|
|
|
"github.com/lightningnetwork/lnd/lnutils"
|
2016-08-13 00:53:18 +02:00
|
|
|
"github.com/lightningnetwork/lnd/lnwallet"
|
2020-05-05 21:10:06 +02:00
|
|
|
"github.com/lightningnetwork/lnd/lnwallet/btcwallet"
|
2019-10-31 03:43:05 +01:00
|
|
|
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
2020-11-09 08:34:51 +01:00
|
|
|
"github.com/lightningnetwork/lnd/lnwallet/chancloser"
|
2019-11-14 05:58:54 +01:00
|
|
|
"github.com/lightningnetwork/lnd/lnwallet/chanfunding"
|
2016-07-13 02:46:25 +02:00
|
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
lnd+rpc: modify rpcServer to fully manaage listeners and gRPC, handle sub-servers
In this commit, we modify the existing rpcServer to fully manage the
macaroons, gRPC server, and also seek out and create all sub-servers.
With this change, the RPC server gains more responsibility, as it
becomes the "root" server in the hierarchy of gRPC sub-servers.
In addition to creating each sub-server, it will also merge the set of
macaroon permissions for each sub-server, with the permissions of the
rest of the RPC infra. As a result, each sub-server is able to
independently specify what it needs w.r.t macaroon permissions and have
that taken care of by the RPC server. In order to achieve this, we need
to unify the creation of the RPC interceptors, and also fully manage the
gRPC server ourselves.
Some examples with various build configs:
```
⛰i make build
Building debug lnd and lncli.
go build -v -tags="dev" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
unknown flag `signrpc.signermacaroonpath'
unknown flag `signrpc.signermacaroonpath'
⛰i make build tags=signerrpc
Building debug lnd and lncli.
go build -v -tags="dev signerrpc" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev signerrpc" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
2018-10-22 17:31:01.132 [INF] LTND: Version: 0.5.0-beta commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty, build=development, logging=default
2018-10-22 17:31:01.133 [INF] LTND: Active chain: Bitcoin (network=simnet)
2018-10-22 17:31:01.140 [INF] CHDB: Checking for schema update: latest_version=6, db_version=6
2018-10-22 17:31:01.236 [INF] LTND: Primary chain is set to: bitcoin
2018-10-22 17:31:02.391 [INF] LNWL: Opened wallet
2018-10-22 17:31:03.315 [INF] LNWL: The wallet has been unlocked without a time limit
2018-10-22 17:31:03.315 [INF] LTND: LightningWallet opened
2018-10-22 17:31:03.319 [INF] LNWL: Catching up block hashes to height 3060, this will take a while...
2018-10-22 17:31:03.320 [INF] HSWC: Restoring in-memory circuit state from disk
2018-10-22 17:31:03.320 [INF] LNWL: Done catching up block hashes
2018-10-22 17:31:03.320 [INF] HSWC: Payment circuits loaded: num_pending=0, num_open=0
2018-10-22 17:31:03.322 [DBG] LTND: Populating dependencies for sub RPC server: Signrpc
```
As for the config, an example is:
```
[signrpc]
signrpc.signermacaroonpath=~/signer.macaroon
```
2018-10-23 03:03:07 +02:00
|
|
|
"github.com/lightningnetwork/lnd/macaroons"
|
2020-07-02 23:46:06 +02:00
|
|
|
"github.com/lightningnetwork/lnd/peer"
|
2019-08-12 19:13:12 +02:00
|
|
|
"github.com/lightningnetwork/lnd/peernotifier"
|
2019-12-11 10:52:27 +01:00
|
|
|
"github.com/lightningnetwork/lnd/record"
|
2016-12-27 06:50:19 +01:00
|
|
|
"github.com/lightningnetwork/lnd/routing"
|
2019-10-10 15:51:09 +02:00
|
|
|
"github.com/lightningnetwork/lnd/routing/route"
|
2020-10-13 11:24:40 +02:00
|
|
|
"github.com/lightningnetwork/lnd/rpcperms"
|
2018-06-15 05:19:45 +02:00
|
|
|
"github.com/lightningnetwork/lnd/signal"
|
2018-11-18 05:44:34 +01:00
|
|
|
"github.com/lightningnetwork/lnd/sweep"
|
2022-10-25 15:34:28 +02:00
|
|
|
"github.com/lightningnetwork/lnd/tlv"
|
2019-10-10 15:51:09 +02:00
|
|
|
"github.com/lightningnetwork/lnd/watchtower"
|
2017-01-03 00:36:15 +01:00
|
|
|
"github.com/lightningnetwork/lnd/zpay32"
|
2017-04-20 04:28:10 +02:00
|
|
|
"github.com/tv42/zbase32"
|
lnd+rpc: modify rpcServer to fully manaage listeners and gRPC, handle sub-servers
In this commit, we modify the existing rpcServer to fully manage the
macaroons, gRPC server, and also seek out and create all sub-servers.
With this change, the RPC server gains more responsibility, as it
becomes the "root" server in the hierarchy of gRPC sub-servers.
In addition to creating each sub-server, it will also merge the set of
macaroon permissions for each sub-server, with the permissions of the
rest of the RPC infra. As a result, each sub-server is able to
independently specify what it needs w.r.t macaroon permissions and have
that taken care of by the RPC server. In order to achieve this, we need
to unify the creation of the RPC interceptors, and also fully manage the
gRPC server ourselves.
Some examples with various build configs:
```
⛰i make build
Building debug lnd and lncli.
go build -v -tags="dev" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
unknown flag `signrpc.signermacaroonpath'
unknown flag `signrpc.signermacaroonpath'
⛰i make build tags=signerrpc
Building debug lnd and lncli.
go build -v -tags="dev signerrpc" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev signerrpc" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
2018-10-22 17:31:01.132 [INF] LTND: Version: 0.5.0-beta commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty, build=development, logging=default
2018-10-22 17:31:01.133 [INF] LTND: Active chain: Bitcoin (network=simnet)
2018-10-22 17:31:01.140 [INF] CHDB: Checking for schema update: latest_version=6, db_version=6
2018-10-22 17:31:01.236 [INF] LTND: Primary chain is set to: bitcoin
2018-10-22 17:31:02.391 [INF] LNWL: Opened wallet
2018-10-22 17:31:03.315 [INF] LNWL: The wallet has been unlocked without a time limit
2018-10-22 17:31:03.315 [INF] LTND: LightningWallet opened
2018-10-22 17:31:03.319 [INF] LNWL: Catching up block hashes to height 3060, this will take a while...
2018-10-22 17:31:03.320 [INF] HSWC: Restoring in-memory circuit state from disk
2018-10-22 17:31:03.320 [INF] LNWL: Done catching up block hashes
2018-10-22 17:31:03.320 [INF] HSWC: Payment circuits loaded: num_pending=0, num_open=0
2018-10-22 17:31:03.322 [DBG] LTND: Populating dependencies for sub RPC server: Signrpc
```
As for the config, an example is:
```
[signrpc]
signrpc.signermacaroonpath=~/signer.macaroon
```
2018-10-23 03:03:07 +02:00
|
|
|
"google.golang.org/grpc"
|
2021-01-14 13:59:56 +01:00
|
|
|
"google.golang.org/grpc/codes"
|
|
|
|
"google.golang.org/grpc/status"
|
2018-07-28 03:39:38 +02:00
|
|
|
"gopkg.in/macaroon-bakery.v2/bakery"
|
2015-12-30 00:09:38 +01:00
|
|
|
)
|
|
|
|
|
2024-04-25 20:34:25 +02:00
|
|
|
const (
|
|
|
|
// defaultNumBlocksEstimate is the number of blocks that we fall back
|
|
|
|
// to issuing an estimate for if a fee pre fence doesn't specify an
|
|
|
|
// explicit conf target or fee rate.
|
|
|
|
defaultNumBlocksEstimate = 6
|
|
|
|
)
|
|
|
|
|
2015-12-30 00:09:38 +01:00
|
|
|
var (
|
2018-01-16 17:18:41 +01:00
|
|
|
// readPermissions is a slice of all entities that allow read
|
|
|
|
// permissions for authorization purposes, all lowercase.
|
|
|
|
readPermissions = []bakery.Op{
|
|
|
|
{
|
|
|
|
Entity: "onchain",
|
|
|
|
Action: "read",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "read",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Entity: "address",
|
|
|
|
Action: "read",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Entity: "message",
|
|
|
|
Action: "read",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Entity: "peers",
|
|
|
|
Action: "read",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Entity: "info",
|
|
|
|
Action: "read",
|
|
|
|
},
|
2018-03-21 00:42:15 +01:00
|
|
|
{
|
|
|
|
Entity: "invoices",
|
|
|
|
Action: "read",
|
|
|
|
},
|
2019-12-11 09:54:49 +01:00
|
|
|
{
|
|
|
|
Entity: "signer",
|
|
|
|
Action: "read",
|
|
|
|
},
|
2020-07-23 18:36:42 +02:00
|
|
|
{
|
|
|
|
Entity: "macaroon",
|
|
|
|
Action: "read",
|
|
|
|
},
|
2018-01-16 17:18:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// writePermissions is a slice of all entities that allow write
|
|
|
|
// permissions for authorization purposes, all lowercase.
|
|
|
|
writePermissions = []bakery.Op{
|
|
|
|
{
|
|
|
|
Entity: "onchain",
|
|
|
|
Action: "write",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "write",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Entity: "address",
|
|
|
|
Action: "write",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Entity: "message",
|
|
|
|
Action: "write",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Entity: "peers",
|
|
|
|
Action: "write",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Entity: "info",
|
|
|
|
Action: "write",
|
|
|
|
},
|
2018-03-21 00:42:15 +01:00
|
|
|
{
|
|
|
|
Entity: "invoices",
|
|
|
|
Action: "write",
|
|
|
|
},
|
2018-12-07 01:22:16 +01:00
|
|
|
{
|
|
|
|
Entity: "signer",
|
|
|
|
Action: "generate",
|
|
|
|
},
|
2019-10-23 13:28:17 +02:00
|
|
|
{
|
|
|
|
Entity: "macaroon",
|
|
|
|
Action: "generate",
|
|
|
|
},
|
2020-07-23 18:36:42 +02:00
|
|
|
{
|
|
|
|
Entity: "macaroon",
|
|
|
|
Action: "write",
|
|
|
|
},
|
2018-03-21 00:42:15 +01:00
|
|
|
}
|
|
|
|
|
2018-03-21 00:40:14 +01:00
|
|
|
// invoicePermissions is a slice of all the entities that allows a user
|
|
|
|
// to only access calls that are related to invoices, so: streaming
|
2018-04-26 23:06:29 +02:00
|
|
|
// RPCs, generating, and listening invoices.
|
2018-03-21 00:40:14 +01:00
|
|
|
invoicePermissions = []bakery.Op{
|
|
|
|
{
|
|
|
|
Entity: "invoices",
|
|
|
|
Action: "read",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Entity: "invoices",
|
|
|
|
Action: "write",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Entity: "address",
|
|
|
|
Action: "read",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Entity: "address",
|
|
|
|
Action: "write",
|
|
|
|
},
|
2018-12-12 17:28:09 +01:00
|
|
|
{
|
|
|
|
Entity: "onchain",
|
|
|
|
Action: "read",
|
|
|
|
},
|
2018-01-16 17:18:41 +01:00
|
|
|
}
|
2019-10-23 13:28:17 +02:00
|
|
|
|
|
|
|
// TODO(guggero): Refactor into constants that are used for all
|
|
|
|
// permissions in this file. Also expose the list of possible
|
|
|
|
// permissions in an RPC when per RPC permissions are
|
|
|
|
// implemented.
|
|
|
|
validActions = []string{"read", "write", "generate"}
|
|
|
|
validEntities = []string{
|
|
|
|
"onchain", "offchain", "address", "message",
|
|
|
|
"peers", "info", "invoices", "signer", "macaroon",
|
2020-09-04 09:22:36 +02:00
|
|
|
macaroons.PermissionEntityCustomURI,
|
2019-10-23 13:28:17 +02:00
|
|
|
}
|
2020-07-23 18:37:13 +02:00
|
|
|
|
|
|
|
// If the --no-macaroons flag is used to start lnd, the macaroon service
|
|
|
|
// is not initialized. errMacaroonDisabled is then returned when
|
|
|
|
// macaroon related services are used.
|
|
|
|
errMacaroonDisabled = fmt.Errorf("macaroon authentication disabled, " +
|
|
|
|
"remove --no-macaroons flag to enable")
|
2019-06-25 12:19:55 +02:00
|
|
|
)
|
2018-01-16 17:18:41 +01:00
|
|
|
|
2019-10-23 13:28:17 +02:00
|
|
|
// stringInSlice returns true if a string is contained in the given slice.
|
|
|
|
func stringInSlice(a string, slice []string) bool {
|
|
|
|
for _, b := range slice {
|
|
|
|
if b == a {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2021-09-22 21:32:02 +02:00
|
|
|
// GetAllPermissions returns all the permissions required to interact with lnd.
|
|
|
|
func GetAllPermissions() []bakery.Op {
|
|
|
|
allPerms := make([]bakery.Op, 0)
|
|
|
|
|
|
|
|
// The map will help keep track of which specific permission pairs have
|
|
|
|
// already been added to the slice.
|
|
|
|
allPermsMap := make(map[string]map[string]struct{})
|
|
|
|
|
|
|
|
for _, perms := range MainRPCServerPermissions() {
|
|
|
|
for _, perm := range perms {
|
|
|
|
entity := perm.Entity
|
|
|
|
action := perm.Action
|
|
|
|
|
|
|
|
// If this specific entity-action permission pair isn't
|
|
|
|
// in the map yet. Add it to map, and the permission
|
|
|
|
// slice.
|
|
|
|
if acts, ok := allPermsMap[entity]; ok {
|
|
|
|
if _, ok := acts[action]; !ok {
|
|
|
|
allPermsMap[entity][action] = struct{}{}
|
|
|
|
|
|
|
|
allPerms = append(
|
|
|
|
allPerms, perm,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
allPermsMap[entity] = make(map[string]struct{})
|
|
|
|
allPermsMap[entity][action] = struct{}{}
|
|
|
|
allPerms = append(allPerms, perm)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return allPerms
|
|
|
|
}
|
|
|
|
|
2020-09-10 10:25:38 +02:00
|
|
|
// MainRPCServerPermissions returns a mapping of the main RPC server calls to
|
2019-06-25 12:19:55 +02:00
|
|
|
// the permissions they require.
|
2020-09-10 10:25:38 +02:00
|
|
|
func MainRPCServerPermissions() map[string][]bakery.Op {
|
2019-06-25 12:19:55 +02:00
|
|
|
return map[string][]bakery.Op{
|
2018-01-16 17:18:41 +01:00
|
|
|
"/lnrpc.Lightning/SendCoins": {{
|
|
|
|
Entity: "onchain",
|
|
|
|
Action: "write",
|
|
|
|
}},
|
2018-09-27 15:49:44 +02:00
|
|
|
"/lnrpc.Lightning/ListUnspent": {{
|
|
|
|
Entity: "onchain",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
2018-01-16 17:18:41 +01:00
|
|
|
"/lnrpc.Lightning/SendMany": {{
|
|
|
|
Entity: "onchain",
|
|
|
|
Action: "write",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/NewAddress": {{
|
|
|
|
Entity: "address",
|
|
|
|
Action: "write",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/SignMessage": {{
|
|
|
|
Entity: "message",
|
|
|
|
Action: "write",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/VerifyMessage": {{
|
|
|
|
Entity: "message",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/ConnectPeer": {{
|
|
|
|
Entity: "peers",
|
|
|
|
Action: "write",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/DisconnectPeer": {{
|
|
|
|
Entity: "peers",
|
|
|
|
Action: "write",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/OpenChannel": {{
|
|
|
|
Entity: "onchain",
|
|
|
|
Action: "write",
|
|
|
|
}, {
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "write",
|
|
|
|
}},
|
2021-08-24 11:21:55 +02:00
|
|
|
"/lnrpc.Lightning/BatchOpenChannel": {{
|
|
|
|
Entity: "onchain",
|
|
|
|
Action: "write",
|
|
|
|
}, {
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "write",
|
|
|
|
}},
|
2018-02-12 02:15:31 +01:00
|
|
|
"/lnrpc.Lightning/OpenChannelSync": {{
|
2018-01-16 17:18:41 +01:00
|
|
|
Entity: "onchain",
|
|
|
|
Action: "write",
|
|
|
|
}, {
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "write",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/CloseChannel": {{
|
|
|
|
Entity: "onchain",
|
|
|
|
Action: "write",
|
|
|
|
}, {
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "write",
|
|
|
|
}},
|
2018-05-29 11:26:47 +02:00
|
|
|
"/lnrpc.Lightning/AbandonChannel": {{
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "write",
|
|
|
|
}},
|
2018-01-16 17:18:41 +01:00
|
|
|
"/lnrpc.Lightning/GetInfo": {{
|
|
|
|
Entity: "info",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
2023-11-16 04:19:24 +01:00
|
|
|
"/lnrpc.Lightning/GetDebugInfo": {{
|
|
|
|
Entity: "info",
|
|
|
|
Action: "read",
|
|
|
|
}, {
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "read",
|
|
|
|
}, {
|
|
|
|
Entity: "onchain",
|
|
|
|
Action: "read",
|
|
|
|
}, {
|
|
|
|
Entity: "peers",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
2020-06-09 08:47:17 +02:00
|
|
|
"/lnrpc.Lightning/GetRecoveryInfo": {{
|
|
|
|
Entity: "info",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
2018-01-16 17:18:41 +01:00
|
|
|
"/lnrpc.Lightning/ListPeers": {{
|
|
|
|
Entity: "peers",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/WalletBalance": {{
|
|
|
|
Entity: "onchain",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
2019-03-05 14:22:30 +01:00
|
|
|
"/lnrpc.Lightning/EstimateFee": {{
|
|
|
|
Entity: "onchain",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
2018-01-16 17:18:41 +01:00
|
|
|
"/lnrpc.Lightning/ChannelBalance": {{
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/PendingChannels": {{
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/ListChannels": {{
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
2019-01-23 03:28:35 +01:00
|
|
|
"/lnrpc.Lightning/SubscribeChannelEvents": {{
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
2018-05-24 11:35:34 +02:00
|
|
|
"/lnrpc.Lightning/ClosedChannels": {{
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
2018-01-16 17:18:41 +01:00
|
|
|
"/lnrpc.Lightning/SendPayment": {{
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "write",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/SendPaymentSync": {{
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "write",
|
|
|
|
}},
|
2018-05-01 10:17:55 +02:00
|
|
|
"/lnrpc.Lightning/SendToRoute": {{
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "write",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/SendToRouteSync": {{
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "write",
|
|
|
|
}},
|
2018-01-16 17:18:41 +01:00
|
|
|
"/lnrpc.Lightning/AddInvoice": {{
|
2018-03-21 00:44:53 +01:00
|
|
|
Entity: "invoices",
|
2018-01-16 17:18:41 +01:00
|
|
|
Action: "write",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/LookupInvoice": {{
|
2018-03-21 00:44:53 +01:00
|
|
|
Entity: "invoices",
|
2018-01-16 17:18:41 +01:00
|
|
|
Action: "read",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/ListInvoices": {{
|
2018-03-21 00:44:53 +01:00
|
|
|
Entity: "invoices",
|
2018-01-16 17:18:41 +01:00
|
|
|
Action: "read",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/SubscribeInvoices": {{
|
2018-03-21 00:44:53 +01:00
|
|
|
Entity: "invoices",
|
2018-01-16 17:18:41 +01:00
|
|
|
Action: "read",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/SubscribeTransactions": {{
|
|
|
|
Entity: "onchain",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/GetTransactions": {{
|
|
|
|
Entity: "onchain",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/DescribeGraph": {{
|
|
|
|
Entity: "info",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
2020-03-19 11:14:28 +01:00
|
|
|
"/lnrpc.Lightning/GetNodeMetrics": {{
|
|
|
|
Entity: "info",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
2018-01-16 17:18:41 +01:00
|
|
|
"/lnrpc.Lightning/GetChanInfo": {{
|
|
|
|
Entity: "info",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/GetNodeInfo": {{
|
|
|
|
Entity: "info",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/QueryRoutes": {{
|
|
|
|
Entity: "info",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/GetNetworkInfo": {{
|
|
|
|
Entity: "info",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/StopDaemon": {{
|
|
|
|
Entity: "info",
|
|
|
|
Action: "write",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/SubscribeChannelGraph": {{
|
|
|
|
Entity: "info",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/ListPayments": {{
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
2021-09-13 11:32:28 +02:00
|
|
|
"/lnrpc.Lightning/DeletePayment": {{
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "write",
|
|
|
|
}},
|
2018-01-16 17:18:41 +01:00
|
|
|
"/lnrpc.Lightning/DeleteAllPayments": {{
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "write",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/DebugLevel": {{
|
|
|
|
Entity: "info",
|
|
|
|
Action: "write",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/DecodePayReq": {{
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/FeeReport": {{
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/UpdateChannelPolicy": {{
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "write",
|
|
|
|
}},
|
2018-02-28 07:23:27 +01:00
|
|
|
"/lnrpc.Lightning/ForwardingHistory": {{
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
2018-12-10 05:12:24 +01:00
|
|
|
"/lnrpc.Lightning/RestoreChannelBackups": {{
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "write",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/ExportChannelBackup": {{
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
2019-03-11 01:54:49 +01:00
|
|
|
"/lnrpc.Lightning/VerifyChanBackup": {{
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
2018-12-10 05:12:24 +01:00
|
|
|
"/lnrpc.Lightning/ExportAllChannelBackups": {{
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/SubscribeChannelBackups": {{
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
2019-08-08 04:17:50 +02:00
|
|
|
"/lnrpc.Lightning/ChannelAcceptor": {{
|
|
|
|
Entity: "onchain",
|
|
|
|
Action: "write",
|
|
|
|
}, {
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "write",
|
|
|
|
}},
|
2019-10-23 13:28:17 +02:00
|
|
|
"/lnrpc.Lightning/BakeMacaroon": {{
|
|
|
|
Entity: "macaroon",
|
|
|
|
Action: "generate",
|
|
|
|
}},
|
2020-07-23 18:36:42 +02:00
|
|
|
"/lnrpc.Lightning/ListMacaroonIDs": {{
|
|
|
|
Entity: "macaroon",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
|
|
|
"/lnrpc.Lightning/DeleteMacaroonID": {{
|
|
|
|
Entity: "macaroon",
|
|
|
|
Action: "write",
|
|
|
|
}},
|
2020-09-04 09:22:38 +02:00
|
|
|
"/lnrpc.Lightning/ListPermissions": {{
|
|
|
|
Entity: "info",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
2021-05-17 08:19:30 +02:00
|
|
|
"/lnrpc.Lightning/CheckMacaroonPermissions": {{
|
|
|
|
Entity: "macaroon",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
2019-12-18 02:05:49 +01:00
|
|
|
"/lnrpc.Lightning/SubscribePeerEvents": {{
|
|
|
|
Entity: "peers",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
lnrpc: add ability to provide chan point shims for funding, new funding modifiers
In this commit, we start to expose some of the new external funding
functionality over the RPC interface.
First, we add a new `funding_shim` field to the regular `OpenChannel`
method. This can be used by a caller to express that certain parameters
of the funding flow have already been negotiated outside the protocol,
and should be used instead. For example, a shim can be provided to use a
particular key for the commitment key (ideally cold) rather than use one
this is generated by the wallet as normal, or signal that signing will
be carried out in an interactive manner (PSBT based).
Next, we add a brand new method: `FundingStateStep`. FundingStateStep is
an advanced funding related call that allows the caller to either
execute some preparatory steps for a funding workflow, or manually
progress a funding workflow. The primary way a funding flow is
identified is via its pending channel ID. As an example, this method can
be used to specify that we're expecting a funding flow for a particular
pending channel ID, for which we need to use specific parameters.
Alternatively, this can be used to interactively drive PSBT signing for
funding for partially complete funding transactions.
The new transition methods (funding state machine modifiers) in this
commit allow a party to register a funding intent that should be used
for a specified incoming pending channel ID. The "responder" to the
external channel flow should use this to prep lnd to be able to handle
the channel flow properly.
2019-11-14 05:54:34 +01:00
|
|
|
"/lnrpc.Lightning/FundingStateStep": {{
|
|
|
|
Entity: "onchain",
|
|
|
|
Action: "write",
|
|
|
|
}, {
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "write",
|
|
|
|
}},
|
2021-08-12 16:07:24 +02:00
|
|
|
lnrpc.RegisterRPCMiddlewareURI: {{
|
|
|
|
Entity: "macaroon",
|
|
|
|
Action: "write",
|
|
|
|
}},
|
2021-05-31 10:03:47 +02:00
|
|
|
"/lnrpc.Lightning/SendCustomMessage": {{
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "write",
|
|
|
|
}},
|
2021-05-31 12:06:48 +02:00
|
|
|
"/lnrpc.Lightning/SubscribeCustomMessages": {{
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
2023-02-28 08:35:36 +01:00
|
|
|
"/lnrpc.Lightning/LookupHtlcResolution": {{
|
2022-08-29 13:20:52 +02:00
|
|
|
Entity: "offchain",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
2022-07-14 19:51:48 +02:00
|
|
|
"/lnrpc.Lightning/ListAliases": {{
|
|
|
|
Entity: "offchain",
|
|
|
|
Action: "read",
|
|
|
|
}},
|
2017-08-18 03:50:57 +02:00
|
|
|
}
|
2019-06-25 12:19:55 +02:00
|
|
|
}
|
2015-12-30 00:09:38 +01:00
|
|
|
|
2016-06-21 20:52:09 +02:00
|
|
|
// rpcServer is a gRPC, RPC front end to the lnd daemon.
|
2016-09-26 19:29:18 +02:00
|
|
|
// TODO(roasbeef): pagination support for the list-style calls
|
2016-01-17 04:07:44 +01:00
|
|
|
type rpcServer struct {
|
|
|
|
started int32 // To be used atomically.
|
|
|
|
shutdown int32 // To be used atomically.
|
|
|
|
|
2021-07-27 12:59:59 +02:00
|
|
|
// Required by the grpc-gateway/v2 library for forward compatibility.
|
|
|
|
// Must be after the atomically used variables to not break struct
|
|
|
|
// alignment.
|
|
|
|
lnrpc.UnimplementedLightningServer
|
|
|
|
|
2017-08-22 08:25:41 +02:00
|
|
|
server *server
|
|
|
|
|
2020-05-14 14:07:26 +02:00
|
|
|
cfg *Config
|
|
|
|
|
lnd+rpc: modify rpcServer to fully manaage listeners and gRPC, handle sub-servers
In this commit, we modify the existing rpcServer to fully manage the
macaroons, gRPC server, and also seek out and create all sub-servers.
With this change, the RPC server gains more responsibility, as it
becomes the "root" server in the hierarchy of gRPC sub-servers.
In addition to creating each sub-server, it will also merge the set of
macaroon permissions for each sub-server, with the permissions of the
rest of the RPC infra. As a result, each sub-server is able to
independently specify what it needs w.r.t macaroon permissions and have
that taken care of by the RPC server. In order to achieve this, we need
to unify the creation of the RPC interceptors, and also fully manage the
gRPC server ourselves.
Some examples with various build configs:
```
⛰i make build
Building debug lnd and lncli.
go build -v -tags="dev" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
unknown flag `signrpc.signermacaroonpath'
unknown flag `signrpc.signermacaroonpath'
⛰i make build tags=signerrpc
Building debug lnd and lncli.
go build -v -tags="dev signerrpc" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev signerrpc" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
2018-10-22 17:31:01.132 [INF] LTND: Version: 0.5.0-beta commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty, build=development, logging=default
2018-10-22 17:31:01.133 [INF] LTND: Active chain: Bitcoin (network=simnet)
2018-10-22 17:31:01.140 [INF] CHDB: Checking for schema update: latest_version=6, db_version=6
2018-10-22 17:31:01.236 [INF] LTND: Primary chain is set to: bitcoin
2018-10-22 17:31:02.391 [INF] LNWL: Opened wallet
2018-10-22 17:31:03.315 [INF] LNWL: The wallet has been unlocked without a time limit
2018-10-22 17:31:03.315 [INF] LTND: LightningWallet opened
2018-10-22 17:31:03.319 [INF] LNWL: Catching up block hashes to height 3060, this will take a while...
2018-10-22 17:31:03.320 [INF] HSWC: Restoring in-memory circuit state from disk
2018-10-22 17:31:03.320 [INF] LNWL: Done catching up block hashes
2018-10-22 17:31:03.320 [INF] HSWC: Payment circuits loaded: num_pending=0, num_open=0
2018-10-22 17:31:03.322 [DBG] LTND: Populating dependencies for sub RPC server: Signrpc
```
As for the config, an example is:
```
[signrpc]
signrpc.signermacaroonpath=~/signer.macaroon
```
2018-10-23 03:03:07 +02:00
|
|
|
// subServers are a set of sub-RPC servers that use the same gRPC and
|
|
|
|
// listening sockets as the main RPC server, but which maintain their
|
|
|
|
// own independent service. This allows us to expose a set of
|
|
|
|
// micro-service like abstractions to the outside world for users to
|
|
|
|
// consume.
|
2021-01-29 15:49:48 +01:00
|
|
|
subServers []lnrpc.SubServer
|
|
|
|
subGrpcHandlers []lnrpc.GrpcHandler
|
lnd+rpc: modify rpcServer to fully manaage listeners and gRPC, handle sub-servers
In this commit, we modify the existing rpcServer to fully manage the
macaroons, gRPC server, and also seek out and create all sub-servers.
With this change, the RPC server gains more responsibility, as it
becomes the "root" server in the hierarchy of gRPC sub-servers.
In addition to creating each sub-server, it will also merge the set of
macaroon permissions for each sub-server, with the permissions of the
rest of the RPC infra. As a result, each sub-server is able to
independently specify what it needs w.r.t macaroon permissions and have
that taken care of by the RPC server. In order to achieve this, we need
to unify the creation of the RPC interceptors, and also fully manage the
gRPC server ourselves.
Some examples with various build configs:
```
⛰i make build
Building debug lnd and lncli.
go build -v -tags="dev" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
unknown flag `signrpc.signermacaroonpath'
unknown flag `signrpc.signermacaroonpath'
⛰i make build tags=signerrpc
Building debug lnd and lncli.
go build -v -tags="dev signerrpc" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev signerrpc" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
2018-10-22 17:31:01.132 [INF] LTND: Version: 0.5.0-beta commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty, build=development, logging=default
2018-10-22 17:31:01.133 [INF] LTND: Active chain: Bitcoin (network=simnet)
2018-10-22 17:31:01.140 [INF] CHDB: Checking for schema update: latest_version=6, db_version=6
2018-10-22 17:31:01.236 [INF] LTND: Primary chain is set to: bitcoin
2018-10-22 17:31:02.391 [INF] LNWL: Opened wallet
2018-10-22 17:31:03.315 [INF] LNWL: The wallet has been unlocked without a time limit
2018-10-22 17:31:03.315 [INF] LTND: LightningWallet opened
2018-10-22 17:31:03.319 [INF] LNWL: Catching up block hashes to height 3060, this will take a while...
2018-10-22 17:31:03.320 [INF] HSWC: Restoring in-memory circuit state from disk
2018-10-22 17:31:03.320 [INF] LNWL: Done catching up block hashes
2018-10-22 17:31:03.320 [INF] HSWC: Payment circuits loaded: num_pending=0, num_open=0
2018-10-22 17:31:03.322 [DBG] LTND: Populating dependencies for sub RPC server: Signrpc
```
As for the config, an example is:
```
[signrpc]
signrpc.signermacaroonpath=~/signer.macaroon
```
2018-10-23 03:03:07 +02:00
|
|
|
|
2019-03-14 14:19:35 +01:00
|
|
|
// routerBackend contains the backend implementation of the router
|
2019-03-11 09:56:05 +01:00
|
|
|
// rpc sub server.
|
2019-03-14 14:19:35 +01:00
|
|
|
routerBackend *routerrpc.RouterBackend
|
2019-03-11 09:56:05 +01:00
|
|
|
|
2019-08-08 04:17:50 +02:00
|
|
|
// chanPredicate is used in the bidirectional ChannelAcceptor streaming
|
|
|
|
// method.
|
2022-07-08 23:18:14 +02:00
|
|
|
chanPredicate chanacceptor.MultiplexAcceptor
|
2019-08-08 04:17:50 +02:00
|
|
|
|
2016-01-17 04:07:44 +01:00
|
|
|
quit chan struct{}
|
2019-10-23 13:28:17 +02:00
|
|
|
|
|
|
|
// macService is the macaroon service that we need to mint new
|
|
|
|
// macaroons.
|
|
|
|
macService *macaroons.Service
|
2019-11-25 14:13:21 +01:00
|
|
|
|
|
|
|
// selfNode is our own pubkey.
|
|
|
|
selfNode route.Vertex
|
2020-09-04 09:22:36 +02:00
|
|
|
|
2021-09-23 16:54:39 +02:00
|
|
|
// interceptorChain is the interceptor added to our gRPC server.
|
2020-10-13 11:24:40 +02:00
|
|
|
interceptorChain *rpcperms.InterceptorChain
|
2020-10-14 11:52:18 +02:00
|
|
|
|
2021-09-23 16:54:39 +02:00
|
|
|
// implCfg is the configuration for some of the interfaces that can be
|
|
|
|
// provided externally.
|
|
|
|
implCfg *ImplementationCfg
|
2020-08-28 11:18:15 +02:00
|
|
|
|
|
|
|
// interceptor is used to be able to request a shutdown
|
|
|
|
interceptor signal.Interceptor
|
2021-10-20 01:19:53 +02:00
|
|
|
|
|
|
|
graphCache sync.RWMutex
|
|
|
|
describeGraphResp *lnrpc.ChannelGraph
|
|
|
|
graphCacheEvictor *time.Timer
|
2015-12-30 00:09:38 +01:00
|
|
|
}
|
|
|
|
|
2016-06-21 20:52:09 +02:00
|
|
|
// A compile time check to ensure that rpcServer fully implements the
|
|
|
|
// LightningServer gRPC service.
|
2015-12-30 00:21:42 +01:00
|
|
|
var _ lnrpc.LightningServer = (*rpcServer)(nil)
|
|
|
|
|
2021-02-02 13:45:29 +01:00
|
|
|
// newRPCServer creates and returns a new instance of the rpcServer. Before
|
|
|
|
// dependencies are added, this will be an non-functioning RPC server only to
|
|
|
|
// be used to register the LightningService with the gRPC server.
|
|
|
|
func newRPCServer(cfg *Config, interceptorChain *rpcperms.InterceptorChain,
|
2021-09-23 16:54:39 +02:00
|
|
|
implCfg *ImplementationCfg, interceptor signal.Interceptor) *rpcServer {
|
2021-02-02 13:45:29 +01:00
|
|
|
|
|
|
|
// We go trhough the list of registered sub-servers, and create a gRPC
|
|
|
|
// handler for each. These are used to register with the gRPC server
|
|
|
|
// before all dependencies are available.
|
|
|
|
registeredSubServers := lnrpc.RegisteredSubServers()
|
|
|
|
|
|
|
|
var subServerHandlers []lnrpc.GrpcHandler
|
|
|
|
for _, subServer := range registeredSubServers {
|
|
|
|
subServerHandlers = append(
|
|
|
|
subServerHandlers, subServer.NewGrpcHandler(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &rpcServer{
|
|
|
|
cfg: cfg,
|
|
|
|
subGrpcHandlers: subServerHandlers,
|
|
|
|
interceptorChain: interceptorChain,
|
2021-09-23 16:54:39 +02:00
|
|
|
implCfg: implCfg,
|
2021-02-02 13:45:29 +01:00
|
|
|
quit: make(chan struct{}, 1),
|
2020-08-28 11:18:15 +02:00
|
|
|
interceptor: interceptor,
|
2021-02-02 13:45:29 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// addDeps populates all dependencies needed by the RPC server, and any
|
|
|
|
// of the sub-servers that it maintains. When this is done, the RPC server can
|
|
|
|
// be started, and start accepting RPC calls.
|
|
|
|
func (r *rpcServer) addDeps(s *server, macService *macaroons.Service,
|
|
|
|
subServerCgs *subRPCServerConfigs, atpl *autopilot.Manager,
|
|
|
|
invoiceRegistry *invoices.InvoiceRegistry, tower *watchtower.Standalone,
|
2022-07-08 23:18:14 +02:00
|
|
|
chanPredicate chanacceptor.MultiplexAcceptor) error {
|
lnd+rpc: modify rpcServer to fully manaage listeners and gRPC, handle sub-servers
In this commit, we modify the existing rpcServer to fully manage the
macaroons, gRPC server, and also seek out and create all sub-servers.
With this change, the RPC server gains more responsibility, as it
becomes the "root" server in the hierarchy of gRPC sub-servers.
In addition to creating each sub-server, it will also merge the set of
macaroon permissions for each sub-server, with the permissions of the
rest of the RPC infra. As a result, each sub-server is able to
independently specify what it needs w.r.t macaroon permissions and have
that taken care of by the RPC server. In order to achieve this, we need
to unify the creation of the RPC interceptors, and also fully manage the
gRPC server ourselves.
Some examples with various build configs:
```
⛰i make build
Building debug lnd and lncli.
go build -v -tags="dev" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
unknown flag `signrpc.signermacaroonpath'
unknown flag `signrpc.signermacaroonpath'
⛰i make build tags=signerrpc
Building debug lnd and lncli.
go build -v -tags="dev signerrpc" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev signerrpc" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
2018-10-22 17:31:01.132 [INF] LTND: Version: 0.5.0-beta commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty, build=development, logging=default
2018-10-22 17:31:01.133 [INF] LTND: Active chain: Bitcoin (network=simnet)
2018-10-22 17:31:01.140 [INF] CHDB: Checking for schema update: latest_version=6, db_version=6
2018-10-22 17:31:01.236 [INF] LTND: Primary chain is set to: bitcoin
2018-10-22 17:31:02.391 [INF] LNWL: Opened wallet
2018-10-22 17:31:03.315 [INF] LNWL: The wallet has been unlocked without a time limit
2018-10-22 17:31:03.315 [INF] LTND: LightningWallet opened
2018-10-22 17:31:03.319 [INF] LNWL: Catching up block hashes to height 3060, this will take a while...
2018-10-22 17:31:03.320 [INF] HSWC: Restoring in-memory circuit state from disk
2018-10-22 17:31:03.320 [INF] LNWL: Done catching up block hashes
2018-10-22 17:31:03.320 [INF] HSWC: Payment circuits loaded: num_pending=0, num_open=0
2018-10-22 17:31:03.322 [DBG] LTND: Populating dependencies for sub RPC server: Signrpc
```
As for the config, an example is:
```
[signrpc]
signrpc.signermacaroonpath=~/signer.macaroon
```
2018-10-23 03:03:07 +02:00
|
|
|
|
2019-03-14 14:19:35 +01:00
|
|
|
// Set up router rpc backend.
|
2021-08-03 09:57:26 +02:00
|
|
|
selfNode, err := s.graphDB.SourceNode()
|
2019-03-14 14:19:35 +01:00
|
|
|
if err != nil {
|
2021-02-02 13:45:29 +01:00
|
|
|
return err
|
2019-03-14 14:19:35 +01:00
|
|
|
}
|
2021-08-03 09:57:26 +02:00
|
|
|
graph := s.graphDB
|
2022-08-23 12:00:54 +02:00
|
|
|
|
2019-03-14 14:19:35 +01:00
|
|
|
routerBackend := &routerrpc.RouterBackend{
|
2020-05-28 19:28:34 +02:00
|
|
|
SelfNode: selfNode.PubKeyBytes,
|
2019-03-14 14:19:35 +01:00
|
|
|
FetchChannelCapacity: func(chanID uint64) (btcutil.Amount,
|
|
|
|
error) {
|
|
|
|
|
|
|
|
info, _, _, err := graph.FetchChannelEdgesByID(chanID)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
return info.Capacity, nil
|
|
|
|
},
|
2022-08-23 12:00:54 +02:00
|
|
|
FetchAmountPairCapacity: func(nodeFrom, nodeTo route.Vertex,
|
|
|
|
amount lnwire.MilliSatoshi) (btcutil.Amount, error) {
|
|
|
|
|
2024-06-26 04:27:13 +02:00
|
|
|
return routing.FetchAmountPairCapacity(
|
2024-06-26 04:58:57 +02:00
|
|
|
graphsession.NewRoutingGraph(graph),
|
|
|
|
selfNode.PubKeyBytes, nodeFrom, nodeTo, amount,
|
2022-08-23 12:00:54 +02:00
|
|
|
)
|
|
|
|
},
|
2019-03-14 15:13:45 +01:00
|
|
|
FetchChannelEndpoints: func(chanID uint64) (route.Vertex,
|
|
|
|
route.Vertex, error) {
|
|
|
|
|
|
|
|
info, _, _, err := graph.FetchChannelEdgesByID(
|
|
|
|
chanID,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return route.Vertex{}, route.Vertex{},
|
|
|
|
fmt.Errorf("unable to fetch channel "+
|
|
|
|
"edges by channel ID %d: %v",
|
|
|
|
chanID, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return info.NodeKey1Bytes, info.NodeKey2Bytes, nil
|
|
|
|
},
|
2020-05-19 11:57:36 +02:00
|
|
|
FindRoute: s.chanRouter.FindRoute,
|
|
|
|
MissionControl: s.missionControl,
|
2021-02-02 13:45:29 +01:00
|
|
|
ActiveNetParams: r.cfg.ActiveNetParams.Params,
|
2020-05-19 11:57:36 +02:00
|
|
|
Tower: s.controlTower,
|
2021-02-02 13:45:29 +01:00
|
|
|
MaxTotalTimelock: r.cfg.MaxOutgoingCltvExpiry,
|
|
|
|
DefaultFinalCltvDelta: uint16(r.cfg.Bitcoin.TimeLockDelta),
|
2020-05-19 11:57:36 +02:00
|
|
|
SubscribeHtlcEvents: s.htlcNotifier.SubscribeHtlcEvents,
|
|
|
|
InterceptableForwarder: s.interceptableSwitch,
|
2021-02-13 08:29:13 +01:00
|
|
|
SetChannelEnabled: func(outpoint wire.OutPoint) error {
|
|
|
|
return s.chanStatusMgr.RequestEnable(outpoint, true)
|
|
|
|
},
|
|
|
|
SetChannelDisabled: func(outpoint wire.OutPoint) error {
|
|
|
|
return s.chanStatusMgr.RequestDisable(outpoint, true)
|
|
|
|
},
|
2023-11-14 13:29:52 +01:00
|
|
|
SetChannelAuto: s.chanStatusMgr.RequestAuto,
|
|
|
|
UseStatusInitiated: subServerCgs.RouterRPC.UseStatusInitiated,
|
2019-03-14 14:19:35 +01:00
|
|
|
}
|
|
|
|
|
2019-12-10 22:09:52 +01:00
|
|
|
genInvoiceFeatures := func() *lnwire.FeatureVector {
|
|
|
|
return s.featureMgr.Get(feature.SetInvoice)
|
|
|
|
}
|
2021-05-06 18:15:01 +02:00
|
|
|
genAmpInvoiceFeatures := func() *lnwire.FeatureVector {
|
|
|
|
return s.featureMgr.Get(feature.SetInvoiceAmp)
|
|
|
|
}
|
2019-12-10 22:09:52 +01:00
|
|
|
|
2021-12-06 05:24:06 +01:00
|
|
|
parseAddr := func(addr string) (net.Addr, error) {
|
|
|
|
return parseAddr(addr, r.cfg.net)
|
|
|
|
}
|
|
|
|
|
lnd+rpc: modify rpcServer to fully manaage listeners and gRPC, handle sub-servers
In this commit, we modify the existing rpcServer to fully manage the
macaroons, gRPC server, and also seek out and create all sub-servers.
With this change, the RPC server gains more responsibility, as it
becomes the "root" server in the hierarchy of gRPC sub-servers.
In addition to creating each sub-server, it will also merge the set of
macaroon permissions for each sub-server, with the permissions of the
rest of the RPC infra. As a result, each sub-server is able to
independently specify what it needs w.r.t macaroon permissions and have
that taken care of by the RPC server. In order to achieve this, we need
to unify the creation of the RPC interceptors, and also fully manage the
gRPC server ourselves.
Some examples with various build configs:
```
⛰i make build
Building debug lnd and lncli.
go build -v -tags="dev" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
unknown flag `signrpc.signermacaroonpath'
unknown flag `signrpc.signermacaroonpath'
⛰i make build tags=signerrpc
Building debug lnd and lncli.
go build -v -tags="dev signerrpc" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev signerrpc" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
2018-10-22 17:31:01.132 [INF] LTND: Version: 0.5.0-beta commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty, build=development, logging=default
2018-10-22 17:31:01.133 [INF] LTND: Active chain: Bitcoin (network=simnet)
2018-10-22 17:31:01.140 [INF] CHDB: Checking for schema update: latest_version=6, db_version=6
2018-10-22 17:31:01.236 [INF] LTND: Primary chain is set to: bitcoin
2018-10-22 17:31:02.391 [INF] LNWL: Opened wallet
2018-10-22 17:31:03.315 [INF] LNWL: The wallet has been unlocked without a time limit
2018-10-22 17:31:03.315 [INF] LTND: LightningWallet opened
2018-10-22 17:31:03.319 [INF] LNWL: Catching up block hashes to height 3060, this will take a while...
2018-10-22 17:31:03.320 [INF] HSWC: Restoring in-memory circuit state from disk
2018-10-22 17:31:03.320 [INF] LNWL: Done catching up block hashes
2018-10-22 17:31:03.320 [INF] HSWC: Payment circuits loaded: num_pending=0, num_open=0
2018-10-22 17:31:03.322 [DBG] LTND: Populating dependencies for sub RPC server: Signrpc
```
As for the config, an example is:
```
[signrpc]
signrpc.signermacaroonpath=~/signer.macaroon
```
2018-10-23 03:03:07 +02:00
|
|
|
var (
|
2021-02-02 13:45:29 +01:00
|
|
|
subServers []lnrpc.SubServer
|
|
|
|
subServerPerms []lnrpc.MacaroonPerms
|
lnd+rpc: modify rpcServer to fully manaage listeners and gRPC, handle sub-servers
In this commit, we modify the existing rpcServer to fully manage the
macaroons, gRPC server, and also seek out and create all sub-servers.
With this change, the RPC server gains more responsibility, as it
becomes the "root" server in the hierarchy of gRPC sub-servers.
In addition to creating each sub-server, it will also merge the set of
macaroon permissions for each sub-server, with the permissions of the
rest of the RPC infra. As a result, each sub-server is able to
independently specify what it needs w.r.t macaroon permissions and have
that taken care of by the RPC server. In order to achieve this, we need
to unify the creation of the RPC interceptors, and also fully manage the
gRPC server ourselves.
Some examples with various build configs:
```
⛰i make build
Building debug lnd and lncli.
go build -v -tags="dev" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
unknown flag `signrpc.signermacaroonpath'
unknown flag `signrpc.signermacaroonpath'
⛰i make build tags=signerrpc
Building debug lnd and lncli.
go build -v -tags="dev signerrpc" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev signerrpc" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
2018-10-22 17:31:01.132 [INF] LTND: Version: 0.5.0-beta commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty, build=development, logging=default
2018-10-22 17:31:01.133 [INF] LTND: Active chain: Bitcoin (network=simnet)
2018-10-22 17:31:01.140 [INF] CHDB: Checking for schema update: latest_version=6, db_version=6
2018-10-22 17:31:01.236 [INF] LTND: Primary chain is set to: bitcoin
2018-10-22 17:31:02.391 [INF] LNWL: Opened wallet
2018-10-22 17:31:03.315 [INF] LNWL: The wallet has been unlocked without a time limit
2018-10-22 17:31:03.315 [INF] LTND: LightningWallet opened
2018-10-22 17:31:03.319 [INF] LNWL: Catching up block hashes to height 3060, this will take a while...
2018-10-22 17:31:03.320 [INF] HSWC: Restoring in-memory circuit state from disk
2018-10-22 17:31:03.320 [INF] LNWL: Done catching up block hashes
2018-10-22 17:31:03.320 [INF] HSWC: Payment circuits loaded: num_pending=0, num_open=0
2018-10-22 17:31:03.322 [DBG] LTND: Populating dependencies for sub RPC server: Signrpc
```
As for the config, an example is:
```
[signrpc]
signrpc.signermacaroonpath=~/signer.macaroon
```
2018-10-23 03:03:07 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// Before we create any of the sub-servers, we need to ensure that all
|
|
|
|
// the dependencies they need are properly populated within each sub
|
|
|
|
// server configuration struct.
|
2020-05-07 05:49:36 +02:00
|
|
|
//
|
|
|
|
// TODO(roasbeef): extend sub-sever config to have both (local vs remote) DB
|
2019-03-14 14:19:35 +01:00
|
|
|
err = subServerCgs.PopulateDependencies(
|
2021-02-02 13:45:29 +01:00
|
|
|
r.cfg, s.cc, r.cfg.networkDir, macService, atpl, invoiceRegistry,
|
|
|
|
s.htlcSwitch, r.cfg.ActiveNetParams.Params, s.chanRouter,
|
2021-08-03 09:57:26 +02:00
|
|
|
routerBackend, s.nodeSigner, s.graphDB, s.chanStateDB,
|
2023-05-16 15:22:45 +02:00
|
|
|
s.sweeper, tower, s.towerClientMgr, r.cfg.net.ResolveTCPAddr,
|
|
|
|
genInvoiceFeatures, genAmpInvoiceFeatures,
|
|
|
|
s.getNodeAnnouncement, s.updateAndBrodcastSelfNode, parseAddr,
|
|
|
|
rpcsLog, s.aliasMgr.GetPeerAlias,
|
lnd+rpc: modify rpcServer to fully manaage listeners and gRPC, handle sub-servers
In this commit, we modify the existing rpcServer to fully manage the
macaroons, gRPC server, and also seek out and create all sub-servers.
With this change, the RPC server gains more responsibility, as it
becomes the "root" server in the hierarchy of gRPC sub-servers.
In addition to creating each sub-server, it will also merge the set of
macaroon permissions for each sub-server, with the permissions of the
rest of the RPC infra. As a result, each sub-server is able to
independently specify what it needs w.r.t macaroon permissions and have
that taken care of by the RPC server. In order to achieve this, we need
to unify the creation of the RPC interceptors, and also fully manage the
gRPC server ourselves.
Some examples with various build configs:
```
⛰i make build
Building debug lnd and lncli.
go build -v -tags="dev" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
unknown flag `signrpc.signermacaroonpath'
unknown flag `signrpc.signermacaroonpath'
⛰i make build tags=signerrpc
Building debug lnd and lncli.
go build -v -tags="dev signerrpc" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev signerrpc" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
2018-10-22 17:31:01.132 [INF] LTND: Version: 0.5.0-beta commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty, build=development, logging=default
2018-10-22 17:31:01.133 [INF] LTND: Active chain: Bitcoin (network=simnet)
2018-10-22 17:31:01.140 [INF] CHDB: Checking for schema update: latest_version=6, db_version=6
2018-10-22 17:31:01.236 [INF] LTND: Primary chain is set to: bitcoin
2018-10-22 17:31:02.391 [INF] LNWL: Opened wallet
2018-10-22 17:31:03.315 [INF] LNWL: The wallet has been unlocked without a time limit
2018-10-22 17:31:03.315 [INF] LTND: LightningWallet opened
2018-10-22 17:31:03.319 [INF] LNWL: Catching up block hashes to height 3060, this will take a while...
2018-10-22 17:31:03.320 [INF] HSWC: Restoring in-memory circuit state from disk
2018-10-22 17:31:03.320 [INF] LNWL: Done catching up block hashes
2018-10-22 17:31:03.320 [INF] HSWC: Payment circuits loaded: num_pending=0, num_open=0
2018-10-22 17:31:03.322 [DBG] LTND: Populating dependencies for sub RPC server: Signrpc
```
As for the config, an example is:
```
[signrpc]
signrpc.signermacaroonpath=~/signer.macaroon
```
2018-10-23 03:03:07 +02:00
|
|
|
)
|
|
|
|
if err != nil {
|
2021-02-02 13:45:29 +01:00
|
|
|
return err
|
lnd+rpc: modify rpcServer to fully manaage listeners and gRPC, handle sub-servers
In this commit, we modify the existing rpcServer to fully manage the
macaroons, gRPC server, and also seek out and create all sub-servers.
With this change, the RPC server gains more responsibility, as it
becomes the "root" server in the hierarchy of gRPC sub-servers.
In addition to creating each sub-server, it will also merge the set of
macaroon permissions for each sub-server, with the permissions of the
rest of the RPC infra. As a result, each sub-server is able to
independently specify what it needs w.r.t macaroon permissions and have
that taken care of by the RPC server. In order to achieve this, we need
to unify the creation of the RPC interceptors, and also fully manage the
gRPC server ourselves.
Some examples with various build configs:
```
⛰i make build
Building debug lnd and lncli.
go build -v -tags="dev" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
unknown flag `signrpc.signermacaroonpath'
unknown flag `signrpc.signermacaroonpath'
⛰i make build tags=signerrpc
Building debug lnd and lncli.
go build -v -tags="dev signerrpc" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev signerrpc" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
2018-10-22 17:31:01.132 [INF] LTND: Version: 0.5.0-beta commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty, build=development, logging=default
2018-10-22 17:31:01.133 [INF] LTND: Active chain: Bitcoin (network=simnet)
2018-10-22 17:31:01.140 [INF] CHDB: Checking for schema update: latest_version=6, db_version=6
2018-10-22 17:31:01.236 [INF] LTND: Primary chain is set to: bitcoin
2018-10-22 17:31:02.391 [INF] LNWL: Opened wallet
2018-10-22 17:31:03.315 [INF] LNWL: The wallet has been unlocked without a time limit
2018-10-22 17:31:03.315 [INF] LTND: LightningWallet opened
2018-10-22 17:31:03.319 [INF] LNWL: Catching up block hashes to height 3060, this will take a while...
2018-10-22 17:31:03.320 [INF] HSWC: Restoring in-memory circuit state from disk
2018-10-22 17:31:03.320 [INF] LNWL: Done catching up block hashes
2018-10-22 17:31:03.320 [INF] HSWC: Payment circuits loaded: num_pending=0, num_open=0
2018-10-22 17:31:03.322 [DBG] LTND: Populating dependencies for sub RPC server: Signrpc
```
As for the config, an example is:
```
[signrpc]
signrpc.signermacaroonpath=~/signer.macaroon
```
2018-10-23 03:03:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Now that the sub-servers have all their dependencies in place, we
|
|
|
|
// can create each sub-server!
|
2021-02-02 13:45:29 +01:00
|
|
|
for _, subServerInstance := range r.subGrpcHandlers {
|
|
|
|
subServer, macPerms, err := subServerInstance.CreateSubServer(
|
|
|
|
subServerCgs,
|
|
|
|
)
|
lnd+rpc: modify rpcServer to fully manaage listeners and gRPC, handle sub-servers
In this commit, we modify the existing rpcServer to fully manage the
macaroons, gRPC server, and also seek out and create all sub-servers.
With this change, the RPC server gains more responsibility, as it
becomes the "root" server in the hierarchy of gRPC sub-servers.
In addition to creating each sub-server, it will also merge the set of
macaroon permissions for each sub-server, with the permissions of the
rest of the RPC infra. As a result, each sub-server is able to
independently specify what it needs w.r.t macaroon permissions and have
that taken care of by the RPC server. In order to achieve this, we need
to unify the creation of the RPC interceptors, and also fully manage the
gRPC server ourselves.
Some examples with various build configs:
```
⛰i make build
Building debug lnd and lncli.
go build -v -tags="dev" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
unknown flag `signrpc.signermacaroonpath'
unknown flag `signrpc.signermacaroonpath'
⛰i make build tags=signerrpc
Building debug lnd and lncli.
go build -v -tags="dev signerrpc" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev signerrpc" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
2018-10-22 17:31:01.132 [INF] LTND: Version: 0.5.0-beta commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty, build=development, logging=default
2018-10-22 17:31:01.133 [INF] LTND: Active chain: Bitcoin (network=simnet)
2018-10-22 17:31:01.140 [INF] CHDB: Checking for schema update: latest_version=6, db_version=6
2018-10-22 17:31:01.236 [INF] LTND: Primary chain is set to: bitcoin
2018-10-22 17:31:02.391 [INF] LNWL: Opened wallet
2018-10-22 17:31:03.315 [INF] LNWL: The wallet has been unlocked without a time limit
2018-10-22 17:31:03.315 [INF] LTND: LightningWallet opened
2018-10-22 17:31:03.319 [INF] LNWL: Catching up block hashes to height 3060, this will take a while...
2018-10-22 17:31:03.320 [INF] HSWC: Restoring in-memory circuit state from disk
2018-10-22 17:31:03.320 [INF] LNWL: Done catching up block hashes
2018-10-22 17:31:03.320 [INF] HSWC: Payment circuits loaded: num_pending=0, num_open=0
2018-10-22 17:31:03.322 [DBG] LTND: Populating dependencies for sub RPC server: Signrpc
```
As for the config, an example is:
```
[signrpc]
signrpc.signermacaroonpath=~/signer.macaroon
```
2018-10-23 03:03:07 +02:00
|
|
|
if err != nil {
|
2021-02-02 13:45:29 +01:00
|
|
|
return err
|
lnd+rpc: modify rpcServer to fully manaage listeners and gRPC, handle sub-servers
In this commit, we modify the existing rpcServer to fully manage the
macaroons, gRPC server, and also seek out and create all sub-servers.
With this change, the RPC server gains more responsibility, as it
becomes the "root" server in the hierarchy of gRPC sub-servers.
In addition to creating each sub-server, it will also merge the set of
macaroon permissions for each sub-server, with the permissions of the
rest of the RPC infra. As a result, each sub-server is able to
independently specify what it needs w.r.t macaroon permissions and have
that taken care of by the RPC server. In order to achieve this, we need
to unify the creation of the RPC interceptors, and also fully manage the
gRPC server ourselves.
Some examples with various build configs:
```
⛰i make build
Building debug lnd and lncli.
go build -v -tags="dev" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
unknown flag `signrpc.signermacaroonpath'
unknown flag `signrpc.signermacaroonpath'
⛰i make build tags=signerrpc
Building debug lnd and lncli.
go build -v -tags="dev signerrpc" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev signerrpc" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
2018-10-22 17:31:01.132 [INF] LTND: Version: 0.5.0-beta commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty, build=development, logging=default
2018-10-22 17:31:01.133 [INF] LTND: Active chain: Bitcoin (network=simnet)
2018-10-22 17:31:01.140 [INF] CHDB: Checking for schema update: latest_version=6, db_version=6
2018-10-22 17:31:01.236 [INF] LTND: Primary chain is set to: bitcoin
2018-10-22 17:31:02.391 [INF] LNWL: Opened wallet
2018-10-22 17:31:03.315 [INF] LNWL: The wallet has been unlocked without a time limit
2018-10-22 17:31:03.315 [INF] LTND: LightningWallet opened
2018-10-22 17:31:03.319 [INF] LNWL: Catching up block hashes to height 3060, this will take a while...
2018-10-22 17:31:03.320 [INF] HSWC: Restoring in-memory circuit state from disk
2018-10-22 17:31:03.320 [INF] LNWL: Done catching up block hashes
2018-10-22 17:31:03.320 [INF] HSWC: Payment circuits loaded: num_pending=0, num_open=0
2018-10-22 17:31:03.322 [DBG] LTND: Populating dependencies for sub RPC server: Signrpc
```
As for the config, an example is:
```
[signrpc]
signrpc.signermacaroonpath=~/signer.macaroon
```
2018-10-23 03:03:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// We'll collect the sub-server, and also the set of
|
|
|
|
// permissions it needs for macaroons so we can apply the
|
|
|
|
// interceptors below.
|
2021-01-29 15:49:48 +01:00
|
|
|
subServers = append(subServers, subServer)
|
lnd+rpc: modify rpcServer to fully manaage listeners and gRPC, handle sub-servers
In this commit, we modify the existing rpcServer to fully manage the
macaroons, gRPC server, and also seek out and create all sub-servers.
With this change, the RPC server gains more responsibility, as it
becomes the "root" server in the hierarchy of gRPC sub-servers.
In addition to creating each sub-server, it will also merge the set of
macaroon permissions for each sub-server, with the permissions of the
rest of the RPC infra. As a result, each sub-server is able to
independently specify what it needs w.r.t macaroon permissions and have
that taken care of by the RPC server. In order to achieve this, we need
to unify the creation of the RPC interceptors, and also fully manage the
gRPC server ourselves.
Some examples with various build configs:
```
⛰i make build
Building debug lnd and lncli.
go build -v -tags="dev" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
unknown flag `signrpc.signermacaroonpath'
unknown flag `signrpc.signermacaroonpath'
⛰i make build tags=signerrpc
Building debug lnd and lncli.
go build -v -tags="dev signerrpc" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev signerrpc" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
2018-10-22 17:31:01.132 [INF] LTND: Version: 0.5.0-beta commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty, build=development, logging=default
2018-10-22 17:31:01.133 [INF] LTND: Active chain: Bitcoin (network=simnet)
2018-10-22 17:31:01.140 [INF] CHDB: Checking for schema update: latest_version=6, db_version=6
2018-10-22 17:31:01.236 [INF] LTND: Primary chain is set to: bitcoin
2018-10-22 17:31:02.391 [INF] LNWL: Opened wallet
2018-10-22 17:31:03.315 [INF] LNWL: The wallet has been unlocked without a time limit
2018-10-22 17:31:03.315 [INF] LTND: LightningWallet opened
2018-10-22 17:31:03.319 [INF] LNWL: Catching up block hashes to height 3060, this will take a while...
2018-10-22 17:31:03.320 [INF] HSWC: Restoring in-memory circuit state from disk
2018-10-22 17:31:03.320 [INF] LNWL: Done catching up block hashes
2018-10-22 17:31:03.320 [INF] HSWC: Payment circuits loaded: num_pending=0, num_open=0
2018-10-22 17:31:03.322 [DBG] LTND: Populating dependencies for sub RPC server: Signrpc
```
As for the config, an example is:
```
[signrpc]
signrpc.signermacaroonpath=~/signer.macaroon
```
2018-10-23 03:03:07 +02:00
|
|
|
subServerPerms = append(subServerPerms, macPerms)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Next, we need to merge the set of sub server macaroon permissions
|
|
|
|
// with the main RPC server permissions so we can unite them under a
|
|
|
|
// single set of interceptors.
|
2020-10-13 11:24:40 +02:00
|
|
|
for m, ops := range MainRPCServerPermissions() {
|
2021-02-02 13:45:29 +01:00
|
|
|
err := r.interceptorChain.AddPermission(m, ops)
|
2020-10-13 11:24:40 +02:00
|
|
|
if err != nil {
|
2021-02-02 13:45:29 +01:00
|
|
|
return err
|
2020-10-13 11:24:40 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
lnd+rpc: modify rpcServer to fully manaage listeners and gRPC, handle sub-servers
In this commit, we modify the existing rpcServer to fully manage the
macaroons, gRPC server, and also seek out and create all sub-servers.
With this change, the RPC server gains more responsibility, as it
becomes the "root" server in the hierarchy of gRPC sub-servers.
In addition to creating each sub-server, it will also merge the set of
macaroon permissions for each sub-server, with the permissions of the
rest of the RPC infra. As a result, each sub-server is able to
independently specify what it needs w.r.t macaroon permissions and have
that taken care of by the RPC server. In order to achieve this, we need
to unify the creation of the RPC interceptors, and also fully manage the
gRPC server ourselves.
Some examples with various build configs:
```
⛰i make build
Building debug lnd and lncli.
go build -v -tags="dev" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
unknown flag `signrpc.signermacaroonpath'
unknown flag `signrpc.signermacaroonpath'
⛰i make build tags=signerrpc
Building debug lnd and lncli.
go build -v -tags="dev signerrpc" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev signerrpc" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
2018-10-22 17:31:01.132 [INF] LTND: Version: 0.5.0-beta commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty, build=development, logging=default
2018-10-22 17:31:01.133 [INF] LTND: Active chain: Bitcoin (network=simnet)
2018-10-22 17:31:01.140 [INF] CHDB: Checking for schema update: latest_version=6, db_version=6
2018-10-22 17:31:01.236 [INF] LTND: Primary chain is set to: bitcoin
2018-10-22 17:31:02.391 [INF] LNWL: Opened wallet
2018-10-22 17:31:03.315 [INF] LNWL: The wallet has been unlocked without a time limit
2018-10-22 17:31:03.315 [INF] LTND: LightningWallet opened
2018-10-22 17:31:03.319 [INF] LNWL: Catching up block hashes to height 3060, this will take a while...
2018-10-22 17:31:03.320 [INF] HSWC: Restoring in-memory circuit state from disk
2018-10-22 17:31:03.320 [INF] LNWL: Done catching up block hashes
2018-10-22 17:31:03.320 [INF] HSWC: Payment circuits loaded: num_pending=0, num_open=0
2018-10-22 17:31:03.322 [DBG] LTND: Populating dependencies for sub RPC server: Signrpc
```
As for the config, an example is:
```
[signrpc]
signrpc.signermacaroonpath=~/signer.macaroon
```
2018-10-23 03:03:07 +02:00
|
|
|
for _, subServerPerm := range subServerPerms {
|
|
|
|
for method, ops := range subServerPerm {
|
2021-02-02 13:45:29 +01:00
|
|
|
err := r.interceptorChain.AddPermission(method, ops)
|
2020-10-13 11:24:40 +02:00
|
|
|
if err != nil {
|
2021-02-02 13:45:29 +01:00
|
|
|
return err
|
lnd+rpc: modify rpcServer to fully manaage listeners and gRPC, handle sub-servers
In this commit, we modify the existing rpcServer to fully manage the
macaroons, gRPC server, and also seek out and create all sub-servers.
With this change, the RPC server gains more responsibility, as it
becomes the "root" server in the hierarchy of gRPC sub-servers.
In addition to creating each sub-server, it will also merge the set of
macaroon permissions for each sub-server, with the permissions of the
rest of the RPC infra. As a result, each sub-server is able to
independently specify what it needs w.r.t macaroon permissions and have
that taken care of by the RPC server. In order to achieve this, we need
to unify the creation of the RPC interceptors, and also fully manage the
gRPC server ourselves.
Some examples with various build configs:
```
⛰i make build
Building debug lnd and lncli.
go build -v -tags="dev" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
unknown flag `signrpc.signermacaroonpath'
unknown flag `signrpc.signermacaroonpath'
⛰i make build tags=signerrpc
Building debug lnd and lncli.
go build -v -tags="dev signerrpc" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev signerrpc" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
2018-10-22 17:31:01.132 [INF] LTND: Version: 0.5.0-beta commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty, build=development, logging=default
2018-10-22 17:31:01.133 [INF] LTND: Active chain: Bitcoin (network=simnet)
2018-10-22 17:31:01.140 [INF] CHDB: Checking for schema update: latest_version=6, db_version=6
2018-10-22 17:31:01.236 [INF] LTND: Primary chain is set to: bitcoin
2018-10-22 17:31:02.391 [INF] LNWL: Opened wallet
2018-10-22 17:31:03.315 [INF] LNWL: The wallet has been unlocked without a time limit
2018-10-22 17:31:03.315 [INF] LTND: LightningWallet opened
2018-10-22 17:31:03.319 [INF] LNWL: Catching up block hashes to height 3060, this will take a while...
2018-10-22 17:31:03.320 [INF] HSWC: Restoring in-memory circuit state from disk
2018-10-22 17:31:03.320 [INF] LNWL: Done catching up block hashes
2018-10-22 17:31:03.320 [INF] HSWC: Payment circuits loaded: num_pending=0, num_open=0
2018-10-22 17:31:03.322 [DBG] LTND: Populating dependencies for sub RPC server: Signrpc
```
As for the config, an example is:
```
[signrpc]
signrpc.signermacaroonpath=~/signer.macaroon
```
2018-10-23 03:03:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-13 17:06:51 +02:00
|
|
|
// External subserver possibly need to register their own permissions
|
|
|
|
// and macaroon validator.
|
2021-09-23 16:54:39 +02:00
|
|
|
for method, ops := range r.implCfg.ExternalValidator.Permissions() {
|
|
|
|
err := r.interceptorChain.AddPermission(method, ops)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-05-04 18:15:03 +02:00
|
|
|
|
2021-09-23 16:54:39 +02:00
|
|
|
// Give the external subservers the possibility to also use
|
|
|
|
// their own validator to check any macaroons attached to calls
|
|
|
|
// to this method. This allows them to have their own root key
|
|
|
|
// ID database and permission entities.
|
|
|
|
err = macService.RegisterExternalValidator(
|
|
|
|
method, r.implCfg.ExternalValidator,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("could not register external "+
|
|
|
|
"macaroon validator: %v", err)
|
2020-05-04 18:15:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-02 13:45:29 +01:00
|
|
|
// Finally, with all the set up complete, add the last dependencies to
|
|
|
|
// the rpc server.
|
|
|
|
r.server = s
|
|
|
|
r.subServers = subServers
|
|
|
|
r.routerBackend = routerBackend
|
|
|
|
r.chanPredicate = chanPredicate
|
|
|
|
r.macService = macService
|
|
|
|
r.selfNode = selfNode.PubKeyBytes
|
2021-10-20 01:19:53 +02:00
|
|
|
|
|
|
|
graphCacheDuration := r.cfg.Caches.RPCGraphCacheDuration
|
|
|
|
if graphCacheDuration != 0 {
|
|
|
|
r.graphCacheEvictor = time.AfterFunc(graphCacheDuration, func() {
|
|
|
|
// Grab the mutex and purge the current populated
|
|
|
|
// describe graph response.
|
|
|
|
r.graphCache.Lock()
|
|
|
|
defer r.graphCache.Unlock()
|
|
|
|
|
|
|
|
r.describeGraphResp = nil
|
|
|
|
|
|
|
|
// Reset ourselves as well at the end so we run again
|
|
|
|
// after the duration.
|
|
|
|
r.graphCacheEvictor.Reset(graphCacheDuration)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-02-02 13:45:29 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// RegisterWithGrpcServer registers the rpcServer and any subservers with the
|
|
|
|
// root gRPC server.
|
|
|
|
func (r *rpcServer) RegisterWithGrpcServer(grpcServer *grpc.Server) error {
|
|
|
|
// Register the main RPC server.
|
|
|
|
lnrpc.RegisterLightningServer(grpcServer, r)
|
lnd+rpc: modify rpcServer to fully manaage listeners and gRPC, handle sub-servers
In this commit, we modify the existing rpcServer to fully manage the
macaroons, gRPC server, and also seek out and create all sub-servers.
With this change, the RPC server gains more responsibility, as it
becomes the "root" server in the hierarchy of gRPC sub-servers.
In addition to creating each sub-server, it will also merge the set of
macaroon permissions for each sub-server, with the permissions of the
rest of the RPC infra. As a result, each sub-server is able to
independently specify what it needs w.r.t macaroon permissions and have
that taken care of by the RPC server. In order to achieve this, we need
to unify the creation of the RPC interceptors, and also fully manage the
gRPC server ourselves.
Some examples with various build configs:
```
⛰i make build
Building debug lnd and lncli.
go build -v -tags="dev" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
unknown flag `signrpc.signermacaroonpath'
unknown flag `signrpc.signermacaroonpath'
⛰i make build tags=signerrpc
Building debug lnd and lncli.
go build -v -tags="dev signerrpc" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev signerrpc" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
2018-10-22 17:31:01.132 [INF] LTND: Version: 0.5.0-beta commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty, build=development, logging=default
2018-10-22 17:31:01.133 [INF] LTND: Active chain: Bitcoin (network=simnet)
2018-10-22 17:31:01.140 [INF] CHDB: Checking for schema update: latest_version=6, db_version=6
2018-10-22 17:31:01.236 [INF] LTND: Primary chain is set to: bitcoin
2018-10-22 17:31:02.391 [INF] LNWL: Opened wallet
2018-10-22 17:31:03.315 [INF] LNWL: The wallet has been unlocked without a time limit
2018-10-22 17:31:03.315 [INF] LTND: LightningWallet opened
2018-10-22 17:31:03.319 [INF] LNWL: Catching up block hashes to height 3060, this will take a while...
2018-10-22 17:31:03.320 [INF] HSWC: Restoring in-memory circuit state from disk
2018-10-22 17:31:03.320 [INF] LNWL: Done catching up block hashes
2018-10-22 17:31:03.320 [INF] HSWC: Payment circuits loaded: num_pending=0, num_open=0
2018-10-22 17:31:03.322 [DBG] LTND: Populating dependencies for sub RPC server: Signrpc
```
As for the config, an example is:
```
[signrpc]
signrpc.signermacaroonpath=~/signer.macaroon
```
2018-10-23 03:03:07 +02:00
|
|
|
|
|
|
|
// Now the main RPC server has been registered, we'll iterate through
|
|
|
|
// all the sub-RPC servers and register them to ensure that requests
|
|
|
|
// are properly routed towards them.
|
2021-02-02 13:45:29 +01:00
|
|
|
for _, subServer := range r.subGrpcHandlers {
|
lnd+rpc: modify rpcServer to fully manaage listeners and gRPC, handle sub-servers
In this commit, we modify the existing rpcServer to fully manage the
macaroons, gRPC server, and also seek out and create all sub-servers.
With this change, the RPC server gains more responsibility, as it
becomes the "root" server in the hierarchy of gRPC sub-servers.
In addition to creating each sub-server, it will also merge the set of
macaroon permissions for each sub-server, with the permissions of the
rest of the RPC infra. As a result, each sub-server is able to
independently specify what it needs w.r.t macaroon permissions and have
that taken care of by the RPC server. In order to achieve this, we need
to unify the creation of the RPC interceptors, and also fully manage the
gRPC server ourselves.
Some examples with various build configs:
```
⛰i make build
Building debug lnd and lncli.
go build -v -tags="dev" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
unknown flag `signrpc.signermacaroonpath'
unknown flag `signrpc.signermacaroonpath'
⛰i make build tags=signerrpc
Building debug lnd and lncli.
go build -v -tags="dev signerrpc" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev signerrpc" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
2018-10-22 17:31:01.132 [INF] LTND: Version: 0.5.0-beta commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty, build=development, logging=default
2018-10-22 17:31:01.133 [INF] LTND: Active chain: Bitcoin (network=simnet)
2018-10-22 17:31:01.140 [INF] CHDB: Checking for schema update: latest_version=6, db_version=6
2018-10-22 17:31:01.236 [INF] LTND: Primary chain is set to: bitcoin
2018-10-22 17:31:02.391 [INF] LNWL: Opened wallet
2018-10-22 17:31:03.315 [INF] LNWL: The wallet has been unlocked without a time limit
2018-10-22 17:31:03.315 [INF] LTND: LightningWallet opened
2018-10-22 17:31:03.319 [INF] LNWL: Catching up block hashes to height 3060, this will take a while...
2018-10-22 17:31:03.320 [INF] HSWC: Restoring in-memory circuit state from disk
2018-10-22 17:31:03.320 [INF] LNWL: Done catching up block hashes
2018-10-22 17:31:03.320 [INF] HSWC: Payment circuits loaded: num_pending=0, num_open=0
2018-10-22 17:31:03.322 [DBG] LTND: Populating dependencies for sub RPC server: Signrpc
```
As for the config, an example is:
```
[signrpc]
signrpc.signermacaroonpath=~/signer.macaroon
```
2018-10-23 03:03:07 +02:00
|
|
|
err := subServer.RegisterWithRootServer(grpcServer)
|
|
|
|
if err != nil {
|
2021-02-02 13:45:29 +01:00
|
|
|
return fmt.Errorf("unable to register "+
|
2021-01-29 15:49:48 +01:00
|
|
|
"sub-server with root: %v", err)
|
lnd+rpc: modify rpcServer to fully manaage listeners and gRPC, handle sub-servers
In this commit, we modify the existing rpcServer to fully manage the
macaroons, gRPC server, and also seek out and create all sub-servers.
With this change, the RPC server gains more responsibility, as it
becomes the "root" server in the hierarchy of gRPC sub-servers.
In addition to creating each sub-server, it will also merge the set of
macaroon permissions for each sub-server, with the permissions of the
rest of the RPC infra. As a result, each sub-server is able to
independently specify what it needs w.r.t macaroon permissions and have
that taken care of by the RPC server. In order to achieve this, we need
to unify the creation of the RPC interceptors, and also fully manage the
gRPC server ourselves.
Some examples with various build configs:
```
⛰i make build
Building debug lnd and lncli.
go build -v -tags="dev" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
unknown flag `signrpc.signermacaroonpath'
unknown flag `signrpc.signermacaroonpath'
⛰i make build tags=signerrpc
Building debug lnd and lncli.
go build -v -tags="dev signerrpc" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev signerrpc" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
2018-10-22 17:31:01.132 [INF] LTND: Version: 0.5.0-beta commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty, build=development, logging=default
2018-10-22 17:31:01.133 [INF] LTND: Active chain: Bitcoin (network=simnet)
2018-10-22 17:31:01.140 [INF] CHDB: Checking for schema update: latest_version=6, db_version=6
2018-10-22 17:31:01.236 [INF] LTND: Primary chain is set to: bitcoin
2018-10-22 17:31:02.391 [INF] LNWL: Opened wallet
2018-10-22 17:31:03.315 [INF] LNWL: The wallet has been unlocked without a time limit
2018-10-22 17:31:03.315 [INF] LTND: LightningWallet opened
2018-10-22 17:31:03.319 [INF] LNWL: Catching up block hashes to height 3060, this will take a while...
2018-10-22 17:31:03.320 [INF] HSWC: Restoring in-memory circuit state from disk
2018-10-22 17:31:03.320 [INF] LNWL: Done catching up block hashes
2018-10-22 17:31:03.320 [INF] HSWC: Payment circuits loaded: num_pending=0, num_open=0
2018-10-22 17:31:03.322 [DBG] LTND: Populating dependencies for sub RPC server: Signrpc
```
As for the config, an example is:
```
[signrpc]
signrpc.signermacaroonpath=~/signer.macaroon
```
2018-10-23 03:03:07 +02:00
|
|
|
}
|
2017-08-18 03:50:57 +02:00
|
|
|
}
|
lnd+rpc: modify rpcServer to fully manaage listeners and gRPC, handle sub-servers
In this commit, we modify the existing rpcServer to fully manage the
macaroons, gRPC server, and also seek out and create all sub-servers.
With this change, the RPC server gains more responsibility, as it
becomes the "root" server in the hierarchy of gRPC sub-servers.
In addition to creating each sub-server, it will also merge the set of
macaroon permissions for each sub-server, with the permissions of the
rest of the RPC infra. As a result, each sub-server is able to
independently specify what it needs w.r.t macaroon permissions and have
that taken care of by the RPC server. In order to achieve this, we need
to unify the creation of the RPC interceptors, and also fully manage the
gRPC server ourselves.
Some examples with various build configs:
```
⛰i make build
Building debug lnd and lncli.
go build -v -tags="dev" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
unknown flag `signrpc.signermacaroonpath'
unknown flag `signrpc.signermacaroonpath'
⛰i make build tags=signerrpc
Building debug lnd and lncli.
go build -v -tags="dev signerrpc" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev signerrpc" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
2018-10-22 17:31:01.132 [INF] LTND: Version: 0.5.0-beta commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty, build=development, logging=default
2018-10-22 17:31:01.133 [INF] LTND: Active chain: Bitcoin (network=simnet)
2018-10-22 17:31:01.140 [INF] CHDB: Checking for schema update: latest_version=6, db_version=6
2018-10-22 17:31:01.236 [INF] LTND: Primary chain is set to: bitcoin
2018-10-22 17:31:02.391 [INF] LNWL: Opened wallet
2018-10-22 17:31:03.315 [INF] LNWL: The wallet has been unlocked without a time limit
2018-10-22 17:31:03.315 [INF] LTND: LightningWallet opened
2018-10-22 17:31:03.319 [INF] LNWL: Catching up block hashes to height 3060, this will take a while...
2018-10-22 17:31:03.320 [INF] HSWC: Restoring in-memory circuit state from disk
2018-10-22 17:31:03.320 [INF] LNWL: Done catching up block hashes
2018-10-22 17:31:03.320 [INF] HSWC: Payment circuits loaded: num_pending=0, num_open=0
2018-10-22 17:31:03.322 [DBG] LTND: Populating dependencies for sub RPC server: Signrpc
```
As for the config, an example is:
```
[signrpc]
signrpc.signermacaroonpath=~/signer.macaroon
```
2018-10-23 03:03:07 +02:00
|
|
|
|
2020-10-14 11:52:18 +02:00
|
|
|
// Before actually listening on the gRPC listener, give external
|
|
|
|
// subservers the chance to register to our gRPC server. Those external
|
|
|
|
// subservers (think GrUB) are responsible for starting/stopping on
|
|
|
|
// their own, we just let them register their services to the same
|
|
|
|
// server instance so all of them can be exposed on the same
|
|
|
|
// port/listener.
|
2021-09-23 16:54:39 +02:00
|
|
|
err := r.implCfg.RegisterGrpcSubserver(grpcServer)
|
|
|
|
if err != nil {
|
|
|
|
rpcsLog.Errorf("error registering external gRPC "+
|
|
|
|
"subserver: %v", err)
|
2020-10-14 11:52:18 +02:00
|
|
|
}
|
|
|
|
|
2021-02-02 13:45:29 +01:00
|
|
|
return nil
|
|
|
|
}
|
2019-11-29 11:35:37 +01:00
|
|
|
|
2021-02-02 13:45:29 +01:00
|
|
|
// Start launches any helper goroutines required for the rpcServer to function.
|
|
|
|
func (r *rpcServer) Start() error {
|
|
|
|
if atomic.AddInt32(&r.started, 1) != 1 {
|
|
|
|
return nil
|
lnd+rpc: modify rpcServer to fully manaage listeners and gRPC, handle sub-servers
In this commit, we modify the existing rpcServer to fully manage the
macaroons, gRPC server, and also seek out and create all sub-servers.
With this change, the RPC server gains more responsibility, as it
becomes the "root" server in the hierarchy of gRPC sub-servers.
In addition to creating each sub-server, it will also merge the set of
macaroon permissions for each sub-server, with the permissions of the
rest of the RPC infra. As a result, each sub-server is able to
independently specify what it needs w.r.t macaroon permissions and have
that taken care of by the RPC server. In order to achieve this, we need
to unify the creation of the RPC interceptors, and also fully manage the
gRPC server ourselves.
Some examples with various build configs:
```
⛰i make build
Building debug lnd and lncli.
go build -v -tags="dev" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
unknown flag `signrpc.signermacaroonpath'
unknown flag `signrpc.signermacaroonpath'
⛰i make build tags=signerrpc
Building debug lnd and lncli.
go build -v -tags="dev signerrpc" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev signerrpc" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
2018-10-22 17:31:01.132 [INF] LTND: Version: 0.5.0-beta commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty, build=development, logging=default
2018-10-22 17:31:01.133 [INF] LTND: Active chain: Bitcoin (network=simnet)
2018-10-22 17:31:01.140 [INF] CHDB: Checking for schema update: latest_version=6, db_version=6
2018-10-22 17:31:01.236 [INF] LTND: Primary chain is set to: bitcoin
2018-10-22 17:31:02.391 [INF] LNWL: Opened wallet
2018-10-22 17:31:03.315 [INF] LNWL: The wallet has been unlocked without a time limit
2018-10-22 17:31:03.315 [INF] LTND: LightningWallet opened
2018-10-22 17:31:03.319 [INF] LNWL: Catching up block hashes to height 3060, this will take a while...
2018-10-22 17:31:03.320 [INF] HSWC: Restoring in-memory circuit state from disk
2018-10-22 17:31:03.320 [INF] LNWL: Done catching up block hashes
2018-10-22 17:31:03.320 [INF] HSWC: Payment circuits loaded: num_pending=0, num_open=0
2018-10-22 17:31:03.322 [DBG] LTND: Populating dependencies for sub RPC server: Signrpc
```
As for the config, an example is:
```
[signrpc]
signrpc.signermacaroonpath=~/signer.macaroon
```
2018-10-23 03:03:07 +02:00
|
|
|
}
|
|
|
|
|
2021-02-02 13:45:29 +01:00
|
|
|
// First, we'll start all the sub-servers to ensure that they're ready
|
|
|
|
// to take new requests in.
|
|
|
|
//
|
|
|
|
// TODO(roasbeef): some may require that the entire daemon be started
|
|
|
|
// at that point
|
|
|
|
for _, subServer := range r.subServers {
|
|
|
|
rpcsLog.Debugf("Starting sub RPC server: %v", subServer.Name())
|
|
|
|
|
|
|
|
if err := subServer.Start(); err != nil {
|
2019-05-31 09:59:27 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-02 13:45:29 +01:00
|
|
|
return nil
|
|
|
|
}
|
2019-01-30 09:09:57 +01:00
|
|
|
|
2021-02-02 13:45:29 +01:00
|
|
|
// RegisterWithRestProxy registers the RPC server and any subservers with the
|
|
|
|
// given REST proxy.
|
|
|
|
func (r *rpcServer) RegisterWithRestProxy(restCtx context.Context,
|
|
|
|
restMux *proxy.ServeMux, restDialOpts []grpc.DialOption,
|
|
|
|
restProxyDest string) error {
|
2020-04-02 17:46:33 +02:00
|
|
|
|
2020-04-02 15:12:28 +02:00
|
|
|
// With our custom REST proxy mux created, register our main RPC and
|
|
|
|
// give all subservers a chance to register as well.
|
lnd+rpc: modify rpcServer to fully manaage listeners and gRPC, handle sub-servers
In this commit, we modify the existing rpcServer to fully manage the
macaroons, gRPC server, and also seek out and create all sub-servers.
With this change, the RPC server gains more responsibility, as it
becomes the "root" server in the hierarchy of gRPC sub-servers.
In addition to creating each sub-server, it will also merge the set of
macaroon permissions for each sub-server, with the permissions of the
rest of the RPC infra. As a result, each sub-server is able to
independently specify what it needs w.r.t macaroon permissions and have
that taken care of by the RPC server. In order to achieve this, we need
to unify the creation of the RPC interceptors, and also fully manage the
gRPC server ourselves.
Some examples with various build configs:
```
⛰i make build
Building debug lnd and lncli.
go build -v -tags="dev" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
unknown flag `signrpc.signermacaroonpath'
unknown flag `signrpc.signermacaroonpath'
⛰i make build tags=signerrpc
Building debug lnd and lncli.
go build -v -tags="dev signerrpc" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev signerrpc" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
2018-10-22 17:31:01.132 [INF] LTND: Version: 0.5.0-beta commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty, build=development, logging=default
2018-10-22 17:31:01.133 [INF] LTND: Active chain: Bitcoin (network=simnet)
2018-10-22 17:31:01.140 [INF] CHDB: Checking for schema update: latest_version=6, db_version=6
2018-10-22 17:31:01.236 [INF] LTND: Primary chain is set to: bitcoin
2018-10-22 17:31:02.391 [INF] LNWL: Opened wallet
2018-10-22 17:31:03.315 [INF] LNWL: The wallet has been unlocked without a time limit
2018-10-22 17:31:03.315 [INF] LTND: LightningWallet opened
2018-10-22 17:31:03.319 [INF] LNWL: Catching up block hashes to height 3060, this will take a while...
2018-10-22 17:31:03.320 [INF] HSWC: Restoring in-memory circuit state from disk
2018-10-22 17:31:03.320 [INF] LNWL: Done catching up block hashes
2018-10-22 17:31:03.320 [INF] HSWC: Payment circuits loaded: num_pending=0, num_open=0
2018-10-22 17:31:03.322 [DBG] LTND: Populating dependencies for sub RPC server: Signrpc
```
As for the config, an example is:
```
[signrpc]
signrpc.signermacaroonpath=~/signer.macaroon
```
2018-10-23 03:03:07 +02:00
|
|
|
err := lnrpc.RegisterLightningHandlerFromEndpoint(
|
2021-02-02 13:45:29 +01:00
|
|
|
restCtx, restMux, restProxyDest, restDialOpts,
|
lnd+rpc: modify rpcServer to fully manaage listeners and gRPC, handle sub-servers
In this commit, we modify the existing rpcServer to fully manage the
macaroons, gRPC server, and also seek out and create all sub-servers.
With this change, the RPC server gains more responsibility, as it
becomes the "root" server in the hierarchy of gRPC sub-servers.
In addition to creating each sub-server, it will also merge the set of
macaroon permissions for each sub-server, with the permissions of the
rest of the RPC infra. As a result, each sub-server is able to
independently specify what it needs w.r.t macaroon permissions and have
that taken care of by the RPC server. In order to achieve this, we need
to unify the creation of the RPC interceptors, and also fully manage the
gRPC server ourselves.
Some examples with various build configs:
```
⛰i make build
Building debug lnd and lncli.
go build -v -tags="dev" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
unknown flag `signrpc.signermacaroonpath'
unknown flag `signrpc.signermacaroonpath'
⛰i make build tags=signerrpc
Building debug lnd and lncli.
go build -v -tags="dev signerrpc" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev signerrpc" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
2018-10-22 17:31:01.132 [INF] LTND: Version: 0.5.0-beta commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty, build=development, logging=default
2018-10-22 17:31:01.133 [INF] LTND: Active chain: Bitcoin (network=simnet)
2018-10-22 17:31:01.140 [INF] CHDB: Checking for schema update: latest_version=6, db_version=6
2018-10-22 17:31:01.236 [INF] LTND: Primary chain is set to: bitcoin
2018-10-22 17:31:02.391 [INF] LNWL: Opened wallet
2018-10-22 17:31:03.315 [INF] LNWL: The wallet has been unlocked without a time limit
2018-10-22 17:31:03.315 [INF] LTND: LightningWallet opened
2018-10-22 17:31:03.319 [INF] LNWL: Catching up block hashes to height 3060, this will take a while...
2018-10-22 17:31:03.320 [INF] HSWC: Restoring in-memory circuit state from disk
2018-10-22 17:31:03.320 [INF] LNWL: Done catching up block hashes
2018-10-22 17:31:03.320 [INF] HSWC: Payment circuits loaded: num_pending=0, num_open=0
2018-10-22 17:31:03.322 [DBG] LTND: Populating dependencies for sub RPC server: Signrpc
```
As for the config, an example is:
```
[signrpc]
signrpc.signermacaroonpath=~/signer.macaroon
```
2018-10-23 03:03:07 +02:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-02-02 13:45:29 +01:00
|
|
|
|
2023-08-08 11:43:25 +02:00
|
|
|
// Register our State service with the REST proxy.
|
|
|
|
err = lnrpc.RegisterStateHandlerFromEndpoint(
|
|
|
|
restCtx, restMux, restProxyDest, restDialOpts,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Register all the subservers with the REST proxy.
|
2021-01-29 15:49:48 +01:00
|
|
|
for _, subServer := range r.subGrpcHandlers {
|
2020-04-02 15:12:28 +02:00
|
|
|
err := subServer.RegisterWithRestServer(
|
2021-02-02 13:45:29 +01:00
|
|
|
restCtx, restMux, restProxyDest, restDialOpts,
|
2020-04-02 15:12:28 +02:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to register REST sub-server "+
|
2021-01-29 15:49:48 +01:00
|
|
|
"with root: %v", err)
|
2020-04-02 15:12:28 +02:00
|
|
|
}
|
|
|
|
}
|
2020-05-04 18:15:03 +02:00
|
|
|
|
|
|
|
// Before listening on any of the interfaces, we also want to give the
|
|
|
|
// external subservers a chance to register their own REST proxy stub
|
|
|
|
// with our mux instance.
|
2021-09-23 16:54:39 +02:00
|
|
|
err = r.implCfg.RegisterRestSubserver(
|
|
|
|
restCtx, restMux, restProxyDest, restDialOpts,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
rpcsLog.Errorf("error registering external REST subserver: %v",
|
|
|
|
err)
|
2020-05-04 18:15:03 +02:00
|
|
|
}
|
2016-01-14 06:41:46 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-06-21 20:52:09 +02:00
|
|
|
// Stop signals any active goroutines for a graceful closure.
|
2016-01-17 04:12:36 +01:00
|
|
|
func (r *rpcServer) Stop() error {
|
|
|
|
if atomic.AddInt32(&r.shutdown, 1) != 1 {
|
|
|
|
return nil
|
2016-01-02 05:27:40 +01:00
|
|
|
}
|
2016-01-17 04:12:36 +01:00
|
|
|
|
lnd+rpc: modify rpcServer to fully manaage listeners and gRPC, handle sub-servers
In this commit, we modify the existing rpcServer to fully manage the
macaroons, gRPC server, and also seek out and create all sub-servers.
With this change, the RPC server gains more responsibility, as it
becomes the "root" server in the hierarchy of gRPC sub-servers.
In addition to creating each sub-server, it will also merge the set of
macaroon permissions for each sub-server, with the permissions of the
rest of the RPC infra. As a result, each sub-server is able to
independently specify what it needs w.r.t macaroon permissions and have
that taken care of by the RPC server. In order to achieve this, we need
to unify the creation of the RPC interceptors, and also fully manage the
gRPC server ourselves.
Some examples with various build configs:
```
⛰i make build
Building debug lnd and lncli.
go build -v -tags="dev" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
unknown flag `signrpc.signermacaroonpath'
unknown flag `signrpc.signermacaroonpath'
⛰i make build tags=signerrpc
Building debug lnd and lncli.
go build -v -tags="dev signerrpc" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev signerrpc" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
2018-10-22 17:31:01.132 [INF] LTND: Version: 0.5.0-beta commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty, build=development, logging=default
2018-10-22 17:31:01.133 [INF] LTND: Active chain: Bitcoin (network=simnet)
2018-10-22 17:31:01.140 [INF] CHDB: Checking for schema update: latest_version=6, db_version=6
2018-10-22 17:31:01.236 [INF] LTND: Primary chain is set to: bitcoin
2018-10-22 17:31:02.391 [INF] LNWL: Opened wallet
2018-10-22 17:31:03.315 [INF] LNWL: The wallet has been unlocked without a time limit
2018-10-22 17:31:03.315 [INF] LTND: LightningWallet opened
2018-10-22 17:31:03.319 [INF] LNWL: Catching up block hashes to height 3060, this will take a while...
2018-10-22 17:31:03.320 [INF] HSWC: Restoring in-memory circuit state from disk
2018-10-22 17:31:03.320 [INF] LNWL: Done catching up block hashes
2018-10-22 17:31:03.320 [INF] HSWC: Payment circuits loaded: num_pending=0, num_open=0
2018-10-22 17:31:03.322 [DBG] LTND: Populating dependencies for sub RPC server: Signrpc
```
As for the config, an example is:
```
[signrpc]
signrpc.signermacaroonpath=~/signer.macaroon
```
2018-10-23 03:03:07 +02:00
|
|
|
rpcsLog.Infof("Stopping RPC Server")
|
|
|
|
|
2016-04-25 05:26:32 +02:00
|
|
|
close(r.quit)
|
|
|
|
|
lnd+rpc: modify rpcServer to fully manaage listeners and gRPC, handle sub-servers
In this commit, we modify the existing rpcServer to fully manage the
macaroons, gRPC server, and also seek out and create all sub-servers.
With this change, the RPC server gains more responsibility, as it
becomes the "root" server in the hierarchy of gRPC sub-servers.
In addition to creating each sub-server, it will also merge the set of
macaroon permissions for each sub-server, with the permissions of the
rest of the RPC infra. As a result, each sub-server is able to
independently specify what it needs w.r.t macaroon permissions and have
that taken care of by the RPC server. In order to achieve this, we need
to unify the creation of the RPC interceptors, and also fully manage the
gRPC server ourselves.
Some examples with various build configs:
```
⛰i make build
Building debug lnd and lncli.
go build -v -tags="dev" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
unknown flag `signrpc.signermacaroonpath'
unknown flag `signrpc.signermacaroonpath'
⛰i make build tags=signerrpc
Building debug lnd and lncli.
go build -v -tags="dev signerrpc" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev signerrpc" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
2018-10-22 17:31:01.132 [INF] LTND: Version: 0.5.0-beta commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty, build=development, logging=default
2018-10-22 17:31:01.133 [INF] LTND: Active chain: Bitcoin (network=simnet)
2018-10-22 17:31:01.140 [INF] CHDB: Checking for schema update: latest_version=6, db_version=6
2018-10-22 17:31:01.236 [INF] LTND: Primary chain is set to: bitcoin
2018-10-22 17:31:02.391 [INF] LNWL: Opened wallet
2018-10-22 17:31:03.315 [INF] LNWL: The wallet has been unlocked without a time limit
2018-10-22 17:31:03.315 [INF] LTND: LightningWallet opened
2018-10-22 17:31:03.319 [INF] LNWL: Catching up block hashes to height 3060, this will take a while...
2018-10-22 17:31:03.320 [INF] HSWC: Restoring in-memory circuit state from disk
2018-10-22 17:31:03.320 [INF] LNWL: Done catching up block hashes
2018-10-22 17:31:03.320 [INF] HSWC: Payment circuits loaded: num_pending=0, num_open=0
2018-10-22 17:31:03.322 [DBG] LTND: Populating dependencies for sub RPC server: Signrpc
```
As for the config, an example is:
```
[signrpc]
signrpc.signermacaroonpath=~/signer.macaroon
```
2018-10-23 03:03:07 +02:00
|
|
|
// After we've signalled all of our active goroutines to exit, we'll
|
|
|
|
// then do the same to signal a graceful shutdown of all the sub
|
|
|
|
// servers.
|
|
|
|
for _, subServer := range r.subServers {
|
|
|
|
rpcsLog.Infof("Stopping %v Sub-RPC Server",
|
|
|
|
subServer.Name())
|
|
|
|
|
|
|
|
if err := subServer.Stop(); err != nil {
|
2018-11-02 23:41:38 +01:00
|
|
|
rpcsLog.Errorf("unable to stop sub-server %v: %v",
|
|
|
|
subServer.Name(), err)
|
lnd+rpc: modify rpcServer to fully manaage listeners and gRPC, handle sub-servers
In this commit, we modify the existing rpcServer to fully manage the
macaroons, gRPC server, and also seek out and create all sub-servers.
With this change, the RPC server gains more responsibility, as it
becomes the "root" server in the hierarchy of gRPC sub-servers.
In addition to creating each sub-server, it will also merge the set of
macaroon permissions for each sub-server, with the permissions of the
rest of the RPC infra. As a result, each sub-server is able to
independently specify what it needs w.r.t macaroon permissions and have
that taken care of by the RPC server. In order to achieve this, we need
to unify the creation of the RPC interceptors, and also fully manage the
gRPC server ourselves.
Some examples with various build configs:
```
⛰i make build
Building debug lnd and lncli.
go build -v -tags="dev" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
unknown flag `signrpc.signermacaroonpath'
unknown flag `signrpc.signermacaroonpath'
⛰i make build tags=signerrpc
Building debug lnd and lncli.
go build -v -tags="dev signerrpc" -o lnd-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd
go build -v -tags="dev signerrpc" -o lncli-debug -ldflags "-X github.com/lightningnetwork/lnd/build.Commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty" github.com/lightningnetwork/lnd/cmd/lncli
⛰i ./lnd-debug --debuglevel=debug --signrpc.signermacaroonpath=~/sign.macaroon
2018-10-22 17:31:01.132 [INF] LTND: Version: 0.5.0-beta commit=v0.5-beta-143-gb2069914c4b76109b7c59320dc48f8a5f30deb75-dirty, build=development, logging=default
2018-10-22 17:31:01.133 [INF] LTND: Active chain: Bitcoin (network=simnet)
2018-10-22 17:31:01.140 [INF] CHDB: Checking for schema update: latest_version=6, db_version=6
2018-10-22 17:31:01.236 [INF] LTND: Primary chain is set to: bitcoin
2018-10-22 17:31:02.391 [INF] LNWL: Opened wallet
2018-10-22 17:31:03.315 [INF] LNWL: The wallet has been unlocked without a time limit
2018-10-22 17:31:03.315 [INF] LTND: LightningWallet opened
2018-10-22 17:31:03.319 [INF] LNWL: Catching up block hashes to height 3060, this will take a while...
2018-10-22 17:31:03.320 [INF] HSWC: Restoring in-memory circuit state from disk
2018-10-22 17:31:03.320 [INF] LNWL: Done catching up block hashes
2018-10-22 17:31:03.320 [INF] HSWC: Payment circuits loaded: num_pending=0, num_open=0
2018-10-22 17:31:03.322 [DBG] LTND: Populating dependencies for sub RPC server: Signrpc
```
As for the config, an example is:
```
[signrpc]
signrpc.signermacaroonpath=~/signer.macaroon
```
2018-10-23 03:03:07 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-17 04:12:36 +01:00
|
|
|
return nil
|
2016-01-02 05:27:40 +01:00
|
|
|
}
|
|
|
|
|
2016-06-29 20:31:29 +02:00
|
|
|
// addrPairsToOutputs converts a map describing a set of outputs to be created,
|
|
|
|
// the outputs themselves. The passed map pairs up an address, to a desired
|
|
|
|
// output value amount. Each address is converted to its corresponding pkScript
|
|
|
|
// to be used within the constructed output(s).
|
2020-08-04 20:56:31 +02:00
|
|
|
func addrPairsToOutputs(addrPairs map[string]int64,
|
|
|
|
params *chaincfg.Params) ([]*wire.TxOut, error) {
|
|
|
|
|
2016-06-29 20:31:29 +02:00
|
|
|
outputs := make([]*wire.TxOut, 0, len(addrPairs))
|
|
|
|
for addr, amt := range addrPairs {
|
2020-08-04 20:56:31 +02:00
|
|
|
addr, err := btcutil.DecodeAddress(addr, params)
|
2016-03-23 02:48:46 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-04-23 04:03:08 +02:00
|
|
|
if !addr.IsForNet(params) {
|
|
|
|
return nil, fmt.Errorf("address is not for %s",
|
|
|
|
params.Name)
|
|
|
|
}
|
|
|
|
|
2016-03-23 02:48:46 +01:00
|
|
|
pkscript, err := txscript.PayToAddrScript(addr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
outputs = append(outputs, wire.NewTxOut(amt, pkscript))
|
2015-12-30 00:09:38 +01:00
|
|
|
}
|
|
|
|
|
2016-06-29 20:31:29 +02:00
|
|
|
return outputs, nil
|
|
|
|
}
|
|
|
|
|
2020-04-06 15:16:02 +02:00
|
|
|
// allowCORS wraps the given http.Handler with a function that adds the
|
|
|
|
// Access-Control-Allow-Origin header to the response.
|
|
|
|
func allowCORS(handler http.Handler, origins []string) http.Handler {
|
|
|
|
allowHeaders := "Access-Control-Allow-Headers"
|
|
|
|
allowMethods := "Access-Control-Allow-Methods"
|
|
|
|
allowOrigin := "Access-Control-Allow-Origin"
|
|
|
|
|
2020-08-20 06:06:28 +02:00
|
|
|
// If the user didn't supply any origins that means CORS is disabled
|
|
|
|
// and we should return the original handler.
|
|
|
|
if len(origins) == 0 {
|
|
|
|
return handler
|
|
|
|
}
|
|
|
|
|
2020-04-06 15:16:02 +02:00
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
origin := r.Header.Get("Origin")
|
|
|
|
|
|
|
|
// Skip everything if the browser doesn't send the Origin field.
|
|
|
|
if origin == "" {
|
|
|
|
handler.ServeHTTP(w, r)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the static header fields first.
|
|
|
|
w.Header().Set(
|
|
|
|
allowHeaders,
|
|
|
|
"Content-Type, Accept, Grpc-Metadata-Macaroon",
|
|
|
|
)
|
|
|
|
w.Header().Set(allowMethods, "GET, POST, DELETE")
|
|
|
|
|
|
|
|
// Either we allow all origins or the incoming request matches
|
|
|
|
// a specific origin in our list of allowed origins.
|
|
|
|
for _, allowedOrigin := range origins {
|
|
|
|
if allowedOrigin == "*" || origin == allowedOrigin {
|
|
|
|
// Only set allowed origin to requested origin.
|
|
|
|
w.Header().Set(allowOrigin, origin)
|
|
|
|
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// For a pre-flight request we only need to send the headers
|
|
|
|
// back. No need to call the rest of the chain.
|
|
|
|
if r.Method == "OPTIONS" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Everything's prepared now, we can pass the request along the
|
|
|
|
// chain of handlers.
|
|
|
|
handler.ServeHTTP(w, r)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2016-06-29 20:31:29 +02:00
|
|
|
// sendCoinsOnChain makes an on-chain transaction in or to send coins to one or
|
|
|
|
// more addresses specified in the passed payment map. The payment map maps an
|
|
|
|
// address to a specified output value to be sent to that address.
|
2017-11-23 07:57:23 +01:00
|
|
|
func (r *rpcServer) sendCoinsOnChain(paymentMap map[string]int64,
|
2024-03-26 18:08:27 +01:00
|
|
|
feeRate chainfee.SatPerKWeight, minConfs int32, label string,
|
|
|
|
strategy wallet.CoinSelectionStrategy) (*chainhash.Hash, error) {
|
2017-11-23 07:57:23 +01:00
|
|
|
|
2020-08-04 20:56:31 +02:00
|
|
|
outputs, err := addrPairsToOutputs(paymentMap, r.cfg.ActiveNetParams.Params)
|
2016-06-29 20:31:29 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-01-10 19:54:49 +01:00
|
|
|
// We first do a dry run, to sanity check we won't spend our wallet
|
|
|
|
// balance below the reserved amount.
|
|
|
|
authoredTx, err := r.server.cc.Wallet.CreateSimpleTx(
|
2024-03-26 18:08:27 +01:00
|
|
|
outputs, feeRate, minConfs, strategy, true,
|
2021-01-10 19:54:49 +01:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-09-30 13:02:35 +02:00
|
|
|
// Check the authored transaction and use the explicitly set change index
|
|
|
|
// to make sure that the wallet reserved balance is not invalidated.
|
|
|
|
_, err = r.server.cc.Wallet.CheckReservedValueTx(
|
|
|
|
lnwallet.CheckReservedValueTxReq{
|
|
|
|
Tx: authoredTx.Tx,
|
|
|
|
ChangeIndex: &authoredTx.ChangeIndex,
|
|
|
|
},
|
|
|
|
)
|
2021-01-10 19:54:49 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-10-14 15:42:52 +02:00
|
|
|
// If that checks out, we're fairly confident that creating sending to
|
2021-01-10 19:54:49 +01:00
|
|
|
// these outputs will keep the wallet balance above the reserve.
|
|
|
|
tx, err := r.server.cc.Wallet.SendOutputs(
|
2024-03-26 18:08:27 +01:00
|
|
|
outputs, feeRate, minConfs, label, strategy,
|
2021-01-10 19:54:49 +01:00
|
|
|
)
|
2018-11-05 12:30:32 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
txHash := tx.TxHash()
|
2018-11-18 05:44:34 +01:00
|
|
|
return &txHash, nil
|
2016-06-29 20:31:29 +02:00
|
|
|
}
|
|
|
|
|
2022-01-21 22:55:32 +01:00
|
|
|
// ListUnspent returns useful information about each unspent output owned by
|
|
|
|
// the wallet, as reported by the underlying `ListUnspentWitness`; the
|
|
|
|
// information returned is: outpoint, amount in satoshis, address, address
|
|
|
|
// type, scriptPubKey in hex and number of confirmations. The result is
|
|
|
|
// filtered to contain outputs whose number of confirmations is between a
|
|
|
|
// minimum and maximum number of confirmations specified by the user, with
|
|
|
|
// 0 meaning unconfirmed.
|
2018-09-27 15:49:44 +02:00
|
|
|
func (r *rpcServer) ListUnspent(ctx context.Context,
|
|
|
|
in *lnrpc.ListUnspentRequest) (*lnrpc.ListUnspentResponse, error) {
|
2018-12-12 01:29:11 +01:00
|
|
|
|
2020-05-21 01:12:39 +02:00
|
|
|
// Validate the confirmation arguments.
|
|
|
|
minConfs, maxConfs, err := lnrpc.ParseConfs(in.MinConfs, in.MaxConfs)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2018-09-27 15:49:44 +02:00
|
|
|
}
|
|
|
|
|
2018-12-12 01:29:11 +01:00
|
|
|
// With our arguments validated, we'll query the internal wallet for
|
|
|
|
// the set of UTXOs that match our query.
|
2020-06-10 04:37:17 +02:00
|
|
|
//
|
|
|
|
// We'll acquire the global coin selection lock to ensure there aren't
|
|
|
|
// any other concurrent processes attempting to lock any UTXOs which may
|
|
|
|
// be shown available to us.
|
|
|
|
var utxos []*lnwallet.Utxo
|
2020-10-06 17:03:42 +02:00
|
|
|
err = r.server.cc.Wallet.WithCoinSelectLock(func() error {
|
|
|
|
utxos, err = r.server.cc.Wallet.ListUnspentWitness(
|
2021-02-20 02:41:53 +01:00
|
|
|
minConfs, maxConfs, in.Account,
|
2020-06-10 04:37:17 +02:00
|
|
|
)
|
|
|
|
return err
|
|
|
|
})
|
2018-09-27 15:49:44 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-08-04 20:56:31 +02:00
|
|
|
rpcUtxos, err := lnrpc.MarshalUtxos(utxos, r.cfg.ActiveNetParams.Params)
|
2020-05-21 01:12:39 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2018-09-27 15:49:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
maxStr := ""
|
|
|
|
if maxConfs != math.MaxInt32 {
|
|
|
|
maxStr = " max=" + fmt.Sprintf("%d", maxConfs)
|
|
|
|
}
|
2018-12-12 01:29:11 +01:00
|
|
|
|
|
|
|
rpcsLog.Debugf("[listunspent] min=%v%v, generated utxos: %v", minConfs,
|
|
|
|
maxStr, utxos)
|
2018-09-27 15:49:44 +02:00
|
|
|
|
2020-05-21 01:12:39 +02:00
|
|
|
return &lnrpc.ListUnspentResponse{
|
|
|
|
Utxos: rpcUtxos,
|
|
|
|
}, nil
|
2018-09-27 15:49:44 +02:00
|
|
|
}
|
|
|
|
|
2019-03-05 14:22:30 +01:00
|
|
|
// EstimateFee handles a request for estimating the fee for sending a
|
|
|
|
// transaction spending to multiple specified outputs in parallel.
|
|
|
|
func (r *rpcServer) EstimateFee(ctx context.Context,
|
|
|
|
in *lnrpc.EstimateFeeRequest) (*lnrpc.EstimateFeeResponse, error) {
|
|
|
|
|
|
|
|
// Create the list of outputs we are spending to.
|
2020-08-04 20:56:31 +02:00
|
|
|
outputs, err := addrPairsToOutputs(in.AddrToAmount, r.cfg.ActiveNetParams.Params)
|
2019-03-05 14:22:30 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Query the fee estimator for the fee rate for the given confirmation
|
|
|
|
// target.
|
|
|
|
target := in.TargetConf
|
2023-11-03 12:20:10 +01:00
|
|
|
feePref := sweep.FeeEstimateInfo{
|
2023-11-03 11:48:09 +01:00
|
|
|
ConfTarget: uint32(target),
|
|
|
|
}
|
|
|
|
|
|
|
|
// Since we are providing a fee estimation as an RPC response, there's
|
|
|
|
// no need to set a max feerate here, so we use 0.
|
|
|
|
feePerKw, err := feePref.Estimate(r.server.cc.FeeEstimator, 0)
|
2019-03-05 14:22:30 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-04-22 19:25:50 +02:00
|
|
|
// Then, we'll extract the minimum number of confirmations that each
|
|
|
|
// output we use to fund the transaction should satisfy.
|
|
|
|
minConfs, err := lnrpc.ExtractMinConfs(
|
|
|
|
in.GetMinConfs(), in.GetSpendUnconfirmed(),
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2024-03-26 18:08:27 +01:00
|
|
|
coinSelectionStrategy, err := lnrpc.UnmarshallCoinSelectionStrategy(
|
|
|
|
in.CoinSelectionStrategy,
|
|
|
|
r.server.cc.Wallet.Cfg.CoinSelectionStrategy,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-03-05 14:22:30 +01:00
|
|
|
// We will ask the wallet to create a tx using this fee rate. We set
|
|
|
|
// dryRun=true to avoid inflating the change addresses in the db.
|
|
|
|
var tx *txauthor.AuthoredTx
|
2020-10-06 17:03:42 +02:00
|
|
|
wallet := r.server.cc.Wallet
|
2019-03-05 14:22:30 +01:00
|
|
|
err = wallet.WithCoinSelectLock(func() error {
|
2024-03-26 18:08:27 +01:00
|
|
|
tx, err = wallet.CreateSimpleTx(
|
|
|
|
outputs, feePerKw, minConfs, coinSelectionStrategy,
|
|
|
|
true,
|
|
|
|
)
|
2019-03-05 14:22:30 +01:00
|
|
|
return err
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Use the created tx to calculate the total fee.
|
|
|
|
totalOutput := int64(0)
|
|
|
|
for _, out := range tx.Tx.TxOut {
|
|
|
|
totalOutput += out.Value
|
|
|
|
}
|
|
|
|
totalFee := int64(tx.TotalInput) - totalOutput
|
|
|
|
|
|
|
|
resp := &lnrpc.EstimateFeeResponse{
|
2021-03-11 01:29:50 +01:00
|
|
|
FeeSat: totalFee,
|
2023-10-13 09:13:50 +02:00
|
|
|
SatPerVbyte: uint64(feePerKw.FeePerVByte()),
|
2021-03-11 01:29:50 +01:00
|
|
|
|
|
|
|
// Deprecated field.
|
2023-10-13 09:13:50 +02:00
|
|
|
FeerateSatPerByte: int64(feePerKw.FeePerVByte()),
|
2019-03-05 14:22:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
rpcsLog.Debugf("[estimatefee] fee estimate for conf target %d: %v",
|
|
|
|
target, resp)
|
|
|
|
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
2024-04-25 20:34:25 +02:00
|
|
|
// maybeUseDefaultConf makes sure that when the user doesn't set either the fee
|
|
|
|
// rate or conf target, the default conf target is used.
|
|
|
|
func maybeUseDefaultConf(satPerByte int64, satPerVByte uint64,
|
|
|
|
targetConf uint32) uint32 {
|
|
|
|
|
|
|
|
// If the fee rate is set, there's no need to use the default conf
|
|
|
|
// target. In this case, we just return the targetConf from the
|
|
|
|
// request.
|
|
|
|
if satPerByte != 0 || satPerVByte != 0 {
|
|
|
|
return targetConf
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return the user specified conf target if set.
|
|
|
|
if targetConf != 0 {
|
|
|
|
return targetConf
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the fee rate is not set, yet the conf target is zero, the default
|
|
|
|
// 6 will be returned.
|
|
|
|
rpcsLog.Errorf("Expected either 'sat_per_vbyte' or 'conf_target' to " +
|
|
|
|
"be set, using default conf of 6 instead")
|
|
|
|
|
|
|
|
return defaultNumBlocksEstimate
|
|
|
|
}
|
|
|
|
|
2016-06-29 20:31:29 +02:00
|
|
|
// SendCoins executes a request to send coins to a particular address. Unlike
|
|
|
|
// SendMany, this RPC call only allows creating a single output at a time.
|
|
|
|
func (r *rpcServer) SendCoins(ctx context.Context,
|
|
|
|
in *lnrpc.SendCoinsRequest) (*lnrpc.SendCoinsResponse, error) {
|
2017-08-22 08:25:41 +02:00
|
|
|
|
2024-04-25 20:34:25 +02:00
|
|
|
// Keep the old behavior prior to 0.18.0 - when the user doesn't set
|
|
|
|
// fee rate or conf target, the default conf target of 6 is used.
|
|
|
|
targetConf := maybeUseDefaultConf(
|
|
|
|
in.SatPerByte, in.SatPerVbyte, uint32(in.TargetConf),
|
|
|
|
)
|
|
|
|
|
2021-03-11 01:29:50 +01:00
|
|
|
// Calculate an appropriate fee rate for this transaction.
|
2023-04-28 01:33:21 +02:00
|
|
|
feePerKw, err := lnrpc.CalculateFeeRate(
|
2021-04-30 12:35:53 +02:00
|
|
|
uint64(in.SatPerByte), in.SatPerVbyte, // nolint:staticcheck
|
2024-04-25 20:34:25 +02:00
|
|
|
targetConf, r.server.cc.FeeEstimator,
|
2017-11-23 07:57:23 +01:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-09-27 17:48:15 +02:00
|
|
|
// Then, we'll extract the minimum number of confirmations that each
|
|
|
|
// output we use to fund the transaction should satisfy.
|
|
|
|
minConfs, err := lnrpc.ExtractMinConfs(in.MinConfs, in.SpendUnconfirmed)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
rpcsLog.Infof("[sendcoins] addr=%v, amt=%v, sat/kw=%v, min_confs=%v, "+
|
2020-12-07 22:45:11 +01:00
|
|
|
"send_all=%v",
|
2020-09-27 17:48:15 +02:00
|
|
|
in.Addr, btcutil.Amount(in.Amount), int64(feePerKw), minConfs,
|
2018-11-18 06:10:32 +01:00
|
|
|
in.SendAll)
|
2016-06-29 20:31:29 +02:00
|
|
|
|
2019-02-27 20:16:34 +01:00
|
|
|
// Decode the address receiving the coins, we need to check whether the
|
|
|
|
// address is valid for this network.
|
2020-08-04 20:56:31 +02:00
|
|
|
targetAddr, err := btcutil.DecodeAddress(
|
|
|
|
in.Addr, r.cfg.ActiveNetParams.Params,
|
|
|
|
)
|
2019-02-27 20:16:34 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make the check on the decoded address according to the active network.
|
2020-08-04 20:56:31 +02:00
|
|
|
if !targetAddr.IsForNet(r.cfg.ActiveNetParams.Params) {
|
2019-02-27 20:16:34 +01:00
|
|
|
return nil, fmt.Errorf("address: %v is not valid for this "+
|
|
|
|
"network: %v", targetAddr.String(),
|
2020-08-04 20:56:31 +02:00
|
|
|
r.cfg.ActiveNetParams.Params.Name)
|
2019-02-27 20:16:34 +01:00
|
|
|
}
|
|
|
|
|
2019-04-10 04:45:55 +02:00
|
|
|
// If the destination address parses to a valid pubkey, we assume the user
|
2019-05-05 00:35:37 +02:00
|
|
|
// accidentally tried to send funds to a bare pubkey address. This check is
|
2019-04-10 04:45:55 +02:00
|
|
|
// here to prevent unintended transfers.
|
|
|
|
decodedAddr, _ := hex.DecodeString(in.Addr)
|
2022-02-23 14:48:00 +01:00
|
|
|
_, err = btcec.ParsePubKey(decodedAddr)
|
2019-04-10 04:45:55 +02:00
|
|
|
if err == nil {
|
|
|
|
return nil, fmt.Errorf("cannot send coins to pubkeys")
|
|
|
|
}
|
|
|
|
|
2020-05-18 14:13:24 +02:00
|
|
|
label, err := labels.ValidateAPI(in.Label)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2024-03-26 18:08:27 +01:00
|
|
|
coinSelectionStrategy, err := lnrpc.UnmarshallCoinSelectionStrategy(
|
|
|
|
in.CoinSelectionStrategy,
|
|
|
|
r.server.cc.Wallet.Cfg.CoinSelectionStrategy,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-11-18 06:10:32 +01:00
|
|
|
var txid *chainhash.Hash
|
|
|
|
|
2020-10-06 17:03:42 +02:00
|
|
|
wallet := r.server.cc.Wallet
|
2023-08-22 05:06:51 +02:00
|
|
|
maxFeeRate := r.cfg.Sweeper.MaxFeeRate.FeePerKWeight()
|
2018-11-18 06:10:32 +01:00
|
|
|
|
|
|
|
// If the send all flag is active, then we'll attempt to sweep all the
|
|
|
|
// coins in the wallet in a single transaction (if possible),
|
|
|
|
// otherwise, we'll respect the amount, and attempt a regular 2-output
|
|
|
|
// send.
|
|
|
|
if in.SendAll {
|
|
|
|
// At this point, the amount shouldn't be set since we've been
|
|
|
|
// instructed to sweep all the coins from the wallet.
|
|
|
|
if in.Amount != 0 {
|
|
|
|
return nil, fmt.Errorf("amount set while SendAll is " +
|
|
|
|
"active")
|
|
|
|
}
|
2016-06-29 20:31:29 +02:00
|
|
|
|
2020-10-06 17:03:42 +02:00
|
|
|
_, bestHeight, err := r.server.cc.ChainIO.GetBestBlock()
|
2018-11-18 06:10:32 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// With the sweeper instance created, we can now generate a
|
|
|
|
// transaction that will sweep ALL outputs from the wallet in a
|
|
|
|
// single transaction. This will be generated in a concurrent
|
2021-01-10 19:54:49 +01:00
|
|
|
// safe manner, so no need to worry about locking. The tx will
|
|
|
|
// pay to the change address created above if we needed to
|
|
|
|
// reserve any value, the rest will go to targetAddr.
|
2018-11-18 06:10:32 +01:00
|
|
|
sweepTxPkg, err := sweep.CraftSweepAllTx(
|
2024-04-01 21:00:54 +02:00
|
|
|
feePerKw, maxFeeRate, uint32(bestHeight), nil,
|
2023-08-22 05:06:51 +02:00
|
|
|
targetAddr, wallet, wallet, wallet.WalletController,
|
2023-08-22 06:42:02 +02:00
|
|
|
r.server.cc.Signer, minConfs,
|
2018-11-18 06:10:32 +01:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-01-10 19:54:49 +01:00
|
|
|
// Before we publish the transaction we make sure it won't
|
|
|
|
// violate our reserved wallet value.
|
|
|
|
var reservedVal btcutil.Amount
|
|
|
|
err = wallet.WithCoinSelectLock(func() error {
|
|
|
|
var err error
|
|
|
|
reservedVal, err = wallet.CheckReservedValueTx(
|
2021-09-30 13:02:35 +02:00
|
|
|
lnwallet.CheckReservedValueTxReq{
|
|
|
|
Tx: sweepTxPkg.SweepTx,
|
|
|
|
},
|
2021-01-10 19:54:49 +01:00
|
|
|
)
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
|
|
|
|
// If sending everything to this address would invalidate our
|
|
|
|
// reserved wallet balance, we create a new sweep tx, where
|
|
|
|
// we'll send the reserved value back to our wallet.
|
|
|
|
if err == lnwallet.ErrReservedValueInvalidated {
|
|
|
|
sweepTxPkg.CancelSweepAttempt()
|
|
|
|
|
|
|
|
rpcsLog.Debugf("Reserved value %v not satisfied after "+
|
|
|
|
"send_all, trying with change output",
|
|
|
|
reservedVal)
|
|
|
|
|
|
|
|
// We'll request a change address from the wallet,
|
|
|
|
// where we'll send this reserved value back to. This
|
|
|
|
// ensures this is an address the wallet knows about,
|
|
|
|
// allowing us to pass the reserved value check.
|
|
|
|
changeAddr, err := r.server.cc.Wallet.NewAddress(
|
2022-08-11 03:32:12 +02:00
|
|
|
lnwallet.TaprootPubkey, true,
|
2021-02-20 02:41:45 +01:00
|
|
|
lnwallet.DefaultAccountName,
|
2021-01-10 19:54:49 +01:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send the reserved value to this change address, the
|
|
|
|
// remaining funds will go to the targetAddr.
|
|
|
|
outputs := []sweep.DeliveryAddr{
|
|
|
|
{
|
|
|
|
Addr: changeAddr,
|
|
|
|
Amt: reservedVal,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
sweepTxPkg, err = sweep.CraftSweepAllTx(
|
2024-04-01 21:00:54 +02:00
|
|
|
feePerKw, maxFeeRate, uint32(bestHeight),
|
2023-08-22 05:06:51 +02:00
|
|
|
outputs, targetAddr, wallet, wallet,
|
2021-09-23 21:40:37 +02:00
|
|
|
wallet.WalletController,
|
2023-08-22 06:42:02 +02:00
|
|
|
r.server.cc.Signer, minConfs,
|
2021-01-10 19:54:49 +01:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sanity check the new tx by re-doing the check.
|
|
|
|
err = wallet.WithCoinSelectLock(func() error {
|
|
|
|
_, err := wallet.CheckReservedValueTx(
|
2021-09-30 13:02:35 +02:00
|
|
|
lnwallet.CheckReservedValueTxReq{
|
|
|
|
Tx: sweepTxPkg.SweepTx,
|
|
|
|
},
|
2021-01-10 19:54:49 +01:00
|
|
|
)
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
sweepTxPkg.CancelSweepAttempt()
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
} else if err != nil {
|
|
|
|
sweepTxPkg.CancelSweepAttempt()
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-11-18 06:10:32 +01:00
|
|
|
rpcsLog.Debugf("Sweeping all coins from wallet to addr=%v, "+
|
|
|
|
"with tx=%v", in.Addr, spew.Sdump(sweepTxPkg.SweepTx))
|
|
|
|
|
|
|
|
// As our sweep transaction was created, successfully, we'll
|
|
|
|
// now attempt to publish it, cancelling the sweep pkg to
|
|
|
|
// return all outputs if it fails.
|
2020-05-18 14:13:24 +02:00
|
|
|
err = wallet.PublishTransaction(sweepTxPkg.SweepTx, label)
|
2018-11-18 06:10:32 +01:00
|
|
|
if err != nil {
|
|
|
|
sweepTxPkg.CancelSweepAttempt()
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("unable to broadcast sweep "+
|
|
|
|
"transaction: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
sweepTXID := sweepTxPkg.SweepTx.TxHash()
|
|
|
|
txid = &sweepTXID
|
|
|
|
} else {
|
|
|
|
|
|
|
|
// We'll now construct out payment map, and use the wallet's
|
|
|
|
// coin selection synchronization method to ensure that no coin
|
|
|
|
// selection (funding, sweep alls, other sends) can proceed
|
|
|
|
// while we instruct the wallet to send this transaction.
|
2019-02-27 20:16:34 +01:00
|
|
|
paymentMap := map[string]int64{targetAddr.String(): in.Amount}
|
2018-11-18 06:10:32 +01:00
|
|
|
err := wallet.WithCoinSelectLock(func() error {
|
2020-05-18 14:13:24 +02:00
|
|
|
newTXID, err := r.sendCoinsOnChain(
|
2024-03-26 18:08:27 +01:00
|
|
|
paymentMap, feePerKw, minConfs,
|
|
|
|
label, coinSelectionStrategy,
|
2020-05-18 14:13:24 +02:00
|
|
|
)
|
2018-11-18 06:10:32 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
txid = newTXID
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-06-29 20:31:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
rpcsLog.Infof("[sendcoins] spend generated txid: %v", txid.String())
|
|
|
|
|
|
|
|
return &lnrpc.SendCoinsResponse{Txid: txid.String()}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// SendMany handles a request for a transaction create multiple specified
|
|
|
|
// outputs in parallel.
|
|
|
|
func (r *rpcServer) SendMany(ctx context.Context,
|
|
|
|
in *lnrpc.SendManyRequest) (*lnrpc.SendManyResponse, error) {
|
2017-08-22 08:25:41 +02:00
|
|
|
|
2024-04-25 20:34:25 +02:00
|
|
|
// Keep the old behavior prior to 0.18.0 - when the user doesn't set
|
|
|
|
// fee rate or conf target, the default conf target of 6 is used.
|
|
|
|
targetConf := maybeUseDefaultConf(
|
|
|
|
in.SatPerByte, in.SatPerVbyte, uint32(in.TargetConf),
|
|
|
|
)
|
|
|
|
|
2021-03-11 01:29:50 +01:00
|
|
|
// Calculate an appropriate fee rate for this transaction.
|
2023-04-28 01:33:21 +02:00
|
|
|
feePerKw, err := lnrpc.CalculateFeeRate(
|
2021-04-30 12:35:53 +02:00
|
|
|
uint64(in.SatPerByte), in.SatPerVbyte, // nolint:staticcheck
|
2024-04-25 20:34:25 +02:00
|
|
|
targetConf, r.server.cc.FeeEstimator,
|
2017-11-23 07:57:23 +01:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-09-27 17:48:15 +02:00
|
|
|
// Then, we'll extract the minimum number of confirmations that each
|
|
|
|
// output we use to fund the transaction should satisfy.
|
|
|
|
minConfs, err := lnrpc.ExtractMinConfs(in.MinConfs, in.SpendUnconfirmed)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-05-18 14:13:24 +02:00
|
|
|
label, err := labels.ValidateAPI(in.Label)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2024-03-26 18:08:27 +01:00
|
|
|
coinSelectionStrategy, err := lnrpc.UnmarshallCoinSelectionStrategy(
|
|
|
|
in.CoinSelectionStrategy,
|
|
|
|
r.server.cc.Wallet.Cfg.CoinSelectionStrategy,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-07-28 03:39:38 +02:00
|
|
|
rpcsLog.Infof("[sendmany] outputs=%v, sat/kw=%v",
|
|
|
|
spew.Sdump(in.AddrToAmount), int64(feePerKw))
|
2017-11-23 07:57:23 +01:00
|
|
|
|
2018-11-18 06:06:59 +01:00
|
|
|
var txid *chainhash.Hash
|
|
|
|
|
|
|
|
// We'll attempt to send to the target set of outputs, ensuring that we
|
|
|
|
// synchronize with any other ongoing coin selection attempts which
|
|
|
|
// happen to also be concurrently executing.
|
2020-10-06 17:03:42 +02:00
|
|
|
wallet := r.server.cc.Wallet
|
2018-11-18 06:06:59 +01:00
|
|
|
err = wallet.WithCoinSelectLock(func() error {
|
|
|
|
sendManyTXID, err := r.sendCoinsOnChain(
|
2024-03-26 18:08:27 +01:00
|
|
|
in.AddrToAmount, feePerKw, minConfs,
|
|
|
|
label, coinSelectionStrategy,
|
2018-11-18 06:06:59 +01:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
txid = sendManyTXID
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
2015-12-30 00:09:38 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-06-29 20:31:29 +02:00
|
|
|
rpcsLog.Infof("[sendmany] spend generated txid: %v", txid.String())
|
2016-04-25 05:26:32 +02:00
|
|
|
|
|
|
|
return &lnrpc.SendManyResponse{Txid: txid.String()}, nil
|
2015-12-30 00:09:38 +01:00
|
|
|
}
|
|
|
|
|
2016-06-21 20:52:09 +02:00
|
|
|
// NewAddress creates a new address under control of the local wallet.
|
2016-04-25 05:26:32 +02:00
|
|
|
func (r *rpcServer) NewAddress(ctx context.Context,
|
|
|
|
in *lnrpc.NewAddressRequest) (*lnrpc.NewAddressResponse, error) {
|
2017-08-22 08:25:41 +02:00
|
|
|
|
2021-02-20 02:41:53 +01:00
|
|
|
// Always use the default wallet account unless one was specified.
|
|
|
|
account := lnwallet.DefaultAccountName
|
|
|
|
if in.Account != "" {
|
|
|
|
account = in.Account
|
|
|
|
}
|
|
|
|
|
2016-04-25 05:26:32 +02:00
|
|
|
// Translate the gRPC proto address type to the wallet controller's
|
|
|
|
// available address types.
|
2019-02-20 04:17:46 +01:00
|
|
|
var (
|
|
|
|
addr btcutil.Address
|
|
|
|
err error
|
|
|
|
)
|
2016-04-25 05:26:32 +02:00
|
|
|
switch in.Type {
|
2018-09-27 15:49:44 +02:00
|
|
|
case lnrpc.AddressType_WITNESS_PUBKEY_HASH:
|
2020-10-06 17:03:42 +02:00
|
|
|
addr, err = r.server.cc.Wallet.NewAddress(
|
2021-02-20 02:41:53 +01:00
|
|
|
lnwallet.WitnessPubKey, false, account,
|
2019-02-20 04:17:46 +01:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-09-27 15:49:44 +02:00
|
|
|
case lnrpc.AddressType_NESTED_PUBKEY_HASH:
|
2020-10-06 17:03:42 +02:00
|
|
|
addr, err = r.server.cc.Wallet.NewAddress(
|
2021-02-20 02:41:53 +01:00
|
|
|
lnwallet.NestedWitnessPubKey, false, account,
|
2019-02-20 04:17:46 +01:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-04-25 05:26:32 +02:00
|
|
|
|
2022-03-18 18:37:46 +01:00
|
|
|
case lnrpc.AddressType_TAPROOT_PUBKEY:
|
|
|
|
addr, err = r.server.cc.Wallet.NewAddress(
|
|
|
|
lnwallet.TaprootPubkey, false, account,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-02-20 04:17:46 +01:00
|
|
|
case lnrpc.AddressType_UNUSED_WITNESS_PUBKEY_HASH:
|
2020-10-06 17:03:42 +02:00
|
|
|
addr, err = r.server.cc.Wallet.LastUnusedAddress(
|
2021-02-20 02:41:53 +01:00
|
|
|
lnwallet.WitnessPubKey, account,
|
2019-02-20 04:17:46 +01:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
case lnrpc.AddressType_UNUSED_NESTED_PUBKEY_HASH:
|
2020-10-06 17:03:42 +02:00
|
|
|
addr, err = r.server.cc.Wallet.LastUnusedAddress(
|
2021-02-20 02:41:53 +01:00
|
|
|
lnwallet.NestedWitnessPubKey, account,
|
2019-02-20 04:17:46 +01:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-03-18 18:37:46 +01:00
|
|
|
|
|
|
|
case lnrpc.AddressType_UNUSED_TAPROOT_PUBKEY:
|
|
|
|
addr, err = r.server.cc.Wallet.LastUnusedAddress(
|
|
|
|
lnwallet.TaprootPubkey, account,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-08-22 16:11:22 +02:00
|
|
|
|
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("unknown address type: %v", in.Type)
|
2015-12-30 00:09:38 +01:00
|
|
|
}
|
|
|
|
|
2021-02-20 02:41:53 +01:00
|
|
|
rpcsLog.Debugf("[newaddress] account=%v type=%v addr=%v", account,
|
|
|
|
in.Type, addr.String())
|
2015-12-30 00:09:38 +01:00
|
|
|
return &lnrpc.NewAddressResponse{Address: addr.String()}, nil
|
|
|
|
}
|
2015-12-31 04:56:57 +01:00
|
|
|
|
2018-04-26 04:45:26 +02:00
|
|
|
var (
|
|
|
|
// signedMsgPrefix is a special prefix that we'll prepend to any
|
|
|
|
// messages we sign/verify. We do this to ensure that we don't
|
|
|
|
// accidentally sign a sighash, or other sensitive material. By
|
|
|
|
// prepending this fragment, we mind message signing to our particular
|
|
|
|
// context.
|
|
|
|
signedMsgPrefix = []byte("Lightning Signed Message:")
|
|
|
|
)
|
|
|
|
|
2017-04-20 04:28:10 +02:00
|
|
|
// SignMessage signs a message with the resident node's private key. The
|
2018-04-26 04:45:26 +02:00
|
|
|
// returned signature string is zbase32 encoded and pubkey recoverable, meaning
|
|
|
|
// that only the message digest and signature are needed for verification.
|
2021-09-23 16:54:27 +02:00
|
|
|
func (r *rpcServer) SignMessage(_ context.Context,
|
2017-04-20 04:28:10 +02:00
|
|
|
in *lnrpc.SignMessageRequest) (*lnrpc.SignMessageResponse, error) {
|
2017-08-22 08:25:41 +02:00
|
|
|
|
2017-04-20 04:28:10 +02:00
|
|
|
if in.Msg == nil {
|
|
|
|
return nil, fmt.Errorf("need a message to sign")
|
|
|
|
}
|
|
|
|
|
2018-04-26 04:45:26 +02:00
|
|
|
in.Msg = append(signedMsgPrefix, in.Msg...)
|
2021-09-23 16:54:27 +02:00
|
|
|
sigBytes, err := r.server.nodeSigner.SignMessageCompact(
|
|
|
|
in.Msg, !in.SingleHash,
|
|
|
|
)
|
2017-04-20 04:28:10 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
sig := zbase32.EncodeToString(sigBytes)
|
|
|
|
return &lnrpc.SignMessageResponse{Signature: sig}, nil
|
|
|
|
}
|
|
|
|
|
2018-04-26 04:45:26 +02:00
|
|
|
// VerifyMessage verifies a signature over a msg. The signature must be zbase32
|
|
|
|
// encoded and signed by an active node in the resident node's channel
|
|
|
|
// database. In addition to returning the validity of the signature,
|
2017-04-29 13:44:29 +02:00
|
|
|
// VerifyMessage also returns the recovered pubkey from the signature.
|
2017-04-20 04:28:10 +02:00
|
|
|
func (r *rpcServer) VerifyMessage(ctx context.Context,
|
|
|
|
in *lnrpc.VerifyMessageRequest) (*lnrpc.VerifyMessageResponse, error) {
|
2017-08-22 08:25:41 +02:00
|
|
|
|
2017-04-20 04:28:10 +02:00
|
|
|
if in.Msg == nil {
|
|
|
|
return nil, fmt.Errorf("need a message to verify")
|
|
|
|
}
|
|
|
|
|
|
|
|
// The signature should be zbase32 encoded
|
|
|
|
sig, err := zbase32.DecodeString(in.Signature)
|
|
|
|
if err != nil {
|
2024-02-26 12:19:38 +01:00
|
|
|
return nil, fmt.Errorf("failed to decode signature: %w", err)
|
2017-04-20 04:28:10 +02:00
|
|
|
}
|
|
|
|
|
2017-04-29 13:44:29 +02:00
|
|
|
// The signature is over the double-sha256 hash of the message.
|
2018-04-26 04:45:26 +02:00
|
|
|
in.Msg = append(signedMsgPrefix, in.Msg...)
|
2017-04-20 04:28:10 +02:00
|
|
|
digest := chainhash.DoubleHashB(in.Msg)
|
|
|
|
|
|
|
|
// RecoverCompact both recovers the pubkey and validates the signature.
|
2022-02-23 14:48:00 +01:00
|
|
|
pubKey, _, err := ecdsa.RecoverCompact(sig, digest)
|
2017-04-20 04:28:10 +02:00
|
|
|
if err != nil {
|
|
|
|
return &lnrpc.VerifyMessageResponse{Valid: false}, nil
|
|
|
|
}
|
2017-04-29 13:44:29 +02:00
|
|
|
pubKeyHex := hex.EncodeToString(pubKey.SerializeCompressed())
|
2017-04-20 04:28:10 +02:00
|
|
|
|
2018-01-31 05:30:00 +01:00
|
|
|
var pub [33]byte
|
|
|
|
copy(pub[:], pubKey.SerializeCompressed())
|
|
|
|
|
2017-04-29 13:44:29 +02:00
|
|
|
// Query the channel graph to ensure a node in the network with active
|
|
|
|
// channels signed the message.
|
2018-04-26 04:45:26 +02:00
|
|
|
//
|
2017-04-20 04:28:10 +02:00
|
|
|
// TODO(phlip9): Require valid nodes to have capital in active channels.
|
2021-08-03 09:57:26 +02:00
|
|
|
graph := r.server.graphDB
|
2018-01-31 05:30:00 +01:00
|
|
|
_, active, err := graph.HasLightningNode(pub)
|
2017-04-20 04:28:10 +02:00
|
|
|
if err != nil {
|
2024-02-26 12:19:38 +01:00
|
|
|
return nil, fmt.Errorf("failed to query graph: %w", err)
|
2017-04-20 04:28:10 +02:00
|
|
|
}
|
|
|
|
|
2017-08-03 05:59:43 +02:00
|
|
|
return &lnrpc.VerifyMessageResponse{
|
|
|
|
Valid: active,
|
|
|
|
Pubkey: pubKeyHex,
|
|
|
|
}, nil
|
2017-04-20 04:28:10 +02:00
|
|
|
}
|
|
|
|
|
2016-06-21 20:52:09 +02:00
|
|
|
// ConnectPeer attempts to establish a connection to a remote peer.
|
2016-01-17 04:12:36 +01:00
|
|
|
func (r *rpcServer) ConnectPeer(ctx context.Context,
|
|
|
|
in *lnrpc.ConnectPeerRequest) (*lnrpc.ConnectPeerResponse, error) {
|
2017-08-22 08:25:41 +02:00
|
|
|
|
2017-08-03 05:59:43 +02:00
|
|
|
// The server hasn't yet started, so it won't be able to service any of
|
|
|
|
// our requests, so we'll bail early here.
|
|
|
|
if !r.server.Started() {
|
2019-10-23 23:35:41 +02:00
|
|
|
return nil, ErrServerNotActive
|
2017-08-03 05:59:43 +02:00
|
|
|
}
|
|
|
|
|
2016-06-21 20:52:09 +02:00
|
|
|
if in.Addr == nil {
|
2016-01-02 05:27:40 +01:00
|
|
|
return nil, fmt.Errorf("need: lnc pubkeyhash@hostname")
|
|
|
|
}
|
|
|
|
|
2016-10-28 04:49:10 +02:00
|
|
|
pubkeyHex, err := hex.DecodeString(in.Addr.Pubkey)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-02-23 14:48:00 +01:00
|
|
|
pubKey, err := btcec.ParsePubKey(pubkeyHex)
|
2016-10-28 04:49:10 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-06-21 20:52:09 +02:00
|
|
|
|
2017-04-21 22:32:17 +02:00
|
|
|
// Connections to ourselves are disallowed for obvious reasons.
|
2020-04-28 10:06:21 +02:00
|
|
|
if pubKey.IsEqual(r.server.identityECDH.PubKey()) {
|
2017-04-21 22:32:17 +02:00
|
|
|
return nil, fmt.Errorf("cannot make connection to self")
|
|
|
|
}
|
|
|
|
|
2020-05-14 14:18:11 +02:00
|
|
|
addr, err := parseAddr(in.Addr.Host, r.cfg.net)
|
2016-06-21 20:52:09 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-10-28 04:49:10 +02:00
|
|
|
peerAddr := &lnwire.NetAddress{
|
2017-04-21 22:32:17 +02:00
|
|
|
IdentityKey: pubKey,
|
2018-04-27 22:59:59 +02:00
|
|
|
Address: addr,
|
2020-08-04 20:56:31 +02:00
|
|
|
ChainNet: r.cfg.ActiveNetParams.Net,
|
2016-10-28 04:49:10 +02:00
|
|
|
}
|
|
|
|
|
2019-01-04 03:38:38 +01:00
|
|
|
rpcsLog.Debugf("[connectpeer] requested connection to %x@%s",
|
|
|
|
peerAddr.IdentityKey.SerializeCompressed(), peerAddr.Address)
|
|
|
|
|
2020-08-25 06:54:31 +02:00
|
|
|
// By default, we will use the global connection timeout value.
|
|
|
|
timeout := r.cfg.ConnectionTimeout
|
|
|
|
|
|
|
|
// Check if the connection timeout is set. If set, we will use it in our
|
|
|
|
// request.
|
|
|
|
if in.Timeout != 0 {
|
|
|
|
timeout = time.Duration(in.Timeout) * time.Second
|
|
|
|
rpcsLog.Debugf(
|
|
|
|
"[connectpeer] connection timeout is set to %v",
|
|
|
|
timeout,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-02-07 13:58:28 +01:00
|
|
|
if err := r.server.ConnectToPeer(
|
|
|
|
peerAddr, in.Perm, timeout,
|
|
|
|
); err != nil {
|
2020-08-25 06:54:31 +02:00
|
|
|
rpcsLog.Errorf(
|
|
|
|
"[connectpeer]: error connecting to peer: %v", err,
|
|
|
|
)
|
2016-06-21 20:52:09 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
rpcsLog.Debugf("Connected to peer: %v", peerAddr.String())
|
2017-01-10 04:08:52 +01:00
|
|
|
return &lnrpc.ConnectPeerResponse{}, nil
|
2016-06-21 20:52:09 +02:00
|
|
|
}
|
2016-06-21 21:32:32 +02:00
|
|
|
|
2017-05-06 01:02:03 +02:00
|
|
|
// DisconnectPeer attempts to disconnect one peer from another identified by a
|
2017-12-30 15:44:31 +01:00
|
|
|
// given pubKey. In the case that we currently have a pending or active channel
|
2017-07-28 01:39:49 +02:00
|
|
|
// with the target peer, this action will be disallowed.
|
2017-05-06 01:02:03 +02:00
|
|
|
func (r *rpcServer) DisconnectPeer(ctx context.Context,
|
|
|
|
in *lnrpc.DisconnectPeerRequest) (*lnrpc.DisconnectPeerResponse, error) {
|
2017-08-22 08:25:41 +02:00
|
|
|
|
2017-05-06 01:02:03 +02:00
|
|
|
rpcsLog.Debugf("[disconnectpeer] from peer(%s)", in.PubKey)
|
|
|
|
|
2017-08-03 05:59:43 +02:00
|
|
|
if !r.server.Started() {
|
2019-10-23 23:35:41 +02:00
|
|
|
return nil, ErrServerNotActive
|
2017-08-03 05:59:43 +02:00
|
|
|
}
|
|
|
|
|
2017-05-06 01:02:03 +02:00
|
|
|
// First we'll validate the string passed in within the request to
|
|
|
|
// ensure that it's a valid hex-string, and also a valid compressed
|
|
|
|
// public key.
|
|
|
|
pubKeyBytes, err := hex.DecodeString(in.PubKey)
|
|
|
|
if err != nil {
|
2024-02-26 12:19:38 +01:00
|
|
|
return nil, fmt.Errorf("unable to decode pubkey bytes: %w", err)
|
2017-05-06 01:02:03 +02:00
|
|
|
}
|
2022-02-23 14:48:00 +01:00
|
|
|
peerPubKey, err := btcec.ParsePubKey(pubKeyBytes)
|
2017-05-06 01:02:03 +02:00
|
|
|
if err != nil {
|
2024-02-26 12:19:38 +01:00
|
|
|
return nil, fmt.Errorf("unable to parse pubkey: %w", err)
|
2017-05-06 01:02:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Next, we'll fetch the pending/active channels we have with a
|
|
|
|
// particular peer.
|
2021-08-03 09:57:26 +02:00
|
|
|
nodeChannels, err := r.server.chanStateDB.FetchOpenChannels(peerPubKey)
|
2017-05-06 01:02:03 +02:00
|
|
|
if err != nil {
|
2024-02-26 12:19:38 +01:00
|
|
|
return nil, fmt.Errorf("unable to fetch channels for peer: %w",
|
|
|
|
err)
|
2017-05-06 01:02:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// In order to avoid erroneously disconnecting from a peer that we have
|
|
|
|
// an active channel with, if we have any channels active with this
|
|
|
|
// peer, then we'll disallow disconnecting from them.
|
2024-01-31 10:13:38 +01:00
|
|
|
if len(nodeChannels) != 0 {
|
|
|
|
// If we are not in a dev environment or the configed dev value
|
|
|
|
// `unsafedisconnect` is false, we return an error since there
|
|
|
|
// are active channels.
|
|
|
|
if !r.cfg.Dev.GetUnsafeDisconnect() {
|
|
|
|
return nil, fmt.Errorf("cannot disconnect from "+
|
|
|
|
"peer(%x), still has %d active channels",
|
|
|
|
pubKeyBytes, len(nodeChannels))
|
|
|
|
}
|
|
|
|
|
|
|
|
// We are in a dev environment, print a warning log and
|
|
|
|
// disconnect.
|
|
|
|
rpcsLog.Warnf("UnsafeDisconnect mode, disconnecting from "+
|
|
|
|
"peer(%x) while there are %d active channels",
|
|
|
|
pubKeyBytes, len(nodeChannels))
|
2017-05-06 01:02:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// With all initial validation complete, we'll now request that the
|
2017-12-30 15:44:31 +01:00
|
|
|
// server disconnects from the peer.
|
2024-01-31 10:13:38 +01:00
|
|
|
err = r.server.DisconnectPeer(peerPubKey)
|
|
|
|
if err != nil {
|
2024-02-26 12:19:38 +01:00
|
|
|
return nil, fmt.Errorf("unable to disconnect peer: %w", err)
|
2017-05-02 21:31:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return &lnrpc.DisconnectPeerResponse{}, nil
|
|
|
|
}
|
|
|
|
|
2019-11-14 05:58:54 +01:00
|
|
|
// newFundingShimAssembler returns a new fully populated
|
|
|
|
// chanfunding.CannedAssembler using a FundingShim obtained from an RPC caller.
|
2020-03-14 00:58:05 +01:00
|
|
|
func newFundingShimAssembler(chanPointShim *lnrpc.ChanPointShim, initiator bool,
|
|
|
|
keyRing keychain.KeyRing) (chanfunding.Assembler, error) {
|
2019-11-14 05:58:54 +01:00
|
|
|
|
|
|
|
// Perform some basic sanity checks to ensure that all the expected
|
|
|
|
// fields are populated.
|
|
|
|
switch {
|
|
|
|
case chanPointShim.RemoteKey == nil:
|
|
|
|
return nil, fmt.Errorf("remote key not set")
|
|
|
|
|
|
|
|
case chanPointShim.LocalKey == nil:
|
|
|
|
return nil, fmt.Errorf("local key desc not set")
|
|
|
|
|
|
|
|
case chanPointShim.LocalKey.RawKeyBytes == nil:
|
|
|
|
return nil, fmt.Errorf("local raw key bytes not set")
|
|
|
|
|
|
|
|
case chanPointShim.LocalKey.KeyLoc == nil:
|
|
|
|
return nil, fmt.Errorf("local key loc not set")
|
|
|
|
|
|
|
|
case chanPointShim.ChanPoint == nil:
|
|
|
|
return nil, fmt.Errorf("chan point not set")
|
|
|
|
|
|
|
|
case len(chanPointShim.PendingChanId) != 32:
|
|
|
|
return nil, fmt.Errorf("pending chan ID not set")
|
|
|
|
}
|
|
|
|
|
|
|
|
// First, we'll map the RPC's channel point to one we can actually use.
|
|
|
|
index := chanPointShim.ChanPoint.OutputIndex
|
2021-02-13 09:05:33 +01:00
|
|
|
txid, err := lnrpc.GetChanPointFundingTxid(chanPointShim.ChanPoint)
|
2019-11-14 05:58:54 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
chanPoint := wire.NewOutPoint(txid, index)
|
|
|
|
|
|
|
|
// Next we'll parse out the remote party's funding key, as well as our
|
|
|
|
// full key descriptor.
|
2022-02-23 14:48:00 +01:00
|
|
|
remoteKey, err := btcec.ParsePubKey(chanPointShim.RemoteKey)
|
2019-11-14 05:58:54 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
shimKeyDesc := chanPointShim.LocalKey
|
2022-02-23 14:48:00 +01:00
|
|
|
localKey, err := btcec.ParsePubKey(shimKeyDesc.RawKeyBytes)
|
2019-11-14 05:58:54 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
localKeyDesc := keychain.KeyDescriptor{
|
|
|
|
PubKey: localKey,
|
|
|
|
KeyLocator: keychain.KeyLocator{
|
|
|
|
Family: keychain.KeyFamily(
|
|
|
|
shimKeyDesc.KeyLoc.KeyFamily,
|
|
|
|
),
|
|
|
|
Index: uint32(shimKeyDesc.KeyLoc.KeyIndex),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify that if we re-derive this key according to the passed
|
|
|
|
// KeyLocator, that we get the exact same key back. Otherwise, we may
|
|
|
|
// end up in a situation where we aren't able to actually sign for this
|
|
|
|
// newly created channel.
|
|
|
|
derivedKey, err := keyRing.DeriveKey(localKeyDesc.KeyLocator)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if !derivedKey.PubKey.IsEqual(localKey) {
|
|
|
|
return nil, fmt.Errorf("KeyLocator does not match attached " +
|
|
|
|
"raw pubkey")
|
|
|
|
}
|
|
|
|
|
|
|
|
// With all the parts assembled, we can now make the canned assembler
|
|
|
|
// to pass into the wallet.
|
2023-01-19 04:33:50 +01:00
|
|
|
//
|
|
|
|
// TODO(roasbeef): update to support musig2
|
2019-11-14 05:58:54 +01:00
|
|
|
return chanfunding.NewCannedAssembler(
|
2020-03-14 00:58:05 +01:00
|
|
|
chanPointShim.ThawHeight, *chanPoint,
|
|
|
|
btcutil.Amount(chanPointShim.Amt), &localKeyDesc,
|
2023-09-25 17:24:22 +02:00
|
|
|
remoteKey, initiator, chanPointShim.Musig2,
|
2019-11-14 05:58:54 +01:00
|
|
|
), nil
|
|
|
|
}
|
|
|
|
|
2024-04-18 14:58:15 +02:00
|
|
|
// newPsbtAssembler returns a new fully populated
|
2020-03-31 09:13:17 +02:00
|
|
|
// chanfunding.PsbtAssembler using a FundingShim obtained from an RPC caller.
|
2024-04-02 20:28:54 +02:00
|
|
|
func newPsbtAssembler(req *lnrpc.OpenChannelRequest,
|
2020-03-31 09:13:17 +02:00
|
|
|
psbtShim *lnrpc.PsbtShim, netParams *chaincfg.Params) (
|
|
|
|
chanfunding.Assembler, error) {
|
|
|
|
|
|
|
|
var (
|
|
|
|
packet *psbt.Packet
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
// Perform some basic sanity checks to ensure that all the expected
|
|
|
|
// fields are populated and none of the incompatible fields are.
|
|
|
|
if len(psbtShim.PendingChanId) != 32 {
|
|
|
|
return nil, fmt.Errorf("pending chan ID not set")
|
|
|
|
}
|
2021-04-30 12:35:53 +02:00
|
|
|
if req.SatPerByte != 0 || req.SatPerVbyte != 0 || req.TargetConf != 0 { // nolint:staticcheck
|
2020-03-31 09:13:17 +02:00
|
|
|
return nil, fmt.Errorf("specifying fee estimation parameters " +
|
|
|
|
"is not supported for PSBT funding")
|
|
|
|
}
|
|
|
|
|
|
|
|
// The base PSBT is optional. But if it's set, it has to be a valid,
|
|
|
|
// binary serialized PSBT.
|
|
|
|
if len(psbtShim.BasePsbt) > 0 {
|
|
|
|
packet, err = psbt.NewFromRawBytes(
|
|
|
|
bytes.NewReader(psbtShim.BasePsbt), false,
|
|
|
|
)
|
|
|
|
if err != nil {
|
2024-03-07 13:19:28 +01:00
|
|
|
return nil, fmt.Errorf("error parsing base PSBT: %w",
|
2020-03-31 09:13:17 +02:00
|
|
|
err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// With all the parts assembled, we can now make the canned assembler
|
|
|
|
// to pass into the wallet.
|
|
|
|
return chanfunding.NewPsbtAssembler(
|
|
|
|
btcutil.Amount(req.LocalFundingAmount), packet, netParams,
|
2020-07-09 02:12:57 +02:00
|
|
|
!psbtShim.NoPublish,
|
2020-03-31 09:13:17 +02:00
|
|
|
), nil
|
|
|
|
}
|
|
|
|
|
2020-03-31 01:59:53 +02:00
|
|
|
// canOpenChannel returns an error if the necessary subsystems for channel
|
|
|
|
// funding are not ready.
|
|
|
|
func (r *rpcServer) canOpenChannel() error {
|
|
|
|
// We can't open a channel until the main server has started.
|
2017-08-03 05:59:43 +02:00
|
|
|
if !r.server.Started() {
|
2019-10-23 23:35:41 +02:00
|
|
|
return ErrServerNotActive
|
2017-08-03 05:59:43 +02:00
|
|
|
}
|
|
|
|
|
2020-03-31 01:55:16 +02:00
|
|
|
// Creation of channels before the wallet syncs up is currently
|
|
|
|
// disallowed.
|
2020-10-06 17:03:42 +02:00
|
|
|
isSynced, _, err := r.server.cc.Wallet.IsSynced()
|
2020-03-31 01:55:16 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !isSynced {
|
|
|
|
return errors.New("channels cannot be created before the " +
|
|
|
|
"wallet is fully synced")
|
|
|
|
}
|
|
|
|
|
2020-03-31 01:59:53 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-08-24 11:21:55 +02:00
|
|
|
// parseOpenChannelReq parses an OpenChannelRequest message into an InitFundingMsg
|
2020-11-24 18:19:39 +01:00
|
|
|
// struct. The logic is abstracted so that it can be shared between OpenChannel
|
|
|
|
// and OpenChannelSync.
|
2020-03-31 01:59:53 +02:00
|
|
|
func (r *rpcServer) parseOpenChannelReq(in *lnrpc.OpenChannelRequest,
|
2020-12-17 16:08:53 +01:00
|
|
|
isSync bool) (*funding.InitFundingMsg, error) {
|
2020-03-31 01:59:53 +02:00
|
|
|
|
|
|
|
rpcsLog.Debugf("[openchannel] request to NodeKey(%x) "+
|
|
|
|
"allocation(us=%v, them=%v)", in.NodePubkey,
|
|
|
|
in.LocalFundingAmount, in.PushSat)
|
|
|
|
|
2016-06-21 21:32:32 +02:00
|
|
|
localFundingAmt := btcutil.Amount(in.LocalFundingAmount)
|
2017-01-10 04:05:11 +01:00
|
|
|
remoteInitialBalance := btcutil.Amount(in.PushSat)
|
|
|
|
|
2021-04-22 20:14:44 +02:00
|
|
|
// If we are not committing the maximum viable balance towards a channel
|
|
|
|
// then the local funding amount must be specified. In case FundMax is
|
|
|
|
// set the funding amount is specified as the interval between minimum
|
|
|
|
// funding amount and by the configured maximum channel size.
|
|
|
|
if !in.FundMax && localFundingAmt == 0 {
|
2022-09-26 13:08:52 +02:00
|
|
|
return nil, fmt.Errorf("local funding amount must be non-zero")
|
|
|
|
}
|
|
|
|
|
2017-01-10 04:05:11 +01:00
|
|
|
// Ensure that the initial balance of the remote party (if pushing
|
2017-08-06 01:52:37 +02:00
|
|
|
// satoshis) does not exceed the amount the local party has requested
|
2021-04-22 20:14:44 +02:00
|
|
|
// for funding. This is only checked if we are not committing the
|
|
|
|
// maximum viable amount towards the channel balance. If we do commit
|
|
|
|
// the maximum then the remote balance is checked in a dedicated FundMax
|
|
|
|
// check.
|
|
|
|
if !in.FundMax && remoteInitialBalance >= localFundingAmt {
|
2020-03-31 01:59:53 +02:00
|
|
|
return nil, fmt.Errorf("amount pushed to remote peer for " +
|
|
|
|
"initial state must be below the local funding amount")
|
2017-01-10 04:05:11 +01:00
|
|
|
}
|
2016-11-11 02:37:21 +01:00
|
|
|
|
2021-04-22 20:14:44 +02:00
|
|
|
// We either allow the fundmax or the psbt flow hence we return an error
|
|
|
|
// if both are set.
|
|
|
|
if in.FundingShim != nil && in.FundMax {
|
|
|
|
return nil, fmt.Errorf("cannot provide a psbt funding shim " +
|
2023-12-21 16:21:35 +01:00
|
|
|
"while committing the maximum wallet balance towards " +
|
2021-04-22 20:14:44 +02:00
|
|
|
"the channel opening")
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the FundMax flag is set, ensure that the acceptable minimum local
|
|
|
|
// amount adheres to the amount to be pushed to the remote, and to
|
|
|
|
// current rules, while also respecting the settings for the maximum
|
|
|
|
// channel size.
|
|
|
|
var minFundAmt, fundUpToMaxAmt btcutil.Amount
|
|
|
|
if in.FundMax {
|
|
|
|
// We assume the configured maximum channel size to be the upper
|
|
|
|
// bound of our "maxed" out funding attempt.
|
|
|
|
fundUpToMaxAmt = btcutil.Amount(r.cfg.MaxChanSize)
|
|
|
|
|
|
|
|
// Since the standard non-fundmax flow requires the minimum
|
|
|
|
// funding amount to be at least in the amount of the initial
|
|
|
|
// remote balance(push amount) we need to adjust the minimum
|
|
|
|
// funding amount accordingly. We initially assume the minimum
|
|
|
|
// allowed channel size as minimum funding amount.
|
|
|
|
minFundAmt = funding.MinChanFundingSize
|
|
|
|
|
|
|
|
// If minFundAmt is less than the initial remote balance we
|
|
|
|
// simply assign the initial remote balance to minFundAmt in
|
|
|
|
// order to fullfil the criterion. Whether or not this so
|
|
|
|
// determined minimum amount is actually available is
|
|
|
|
// ascertained downstream in the lnwallet's reservation
|
|
|
|
// workflow.
|
|
|
|
if remoteInitialBalance >= minFundAmt {
|
|
|
|
minFundAmt = remoteInitialBalance
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
minHtlcIn := lnwire.MilliSatoshi(in.MinHtlcMsat)
|
|
|
|
remoteCsvDelay := uint16(in.RemoteCsvDelay)
|
|
|
|
maxValue := lnwire.MilliSatoshi(in.RemoteMaxValueInFlightMsat)
|
|
|
|
maxHtlcs := uint16(in.RemoteMaxHtlcs)
|
|
|
|
remoteChanReserve := btcutil.Amount(in.RemoteChanReserveSat)
|
|
|
|
|
|
|
|
globalFeatureSet := r.server.featureMgr.Get(feature.SetNodeAnn)
|
|
|
|
|
2022-09-10 18:04:48 +02:00
|
|
|
// Determine if the user provided channel fees
|
|
|
|
// and if so pass them on to the funding workflow.
|
|
|
|
var channelBaseFee, channelFeeRate *uint64
|
|
|
|
if in.UseBaseFee {
|
|
|
|
channelBaseFee = &in.BaseFee
|
|
|
|
}
|
|
|
|
if in.UseFeeRate {
|
|
|
|
channelFeeRate = &in.FeeRate
|
|
|
|
}
|
|
|
|
|
2022-09-29 12:32:28 +02:00
|
|
|
// Ensure that the remote channel reserve does not exceed 20% of the
|
|
|
|
// channel capacity.
|
2021-04-22 20:14:44 +02:00
|
|
|
if !in.FundMax && remoteChanReserve >= localFundingAmt/5 {
|
2022-09-29 12:32:28 +02:00
|
|
|
return nil, fmt.Errorf("remote channel reserve must be less " +
|
|
|
|
"than the %%20 of the channel capacity")
|
|
|
|
}
|
|
|
|
|
2017-08-06 01:52:37 +02:00
|
|
|
// Ensure that the user doesn't exceed the current soft-limit for
|
|
|
|
// channel size. If the funding amount is above the soft-limit, then
|
|
|
|
// we'll reject the request.
|
2021-04-22 20:14:44 +02:00
|
|
|
// If the FundMax flag is set the local amount is determined downstream
|
|
|
|
// in the wallet hence we do not check it here against the maximum
|
|
|
|
// funding amount. Only if the localFundingAmt is specified we can check
|
|
|
|
// if it exceeds the maximum funding amount.
|
2020-07-02 06:05:43 +02:00
|
|
|
wumboEnabled := globalFeatureSet.HasFeature(
|
|
|
|
lnwire.WumboChannelsOptional,
|
|
|
|
)
|
2021-04-22 20:14:44 +02:00
|
|
|
if !in.FundMax && !wumboEnabled && localFundingAmt > MaxFundingAmount {
|
2020-03-31 01:59:53 +02:00
|
|
|
return nil, fmt.Errorf("funding amount is too large, the max "+
|
2019-05-15 09:02:53 +02:00
|
|
|
"channel size is: %v", MaxFundingAmount)
|
2017-08-06 01:52:37 +02:00
|
|
|
}
|
|
|
|
|
2018-03-19 00:58:39 +01:00
|
|
|
// Restrict the size of the channel we'll actually open. At a later
|
2021-04-22 20:14:44 +02:00
|
|
|
// level, we'll ensure that the output we create, after accounting for
|
|
|
|
// fees, does not leave a dust output. In case of the FundMax flow
|
|
|
|
// dedicated checks ensure that the lower boundary of the channel size
|
|
|
|
// is at least in the amount of MinChanFundingSize or potentially higher
|
|
|
|
// if a remote balance is specified.
|
|
|
|
if !in.FundMax && localFundingAmt < funding.MinChanFundingSize {
|
2020-03-31 01:59:53 +02:00
|
|
|
return nil, fmt.Errorf("channel is too small, the minimum "+
|
2020-11-17 00:28:56 +01:00
|
|
|
"channel size is: %v SAT", int64(funding.MinChanFundingSize))
|
2017-01-17 05:46:32 +01:00
|
|
|
}
|
|
|
|
|
2020-08-13 00:46:17 +02:00
|
|
|
// Prevent users from submitting a max-htlc value that would exceed the
|
|
|
|
// protocol maximum.
|
|
|
|
if maxHtlcs > input.MaxHTLCNumber/2 {
|
|
|
|
return nil, fmt.Errorf("remote-max-htlcs (%v) cannot be "+
|
|
|
|
"greater than %v", maxHtlcs, input.MaxHTLCNumber/2)
|
|
|
|
}
|
|
|
|
|
2018-10-17 01:44:26 +02:00
|
|
|
// Then, we'll extract the minimum number of confirmations that each
|
|
|
|
// output we use to fund the channel's funding transaction should
|
|
|
|
// satisfy.
|
2020-09-27 17:48:15 +02:00
|
|
|
minConfs, err := lnrpc.ExtractMinConfs(in.MinConfs, in.SpendUnconfirmed)
|
2018-10-17 01:44:26 +02:00
|
|
|
if err != nil {
|
2020-03-31 01:59:53 +02:00
|
|
|
return nil, err
|
2018-08-10 04:44:25 +02:00
|
|
|
}
|
|
|
|
|
2017-03-14 04:39:16 +01:00
|
|
|
// TODO(roasbeef): also return channel ID?
|
|
|
|
|
2020-03-31 01:56:12 +02:00
|
|
|
var nodePubKey *btcec.PublicKey
|
2017-04-21 22:32:17 +02:00
|
|
|
|
2020-03-31 01:59:53 +02:00
|
|
|
// Parse the remote pubkey the NodePubkey field of the request. If it's
|
|
|
|
// not present, we'll fallback to the deprecated version that parses the
|
|
|
|
// key from a hex string if this is for REST for backwards compatibility.
|
|
|
|
switch {
|
|
|
|
// Parse the raw bytes of the node key into a pubkey object so we can
|
|
|
|
// easily manipulate it.
|
|
|
|
case len(in.NodePubkey) > 0:
|
2022-02-23 14:48:00 +01:00
|
|
|
nodePubKey, err = btcec.ParsePubKey(in.NodePubkey)
|
2020-03-31 01:59:53 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Decode the provided target node's public key, parsing it into a pub
|
|
|
|
// key object. For all sync call, byte slices are expected to be encoded
|
|
|
|
// as hex strings.
|
|
|
|
case isSync:
|
2021-04-30 12:35:53 +02:00
|
|
|
keyBytes, err := hex.DecodeString(in.NodePubkeyString) // nolint:staticcheck
|
2020-03-31 01:59:53 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-02-23 14:48:00 +01:00
|
|
|
nodePubKey, err = btcec.ParsePubKey(keyBytes)
|
2020-03-31 01:59:53 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("NodePubkey is not set")
|
2018-02-14 06:48:42 +01:00
|
|
|
}
|
2017-04-21 22:32:17 +02:00
|
|
|
|
2018-02-14 06:48:42 +01:00
|
|
|
// Making a channel to ourselves wouldn't be of any use, so we
|
|
|
|
// explicitly disallow them.
|
2020-04-28 10:06:21 +02:00
|
|
|
if nodePubKey.IsEqual(r.server.identityECDH.PubKey()) {
|
2020-03-31 01:59:53 +02:00
|
|
|
return nil, fmt.Errorf("cannot open channel to self")
|
2016-10-28 04:49:10 +02:00
|
|
|
}
|
|
|
|
|
2024-01-26 04:27:12 +01:00
|
|
|
var feeRate chainfee.SatPerKWeight
|
|
|
|
|
|
|
|
// Skip estimating fee rate for PSBT funding.
|
|
|
|
if in.FundingShim == nil || in.FundingShim.GetPsbtShim() == nil {
|
2024-04-25 20:34:25 +02:00
|
|
|
// Keep the old behavior prior to 0.18.0 - when the user
|
|
|
|
// doesn't set fee rate or conf target, the default conf target
|
|
|
|
// of 6 is used.
|
|
|
|
targetConf := maybeUseDefaultConf(
|
|
|
|
in.SatPerByte, in.SatPerVbyte, uint32(in.TargetConf),
|
|
|
|
)
|
|
|
|
|
2024-01-26 04:27:12 +01:00
|
|
|
// Calculate an appropriate fee rate for this transaction.
|
|
|
|
feeRate, err = lnrpc.CalculateFeeRate(
|
|
|
|
uint64(in.SatPerByte), in.SatPerVbyte,
|
2024-04-25 20:34:25 +02:00
|
|
|
targetConf, r.server.cc.FeeEstimator,
|
2024-01-26 04:27:12 +01:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-11-23 07:57:23 +01:00
|
|
|
|
2024-01-26 04:27:12 +01:00
|
|
|
rpcsLog.Debugf("[openchannel]: using fee of %v sat/kw for "+
|
|
|
|
"funding tx", int64(feeRate))
|
|
|
|
}
|
2017-11-23 07:57:23 +01:00
|
|
|
|
2020-11-09 08:34:51 +01:00
|
|
|
script, err := chancloser.ParseUpfrontShutdownAddress(
|
2020-08-04 20:56:31 +02:00
|
|
|
in.CloseAddress, r.cfg.ActiveNetParams.Params,
|
|
|
|
)
|
2019-12-17 20:58:28 +01:00
|
|
|
if err != nil {
|
2024-03-07 13:19:28 +01:00
|
|
|
return nil, fmt.Errorf("error parsing upfront shutdown: %w",
|
2020-03-31 01:59:53 +02:00
|
|
|
err)
|
2019-12-17 20:58:28 +01:00
|
|
|
}
|
|
|
|
|
2021-03-04 04:39:53 +01:00
|
|
|
var channelType *lnwire.ChannelType
|
|
|
|
switch in.CommitmentType {
|
|
|
|
case lnrpc.CommitmentType_UNKNOWN_COMMITMENT_TYPE:
|
2022-04-04 22:59:31 +02:00
|
|
|
if in.ZeroConf {
|
|
|
|
return nil, fmt.Errorf("use anchors for zero-conf")
|
|
|
|
}
|
2021-03-04 04:39:53 +01:00
|
|
|
|
|
|
|
case lnrpc.CommitmentType_LEGACY:
|
|
|
|
channelType = new(lnwire.ChannelType)
|
|
|
|
*channelType = lnwire.ChannelType(*lnwire.NewRawFeatureVector())
|
|
|
|
|
|
|
|
case lnrpc.CommitmentType_STATIC_REMOTE_KEY:
|
|
|
|
channelType = new(lnwire.ChannelType)
|
|
|
|
*channelType = lnwire.ChannelType(*lnwire.NewRawFeatureVector(
|
|
|
|
lnwire.StaticRemoteKeyRequired,
|
|
|
|
))
|
|
|
|
|
|
|
|
case lnrpc.CommitmentType_ANCHORS:
|
|
|
|
channelType = new(lnwire.ChannelType)
|
2022-04-04 22:59:31 +02:00
|
|
|
fv := lnwire.NewRawFeatureVector(
|
2021-03-04 04:39:53 +01:00
|
|
|
lnwire.StaticRemoteKeyRequired,
|
|
|
|
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
2022-04-04 22:59:31 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
if in.ZeroConf {
|
|
|
|
fv.Set(lnwire.ZeroConfRequired)
|
|
|
|
}
|
|
|
|
|
|
|
|
if in.ScidAlias {
|
|
|
|
fv.Set(lnwire.ScidAliasRequired)
|
|
|
|
}
|
|
|
|
|
|
|
|
*channelType = lnwire.ChannelType(*fv)
|
2021-03-04 04:39:53 +01:00
|
|
|
|
2021-07-15 02:21:04 +02:00
|
|
|
case lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE:
|
|
|
|
channelType = new(lnwire.ChannelType)
|
2022-04-04 22:59:31 +02:00
|
|
|
fv := lnwire.NewRawFeatureVector(
|
2021-07-15 02:21:04 +02:00
|
|
|
lnwire.StaticRemoteKeyRequired,
|
|
|
|
lnwire.AnchorsZeroFeeHtlcTxRequired,
|
|
|
|
lnwire.ScriptEnforcedLeaseRequired,
|
2022-04-04 22:59:31 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
if in.ZeroConf {
|
|
|
|
fv.Set(lnwire.ZeroConfRequired)
|
|
|
|
}
|
|
|
|
|
|
|
|
if in.ScidAlias {
|
|
|
|
fv.Set(lnwire.ScidAliasRequired)
|
|
|
|
}
|
|
|
|
|
|
|
|
*channelType = lnwire.ChannelType(*fv)
|
2021-07-15 02:21:04 +02:00
|
|
|
|
2023-01-20 05:42:06 +01:00
|
|
|
case lnrpc.CommitmentType_SIMPLE_TAPROOT:
|
|
|
|
// If the taproot channel type is being set, then the channel
|
|
|
|
// MUST be private (unadvertised) for now.
|
|
|
|
if !in.Private {
|
|
|
|
return nil, fmt.Errorf("taproot channels must be " +
|
|
|
|
"private")
|
|
|
|
}
|
|
|
|
|
|
|
|
channelType = new(lnwire.ChannelType)
|
|
|
|
fv := lnwire.NewRawFeatureVector(
|
2023-08-19 02:04:07 +02:00
|
|
|
lnwire.SimpleTaprootChannelsRequiredStaging,
|
2023-01-20 05:42:06 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
// TODO(roasbeef): no need for the rest as they're now
|
|
|
|
// implicit?
|
|
|
|
|
|
|
|
if in.ZeroConf {
|
|
|
|
fv.Set(lnwire.ZeroConfRequired)
|
|
|
|
}
|
|
|
|
|
|
|
|
if in.ScidAlias {
|
|
|
|
fv.Set(lnwire.ScidAliasRequired)
|
|
|
|
}
|
|
|
|
|
|
|
|
*channelType = lnwire.ChannelType(*fv)
|
|
|
|
|
2021-03-04 04:39:53 +01:00
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("unhandled request channel type %v",
|
|
|
|
in.CommitmentType)
|
|
|
|
}
|
|
|
|
|
2023-05-03 22:19:43 +02:00
|
|
|
// We limit the channel memo to be 500 characters long. This enforces
|
|
|
|
// a reasonable upper bound on storage consumption. This also mimics
|
|
|
|
// the length limit for the label of a TX.
|
|
|
|
const maxMemoLength = 500
|
|
|
|
if len(in.Memo) > maxMemoLength {
|
|
|
|
return nil, fmt.Errorf("provided memo (%s) is of length %d, "+
|
|
|
|
"exceeds %d", in.Memo, len(in.Memo), maxMemoLength)
|
|
|
|
}
|
|
|
|
|
2023-06-10 21:25:21 +02:00
|
|
|
// Check, if manually selected outpoints are present to fund a channel.
|
|
|
|
var outpoints []wire.OutPoint
|
|
|
|
if len(in.Outpoints) > 0 {
|
|
|
|
outpoints, err = toWireOutpoints(in.Outpoints)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("can't create outpoints %w", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-11 02:37:21 +01:00
|
|
|
// Instruct the server to trigger the necessary events to attempt to
|
|
|
|
// open a new channel. A stream is returned in place, this stream will
|
|
|
|
// be used to consume updates of the state of the pending channel.
|
2020-12-17 16:08:53 +01:00
|
|
|
return &funding.InitFundingMsg{
|
2021-04-22 20:14:44 +02:00
|
|
|
TargetPubkey: nodePubKey,
|
|
|
|
ChainHash: *r.cfg.ActiveNetParams.GenesisHash,
|
|
|
|
LocalFundingAmt: localFundingAmt,
|
|
|
|
BaseFee: channelBaseFee,
|
|
|
|
FeeRate: channelFeeRate,
|
|
|
|
PushAmt: lnwire.NewMSatFromSatoshis(
|
|
|
|
remoteInitialBalance,
|
|
|
|
),
|
2022-09-29 12:32:28 +02:00
|
|
|
MinHtlcIn: minHtlcIn,
|
|
|
|
FundingFeePerKw: feeRate,
|
|
|
|
Private: in.Private,
|
|
|
|
RemoteCsvDelay: remoteCsvDelay,
|
|
|
|
RemoteChanReserve: remoteChanReserve,
|
|
|
|
MinConfs: minConfs,
|
|
|
|
ShutdownScript: script,
|
|
|
|
MaxValueInFlight: maxValue,
|
|
|
|
MaxHtlcs: maxHtlcs,
|
|
|
|
MaxLocalCsv: uint16(in.MaxLocalCsv),
|
|
|
|
ChannelType: channelType,
|
2021-04-22 20:14:44 +02:00
|
|
|
FundUpToMaxAmt: fundUpToMaxAmt,
|
|
|
|
MinFundAmt: minFundAmt,
|
2023-05-03 22:21:44 +02:00
|
|
|
Memo: []byte(in.Memo),
|
2023-06-10 21:25:21 +02:00
|
|
|
Outpoints: outpoints,
|
2020-03-31 01:59:53 +02:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2023-06-10 21:25:21 +02:00
|
|
|
// toWireOutpoints converts a list of outpoints from the rpc format to the wire
|
|
|
|
// format.
|
|
|
|
func toWireOutpoints(outpoints []*lnrpc.OutPoint) ([]wire.OutPoint, error) {
|
|
|
|
var wireOutpoints []wire.OutPoint
|
|
|
|
for _, outpoint := range outpoints {
|
|
|
|
hash, err := chainhash.NewHashFromStr(outpoint.TxidStr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("cannot create chainhash")
|
|
|
|
}
|
|
|
|
|
|
|
|
wireOutpoint := wire.NewOutPoint(
|
|
|
|
hash, outpoint.OutputIndex,
|
|
|
|
)
|
|
|
|
wireOutpoints = append(wireOutpoints, *wireOutpoint)
|
|
|
|
}
|
|
|
|
|
|
|
|
return wireOutpoints, nil
|
|
|
|
}
|
|
|
|
|
2020-03-31 01:59:53 +02:00
|
|
|
// OpenChannel attempts to open a singly funded channel specified in the
|
|
|
|
// request to a remote peer.
|
|
|
|
func (r *rpcServer) OpenChannel(in *lnrpc.OpenChannelRequest,
|
|
|
|
updateStream lnrpc.Lightning_OpenChannelServer) error {
|
|
|
|
|
|
|
|
if err := r.canOpenChannel(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
req, err := r.parseOpenChannelReq(in, false)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2018-08-10 04:17:16 +02:00
|
|
|
}
|
|
|
|
|
2019-11-14 05:58:54 +01:00
|
|
|
// If the user has provided a shim, then we'll now augment the based
|
|
|
|
// open channel request with this additional logic.
|
|
|
|
if in.FundingShim != nil {
|
2020-03-31 09:13:17 +02:00
|
|
|
switch {
|
2019-11-14 05:58:54 +01:00
|
|
|
// If we have a chan point shim, then this means the funding
|
|
|
|
// transaction was crafted externally. In this case we only
|
|
|
|
// need to hand a channel point down into the wallet.
|
2020-03-31 09:13:17 +02:00
|
|
|
case in.FundingShim.GetChanPointShim() != nil:
|
2019-11-14 05:58:54 +01:00
|
|
|
chanPointShim := in.FundingShim.GetChanPointShim()
|
|
|
|
|
|
|
|
// Map the channel point shim into a new
|
|
|
|
// chanfunding.CannedAssembler that the wallet will use
|
|
|
|
// to obtain the channel point details.
|
2020-11-24 18:19:39 +01:00
|
|
|
copy(req.PendingChanID[:], chanPointShim.PendingChanId)
|
|
|
|
req.ChanFunder, err = newFundingShimAssembler(
|
2020-10-06 17:03:42 +02:00
|
|
|
chanPointShim, true, r.server.cc.KeyRing,
|
2019-11-14 05:58:54 +01:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-03-31 09:13:17 +02:00
|
|
|
|
|
|
|
// If we have a PSBT shim, then this means the funding
|
|
|
|
// transaction will be crafted outside of the wallet, once the
|
|
|
|
// funding multisig output script is known. We'll create an
|
|
|
|
// intent that will supervise the multi-step process.
|
|
|
|
case in.FundingShim.GetPsbtShim() != nil:
|
|
|
|
psbtShim := in.FundingShim.GetPsbtShim()
|
|
|
|
|
|
|
|
// Instruct the wallet to use the new
|
|
|
|
// chanfunding.PsbtAssembler to construct the funding
|
|
|
|
// transaction.
|
2020-11-24 18:19:39 +01:00
|
|
|
copy(req.PendingChanID[:], psbtShim.PendingChanId)
|
2024-04-02 20:28:54 +02:00
|
|
|
|
|
|
|
// NOTE: For the PSBT case we do also allow unconfirmed
|
|
|
|
// utxos to fund the psbt transaction because we make
|
|
|
|
// sure we only use stable utxos.
|
2020-11-24 18:19:39 +01:00
|
|
|
req.ChanFunder, err = newPsbtAssembler(
|
2024-04-02 20:28:54 +02:00
|
|
|
in, psbtShim,
|
2020-10-06 17:03:42 +02:00
|
|
|
&r.server.cc.Wallet.Cfg.NetParams,
|
2020-03-31 09:13:17 +02:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-11-14 05:58:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-10 04:17:16 +02:00
|
|
|
updateChan, errChan := r.server.OpenChannel(req)
|
2016-01-02 05:27:40 +01:00
|
|
|
|
2016-08-31 01:52:53 +02:00
|
|
|
var outpoint wire.OutPoint
|
|
|
|
out:
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case err := <-errChan:
|
2018-02-20 00:01:23 +01:00
|
|
|
rpcsLog.Errorf("unable to open channel to NodeKey(%x): %v",
|
2020-11-24 18:19:39 +01:00
|
|
|
req.TargetPubkey.SerializeCompressed(), err)
|
2016-07-08 00:30:55 +02:00
|
|
|
return err
|
2016-08-31 01:52:53 +02:00
|
|
|
case fundingUpdate := <-updateChan:
|
|
|
|
rpcsLog.Tracef("[openchannel] sending update: %v",
|
|
|
|
fundingUpdate)
|
|
|
|
if err := updateStream.Send(fundingUpdate); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// If a final channel open update is being sent, then
|
|
|
|
// we can break out of our recv loop as we no longer
|
|
|
|
// need to process any further updates.
|
2020-09-16 06:08:46 +02:00
|
|
|
update, ok := fundingUpdate.Update.(*lnrpc.OpenStatusUpdate_ChanOpen)
|
|
|
|
if ok {
|
2016-08-31 01:52:53 +02:00
|
|
|
chanPoint := update.ChanOpen.ChannelPoint
|
2021-02-13 09:05:33 +01:00
|
|
|
txid, err := lnrpc.GetChanPointFundingTxid(chanPoint)
|
2018-01-11 05:59:30 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-08-31 01:52:53 +02:00
|
|
|
outpoint = wire.OutPoint{
|
2018-12-10 05:12:24 +01:00
|
|
|
Hash: *txid,
|
2016-08-31 01:52:53 +02:00
|
|
|
Index: chanPoint.OutputIndex,
|
|
|
|
}
|
|
|
|
|
|
|
|
break out
|
|
|
|
}
|
|
|
|
case <-r.quit:
|
|
|
|
return nil
|
2016-07-08 00:30:55 +02:00
|
|
|
}
|
|
|
|
}
|
2016-08-31 01:52:53 +02:00
|
|
|
|
2018-02-20 00:01:23 +01:00
|
|
|
rpcsLog.Tracef("[openchannel] success NodeKey(%x), ChannelPoint(%v)",
|
2020-11-24 18:19:39 +01:00
|
|
|
req.TargetPubkey.SerializeCompressed(), outpoint)
|
2016-07-08 00:30:55 +02:00
|
|
|
return nil
|
2016-06-21 21:32:32 +02:00
|
|
|
}
|
|
|
|
|
2016-11-11 02:37:21 +01:00
|
|
|
// OpenChannelSync is a synchronous version of the OpenChannel RPC call. This
|
|
|
|
// call is meant to be consumed by clients to the REST proxy. As with all other
|
|
|
|
// sync calls, all byte slices are instead to be populated as hex encoded
|
|
|
|
// strings.
|
|
|
|
func (r *rpcServer) OpenChannelSync(ctx context.Context,
|
|
|
|
in *lnrpc.OpenChannelRequest) (*lnrpc.ChannelPoint, error) {
|
2017-08-22 08:25:41 +02:00
|
|
|
|
2020-03-31 01:59:53 +02:00
|
|
|
if err := r.canOpenChannel(); err != nil {
|
2017-01-25 02:12:51 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-03-31 01:59:53 +02:00
|
|
|
req, err := r.parseOpenChannelReq(in, true)
|
2016-11-11 02:37:21 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-08-10 04:17:16 +02:00
|
|
|
updateChan, errChan := r.server.OpenChannel(req)
|
2016-11-11 02:37:21 +01:00
|
|
|
select {
|
|
|
|
// If an error occurs them immediately return the error to the client.
|
|
|
|
case err := <-errChan:
|
2018-02-20 00:01:23 +01:00
|
|
|
rpcsLog.Errorf("unable to open channel to NodeKey(%x): %v",
|
2020-11-24 18:19:39 +01:00
|
|
|
req.TargetPubkey.SerializeCompressed(), err)
|
2016-11-11 02:37:21 +01:00
|
|
|
return nil, err
|
|
|
|
|
|
|
|
// Otherwise, wait for the first channel update. The first update sent
|
|
|
|
// is when the funding transaction is broadcast to the network.
|
|
|
|
case fundingUpdate := <-updateChan:
|
|
|
|
rpcsLog.Tracef("[openchannel] sending update: %v",
|
|
|
|
fundingUpdate)
|
|
|
|
|
|
|
|
// Parse out the txid of the pending funding transaction. The
|
|
|
|
// sync client can use this to poll against the list of
|
|
|
|
// PendingChannels.
|
|
|
|
openUpdate := fundingUpdate.Update.(*lnrpc.OpenStatusUpdate_ChanPending)
|
|
|
|
chanUpdate := openUpdate.ChanPending
|
|
|
|
|
|
|
|
return &lnrpc.ChannelPoint{
|
2018-01-11 05:59:30 +01:00
|
|
|
FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
|
|
|
|
FundingTxidBytes: chanUpdate.Txid,
|
|
|
|
},
|
2018-11-26 04:12:38 +01:00
|
|
|
OutputIndex: chanUpdate.OutputIndex,
|
2016-11-11 02:37:21 +01:00
|
|
|
}, nil
|
|
|
|
case <-r.quit:
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-24 11:21:55 +02:00
|
|
|
// BatchOpenChannel attempts to open multiple single-funded channels in a
|
|
|
|
// single transaction in an atomic way. This means either all channel open
|
|
|
|
// requests succeed at once or all attempts are aborted if any of them fail.
|
|
|
|
// This is the safer variant of using PSBTs to manually fund a batch of
|
|
|
|
// channels through the OpenChannel RPC.
|
|
|
|
func (r *rpcServer) BatchOpenChannel(ctx context.Context,
|
|
|
|
in *lnrpc.BatchOpenChannelRequest) (*lnrpc.BatchOpenChannelResponse,
|
|
|
|
error) {
|
|
|
|
|
|
|
|
if err := r.canOpenChannel(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// We need the wallet kit server to do the heavy lifting on the PSBT
|
|
|
|
// part. If we didn't rely on re-using the wallet kit server's logic we
|
|
|
|
// would need to re-implement everything here. Since we deliver lnd with
|
|
|
|
// the wallet kit server enabled by default we can assume it's okay to
|
|
|
|
// make this functionality dependent on that server being active.
|
|
|
|
var walletKitServer walletrpc.WalletKitServer
|
|
|
|
for _, subServer := range r.subServers {
|
|
|
|
if subServer.Name() == walletrpc.SubServerName {
|
|
|
|
walletKitServer = subServer.(walletrpc.WalletKitServer)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if walletKitServer == nil {
|
|
|
|
return nil, fmt.Errorf("batch channel open is only possible " +
|
|
|
|
"if walletrpc subserver is active")
|
|
|
|
}
|
|
|
|
|
|
|
|
rpcsLog.Debugf("[batchopenchannel] request to open batch of %d "+
|
|
|
|
"channels", len(in.Channels))
|
|
|
|
|
|
|
|
// Make sure there is at least one channel to open. We could say we want
|
|
|
|
// at least two channels for a batch. But maybe it's nice if developers
|
|
|
|
// can use the same API for a single channel as well as a batch of
|
|
|
|
// channels.
|
|
|
|
if len(in.Channels) == 0 {
|
|
|
|
return nil, fmt.Errorf("specify at least one channel")
|
|
|
|
}
|
|
|
|
|
|
|
|
// In case we remove a pending channel from the database, we need to set
|
|
|
|
// a close height, so we'll just use the current best known height.
|
|
|
|
_, bestHeight, err := r.server.cc.ChainIO.GetBestBlock()
|
|
|
|
if err != nil {
|
2024-02-26 12:19:38 +01:00
|
|
|
return nil, fmt.Errorf("error fetching best block: %w", err)
|
2021-08-24 11:21:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// So far everything looks good and we can now start the heavy lifting
|
|
|
|
// that's done in the funding package.
|
|
|
|
requestParser := func(req *lnrpc.OpenChannelRequest) (
|
|
|
|
*funding.InitFundingMsg, error) {
|
|
|
|
|
|
|
|
return r.parseOpenChannelReq(req, false)
|
|
|
|
}
|
|
|
|
channelAbandoner := func(point *wire.OutPoint) error {
|
|
|
|
return r.abandonChan(point, uint32(bestHeight))
|
|
|
|
}
|
|
|
|
batcher := funding.NewBatcher(&funding.BatchConfig{
|
|
|
|
RequestParser: requestParser,
|
|
|
|
ChannelAbandoner: channelAbandoner,
|
|
|
|
ChannelOpener: r.server.OpenChannel,
|
|
|
|
WalletKitServer: walletKitServer,
|
|
|
|
Wallet: r.server.cc.Wallet,
|
|
|
|
NetParams: &r.server.cc.Wallet.Cfg.NetParams,
|
|
|
|
Quit: r.quit,
|
|
|
|
})
|
|
|
|
rpcPoints, err := batcher.BatchFund(ctx, in)
|
|
|
|
if err != nil {
|
2024-02-26 12:19:38 +01:00
|
|
|
return nil, fmt.Errorf("batch funding failed: %w", err)
|
2021-08-24 11:21:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Now all that's left to do is send back the response with the channel
|
|
|
|
// points we created.
|
|
|
|
return &lnrpc.BatchOpenChannelResponse{
|
|
|
|
PendingChannels: rpcPoints,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2018-01-11 05:59:30 +01:00
|
|
|
// CloseChannel attempts to close an active channel identified by its channel
|
2016-06-21 21:32:32 +02:00
|
|
|
// point. The actions of this method can additionally be augmented to attempt
|
|
|
|
// a force close after a timeout period in the case of an inactive peer.
|
2016-07-08 00:30:55 +02:00
|
|
|
func (r *rpcServer) CloseChannel(in *lnrpc.CloseChannelRequest,
|
|
|
|
updateStream lnrpc.Lightning_CloseChannelServer) error {
|
2017-08-22 08:25:41 +02:00
|
|
|
|
2019-10-23 23:37:01 +02:00
|
|
|
if !r.server.Started() {
|
|
|
|
return ErrServerNotActive
|
|
|
|
}
|
|
|
|
|
2018-12-12 00:56:00 +01:00
|
|
|
// If the user didn't specify a channel point, then we'll reject this
|
|
|
|
// request all together.
|
|
|
|
if in.GetChannelPoint() == nil {
|
|
|
|
return fmt.Errorf("must specify channel point in close channel")
|
|
|
|
}
|
|
|
|
|
2019-06-20 12:02:36 +02:00
|
|
|
// If force closing a channel, the fee set in the commitment transaction
|
|
|
|
// is used.
|
2021-04-30 12:35:53 +02:00
|
|
|
if in.Force && (in.SatPerByte != 0 || in.SatPerVbyte != 0 || // nolint:staticcheck
|
2021-03-11 01:29:50 +01:00
|
|
|
in.TargetConf != 0) {
|
|
|
|
|
2019-06-20 12:02:36 +02:00
|
|
|
return fmt.Errorf("force closing a channel uses a pre-defined fee")
|
|
|
|
}
|
|
|
|
|
2016-09-12 21:42:26 +02:00
|
|
|
force := in.Force
|
2016-06-21 21:32:32 +02:00
|
|
|
index := in.ChannelPoint.OutputIndex
|
2021-02-13 09:05:33 +01:00
|
|
|
txid, err := lnrpc.GetChanPointFundingTxid(in.GetChannelPoint())
|
2018-01-11 05:59:30 +01:00
|
|
|
if err != nil {
|
|
|
|
rpcsLog.Errorf("[closechannel] unable to get funding txid: %v", err)
|
|
|
|
return err
|
|
|
|
}
|
2017-01-04 01:02:51 +01:00
|
|
|
chanPoint := wire.NewOutPoint(txid, index)
|
2016-06-21 21:32:32 +02:00
|
|
|
|
2017-09-12 18:00:40 +02:00
|
|
|
rpcsLog.Tracef("[closechannel] request for ChannelPoint(%v), force=%v",
|
|
|
|
chanPoint, force)
|
2017-01-04 01:02:51 +01:00
|
|
|
|
|
|
|
var (
|
2018-12-20 18:52:27 +01:00
|
|
|
updateChan chan interface{}
|
2017-01-04 01:02:51 +01:00
|
|
|
errChan chan error
|
|
|
|
)
|
|
|
|
|
2017-09-12 18:00:40 +02:00
|
|
|
// TODO(roasbeef): if force and peer online then don't force?
|
|
|
|
|
2018-03-30 21:51:01 +02:00
|
|
|
// First, we'll fetch the channel as is, as we'll need to examine it
|
|
|
|
// regardless of if this is a force close or not.
|
2021-08-23 12:16:37 +02:00
|
|
|
channel, err := r.server.chanStateDB.FetchChannel(nil, *chanPoint)
|
2018-03-30 21:51:01 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-08-12 16:03:35 +02:00
|
|
|
// We can't coop or force close restored channels or channels that have
|
|
|
|
// experienced local data loss. Normally we would detect this in the
|
|
|
|
// channel arbitrator if the channel has the status
|
|
|
|
// ChanStatusLocalDataLoss after connecting to its peer. But if no
|
|
|
|
// connection can be established, the channel arbitrator doesn't know it
|
|
|
|
// can't be force closed yet.
|
|
|
|
if channel.HasChanStatus(channeldb.ChanStatusRestored) ||
|
|
|
|
channel.HasChanStatus(channeldb.ChanStatusLocalDataLoss) {
|
|
|
|
|
|
|
|
return fmt.Errorf("cannot close channel with state: %v",
|
|
|
|
channel.ChanStatus())
|
|
|
|
}
|
|
|
|
|
2020-04-03 02:40:08 +02:00
|
|
|
// Retrieve the best height of the chain, which we'll use to complete
|
|
|
|
// either closing flow.
|
2020-10-06 17:03:42 +02:00
|
|
|
_, bestHeight, err := r.server.cc.ChainIO.GetBestBlock()
|
2020-03-14 00:58:05 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-01-04 01:02:51 +01:00
|
|
|
// If a force closure was requested, then we'll handle all the details
|
|
|
|
// around the creation and broadcast of the unilateral closure
|
|
|
|
// transaction here rather than going to the switch as we don't require
|
|
|
|
// interaction from the peer.
|
|
|
|
if force {
|
2017-05-11 02:27:05 +02:00
|
|
|
|
2017-05-05 01:04:41 +02:00
|
|
|
// As we're force closing this channel, as a precaution, we'll
|
|
|
|
// ensure that the switch doesn't continue to see this channel
|
|
|
|
// as eligible for forwarding HTLC's. If the peer is online,
|
|
|
|
// then we'll also purge all of its indexes.
|
2020-04-03 02:39:47 +02:00
|
|
|
remotePub := channel.IdentityPub
|
2017-08-09 00:49:32 +02:00
|
|
|
if peer, err := r.server.FindPeer(remotePub); err == nil {
|
2017-05-16 03:13:39 +02:00
|
|
|
// TODO(roasbeef): actually get the active channel
|
|
|
|
// instead too?
|
|
|
|
// * so only need to grab from database
|
2020-04-03 02:39:47 +02:00
|
|
|
peer.WipeChannel(&channel.FundingOutpoint)
|
2017-05-05 01:04:41 +02:00
|
|
|
} else {
|
2024-01-29 22:19:15 +01:00
|
|
|
chanID := lnwire.NewChanIDFromOutPoint(
|
|
|
|
channel.FundingOutpoint,
|
|
|
|
)
|
2017-05-02 22:04:58 +02:00
|
|
|
r.server.htlcSwitch.RemoveLink(chanID)
|
2017-05-05 01:04:41 +02:00
|
|
|
}
|
|
|
|
|
2017-05-05 01:05:34 +02:00
|
|
|
// With the necessary indexes cleaned up, we'll now force close
|
|
|
|
// the channel.
|
2018-01-17 05:03:28 +01:00
|
|
|
chainArbitrator := r.server.chainArb
|
|
|
|
closingTx, err := chainArbitrator.ForceCloseContract(
|
|
|
|
*chanPoint,
|
|
|
|
)
|
2017-01-04 01:02:51 +01:00
|
|
|
if err != nil {
|
2017-02-21 04:53:04 +01:00
|
|
|
rpcsLog.Errorf("unable to force close transaction: %v", err)
|
2017-01-04 01:02:51 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-01-17 05:03:28 +01:00
|
|
|
closingTxid := closingTx.TxHash()
|
|
|
|
|
2017-05-05 01:05:34 +02:00
|
|
|
// With the transaction broadcast, we send our first update to
|
|
|
|
// the client.
|
2018-12-20 18:52:27 +01:00
|
|
|
updateChan = make(chan interface{}, 2)
|
2020-07-02 23:46:06 +02:00
|
|
|
updateChan <- &peer.PendingUpdate{
|
2018-12-20 18:52:27 +01:00
|
|
|
Txid: closingTxid[:],
|
2017-05-05 01:05:34 +02:00
|
|
|
}
|
2016-11-29 03:44:14 +01:00
|
|
|
|
2017-05-05 01:05:34 +02:00
|
|
|
errChan = make(chan error, 1)
|
2020-10-06 17:03:42 +02:00
|
|
|
notifier := r.server.cc.ChainNotifier
|
2020-07-02 23:46:06 +02:00
|
|
|
go peer.WaitForChanToClose(uint32(bestHeight), notifier, errChan, chanPoint,
|
2018-05-31 07:18:44 +02:00
|
|
|
&closingTxid, closingTx.TxOut[0].PkScript, func() {
|
2017-05-11 02:27:05 +02:00
|
|
|
// Respond to the local subsystem which
|
|
|
|
// requested the channel closure.
|
2020-07-02 23:46:06 +02:00
|
|
|
updateChan <- &peer.ChannelCloseUpdate{
|
2018-12-20 18:52:27 +01:00
|
|
|
ClosingTxid: closingTxid[:],
|
|
|
|
Success: true,
|
2017-05-11 02:27:05 +02:00
|
|
|
}
|
2017-08-05 03:29:14 +02:00
|
|
|
})
|
2017-01-04 01:02:51 +01:00
|
|
|
} else {
|
2020-04-03 02:40:08 +02:00
|
|
|
// If this is a frozen channel, then we only allow the co-op
|
2020-07-02 02:45:59 +02:00
|
|
|
// close to proceed if we were the responder to this channel if
|
|
|
|
// the absolute thaw height has not been met.
|
|
|
|
if channel.IsInitiator {
|
|
|
|
absoluteThawHeight, err := channel.AbsoluteThawHeight()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if uint32(bestHeight) < absoluteThawHeight {
|
|
|
|
return fmt.Errorf("cannot co-op close frozen "+
|
|
|
|
"channel as initiator until height=%v, "+
|
|
|
|
"(current_height=%v)",
|
|
|
|
absoluteThawHeight, bestHeight)
|
|
|
|
}
|
2020-04-03 02:40:08 +02:00
|
|
|
}
|
|
|
|
|
2018-04-05 18:52:35 +02:00
|
|
|
// If the link is not known by the switch, we cannot gracefully close
|
|
|
|
// the channel.
|
2024-01-29 22:19:15 +01:00
|
|
|
channelID := lnwire.NewChanIDFromOutPoint(*chanPoint)
|
2018-04-05 18:52:35 +02:00
|
|
|
if _, err := r.server.htlcSwitch.GetLink(channelID); err != nil {
|
|
|
|
rpcsLog.Debugf("Trying to non-force close offline channel with "+
|
|
|
|
"chan_point=%v", chanPoint)
|
|
|
|
return fmt.Errorf("unable to gracefully close channel while peer "+
|
|
|
|
"is offline (try force closing it instead): %v", err)
|
|
|
|
}
|
|
|
|
|
2024-04-25 20:34:25 +02:00
|
|
|
// Keep the old behavior prior to 0.18.0 - when the user
|
|
|
|
// doesn't set fee rate or conf target, the default conf target
|
|
|
|
// of 6 is used.
|
|
|
|
targetConf := maybeUseDefaultConf(
|
|
|
|
in.SatPerByte, in.SatPerVbyte, uint32(in.TargetConf),
|
|
|
|
)
|
|
|
|
|
2018-01-17 05:03:28 +01:00
|
|
|
// Based on the passed fee related parameters, we'll determine
|
|
|
|
// an appropriate fee rate for the cooperative closure
|
2017-11-23 07:57:23 +01:00
|
|
|
// transaction.
|
2023-04-28 01:33:21 +02:00
|
|
|
feeRate, err := lnrpc.CalculateFeeRate(
|
2021-04-30 12:35:53 +02:00
|
|
|
uint64(in.SatPerByte), in.SatPerVbyte, // nolint:staticcheck
|
2024-04-25 20:34:25 +02:00
|
|
|
targetConf, r.server.cc.FeeEstimator,
|
2017-11-23 07:57:23 +01:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-07-28 03:39:38 +02:00
|
|
|
rpcsLog.Debugf("Target sat/kw for closing transaction: %v",
|
2018-02-13 15:13:01 +01:00
|
|
|
int64(feeRate))
|
2017-11-23 07:57:23 +01:00
|
|
|
|
2024-01-11 00:43:26 +01:00
|
|
|
// If the user hasn't specified NoWait, then before we attempt
|
|
|
|
// to close the channel we ensure there are no active HTLCs on
|
|
|
|
// the link.
|
|
|
|
if !in.NoWait && len(channel.ActiveHtlcs()) != 0 {
|
2018-03-30 22:04:59 +02:00
|
|
|
return fmt.Errorf("cannot co-op close channel " +
|
2018-03-30 21:51:01 +02:00
|
|
|
"with active htlcs")
|
|
|
|
}
|
|
|
|
|
2017-01-04 01:02:51 +01:00
|
|
|
// Otherwise, the caller has requested a regular interactive
|
|
|
|
// cooperative channel closure. So we'll forward the request to
|
|
|
|
// the htlc switch which will handle the negotiation and
|
|
|
|
// broadcast details.
|
2019-12-09 14:44:00 +01:00
|
|
|
|
|
|
|
var deliveryScript lnwire.DeliveryAddress
|
|
|
|
|
|
|
|
// If a delivery address to close out to was specified, decode it.
|
|
|
|
if len(in.DeliveryAddress) > 0 {
|
|
|
|
// Decode the address provided.
|
|
|
|
addr, err := btcutil.DecodeAddress(
|
2020-08-04 20:56:31 +02:00
|
|
|
in.DeliveryAddress, r.cfg.ActiveNetParams.Params,
|
2019-12-09 14:44:00 +01:00
|
|
|
)
|
|
|
|
if err != nil {
|
2022-04-23 04:03:08 +02:00
|
|
|
return fmt.Errorf("invalid delivery address: "+
|
|
|
|
"%v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !addr.IsForNet(r.cfg.ActiveNetParams.Params) {
|
|
|
|
return fmt.Errorf("delivery address is not "+
|
|
|
|
"for %s",
|
|
|
|
r.cfg.ActiveNetParams.Params.Name)
|
2019-12-09 14:44:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Create a script to pay out to the address provided.
|
|
|
|
deliveryScript, err = txscript.PayToAddrScript(addr)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-27 01:41:59 +02:00
|
|
|
maxFee := chainfee.SatPerKVByte(
|
|
|
|
in.MaxFeePerVbyte * 1000,
|
|
|
|
).FeePerKWeight()
|
2018-06-15 02:14:31 +02:00
|
|
|
updateChan, errChan = r.server.htlcSwitch.CloseLink(
|
2021-09-14 04:00:36 +02:00
|
|
|
chanPoint, contractcourt.CloseRegular, feeRate,
|
2022-07-27 01:41:59 +02:00
|
|
|
maxFee, deliveryScript,
|
2018-06-15 02:14:31 +02:00
|
|
|
)
|
2017-01-04 01:02:51 +01:00
|
|
|
}
|
2024-01-11 00:43:26 +01:00
|
|
|
|
|
|
|
// If the user doesn't want to wait for the txid to come back then we
|
|
|
|
// will send an empty update to kick off the stream.
|
|
|
|
if in.NoWait {
|
|
|
|
rpcsLog.Trace("[closechannel] sending instant update")
|
|
|
|
if err := updateStream.Send(
|
|
|
|
&lnrpc.CloseStatusUpdate{
|
|
|
|
Update: &lnrpc.CloseStatusUpdate_CloseInstant{},
|
|
|
|
},
|
|
|
|
); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2016-08-31 01:52:53 +02:00
|
|
|
out:
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case err := <-errChan:
|
|
|
|
rpcsLog.Errorf("[closechannel] unable to close "+
|
2017-01-04 01:02:51 +01:00
|
|
|
"ChannelPoint(%v): %v", chanPoint, err)
|
2016-07-08 00:30:55 +02:00
|
|
|
return err
|
2016-08-31 01:52:53 +02:00
|
|
|
case closingUpdate := <-updateChan:
|
2018-12-20 18:52:27 +01:00
|
|
|
rpcClosingUpdate, err := createRPCCloseUpdate(
|
|
|
|
closingUpdate,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-08-31 04:34:13 +02:00
|
|
|
rpcsLog.Tracef("[closechannel] sending update: %v",
|
2018-12-20 18:52:27 +01:00
|
|
|
rpcClosingUpdate)
|
|
|
|
|
|
|
|
if err := updateStream.Send(rpcClosingUpdate); err != nil {
|
2016-08-31 01:52:53 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// If a final channel closing updates is being sent,
|
|
|
|
// then we can break out of our dispatch loop as we no
|
|
|
|
// longer need to process any further updates.
|
2018-12-20 18:52:27 +01:00
|
|
|
switch closeUpdate := closingUpdate.(type) {
|
2020-07-02 23:46:06 +02:00
|
|
|
case *peer.ChannelCloseUpdate:
|
2018-12-20 18:52:27 +01:00
|
|
|
h, _ := chainhash.NewHash(closeUpdate.ClosingTxid)
|
2016-09-12 21:42:26 +02:00
|
|
|
rpcsLog.Infof("[closechannel] close completed: "+
|
2016-08-31 01:52:53 +02:00
|
|
|
"txid(%v)", h)
|
|
|
|
break out
|
|
|
|
}
|
|
|
|
case <-r.quit:
|
|
|
|
return nil
|
2016-07-08 00:30:55 +02:00
|
|
|
}
|
2016-06-21 21:32:32 +02:00
|
|
|
}
|
|
|
|
|
2016-07-08 00:30:55 +02:00
|
|
|
return nil
|
2016-06-21 21:32:32 +02:00
|
|
|
}
|
2016-01-02 05:27:40 +01:00
|
|
|
|
2018-12-20 18:52:27 +01:00
|
|
|
func createRPCCloseUpdate(update interface{}) (
|
|
|
|
*lnrpc.CloseStatusUpdate, error) {
|
|
|
|
|
|
|
|
switch u := update.(type) {
|
2020-07-02 23:46:06 +02:00
|
|
|
case *peer.ChannelCloseUpdate:
|
2018-12-20 18:52:27 +01:00
|
|
|
return &lnrpc.CloseStatusUpdate{
|
|
|
|
Update: &lnrpc.CloseStatusUpdate_ChanClose{
|
|
|
|
ChanClose: &lnrpc.ChannelCloseUpdate{
|
|
|
|
ClosingTxid: u.ClosingTxid,
|
2022-04-12 19:30:11 +02:00
|
|
|
Success: u.Success,
|
2018-12-20 18:52:27 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}, nil
|
2020-07-02 23:46:06 +02:00
|
|
|
case *peer.PendingUpdate:
|
2018-12-20 18:52:27 +01:00
|
|
|
return &lnrpc.CloseStatusUpdate{
|
|
|
|
Update: &lnrpc.CloseStatusUpdate_ClosePending{
|
|
|
|
ClosePending: &lnrpc.PendingUpdate{
|
|
|
|
Txid: u.Txid,
|
|
|
|
OutputIndex: u.OutputIndex,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, errors.New("unknown close status update")
|
|
|
|
}
|
|
|
|
|
2019-11-14 01:09:20 +01:00
|
|
|
// abandonChanFromGraph attempts to remove a channel from the channel graph. If
|
|
|
|
// we can't find the chanID in the graph, then we assume it has already been
|
|
|
|
// removed, and will return a nop.
|
|
|
|
func abandonChanFromGraph(chanGraph *channeldb.ChannelGraph,
|
|
|
|
chanPoint *wire.OutPoint) error {
|
|
|
|
|
|
|
|
// First, we'll obtain the channel ID. If we can't locate this, then
|
|
|
|
// it's the case that the channel may have already been removed from
|
|
|
|
// the graph, so we'll return a nil error.
|
|
|
|
chanID, err := chanGraph.ChannelID(chanPoint)
|
|
|
|
switch {
|
2024-06-06 19:14:33 +02:00
|
|
|
case errors.Is(err, channeldb.ErrEdgeNotFound):
|
2019-11-14 01:09:20 +01:00
|
|
|
return nil
|
|
|
|
case err != nil:
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the channel ID is still in the graph, then that means the channel
|
|
|
|
// is still open, so we'll now move to purge it from the graph.
|
server+funding: allow scid-alias, zero-conf chantypes, scid-alias
feature-bit channels
This allows opening zero-conf chan-type, scid-alias chan-type, and
scid-alias feature-bit channels. scid-alias chan-type channels are
required to be private. Two paths are available for opening a zero-conf
channel:
* explicit chan-type negotiation
* LDK carve-out where chan-types are not used, LND is on the
receiving end, and a ChannelAcceptor is used to enable zero-conf
When a zero-conf channel is negotiated, the funding manager:
* sends a FundingLocked with an alias
* waits for a FundingLocked from the remote peer
* calls addToRouterGraph to persist the channel using our alias in
the graph. The peer's alias is used to send them a ChannelUpdate.
* wait for six confirmations. If public, the alias edge in the
graph is deleted and replaced (not atomically) with the confirmed
edge. Our policy is also read-and-replaced, but the counterparty's
policy won't exist until they send it to us.
When a scid-alias-feature channel is negotiated, the funding manager:
* sends a FundingLocked with an alias:
* calls addToRouterGraph, sends ChannelUpdate with the confirmed SCID
since it exists.
* when six confirmations occurs, the edge is deleted and re-inserted
since the peer may have sent us an alias ChannelUpdate that we are
storing in the graph.
Since it is possible for a user to toggle the scid-alias-feature-bit
to on while channels exist in the funding manager, care has been taken
to ensure that an alias is ALWAYS sent in the funding_locked message
if this happens.
2022-04-04 22:47:05 +02:00
|
|
|
return chanGraph.DeleteChannelEdges(false, true, chanID)
|
2019-11-14 01:09:20 +01:00
|
|
|
}
|
|
|
|
|
2021-08-24 11:21:54 +02:00
|
|
|
// abandonChan removes a channel from the database, graph and contract court.
|
|
|
|
func (r *rpcServer) abandonChan(chanPoint *wire.OutPoint,
|
|
|
|
bestHeight uint32) error {
|
|
|
|
|
2023-07-11 09:09:39 +02:00
|
|
|
// Before we remove the channel we cancel the rebroadcasting of the
|
|
|
|
// transaction. If this transaction does not exist in the rebroadcast
|
|
|
|
// queue anymore it is a noop.
|
|
|
|
txid, err := chainhash.NewHash(chanPoint.Hash[:])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
r.server.cc.Wallet.CancelRebroadcast(*txid)
|
|
|
|
|
2021-08-24 11:21:54 +02:00
|
|
|
// Abandoning a channel is a three-step process: remove from the open
|
|
|
|
// channel state, remove from the graph, remove from the contract
|
|
|
|
// court. Between any step it's possible that the users restarts the
|
|
|
|
// process all over again. As a result, each of the steps below are
|
|
|
|
// intended to be idempotent.
|
2023-07-11 09:09:39 +02:00
|
|
|
err = r.server.chanStateDB.AbandonChannel(chanPoint, bestHeight)
|
2021-08-24 11:21:54 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = abandonChanFromGraph(r.server.graphDB, chanPoint)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = r.server.chainArb.ResolveContract(*chanPoint)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// If this channel was in the process of being closed, but didn't fully
|
|
|
|
// close, then it's possible that the nursery is hanging on to some
|
|
|
|
// state. To err on the side of caution, we'll now attempt to wipe any
|
|
|
|
// state for this channel from the nursery.
|
2021-09-14 04:00:36 +02:00
|
|
|
err = r.server.utxoNursery.RemoveChannel(chanPoint)
|
|
|
|
if err != nil && err != contractcourt.ErrContractNotFound {
|
2021-08-24 11:21:54 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finally, notify the backup listeners that the channel can be removed
|
|
|
|
// from any channel backups.
|
|
|
|
r.server.channelNotifier.NotifyClosedChannelEvent(*chanPoint)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-05-29 11:26:47 +02:00
|
|
|
// AbandonChannel removes all channel state from the database except for a
|
|
|
|
// close summary. This method can be used to get rid of permanently unusable
|
|
|
|
// channels due to bugs fixed in newer versions of lnd.
|
2020-08-21 13:08:36 +02:00
|
|
|
func (r *rpcServer) AbandonChannel(_ context.Context,
|
2018-05-29 11:26:47 +02:00
|
|
|
in *lnrpc.AbandonChannelRequest) (*lnrpc.AbandonChannelResponse, error) {
|
|
|
|
|
2018-09-26 11:43:46 +02:00
|
|
|
// If this isn't the dev build, then we won't allow the RPC to be
|
2018-09-24 03:58:39 +02:00
|
|
|
// executed, as it's an advanced feature and won't be activated in
|
2020-08-21 13:08:36 +02:00
|
|
|
// regular production/release builds except for the explicit case of
|
2021-05-26 22:39:56 +02:00
|
|
|
// externally funded channels that are still pending. Due to repeated
|
|
|
|
// requests, we also allow this requirement to be overwritten by a new
|
|
|
|
// flag that attests to the user knowing what they're doing and the risk
|
|
|
|
// associated with the command/RPC.
|
|
|
|
if !in.IKnowWhatIAmDoing && !in.PendingFundingShimOnly &&
|
|
|
|
!build.IsDevBuild() {
|
|
|
|
|
2018-05-29 11:26:47 +02:00
|
|
|
return nil, fmt.Errorf("AbandonChannel RPC call only " +
|
2018-09-26 11:43:46 +02:00
|
|
|
"available in dev builds")
|
2018-05-29 11:26:47 +02:00
|
|
|
}
|
|
|
|
|
2018-09-24 03:58:39 +02:00
|
|
|
// We'll parse out the arguments to we can obtain the chanPoint of the
|
|
|
|
// target channel.
|
2021-02-13 09:05:33 +01:00
|
|
|
txid, err := lnrpc.GetChanPointFundingTxid(in.GetChannelPoint())
|
2018-05-29 11:26:47 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-09-24 03:58:39 +02:00
|
|
|
index := in.ChannelPoint.OutputIndex
|
2018-05-29 11:26:47 +02:00
|
|
|
chanPoint := wire.NewOutPoint(txid, index)
|
|
|
|
|
2019-11-14 01:09:20 +01:00
|
|
|
// When we remove the channel from the database, we need to set a close
|
|
|
|
// height, so we'll just use the current best known height.
|
2020-10-06 17:03:42 +02:00
|
|
|
_, bestHeight, err := r.server.cc.ChainIO.GetBestBlock()
|
2018-05-29 11:26:47 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-08-23 12:16:37 +02:00
|
|
|
dbChan, err := r.server.chanStateDB.FetchChannel(nil, *chanPoint)
|
2019-11-14 01:09:20 +01:00
|
|
|
switch {
|
|
|
|
// If the channel isn't found in the set of open channels, then we can
|
|
|
|
// continue on as it can't be loaded into the link/peer.
|
|
|
|
case err == channeldb.ErrChannelNotFound:
|
|
|
|
break
|
|
|
|
|
|
|
|
// If the channel is still known to be open, then before we modify any
|
|
|
|
// on-disk state, we'll remove the channel from the switch and peer
|
|
|
|
// state if it's been loaded in.
|
|
|
|
case err == nil:
|
2020-08-21 13:08:36 +02:00
|
|
|
// If the user requested the more safe version that only allows
|
|
|
|
// the removal of externally (shim) funded channels that are
|
|
|
|
// still pending, we enforce this option now that we know the
|
|
|
|
// state of the channel.
|
|
|
|
//
|
|
|
|
// TODO(guggero): Properly store the funding type (wallet, shim,
|
|
|
|
// PSBT) on the channel so we don't need to use the thaw height.
|
|
|
|
isShimFunded := dbChan.ThawHeight > 0
|
|
|
|
isPendingShimFunded := isShimFunded && dbChan.IsPending
|
2021-05-26 22:39:56 +02:00
|
|
|
if !in.IKnowWhatIAmDoing && in.PendingFundingShimOnly &&
|
|
|
|
!isPendingShimFunded {
|
|
|
|
|
2020-08-21 13:08:36 +02:00
|
|
|
return nil, fmt.Errorf("channel %v is not externally "+
|
|
|
|
"funded or not pending", chanPoint)
|
|
|
|
}
|
|
|
|
|
2019-11-14 01:09:20 +01:00
|
|
|
// We'll mark the channel as borked before we remove the state
|
|
|
|
// from the switch/peer so it won't be loaded back in if the
|
|
|
|
// peer reconnects.
|
|
|
|
if err := dbChan.MarkBorked(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
remotePub := dbChan.IdentityPub
|
|
|
|
if peer, err := r.server.FindPeer(remotePub); err == nil {
|
2020-04-03 02:39:29 +02:00
|
|
|
peer.WipeChannel(chanPoint)
|
2019-11-14 01:09:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-08-24 11:21:54 +02:00
|
|
|
// Remove the channel from the graph, database and contract court.
|
|
|
|
if err := r.abandonChan(chanPoint, uint32(bestHeight)); err != nil {
|
2018-09-24 03:58:39 +02:00
|
|
|
return nil, err
|
2018-05-29 11:26:47 +02:00
|
|
|
}
|
2020-03-04 10:57:06 +01:00
|
|
|
|
2018-05-29 11:26:47 +02:00
|
|
|
return &lnrpc.AbandonChannelResponse{}, nil
|
|
|
|
}
|
|
|
|
|
2017-07-28 01:39:49 +02:00
|
|
|
// GetInfo returns general information concerning the lightning node including
|
2018-02-07 04:13:07 +01:00
|
|
|
// its identity pubkey, alias, the chains it is connected to, and information
|
2017-07-28 01:39:49 +02:00
|
|
|
// concerning the number of open+pending channels.
|
2021-05-25 09:45:48 +02:00
|
|
|
func (r *rpcServer) GetInfo(_ context.Context,
|
|
|
|
_ *lnrpc.GetInfoRequest) (*lnrpc.GetInfoResponse, error) {
|
2017-08-22 08:25:41 +02:00
|
|
|
|
2016-07-06 03:57:08 +02:00
|
|
|
serverPeers := r.server.Peers()
|
|
|
|
|
2021-08-03 09:57:26 +02:00
|
|
|
openChannels, err := r.server.chanStateDB.FetchAllOpenChannels()
|
2018-08-28 18:24:08 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-02-26 00:21:08 +01:00
|
|
|
|
|
|
|
var activeChannels uint32
|
|
|
|
for _, channel := range openChannels {
|
2024-01-29 22:19:15 +01:00
|
|
|
chanID := lnwire.NewChanIDFromOutPoint(channel.FundingOutpoint)
|
2019-02-26 00:21:08 +01:00
|
|
|
if r.server.htlcSwitch.HasActiveLink(chanID) {
|
|
|
|
activeChannels++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-28 18:24:08 +02:00
|
|
|
inactiveChannels := uint32(len(openChannels)) - activeChannels
|
|
|
|
|
2021-08-03 09:57:26 +02:00
|
|
|
pendingChannels, err := r.server.chanStateDB.FetchPendingChannels()
|
2017-01-23 08:31:01 +01:00
|
|
|
if err != nil {
|
2017-08-09 03:01:15 +02:00
|
|
|
return nil, fmt.Errorf("unable to get retrieve pending "+
|
2017-06-17 00:11:02 +02:00
|
|
|
"channels: %v", err)
|
2017-01-23 08:31:01 +01:00
|
|
|
}
|
2017-08-09 03:01:15 +02:00
|
|
|
nPendingChannels := uint32(len(pendingChannels))
|
2017-01-23 08:31:01 +01:00
|
|
|
|
2020-04-28 10:06:21 +02:00
|
|
|
idPub := r.server.identityECDH.PubKey().SerializeCompressed()
|
2018-01-07 06:54:14 +01:00
|
|
|
encodedIDPub := hex.EncodeToString(idPub)
|
2016-07-06 03:57:08 +02:00
|
|
|
|
2020-10-06 17:03:42 +02:00
|
|
|
bestHash, bestHeight, err := r.server.cc.ChainIO.GetBestBlock()
|
2016-12-13 00:34:47 +01:00
|
|
|
if err != nil {
|
2024-02-26 12:19:38 +01:00
|
|
|
return nil, fmt.Errorf("unable to get best block info: %w", err)
|
2016-12-13 00:34:47 +01:00
|
|
|
}
|
|
|
|
|
2020-10-06 17:03:42 +02:00
|
|
|
isSynced, bestHeaderTimestamp, err := r.server.cc.Wallet.IsSynced()
|
2016-12-13 00:34:47 +01:00
|
|
|
if err != nil {
|
2017-05-15 04:20:26 +02:00
|
|
|
return nil, fmt.Errorf("unable to sync PoV of the wallet "+
|
|
|
|
"with current best block in the main chain: %v", err)
|
2016-12-13 00:34:47 +01:00
|
|
|
}
|
|
|
|
|
2021-05-25 09:45:48 +02:00
|
|
|
// If the router does full channel validation, it has a lot of work to
|
|
|
|
// do for each block. So it might be possible that it isn't yet up to
|
|
|
|
// date with the most recent block, even if the wallet is. This can
|
|
|
|
// happen in environments with high CPU load (such as parallel itests).
|
|
|
|
// Since the `synced_to_chain` flag in the response of this call is used
|
|
|
|
// by many wallets (and also our itests) to make sure everything's up to
|
|
|
|
// date, we add the router's state to it. So the flag will only toggle
|
|
|
|
// to true once the router was also able to catch up.
|
|
|
|
if !r.cfg.Routing.AssumeChannelValid {
|
2024-06-17 01:30:01 +02:00
|
|
|
routerHeight := r.server.graphBuilder.SyncedHeight()
|
2021-05-25 09:45:48 +02:00
|
|
|
isSynced = isSynced && uint32(bestHeight) == routerHeight
|
|
|
|
}
|
2021-05-07 19:11:20 +02:00
|
|
|
|
2020-10-06 20:17:14 +02:00
|
|
|
network := lncfg.NormalizeNetwork(r.cfg.ActiveNetParams.Name)
|
2023-08-03 18:12:49 +02:00
|
|
|
activeChains := []*lnrpc.Chain{
|
|
|
|
{
|
|
|
|
Chain: BitcoinChainName,
|
2019-01-02 16:10:12 +01:00
|
|
|
Network: network,
|
2023-08-03 18:12:49 +02:00
|
|
|
},
|
2017-05-03 04:51:33 +02:00
|
|
|
}
|
2017-06-06 00:18:06 +02:00
|
|
|
|
2018-01-07 06:54:14 +01:00
|
|
|
// Check if external IP addresses were provided to lnd and use them
|
|
|
|
// to set the URIs.
|
2023-04-05 10:21:59 +02:00
|
|
|
nodeAnn := r.server.getNodeAnnouncement()
|
|
|
|
|
2018-01-07 06:54:14 +01:00
|
|
|
addrs := nodeAnn.Addresses
|
|
|
|
uris := make([]string, len(addrs))
|
|
|
|
for i, addr := range addrs {
|
|
|
|
uris[i] = fmt.Sprintf("%s@%s", encodedIDPub, addr.String())
|
|
|
|
}
|
|
|
|
|
2019-07-29 22:31:05 +02:00
|
|
|
isGraphSynced := r.server.authGossiper.SyncManager().IsGraphSynced()
|
|
|
|
|
2019-12-17 20:58:28 +01:00
|
|
|
features := make(map[uint32]*lnrpc.Feature)
|
|
|
|
sets := r.server.featureMgr.ListSets()
|
|
|
|
|
|
|
|
for _, set := range sets {
|
|
|
|
// Get the a list of lnrpc features for each set we support.
|
|
|
|
featureVector := r.server.featureMgr.Get(set)
|
|
|
|
rpcFeatures := invoicesrpc.CreateRPCFeatures(featureVector)
|
|
|
|
|
|
|
|
// Add the features to our map of features, allowing over writing of
|
|
|
|
// existing values because features in different sets with the same bit
|
|
|
|
// are duplicated across sets.
|
|
|
|
for bit, feature := range rpcFeatures {
|
|
|
|
features[bit] = feature
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-15 03:16:53 +01:00
|
|
|
// TODO(roasbeef): add synced height n stuff
|
2023-02-13 11:53:10 +01:00
|
|
|
|
|
|
|
isTestNet := chainreg.IsTestnet(&r.cfg.ActiveNetParams)
|
2024-06-17 01:30:01 +02:00
|
|
|
nodeColor := graph.EncodeHexColor(nodeAnn.RGBColor)
|
2023-02-13 11:53:10 +01:00
|
|
|
version := build.Version() + " commit=" + build.Commit
|
|
|
|
|
2016-07-06 03:57:08 +02:00
|
|
|
return &lnrpc.GetInfoResponse{
|
2023-02-13 11:44:48 +01:00
|
|
|
IdentityPubkey: encodedIDPub,
|
|
|
|
NumPendingChannels: nPendingChannels,
|
|
|
|
NumActiveChannels: activeChannels,
|
|
|
|
NumInactiveChannels: inactiveChannels,
|
|
|
|
NumPeers: uint32(len(serverPeers)),
|
|
|
|
BlockHeight: uint32(bestHeight),
|
|
|
|
BlockHash: bestHash.String(),
|
|
|
|
SyncedToChain: isSynced,
|
|
|
|
Testnet: isTestNet,
|
|
|
|
Chains: activeChains,
|
|
|
|
Uris: uris,
|
|
|
|
Alias: nodeAnn.Alias.String(),
|
|
|
|
Color: nodeColor,
|
|
|
|
BestHeaderTimestamp: bestHeaderTimestamp,
|
|
|
|
Version: version,
|
|
|
|
CommitHash: build.CommitHash,
|
|
|
|
SyncedToGraph: isGraphSynced,
|
|
|
|
Features: features,
|
|
|
|
RequireHtlcInterceptor: r.cfg.RequireInterceptor,
|
|
|
|
StoreFinalHtlcResolutions: r.cfg.StoreFinalHtlcResolutions,
|
2016-07-06 03:57:08 +02:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2023-11-16 04:19:24 +01:00
|
|
|
// GetDebugInfo returns debug information concerning the state of the daemon
|
|
|
|
// and its subsystems. This includes the full configuration and the latest log
|
|
|
|
// entries from the log file.
|
|
|
|
func (r *rpcServer) GetDebugInfo(_ context.Context,
|
|
|
|
_ *lnrpc.GetDebugInfoRequest) (*lnrpc.GetDebugInfoResponse, error) {
|
|
|
|
|
2024-01-31 07:58:10 +01:00
|
|
|
flatConfig, _, err := configToFlatMap(*r.cfg)
|
2023-11-16 04:19:24 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("error converting config to flat map: "+
|
|
|
|
"%w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
logFileName := filepath.Join(r.cfg.LogDir, defaultLogFilename)
|
|
|
|
logContent, err := os.ReadFile(logFileName)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("error reading log file '%s': %w",
|
|
|
|
logFileName, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &lnrpc.GetDebugInfoResponse{
|
|
|
|
Config: flatConfig,
|
|
|
|
Log: strings.Split(string(logContent), "\n"),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2020-06-09 08:47:17 +02:00
|
|
|
// GetRecoveryInfo returns a boolean indicating whether the wallet is started
|
|
|
|
// in recovery mode, whether the recovery is finished, and the progress made
|
|
|
|
// so far.
|
|
|
|
func (r *rpcServer) GetRecoveryInfo(ctx context.Context,
|
|
|
|
in *lnrpc.GetRecoveryInfoRequest) (*lnrpc.GetRecoveryInfoResponse, error) {
|
|
|
|
|
2020-10-06 17:03:42 +02:00
|
|
|
isRecoveryMode, progress, err := r.server.cc.Wallet.GetRecoveryInfo()
|
2020-06-09 08:47:17 +02:00
|
|
|
if err != nil {
|
2024-02-26 12:19:38 +01:00
|
|
|
return nil, fmt.Errorf("unable to get wallet recovery info: %w",
|
|
|
|
err)
|
2020-06-09 08:47:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
rpcsLog.Debugf("[getrecoveryinfo] is recovery mode=%v, progress=%v",
|
|
|
|
isRecoveryMode, progress)
|
|
|
|
|
|
|
|
return &lnrpc.GetRecoveryInfoResponse{
|
|
|
|
RecoveryMode: isRecoveryMode,
|
|
|
|
RecoveryFinished: progress == 1,
|
|
|
|
Progress: progress,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2016-06-21 20:52:09 +02:00
|
|
|
// ListPeers returns a verbose listing of all currently active peers.
|
|
|
|
func (r *rpcServer) ListPeers(ctx context.Context,
|
|
|
|
in *lnrpc.ListPeersRequest) (*lnrpc.ListPeersResponse, error) {
|
2017-08-22 08:25:41 +02:00
|
|
|
|
2016-06-21 20:52:09 +02:00
|
|
|
serverPeers := r.server.Peers()
|
|
|
|
resp := &lnrpc.ListPeersResponse{
|
|
|
|
Peers: make([]*lnrpc.Peer, 0, len(serverPeers)),
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, serverPeer := range serverPeers {
|
2017-01-26 03:24:59 +01:00
|
|
|
var (
|
|
|
|
satSent int64
|
|
|
|
satRecv int64
|
|
|
|
)
|
|
|
|
|
|
|
|
// In order to display the total number of satoshis of outbound
|
|
|
|
// (sent) and inbound (recv'd) satoshis that have been
|
|
|
|
// transported through this peer, we'll sum up the sent/recv'd
|
2017-05-18 20:55:25 +02:00
|
|
|
// values for each of the active channels we have with the
|
2017-01-26 03:24:59 +01:00
|
|
|
// peer.
|
|
|
|
chans := serverPeer.ChannelSnapshots()
|
|
|
|
for _, c := range chans {
|
2017-11-11 00:24:49 +01:00
|
|
|
satSent += int64(c.TotalMSatSent.ToSatoshis())
|
|
|
|
satRecv += int64(c.TotalMSatReceived.ToSatoshis())
|
2017-01-26 03:24:59 +01:00
|
|
|
}
|
|
|
|
|
2019-03-23 03:57:03 +01:00
|
|
|
nodePub := serverPeer.PubKey()
|
|
|
|
|
|
|
|
// Retrieve the peer's sync type. If we don't currently have a
|
|
|
|
// syncer for the peer, then we'll default to a passive sync.
|
|
|
|
// This can happen if the RPC is called while a peer is
|
|
|
|
// initializing.
|
|
|
|
syncer, ok := r.server.authGossiper.SyncManager().GossipSyncer(
|
|
|
|
nodePub,
|
|
|
|
)
|
|
|
|
|
|
|
|
var lnrpcSyncType lnrpc.Peer_SyncType
|
|
|
|
if !ok {
|
|
|
|
rpcsLog.Warnf("Gossip syncer for peer=%x not found",
|
|
|
|
nodePub)
|
|
|
|
lnrpcSyncType = lnrpc.Peer_UNKNOWN_SYNC
|
|
|
|
} else {
|
|
|
|
syncType := syncer.SyncType()
|
|
|
|
switch syncType {
|
|
|
|
case discovery.ActiveSync:
|
|
|
|
lnrpcSyncType = lnrpc.Peer_ACTIVE_SYNC
|
|
|
|
case discovery.PassiveSync:
|
|
|
|
lnrpcSyncType = lnrpc.Peer_PASSIVE_SYNC
|
2021-01-29 09:14:07 +01:00
|
|
|
case discovery.PinnedSync:
|
|
|
|
lnrpcSyncType = lnrpc.Peer_PINNED_SYNC
|
2019-03-23 03:57:03 +01:00
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("unhandled sync type %v",
|
|
|
|
syncType)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-14 16:05:07 +01:00
|
|
|
features := invoicesrpc.CreateRPCFeatures(
|
|
|
|
serverPeer.RemoteFeatures(),
|
|
|
|
)
|
|
|
|
|
2020-07-02 23:46:06 +02:00
|
|
|
rpcPeer := &lnrpc.Peer{
|
2021-08-13 00:31:43 +02:00
|
|
|
PubKey: hex.EncodeToString(nodePub[:]),
|
|
|
|
Address: serverPeer.Conn().RemoteAddr().String(),
|
|
|
|
Inbound: serverPeer.Inbound(),
|
|
|
|
BytesRecv: serverPeer.BytesReceived(),
|
|
|
|
BytesSent: serverPeer.BytesSent(),
|
|
|
|
SatSent: satSent,
|
|
|
|
SatRecv: satRecv,
|
|
|
|
PingTime: serverPeer.PingTime(),
|
|
|
|
SyncType: lnrpcSyncType,
|
|
|
|
Features: features,
|
|
|
|
LastPingPayload: serverPeer.LastRemotePingPayload(),
|
2016-06-21 20:52:09 +02:00
|
|
|
}
|
|
|
|
|
2020-03-13 08:15:49 +01:00
|
|
|
var peerErrors []interface{}
|
|
|
|
|
|
|
|
// If we only want the most recent error, get the most recent
|
|
|
|
// error from the buffer and add it to our list of errors if
|
|
|
|
// it is non-nil. If we want all the stored errors, simply
|
|
|
|
// add the full list to our set of errors.
|
|
|
|
if in.LatestError {
|
2020-06-30 03:29:22 +02:00
|
|
|
latestErr := serverPeer.ErrorBuffer().Latest()
|
2020-03-13 08:15:49 +01:00
|
|
|
if latestErr != nil {
|
|
|
|
peerErrors = []interface{}{latestErr}
|
|
|
|
}
|
|
|
|
} else {
|
2020-06-30 03:29:22 +02:00
|
|
|
peerErrors = serverPeer.ErrorBuffer().List()
|
2020-03-13 08:15:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add the relevant peer errors to our response.
|
|
|
|
for _, error := range peerErrors {
|
2020-07-02 23:46:06 +02:00
|
|
|
tsError := error.(*peer.TimestampedError)
|
2020-03-13 08:15:49 +01:00
|
|
|
|
|
|
|
rpcErr := &lnrpc.TimestampedError{
|
2020-06-27 03:04:15 +02:00
|
|
|
Timestamp: uint64(tsError.Timestamp.Unix()),
|
|
|
|
Error: tsError.Error.Error(),
|
2020-03-13 08:15:49 +01:00
|
|
|
}
|
|
|
|
|
2020-07-02 23:46:06 +02:00
|
|
|
rpcPeer.Errors = append(rpcPeer.Errors, rpcErr)
|
2020-03-13 08:15:49 +01:00
|
|
|
}
|
|
|
|
|
2020-08-27 09:20:46 +02:00
|
|
|
// If the server has started, we can query the event store
|
|
|
|
// for our peer's flap count. If we do so when the server has
|
|
|
|
// not started, the request will block.
|
|
|
|
if r.server.Started() {
|
|
|
|
vertex, err := route.NewVertexFromBytes(nodePub[:])
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
flap, ts, err := r.server.chanEventStore.FlapCount(
|
|
|
|
vertex,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// If our timestamp is non-nil, we have values for our
|
|
|
|
// peer's flap count, so we set them.
|
|
|
|
if ts != nil {
|
|
|
|
rpcPeer.FlapCount = int32(flap)
|
|
|
|
rpcPeer.LastFlapNs = ts.UnixNano()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-02 23:46:06 +02:00
|
|
|
resp.Peers = append(resp.Peers, rpcPeer)
|
2016-06-21 20:52:09 +02:00
|
|
|
}
|
|
|
|
|
2016-06-29 20:31:29 +02:00
|
|
|
rpcsLog.Debugf("[listpeers] yielded %v peers", serverPeers)
|
2016-06-21 20:52:09 +02:00
|
|
|
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
2019-08-12 19:13:12 +02:00
|
|
|
// SubscribePeerEvents returns a uni-directional stream (server -> client)
|
|
|
|
// for notifying the client of peer online and offline events.
|
|
|
|
func (r *rpcServer) SubscribePeerEvents(req *lnrpc.PeerEventSubscription,
|
|
|
|
eventStream lnrpc.Lightning_SubscribePeerEventsServer) error {
|
|
|
|
|
|
|
|
peerEventSub, err := r.server.peerNotifier.SubscribePeerEvents()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer peerEventSub.Cancel()
|
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
// A new update has been sent by the peer notifier, we'll
|
|
|
|
// marshal it into the form expected by the gRPC client, then
|
|
|
|
// send it off to the client.
|
|
|
|
case e := <-peerEventSub.Updates():
|
|
|
|
var event *lnrpc.PeerEvent
|
|
|
|
|
|
|
|
switch peerEvent := e.(type) {
|
|
|
|
case peernotifier.PeerOfflineEvent:
|
|
|
|
event = &lnrpc.PeerEvent{
|
|
|
|
PubKey: hex.EncodeToString(peerEvent.PubKey[:]),
|
|
|
|
Type: lnrpc.PeerEvent_PEER_OFFLINE,
|
|
|
|
}
|
|
|
|
|
|
|
|
case peernotifier.PeerOnlineEvent:
|
|
|
|
event = &lnrpc.PeerEvent{
|
|
|
|
PubKey: hex.EncodeToString(peerEvent.PubKey[:]),
|
|
|
|
Type: lnrpc.PeerEvent_PEER_ONLINE,
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("unexpected peer event: %v", event)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := eventStream.Send(event); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-07-18 22:41:11 +02:00
|
|
|
|
2021-09-06 19:08:21 +02:00
|
|
|
// The response stream's context for whatever reason has been
|
|
|
|
// closed. If context is closed by an exceeded deadline we will
|
|
|
|
// return an error.
|
2020-07-18 22:41:11 +02:00
|
|
|
case <-eventStream.Context().Done():
|
2021-09-06 19:08:21 +02:00
|
|
|
if errors.Is(eventStream.Context().Err(), context.Canceled) {
|
|
|
|
return nil
|
|
|
|
}
|
2020-07-18 22:41:11 +02:00
|
|
|
return eventStream.Context().Err()
|
|
|
|
|
2019-08-12 19:13:12 +02:00
|
|
|
case <-r.quit:
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-26 14:07:55 +01:00
|
|
|
// WalletBalance returns total unspent outputs(confirmed and unconfirmed), all
|
|
|
|
// confirmed unspent outputs and all unconfirmed unspent outputs under control
|
2016-06-21 20:46:27 +02:00
|
|
|
// by the wallet. This method can be modified by having the request specify
|
|
|
|
// only witness outputs should be factored into the final output sum.
|
2022-04-04 22:49:14 +02:00
|
|
|
// TODO(roasbeef): add async hooks into wallet balance changes.
|
2016-06-21 20:46:27 +02:00
|
|
|
func (r *rpcServer) WalletBalance(ctx context.Context,
|
|
|
|
in *lnrpc.WalletBalanceRequest) (*lnrpc.WalletBalanceResponse, error) {
|
2017-08-22 08:25:41 +02:00
|
|
|
|
2021-02-20 02:42:16 +01:00
|
|
|
// Retrieve all existing wallet accounts. We'll compute the confirmed
|
|
|
|
// and unconfirmed balance for each and tally them up.
|
2023-07-26 17:45:05 +02:00
|
|
|
accounts, err := r.server.cc.Wallet.ListAccounts(in.Account, nil)
|
2016-08-13 00:53:18 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2016-06-21 20:46:27 +02:00
|
|
|
}
|
|
|
|
|
2021-02-20 02:42:16 +01:00
|
|
|
var totalBalance, confirmedBalance, unconfirmedBalance btcutil.Amount
|
|
|
|
rpcAccountBalances := make(
|
|
|
|
map[string]*lnrpc.WalletAccountBalance, len(accounts),
|
2021-02-20 02:41:50 +01:00
|
|
|
)
|
2021-02-20 02:42:16 +01:00
|
|
|
for _, account := range accounts {
|
|
|
|
// There are two default accounts, one for NP2WKH outputs and
|
|
|
|
// another for P2WKH outputs. The balance will be computed for
|
|
|
|
// both given one call to ConfirmedBalance with the default
|
|
|
|
// wallet and imported account, so we'll skip the second
|
|
|
|
// instance to avoid inflating the balance.
|
|
|
|
switch account.AccountName {
|
|
|
|
case waddrmgr.ImportedAddrAccountName:
|
|
|
|
// Omit the imported account from the response unless we
|
|
|
|
// actually have any keys imported.
|
|
|
|
if account.ImportedKeyCount == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
fallthrough
|
2017-11-26 14:07:55 +01:00
|
|
|
|
2021-02-20 02:42:16 +01:00
|
|
|
case lnwallet.DefaultAccountName:
|
|
|
|
if _, ok := rpcAccountBalances[account.AccountName]; ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
|
2021-10-14 15:42:56 +02:00
|
|
|
// There now also are the accounts for the internal channel
|
|
|
|
// related keys. We skip those as they'll never have any direct
|
|
|
|
// balance.
|
|
|
|
if account.KeyScope.Purpose == keychain.BIP0043Purpose {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2021-02-20 02:42:16 +01:00
|
|
|
// Get total balance, from txs that have >= 0 confirmations.
|
|
|
|
totalBal, err := r.server.cc.Wallet.ConfirmedBalance(
|
|
|
|
0, account.AccountName,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
totalBalance += totalBal
|
|
|
|
|
|
|
|
// Get confirmed balance, from txs that have >= 1 confirmations.
|
|
|
|
// TODO(halseth): get both unconfirmed and confirmed balance in
|
|
|
|
// one call, as this is racy.
|
2023-10-15 23:20:03 +02:00
|
|
|
if in.MinConfs <= 0 {
|
|
|
|
in.MinConfs = 1
|
|
|
|
}
|
2021-02-20 02:42:16 +01:00
|
|
|
confirmedBal, err := r.server.cc.Wallet.ConfirmedBalance(
|
2023-10-15 23:20:03 +02:00
|
|
|
in.MinConfs, account.AccountName,
|
2021-02-20 02:42:16 +01:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
confirmedBalance += confirmedBal
|
|
|
|
|
|
|
|
// Get unconfirmed balance, from txs with 0 confirmations.
|
|
|
|
unconfirmedBal := totalBal - confirmedBal
|
|
|
|
unconfirmedBalance += unconfirmedBal
|
|
|
|
|
|
|
|
rpcAccountBalances[account.AccountName] = &lnrpc.WalletAccountBalance{
|
|
|
|
ConfirmedBalance: int64(confirmedBal),
|
|
|
|
UnconfirmedBalance: int64(unconfirmedBal),
|
|
|
|
}
|
|
|
|
}
|
2017-11-26 14:07:55 +01:00
|
|
|
|
2022-02-18 03:21:05 +01:00
|
|
|
// Now that we have the base balance accounted for with each account,
|
|
|
|
// we'll look at the set of locked UTXOs to tally that as well. If we
|
|
|
|
// don't display this, then anytime we attempt a funding reservation,
|
|
|
|
// the outputs will chose as being "gone" until they're confirmed on
|
|
|
|
// chain.
|
|
|
|
var lockedBalance btcutil.Amount
|
|
|
|
leases, err := r.server.cc.Wallet.ListLeasedOutputs()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for _, leasedOutput := range leases {
|
|
|
|
utxoInfo, err := r.server.cc.Wallet.FetchInputInfo(
|
|
|
|
&leasedOutput.Outpoint,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
lockedBalance += utxoInfo.Value
|
|
|
|
}
|
|
|
|
|
2022-06-20 15:12:13 +02:00
|
|
|
// Get the current number of non-private anchor channels.
|
|
|
|
currentNumAnchorChans, err := r.server.cc.Wallet.CurrentNumAnchorChans()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the required reserve for the wallet.
|
|
|
|
requiredReserve := r.server.cc.Wallet.RequiredReserve(
|
|
|
|
uint32(currentNumAnchorChans),
|
|
|
|
)
|
|
|
|
|
2019-05-24 14:17:48 +02:00
|
|
|
rpcsLog.Debugf("[walletbalance] Total balance=%v (confirmed=%v, "+
|
2021-02-20 02:42:16 +01:00
|
|
|
"unconfirmed=%v)", totalBalance, confirmedBalance,
|
|
|
|
unconfirmedBalance)
|
2016-06-21 20:46:27 +02:00
|
|
|
|
2017-02-10 00:28:32 +01:00
|
|
|
return &lnrpc.WalletBalanceResponse{
|
2022-06-20 15:12:13 +02:00
|
|
|
TotalBalance: int64(totalBalance),
|
|
|
|
ConfirmedBalance: int64(confirmedBalance),
|
|
|
|
UnconfirmedBalance: int64(unconfirmedBalance),
|
|
|
|
LockedBalance: int64(lockedBalance),
|
|
|
|
ReservedBalanceAnchorChan: int64(requiredReserve),
|
|
|
|
AccountBalance: rpcAccountBalances,
|
2017-02-10 00:28:32 +01:00
|
|
|
}, nil
|
2015-12-31 06:40:41 +01:00
|
|
|
}
|
2016-07-08 00:33:52 +02:00
|
|
|
|
2016-09-15 21:24:52 +02:00
|
|
|
// ChannelBalance returns the total available channel flow across all open
|
|
|
|
// channels in satoshis.
|
2016-09-15 20:59:51 +02:00
|
|
|
func (r *rpcServer) ChannelBalance(ctx context.Context,
|
2020-08-06 12:01:31 +02:00
|
|
|
in *lnrpc.ChannelBalanceRequest) (
|
|
|
|
*lnrpc.ChannelBalanceResponse, error) {
|
|
|
|
|
|
|
|
var (
|
|
|
|
localBalance lnwire.MilliSatoshi
|
|
|
|
remoteBalance lnwire.MilliSatoshi
|
|
|
|
unsettledLocalBalance lnwire.MilliSatoshi
|
|
|
|
unsettledRemoteBalance lnwire.MilliSatoshi
|
|
|
|
pendingOpenLocalBalance lnwire.MilliSatoshi
|
|
|
|
pendingOpenRemoteBalance lnwire.MilliSatoshi
|
|
|
|
)
|
2017-08-22 08:25:41 +02:00
|
|
|
|
2021-08-03 09:57:26 +02:00
|
|
|
openChannels, err := r.server.chanStateDB.FetchAllOpenChannels()
|
2016-11-12 00:48:15 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-03-27 14:25:46 +02:00
|
|
|
for _, channel := range openChannels {
|
2020-08-06 12:01:31 +02:00
|
|
|
c := channel.LocalCommitment
|
|
|
|
localBalance += c.LocalBalance
|
|
|
|
remoteBalance += c.RemoteBalance
|
|
|
|
|
|
|
|
// Add pending htlc amount.
|
|
|
|
for _, htlc := range c.Htlcs {
|
|
|
|
if htlc.Incoming {
|
|
|
|
unsettledLocalBalance += htlc.Amt
|
|
|
|
} else {
|
|
|
|
unsettledRemoteBalance += htlc.Amt
|
|
|
|
}
|
|
|
|
}
|
2018-03-27 14:25:46 +02:00
|
|
|
}
|
|
|
|
|
2021-08-03 09:57:26 +02:00
|
|
|
pendingChannels, err := r.server.chanStateDB.FetchPendingChannels()
|
2018-03-27 14:25:46 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, channel := range pendingChannels {
|
2020-08-06 12:01:31 +02:00
|
|
|
c := channel.LocalCommitment
|
|
|
|
pendingOpenLocalBalance += c.LocalBalance
|
|
|
|
pendingOpenRemoteBalance += c.RemoteBalance
|
2016-09-15 20:59:51 +02:00
|
|
|
}
|
|
|
|
|
2020-08-06 12:01:31 +02:00
|
|
|
rpcsLog.Debugf("[channelbalance] local_balance=%v remote_balance=%v "+
|
|
|
|
"unsettled_local_balance=%v unsettled_remote_balance=%v "+
|
2020-12-22 09:39:43 +01:00
|
|
|
"pending_open_local_balance=%v pending_open_remote_balance=%v",
|
2020-08-06 12:01:31 +02:00
|
|
|
localBalance, remoteBalance, unsettledLocalBalance,
|
|
|
|
unsettledRemoteBalance, pendingOpenLocalBalance,
|
|
|
|
pendingOpenRemoteBalance)
|
2019-06-14 02:32:13 +02:00
|
|
|
|
2018-04-01 01:40:50 +02:00
|
|
|
return &lnrpc.ChannelBalanceResponse{
|
2020-08-06 12:01:31 +02:00
|
|
|
LocalBalance: &lnrpc.Amount{
|
|
|
|
Sat: uint64(localBalance.ToSatoshis()),
|
|
|
|
Msat: uint64(localBalance),
|
|
|
|
},
|
|
|
|
RemoteBalance: &lnrpc.Amount{
|
|
|
|
Sat: uint64(remoteBalance.ToSatoshis()),
|
|
|
|
Msat: uint64(remoteBalance),
|
|
|
|
},
|
|
|
|
UnsettledLocalBalance: &lnrpc.Amount{
|
|
|
|
Sat: uint64(unsettledLocalBalance.ToSatoshis()),
|
|
|
|
Msat: uint64(unsettledLocalBalance),
|
|
|
|
},
|
|
|
|
UnsettledRemoteBalance: &lnrpc.Amount{
|
|
|
|
Sat: uint64(unsettledRemoteBalance.ToSatoshis()),
|
|
|
|
Msat: uint64(unsettledRemoteBalance),
|
|
|
|
},
|
|
|
|
PendingOpenLocalBalance: &lnrpc.Amount{
|
|
|
|
Sat: uint64(pendingOpenLocalBalance.ToSatoshis()),
|
|
|
|
Msat: uint64(pendingOpenLocalBalance),
|
|
|
|
},
|
|
|
|
PendingOpenRemoteBalance: &lnrpc.Amount{
|
|
|
|
Sat: uint64(pendingOpenRemoteBalance.ToSatoshis()),
|
|
|
|
Msat: uint64(pendingOpenRemoteBalance),
|
|
|
|
},
|
|
|
|
|
|
|
|
// Deprecated fields.
|
|
|
|
Balance: int64(localBalance.ToSatoshis()),
|
|
|
|
PendingOpenBalance: int64(pendingOpenLocalBalance.ToSatoshis()),
|
2018-04-01 01:40:50 +02:00
|
|
|
}, nil
|
2016-09-15 20:59:51 +02:00
|
|
|
}
|
|
|
|
|
2022-01-07 09:38:51 +01:00
|
|
|
type (
|
|
|
|
pendingOpenChannels []*lnrpc.PendingChannelsResponse_PendingOpenChannel
|
|
|
|
pendingForceClose []*lnrpc.PendingChannelsResponse_ForceClosedChannel
|
|
|
|
waitingCloseChannels []*lnrpc.PendingChannelsResponse_WaitingCloseChannel
|
|
|
|
)
|
2020-04-03 09:13:53 +02:00
|
|
|
|
2022-01-07 09:38:51 +01:00
|
|
|
// fetchPendingOpenChannels queries the database for a list of channels that
|
|
|
|
// have pending open state. The returned result is used in the response of the
|
|
|
|
// PendingChannels RPC.
|
|
|
|
func (r *rpcServer) fetchPendingOpenChannels() (pendingOpenChannels, error) {
|
2017-05-05 01:13:13 +02:00
|
|
|
// First, we'll populate the response with all the channels that are
|
|
|
|
// soon to be opened. We can easily fetch this data from the database
|
|
|
|
// and map the db struct to the proto response.
|
2022-01-07 09:38:51 +01:00
|
|
|
channels, err := r.server.chanStateDB.FetchPendingChannels()
|
2017-05-05 01:13:13 +02:00
|
|
|
if err != nil {
|
2018-01-17 05:21:18 +01:00
|
|
|
rpcsLog.Errorf("unable to fetch pending channels: %v", err)
|
2017-05-05 01:13:13 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
2022-01-07 09:38:51 +01:00
|
|
|
|
2023-03-04 00:26:37 +01:00
|
|
|
_, currentHeight, err := r.server.cc.ChainIO.GetBestBlock()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-01-07 09:38:51 +01:00
|
|
|
result := make(pendingOpenChannels, len(channels))
|
|
|
|
for i, pendingChan := range channels {
|
2017-05-05 01:13:13 +02:00
|
|
|
pub := pendingChan.IdentityPub.SerializeCompressed()
|
2017-05-17 04:14:48 +02:00
|
|
|
|
|
|
|
// As this is required for display purposes, we'll calculate
|
|
|
|
// the weight of the commitment transaction. We also add on the
|
|
|
|
// estimated weight of the witness to calculate the weight of
|
|
|
|
// the transaction if it were to be immediately unilaterally
|
|
|
|
// broadcast.
|
|
|
|
// TODO(roasbeef): query for funding tx from wallet, display
|
|
|
|
// that also?
|
2023-09-27 13:45:52 +02:00
|
|
|
var witnessWeight int64
|
|
|
|
if pendingChan.ChanType.IsTaproot() {
|
|
|
|
witnessWeight = input.TaprootKeyPathWitnessSize
|
|
|
|
} else {
|
|
|
|
witnessWeight = input.WitnessCommitmentTxWeight
|
|
|
|
}
|
|
|
|
|
2017-11-11 00:24:49 +01:00
|
|
|
localCommitment := pendingChan.LocalCommitment
|
|
|
|
utx := btcutil.NewTx(localCommitment.CommitTx)
|
2017-05-17 04:14:48 +02:00
|
|
|
commitBaseWeight := blockchain.GetTransactionWeight(utx)
|
2023-09-27 13:45:52 +02:00
|
|
|
commitWeight := commitBaseWeight + witnessWeight
|
2017-05-17 04:14:48 +02:00
|
|
|
|
2023-03-04 00:26:37 +01:00
|
|
|
// FundingExpiryBlocks is the distance from the current block
|
|
|
|
// height to the broadcast height + MaxWaitNumBlocksFundingConf.
|
|
|
|
maxFundingHeight := funding.MaxWaitNumBlocksFundingConf +
|
|
|
|
pendingChan.BroadcastHeight()
|
|
|
|
fundingExpiryBlocks := int32(maxFundingHeight) - currentHeight
|
|
|
|
|
2022-01-07 09:38:51 +01:00
|
|
|
result[i] = &lnrpc.PendingChannelsResponse_PendingOpenChannel{
|
2018-01-04 21:20:25 +01:00
|
|
|
Channel: &lnrpc.PendingChannelsResponse_PendingChannel{
|
2019-06-23 12:06:09 +02:00
|
|
|
RemoteNodePub: hex.EncodeToString(pub),
|
|
|
|
ChannelPoint: pendingChan.FundingOutpoint.String(),
|
|
|
|
Capacity: int64(pendingChan.Capacity),
|
|
|
|
LocalBalance: int64(localCommitment.LocalBalance.ToSatoshis()),
|
|
|
|
RemoteBalance: int64(localCommitment.RemoteBalance.ToSatoshis()),
|
|
|
|
LocalChanReserveSat: int64(pendingChan.LocalChanCfg.ChanReserve),
|
|
|
|
RemoteChanReserveSat: int64(pendingChan.RemoteChanCfg.ChanReserve),
|
2020-04-03 09:13:53 +02:00
|
|
|
Initiator: rpcInitiator(pendingChan.IsInitiator),
|
2020-03-30 20:36:38 +02:00
|
|
|
CommitmentType: rpcCommitmentType(pendingChan.ChanType),
|
2022-01-16 22:10:14 +01:00
|
|
|
Private: isPrivate(pendingChan),
|
2023-05-25 05:12:30 +02:00
|
|
|
Memo: string(pendingChan.Memo),
|
2017-05-05 01:13:13 +02:00
|
|
|
},
|
2023-03-04 00:26:37 +01:00
|
|
|
CommitWeight: commitWeight,
|
|
|
|
CommitFee: int64(localCommitment.CommitFee),
|
|
|
|
FeePerKw: int64(localCommitment.FeePerKw),
|
|
|
|
FundingExpiryBlocks: fundingExpiryBlocks,
|
2017-05-05 01:13:13 +02:00
|
|
|
// TODO(roasbeef): need to track confirmation height
|
2017-01-23 08:31:01 +01:00
|
|
|
}
|
2017-05-05 01:13:13 +02:00
|
|
|
}
|
|
|
|
|
2022-01-07 09:38:51 +01:00
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// fetchPendingForceCloseChannels queries the database for a list of channels
|
|
|
|
// that have their closing transactions confirmed but not fully resolved yet.
|
|
|
|
// The returned result is used in the response of the PendingChannels RPC.
|
|
|
|
func (r *rpcServer) fetchPendingForceCloseChannels() (pendingForceClose,
|
|
|
|
int64, error) {
|
|
|
|
|
2020-10-06 17:03:42 +02:00
|
|
|
_, currentHeight, err := r.server.cc.ChainIO.GetBestBlock()
|
2017-05-05 01:13:13 +02:00
|
|
|
if err != nil {
|
2022-01-07 09:38:51 +01:00
|
|
|
return nil, 0, err
|
2017-05-05 01:13:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Next, we'll examine the channels that are soon to be closed so we
|
|
|
|
// can populate these fields within the response.
|
2022-01-07 09:38:51 +01:00
|
|
|
channels, err := r.server.chanStateDB.FetchClosedChannels(true)
|
2017-05-05 01:13:13 +02:00
|
|
|
if err != nil {
|
2018-01-17 05:21:18 +01:00
|
|
|
rpcsLog.Errorf("unable to fetch closed channels: %v", err)
|
2022-01-07 09:38:51 +01:00
|
|
|
return nil, 0, err
|
2017-05-05 01:13:13 +02:00
|
|
|
}
|
2022-01-07 09:38:51 +01:00
|
|
|
|
|
|
|
result := make(pendingForceClose, 0)
|
|
|
|
limboBalance := int64(0)
|
|
|
|
|
|
|
|
for _, pendingClose := range channels {
|
2017-05-05 01:13:13 +02:00
|
|
|
// First construct the channel struct itself, this will be
|
|
|
|
// needed regardless of how this channel was closed.
|
|
|
|
pub := pendingClose.RemotePub.SerializeCompressed()
|
|
|
|
chanPoint := pendingClose.ChanPoint
|
2020-04-03 09:13:53 +02:00
|
|
|
|
|
|
|
// Create the pending channel. If this channel was closed before
|
|
|
|
// we started storing historical channel data, we will not know
|
|
|
|
// who initiated the channel, so we set the initiator field to
|
|
|
|
// unknown.
|
2018-01-04 21:20:25 +01:00
|
|
|
channel := &lnrpc.PendingChannelsResponse_PendingChannel{
|
2020-03-30 20:36:38 +02:00
|
|
|
RemoteNodePub: hex.EncodeToString(pub),
|
|
|
|
ChannelPoint: chanPoint.String(),
|
|
|
|
Capacity: int64(pendingClose.Capacity),
|
|
|
|
LocalBalance: int64(pendingClose.SettledBalance),
|
|
|
|
CommitmentType: lnrpc.CommitmentType_UNKNOWN_COMMITMENT_TYPE,
|
2020-04-03 09:13:53 +02:00
|
|
|
Initiator: lnrpc.Initiator_INITIATOR_UNKNOWN,
|
2017-05-05 01:13:13 +02:00
|
|
|
}
|
|
|
|
|
2020-03-25 07:40:44 +01:00
|
|
|
// Lookup the channel in the historical channel bucket to obtain
|
|
|
|
// initiator information. If the historical channel bucket was
|
|
|
|
// not found, or the channel itself, this channel was closed
|
|
|
|
// in a version before we started persisting historical
|
|
|
|
// channels, so we silence the error.
|
2021-08-03 09:57:26 +02:00
|
|
|
historical, err := r.server.chanStateDB.FetchHistoricalChannel(
|
2020-03-25 07:40:44 +01:00
|
|
|
&pendingClose.ChanPoint,
|
|
|
|
)
|
|
|
|
switch err {
|
|
|
|
// If the channel was closed in a version that did not record
|
|
|
|
// historical channels, ignore the error.
|
|
|
|
case channeldb.ErrNoHistoricalBucket:
|
|
|
|
case channeldb.ErrChannelNotFound:
|
|
|
|
|
|
|
|
case nil:
|
2020-04-03 09:13:53 +02:00
|
|
|
channel.Initiator = rpcInitiator(historical.IsInitiator)
|
2020-03-30 20:36:38 +02:00
|
|
|
channel.CommitmentType = rpcCommitmentType(
|
|
|
|
historical.ChanType,
|
|
|
|
)
|
2020-03-25 07:40:44 +01:00
|
|
|
|
2021-09-27 22:21:50 +02:00
|
|
|
// Get the number of forwarding packages from the
|
|
|
|
// historical channel.
|
|
|
|
fwdPkgs, err := historical.LoadFwdPkgs()
|
|
|
|
if err != nil {
|
|
|
|
rpcsLog.Errorf("unable to load forwarding "+
|
|
|
|
"packages for channel:%s, %v",
|
|
|
|
historical.ShortChannelID, err)
|
2022-01-07 09:38:51 +01:00
|
|
|
return nil, 0, err
|
2021-09-27 22:21:50 +02:00
|
|
|
}
|
|
|
|
channel.NumForwardingPackages = int64(len(fwdPkgs))
|
|
|
|
|
2021-11-04 18:20:14 +01:00
|
|
|
channel.RemoteBalance = int64(
|
|
|
|
historical.LocalCommitment.RemoteBalance.ToSatoshis(),
|
|
|
|
)
|
|
|
|
|
2022-01-16 22:10:14 +01:00
|
|
|
channel.Private = isPrivate(historical)
|
2023-05-25 05:12:30 +02:00
|
|
|
channel.Memo = string(historical.Memo)
|
2022-01-16 22:10:14 +01:00
|
|
|
|
2020-03-25 07:40:44 +01:00
|
|
|
// If the error is non-nil, and not due to older versions of lnd
|
|
|
|
// not persisting historical channels, return it.
|
|
|
|
default:
|
2022-01-07 09:38:51 +01:00
|
|
|
return nil, 0, err
|
2020-03-25 07:40:44 +01:00
|
|
|
}
|
|
|
|
|
2017-05-05 01:13:13 +02:00
|
|
|
closeTXID := pendingClose.ClosingTXID.String()
|
|
|
|
|
|
|
|
switch pendingClose.CloseType {
|
|
|
|
|
2020-03-25 07:41:00 +01:00
|
|
|
// A coop closed channel should never be in the "pending close"
|
|
|
|
// state. If a node upgraded from an older lnd version in the
|
|
|
|
// middle of a their channel confirming, it will be in this
|
|
|
|
// state. We log a warning that the channel will not be included
|
|
|
|
// in the now deprecated pending close channels field.
|
2017-05-05 01:13:13 +02:00
|
|
|
case channeldb.CooperativeClose:
|
2021-10-22 22:20:55 +02:00
|
|
|
rpcsLog.Warnf("channel %v cooperatively closed and "+
|
2020-03-25 07:41:00 +01:00
|
|
|
"in pending close state",
|
|
|
|
pendingClose.ChanPoint)
|
2017-05-15 03:51:26 +02:00
|
|
|
|
2017-05-05 01:13:13 +02:00
|
|
|
// If the channel was force closed, then we'll need to query
|
|
|
|
// the utxoNursery for additional information.
|
2018-03-27 14:25:46 +02:00
|
|
|
// TODO(halseth): distinguish remote and local case?
|
|
|
|
case channeldb.LocalForceClose, channeldb.RemoteForceClose:
|
2018-01-04 21:20:25 +01:00
|
|
|
forceClose := &lnrpc.PendingChannelsResponse_ForceClosedChannel{
|
2017-05-05 01:13:13 +02:00
|
|
|
Channel: channel,
|
|
|
|
ClosingTxid: closeTXID,
|
|
|
|
}
|
|
|
|
|
2018-09-07 16:05:57 +02:00
|
|
|
// Fetch reports from both nursery and resolvers. At the
|
|
|
|
// moment this is not an atomic snapshot. This is
|
|
|
|
// planned to be resolved when the nursery is removed
|
|
|
|
// and channel arbitrator will be the single source for
|
|
|
|
// these kind of reports.
|
2019-01-16 19:28:45 +01:00
|
|
|
err := r.nurseryPopulateForceCloseResp(
|
|
|
|
&chanPoint, currentHeight, forceClose,
|
|
|
|
)
|
|
|
|
if err != nil {
|
2022-08-02 11:19:01 +02:00
|
|
|
rpcsLog.Errorf("unable to populate nursery "+
|
|
|
|
"force close resp:%s, %v",
|
|
|
|
chanPoint, err)
|
2022-01-07 09:38:51 +01:00
|
|
|
return nil, 0, err
|
2016-07-08 00:33:52 +02:00
|
|
|
}
|
2017-05-05 01:13:13 +02:00
|
|
|
|
2018-09-07 16:05:57 +02:00
|
|
|
err = r.arbitratorPopulateForceCloseResp(
|
|
|
|
&chanPoint, currentHeight, forceClose,
|
|
|
|
)
|
|
|
|
if err != nil {
|
2022-08-02 11:19:01 +02:00
|
|
|
rpcsLog.Errorf("unable to populate arbitrator "+
|
|
|
|
"force close resp:%s, %v",
|
|
|
|
chanPoint, err)
|
2022-01-07 09:38:51 +01:00
|
|
|
return nil, 0, err
|
2018-09-07 16:05:57 +02:00
|
|
|
}
|
|
|
|
|
2022-01-07 09:38:51 +01:00
|
|
|
limboBalance += forceClose.LimboBalance
|
|
|
|
result = append(result, forceClose)
|
2016-07-08 00:33:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-07 09:38:51 +01:00
|
|
|
return result, limboBalance, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// fetchWaitingCloseChannels queries the database for a list of channels
|
|
|
|
// that have their closing transactions broadcast but not confirmed yet.
|
|
|
|
// The returned result is used in the response of the PendingChannels RPC.
|
2024-01-25 11:44:25 +01:00
|
|
|
func (r *rpcServer) fetchWaitingCloseChannels(
|
|
|
|
includeRawTx bool) (waitingCloseChannels, int64, error) {
|
2022-01-07 09:38:51 +01:00
|
|
|
|
2018-03-27 14:25:46 +02:00
|
|
|
// We'll also fetch all channels that are open, but have had their
|
|
|
|
// commitment broadcasted, meaning they are waiting for the closing
|
|
|
|
// transaction to confirm.
|
2022-01-07 09:38:51 +01:00
|
|
|
channels, err := r.server.chanStateDB.FetchWaitingCloseChannels()
|
2018-03-27 14:25:46 +02:00
|
|
|
if err != nil {
|
|
|
|
rpcsLog.Errorf("unable to fetch channels waiting close: %v",
|
|
|
|
err)
|
2022-01-07 09:38:51 +01:00
|
|
|
return nil, 0, err
|
2018-03-27 14:25:46 +02:00
|
|
|
}
|
|
|
|
|
2022-01-07 09:38:51 +01:00
|
|
|
result := make(waitingCloseChannels, 0)
|
|
|
|
limboBalance := int64(0)
|
|
|
|
|
2024-01-25 11:44:25 +01:00
|
|
|
// getClosingTx is a helper closure that tries to find the closing tx of
|
|
|
|
// a given waiting close channel. Notice that if the remote closes the
|
|
|
|
// channel, we may not have the closing tx.
|
|
|
|
getClosingTx := func(c *channeldb.OpenChannel) (*wire.MsgTx, error) {
|
2022-01-07 10:27:22 +01:00
|
|
|
var (
|
|
|
|
tx *wire.MsgTx
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
2024-01-25 11:44:25 +01:00
|
|
|
// First, we try to locate the force closing tx. If not found,
|
|
|
|
// we will then try to find its coop closing tx.
|
2022-01-07 10:27:22 +01:00
|
|
|
tx, err = c.BroadcastedCommitment()
|
|
|
|
if err == nil {
|
2024-01-25 11:44:25 +01:00
|
|
|
return tx, nil
|
2022-01-07 10:27:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// If the error returned is not ErrNoCloseTx, something
|
|
|
|
// unexpected happened and we will return the error.
|
|
|
|
if err != channeldb.ErrNoCloseTx {
|
2024-01-25 11:44:25 +01:00
|
|
|
return nil, err
|
2022-01-07 10:27:22 +01:00
|
|
|
}
|
|
|
|
|
2024-01-25 11:44:25 +01:00
|
|
|
// Otherwise, we continue to locate its coop closing tx.
|
2022-01-07 10:27:22 +01:00
|
|
|
tx, err = c.BroadcastedCooperative()
|
|
|
|
if err == nil {
|
2024-01-25 11:44:25 +01:00
|
|
|
return tx, nil
|
2022-01-07 10:27:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Return the error if it's not ErrNoCloseTx.
|
|
|
|
if err != channeldb.ErrNoCloseTx {
|
2024-01-25 11:44:25 +01:00
|
|
|
return nil, err
|
2022-01-07 10:27:22 +01:00
|
|
|
}
|
|
|
|
|
2024-01-25 11:44:25 +01:00
|
|
|
// Otherwise return an empty tx. This can happen if the remote
|
|
|
|
// broadcast the closing tx and we haven't recorded it yet.
|
|
|
|
return nil, nil
|
2022-01-07 10:27:22 +01:00
|
|
|
}
|
|
|
|
|
2022-01-07 09:38:51 +01:00
|
|
|
for _, waitingClose := range channels {
|
2018-03-27 14:25:46 +02:00
|
|
|
pub := waitingClose.IdentityPub.SerializeCompressed()
|
|
|
|
chanPoint := waitingClose.FundingOutpoint
|
2020-03-11 16:48:45 +01:00
|
|
|
|
|
|
|
var commitments lnrpc.PendingChannelsResponse_Commitments
|
|
|
|
|
|
|
|
// Report local commit. May not be present when DLP is active.
|
|
|
|
if waitingClose.LocalCommitment.CommitTx != nil {
|
|
|
|
commitments.LocalTxid =
|
|
|
|
waitingClose.LocalCommitment.CommitTx.TxHash().
|
|
|
|
String()
|
2020-03-25 07:41:00 +01:00
|
|
|
|
|
|
|
commitments.LocalCommitFeeSat = uint64(
|
|
|
|
waitingClose.LocalCommitment.CommitFee,
|
|
|
|
)
|
2020-03-11 16:48:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Report remote commit. May not be present when DLP is active.
|
|
|
|
if waitingClose.RemoteCommitment.CommitTx != nil {
|
|
|
|
commitments.RemoteTxid =
|
|
|
|
waitingClose.RemoteCommitment.CommitTx.TxHash().
|
|
|
|
String()
|
2020-03-25 07:41:00 +01:00
|
|
|
|
|
|
|
commitments.RemoteCommitFeeSat = uint64(
|
|
|
|
waitingClose.RemoteCommitment.CommitFee,
|
|
|
|
)
|
2020-03-11 16:48:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Report the remote pending commit if any.
|
|
|
|
remoteCommitDiff, err := waitingClose.RemoteCommitChainTip()
|
|
|
|
|
|
|
|
switch {
|
|
|
|
// Don't set hash if there is no pending remote commit.
|
|
|
|
case err == channeldb.ErrNoPendingCommit:
|
|
|
|
|
|
|
|
// An unexpected error occurred.
|
|
|
|
case err != nil:
|
2022-01-07 09:38:51 +01:00
|
|
|
return nil, 0, err
|
2020-03-11 16:48:45 +01:00
|
|
|
|
|
|
|
// There is a pending remote commit. Set its hash in the
|
|
|
|
// response.
|
|
|
|
default:
|
|
|
|
hash := remoteCommitDiff.Commitment.CommitTx.TxHash()
|
|
|
|
commitments.RemotePendingTxid = hash.String()
|
2020-03-25 07:41:00 +01:00
|
|
|
commitments.RemoteCommitFeeSat = uint64(
|
|
|
|
remoteCommitDiff.Commitment.CommitFee,
|
|
|
|
)
|
2020-03-11 16:48:45 +01:00
|
|
|
}
|
|
|
|
|
2021-08-27 11:24:30 +02:00
|
|
|
fwdPkgs, err := waitingClose.LoadFwdPkgs()
|
|
|
|
if err != nil {
|
|
|
|
rpcsLog.Errorf("unable to load forwarding packages "+
|
|
|
|
"for channel:%s, %v",
|
|
|
|
waitingClose.ShortChannelID, err)
|
2022-01-07 09:38:51 +01:00
|
|
|
return nil, 0, err
|
2021-08-27 11:24:30 +02:00
|
|
|
}
|
|
|
|
|
2024-01-25 11:44:25 +01:00
|
|
|
// Get the closing tx.
|
|
|
|
// NOTE: the closing tx could be nil here if it's the remote
|
|
|
|
// that broadcasted the closing tx.
|
|
|
|
closingTx, err := getClosingTx(waitingClose)
|
2022-01-07 10:27:22 +01:00
|
|
|
if err != nil {
|
2024-01-25 11:44:25 +01:00
|
|
|
rpcsLog.Errorf("unable to find closing tx for "+
|
2022-01-07 10:27:22 +01:00
|
|
|
"channel:%s, %v",
|
|
|
|
waitingClose.ShortChannelID, err)
|
|
|
|
return nil, 0, err
|
|
|
|
}
|
|
|
|
|
2018-03-27 14:25:46 +02:00
|
|
|
channel := &lnrpc.PendingChannelsResponse_PendingChannel{
|
2021-08-27 11:24:30 +02:00
|
|
|
RemoteNodePub: hex.EncodeToString(pub),
|
|
|
|
ChannelPoint: chanPoint.String(),
|
|
|
|
Capacity: int64(waitingClose.Capacity),
|
|
|
|
LocalBalance: int64(waitingClose.LocalCommitment.LocalBalance.ToSatoshis()),
|
|
|
|
RemoteBalance: int64(waitingClose.LocalCommitment.RemoteBalance.ToSatoshis()),
|
|
|
|
LocalChanReserveSat: int64(waitingClose.LocalChanCfg.ChanReserve),
|
|
|
|
RemoteChanReserveSat: int64(waitingClose.RemoteChanCfg.ChanReserve),
|
|
|
|
Initiator: rpcInitiator(waitingClose.IsInitiator),
|
|
|
|
CommitmentType: rpcCommitmentType(waitingClose.ChanType),
|
|
|
|
NumForwardingPackages: int64(len(fwdPkgs)),
|
2021-11-13 05:18:29 +01:00
|
|
|
ChanStatusFlags: waitingClose.ChanStatus().String(),
|
2022-01-16 22:10:14 +01:00
|
|
|
Private: isPrivate(waitingClose),
|
2023-05-25 05:12:30 +02:00
|
|
|
Memo: string(waitingClose.Memo),
|
2018-03-27 14:25:46 +02:00
|
|
|
}
|
|
|
|
|
2024-01-25 11:44:25 +01:00
|
|
|
var closingTxid, closingTxHex string
|
|
|
|
if closingTx != nil {
|
|
|
|
closingTxid = closingTx.TxHash().String()
|
|
|
|
if includeRawTx {
|
|
|
|
var txBuf bytes.Buffer
|
|
|
|
err = closingTx.Serialize(&txBuf)
|
|
|
|
if err != nil {
|
|
|
|
return nil, 0, fmt.Errorf("failed to "+
|
|
|
|
"serialize closing transaction"+
|
|
|
|
": %w", err)
|
|
|
|
}
|
|
|
|
closingTxHex = hex.EncodeToString(txBuf.Bytes())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-11 16:48:45 +01:00
|
|
|
waitingCloseResp := &lnrpc.PendingChannelsResponse_WaitingCloseChannel{
|
|
|
|
Channel: channel,
|
|
|
|
LimboBalance: channel.LocalBalance,
|
|
|
|
Commitments: &commitments,
|
2022-01-07 10:27:22 +01:00
|
|
|
ClosingTxid: closingTxid,
|
2024-01-25 11:44:25 +01:00
|
|
|
ClosingTxHex: closingTxHex,
|
2020-03-11 16:48:45 +01:00
|
|
|
}
|
|
|
|
|
2018-03-27 14:25:46 +02:00
|
|
|
// A close tx has been broadcasted, all our balance will be in
|
|
|
|
// limbo until it confirms.
|
2022-01-07 09:38:51 +01:00
|
|
|
result = append(result, waitingCloseResp)
|
|
|
|
limboBalance += channel.LocalBalance
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, limboBalance, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// PendingChannels returns a list of all the channels that are currently
|
|
|
|
// considered "pending". A channel is pending if it has finished the funding
|
|
|
|
// workflow and is waiting for confirmations for the funding txn, or is in the
|
|
|
|
// process of closure, either initiated cooperatively or non-cooperatively.
|
|
|
|
func (r *rpcServer) PendingChannels(ctx context.Context,
|
|
|
|
in *lnrpc.PendingChannelsRequest) (
|
|
|
|
*lnrpc.PendingChannelsResponse, error) {
|
|
|
|
|
|
|
|
resp := &lnrpc.PendingChannelsResponse{}
|
2018-03-27 14:25:46 +02:00
|
|
|
|
2022-01-07 09:38:51 +01:00
|
|
|
// First, we find all the channels that will soon be opened.
|
|
|
|
pendingOpenChannels, err := r.fetchPendingOpenChannels()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
resp.PendingOpenChannels = pendingOpenChannels
|
|
|
|
|
|
|
|
// Second, we fetch all channels that considered pending force closing.
|
|
|
|
// This means the channels here have their closing transactions
|
|
|
|
// confirmed but not considered fully resolved yet. For instance, they
|
|
|
|
// may have a second level HTLCs to be resolved onchain.
|
|
|
|
pendingCloseChannels, limbo, err := r.fetchPendingForceCloseChannels()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
resp.PendingForceClosingChannels = pendingCloseChannels
|
|
|
|
resp.TotalLimboBalance = limbo
|
|
|
|
|
|
|
|
// Third, we fetch all channels that are open, but have had their
|
|
|
|
// commitment broadcasted, meaning they are waiting for the closing
|
|
|
|
// transaction to confirm.
|
2024-01-25 11:44:25 +01:00
|
|
|
waitingCloseChannels, limbo, err := r.fetchWaitingCloseChannels(
|
|
|
|
in.IncludeRawTx,
|
|
|
|
)
|
2022-01-07 09:38:51 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2018-03-27 14:25:46 +02:00
|
|
|
}
|
2022-01-07 09:38:51 +01:00
|
|
|
resp.WaitingCloseChannels = waitingCloseChannels
|
|
|
|
resp.TotalLimboBalance += limbo
|
2018-03-27 14:25:46 +02:00
|
|
|
|
2017-05-05 01:13:13 +02:00
|
|
|
return resp, nil
|
2016-07-08 00:33:52 +02:00
|
|
|
}
|
2016-07-13 02:46:25 +02:00
|
|
|
|
2018-09-07 16:05:57 +02:00
|
|
|
// arbitratorPopulateForceCloseResp populates the pending channels response
|
|
|
|
// message with channel resolution information from the contract resolvers.
|
|
|
|
func (r *rpcServer) arbitratorPopulateForceCloseResp(chanPoint *wire.OutPoint,
|
|
|
|
currentHeight int32,
|
|
|
|
forceClose *lnrpc.PendingChannelsResponse_ForceClosedChannel) error {
|
|
|
|
|
|
|
|
// Query for contract resolvers state.
|
|
|
|
arbitrator, err := r.server.chainArb.GetChannelArbitrator(*chanPoint)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
reports := arbitrator.Report()
|
|
|
|
|
|
|
|
for _, report := range reports {
|
2019-10-29 17:23:08 +01:00
|
|
|
switch report.Type {
|
|
|
|
// For a direct output, populate/update the top level
|
|
|
|
// response properties.
|
|
|
|
case contractcourt.ReportOutputUnencumbered:
|
|
|
|
// Populate the maturity height fields for the direct
|
|
|
|
// commitment output to us.
|
|
|
|
forceClose.MaturityHeight = report.MaturityHeight
|
|
|
|
|
|
|
|
// If the transaction has been confirmed, then we can
|
|
|
|
// compute how many blocks it has left.
|
|
|
|
if forceClose.MaturityHeight != 0 {
|
|
|
|
forceClose.BlocksTilMaturity =
|
|
|
|
int32(forceClose.MaturityHeight) -
|
|
|
|
currentHeight
|
|
|
|
}
|
2018-09-07 16:05:57 +02:00
|
|
|
|
2019-10-29 17:23:08 +01:00
|
|
|
// Add htlcs to the PendingHtlcs response property.
|
|
|
|
case contractcourt.ReportOutputIncomingHtlc,
|
|
|
|
contractcourt.ReportOutputOutgoingHtlc:
|
|
|
|
|
2020-03-10 15:13:50 +01:00
|
|
|
// Don't report details on htlcs that are no longer in
|
|
|
|
// limbo.
|
|
|
|
if report.LimboBalance == 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
2019-10-29 17:23:08 +01:00
|
|
|
incoming := report.Type == contractcourt.ReportOutputIncomingHtlc
|
|
|
|
htlc := &lnrpc.PendingHTLC{
|
|
|
|
Incoming: incoming,
|
|
|
|
Amount: int64(report.Amount),
|
|
|
|
Outpoint: report.Outpoint.String(),
|
|
|
|
MaturityHeight: report.MaturityHeight,
|
|
|
|
Stage: report.Stage,
|
|
|
|
}
|
2018-09-07 16:05:57 +02:00
|
|
|
|
2019-10-29 17:23:08 +01:00
|
|
|
if htlc.MaturityHeight != 0 {
|
|
|
|
htlc.BlocksTilMaturity =
|
|
|
|
int32(htlc.MaturityHeight) - currentHeight
|
|
|
|
}
|
|
|
|
|
|
|
|
forceClose.PendingHtlcs = append(forceClose.PendingHtlcs, htlc)
|
|
|
|
|
2020-03-10 13:39:01 +01:00
|
|
|
case contractcourt.ReportOutputAnchor:
|
|
|
|
// There are three resolution states for the anchor:
|
|
|
|
// limbo, lost and recovered. Derive the current state
|
|
|
|
// from the limbo and recovered balances.
|
|
|
|
switch {
|
|
|
|
case report.RecoveredBalance != 0:
|
|
|
|
forceClose.Anchor = lnrpc.PendingChannelsResponse_ForceClosedChannel_RECOVERED
|
|
|
|
|
|
|
|
case report.LimboBalance != 0:
|
|
|
|
forceClose.Anchor = lnrpc.PendingChannelsResponse_ForceClosedChannel_LIMBO
|
|
|
|
|
|
|
|
default:
|
|
|
|
forceClose.Anchor = lnrpc.PendingChannelsResponse_ForceClosedChannel_LOST
|
|
|
|
}
|
|
|
|
|
2019-10-29 17:23:08 +01:00
|
|
|
default:
|
|
|
|
return fmt.Errorf("unknown report output type: %v",
|
|
|
|
report.Type)
|
2018-09-07 16:05:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
forceClose.LimboBalance += int64(report.LimboBalance)
|
|
|
|
forceClose.RecoveredBalance += int64(report.RecoveredBalance)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-01-16 19:28:45 +01:00
|
|
|
// nurseryPopulateForceCloseResp populates the pending channels response
|
|
|
|
// message with contract resolution information from utxonursery.
|
|
|
|
func (r *rpcServer) nurseryPopulateForceCloseResp(chanPoint *wire.OutPoint,
|
|
|
|
currentHeight int32,
|
|
|
|
forceClose *lnrpc.PendingChannelsResponse_ForceClosedChannel) error {
|
|
|
|
|
|
|
|
// Query for the maturity state for this force closed channel. If we
|
|
|
|
// didn't have any time-locked outputs, then the nursery may not know of
|
|
|
|
// the contract.
|
|
|
|
nurseryInfo, err := r.server.utxoNursery.NurseryReport(chanPoint)
|
2021-09-14 04:00:36 +02:00
|
|
|
if err == contractcourt.ErrContractNotFound {
|
2019-01-16 19:28:45 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to obtain "+
|
|
|
|
"nursery report for ChannelPoint(%v): %v",
|
|
|
|
chanPoint, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the nursery knows of this channel, then we can populate
|
|
|
|
// information detailing exactly how much funds are time locked and also
|
|
|
|
// the height in which we can ultimately sweep the funds into the
|
|
|
|
// wallet.
|
2021-09-14 04:00:36 +02:00
|
|
|
forceClose.LimboBalance = int64(nurseryInfo.LimboBalance)
|
|
|
|
forceClose.RecoveredBalance = int64(nurseryInfo.RecoveredBalance)
|
2019-01-16 19:28:45 +01:00
|
|
|
|
2021-09-14 04:00:36 +02:00
|
|
|
for _, htlcReport := range nurseryInfo.Htlcs {
|
2019-01-16 19:28:45 +01:00
|
|
|
// TODO(conner) set incoming flag appropriately after handling
|
|
|
|
// incoming incubation
|
|
|
|
htlc := &lnrpc.PendingHTLC{
|
|
|
|
Incoming: false,
|
2021-09-14 04:00:36 +02:00
|
|
|
Amount: int64(htlcReport.Amount),
|
|
|
|
Outpoint: htlcReport.Outpoint.String(),
|
|
|
|
MaturityHeight: htlcReport.MaturityHeight,
|
|
|
|
Stage: htlcReport.Stage,
|
2019-01-16 19:28:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if htlc.MaturityHeight != 0 {
|
|
|
|
htlc.BlocksTilMaturity =
|
|
|
|
int32(htlc.MaturityHeight) -
|
|
|
|
currentHeight
|
|
|
|
}
|
|
|
|
|
|
|
|
forceClose.PendingHtlcs = append(forceClose.PendingHtlcs,
|
|
|
|
htlc)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-06-15 02:14:31 +02:00
|
|
|
// ClosedChannels returns a list of all the channels have been closed.
|
2018-05-24 11:35:34 +02:00
|
|
|
// This does not include channels that are still in the process of closing.
|
|
|
|
func (r *rpcServer) ClosedChannels(ctx context.Context,
|
2018-06-15 02:14:31 +02:00
|
|
|
in *lnrpc.ClosedChannelsRequest) (*lnrpc.ClosedChannelsResponse,
|
2018-05-24 11:35:34 +02:00
|
|
|
error) {
|
|
|
|
|
|
|
|
// Show all channels when no filter flags are set.
|
2018-06-15 02:14:31 +02:00
|
|
|
filterResults := in.Cooperative || in.LocalForce ||
|
2018-05-29 11:26:47 +02:00
|
|
|
in.RemoteForce || in.Breach || in.FundingCanceled ||
|
|
|
|
in.Abandoned
|
2018-05-24 11:35:34 +02:00
|
|
|
|
|
|
|
resp := &lnrpc.ClosedChannelsResponse{}
|
|
|
|
|
2021-08-03 09:57:26 +02:00
|
|
|
dbChannels, err := r.server.chanStateDB.FetchClosedChannels(false)
|
2018-05-24 11:35:34 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-06-15 02:14:31 +02:00
|
|
|
// In order to make the response easier to parse for clients, we'll
|
|
|
|
// sort the set of closed channels by their closing height before
|
|
|
|
// serializing the proto response.
|
|
|
|
sort.Slice(dbChannels, func(i, j int) bool {
|
|
|
|
return dbChannels[i].CloseHeight < dbChannels[j].CloseHeight
|
|
|
|
})
|
|
|
|
|
2018-05-24 11:35:34 +02:00
|
|
|
for _, dbChannel := range dbChannels {
|
|
|
|
if dbChannel.IsPending {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
switch dbChannel.CloseType {
|
|
|
|
case channeldb.CooperativeClose:
|
|
|
|
if filterResults && !in.Cooperative {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
case channeldb.LocalForceClose:
|
|
|
|
if filterResults && !in.LocalForce {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
case channeldb.RemoteForceClose:
|
|
|
|
if filterResults && !in.RemoteForce {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
case channeldb.BreachClose:
|
|
|
|
if filterResults && !in.Breach {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
case channeldb.FundingCanceled:
|
|
|
|
if filterResults && !in.FundingCanceled {
|
|
|
|
continue
|
|
|
|
}
|
2018-05-29 11:26:47 +02:00
|
|
|
case channeldb.Abandoned:
|
|
|
|
if filterResults && !in.Abandoned {
|
|
|
|
continue
|
|
|
|
}
|
2018-05-24 11:35:34 +02:00
|
|
|
}
|
|
|
|
|
2020-02-21 12:24:24 +01:00
|
|
|
channel, err := r.createRPCClosedChannel(dbChannel)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-05-24 11:35:34 +02:00
|
|
|
resp.Channels = append(resp.Channels, channel)
|
|
|
|
}
|
|
|
|
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
2023-02-28 08:35:36 +01:00
|
|
|
// LookupHtlcResolution retrieves a final htlc resolution from the database. If
|
|
|
|
// the htlc has no final resolution yet, a NotFound grpc status code is
|
|
|
|
// returned.
|
|
|
|
func (r *rpcServer) LookupHtlcResolution(
|
2023-05-12 10:09:56 +02:00
|
|
|
_ context.Context, in *lnrpc.LookupHtlcResolutionRequest) (
|
2023-02-28 08:35:36 +01:00
|
|
|
*lnrpc.LookupHtlcResolutionResponse, error) {
|
2022-08-29 13:20:52 +02:00
|
|
|
|
2023-02-27 09:27:10 +01:00
|
|
|
if !r.cfg.StoreFinalHtlcResolutions {
|
|
|
|
return nil, status.Error(codes.Unavailable, "cannot lookup "+
|
|
|
|
"with flag --store-final-htlc-resolutions=false")
|
|
|
|
}
|
|
|
|
|
2022-08-29 13:20:52 +02:00
|
|
|
chanID := lnwire.NewShortChanIDFromInt(in.ChanId)
|
|
|
|
|
|
|
|
info, err := r.server.chanStateDB.LookupFinalHtlc(chanID, in.HtlcIndex)
|
|
|
|
switch {
|
|
|
|
case errors.Is(err, channeldb.ErrHtlcUnknown):
|
|
|
|
return nil, status.Error(codes.NotFound, err.Error())
|
|
|
|
|
|
|
|
case err != nil:
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2023-02-28 08:35:36 +01:00
|
|
|
return &lnrpc.LookupHtlcResolutionResponse{
|
2022-08-29 13:20:52 +02:00
|
|
|
Settled: info.Settled,
|
|
|
|
Offchain: info.Offchain,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2017-07-28 01:39:49 +02:00
|
|
|
// ListChannels returns a description of all the open channels that this node
|
|
|
|
// is a participant in.
|
2016-09-26 05:04:03 +02:00
|
|
|
func (r *rpcServer) ListChannels(ctx context.Context,
|
|
|
|
in *lnrpc.ListChannelsRequest) (*lnrpc.ListChannelsResponse, error) {
|
2017-08-22 08:25:41 +02:00
|
|
|
|
2018-03-13 20:11:19 +01:00
|
|
|
if in.ActiveOnly && in.InactiveOnly {
|
|
|
|
return nil, fmt.Errorf("either `active_only` or " +
|
|
|
|
"`inactive_only` can be set, but not both")
|
|
|
|
}
|
|
|
|
|
|
|
|
if in.PublicOnly && in.PrivateOnly {
|
|
|
|
return nil, fmt.Errorf("either `public_only` or " +
|
|
|
|
"`private_only` can be set, but not both")
|
|
|
|
}
|
|
|
|
|
2020-03-03 14:11:33 +01:00
|
|
|
if len(in.Peer) > 0 && len(in.Peer) != 33 {
|
|
|
|
_, err := route.NewVertexFromBytes(in.Peer)
|
2024-02-26 12:19:38 +01:00
|
|
|
return nil, fmt.Errorf("invalid `peer` key: %w", err)
|
2020-03-03 14:11:33 +01:00
|
|
|
}
|
|
|
|
|
2016-09-26 05:04:03 +02:00
|
|
|
resp := &lnrpc.ListChannelsResponse{}
|
|
|
|
|
2021-08-03 09:57:26 +02:00
|
|
|
dbChannels, err := r.server.chanStateDB.FetchAllOpenChannels()
|
2016-10-27 00:09:01 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-02-25 16:41:28 +01:00
|
|
|
rpcsLog.Debugf("[listchannels] fetched %v channels from DB",
|
2016-10-27 00:09:01 +02:00
|
|
|
len(dbChannels))
|
|
|
|
|
|
|
|
for _, dbChannel := range dbChannels {
|
2017-03-08 23:45:58 +01:00
|
|
|
nodePub := dbChannel.IdentityPub
|
2020-03-03 14:11:33 +01:00
|
|
|
nodePubBytes := nodePub.SerializeCompressed()
|
2017-07-30 23:25:03 +02:00
|
|
|
chanPoint := dbChannel.FundingOutpoint
|
2016-12-27 06:50:19 +01:00
|
|
|
|
2020-03-03 14:11:33 +01:00
|
|
|
// If the caller requested channels for a target node, skip any
|
|
|
|
// that don't match the provided pubkey.
|
|
|
|
if len(in.Peer) > 0 && !bytes.Equal(nodePubBytes, in.Peer) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2017-03-08 23:45:58 +01:00
|
|
|
var peerOnline bool
|
2017-08-09 00:49:32 +02:00
|
|
|
if _, err := r.server.FindPeer(nodePub); err == nil {
|
2017-03-08 23:45:58 +01:00
|
|
|
peerOnline = true
|
|
|
|
}
|
|
|
|
|
2024-01-29 22:19:15 +01:00
|
|
|
channelID := lnwire.NewChanIDFromOutPoint(chanPoint)
|
2017-11-19 01:30:51 +01:00
|
|
|
var linkActive bool
|
2017-12-06 02:51:06 +01:00
|
|
|
if link, err := r.server.htlcSwitch.GetLink(channelID); err == nil {
|
|
|
|
// A channel is only considered active if it is known
|
|
|
|
// by the switch *and* able to forward
|
|
|
|
// incoming/outgoing payments.
|
|
|
|
linkActive = link.EligibleToForward()
|
2017-11-19 01:30:51 +01:00
|
|
|
}
|
|
|
|
|
2018-03-13 20:11:19 +01:00
|
|
|
// Next, we'll determine whether we should add this channel to
|
|
|
|
// our list depending on the type of channels requested to us.
|
|
|
|
isActive := peerOnline && linkActive
|
2023-01-15 10:01:09 +01:00
|
|
|
channel, err := createRPCOpenChannel(
|
|
|
|
r, dbChannel, isActive, in.PeerAliasLookup,
|
|
|
|
)
|
2019-12-17 16:36:27 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-03-13 20:11:19 +01:00
|
|
|
|
|
|
|
// We'll only skip returning this channel if we were requested
|
|
|
|
// for a specific kind and this channel doesn't satisfy it.
|
|
|
|
switch {
|
|
|
|
case in.ActiveOnly && !isActive:
|
|
|
|
continue
|
|
|
|
case in.InactiveOnly && isActive:
|
|
|
|
continue
|
2018-09-28 01:34:43 +02:00
|
|
|
case in.PublicOnly && channel.Private:
|
2018-03-13 20:11:19 +01:00
|
|
|
continue
|
2018-09-28 01:34:43 +02:00
|
|
|
case in.PrivateOnly && !channel.Private:
|
2018-03-13 20:11:19 +01:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2018-09-28 01:34:43 +02:00
|
|
|
resp.Channels = append(resp.Channels, channel)
|
|
|
|
}
|
2016-10-27 00:09:01 +02:00
|
|
|
|
2018-09-28 01:34:43 +02:00
|
|
|
return resp, nil
|
|
|
|
}
|
2018-12-07 01:49:05 +01:00
|
|
|
|
2020-03-30 20:35:24 +02:00
|
|
|
// rpcCommitmentType takes the channel type and converts it to an rpc commitment
|
|
|
|
// type value.
|
|
|
|
func rpcCommitmentType(chanType channeldb.ChannelType) lnrpc.CommitmentType {
|
|
|
|
// Extract the commitment type from the channel type flags. We must
|
|
|
|
// first check whether it has anchors, since in that case it would also
|
|
|
|
// be tweakless.
|
2023-01-20 05:42:06 +01:00
|
|
|
switch {
|
|
|
|
case chanType.IsTaproot():
|
|
|
|
return lnrpc.CommitmentType_SIMPLE_TAPROOT
|
|
|
|
|
|
|
|
case chanType.HasLeaseExpiration():
|
2021-07-15 02:21:04 +02:00
|
|
|
return lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE
|
|
|
|
|
2023-01-20 05:42:06 +01:00
|
|
|
case chanType.HasAnchors():
|
2020-03-30 20:35:24 +02:00
|
|
|
return lnrpc.CommitmentType_ANCHORS
|
|
|
|
|
2023-01-20 05:42:06 +01:00
|
|
|
case chanType.IsTweakless():
|
2020-03-30 20:35:24 +02:00
|
|
|
return lnrpc.CommitmentType_STATIC_REMOTE_KEY
|
2023-01-20 05:42:06 +01:00
|
|
|
default:
|
2020-03-30 20:35:24 +02:00
|
|
|
|
2023-01-20 05:42:06 +01:00
|
|
|
return lnrpc.CommitmentType_LEGACY
|
|
|
|
}
|
2020-03-30 20:35:24 +02:00
|
|
|
}
|
|
|
|
|
2020-06-10 14:21:13 +02:00
|
|
|
// createChannelConstraint creates a *lnrpc.ChannelConstraints using the
|
|
|
|
// *Channeldb.ChannelConfig.
|
|
|
|
func createChannelConstraint(
|
|
|
|
chanCfg *channeldb.ChannelConfig) *lnrpc.ChannelConstraints {
|
|
|
|
return &lnrpc.ChannelConstraints{
|
|
|
|
CsvDelay: uint32(chanCfg.CsvDelay),
|
|
|
|
ChanReserveSat: uint64(chanCfg.ChanReserve),
|
|
|
|
DustLimitSat: uint64(chanCfg.DustLimit),
|
|
|
|
MaxPendingAmtMsat: uint64(chanCfg.MaxPendingAmount),
|
|
|
|
MinHtlcMsat: uint64(chanCfg.MinHTLC),
|
|
|
|
MaxAcceptedHtlcs: uint32(chanCfg.MaxAcceptedHtlcs),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-16 22:10:14 +01:00
|
|
|
// isPrivate evaluates the ChannelFlags of the db channel to determine if the
|
|
|
|
// channel is private or not.
|
|
|
|
func isPrivate(dbChannel *channeldb.OpenChannel) bool {
|
|
|
|
if dbChannel == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return dbChannel.ChannelFlags&lnwire.FFAnnounceChannel != 1
|
|
|
|
}
|
|
|
|
|
2018-09-28 01:34:43 +02:00
|
|
|
// createRPCOpenChannel creates an *lnrpc.Channel from the *channeldb.Channel.
|
2022-01-16 18:24:20 +01:00
|
|
|
func createRPCOpenChannel(r *rpcServer, dbChannel *channeldb.OpenChannel,
|
2023-01-15 10:01:09 +01:00
|
|
|
isActive, peerAliasLookup bool) (*lnrpc.Channel, error) {
|
2018-09-28 01:34:43 +02:00
|
|
|
|
|
|
|
nodePub := dbChannel.IdentityPub
|
|
|
|
nodeID := hex.EncodeToString(nodePub.SerializeCompressed())
|
|
|
|
chanPoint := dbChannel.FundingOutpoint
|
2024-01-29 22:19:15 +01:00
|
|
|
chanID := lnwire.NewChanIDFromOutPoint(chanPoint)
|
2018-09-28 01:34:43 +02:00
|
|
|
|
|
|
|
// As this is required for display purposes, we'll calculate
|
|
|
|
// the weight of the commitment transaction. We also add on the
|
|
|
|
// estimated weight of the witness to calculate the weight of
|
|
|
|
// the transaction if it were to be immediately unilaterally
|
|
|
|
// broadcast.
|
2023-09-27 13:45:52 +02:00
|
|
|
var witnessWeight int64
|
|
|
|
if dbChannel.ChanType.IsTaproot() {
|
|
|
|
witnessWeight = input.TaprootKeyPathWitnessSize
|
|
|
|
} else {
|
|
|
|
witnessWeight = input.WitnessCommitmentTxWeight
|
|
|
|
}
|
|
|
|
|
2018-09-28 01:34:43 +02:00
|
|
|
localCommit := dbChannel.LocalCommitment
|
|
|
|
utx := btcutil.NewTx(localCommit.CommitTx)
|
|
|
|
commitBaseWeight := blockchain.GetTransactionWeight(utx)
|
2023-09-27 13:45:52 +02:00
|
|
|
commitWeight := commitBaseWeight + witnessWeight
|
2018-09-28 01:34:43 +02:00
|
|
|
|
|
|
|
localBalance := localCommit.LocalBalance
|
|
|
|
remoteBalance := localCommit.RemoteBalance
|
|
|
|
|
|
|
|
// As an artifact of our usage of mSAT internally, either party
|
|
|
|
// may end up in a state where they're holding a fractional
|
|
|
|
// amount of satoshis which can't be expressed within the
|
|
|
|
// actual commitment output. Since we round down when going
|
|
|
|
// from mSAT -> SAT, we may at any point be adding an
|
|
|
|
// additional SAT to miners fees. As a result, we display a
|
|
|
|
// commitment fee that accounts for this externally.
|
|
|
|
var sumOutputs btcutil.Amount
|
|
|
|
for _, txOut := range localCommit.CommitTx.TxOut {
|
|
|
|
sumOutputs += btcutil.Amount(txOut.Value)
|
|
|
|
}
|
|
|
|
externalCommitFee := dbChannel.Capacity - sumOutputs
|
|
|
|
|
2020-03-30 20:35:24 +02:00
|
|
|
// Extract the commitment type from the channel type flags.
|
|
|
|
commitmentType := rpcCommitmentType(dbChannel.ChanType)
|
2020-03-04 13:21:28 +01:00
|
|
|
|
2022-07-13 21:27:40 +02:00
|
|
|
dbScid := dbChannel.ShortChannelID
|
|
|
|
|
|
|
|
// Fetch the set of aliases for the channel.
|
|
|
|
channelAliases := r.server.aliasMgr.GetAliases(dbScid)
|
|
|
|
|
2023-01-27 02:15:14 +01:00
|
|
|
// Fetch the peer alias. If one does not exist, errNoPeerAlias
|
|
|
|
// is returned and peerScidAlias will be an empty ShortChannelID.
|
|
|
|
peerScidAlias, _ := r.server.aliasMgr.GetPeerAlias(chanID)
|
|
|
|
|
2018-09-28 01:34:43 +02:00
|
|
|
channel := &lnrpc.Channel{
|
|
|
|
Active: isActive,
|
2022-01-16 22:10:14 +01:00
|
|
|
Private: isPrivate(dbChannel),
|
2018-09-28 01:34:43 +02:00
|
|
|
RemotePubkey: nodeID,
|
|
|
|
ChannelPoint: chanPoint.String(),
|
2022-07-13 21:27:40 +02:00
|
|
|
ChanId: dbScid.ToUint64(),
|
2018-09-28 01:34:43 +02:00
|
|
|
Capacity: int64(dbChannel.Capacity),
|
|
|
|
LocalBalance: int64(localBalance.ToSatoshis()),
|
|
|
|
RemoteBalance: int64(remoteBalance.ToSatoshis()),
|
|
|
|
CommitFee: int64(externalCommitFee),
|
|
|
|
CommitWeight: commitWeight,
|
|
|
|
FeePerKw: int64(localCommit.FeePerKw),
|
|
|
|
TotalSatoshisSent: int64(dbChannel.TotalMSatSent.ToSatoshis()),
|
|
|
|
TotalSatoshisReceived: int64(dbChannel.TotalMSatReceived.ToSatoshis()),
|
|
|
|
NumUpdates: localCommit.CommitHeight,
|
|
|
|
PendingHtlcs: make([]*lnrpc.HTLC, len(localCommit.Htlcs)),
|
|
|
|
Initiator: dbChannel.IsInitiator,
|
2018-12-10 05:12:24 +01:00
|
|
|
ChanStatusFlags: dbChannel.ChanStatus().String(),
|
2020-03-04 13:21:28 +01:00
|
|
|
StaticRemoteKey: commitmentType == lnrpc.CommitmentType_STATIC_REMOTE_KEY,
|
|
|
|
CommitmentType: commitmentType,
|
2020-03-14 00:58:05 +01:00
|
|
|
ThawHeight: dbChannel.ThawHeight,
|
2020-06-10 14:21:13 +02:00
|
|
|
LocalConstraints: createChannelConstraint(
|
|
|
|
&dbChannel.LocalChanCfg,
|
|
|
|
),
|
|
|
|
RemoteConstraints: createChannelConstraint(
|
|
|
|
&dbChannel.RemoteChanCfg,
|
|
|
|
),
|
2022-07-13 21:27:40 +02:00
|
|
|
AliasScids: make([]uint64, 0, len(channelAliases)),
|
2023-01-27 02:15:14 +01:00
|
|
|
PeerScidAlias: peerScidAlias.ToUint64(),
|
2022-07-13 21:27:40 +02:00
|
|
|
ZeroConf: dbChannel.IsZeroConf(),
|
|
|
|
ZeroConfConfirmedScid: dbChannel.ZeroConfRealScid().ToUint64(),
|
2023-05-04 19:53:05 +02:00
|
|
|
Memo: string(dbChannel.Memo),
|
2020-06-10 14:21:13 +02:00
|
|
|
// TODO: remove the following deprecated fields
|
|
|
|
CsvDelay: uint32(dbChannel.LocalChanCfg.CsvDelay),
|
|
|
|
LocalChanReserveSat: int64(dbChannel.LocalChanCfg.ChanReserve),
|
|
|
|
RemoteChanReserveSat: int64(dbChannel.RemoteChanCfg.ChanReserve),
|
2018-09-28 01:34:43 +02:00
|
|
|
}
|
|
|
|
|
2023-01-15 10:01:09 +01:00
|
|
|
// Look up our channel peer's node alias if the caller requests it.
|
|
|
|
if peerAliasLookup {
|
|
|
|
peerAlias, err := r.server.graphDB.LookupAlias(nodePub)
|
|
|
|
if err != nil {
|
2023-03-01 23:38:08 +01:00
|
|
|
peerAlias = fmt.Sprintf("unable to lookup "+
|
|
|
|
"peer alias: %v", err)
|
2023-01-15 10:01:09 +01:00
|
|
|
}
|
|
|
|
channel.PeerAlias = peerAlias
|
|
|
|
}
|
|
|
|
|
2022-07-13 21:27:40 +02:00
|
|
|
// Populate the set of aliases.
|
|
|
|
for _, chanAlias := range channelAliases {
|
|
|
|
channel.AliasScids = append(
|
|
|
|
channel.AliasScids, chanAlias.ToUint64(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2018-09-28 01:34:43 +02:00
|
|
|
for i, htlc := range localCommit.Htlcs {
|
|
|
|
var rHash [32]byte
|
|
|
|
copy(rHash[:], htlc.RHash[:])
|
2020-10-14 17:44:18 +02:00
|
|
|
|
|
|
|
circuitMap := r.server.htlcSwitch.CircuitLookup()
|
|
|
|
|
|
|
|
var forwardingChannel, forwardingHtlcIndex uint64
|
|
|
|
switch {
|
|
|
|
case htlc.Incoming:
|
|
|
|
circuit := circuitMap.LookupCircuit(
|
|
|
|
htlcswitch.CircuitKey{
|
|
|
|
ChanID: dbChannel.ShortChannelID,
|
|
|
|
HtlcID: htlc.HtlcIndex,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
if circuit != nil && circuit.Outgoing != nil {
|
|
|
|
forwardingChannel = circuit.Outgoing.ChanID.
|
|
|
|
ToUint64()
|
|
|
|
|
|
|
|
forwardingHtlcIndex = circuit.Outgoing.HtlcID
|
|
|
|
}
|
|
|
|
|
|
|
|
case !htlc.Incoming:
|
|
|
|
circuit := circuitMap.LookupOpenCircuit(
|
|
|
|
htlcswitch.CircuitKey{
|
|
|
|
ChanID: dbChannel.ShortChannelID,
|
|
|
|
HtlcID: htlc.HtlcIndex,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
// If the incoming channel id is the special hop.Source
|
|
|
|
// value, the htlc index is a local payment identifier.
|
|
|
|
// In this case, report nothing.
|
|
|
|
if circuit != nil &&
|
|
|
|
circuit.Incoming.ChanID != hop.Source {
|
|
|
|
|
|
|
|
forwardingChannel = circuit.Incoming.ChanID.
|
|
|
|
ToUint64()
|
|
|
|
|
|
|
|
forwardingHtlcIndex = circuit.Incoming.HtlcID
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-28 01:34:43 +02:00
|
|
|
channel.PendingHtlcs[i] = &lnrpc.HTLC{
|
2020-10-14 17:44:18 +02:00
|
|
|
Incoming: htlc.Incoming,
|
|
|
|
Amount: int64(htlc.Amt.ToSatoshis()),
|
|
|
|
HashLock: rHash[:],
|
|
|
|
ExpirationHeight: htlc.RefundTimeout,
|
|
|
|
HtlcIndex: htlc.HtlcIndex,
|
|
|
|
ForwardingChannel: forwardingChannel,
|
|
|
|
ForwardingHtlcIndex: forwardingHtlcIndex,
|
2016-09-26 05:04:03 +02:00
|
|
|
}
|
|
|
|
|
2018-09-28 01:34:43 +02:00
|
|
|
// Add the Pending Htlc Amount to UnsettledBalance field.
|
|
|
|
channel.UnsettledBalance += channel.PendingHtlcs[i].Amount
|
2016-09-26 05:04:03 +02:00
|
|
|
}
|
|
|
|
|
2020-03-18 09:52:43 +01:00
|
|
|
// If we initiated opening the channel, the zero height remote balance
|
|
|
|
// is the push amount. Otherwise, our starting balance is the push
|
|
|
|
// amount. If there is no push amount, these values will simply be zero.
|
|
|
|
if dbChannel.IsInitiator {
|
2022-05-14 19:50:30 +02:00
|
|
|
amt := dbChannel.InitialRemoteBalance.ToSatoshis()
|
|
|
|
channel.PushAmountSat = uint64(amt)
|
2020-03-18 09:52:43 +01:00
|
|
|
} else {
|
2022-05-14 19:50:30 +02:00
|
|
|
amt := dbChannel.InitialLocalBalance.ToSatoshis()
|
|
|
|
channel.PushAmountSat = uint64(amt)
|
2020-03-18 09:52:43 +01:00
|
|
|
}
|
|
|
|
|
2020-04-11 03:26:31 +02:00
|
|
|
if len(dbChannel.LocalShutdownScript) > 0 {
|
|
|
|
_, addresses, _, err := txscript.ExtractPkScriptAddrs(
|
2020-08-04 20:56:31 +02:00
|
|
|
dbChannel.LocalShutdownScript, r.cfg.ActiveNetParams.Params,
|
2020-04-11 03:26:31 +02:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// We only expect one upfront shutdown address for a channel. If
|
|
|
|
// LocalShutdownScript is non-zero, there should be one payout
|
|
|
|
// address set.
|
|
|
|
if len(addresses) != 1 {
|
|
|
|
return nil, fmt.Errorf("expected one upfront shutdown "+
|
|
|
|
"address, got: %v", len(addresses))
|
|
|
|
}
|
|
|
|
|
|
|
|
channel.CloseAddress = addresses[0].String()
|
|
|
|
}
|
|
|
|
|
2020-04-11 03:31:46 +02:00
|
|
|
// If the server hasn't fully started yet, it's possible that the
|
|
|
|
// channel event store hasn't either, so it won't be able to consume any
|
|
|
|
// requests until then. To prevent blocking, we'll just omit the uptime
|
|
|
|
// related fields for now.
|
|
|
|
if !r.server.Started() {
|
|
|
|
return channel, nil
|
|
|
|
}
|
2019-12-17 16:36:28 +01:00
|
|
|
|
2020-09-08 13:47:18 +02:00
|
|
|
peer, err := route.NewVertexFromBytes(nodePub.SerializeCompressed())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-09-08 13:47:17 +02:00
|
|
|
// Query the event store for additional information about the channel.
|
|
|
|
// Do not fail if it is not available, because there is a potential
|
|
|
|
// race between a channel being added to our node and the event store
|
|
|
|
// being notified of it.
|
2020-04-11 03:31:46 +02:00
|
|
|
outpoint := dbChannel.FundingOutpoint
|
2020-09-08 13:47:18 +02:00
|
|
|
info, err := r.server.chanEventStore.GetChanInfo(outpoint, peer)
|
2019-12-17 16:36:27 +01:00
|
|
|
switch err {
|
2020-09-08 13:47:17 +02:00
|
|
|
// If the store does not know about the channel, we just log it.
|
2019-12-17 16:36:27 +01:00
|
|
|
case chanfitness.ErrChannelNotFound:
|
|
|
|
rpcsLog.Infof("channel: %v not found by channel event store",
|
2019-12-17 16:36:28 +01:00
|
|
|
outpoint)
|
2019-12-17 16:36:27 +01:00
|
|
|
|
2020-09-08 13:47:17 +02:00
|
|
|
// If we got our channel info, we further populate the channel.
|
2019-12-17 16:36:27 +01:00
|
|
|
case nil:
|
2020-09-08 13:47:17 +02:00
|
|
|
channel.Uptime = int64(info.Uptime.Seconds())
|
|
|
|
channel.Lifetime = int64(info.Lifetime.Seconds())
|
2019-12-17 16:36:27 +01:00
|
|
|
|
2020-09-08 13:47:17 +02:00
|
|
|
// If we get an unexpected error, we return it.
|
|
|
|
default:
|
2019-12-17 16:36:27 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return channel, nil
|
2018-09-28 01:34:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// createRPCClosedChannel creates an *lnrpc.ClosedChannelSummary from a
|
|
|
|
// *channeldb.ChannelCloseSummary.
|
2020-02-21 12:24:24 +01:00
|
|
|
func (r *rpcServer) createRPCClosedChannel(
|
|
|
|
dbChannel *channeldb.ChannelCloseSummary) (*lnrpc.ChannelCloseSummary, error) {
|
2018-09-28 01:34:43 +02:00
|
|
|
|
|
|
|
nodePub := dbChannel.RemotePub
|
|
|
|
nodeID := hex.EncodeToString(nodePub.SerializeCompressed())
|
|
|
|
|
2020-02-21 12:24:24 +01:00
|
|
|
var (
|
|
|
|
closeType lnrpc.ChannelCloseSummary_ClosureType
|
2020-04-03 09:09:39 +02:00
|
|
|
openInit lnrpc.Initiator
|
|
|
|
closeInitiator lnrpc.Initiator
|
2020-02-21 12:24:24 +01:00
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
// Lookup local and remote cooperative initiators. If these values
|
|
|
|
// are not known they will just return unknown.
|
|
|
|
openInit, closeInitiator, err = r.getInitiators(
|
|
|
|
&dbChannel.ChanPoint,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert the close type to rpc type.
|
2018-09-28 01:34:43 +02:00
|
|
|
switch dbChannel.CloseType {
|
|
|
|
case channeldb.CooperativeClose:
|
|
|
|
closeType = lnrpc.ChannelCloseSummary_COOPERATIVE_CLOSE
|
|
|
|
case channeldb.LocalForceClose:
|
|
|
|
closeType = lnrpc.ChannelCloseSummary_LOCAL_FORCE_CLOSE
|
|
|
|
case channeldb.RemoteForceClose:
|
|
|
|
closeType = lnrpc.ChannelCloseSummary_REMOTE_FORCE_CLOSE
|
|
|
|
case channeldb.BreachClose:
|
|
|
|
closeType = lnrpc.ChannelCloseSummary_BREACH_CLOSE
|
|
|
|
case channeldb.FundingCanceled:
|
|
|
|
closeType = lnrpc.ChannelCloseSummary_FUNDING_CANCELED
|
|
|
|
case channeldb.Abandoned:
|
|
|
|
closeType = lnrpc.ChannelCloseSummary_ABANDONED
|
|
|
|
}
|
|
|
|
|
2022-07-13 21:27:40 +02:00
|
|
|
dbScid := dbChannel.ShortChanID
|
|
|
|
|
|
|
|
// Fetch the set of aliases for this channel.
|
|
|
|
channelAliases := r.server.aliasMgr.GetAliases(dbScid)
|
|
|
|
|
2020-07-07 10:32:12 +02:00
|
|
|
channel := &lnrpc.ChannelCloseSummary{
|
2018-09-28 01:34:43 +02:00
|
|
|
Capacity: int64(dbChannel.Capacity),
|
|
|
|
RemotePubkey: nodeID,
|
|
|
|
CloseHeight: dbChannel.CloseHeight,
|
|
|
|
CloseType: closeType,
|
|
|
|
ChannelPoint: dbChannel.ChanPoint.String(),
|
|
|
|
ChanId: dbChannel.ShortChanID.ToUint64(),
|
|
|
|
SettledBalance: int64(dbChannel.SettledBalance),
|
|
|
|
TimeLockedBalance: int64(dbChannel.TimeLockedBalance),
|
|
|
|
ChainHash: dbChannel.ChainHash.String(),
|
|
|
|
ClosingTxHash: dbChannel.ClosingTXID.String(),
|
2020-02-21 12:24:24 +01:00
|
|
|
OpenInitiator: openInit,
|
|
|
|
CloseInitiator: closeInitiator,
|
2022-07-13 21:27:40 +02:00
|
|
|
AliasScids: make([]uint64, 0, len(channelAliases)),
|
|
|
|
}
|
|
|
|
|
|
|
|
// Populate the set of aliases.
|
|
|
|
for _, chanAlias := range channelAliases {
|
|
|
|
channel.AliasScids = append(
|
|
|
|
channel.AliasScids, chanAlias.ToUint64(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Populate any historical data that the summary needs.
|
|
|
|
histChan, err := r.server.chanStateDB.FetchHistoricalChannel(
|
|
|
|
&dbChannel.ChanPoint,
|
|
|
|
)
|
|
|
|
switch err {
|
|
|
|
// The channel was closed in a pre-historic version of lnd. Ignore the
|
|
|
|
// error.
|
|
|
|
case channeldb.ErrNoHistoricalBucket:
|
|
|
|
case channeldb.ErrChannelNotFound:
|
|
|
|
|
|
|
|
case nil:
|
|
|
|
if histChan.IsZeroConf() && histChan.ZeroConfConfirmed() {
|
|
|
|
// If the channel was zero-conf, it may have confirmed.
|
|
|
|
// Populate the confirmed SCID if so.
|
|
|
|
confirmedScid := histChan.ZeroConfRealScid().ToUint64()
|
|
|
|
channel.ZeroConfConfirmedScid = confirmedScid
|
|
|
|
}
|
|
|
|
|
|
|
|
// Non-nil error not due to older versions of lnd.
|
|
|
|
default:
|
|
|
|
return nil, err
|
2020-07-07 10:32:12 +02:00
|
|
|
}
|
|
|
|
|
2021-09-21 19:18:17 +02:00
|
|
|
reports, err := r.server.miscDB.FetchChannelReports(
|
2020-08-04 20:56:31 +02:00
|
|
|
*r.cfg.ActiveNetParams.GenesisHash, &dbChannel.ChanPoint,
|
2020-07-07 10:32:12 +02:00
|
|
|
)
|
|
|
|
switch err {
|
|
|
|
// If the channel does not have its resolver outcomes stored,
|
|
|
|
// ignore it.
|
|
|
|
case channeldb.ErrNoChainHashBucket:
|
|
|
|
fallthrough
|
|
|
|
case channeldb.ErrNoChannelSummaries:
|
|
|
|
return channel, nil
|
|
|
|
|
|
|
|
// If there is no error, fallthrough the switch to process reports.
|
|
|
|
case nil:
|
|
|
|
|
|
|
|
// If another error occurred, return it.
|
|
|
|
default:
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, report := range reports {
|
|
|
|
rpcResolution, err := rpcChannelResolution(report)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
channel.Resolutions = append(channel.Resolutions, rpcResolution)
|
|
|
|
}
|
|
|
|
|
|
|
|
return channel, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func rpcChannelResolution(report *channeldb.ResolverReport) (*lnrpc.Resolution,
|
|
|
|
error) {
|
|
|
|
|
|
|
|
res := &lnrpc.Resolution{
|
|
|
|
AmountSat: uint64(report.Amount),
|
2023-04-21 14:51:56 +02:00
|
|
|
Outpoint: lnrpc.MarshalOutPoint(&report.OutPoint),
|
2020-07-07 10:32:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if report.SpendTxID != nil {
|
|
|
|
res.SweepTxid = report.SpendTxID.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
switch report.ResolverType {
|
|
|
|
case channeldb.ResolverTypeAnchor:
|
|
|
|
res.ResolutionType = lnrpc.ResolutionType_ANCHOR
|
|
|
|
|
|
|
|
case channeldb.ResolverTypeIncomingHtlc:
|
|
|
|
res.ResolutionType = lnrpc.ResolutionType_INCOMING_HTLC
|
|
|
|
|
|
|
|
case channeldb.ResolverTypeOutgoingHtlc:
|
|
|
|
res.ResolutionType = lnrpc.ResolutionType_OUTGOING_HTLC
|
|
|
|
|
|
|
|
case channeldb.ResolverTypeCommit:
|
|
|
|
res.ResolutionType = lnrpc.ResolutionType_COMMIT
|
|
|
|
|
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("unknown resolver type: %v",
|
|
|
|
report.ResolverType)
|
|
|
|
}
|
|
|
|
|
|
|
|
switch report.ResolverOutcome {
|
|
|
|
case channeldb.ResolverOutcomeClaimed:
|
|
|
|
res.Outcome = lnrpc.ResolutionOutcome_CLAIMED
|
|
|
|
|
|
|
|
case channeldb.ResolverOutcomeUnclaimed:
|
|
|
|
res.Outcome = lnrpc.ResolutionOutcome_UNCLAIMED
|
|
|
|
|
|
|
|
case channeldb.ResolverOutcomeAbandoned:
|
|
|
|
res.Outcome = lnrpc.ResolutionOutcome_ABANDONED
|
|
|
|
|
|
|
|
case channeldb.ResolverOutcomeFirstStage:
|
|
|
|
res.Outcome = lnrpc.ResolutionOutcome_FIRST_STAGE
|
|
|
|
|
|
|
|
case channeldb.ResolverOutcomeTimeout:
|
|
|
|
res.Outcome = lnrpc.ResolutionOutcome_TIMEOUT
|
|
|
|
|
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("unknown outcome: %v",
|
|
|
|
report.ResolverOutcome)
|
|
|
|
}
|
|
|
|
|
|
|
|
return res, nil
|
2020-02-21 12:24:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// getInitiators returns an initiator enum that provides information about the
|
|
|
|
// party that initiated channel's open and close. This information is obtained
|
|
|
|
// from the historical channel bucket, so unknown values are returned when the
|
|
|
|
// channel is not present (which indicates that it was closed before we started
|
|
|
|
// writing channels to the historical close bucket).
|
|
|
|
func (r *rpcServer) getInitiators(chanPoint *wire.OutPoint) (
|
2020-04-03 09:09:39 +02:00
|
|
|
lnrpc.Initiator,
|
|
|
|
lnrpc.Initiator, error) {
|
2020-02-21 12:24:24 +01:00
|
|
|
|
|
|
|
var (
|
2020-04-03 09:09:39 +02:00
|
|
|
openInitiator = lnrpc.Initiator_INITIATOR_UNKNOWN
|
|
|
|
closeInitiator = lnrpc.Initiator_INITIATOR_UNKNOWN
|
2020-02-21 12:24:24 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
// To get the close initiator for cooperative closes, we need
|
|
|
|
// to get the channel status from the historical channel bucket.
|
2021-08-03 09:57:26 +02:00
|
|
|
histChan, err := r.server.chanStateDB.FetchHistoricalChannel(chanPoint)
|
2020-02-21 12:24:24 +01:00
|
|
|
switch {
|
|
|
|
// The node has upgraded from a version where we did not store
|
|
|
|
// historical channels, and has not closed a channel since. Do
|
|
|
|
// not return an error, initiator values are unknown.
|
|
|
|
case err == channeldb.ErrNoHistoricalBucket:
|
|
|
|
return openInitiator, closeInitiator, nil
|
|
|
|
|
|
|
|
// The channel was closed before we started storing historical
|
|
|
|
// channels. Do not return an error, initiator values are unknown.
|
|
|
|
case err == channeldb.ErrChannelNotFound:
|
|
|
|
return openInitiator, closeInitiator, nil
|
|
|
|
|
|
|
|
case err != nil:
|
|
|
|
return 0, 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we successfully looked up the channel, determine initiator based
|
|
|
|
// on channels status.
|
|
|
|
if histChan.IsInitiator {
|
2020-04-03 09:09:39 +02:00
|
|
|
openInitiator = lnrpc.Initiator_INITIATOR_LOCAL
|
2020-02-21 12:24:24 +01:00
|
|
|
} else {
|
2020-04-03 09:09:39 +02:00
|
|
|
openInitiator = lnrpc.Initiator_INITIATOR_REMOTE
|
2020-02-21 12:24:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
localInit := histChan.HasChanStatus(
|
|
|
|
channeldb.ChanStatusLocalCloseInitiator,
|
|
|
|
)
|
|
|
|
|
|
|
|
remoteInit := histChan.HasChanStatus(
|
|
|
|
channeldb.ChanStatusRemoteCloseInitiator,
|
|
|
|
)
|
|
|
|
|
|
|
|
switch {
|
|
|
|
// There is a possible case where closes were attempted by both parties.
|
|
|
|
// We return the initiator as both in this case to provide full
|
|
|
|
// information about the close.
|
|
|
|
case localInit && remoteInit:
|
2020-04-03 09:09:39 +02:00
|
|
|
closeInitiator = lnrpc.Initiator_INITIATOR_BOTH
|
2020-02-21 12:24:24 +01:00
|
|
|
|
|
|
|
case localInit:
|
2020-04-03 09:09:39 +02:00
|
|
|
closeInitiator = lnrpc.Initiator_INITIATOR_LOCAL
|
2020-02-21 12:24:24 +01:00
|
|
|
|
|
|
|
case remoteInit:
|
2020-04-03 09:09:39 +02:00
|
|
|
closeInitiator = lnrpc.Initiator_INITIATOR_REMOTE
|
2018-09-28 01:34:43 +02:00
|
|
|
}
|
2020-02-21 12:24:24 +01:00
|
|
|
|
|
|
|
return openInitiator, closeInitiator, nil
|
2016-09-26 05:04:03 +02:00
|
|
|
}
|
|
|
|
|
2019-01-23 03:28:35 +01:00
|
|
|
// SubscribeChannelEvents returns a uni-directional stream (server -> client)
|
|
|
|
// for notifying the client of newly active, inactive or closed channels.
|
|
|
|
func (r *rpcServer) SubscribeChannelEvents(req *lnrpc.ChannelEventSubscription,
|
|
|
|
updateStream lnrpc.Lightning_SubscribeChannelEventsServer) error {
|
|
|
|
|
|
|
|
channelEventSub, err := r.server.channelNotifier.SubscribeChannelEvents()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure that the resources for the client is cleaned up once either
|
|
|
|
// the server, or client exits.
|
|
|
|
defer channelEventSub.Cancel()
|
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
// A new update has been sent by the channel router, we'll
|
|
|
|
// marshal it into the form expected by the gRPC client, then
|
|
|
|
// send it off to the client(s).
|
|
|
|
case e := <-channelEventSub.Updates():
|
|
|
|
var update *lnrpc.ChannelEventUpdate
|
|
|
|
switch event := e.(type) {
|
2019-09-29 12:13:01 +02:00
|
|
|
case channelnotifier.PendingOpenChannelEvent:
|
|
|
|
update = &lnrpc.ChannelEventUpdate{
|
|
|
|
Type: lnrpc.ChannelEventUpdate_PENDING_OPEN_CHANNEL,
|
|
|
|
Channel: &lnrpc.ChannelEventUpdate_PendingOpenChannel{
|
|
|
|
PendingOpenChannel: &lnrpc.PendingUpdate{
|
|
|
|
Txid: event.ChannelPoint.Hash[:],
|
|
|
|
OutputIndex: event.ChannelPoint.Index,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2019-01-23 03:28:35 +01:00
|
|
|
case channelnotifier.OpenChannelEvent:
|
2022-01-16 18:24:20 +01:00
|
|
|
channel, err := createRPCOpenChannel(
|
2023-01-15 10:01:09 +01:00
|
|
|
r, event.Channel, true, false,
|
2022-01-16 18:24:20 +01:00
|
|
|
)
|
2019-12-17 16:36:27 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-01-23 03:28:35 +01:00
|
|
|
update = &lnrpc.ChannelEventUpdate{
|
|
|
|
Type: lnrpc.ChannelEventUpdate_OPEN_CHANNEL,
|
|
|
|
Channel: &lnrpc.ChannelEventUpdate_OpenChannel{
|
|
|
|
OpenChannel: channel,
|
|
|
|
},
|
|
|
|
}
|
2019-12-16 06:38:08 +01:00
|
|
|
|
2019-01-23 03:28:35 +01:00
|
|
|
case channelnotifier.ClosedChannelEvent:
|
2020-02-21 12:24:24 +01:00
|
|
|
closedChannel, err := r.createRPCClosedChannel(
|
|
|
|
event.CloseSummary,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-01-23 03:28:35 +01:00
|
|
|
update = &lnrpc.ChannelEventUpdate{
|
|
|
|
Type: lnrpc.ChannelEventUpdate_CLOSED_CHANNEL,
|
|
|
|
Channel: &lnrpc.ChannelEventUpdate_ClosedChannel{
|
|
|
|
ClosedChannel: closedChannel,
|
|
|
|
},
|
|
|
|
}
|
2019-12-16 06:38:08 +01:00
|
|
|
|
2019-01-23 03:28:35 +01:00
|
|
|
case channelnotifier.ActiveChannelEvent:
|
|
|
|
update = &lnrpc.ChannelEventUpdate{
|
|
|
|
Type: lnrpc.ChannelEventUpdate_ACTIVE_CHANNEL,
|
|
|
|
Channel: &lnrpc.ChannelEventUpdate_ActiveChannel{
|
|
|
|
ActiveChannel: &lnrpc.ChannelPoint{
|
|
|
|
FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
|
|
|
|
FundingTxidBytes: event.ChannelPoint.Hash[:],
|
|
|
|
},
|
|
|
|
OutputIndex: event.ChannelPoint.Index,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2019-12-16 06:38:08 +01:00
|
|
|
|
2019-01-23 03:28:35 +01:00
|
|
|
case channelnotifier.InactiveChannelEvent:
|
|
|
|
update = &lnrpc.ChannelEventUpdate{
|
|
|
|
Type: lnrpc.ChannelEventUpdate_INACTIVE_CHANNEL,
|
|
|
|
Channel: &lnrpc.ChannelEventUpdate_InactiveChannel{
|
|
|
|
InactiveChannel: &lnrpc.ChannelPoint{
|
|
|
|
FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
|
|
|
|
FundingTxidBytes: event.ChannelPoint.Hash[:],
|
|
|
|
},
|
|
|
|
OutputIndex: event.ChannelPoint.Index,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2019-12-16 06:38:08 +01:00
|
|
|
|
2022-11-23 23:57:53 +01:00
|
|
|
// Completely ignore ActiveLinkEvent and
|
|
|
|
// InactiveLinkEvent as this is explicitly not exposed
|
|
|
|
// to the RPC.
|
|
|
|
case channelnotifier.ActiveLinkEvent,
|
|
|
|
channelnotifier.InactiveLinkEvent:
|
|
|
|
|
2020-03-07 04:42:58 +01:00
|
|
|
continue
|
|
|
|
|
2021-08-09 13:55:34 +02:00
|
|
|
case channelnotifier.FullyResolvedChannelEvent:
|
|
|
|
update = &lnrpc.ChannelEventUpdate{
|
|
|
|
Type: lnrpc.ChannelEventUpdate_FULLY_RESOLVED_CHANNEL,
|
|
|
|
Channel: &lnrpc.ChannelEventUpdate_FullyResolvedChannel{
|
|
|
|
FullyResolvedChannel: &lnrpc.ChannelPoint{
|
|
|
|
FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
|
|
|
|
FundingTxidBytes: event.ChannelPoint.Hash[:],
|
|
|
|
},
|
|
|
|
OutputIndex: event.ChannelPoint.Index,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2019-01-23 03:28:35 +01:00
|
|
|
default:
|
|
|
|
return fmt.Errorf("unexpected channel event update: %v", event)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := updateStream.Send(update); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-07-18 22:41:11 +02:00
|
|
|
|
2021-09-06 19:08:21 +02:00
|
|
|
// The response stream's context for whatever reason has been
|
|
|
|
// closed. If context is closed by an exceeded deadline we will
|
|
|
|
// return an error.
|
2020-07-18 22:41:11 +02:00
|
|
|
case <-updateStream.Context().Done():
|
2021-09-06 19:08:21 +02:00
|
|
|
if errors.Is(updateStream.Context().Err(), context.Canceled) {
|
|
|
|
return nil
|
|
|
|
}
|
2020-07-18 22:41:11 +02:00
|
|
|
return updateStream.Context().Err()
|
|
|
|
|
2019-01-23 03:28:35 +01:00
|
|
|
case <-r.quit:
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-07 05:40:28 +02:00
|
|
|
// paymentStream enables different types of payment streams, such as:
|
|
|
|
// lnrpc.Lightning_SendPaymentServer and lnrpc.Lightning_SendToRouteServer to
|
|
|
|
// execute sendPayment. We use this struct as a sort of bridge to enable code
|
|
|
|
// re-use between SendPayment and SendToRoute.
|
2018-05-01 10:17:55 +02:00
|
|
|
type paymentStream struct {
|
2018-06-07 05:40:28 +02:00
|
|
|
recv func() (*rpcPaymentRequest, error)
|
2018-05-01 10:17:55 +02:00
|
|
|
send func(*lnrpc.SendResponse) error
|
|
|
|
}
|
|
|
|
|
2018-06-07 05:40:28 +02:00
|
|
|
// rpcPaymentRequest wraps lnrpc.SendRequest so that routes from
|
|
|
|
// lnrpc.SendToRouteRequest can be passed to sendPayment.
|
|
|
|
type rpcPaymentRequest struct {
|
2018-05-01 10:17:55 +02:00
|
|
|
*lnrpc.SendRequest
|
2018-08-08 11:09:30 +02:00
|
|
|
route *route.Route
|
2018-05-01 10:17:55 +02:00
|
|
|
}
|
|
|
|
|
2016-07-13 02:46:25 +02:00
|
|
|
// SendPayment dispatches a bi-directional streaming RPC for sending payments
|
|
|
|
// through the Lightning Network. A single RPC invocation creates a persistent
|
|
|
|
// bi-directional stream allowing clients to rapidly send payments through the
|
|
|
|
// Lightning Network with a single persistent connection.
|
2018-05-01 10:17:55 +02:00
|
|
|
func (r *rpcServer) SendPayment(stream lnrpc.Lightning_SendPaymentServer) error {
|
2018-10-15 21:13:37 +02:00
|
|
|
var lock sync.Mutex
|
|
|
|
|
2018-05-01 10:17:55 +02:00
|
|
|
return r.sendPayment(&paymentStream{
|
2018-06-07 05:40:28 +02:00
|
|
|
recv: func() (*rpcPaymentRequest, error) {
|
2018-05-01 10:17:55 +02:00
|
|
|
req, err := stream.Recv()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-06-07 05:40:28 +02:00
|
|
|
return &rpcPaymentRequest{
|
2018-05-01 10:17:55 +02:00
|
|
|
SendRequest: req,
|
|
|
|
}, nil
|
|
|
|
},
|
2018-10-15 21:13:37 +02:00
|
|
|
send: func(r *lnrpc.SendResponse) error {
|
|
|
|
// Calling stream.Send concurrently is not safe.
|
|
|
|
lock.Lock()
|
|
|
|
defer lock.Unlock()
|
|
|
|
return stream.Send(r)
|
|
|
|
},
|
2018-05-01 10:17:55 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// SendToRoute dispatches a bi-directional streaming RPC for sending payments
|
|
|
|
// through the Lightning Network via predefined routes passed in. A single RPC
|
|
|
|
// invocation creates a persistent bi-directional stream allowing clients to
|
2018-06-07 05:40:28 +02:00
|
|
|
// rapidly send payments through the Lightning Network with a single persistent
|
|
|
|
// connection.
|
2018-05-01 10:17:55 +02:00
|
|
|
func (r *rpcServer) SendToRoute(stream lnrpc.Lightning_SendToRouteServer) error {
|
2018-10-15 21:13:37 +02:00
|
|
|
var lock sync.Mutex
|
|
|
|
|
2018-05-01 10:17:55 +02:00
|
|
|
return r.sendPayment(&paymentStream{
|
2018-06-07 05:40:28 +02:00
|
|
|
recv: func() (*rpcPaymentRequest, error) {
|
2018-05-01 10:17:55 +02:00
|
|
|
req, err := stream.Recv()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-03-14 15:13:45 +01:00
|
|
|
return r.unmarshallSendToRouteRequest(req)
|
2018-05-01 10:17:55 +02:00
|
|
|
},
|
2018-10-15 21:13:37 +02:00
|
|
|
send: func(r *lnrpc.SendResponse) error {
|
|
|
|
// Calling stream.Send concurrently is not safe.
|
|
|
|
lock.Lock()
|
|
|
|
defer lock.Unlock()
|
|
|
|
return stream.Send(r)
|
|
|
|
},
|
2018-05-01 10:17:55 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-01-21 12:50:36 +01:00
|
|
|
// unmarshallSendToRouteRequest unmarshalls an rpc sendtoroute request
|
2019-03-14 15:13:45 +01:00
|
|
|
func (r *rpcServer) unmarshallSendToRouteRequest(
|
|
|
|
req *lnrpc.SendToRouteRequest) (*rpcPaymentRequest, error) {
|
2019-01-21 12:50:36 +01:00
|
|
|
|
2018-08-08 11:09:30 +02:00
|
|
|
if req.Route == nil {
|
|
|
|
return nil, fmt.Errorf("unable to send, no route provided")
|
2019-01-21 12:50:36 +01:00
|
|
|
}
|
|
|
|
|
2019-03-14 15:13:45 +01:00
|
|
|
route, err := r.routerBackend.UnmarshallRoute(req.Route)
|
2018-08-08 11:09:30 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2019-01-21 12:50:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return &rpcPaymentRequest{
|
|
|
|
SendRequest: &lnrpc.SendRequest{
|
2019-02-07 00:48:23 +01:00
|
|
|
PaymentHash: req.PaymentHash,
|
|
|
|
PaymentHashString: req.PaymentHashString,
|
2019-01-21 12:50:36 +01:00
|
|
|
},
|
2018-08-08 11:09:30 +02:00
|
|
|
route: route,
|
2019-01-21 12:50:36 +01:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2018-06-07 05:40:28 +02:00
|
|
|
// rpcPaymentIntent is a small wrapper struct around the of values we can
|
|
|
|
// receive from a client over RPC if they wish to send a payment. We'll either
|
|
|
|
// extract these fields from a payment request (which may include routing
|
|
|
|
// hints), or we'll get a fully populated route from the user that we'll pass
|
|
|
|
// directly to the channel router for dispatching.
|
|
|
|
type rpcPaymentIntent struct {
|
2020-05-07 11:48:39 +02:00
|
|
|
msat lnwire.MilliSatoshi
|
|
|
|
feeLimit lnwire.MilliSatoshi
|
|
|
|
cltvLimit uint32
|
|
|
|
dest route.Vertex
|
|
|
|
rHash [32]byte
|
|
|
|
cltvDelta uint16
|
|
|
|
routeHints [][]zpay32.HopHint
|
|
|
|
outgoingChannelIDs []uint64
|
|
|
|
lastHop *route.Vertex
|
|
|
|
destFeatures *lnwire.FeatureVector
|
|
|
|
paymentAddr *[32]byte
|
|
|
|
payReq []byte
|
2021-10-04 09:33:12 +02:00
|
|
|
metadata []byte
|
2018-06-07 05:40:28 +02:00
|
|
|
|
2019-12-11 10:52:27 +01:00
|
|
|
destCustomRecords record.CustomSet
|
2019-07-31 06:44:02 +02:00
|
|
|
|
2018-08-08 11:09:30 +02:00
|
|
|
route *route.Route
|
2018-06-07 05:40:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// extractPaymentIntent attempts to parse the complete details required to
|
|
|
|
// dispatch a client from the information presented by an RPC client. There are
|
|
|
|
// three ways a client can specify their payment details: a payment request,
|
|
|
|
// via manual details, or via a complete route.
|
2019-11-25 14:13:21 +01:00
|
|
|
func (r *rpcServer) extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPaymentIntent, error) {
|
2018-06-07 05:40:28 +02:00
|
|
|
payIntent := rpcPaymentIntent{}
|
|
|
|
|
|
|
|
// If a route was specified, then we can use that directly.
|
2018-08-08 11:09:30 +02:00
|
|
|
if rpcPayReq.route != nil {
|
2018-06-07 05:40:28 +02:00
|
|
|
// If the user is using the REST interface, then they'll be
|
|
|
|
// passing the payment hash as a hex encoded string.
|
|
|
|
if rpcPayReq.PaymentHashString != "" {
|
|
|
|
paymentHash, err := hex.DecodeString(
|
|
|
|
rpcPayReq.PaymentHashString,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return payIntent, err
|
|
|
|
}
|
|
|
|
|
|
|
|
copy(payIntent.rHash[:], paymentHash)
|
|
|
|
} else {
|
|
|
|
copy(payIntent.rHash[:], rpcPayReq.PaymentHash)
|
|
|
|
}
|
|
|
|
|
2018-08-08 11:09:30 +02:00
|
|
|
payIntent.route = rpcPayReq.route
|
2018-06-07 05:40:28 +02:00
|
|
|
return payIntent, nil
|
|
|
|
}
|
|
|
|
|
2019-02-01 13:53:27 +01:00
|
|
|
// If there are no routes specified, pass along a outgoing channel
|
2020-05-07 11:48:39 +02:00
|
|
|
// restriction if specified. The main server rpc does not support
|
|
|
|
// multiple channel restrictions.
|
2019-02-01 13:53:27 +01:00
|
|
|
if rpcPayReq.OutgoingChanId != 0 {
|
2020-05-07 11:48:39 +02:00
|
|
|
payIntent.outgoingChannelIDs = []uint64{
|
|
|
|
rpcPayReq.OutgoingChanId,
|
|
|
|
}
|
2019-02-01 13:53:27 +01:00
|
|
|
}
|
|
|
|
|
2019-11-18 12:08:42 +01:00
|
|
|
// Pass along a last hop restriction if specified.
|
|
|
|
if len(rpcPayReq.LastHopPubkey) > 0 {
|
|
|
|
lastHop, err := route.NewVertexFromBytes(
|
|
|
|
rpcPayReq.LastHopPubkey,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return payIntent, err
|
|
|
|
}
|
|
|
|
payIntent.lastHop = &lastHop
|
|
|
|
}
|
|
|
|
|
2019-10-11 21:46:10 +02:00
|
|
|
// Take the CLTV limit from the request if set, otherwise use the max.
|
|
|
|
cltvLimit, err := routerrpc.ValidateCLTVLimit(
|
2020-05-14 14:07:26 +02:00
|
|
|
rpcPayReq.CltvLimit, r.cfg.MaxOutgoingCltvExpiry,
|
2019-10-11 21:46:10 +02:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return payIntent, err
|
2018-11-04 11:11:48 +01:00
|
|
|
}
|
2019-10-11 21:46:10 +02:00
|
|
|
payIntent.cltvLimit = cltvLimit
|
2018-11-04 11:11:48 +01:00
|
|
|
|
2019-12-12 14:43:36 +01:00
|
|
|
customRecords := record.CustomSet(rpcPayReq.DestCustomRecords)
|
|
|
|
if err := customRecords.Validate(); err != nil {
|
2019-11-20 12:00:25 +01:00
|
|
|
return payIntent, err
|
2019-07-31 06:44:02 +02:00
|
|
|
}
|
2019-12-12 14:43:36 +01:00
|
|
|
payIntent.destCustomRecords = customRecords
|
2019-07-31 06:44:02 +02:00
|
|
|
|
2019-11-25 14:13:21 +01:00
|
|
|
validateDest := func(dest route.Vertex) error {
|
|
|
|
if rpcPayReq.AllowSelfPayment {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if dest == r.selfNode {
|
|
|
|
return errors.New("self-payments not allowed")
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-06-07 05:40:28 +02:00
|
|
|
// If the payment request field isn't blank, then the details of the
|
|
|
|
// invoice are encoded entirely within the encoded payReq. So we'll
|
|
|
|
// attempt to decode it, populating the payment accordingly.
|
|
|
|
if rpcPayReq.PaymentRequest != "" {
|
|
|
|
payReq, err := zpay32.Decode(
|
2020-08-04 20:56:31 +02:00
|
|
|
rpcPayReq.PaymentRequest, r.cfg.ActiveNetParams.Params,
|
2018-06-07 05:40:28 +02:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return payIntent, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Next, we'll ensure that this payreq hasn't already expired.
|
2019-04-18 09:34:28 +02:00
|
|
|
err = routerrpc.ValidatePayReqExpiry(payReq)
|
2018-06-07 05:40:28 +02:00
|
|
|
if err != nil {
|
|
|
|
return payIntent, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the amount was not included in the invoice, then we let
|
2022-11-11 23:31:04 +01:00
|
|
|
// the payer specify the amount of satoshis they wish to send.
|
2018-06-07 05:40:28 +02:00
|
|
|
// We override the amount to pay with the amount provided from
|
|
|
|
// the payment request.
|
|
|
|
if payReq.MilliSat == nil {
|
2019-11-11 16:44:08 +01:00
|
|
|
amt, err := lnrpc.UnmarshallAmt(
|
|
|
|
rpcPayReq.Amt, rpcPayReq.AmtMsat,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return payIntent, err
|
|
|
|
}
|
|
|
|
if amt == 0 {
|
2018-06-12 01:25:34 +02:00
|
|
|
return payIntent, errors.New("amount must be " +
|
|
|
|
"specified when paying a zero amount " +
|
|
|
|
"invoice")
|
|
|
|
}
|
|
|
|
|
2019-11-11 16:44:08 +01:00
|
|
|
payIntent.msat = amt
|
2018-06-07 05:40:28 +02:00
|
|
|
} else {
|
|
|
|
payIntent.msat = *payReq.MilliSat
|
|
|
|
}
|
|
|
|
|
2018-04-19 16:27:37 +02:00
|
|
|
// Calculate the fee limit that should be used for this payment.
|
2019-11-11 16:24:33 +01:00
|
|
|
payIntent.feeLimit = lnrpc.CalculateFeeLimit(
|
2018-04-19 16:27:37 +02:00
|
|
|
rpcPayReq.FeeLimit, payIntent.msat,
|
|
|
|
)
|
|
|
|
|
2018-06-07 05:40:28 +02:00
|
|
|
copy(payIntent.rHash[:], payReq.PaymentHash[:])
|
2019-03-05 16:55:19 +01:00
|
|
|
destKey := payReq.Destination.SerializeCompressed()
|
|
|
|
copy(payIntent.dest[:], destKey)
|
2018-06-07 05:40:28 +02:00
|
|
|
payIntent.cltvDelta = uint16(payReq.MinFinalCLTVExpiry())
|
|
|
|
payIntent.routeHints = payReq.RouteHints
|
2019-05-30 01:31:32 +02:00
|
|
|
payIntent.payReq = []byte(rpcPayReq.PaymentRequest)
|
2019-12-19 08:56:59 +01:00
|
|
|
payIntent.destFeatures = payReq.Features
|
|
|
|
payIntent.paymentAddr = payReq.PaymentAddr
|
2021-10-04 09:33:12 +02:00
|
|
|
payIntent.metadata = payReq.Metadata
|
2018-06-07 05:40:28 +02:00
|
|
|
|
2019-11-25 14:13:21 +01:00
|
|
|
if err := validateDest(payIntent.dest); err != nil {
|
|
|
|
return payIntent, err
|
|
|
|
}
|
|
|
|
|
2021-07-30 17:41:11 +02:00
|
|
|
// Do bounds checking with the block padding.
|
|
|
|
err = routing.ValidateCLTVLimit(
|
|
|
|
payIntent.cltvLimit, payIntent.cltvDelta, true,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return payIntent, err
|
|
|
|
}
|
|
|
|
|
2018-06-07 05:40:28 +02:00
|
|
|
return payIntent, nil
|
2018-04-19 16:27:37 +02:00
|
|
|
}
|
2018-06-07 05:40:28 +02:00
|
|
|
|
2018-04-19 16:27:37 +02:00
|
|
|
// At this point, a destination MUST be specified, so we'll convert it
|
|
|
|
// into the proper representation now. The destination will either be
|
|
|
|
// encoded as raw bytes, or via a hex string.
|
2019-03-05 16:55:19 +01:00
|
|
|
var pubBytes []byte
|
2018-04-19 16:27:37 +02:00
|
|
|
if len(rpcPayReq.Dest) != 0 {
|
2019-03-05 16:55:19 +01:00
|
|
|
pubBytes = rpcPayReq.Dest
|
2018-04-19 16:27:37 +02:00
|
|
|
} else {
|
2019-03-05 16:55:19 +01:00
|
|
|
var err error
|
|
|
|
pubBytes, err = hex.DecodeString(rpcPayReq.DestString)
|
2018-04-19 16:27:37 +02:00
|
|
|
if err != nil {
|
|
|
|
return payIntent, err
|
|
|
|
}
|
|
|
|
}
|
2019-03-05 16:55:19 +01:00
|
|
|
if len(pubBytes) != 33 {
|
|
|
|
return payIntent, errors.New("invalid key length")
|
|
|
|
}
|
|
|
|
copy(payIntent.dest[:], pubBytes)
|
2018-06-07 05:40:28 +02:00
|
|
|
|
2019-11-25 14:13:21 +01:00
|
|
|
if err := validateDest(payIntent.dest); err != nil {
|
|
|
|
return payIntent, err
|
|
|
|
}
|
|
|
|
|
2021-03-15 09:30:25 +01:00
|
|
|
// Payment address may not be needed by legacy invoices.
|
|
|
|
if len(rpcPayReq.PaymentAddr) != 0 && len(rpcPayReq.PaymentAddr) != 32 {
|
|
|
|
return payIntent, errors.New("invalid payment address length")
|
|
|
|
}
|
2021-04-07 04:41:31 +02:00
|
|
|
|
2021-06-22 10:58:13 +02:00
|
|
|
// Set the payment address if it was explicitly defined with the
|
|
|
|
// rpcPaymentRequest.
|
|
|
|
// Note that the payment address for the payIntent should be nil if none
|
|
|
|
// was provided with the rpcPaymentRequest.
|
|
|
|
if len(rpcPayReq.PaymentAddr) != 0 {
|
2021-04-07 04:41:31 +02:00
|
|
|
payIntent.paymentAddr = &[32]byte{}
|
2021-06-22 10:58:13 +02:00
|
|
|
copy(payIntent.paymentAddr[:], rpcPayReq.PaymentAddr)
|
2021-04-07 04:41:31 +02:00
|
|
|
}
|
2021-03-15 09:30:25 +01:00
|
|
|
|
2018-04-19 16:27:37 +02:00
|
|
|
// Otherwise, If the payment request field was not specified
|
|
|
|
// (and a custom route wasn't specified), construct the payment
|
|
|
|
// from the other fields.
|
2019-11-11 16:44:08 +01:00
|
|
|
payIntent.msat, err = lnrpc.UnmarshallAmt(
|
|
|
|
rpcPayReq.Amt, rpcPayReq.AmtMsat,
|
2018-04-19 16:27:37 +02:00
|
|
|
)
|
2019-11-11 16:44:08 +01:00
|
|
|
if err != nil {
|
|
|
|
return payIntent, err
|
|
|
|
}
|
2018-06-07 05:40:28 +02:00
|
|
|
|
2018-04-19 16:27:37 +02:00
|
|
|
// Calculate the fee limit that should be used for this payment.
|
2019-11-11 16:24:33 +01:00
|
|
|
payIntent.feeLimit = lnrpc.CalculateFeeLimit(
|
2018-04-19 16:27:37 +02:00
|
|
|
rpcPayReq.FeeLimit, payIntent.msat,
|
|
|
|
)
|
|
|
|
|
2019-04-18 09:45:21 +02:00
|
|
|
if rpcPayReq.FinalCltvDelta != 0 {
|
|
|
|
payIntent.cltvDelta = uint16(rpcPayReq.FinalCltvDelta)
|
|
|
|
} else {
|
2020-01-10 10:25:53 +01:00
|
|
|
// If no final cltv delta is given, assume the default that we
|
|
|
|
// use when creating an invoice. We do not assume the default of
|
|
|
|
// 9 blocks that is defined in BOLT-11, because this is never
|
|
|
|
// enough for other lnd nodes.
|
2020-05-14 14:07:26 +02:00
|
|
|
payIntent.cltvDelta = uint16(r.cfg.Bitcoin.TimeLockDelta)
|
2019-04-18 09:45:21 +02:00
|
|
|
}
|
2018-04-19 16:27:37 +02:00
|
|
|
|
2021-05-12 20:44:56 +02:00
|
|
|
// Do bounds checking with the block padding so the router isn't left
|
|
|
|
// with a zombie payment in case the user messes up.
|
|
|
|
err = routing.ValidateCLTVLimit(
|
|
|
|
payIntent.cltvLimit, payIntent.cltvDelta, true,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return payIntent, err
|
|
|
|
}
|
|
|
|
|
2018-07-01 00:13:14 +02:00
|
|
|
// If the user is manually specifying payment details, then the payment
|
|
|
|
// hash may be encoded as a string.
|
|
|
|
switch {
|
|
|
|
case rpcPayReq.PaymentHashString != "":
|
2018-04-19 16:27:37 +02:00
|
|
|
paymentHash, err := hex.DecodeString(
|
|
|
|
rpcPayReq.PaymentHashString,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return payIntent, err
|
2018-06-07 05:40:28 +02:00
|
|
|
}
|
2018-04-19 16:27:37 +02:00
|
|
|
|
|
|
|
copy(payIntent.rHash[:], paymentHash)
|
2018-06-07 05:40:28 +02:00
|
|
|
|
2018-07-01 00:13:14 +02:00
|
|
|
default:
|
|
|
|
copy(payIntent.rHash[:], rpcPayReq.PaymentHash)
|
2018-06-07 05:40:28 +02:00
|
|
|
}
|
|
|
|
|
2019-12-19 08:57:38 +01:00
|
|
|
// Unmarshal any custom destination features.
|
|
|
|
payIntent.destFeatures, err = routerrpc.UnmarshalFeatures(
|
|
|
|
rpcPayReq.DestFeatures,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return payIntent, err
|
|
|
|
}
|
|
|
|
|
2018-06-07 05:40:28 +02:00
|
|
|
return payIntent, nil
|
|
|
|
}
|
|
|
|
|
2018-07-31 10:29:12 +02:00
|
|
|
type paymentIntentResponse struct {
|
2019-04-05 17:36:11 +02:00
|
|
|
Route *route.Route
|
2018-07-31 10:29:12 +02:00
|
|
|
Preimage [32]byte
|
|
|
|
Err error
|
|
|
|
}
|
|
|
|
|
2018-06-07 05:40:28 +02:00
|
|
|
// dispatchPaymentIntent attempts to fully dispatch an RPC payment intent.
|
|
|
|
// We'll either pass the payment as a whole to the channel router, or give it a
|
|
|
|
// pre-built route. The first error this method returns denotes if we were
|
|
|
|
// unable to save the payment. The second error returned denotes if the payment
|
|
|
|
// didn't succeed.
|
2018-07-31 10:29:12 +02:00
|
|
|
func (r *rpcServer) dispatchPaymentIntent(
|
|
|
|
payIntent *rpcPaymentIntent) (*paymentIntentResponse, error) {
|
|
|
|
|
2018-06-07 05:40:28 +02:00
|
|
|
// Construct a payment request to send to the channel router. If the
|
|
|
|
// payment is successful, the route chosen will be returned. Otherwise,
|
|
|
|
// we'll get a non-nil error.
|
|
|
|
var (
|
|
|
|
preImage [32]byte
|
2019-04-05 17:36:11 +02:00
|
|
|
route *route.Route
|
2018-06-07 05:40:28 +02:00
|
|
|
routerErr error
|
|
|
|
)
|
|
|
|
|
|
|
|
// If a route was specified, then we'll pass the route directly to the
|
|
|
|
// router, otherwise we'll create a payment session to execute it.
|
2018-08-08 11:09:30 +02:00
|
|
|
if payIntent.route == nil {
|
2018-06-07 05:40:28 +02:00
|
|
|
payment := &routing.LightningPayment{
|
2020-05-07 11:48:39 +02:00
|
|
|
Target: payIntent.dest,
|
|
|
|
Amount: payIntent.msat,
|
|
|
|
FinalCLTVDelta: payIntent.cltvDelta,
|
|
|
|
FeeLimit: payIntent.feeLimit,
|
|
|
|
CltvLimit: payIntent.cltvLimit,
|
|
|
|
RouteHints: payIntent.routeHints,
|
|
|
|
OutgoingChannelIDs: payIntent.outgoingChannelIDs,
|
|
|
|
LastHop: payIntent.lastHop,
|
|
|
|
PaymentRequest: payIntent.payReq,
|
|
|
|
PayAttemptTimeout: routing.DefaultPayAttemptTimeout,
|
|
|
|
DestCustomRecords: payIntent.destCustomRecords,
|
|
|
|
DestFeatures: payIntent.destFeatures,
|
|
|
|
PaymentAddr: payIntent.paymentAddr,
|
2021-10-04 09:33:12 +02:00
|
|
|
Metadata: payIntent.metadata,
|
2020-01-28 16:07:34 +01:00
|
|
|
|
|
|
|
// Don't enable multi-part payments on the main rpc.
|
|
|
|
// Users need to use routerrpc for that.
|
2020-04-22 09:19:11 +02:00
|
|
|
MaxParts: 1,
|
2018-06-07 05:40:28 +02:00
|
|
|
}
|
2021-03-31 12:23:08 +02:00
|
|
|
err := payment.SetPaymentHash(payIntent.rHash)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-06-07 05:40:28 +02:00
|
|
|
|
|
|
|
preImage, route, routerErr = r.server.chanRouter.SendPayment(
|
|
|
|
payment,
|
|
|
|
)
|
|
|
|
} else {
|
2020-05-06 15:36:51 +02:00
|
|
|
var attempt *channeldb.HTLCAttempt
|
|
|
|
attempt, routerErr = r.server.chanRouter.SendToRoute(
|
2018-08-08 11:09:30 +02:00
|
|
|
payIntent.rHash, payIntent.route,
|
2018-06-07 05:40:28 +02:00
|
|
|
)
|
2018-08-08 11:09:30 +02:00
|
|
|
|
2020-05-06 15:36:51 +02:00
|
|
|
if routerErr == nil {
|
|
|
|
preImage = attempt.Settle.Preimage
|
|
|
|
}
|
|
|
|
|
2018-08-08 11:09:30 +02:00
|
|
|
route = payIntent.route
|
2018-06-07 05:40:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// If the route failed, then we'll return a nil save err, but a non-nil
|
|
|
|
// routing err.
|
|
|
|
if routerErr != nil {
|
2019-06-21 09:32:51 +02:00
|
|
|
rpcsLog.Warnf("Unable to send payment: %v", routerErr)
|
|
|
|
|
2018-07-31 10:29:12 +02:00
|
|
|
return &paymentIntentResponse{
|
|
|
|
Err: routerErr,
|
|
|
|
}, nil
|
2018-06-07 05:40:28 +02:00
|
|
|
}
|
|
|
|
|
2018-07-31 10:29:12 +02:00
|
|
|
return &paymentIntentResponse{
|
|
|
|
Route: route,
|
|
|
|
Preimage: preImage,
|
|
|
|
}, nil
|
2018-06-07 05:40:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// sendPayment takes a paymentStream (a source of pre-built routes or payment
|
|
|
|
// requests) and continually attempt to dispatch payment requests written to
|
|
|
|
// the write end of the stream. Responses will also be streamed back to the
|
|
|
|
// client via the write end of the stream. This method is by both SendToRoute
|
|
|
|
// and SendPayment as the logic is virtually identical.
|
2018-05-01 10:17:55 +02:00
|
|
|
func (r *rpcServer) sendPayment(stream *paymentStream) error {
|
2018-06-07 05:40:28 +02:00
|
|
|
payChan := make(chan *rpcPaymentIntent)
|
2016-07-22 01:22:30 +02:00
|
|
|
errChan := make(chan error, 1)
|
2016-10-27 00:05:10 +02:00
|
|
|
|
2017-08-03 05:59:43 +02:00
|
|
|
// We don't allow payments to be sent while the daemon itself is still
|
|
|
|
// syncing as we may be trying to sent a payment over a "stale"
|
|
|
|
// channel.
|
|
|
|
if !r.server.Started() {
|
2019-10-23 23:35:41 +02:00
|
|
|
return ErrServerNotActive
|
2017-08-03 05:59:43 +02:00
|
|
|
}
|
|
|
|
|
2017-06-17 00:11:02 +02:00
|
|
|
// TODO(roasbeef): check payment filter to see if already used?
|
|
|
|
|
2017-04-12 06:24:16 +02:00
|
|
|
// In order to limit the level of concurrency and prevent a client from
|
|
|
|
// attempting to OOM the server, we'll set up a semaphore to create an
|
|
|
|
// upper ceiling on the number of outstanding payments.
|
|
|
|
const numOutstandingPayments = 2000
|
|
|
|
htlcSema := make(chan struct{}, numOutstandingPayments)
|
|
|
|
for i := 0; i < numOutstandingPayments; i++ {
|
|
|
|
htlcSema <- struct{}{}
|
|
|
|
}
|
|
|
|
|
2018-11-02 11:33:01 +01:00
|
|
|
// We keep track of the running goroutines and set up a quit signal we
|
|
|
|
// can use to request them to exit if the method returns because of an
|
|
|
|
// encountered error.
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
reqQuit := make(chan struct{})
|
|
|
|
defer close(reqQuit)
|
|
|
|
|
2016-10-27 00:05:10 +02:00
|
|
|
// Launch a new goroutine to handle reading new payment requests from
|
|
|
|
// the client. This way we can handle errors independently of blocking
|
|
|
|
// and waiting for the next payment request to come through.
|
2018-10-15 21:13:37 +02:00
|
|
|
// TODO(joostjager): Callers expect result to come in in the same order
|
|
|
|
// as the request were sent, but this is far from guarantueed in the
|
|
|
|
// code below.
|
2018-11-02 11:33:01 +01:00
|
|
|
wg.Add(1)
|
2016-10-27 00:05:10 +02:00
|
|
|
go func() {
|
2018-11-02 11:33:01 +01:00
|
|
|
defer wg.Done()
|
|
|
|
|
2016-10-27 00:05:10 +02:00
|
|
|
for {
|
|
|
|
select {
|
2017-08-22 09:24:37 +02:00
|
|
|
case <-reqQuit:
|
|
|
|
return
|
2018-11-02 11:33:01 +01:00
|
|
|
|
2016-10-27 00:05:10 +02:00
|
|
|
default:
|
|
|
|
// Receive the next pending payment within the
|
|
|
|
// stream sent by the client. If we read the
|
|
|
|
// EOF sentinel, then the client has closed the
|
|
|
|
// stream, and we can exit normally.
|
2018-05-01 10:17:55 +02:00
|
|
|
nextPayment, err := stream.recv()
|
2016-10-27 00:05:10 +02:00
|
|
|
if err == io.EOF {
|
2018-11-02 11:33:01 +01:00
|
|
|
close(payChan)
|
2016-10-27 00:05:10 +02:00
|
|
|
return
|
|
|
|
} else if err != nil {
|
2018-11-02 11:33:01 +01:00
|
|
|
rpcsLog.Errorf("Failed receiving from "+
|
|
|
|
"stream: %v", err)
|
|
|
|
|
2017-08-22 09:24:37 +02:00
|
|
|
select {
|
|
|
|
case errChan <- err:
|
2018-11-02 11:33:01 +01:00
|
|
|
default:
|
2017-08-22 09:24:37 +02:00
|
|
|
}
|
2016-10-27 00:05:10 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-09-05 18:08:02 +02:00
|
|
|
// Populate the next payment, either from the
|
|
|
|
// payment request, or from the explicitly set
|
2018-06-07 05:40:28 +02:00
|
|
|
// fields. If the payment proto wasn't well
|
|
|
|
// formed, then we'll send an error reply and
|
|
|
|
// wait for the next payment.
|
2019-11-25 14:13:21 +01:00
|
|
|
payIntent, err := r.extractPaymentIntent(
|
|
|
|
nextPayment,
|
|
|
|
)
|
2018-06-07 05:40:28 +02:00
|
|
|
if err != nil {
|
|
|
|
if err := stream.send(&lnrpc.SendResponse{
|
|
|
|
PaymentError: err.Error(),
|
2018-12-05 07:57:44 +01:00
|
|
|
PaymentHash: payIntent.rHash[:],
|
2018-06-07 05:40:28 +02:00
|
|
|
}); err != nil {
|
2018-11-02 11:33:01 +01:00
|
|
|
rpcsLog.Errorf("Failed "+
|
|
|
|
"sending on "+
|
|
|
|
"stream: %v", err)
|
|
|
|
|
2018-06-07 05:40:28 +02:00
|
|
|
select {
|
|
|
|
case errChan <- err:
|
2018-11-02 11:33:01 +01:00
|
|
|
default:
|
2017-09-05 18:08:02 +02:00
|
|
|
}
|
2018-11-02 11:33:01 +01:00
|
|
|
return
|
2017-09-05 18:08:02 +02:00
|
|
|
}
|
2018-06-07 05:40:28 +02:00
|
|
|
continue
|
2017-01-03 00:36:15 +01:00
|
|
|
}
|
|
|
|
|
2018-06-07 05:40:28 +02:00
|
|
|
// If the payment was well formed, then we'll
|
|
|
|
// send to the dispatch goroutine, or exit,
|
2018-11-02 11:33:01 +01:00
|
|
|
// which ever comes first.
|
2017-08-22 09:24:37 +02:00
|
|
|
select {
|
2018-06-07 05:40:28 +02:00
|
|
|
case payChan <- &payIntent:
|
2017-08-22 09:24:37 +02:00
|
|
|
case <-reqQuit:
|
|
|
|
return
|
|
|
|
}
|
2016-10-27 00:05:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
2016-09-21 02:15:26 +02:00
|
|
|
|
2018-11-02 11:33:01 +01:00
|
|
|
sendLoop:
|
2016-07-13 02:46:25 +02:00
|
|
|
for {
|
2016-07-22 01:22:30 +02:00
|
|
|
select {
|
2018-11-02 11:33:01 +01:00
|
|
|
|
|
|
|
// If we encounter and error either during sending or
|
|
|
|
// receiving, we return directly, closing the stream.
|
2016-07-22 01:22:30 +02:00
|
|
|
case err := <-errChan:
|
2016-07-13 02:46:25 +02:00
|
|
|
return err
|
2016-12-27 06:51:18 +01:00
|
|
|
|
2018-11-02 11:33:01 +01:00
|
|
|
case <-r.quit:
|
|
|
|
return errors.New("rpc server shutting down")
|
|
|
|
|
2018-11-02 11:33:01 +01:00
|
|
|
case payIntent, ok := <-payChan:
|
|
|
|
// If the receive loop is done, we break the send loop
|
|
|
|
// and wait for the ongoing payments to finish before
|
|
|
|
// exiting.
|
|
|
|
if !ok {
|
|
|
|
break sendLoop
|
|
|
|
}
|
|
|
|
|
2016-11-11 02:37:21 +01:00
|
|
|
// We launch a new goroutine to execute the current
|
|
|
|
// payment so we can continue to serve requests while
|
2016-12-27 06:51:18 +01:00
|
|
|
// this payment is being dispatched.
|
2018-11-02 11:33:01 +01:00
|
|
|
wg.Add(1)
|
2022-11-21 13:06:24 +01:00
|
|
|
go func(payIntent *rpcPaymentIntent) {
|
2018-11-02 11:33:01 +01:00
|
|
|
defer wg.Done()
|
|
|
|
|
2017-04-12 06:24:16 +02:00
|
|
|
// Attempt to grab a free semaphore slot, using
|
|
|
|
// a defer to eventually release the slot
|
|
|
|
// regardless of payment success.
|
2018-11-02 11:33:01 +01:00
|
|
|
select {
|
|
|
|
case <-htlcSema:
|
|
|
|
case <-reqQuit:
|
|
|
|
return
|
|
|
|
}
|
2017-04-12 06:24:16 +02:00
|
|
|
defer func() {
|
|
|
|
htlcSema <- struct{}{}
|
|
|
|
}()
|
|
|
|
|
2018-07-31 10:29:12 +02:00
|
|
|
resp, saveErr := r.dispatchPaymentIntent(
|
2018-06-07 05:40:28 +02:00
|
|
|
payIntent,
|
2018-05-01 10:17:55 +02:00
|
|
|
)
|
|
|
|
|
2018-06-07 05:40:28 +02:00
|
|
|
switch {
|
2018-07-31 10:29:12 +02:00
|
|
|
// If we were unable to save the state of the
|
|
|
|
// payment, then we'll return the error to the
|
|
|
|
// user, and terminate.
|
|
|
|
case saveErr != nil:
|
2018-11-02 11:33:01 +01:00
|
|
|
rpcsLog.Errorf("Failed dispatching "+
|
|
|
|
"payment intent: %v", saveErr)
|
|
|
|
|
|
|
|
select {
|
|
|
|
case errChan <- saveErr:
|
|
|
|
default:
|
|
|
|
}
|
2018-07-31 10:29:12 +02:00
|
|
|
return
|
|
|
|
|
2018-06-07 05:40:28 +02:00
|
|
|
// If we receive payment error than, instead of
|
|
|
|
// terminating the stream, send error response
|
|
|
|
// to the user.
|
2018-07-31 10:29:12 +02:00
|
|
|
case resp.Err != nil:
|
2018-05-01 10:17:55 +02:00
|
|
|
err := stream.send(&lnrpc.SendResponse{
|
2018-07-31 10:29:12 +02:00
|
|
|
PaymentError: resp.Err.Error(),
|
2018-12-05 07:57:44 +01:00
|
|
|
PaymentHash: payIntent.rHash[:],
|
2017-05-19 14:18:21 +02:00
|
|
|
})
|
|
|
|
if err != nil {
|
2018-11-02 11:33:01 +01:00
|
|
|
rpcsLog.Errorf("Failed "+
|
|
|
|
"sending error "+
|
|
|
|
"response: %v", err)
|
|
|
|
|
|
|
|
select {
|
|
|
|
case errChan <- err:
|
|
|
|
default:
|
|
|
|
}
|
2017-05-19 14:18:21 +02:00
|
|
|
}
|
2016-07-22 01:22:30 +02:00
|
|
|
return
|
2016-12-27 06:51:18 +01:00
|
|
|
}
|
2016-12-21 10:19:01 +01:00
|
|
|
|
2019-07-31 06:44:02 +02:00
|
|
|
backend := r.routerBackend
|
|
|
|
marshalledRouted, err := backend.MarshallRoute(
|
|
|
|
resp.Route,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
errChan <- err
|
|
|
|
return
|
|
|
|
}
|
2019-03-14 14:19:35 +01:00
|
|
|
|
2019-07-31 06:44:02 +02:00
|
|
|
err = stream.send(&lnrpc.SendResponse{
|
2018-12-05 07:57:44 +01:00
|
|
|
PaymentHash: payIntent.rHash[:],
|
2018-07-31 10:29:12 +02:00
|
|
|
PaymentPreimage: resp.Preimage[:],
|
|
|
|
PaymentRoute: marshalledRouted,
|
2017-02-02 03:29:46 +01:00
|
|
|
})
|
|
|
|
if err != nil {
|
2018-11-02 11:33:01 +01:00
|
|
|
rpcsLog.Errorf("Failed sending "+
|
|
|
|
"response: %v", err)
|
|
|
|
|
|
|
|
select {
|
|
|
|
case errChan <- err:
|
|
|
|
default:
|
|
|
|
}
|
2016-07-22 01:22:30 +02:00
|
|
|
return
|
|
|
|
}
|
2022-11-21 13:06:24 +01:00
|
|
|
}(payIntent)
|
2016-07-13 02:46:25 +02:00
|
|
|
}
|
|
|
|
}
|
2018-11-02 11:33:01 +01:00
|
|
|
|
|
|
|
// Wait for all goroutines to finish before closing the stream.
|
|
|
|
wg.Wait()
|
|
|
|
return nil
|
2016-07-13 02:46:25 +02:00
|
|
|
}
|
2016-07-15 13:02:59 +02:00
|
|
|
|
2016-11-11 02:37:21 +01:00
|
|
|
// SendPaymentSync is the synchronous non-streaming version of SendPayment.
|
|
|
|
// This RPC is intended to be consumed by clients of the REST proxy.
|
|
|
|
// Additionally, this RPC expects the destination's public key and the payment
|
|
|
|
// hash (if any) to be encoded as hex strings.
|
|
|
|
func (r *rpcServer) SendPaymentSync(ctx context.Context,
|
|
|
|
nextPayment *lnrpc.SendRequest) (*lnrpc.SendResponse, error) {
|
2017-08-22 09:28:40 +02:00
|
|
|
|
2024-06-06 15:15:51 +02:00
|
|
|
return r.sendPaymentSync(&rpcPaymentRequest{
|
2018-05-01 10:17:55 +02:00
|
|
|
SendRequest: nextPayment,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// SendToRouteSync is the synchronous non-streaming version of SendToRoute.
|
|
|
|
// This RPC is intended to be consumed by clients of the REST proxy.
|
2018-06-07 05:40:28 +02:00
|
|
|
// Additionally, this RPC expects the payment hash (if any) to be encoded as
|
|
|
|
// hex strings.
|
2018-05-01 10:17:55 +02:00
|
|
|
func (r *rpcServer) SendToRouteSync(ctx context.Context,
|
|
|
|
req *lnrpc.SendToRouteRequest) (*lnrpc.SendResponse, error) {
|
|
|
|
|
2018-08-08 11:09:30 +02:00
|
|
|
if req.Route == nil {
|
2018-05-01 10:17:55 +02:00
|
|
|
return nil, fmt.Errorf("unable to send, no routes provided")
|
|
|
|
}
|
|
|
|
|
2019-03-14 15:13:45 +01:00
|
|
|
paymentRequest, err := r.unmarshallSendToRouteRequest(req)
|
2019-01-21 12:50:36 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2018-05-01 10:17:55 +02:00
|
|
|
}
|
|
|
|
|
2024-06-06 15:15:51 +02:00
|
|
|
return r.sendPaymentSync(paymentRequest)
|
2018-05-01 10:17:55 +02:00
|
|
|
}
|
|
|
|
|
2018-06-07 05:40:28 +02:00
|
|
|
// sendPaymentSync is the synchronous variant of sendPayment. It will block and
|
|
|
|
// wait until the payment has been fully completed.
|
2024-06-06 15:15:51 +02:00
|
|
|
func (r *rpcServer) sendPaymentSync(
|
2018-06-07 05:40:28 +02:00
|
|
|
nextPayment *rpcPaymentRequest) (*lnrpc.SendResponse, error) {
|
2018-05-01 10:17:55 +02:00
|
|
|
|
2017-08-03 05:59:43 +02:00
|
|
|
// We don't allow payments to be sent while the daemon itself is still
|
|
|
|
// syncing as we may be trying to sent a payment over a "stale"
|
|
|
|
// channel.
|
|
|
|
if !r.server.Started() {
|
2019-10-23 23:35:41 +02:00
|
|
|
return nil, ErrServerNotActive
|
2017-08-03 05:59:43 +02:00
|
|
|
}
|
|
|
|
|
2018-06-07 05:40:28 +02:00
|
|
|
// First we'll attempt to map the proto describing the next payment to
|
|
|
|
// an intent that we can pass to local sub-systems.
|
2019-11-25 14:13:21 +01:00
|
|
|
payIntent, err := r.extractPaymentIntent(nextPayment)
|
2018-06-07 05:40:28 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2017-10-23 03:31:57 +02:00
|
|
|
}
|
2018-05-01 10:17:55 +02:00
|
|
|
|
2018-06-07 05:40:28 +02:00
|
|
|
// With the payment validated, we'll now attempt to dispatch the
|
|
|
|
// payment.
|
2018-07-31 10:29:12 +02:00
|
|
|
resp, saveErr := r.dispatchPaymentIntent(&payIntent)
|
2018-06-07 05:40:28 +02:00
|
|
|
switch {
|
2018-07-31 10:29:12 +02:00
|
|
|
case saveErr != nil:
|
|
|
|
return nil, saveErr
|
|
|
|
|
|
|
|
case resp.Err != nil:
|
2017-10-26 00:32:00 +02:00
|
|
|
return &lnrpc.SendResponse{
|
2018-07-31 10:29:12 +02:00
|
|
|
PaymentError: resp.Err.Error(),
|
2018-12-05 07:57:44 +01:00
|
|
|
PaymentHash: payIntent.rHash[:],
|
2017-10-26 00:32:00 +02:00
|
|
|
}, nil
|
2016-12-27 06:51:18 +01:00
|
|
|
}
|
|
|
|
|
2019-07-31 06:44:02 +02:00
|
|
|
rpcRoute, err := r.routerBackend.MarshallRoute(resp.Route)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-02-21 01:33:45 +01:00
|
|
|
return &lnrpc.SendResponse{
|
2018-12-05 07:57:44 +01:00
|
|
|
PaymentHash: payIntent.rHash[:],
|
2018-07-31 10:29:12 +02:00
|
|
|
PaymentPreimage: resp.Preimage[:],
|
2019-07-31 06:44:02 +02:00
|
|
|
PaymentRoute: rpcRoute,
|
2017-02-21 01:33:45 +01:00
|
|
|
}, nil
|
2016-11-11 02:37:21 +01:00
|
|
|
}
|
|
|
|
|
2016-09-19 21:04:56 +02:00
|
|
|
// AddInvoice attempts to add a new invoice to the invoice database. Any
|
|
|
|
// duplicated invoices are rejected, therefore all invoices *must* have a
|
|
|
|
// unique payment preimage.
|
|
|
|
func (r *rpcServer) AddInvoice(ctx context.Context,
|
|
|
|
invoice *lnrpc.Invoice) (*lnrpc.AddInvoiceResponse, error) {
|
2017-08-22 08:25:41 +02:00
|
|
|
|
2020-05-14 14:07:26 +02:00
|
|
|
defaultDelta := r.cfg.Bitcoin.TimeLockDelta
|
2017-09-05 18:08:02 +02:00
|
|
|
|
2019-01-14 17:56:59 +01:00
|
|
|
addInvoiceCfg := &invoicesrpc.AddInvoiceConfig{
|
|
|
|
AddInvoice: r.server.invoices.AddInvoice,
|
|
|
|
IsChannelActive: r.server.htlcSwitch.HasActiveLink,
|
2020-08-04 20:56:31 +02:00
|
|
|
ChainParams: r.cfg.ActiveNetParams.Params,
|
2019-01-14 17:56:59 +01:00
|
|
|
NodeSigner: r.server.nodeSigner,
|
|
|
|
DefaultCLTVExpiry: defaultDelta,
|
2021-08-03 09:57:26 +02:00
|
|
|
ChanDB: r.server.chanStateDB,
|
|
|
|
Graph: r.server.graphDB,
|
2019-12-10 22:09:52 +01:00
|
|
|
GenInvoiceFeatures: func() *lnwire.FeatureVector {
|
|
|
|
return r.server.featureMgr.Get(feature.SetInvoice)
|
|
|
|
},
|
2021-05-06 18:15:01 +02:00
|
|
|
GenAmpInvoiceFeatures: func() *lnwire.FeatureVector {
|
|
|
|
return r.server.featureMgr.Get(feature.SetInvoiceAmp)
|
|
|
|
},
|
2022-04-04 22:49:14 +02:00
|
|
|
GetAlias: r.server.aliasMgr.GetPeerAlias,
|
2017-09-05 18:08:02 +02:00
|
|
|
}
|
|
|
|
|
2019-11-15 08:59:14 +01:00
|
|
|
value, err := lnrpc.UnmarshallAmt(invoice.Value, invoice.ValueMsat)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-11-03 14:00:27 +01:00
|
|
|
// Convert the passed routing hints to the required format.
|
|
|
|
routeHints, err := invoicesrpc.CreateZpay32HopHints(invoice.RouteHints)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-01-15 10:06:48 +01:00
|
|
|
addInvoiceData := &invoicesrpc.AddInvoiceData{
|
|
|
|
Memo: invoice.Memo,
|
2019-11-15 08:59:14 +01:00
|
|
|
Value: value,
|
2019-01-15 10:06:48 +01:00
|
|
|
DescriptionHash: invoice.DescriptionHash,
|
|
|
|
Expiry: invoice.Expiry,
|
|
|
|
FallbackAddr: invoice.FallbackAddr,
|
|
|
|
CltvExpiry: invoice.CltvExpiry,
|
|
|
|
Private: invoice.Private,
|
2019-11-03 14:00:27 +01:00
|
|
|
RouteHints: routeHints,
|
2021-05-06 18:15:33 +02:00
|
|
|
Amp: invoice.IsAmp,
|
2019-01-15 10:06:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if invoice.RPreimage != nil {
|
|
|
|
preimage, err := lntypes.MakePreimage(invoice.RPreimage)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
addInvoiceData.Preimage = &preimage
|
|
|
|
}
|
|
|
|
|
|
|
|
hash, dbInvoice, err := invoicesrpc.AddInvoice(
|
|
|
|
ctx, addInvoiceCfg, addInvoiceData,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &lnrpc.AddInvoiceResponse{
|
|
|
|
AddIndex: dbInvoice.AddIndex,
|
|
|
|
PaymentRequest: string(dbInvoice.PaymentRequest),
|
|
|
|
RHash: hash[:],
|
2020-11-25 04:05:14 +01:00
|
|
|
PaymentAddr: dbInvoice.Terms.PaymentAddr[:],
|
2019-01-15 10:06:48 +01:00
|
|
|
}, nil
|
2016-09-19 21:04:56 +02:00
|
|
|
}
|
|
|
|
|
2018-02-07 04:11:11 +01:00
|
|
|
// LookupInvoice attempts to look up an invoice according to its payment hash.
|
2016-09-19 21:04:56 +02:00
|
|
|
// The passed payment hash *must* be exactly 32 bytes, if not an error is
|
|
|
|
// returned.
|
|
|
|
func (r *rpcServer) LookupInvoice(ctx context.Context,
|
|
|
|
req *lnrpc.PaymentHash) (*lnrpc.Invoice, error) {
|
2017-08-22 08:25:41 +02:00
|
|
|
|
2016-10-15 23:41:11 +02:00
|
|
|
var (
|
|
|
|
payHash [32]byte
|
|
|
|
rHash []byte
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
// If the RHash as a raw string was provided, then decode that and use
|
|
|
|
// that directly. Otherwise, we use the raw bytes provided.
|
|
|
|
if req.RHashStr != "" {
|
|
|
|
rHash, err = hex.DecodeString(req.RHashStr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
rHash = req.RHash
|
2016-09-19 21:04:56 +02:00
|
|
|
}
|
|
|
|
|
2016-10-15 23:41:11 +02:00
|
|
|
// Ensure that the payment hash is *exactly* 32-bytes.
|
|
|
|
if len(rHash) != 0 && len(rHash) != 32 {
|
|
|
|
return nil, fmt.Errorf("payment hash must be exactly "+
|
|
|
|
"32 bytes, is instead %v", len(rHash))
|
|
|
|
}
|
|
|
|
copy(payHash[:], rHash)
|
2016-09-19 21:04:56 +02:00
|
|
|
|
2016-09-26 19:29:18 +02:00
|
|
|
rpcsLog.Tracef("[lookupinvoice] searching for invoice %x", payHash[:])
|
|
|
|
|
2023-10-11 13:42:59 +02:00
|
|
|
invoice, err := r.server.invoices.LookupInvoice(ctx, payHash)
|
2021-09-20 10:34:26 +02:00
|
|
|
switch {
|
2022-11-30 12:00:37 +01:00
|
|
|
case errors.Is(err, invoices.ErrInvoiceNotFound):
|
2021-09-20 10:34:26 +02:00
|
|
|
return nil, status.Error(codes.NotFound, err.Error())
|
|
|
|
case err != nil:
|
2016-09-19 21:04:56 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-09-26 19:29:18 +02:00
|
|
|
rpcsLog.Tracef("[lookupinvoice] located invoice %v",
|
2024-07-25 16:18:00 +02:00
|
|
|
lnutils.SpewLogClosure(invoice))
|
2016-09-26 19:29:18 +02:00
|
|
|
|
2019-01-03 19:13:08 +01:00
|
|
|
rpcInvoice, err := invoicesrpc.CreateRPCInvoice(
|
2020-08-04 20:56:31 +02:00
|
|
|
&invoice, r.cfg.ActiveNetParams.Params,
|
2019-01-03 19:13:08 +01:00
|
|
|
)
|
2017-09-05 18:08:02 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return rpcInvoice, nil
|
2016-09-19 21:04:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// ListInvoices returns a list of all the invoices currently stored within the
|
|
|
|
// database. Any active debug invoices are ignored.
|
|
|
|
func (r *rpcServer) ListInvoices(ctx context.Context,
|
|
|
|
req *lnrpc.ListInvoiceRequest) (*lnrpc.ListInvoiceResponse, error) {
|
2017-08-22 08:25:41 +02:00
|
|
|
|
2018-08-11 05:24:43 +02:00
|
|
|
// If the number of invoices was not specified, then we'll default to
|
|
|
|
// returning the latest 100 invoices.
|
|
|
|
if req.NumMaxInvoices == 0 {
|
|
|
|
req.NumMaxInvoices = 100
|
2016-09-19 21:04:56 +02:00
|
|
|
}
|
|
|
|
|
2022-11-15 19:03:16 +01:00
|
|
|
// If both dates are set, we check that the start date is less than the
|
|
|
|
// end date, otherwise we'll get an empty result.
|
|
|
|
if req.CreationDateStart != 0 && req.CreationDateEnd != 0 {
|
|
|
|
if req.CreationDateStart >= req.CreationDateEnd {
|
|
|
|
return nil, fmt.Errorf("start date(%v) must be before "+
|
|
|
|
"end date(%v)", req.CreationDateStart,
|
|
|
|
req.CreationDateEnd)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-11 05:24:43 +02:00
|
|
|
// Next, we'll map the proto request into a format that is understood by
|
|
|
|
// the database.
|
2022-11-30 12:00:37 +01:00
|
|
|
q := invoices.InvoiceQuery{
|
2024-01-26 04:11:37 +01:00
|
|
|
IndexOffset: req.IndexOffset,
|
|
|
|
NumMaxInvoices: req.NumMaxInvoices,
|
|
|
|
PendingOnly: req.PendingOnly,
|
|
|
|
Reversed: req.Reversed,
|
|
|
|
CreationDateStart: int64(req.CreationDateStart),
|
|
|
|
CreationDateEnd: int64(req.CreationDateEnd),
|
2022-11-15 19:03:16 +01:00
|
|
|
}
|
|
|
|
|
2023-12-07 21:25:19 +01:00
|
|
|
invoiceSlice, err := r.server.invoicesDB.QueryInvoices(ctx, q)
|
2018-08-11 05:24:43 +02:00
|
|
|
if err != nil {
|
2024-02-26 12:19:38 +01:00
|
|
|
return nil, fmt.Errorf("unable to query invoices: %w", err)
|
2018-08-11 05:24:43 +02:00
|
|
|
}
|
2017-04-24 04:20:20 +02:00
|
|
|
|
2018-08-11 05:24:43 +02:00
|
|
|
// Before returning the response, we'll need to convert each invoice
|
|
|
|
// into it's proto representation.
|
|
|
|
resp := &lnrpc.ListInvoiceResponse{
|
2018-09-11 03:21:13 +02:00
|
|
|
Invoices: make([]*lnrpc.Invoice, len(invoiceSlice.Invoices)),
|
|
|
|
FirstIndexOffset: invoiceSlice.FirstIndexOffset,
|
|
|
|
LastIndexOffset: invoiceSlice.LastIndexOffset,
|
2018-08-11 05:24:43 +02:00
|
|
|
}
|
|
|
|
for i, invoice := range invoiceSlice.Invoices {
|
2020-08-04 20:56:31 +02:00
|
|
|
invoice := invoice
|
2019-01-03 19:13:08 +01:00
|
|
|
resp.Invoices[i], err = invoicesrpc.CreateRPCInvoice(
|
2020-08-04 20:56:31 +02:00
|
|
|
&invoice, r.cfg.ActiveNetParams.Params,
|
2019-01-03 19:13:08 +01:00
|
|
|
)
|
2017-09-05 18:08:02 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2016-09-19 21:04:56 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-11 05:24:43 +02:00
|
|
|
return resp, nil
|
2016-09-19 21:04:56 +02:00
|
|
|
}
|
|
|
|
|
2017-12-30 15:44:31 +01:00
|
|
|
// SubscribeInvoices returns a uni-directional stream (server -> client) for
|
2016-10-15 23:41:11 +02:00
|
|
|
// notifying the client of newly added/settled invoices.
|
|
|
|
func (r *rpcServer) SubscribeInvoices(req *lnrpc.InvoiceSubscription,
|
|
|
|
updateStream lnrpc.Lightning_SubscribeInvoicesServer) error {
|
2017-08-22 08:25:41 +02:00
|
|
|
|
2020-05-20 05:31:38 +02:00
|
|
|
invoiceClient, err := r.server.invoices.SubscribeNotifications(
|
2023-10-11 13:42:59 +02:00
|
|
|
updateStream.Context(), req.AddIndex, req.SettleIndex,
|
2018-04-25 06:08:19 +02:00
|
|
|
)
|
2020-05-20 05:31:38 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-10-15 23:41:11 +02:00
|
|
|
defer invoiceClient.Cancel()
|
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
2018-04-25 06:08:19 +02:00
|
|
|
case newInvoice := <-invoiceClient.NewInvoices:
|
2019-01-03 19:13:08 +01:00
|
|
|
rpcInvoice, err := invoicesrpc.CreateRPCInvoice(
|
2020-08-04 20:56:31 +02:00
|
|
|
newInvoice, r.cfg.ActiveNetParams.Params,
|
2019-01-03 19:13:08 +01:00
|
|
|
)
|
2018-04-25 06:08:19 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-09-05 18:08:02 +02:00
|
|
|
|
2018-04-25 06:08:19 +02:00
|
|
|
if err := updateStream.Send(rpcInvoice); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
case settledInvoice := <-invoiceClient.SettledInvoices:
|
2019-01-03 19:13:08 +01:00
|
|
|
rpcInvoice, err := invoicesrpc.CreateRPCInvoice(
|
2020-08-04 20:56:31 +02:00
|
|
|
settledInvoice, r.cfg.ActiveNetParams.Params,
|
2019-01-03 19:13:08 +01:00
|
|
|
)
|
2017-09-05 18:08:02 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
2016-10-15 23:41:11 +02:00
|
|
|
}
|
2017-09-05 18:08:02 +02:00
|
|
|
|
|
|
|
if err := updateStream.Send(rpcInvoice); err != nil {
|
2016-10-15 23:41:11 +02:00
|
|
|
return err
|
|
|
|
}
|
2018-04-25 06:03:05 +02:00
|
|
|
|
2021-09-06 19:08:21 +02:00
|
|
|
// The response stream's context for whatever reason has been
|
|
|
|
// closed. If context is closed by an exceeded deadline we will
|
|
|
|
// return an error.
|
2020-07-18 22:41:11 +02:00
|
|
|
case <-updateStream.Context().Done():
|
2021-09-06 19:08:21 +02:00
|
|
|
if errors.Is(updateStream.Context().Err(), context.Canceled) {
|
|
|
|
return nil
|
|
|
|
}
|
2020-07-18 22:41:11 +02:00
|
|
|
return updateStream.Context().Err()
|
|
|
|
|
2016-10-15 23:41:11 +02:00
|
|
|
case <-r.quit:
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// SubscribeTransactions creates a uni-directional stream (server -> client) in
|
|
|
|
// which any newly discovered transactions relevant to the wallet are sent
|
|
|
|
// over.
|
|
|
|
func (r *rpcServer) SubscribeTransactions(req *lnrpc.GetTransactionsRequest,
|
|
|
|
updateStream lnrpc.Lightning_SubscribeTransactionsServer) error {
|
2017-08-22 08:25:41 +02:00
|
|
|
|
2020-10-06 17:03:42 +02:00
|
|
|
txClient, err := r.server.cc.Wallet.SubscribeTransactions()
|
2016-10-15 23:41:11 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer txClient.Cancel()
|
2021-06-05 23:05:54 +02:00
|
|
|
rpcsLog.Infof("New transaction subscription")
|
2016-10-15 23:41:11 +02:00
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case tx := <-txClient.ConfirmedTransactions():
|
2021-10-14 08:41:25 +02:00
|
|
|
detail := lnrpc.RPCTransaction(tx)
|
2016-10-15 23:41:11 +02:00
|
|
|
if err := updateStream.Send(detail); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-04-25 06:03:05 +02:00
|
|
|
|
2016-10-15 23:41:11 +02:00
|
|
|
case tx := <-txClient.UnconfirmedTransactions():
|
2021-10-14 08:41:25 +02:00
|
|
|
detail := lnrpc.RPCTransaction(tx)
|
2016-10-15 23:41:11 +02:00
|
|
|
if err := updateStream.Send(detail); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-04-25 06:03:05 +02:00
|
|
|
|
2021-09-06 19:08:21 +02:00
|
|
|
// The response stream's context for whatever reason has been
|
|
|
|
// closed. If context is closed by an exceeded deadline we will
|
|
|
|
// return an error.
|
2020-07-18 22:41:11 +02:00
|
|
|
case <-updateStream.Context().Done():
|
2021-09-06 19:08:21 +02:00
|
|
|
rpcsLog.Infof("Canceling transaction subscription")
|
|
|
|
if errors.Is(updateStream.Context().Err(), context.Canceled) {
|
|
|
|
return nil
|
|
|
|
}
|
2020-07-18 22:41:11 +02:00
|
|
|
return updateStream.Context().Err()
|
|
|
|
|
2016-10-15 23:41:11 +02:00
|
|
|
case <-r.quit:
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetTransactions returns a list of describing all the known transactions
|
|
|
|
// relevant to the wallet.
|
2017-08-18 03:50:57 +02:00
|
|
|
func (r *rpcServer) GetTransactions(ctx context.Context,
|
2020-05-05 21:10:06 +02:00
|
|
|
req *lnrpc.GetTransactionsRequest) (*lnrpc.TransactionDetails, error) {
|
2017-08-22 08:25:41 +02:00
|
|
|
|
2020-05-05 21:10:06 +02:00
|
|
|
// To remain backwards compatible with the old api, default to the
|
|
|
|
// special case end height which will return transactions from the start
|
|
|
|
// height until the chain tip, including unconfirmed transactions.
|
2020-05-20 09:12:53 +02:00
|
|
|
var endHeight = btcwallet.UnconfirmedHeight
|
2016-10-15 23:41:11 +02:00
|
|
|
|
2020-05-05 21:10:06 +02:00
|
|
|
// If the user has provided an end height, we overwrite our default.
|
|
|
|
if req.EndHeight != 0 {
|
|
|
|
endHeight = req.EndHeight
|
2016-10-15 23:41:11 +02:00
|
|
|
}
|
2018-09-15 11:07:27 +02:00
|
|
|
|
2020-10-06 17:03:42 +02:00
|
|
|
transactions, err := r.server.cc.Wallet.ListTransactionDetails(
|
2021-02-20 02:41:53 +01:00
|
|
|
req.StartHeight, endHeight, req.Account,
|
2020-05-05 21:10:06 +02:00
|
|
|
)
|
2016-10-15 23:41:11 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-05-05 21:34:38 +02:00
|
|
|
return lnrpc.RPCTransactionDetails(transactions), nil
|
2016-10-15 23:41:11 +02:00
|
|
|
}
|
|
|
|
|
2016-12-27 06:51:47 +01:00
|
|
|
// DescribeGraph returns a description of the latest graph state from the PoV
|
|
|
|
// of the node. The graph information is partitioned into two components: all
|
|
|
|
// the nodes/vertexes, and all the edges that connect the vertexes themselves.
|
|
|
|
// As this is a directed graph, the edges also contain the node directional
|
|
|
|
// specific routing policy which includes: the time lock delta, fee
|
|
|
|
// information, etc.
|
2017-08-18 03:50:57 +02:00
|
|
|
func (r *rpcServer) DescribeGraph(ctx context.Context,
|
2018-09-26 17:30:09 +02:00
|
|
|
req *lnrpc.ChannelGraphRequest) (*lnrpc.ChannelGraph, error) {
|
2017-08-22 08:25:41 +02:00
|
|
|
|
2016-12-27 06:51:47 +01:00
|
|
|
resp := &lnrpc.ChannelGraph{}
|
2018-09-26 17:30:09 +02:00
|
|
|
includeUnannounced := req.IncludeUnannounced
|
2016-09-21 02:15:26 +02:00
|
|
|
|
2021-10-20 01:19:53 +02:00
|
|
|
// Check to see if the cache is already populated, if so then we can
|
|
|
|
// just return it directly.
|
|
|
|
//
|
|
|
|
// TODO(roasbeef): move this to an interceptor level feature?
|
|
|
|
graphCacheActive := r.cfg.Caches.RPCGraphCacheDuration != 0
|
|
|
|
if graphCacheActive {
|
|
|
|
r.graphCache.Lock()
|
|
|
|
defer r.graphCache.Unlock()
|
|
|
|
|
|
|
|
if r.describeGraphResp != nil {
|
|
|
|
return r.describeGraphResp, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-14 22:17:51 +02:00
|
|
|
// Obtain the pointer to the global singleton channel graph, this will
|
2016-12-27 06:51:47 +01:00
|
|
|
// provide a consistent view of the graph due to bolt db's
|
|
|
|
// transactional model.
|
2021-08-03 09:57:26 +02:00
|
|
|
graph := r.server.graphDB
|
2016-12-27 06:51:47 +01:00
|
|
|
|
|
|
|
// First iterate through all the known nodes (connected or unconnected
|
|
|
|
// within the graph), collating their current state into the RPC
|
|
|
|
// response.
|
2020-05-07 00:45:50 +02:00
|
|
|
err := graph.ForEachNode(func(_ kvdb.RTx, node *channeldb.LightningNode) error {
|
2022-10-25 15:48:55 +02:00
|
|
|
lnNode := marshalNode(node)
|
2020-03-19 11:14:28 +01:00
|
|
|
|
|
|
|
resp.Nodes = append(resp.Nodes, lnNode)
|
2017-02-17 10:29:23 +01:00
|
|
|
|
2016-12-27 06:51:47 +01:00
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Next, for each active channel we know of within the graph, create a
|
|
|
|
// similar response which details both the edge information as well as
|
|
|
|
// the routing policies of th nodes connecting the two edges.
|
2023-11-08 10:18:45 +01:00
|
|
|
err = graph.ForEachChannel(func(edgeInfo *models.ChannelEdgeInfo,
|
|
|
|
c1, c2 *models.ChannelEdgePolicy) error {
|
2017-03-06 02:28:12 +01:00
|
|
|
|
2018-09-26 17:30:09 +02:00
|
|
|
// Do not include unannounced channels unless specifically
|
|
|
|
// requested. Unannounced channels include both private channels as
|
|
|
|
// well as public channels whose authentication proof were not
|
|
|
|
// confirmed yet, hence were not announced.
|
|
|
|
if !includeUnannounced && edgeInfo.AuthProof == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-11-08 10:40:20 +01:00
|
|
|
edge := marshalDBEdge(edgeInfo, c1, c2)
|
2016-12-27 06:51:47 +01:00
|
|
|
resp.Edges = append(resp.Edges, edge)
|
2018-09-26 17:30:09 +02:00
|
|
|
|
2016-12-27 06:51:47 +01:00
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil && err != channeldb.ErrGraphNoEdgesFound {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-10-20 01:19:53 +02:00
|
|
|
// We still have the mutex held, so we can safely populate the cache
|
|
|
|
// now to save on GC churn for this query, but only if the cache isn't
|
|
|
|
// disabled.
|
|
|
|
if graphCacheActive {
|
|
|
|
r.describeGraphResp = resp
|
|
|
|
}
|
|
|
|
|
2016-12-27 06:51:47 +01:00
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
2022-10-25 15:34:28 +02:00
|
|
|
// marshalExtraOpaqueData marshals the given tlv data. If the tlv stream is
|
|
|
|
// malformed or empty, an empty map is returned. This makes the method safe to
|
|
|
|
// use on unvalidated data.
|
|
|
|
func marshalExtraOpaqueData(data []byte) map[uint64][]byte {
|
|
|
|
r := bytes.NewReader(data)
|
|
|
|
|
|
|
|
tlvStream, err := tlv.NewStream()
|
|
|
|
if err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-12-02 20:51:53 +01:00
|
|
|
// Since ExtraOpaqueData is provided by a potentially malicious peer,
|
|
|
|
// pass it into the P2P decoding variant.
|
|
|
|
parsedTypes, err := tlvStream.DecodeWithParsedTypesP2P(r)
|
2022-10-25 15:34:28 +02:00
|
|
|
if err != nil || len(parsedTypes) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
records := make(map[uint64][]byte)
|
|
|
|
for k, v := range parsedTypes {
|
|
|
|
records[uint64(k)] = v
|
|
|
|
}
|
|
|
|
|
|
|
|
return records
|
|
|
|
}
|
|
|
|
|
2022-09-23 14:42:28 +02:00
|
|
|
// extractInboundFeeSafe tries to extract the inbound fee from the given extra
|
|
|
|
// opaque data tlv block. If parsing fails, a zero inbound fee is returned. This
|
|
|
|
// function is typically used on unvalidated data coming stored in the database.
|
|
|
|
// There is not much we can do other than ignoring errors here.
|
|
|
|
func extractInboundFeeSafe(data lnwire.ExtraOpaqueData) lnwire.Fee {
|
|
|
|
var inboundFee lnwire.Fee
|
|
|
|
|
|
|
|
_, err := data.ExtractRecords(&inboundFee)
|
|
|
|
if err != nil {
|
|
|
|
// Return zero fee. Do not return the inboundFee variable
|
|
|
|
// because it may be undefined.
|
|
|
|
return lnwire.Fee{}
|
|
|
|
}
|
|
|
|
|
|
|
|
return inboundFee
|
|
|
|
}
|
|
|
|
|
2023-11-08 10:40:20 +01:00
|
|
|
func marshalDBEdge(edgeInfo *models.ChannelEdgeInfo,
|
2023-11-08 10:18:45 +01:00
|
|
|
c1, c2 *models.ChannelEdgePolicy) *lnrpc.ChannelEdge {
|
2017-03-06 02:28:12 +01:00
|
|
|
|
2021-05-13 01:10:05 +02:00
|
|
|
// Make sure the policies match the node they belong to. c1 should point
|
|
|
|
// to the policy for NodeKey1, and c2 for NodeKey2.
|
2021-05-17 09:36:05 +02:00
|
|
|
if c1 != nil && c1.ChannelFlags&lnwire.ChanUpdateDirection == 1 ||
|
|
|
|
c2 != nil && c2.ChannelFlags&lnwire.ChanUpdateDirection == 0 {
|
2019-08-28 22:30:06 +02:00
|
|
|
|
|
|
|
c2, c1 = c1, c2
|
|
|
|
}
|
|
|
|
|
2019-08-28 22:31:12 +02:00
|
|
|
var lastUpdate int64
|
2017-01-17 22:20:06 +01:00
|
|
|
if c1 != nil {
|
|
|
|
lastUpdate = c1.LastUpdate.Unix()
|
|
|
|
}
|
2019-08-28 22:31:12 +02:00
|
|
|
if c2 != nil && c2.LastUpdate.Unix() > lastUpdate {
|
|
|
|
lastUpdate = c2.LastUpdate.Unix()
|
|
|
|
}
|
2016-12-27 06:51:47 +01:00
|
|
|
|
2022-10-25 15:34:28 +02:00
|
|
|
customRecords := marshalExtraOpaqueData(edgeInfo.ExtraOpaqueData)
|
|
|
|
|
2016-12-27 06:51:47 +01:00
|
|
|
edge := &lnrpc.ChannelEdge{
|
2017-03-06 02:28:12 +01:00
|
|
|
ChannelId: edgeInfo.ChannelID,
|
|
|
|
ChanPoint: edgeInfo.ChannelPoint.String(),
|
2017-01-17 22:20:06 +01:00
|
|
|
// TODO(roasbeef): update should be on edge info itself
|
2022-10-25 15:34:28 +02:00
|
|
|
LastUpdate: uint32(lastUpdate),
|
|
|
|
Node1Pub: hex.EncodeToString(edgeInfo.NodeKey1Bytes[:]),
|
|
|
|
Node2Pub: hex.EncodeToString(edgeInfo.NodeKey2Bytes[:]),
|
|
|
|
Capacity: int64(edgeInfo.Capacity),
|
|
|
|
CustomRecords: customRecords,
|
2016-12-27 06:51:47 +01:00
|
|
|
}
|
|
|
|
|
2017-01-17 22:20:06 +01:00
|
|
|
if c1 != nil {
|
2022-10-25 11:16:14 +02:00
|
|
|
edge.Node1Policy = marshalDBRoutingPolicy(c1)
|
2016-12-27 06:51:47 +01:00
|
|
|
}
|
|
|
|
|
2017-01-17 22:20:06 +01:00
|
|
|
if c2 != nil {
|
2022-10-25 11:16:14 +02:00
|
|
|
edge.Node2Policy = marshalDBRoutingPolicy(c2)
|
2016-12-27 06:51:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return edge
|
|
|
|
}
|
|
|
|
|
2022-10-25 11:16:14 +02:00
|
|
|
func marshalDBRoutingPolicy(
|
2023-11-08 10:18:45 +01:00
|
|
|
policy *models.ChannelEdgePolicy) *lnrpc.RoutingPolicy {
|
2022-10-25 11:16:14 +02:00
|
|
|
|
|
|
|
disabled := policy.ChannelFlags&lnwire.ChanUpdateDisabled != 0
|
|
|
|
|
2022-10-25 15:34:28 +02:00
|
|
|
customRecords := marshalExtraOpaqueData(policy.ExtraOpaqueData)
|
2022-09-23 14:42:28 +02:00
|
|
|
inboundFee := extractInboundFeeSafe(policy.ExtraOpaqueData)
|
2022-10-25 15:34:28 +02:00
|
|
|
|
2022-10-25 11:16:14 +02:00
|
|
|
return &lnrpc.RoutingPolicy{
|
|
|
|
TimeLockDelta: uint32(policy.TimeLockDelta),
|
|
|
|
MinHtlc: int64(policy.MinHTLC),
|
|
|
|
MaxHtlcMsat: uint64(policy.MaxHTLC),
|
|
|
|
FeeBaseMsat: int64(policy.FeeBaseMSat),
|
|
|
|
FeeRateMilliMsat: int64(policy.FeeProportionalMillionths),
|
|
|
|
Disabled: disabled,
|
|
|
|
LastUpdate: uint32(policy.LastUpdate.Unix()),
|
2022-10-25 15:34:28 +02:00
|
|
|
CustomRecords: customRecords,
|
2022-09-23 14:42:28 +02:00
|
|
|
|
|
|
|
InboundFeeBaseMsat: inboundFee.BaseFee,
|
|
|
|
InboundFeeRateMilliMsat: inboundFee.FeeRate,
|
2022-10-25 11:16:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-19 11:14:28 +01:00
|
|
|
// GetNodeMetrics returns all available node metrics calculated from the
|
|
|
|
// current channel graph.
|
|
|
|
func (r *rpcServer) GetNodeMetrics(ctx context.Context,
|
|
|
|
req *lnrpc.NodeMetricsRequest) (*lnrpc.NodeMetricsResponse, error) {
|
|
|
|
|
|
|
|
// Get requested metric types.
|
|
|
|
getCentrality := false
|
|
|
|
for _, t := range req.Types {
|
|
|
|
if t == lnrpc.NodeMetricType_BETWEENNESS_CENTRALITY {
|
|
|
|
getCentrality = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only centrality can be requested for now.
|
|
|
|
if !getCentrality {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
resp := &lnrpc.NodeMetricsResponse{
|
2020-03-28 16:14:26 +01:00
|
|
|
BetweennessCentrality: make(map[string]*lnrpc.FloatMetric),
|
2020-03-19 11:14:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Obtain the pointer to the global singleton channel graph, this will
|
|
|
|
// provide a consistent view of the graph due to bolt db's
|
|
|
|
// transactional model.
|
2021-08-03 09:57:26 +02:00
|
|
|
graph := r.server.graphDB
|
2020-03-19 11:14:28 +01:00
|
|
|
|
|
|
|
// Calculate betweenness centrality if requested. Note that depending on the
|
|
|
|
// graph size, this may take up to a few minutes.
|
|
|
|
channelGraph := autopilot.ChannelGraphFromDatabase(graph)
|
2020-03-24 17:28:55 +01:00
|
|
|
centralityMetric, err := autopilot.NewBetweennessCentralityMetric(
|
|
|
|
runtime.NumCPU(),
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-03-19 11:14:28 +01:00
|
|
|
if err := centralityMetric.Refresh(channelGraph); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fill normalized and non normalized centrality.
|
|
|
|
centrality := centralityMetric.GetMetric(true)
|
|
|
|
for nodeID, val := range centrality {
|
2020-03-28 16:14:26 +01:00
|
|
|
resp.BetweennessCentrality[hex.EncodeToString(nodeID[:])] =
|
|
|
|
&lnrpc.FloatMetric{
|
|
|
|
NormalizedValue: val,
|
|
|
|
}
|
2020-03-19 11:14:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
centrality = centralityMetric.GetMetric(false)
|
|
|
|
for nodeID, val := range centrality {
|
|
|
|
resp.BetweennessCentrality[hex.EncodeToString(nodeID[:])].Value = val
|
|
|
|
}
|
|
|
|
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
2017-08-04 14:57:48 +02:00
|
|
|
// GetChanInfo returns the latest authenticated network announcement for the
|
2024-06-06 13:29:54 +02:00
|
|
|
// given channel identified by either its channel ID or a channel outpoint. Both
|
|
|
|
// uniquely identify the location of transaction's funding output within the
|
|
|
|
// blockchain. The former is an 8-byte integer, while the latter is a string
|
|
|
|
// formatted as funding_txid:output_index.
|
|
|
|
func (r *rpcServer) GetChanInfo(_ context.Context,
|
2017-08-22 08:25:41 +02:00
|
|
|
in *lnrpc.ChanInfoRequest) (*lnrpc.ChannelEdge, error) {
|
|
|
|
|
2021-08-03 09:57:26 +02:00
|
|
|
graph := r.server.graphDB
|
2017-08-22 08:25:41 +02:00
|
|
|
|
2024-06-06 13:29:54 +02:00
|
|
|
var (
|
|
|
|
edgeInfo *models.ChannelEdgeInfo
|
|
|
|
edge1, edge2 *models.ChannelEdgePolicy
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case in.ChanId != 0:
|
|
|
|
edgeInfo, edge1, edge2, err = graph.FetchChannelEdgesByID(
|
|
|
|
in.ChanId,
|
|
|
|
)
|
|
|
|
|
|
|
|
case in.ChanPoint != "":
|
|
|
|
var chanPoint *wire.OutPoint
|
|
|
|
chanPoint, err = wire.NewOutPointFromString(in.ChanPoint)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
edgeInfo, edge1, edge2, err = graph.FetchChannelEdgesByOutpoint(
|
|
|
|
chanPoint,
|
|
|
|
)
|
|
|
|
|
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("specify either chan_id or chan_point")
|
|
|
|
}
|
2016-12-27 06:51:47 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert the database's edge format into the network/RPC edge format
|
|
|
|
// which couples the edge itself along with the directional node
|
|
|
|
// routing policies of each node involved within the channel.
|
2023-11-08 10:40:20 +01:00
|
|
|
channelEdge := marshalDBEdge(edgeInfo, edge1, edge2)
|
2016-12-27 06:51:47 +01:00
|
|
|
|
|
|
|
return channelEdge, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetNodeInfo returns the latest advertised and aggregate authenticated
|
|
|
|
// channel information for the specified node identified by its public key.
|
2017-08-22 08:25:41 +02:00
|
|
|
func (r *rpcServer) GetNodeInfo(ctx context.Context,
|
|
|
|
in *lnrpc.NodeInfoRequest) (*lnrpc.NodeInfo, error) {
|
|
|
|
|
2021-08-03 09:57:26 +02:00
|
|
|
graph := r.server.graphDB
|
2016-12-27 06:51:47 +01:00
|
|
|
|
|
|
|
// First, parse the hex-encoded public key into a full in-memory public
|
|
|
|
// key object we can work with for querying.
|
2019-12-18 12:43:18 +01:00
|
|
|
pubKey, err := route.NewVertexFromStr(in.PubKey)
|
2016-12-27 06:51:47 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// With the public key decoded, attempt to fetch the node corresponding
|
|
|
|
// to this public key. If the node cannot be found, then an error will
|
|
|
|
// be returned.
|
2024-06-15 02:29:26 +02:00
|
|
|
node, err := graph.FetchLightningNode(pubKey)
|
2021-01-14 13:59:56 +01:00
|
|
|
switch {
|
|
|
|
case err == channeldb.ErrGraphNodeNotFound:
|
|
|
|
return nil, status.Error(codes.NotFound, err.Error())
|
|
|
|
case err != nil:
|
2016-12-27 06:51:47 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// With the node obtained, we'll now iterate through all its out going
|
|
|
|
// edges to gather some basic statistics about its out going channels.
|
|
|
|
var (
|
2018-02-07 04:11:11 +01:00
|
|
|
numChannels uint32
|
|
|
|
totalCapacity btcutil.Amount
|
2018-12-10 16:37:08 +01:00
|
|
|
channels []*lnrpc.ChannelEdge
|
2016-12-27 06:51:47 +01:00
|
|
|
)
|
2018-12-10 16:37:08 +01:00
|
|
|
|
2024-06-15 02:34:53 +02:00
|
|
|
err = graph.ForEachNodeChannel(node.PubKeyBytes,
|
2023-11-08 10:18:45 +01:00
|
|
|
func(_ kvdb.RTx, edge *models.ChannelEdgeInfo,
|
|
|
|
c1, c2 *models.ChannelEdgePolicy) error {
|
2017-03-06 02:28:12 +01:00
|
|
|
|
2023-10-23 13:44:40 +02:00
|
|
|
numChannels++
|
|
|
|
totalCapacity += edge.Capacity
|
|
|
|
|
|
|
|
// Only populate the node's channels if the user
|
|
|
|
// requested them.
|
|
|
|
if in.IncludeChannels {
|
|
|
|
// Do not include unannounced channels - private
|
|
|
|
// channels or public channels whose
|
|
|
|
// authentication proof were not confirmed yet.
|
|
|
|
if edge.AuthProof == nil {
|
|
|
|
return nil
|
|
|
|
}
|
2018-12-10 16:37:08 +01:00
|
|
|
|
2023-10-23 13:44:40 +02:00
|
|
|
// Convert the database's edge format into the
|
|
|
|
// network/RPC edge format.
|
2023-11-08 10:40:20 +01:00
|
|
|
channelEdge := marshalDBEdge(edge, c1, c2)
|
2023-10-23 13:44:40 +02:00
|
|
|
channels = append(channels, channelEdge)
|
2019-06-17 20:08:44 +02:00
|
|
|
}
|
2018-12-10 16:37:08 +01:00
|
|
|
|
2023-10-23 13:44:40 +02:00
|
|
|
return nil
|
|
|
|
},
|
|
|
|
)
|
|
|
|
if err != nil {
|
2016-12-27 06:51:47 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
2016-09-21 02:15:26 +02:00
|
|
|
|
2022-10-25 15:48:55 +02:00
|
|
|
return &lnrpc.NodeInfo{
|
|
|
|
Node: marshalNode(node),
|
|
|
|
NumChannels: numChannels,
|
|
|
|
TotalCapacity: int64(totalCapacity),
|
|
|
|
Channels: channels,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func marshalNode(node *channeldb.LightningNode) *lnrpc.LightningNode {
|
|
|
|
nodeAddrs := make([]*lnrpc.NodeAddress, len(node.Addresses))
|
|
|
|
for i, addr := range node.Addresses {
|
2017-02-17 10:29:23 +01:00
|
|
|
nodeAddr := &lnrpc.NodeAddress{
|
|
|
|
Network: addr.Network(),
|
|
|
|
Addr: addr.String(),
|
|
|
|
}
|
2022-10-25 15:48:55 +02:00
|
|
|
nodeAddrs[i] = nodeAddr
|
2017-02-17 10:29:23 +01:00
|
|
|
}
|
2017-12-03 03:37:34 +01:00
|
|
|
|
2019-12-14 16:04:51 +01:00
|
|
|
features := invoicesrpc.CreateRPCFeatures(node.Features)
|
|
|
|
|
2022-10-25 15:34:28 +02:00
|
|
|
customRecords := marshalExtraOpaqueData(node.ExtraOpaqueData)
|
|
|
|
|
2022-10-25 15:48:55 +02:00
|
|
|
return &lnrpc.LightningNode{
|
2022-10-25 15:34:28 +02:00
|
|
|
LastUpdate: uint32(node.LastUpdate.Unix()),
|
|
|
|
PubKey: hex.EncodeToString(node.PubKeyBytes[:]),
|
|
|
|
Addresses: nodeAddrs,
|
|
|
|
Alias: node.Alias,
|
2024-06-17 01:30:01 +02:00
|
|
|
Color: graph.EncodeHexColor(node.Color),
|
2022-10-25 15:34:28 +02:00
|
|
|
Features: features,
|
|
|
|
CustomRecords: customRecords,
|
2022-10-25 15:48:55 +02:00
|
|
|
}
|
2016-12-27 06:51:47 +01:00
|
|
|
}
|
|
|
|
|
2017-03-21 03:05:37 +01:00
|
|
|
// QueryRoutes attempts to query the daemons' Channel Router for a possible
|
2016-12-27 06:51:47 +01:00
|
|
|
// route to a target destination capable of carrying a specific amount of
|
2022-01-13 17:29:43 +01:00
|
|
|
// satoshis within the route's flow. The returned route contains the full
|
2016-12-27 06:51:47 +01:00
|
|
|
// details required to craft and send an HTLC, also including the necessary
|
2018-02-07 04:11:11 +01:00
|
|
|
// information that should be present within the Sphinx packet encapsulated
|
2016-12-27 06:51:47 +01:00
|
|
|
// within the HTLC.
|
|
|
|
//
|
|
|
|
// TODO(roasbeef): should return a slice of routes in reality
|
2022-08-22 20:58:42 +02:00
|
|
|
// - create separate PR to send based on well formatted route
|
2017-08-18 03:50:57 +02:00
|
|
|
func (r *rpcServer) QueryRoutes(ctx context.Context,
|
2017-03-21 03:05:37 +01:00
|
|
|
in *lnrpc.QueryRoutesRequest) (*lnrpc.QueryRoutesResponse, error) {
|
2017-08-22 08:25:41 +02:00
|
|
|
|
2019-03-14 14:19:35 +01:00
|
|
|
return r.routerBackend.QueryRoutes(ctx, in)
|
2016-12-27 06:51:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetNetworkInfo returns some basic stats about the known channel graph from
|
|
|
|
// the PoV of the node.
|
2017-08-22 08:25:41 +02:00
|
|
|
func (r *rpcServer) GetNetworkInfo(ctx context.Context,
|
|
|
|
_ *lnrpc.NetworkInfoRequest) (*lnrpc.NetworkInfo, error) {
|
|
|
|
|
2021-08-03 09:57:26 +02:00
|
|
|
graph := r.server.graphDB
|
2016-12-27 06:51:47 +01:00
|
|
|
|
|
|
|
var (
|
|
|
|
numNodes uint32
|
|
|
|
numChannels uint32
|
|
|
|
maxChanOut uint32
|
|
|
|
totalNetworkCapacity btcutil.Amount
|
|
|
|
minChannelSize btcutil.Amount = math.MaxInt64
|
|
|
|
maxChannelSize btcutil.Amount
|
2019-03-27 15:04:15 +01:00
|
|
|
medianChanSize btcutil.Amount
|
2016-12-27 06:51:47 +01:00
|
|
|
)
|
|
|
|
|
2017-04-14 22:17:51 +02:00
|
|
|
// We'll use this map to de-duplicate channels during our traversal.
|
|
|
|
// This is needed since channels are directional, so there will be two
|
|
|
|
// edges for each channel within the graph.
|
|
|
|
seenChans := make(map[uint64]struct{})
|
2016-12-27 06:51:47 +01:00
|
|
|
|
2019-03-27 15:04:15 +01:00
|
|
|
// We also keep a list of all encountered capacities, in order to
|
|
|
|
// calculate the median channel size.
|
|
|
|
var allChans []btcutil.Amount
|
|
|
|
|
2017-04-14 22:17:51 +02:00
|
|
|
// We'll run through all the known nodes in the within our view of the
|
2016-12-27 06:51:47 +01:00
|
|
|
// network, tallying up the total number of nodes, and also gathering
|
2017-04-14 22:17:51 +02:00
|
|
|
// each node so we can measure the graph diameter and degree stats
|
2016-12-27 06:51:47 +01:00
|
|
|
// below.
|
2021-10-20 01:04:51 +02:00
|
|
|
err := graph.ForEachNodeCached(func(node route.Vertex,
|
|
|
|
edges map[uint64]*channeldb.DirectedChannel) error {
|
|
|
|
|
2017-04-14 22:17:51 +02:00
|
|
|
// Increment the total number of nodes with each iteration.
|
2016-12-27 06:51:47 +01:00
|
|
|
numNodes++
|
|
|
|
|
2017-04-14 22:17:51 +02:00
|
|
|
// For each channel we'll compute the out degree of each node,
|
|
|
|
// and also update our running tallies of the min/max channel
|
|
|
|
// capacity, as well as the total channel capacity. We pass
|
|
|
|
// through the db transaction from the outer view so we can
|
|
|
|
// re-use it within this inner view.
|
2016-12-27 06:51:47 +01:00
|
|
|
var outDegree uint32
|
2021-10-20 01:04:51 +02:00
|
|
|
for _, edge := range edges {
|
2017-04-14 22:17:51 +02:00
|
|
|
// Bump up the out degree for this node for each
|
|
|
|
// channel encountered.
|
2016-12-27 06:51:47 +01:00
|
|
|
outDegree++
|
|
|
|
|
2017-04-14 22:17:51 +02:00
|
|
|
// If we've already seen this channel, then we'll
|
|
|
|
// return early to ensure that we don't double-count
|
|
|
|
// stats.
|
|
|
|
if _, ok := seenChans[edge.ChannelID]; ok {
|
|
|
|
return nil
|
|
|
|
}
|
2016-12-27 06:51:47 +01:00
|
|
|
|
2017-04-14 22:17:51 +02:00
|
|
|
// Compare the capacity of this channel against the
|
|
|
|
// running min/max to see if we should update the
|
|
|
|
// extrema.
|
|
|
|
chanCapacity := edge.Capacity
|
|
|
|
if chanCapacity < minChannelSize {
|
|
|
|
minChannelSize = chanCapacity
|
|
|
|
}
|
|
|
|
if chanCapacity > maxChannelSize {
|
|
|
|
maxChannelSize = chanCapacity
|
|
|
|
}
|
2017-03-06 02:28:12 +01:00
|
|
|
|
2017-04-14 22:17:51 +02:00
|
|
|
// Accumulate the total capacity of this channel to the
|
|
|
|
// network wide-capacity.
|
|
|
|
totalNetworkCapacity += chanCapacity
|
2016-12-27 06:51:47 +01:00
|
|
|
|
2017-04-14 22:17:51 +02:00
|
|
|
numChannels++
|
2016-12-27 06:51:47 +01:00
|
|
|
|
2017-04-14 22:17:51 +02:00
|
|
|
seenChans[edge.ChannelID] = struct{}{}
|
2019-03-27 15:04:15 +01:00
|
|
|
allChans = append(allChans, edge.Capacity)
|
2017-04-14 22:17:51 +02:00
|
|
|
}
|
2016-12-27 06:51:47 +01:00
|
|
|
|
2017-04-14 22:17:51 +02:00
|
|
|
// Finally, if the out degree of this node is greater than what
|
|
|
|
// we've seen so far, update the maxChanOut variable.
|
|
|
|
if outDegree > maxChanOut {
|
|
|
|
maxChanOut = outDegree
|
|
|
|
}
|
2016-12-27 06:51:47 +01:00
|
|
|
|
|
|
|
return nil
|
2021-10-20 01:04:51 +02:00
|
|
|
})
|
|
|
|
if err != nil {
|
2016-12-27 06:51:47 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-07-16 01:59:31 +02:00
|
|
|
// Query the graph for the current number of zombie channels.
|
|
|
|
numZombies, err := graph.NumZombies()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-03-27 15:04:15 +01:00
|
|
|
// Find the median.
|
|
|
|
medianChanSize = autopilot.Median(allChans)
|
|
|
|
|
2017-05-25 02:28:35 +02:00
|
|
|
// If we don't have any channels, then reset the minChannelSize to zero
|
2017-12-03 03:37:34 +01:00
|
|
|
// to avoid outputting NaN in encoded JSON.
|
2017-05-25 02:28:35 +02:00
|
|
|
if numChannels == 0 {
|
|
|
|
minChannelSize = 0
|
|
|
|
}
|
|
|
|
|
2021-12-22 11:03:03 +01:00
|
|
|
// Graph diameter.
|
2021-12-04 12:28:07 +01:00
|
|
|
channelGraph := autopilot.ChannelGraphFromCachedDatabase(graph)
|
2021-12-22 11:03:03 +01:00
|
|
|
simpleGraph, err := autopilot.NewSimpleGraph(channelGraph)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
start := time.Now()
|
|
|
|
diameter := simpleGraph.DiameterRadialCutoff()
|
|
|
|
rpcsLog.Infof("elapsed time for diameter (%d) calculation: %v", diameter,
|
|
|
|
time.Since(start))
|
2017-01-30 00:02:57 +01:00
|
|
|
|
2016-12-27 06:51:47 +01:00
|
|
|
// TODO(roasbeef): also add oldest channel?
|
2017-05-25 02:28:35 +02:00
|
|
|
netInfo := &lnrpc.NetworkInfo{
|
2021-12-22 11:03:03 +01:00
|
|
|
GraphDiameter: diameter,
|
2016-12-27 06:51:47 +01:00
|
|
|
MaxOutDegree: maxChanOut,
|
2019-03-24 13:22:15 +01:00
|
|
|
AvgOutDegree: float64(2*numChannels) / float64(numNodes),
|
2016-12-27 06:51:47 +01:00
|
|
|
NumNodes: numNodes,
|
|
|
|
NumChannels: numChannels,
|
|
|
|
TotalNetworkCapacity: int64(totalNetworkCapacity),
|
|
|
|
AvgChannelSize: float64(totalNetworkCapacity) / float64(numChannels),
|
2017-05-25 02:28:35 +02:00
|
|
|
|
2019-03-27 15:04:15 +01:00
|
|
|
MinChannelSize: int64(minChannelSize),
|
|
|
|
MaxChannelSize: int64(maxChannelSize),
|
|
|
|
MedianChannelSizeSat: int64(medianChanSize),
|
2019-07-16 01:59:31 +02:00
|
|
|
NumZombieChans: numZombies,
|
2017-05-25 02:28:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Similarly, if we don't have any channels, then we'll also set the
|
|
|
|
// average channel size to zero in order to avoid weird JSON encoding
|
|
|
|
// outputs.
|
|
|
|
if numChannels == 0 {
|
|
|
|
netInfo.AvgChannelSize = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
return netInfo, nil
|
2016-07-15 13:02:59 +02:00
|
|
|
}
|
2016-12-05 12:59:36 +01:00
|
|
|
|
2017-05-11 23:55:56 +02:00
|
|
|
// StopDaemon will send a shutdown request to the interrupt handler, triggering
|
|
|
|
// a graceful shutdown of the daemon.
|
2021-06-07 13:15:10 +02:00
|
|
|
func (r *rpcServer) StopDaemon(_ context.Context,
|
2017-08-22 08:25:41 +02:00
|
|
|
_ *lnrpc.StopRequest) (*lnrpc.StopResponse, error) {
|
2021-06-07 13:15:10 +02:00
|
|
|
|
|
|
|
// Before we even consider a shutdown, are we currently in recovery
|
|
|
|
// mode? We don't want to allow shutting down during recovery because
|
|
|
|
// that would mean the user would have to manually continue the rescan
|
|
|
|
// process next time by using `lncli unlock --recovery_window X`
|
|
|
|
// otherwise some funds wouldn't be picked up.
|
|
|
|
isRecoveryMode, progress, err := r.server.cc.Wallet.GetRecoveryInfo()
|
|
|
|
if err != nil {
|
2024-03-07 13:19:28 +01:00
|
|
|
return nil, fmt.Errorf("unable to get wallet recovery info: %w",
|
2021-06-07 13:15:10 +02:00
|
|
|
err)
|
|
|
|
}
|
|
|
|
if isRecoveryMode && progress < 1 {
|
|
|
|
return nil, fmt.Errorf("wallet recovery in progress, cannot " +
|
|
|
|
"shut down, please wait until rescan finishes")
|
|
|
|
}
|
|
|
|
|
2020-08-28 11:18:15 +02:00
|
|
|
r.interceptor.RequestShutdown()
|
2017-05-11 23:55:56 +02:00
|
|
|
return &lnrpc.StopResponse{}, nil
|
|
|
|
}
|
|
|
|
|
2017-03-14 04:39:16 +01:00
|
|
|
// SubscribeChannelGraph launches a streaming RPC that allows the caller to
|
|
|
|
// receive notifications upon any changes the channel graph topology from the
|
|
|
|
// review of the responding node. Events notified include: new nodes coming
|
|
|
|
// online, nodes updating their authenticated attributes, new channels being
|
|
|
|
// advertised, updates in the routing policy for a directional channel edge,
|
|
|
|
// and finally when prior channels are closed on-chain.
|
|
|
|
func (r *rpcServer) SubscribeChannelGraph(req *lnrpc.GraphTopologySubscription,
|
|
|
|
updateStream lnrpc.Lightning_SubscribeChannelGraphServer) error {
|
2017-08-22 08:25:41 +02:00
|
|
|
|
2017-03-14 04:39:16 +01:00
|
|
|
// First, we start by subscribing to a new intent to receive
|
|
|
|
// notifications from the channel router.
|
2024-06-17 01:30:01 +02:00
|
|
|
client, err := r.server.graphBuilder.SubscribeTopology()
|
2017-03-14 04:39:16 +01:00
|
|
|
if err != nil {
|
2017-03-15 04:06:33 +01:00
|
|
|
return err
|
2017-03-14 04:39:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure that the resources for the topology update client is cleaned
|
|
|
|
// up once either the server, or client exists.
|
|
|
|
defer client.Cancel()
|
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
|
|
|
|
// A new update has been sent by the channel router, we'll
|
2017-03-15 04:06:33 +01:00
|
|
|
// marshal it into the form expected by the gRPC client, then
|
2017-03-14 04:39:16 +01:00
|
|
|
// send it off.
|
|
|
|
case topChange, ok := <-client.TopologyChanges:
|
|
|
|
// If the second value from the channel read is nil,
|
|
|
|
// then this means that the channel router is exiting
|
2019-10-03 17:22:43 +02:00
|
|
|
// or the notification client was canceled. So we'll
|
2017-03-14 04:39:16 +01:00
|
|
|
// exit early.
|
|
|
|
if !ok {
|
2017-05-11 23:55:56 +02:00
|
|
|
return errors.New("server shutting down")
|
2017-03-14 04:39:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Convert the struct from the channel router into the
|
|
|
|
// form expected by the gRPC service then send it off
|
|
|
|
// to the client.
|
|
|
|
graphUpdate := marshallTopologyChange(topChange)
|
|
|
|
if err := updateStream.Send(graphUpdate); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-09-06 19:08:21 +02:00
|
|
|
// The response stream's context for whatever reason has been
|
|
|
|
// closed. If context is closed by an exceeded deadline
|
|
|
|
// we will return an error.
|
2020-07-18 22:41:11 +02:00
|
|
|
case <-updateStream.Context().Done():
|
2021-09-06 19:08:21 +02:00
|
|
|
if errors.Is(updateStream.Context().Err(), context.Canceled) {
|
|
|
|
return nil
|
|
|
|
}
|
2020-07-18 22:41:11 +02:00
|
|
|
return updateStream.Context().Err()
|
|
|
|
|
2017-03-14 04:39:16 +01:00
|
|
|
// The server is quitting, so we'll exit immediately. Returning
|
|
|
|
// nil will close the clients read end of the stream.
|
|
|
|
case <-r.quit:
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-07 04:11:11 +01:00
|
|
|
// marshallTopologyChange performs a mapping from the topology change struct
|
2017-03-14 04:39:16 +01:00
|
|
|
// returned by the router to the form of notifications expected by the current
|
|
|
|
// gRPC service.
|
2024-06-18 21:34:25 +02:00
|
|
|
func marshallTopologyChange(
|
|
|
|
topChange *graph.TopologyChange) *lnrpc.GraphTopologyUpdate {
|
2017-03-14 04:39:16 +01:00
|
|
|
|
|
|
|
// encodeKey is a simple helper function that converts a live public
|
|
|
|
// key into a hex-encoded version of the compressed serialization for
|
|
|
|
// the public key.
|
|
|
|
encodeKey := func(k *btcec.PublicKey) string {
|
|
|
|
return hex.EncodeToString(k.SerializeCompressed())
|
|
|
|
}
|
|
|
|
|
|
|
|
nodeUpdates := make([]*lnrpc.NodeUpdate, len(topChange.NodeUpdates))
|
|
|
|
for i, nodeUpdate := range topChange.NodeUpdates {
|
2024-06-18 21:34:25 +02:00
|
|
|
nodeAddrs := make(
|
|
|
|
[]*lnrpc.NodeAddress, 0, len(nodeUpdate.Addresses),
|
|
|
|
)
|
2021-04-09 18:29:22 +02:00
|
|
|
for _, addr := range nodeUpdate.Addresses {
|
|
|
|
nodeAddr := &lnrpc.NodeAddress{
|
|
|
|
Network: addr.Network(),
|
|
|
|
Addr: addr.String(),
|
|
|
|
}
|
|
|
|
nodeAddrs = append(nodeAddrs, nodeAddr)
|
|
|
|
}
|
|
|
|
|
2017-03-14 04:39:16 +01:00
|
|
|
addrs := make([]string, len(nodeUpdate.Addresses))
|
|
|
|
for i, addr := range nodeUpdate.Addresses {
|
|
|
|
addrs[i] = addr.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
nodeUpdates[i] = &lnrpc.NodeUpdate{
|
2021-04-09 18:29:22 +02:00
|
|
|
Addresses: addrs,
|
|
|
|
NodeAddresses: nodeAddrs,
|
|
|
|
IdentityKey: encodeKey(nodeUpdate.IdentityKey),
|
|
|
|
Alias: nodeUpdate.Alias,
|
|
|
|
Color: nodeUpdate.Color,
|
2020-12-03 16:41:20 +01:00
|
|
|
Features: invoicesrpc.CreateRPCFeatures(
|
|
|
|
nodeUpdate.Features,
|
|
|
|
),
|
2017-03-14 04:39:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
channelUpdates := make([]*lnrpc.ChannelEdgeUpdate, len(topChange.ChannelEdgeUpdates))
|
|
|
|
for i, channelUpdate := range topChange.ChannelEdgeUpdates {
|
2024-05-07 18:58:25 +02:00
|
|
|
|
|
|
|
customRecords := marshalExtraOpaqueData(
|
|
|
|
channelUpdate.ExtraOpaqueData,
|
|
|
|
)
|
|
|
|
inboundFee := extractInboundFeeSafe(
|
|
|
|
channelUpdate.ExtraOpaqueData,
|
|
|
|
)
|
|
|
|
|
2017-03-14 04:39:16 +01:00
|
|
|
channelUpdates[i] = &lnrpc.ChannelEdgeUpdate{
|
|
|
|
ChanId: channelUpdate.ChanID,
|
|
|
|
ChanPoint: &lnrpc.ChannelPoint{
|
2018-01-11 05:59:30 +01:00
|
|
|
FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
|
|
|
|
FundingTxidBytes: channelUpdate.ChanPoint.Hash[:],
|
|
|
|
},
|
2017-03-14 04:39:16 +01:00
|
|
|
OutputIndex: channelUpdate.ChanPoint.Index,
|
|
|
|
},
|
|
|
|
Capacity: int64(channelUpdate.Capacity),
|
|
|
|
RoutingPolicy: &lnrpc.RoutingPolicy{
|
2024-05-07 18:58:25 +02:00
|
|
|
TimeLockDelta: uint32(
|
|
|
|
channelUpdate.TimeLockDelta,
|
|
|
|
),
|
|
|
|
MinHtlc: int64(
|
|
|
|
channelUpdate.MinHTLC,
|
|
|
|
),
|
|
|
|
MaxHtlcMsat: uint64(
|
|
|
|
channelUpdate.MaxHTLC,
|
|
|
|
),
|
|
|
|
FeeBaseMsat: int64(
|
|
|
|
channelUpdate.BaseFee,
|
|
|
|
),
|
|
|
|
FeeRateMilliMsat: int64(
|
|
|
|
channelUpdate.FeeRate,
|
|
|
|
),
|
|
|
|
Disabled: channelUpdate.Disabled,
|
|
|
|
InboundFeeBaseMsat: inboundFee.BaseFee,
|
|
|
|
InboundFeeRateMilliMsat: inboundFee.FeeRate,
|
|
|
|
CustomRecords: customRecords,
|
2017-03-14 04:39:16 +01:00
|
|
|
},
|
|
|
|
AdvertisingNode: encodeKey(channelUpdate.AdvertisingNode),
|
|
|
|
ConnectingNode: encodeKey(channelUpdate.ConnectingNode),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
closedChans := make([]*lnrpc.ClosedChannelUpdate, len(topChange.ClosedChannels))
|
|
|
|
for i, closedChan := range topChange.ClosedChannels {
|
|
|
|
closedChans[i] = &lnrpc.ClosedChannelUpdate{
|
|
|
|
ChanId: closedChan.ChanID,
|
|
|
|
Capacity: int64(closedChan.Capacity),
|
|
|
|
ClosedHeight: closedChan.ClosedHeight,
|
|
|
|
ChanPoint: &lnrpc.ChannelPoint{
|
2018-01-11 05:59:30 +01:00
|
|
|
FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
|
|
|
|
FundingTxidBytes: closedChan.ChanPoint.Hash[:],
|
|
|
|
},
|
2017-03-14 04:39:16 +01:00
|
|
|
OutputIndex: closedChan.ChanPoint.Index,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return &lnrpc.GraphTopologyUpdate{
|
|
|
|
NodeUpdates: nodeUpdates,
|
|
|
|
ChannelUpdates: channelUpdates,
|
|
|
|
ClosedChans: closedChans,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-17 07:35:03 +01:00
|
|
|
// ListPayments returns a list of outgoing payments determined by a paginated
|
|
|
|
// database query.
|
2017-08-18 03:50:57 +02:00
|
|
|
func (r *rpcServer) ListPayments(ctx context.Context,
|
2019-06-11 14:59:05 +02:00
|
|
|
req *lnrpc.ListPaymentsRequest) (*lnrpc.ListPaymentsResponse, error) {
|
2017-08-22 08:25:41 +02:00
|
|
|
|
2022-11-15 19:15:24 +01:00
|
|
|
// If both dates are set, we check that the start date is less than the
|
|
|
|
// end date, otherwise we'll get an empty result.
|
|
|
|
if req.CreationDateStart != 0 && req.CreationDateEnd != 0 {
|
|
|
|
if req.CreationDateStart >= req.CreationDateEnd {
|
|
|
|
return nil, fmt.Errorf("start date(%v) must be before "+
|
|
|
|
"end date(%v)", req.CreationDateStart,
|
|
|
|
req.CreationDateEnd)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-17 07:35:03 +01:00
|
|
|
query := channeldb.PaymentsQuery{
|
|
|
|
IndexOffset: req.IndexOffset,
|
|
|
|
MaxPayments: req.MaxPayments,
|
|
|
|
Reversed: req.Reversed,
|
|
|
|
IncludeIncomplete: req.IncludeIncomplete,
|
2022-04-26 21:25:30 +02:00
|
|
|
CountTotal: req.CountTotalPayments,
|
2024-01-26 03:26:25 +01:00
|
|
|
CreationDateStart: int64(req.CreationDateStart),
|
|
|
|
CreationDateEnd: int64(req.CreationDateEnd),
|
2022-11-15 19:15:24 +01:00
|
|
|
}
|
|
|
|
|
2020-01-17 07:35:03 +01:00
|
|
|
// If the maximum number of payments wasn't specified, then we'll
|
|
|
|
// default to return the maximal number of payments representable.
|
|
|
|
if req.MaxPayments == 0 {
|
|
|
|
query.MaxPayments = math.MaxUint64
|
|
|
|
}
|
|
|
|
|
2021-09-21 19:18:17 +02:00
|
|
|
paymentsQuerySlice, err := r.server.miscDB.QueryPayments(query)
|
2019-05-23 20:05:29 +02:00
|
|
|
if err != nil {
|
2016-12-05 12:59:36 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
2016-12-21 10:19:01 +01:00
|
|
|
|
2020-01-17 07:35:03 +01:00
|
|
|
paymentsResp := &lnrpc.ListPaymentsResponse{
|
|
|
|
LastIndexOffset: paymentsQuerySlice.LastIndexOffset,
|
|
|
|
FirstIndexOffset: paymentsQuerySlice.FirstIndexOffset,
|
2022-04-26 21:25:30 +02:00
|
|
|
TotalNumPayments: paymentsQuerySlice.TotalCount,
|
2020-01-17 07:35:03 +01:00
|
|
|
}
|
2019-06-11 14:59:05 +02:00
|
|
|
|
2020-01-17 07:35:03 +01:00
|
|
|
for _, payment := range paymentsQuerySlice.Payments {
|
2020-04-03 17:01:47 +02:00
|
|
|
payment := payment
|
2019-05-23 20:05:29 +02:00
|
|
|
|
2020-05-04 21:13:47 +02:00
|
|
|
rpcPayment, err := r.routerBackend.MarshallPayment(payment)
|
2019-06-11 14:59:05 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-04-03 17:01:47 +02:00
|
|
|
paymentsResp.Payments = append(
|
|
|
|
paymentsResp.Payments, rpcPayment,
|
|
|
|
)
|
2016-12-05 12:59:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return paymentsResp, nil
|
|
|
|
}
|
|
|
|
|
2021-09-13 11:32:28 +02:00
|
|
|
// DeletePayment deletes a payment from the DB given its payment hash. If
|
|
|
|
// failedHtlcsOnly is set, only failed HTLC attempts of the payment will be
|
|
|
|
// deleted.
|
|
|
|
func (r *rpcServer) DeletePayment(ctx context.Context,
|
|
|
|
req *lnrpc.DeletePaymentRequest) (
|
|
|
|
*lnrpc.DeletePaymentResponse, error) {
|
|
|
|
|
|
|
|
hash, err := lntypes.MakeHash(req.PaymentHash)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
rpcsLog.Infof("[DeletePayment] payment_identifier=%v, "+
|
|
|
|
"failed_htlcs_only=%v", hash, req.FailedHtlcsOnly)
|
|
|
|
|
2021-09-21 19:18:17 +02:00
|
|
|
err = r.server.miscDB.DeletePayment(hash, req.FailedHtlcsOnly)
|
2021-09-13 11:32:28 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &lnrpc.DeletePaymentResponse{}, nil
|
|
|
|
}
|
|
|
|
|
2016-12-05 12:59:36 +01:00
|
|
|
// DeleteAllPayments deletes all outgoing payments from DB.
|
2017-08-18 03:50:57 +02:00
|
|
|
func (r *rpcServer) DeleteAllPayments(ctx context.Context,
|
2019-06-11 15:11:24 +02:00
|
|
|
req *lnrpc.DeleteAllPaymentsRequest) (
|
|
|
|
*lnrpc.DeleteAllPaymentsResponse, error) {
|
2017-08-22 08:25:41 +02:00
|
|
|
|
2024-04-21 22:43:12 +02:00
|
|
|
switch {
|
|
|
|
// Since this is a destructive operation, at least one of the options
|
|
|
|
// must be set to true.
|
|
|
|
case !req.AllPayments && !req.FailedPaymentsOnly &&
|
|
|
|
!req.FailedHtlcsOnly:
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("at least one of the options " +
|
|
|
|
"`all_payments`, `failed_payments_only`, or " +
|
|
|
|
"`failed_htlcs_only` must be set to true")
|
|
|
|
|
|
|
|
// `all_payments` cannot be true with `failed_payments_only` or
|
|
|
|
// `failed_htlcs_only`. `all_payments` includes all records, making
|
|
|
|
// these options contradictory.
|
|
|
|
case req.AllPayments &&
|
|
|
|
(req.FailedPaymentsOnly || req.FailedHtlcsOnly):
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("`all_payments` cannot be set to true " +
|
|
|
|
"while either `failed_payments_only` or " +
|
|
|
|
"`failed_htlcs_only` is also set to true")
|
|
|
|
}
|
|
|
|
|
2021-01-22 15:47:55 +01:00
|
|
|
rpcsLog.Infof("[DeleteAllPayments] failed_payments_only=%v, "+
|
|
|
|
"failed_htlcs_only=%v", req.FailedPaymentsOnly,
|
|
|
|
req.FailedHtlcsOnly)
|
2016-12-21 10:19:01 +01:00
|
|
|
|
2021-09-21 19:18:17 +02:00
|
|
|
err := r.server.miscDB.DeletePayments(
|
2021-01-22 15:47:55 +01:00
|
|
|
req.FailedPaymentsOnly, req.FailedHtlcsOnly,
|
2019-06-11 15:11:24 +02:00
|
|
|
)
|
|
|
|
if err != nil {
|
2016-12-31 01:41:59 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &lnrpc.DeleteAllPaymentsResponse{}, nil
|
2016-12-27 06:51:18 +01:00
|
|
|
}
|
2016-12-27 06:51:47 +01:00
|
|
|
|
2017-01-15 03:16:53 +01:00
|
|
|
// DebugLevel allows a caller to programmatically set the logging verbosity of
|
|
|
|
// lnd. The logging can be targeted according to a coarse daemon-wide logging
|
|
|
|
// level, or in a granular fashion to specify the logging for a target
|
|
|
|
// sub-system.
|
|
|
|
func (r *rpcServer) DebugLevel(ctx context.Context,
|
|
|
|
req *lnrpc.DebugLevelRequest) (*lnrpc.DebugLevelResponse, error) {
|
2017-08-22 08:25:41 +02:00
|
|
|
|
2017-01-15 03:16:53 +01:00
|
|
|
// If show is set, then we simply print out the list of available
|
|
|
|
// sub-systems.
|
|
|
|
if req.Show {
|
|
|
|
return &lnrpc.DebugLevelResponse{
|
2019-09-20 16:22:02 +02:00
|
|
|
SubSystems: strings.Join(
|
2020-05-25 18:22:41 +02:00
|
|
|
r.cfg.LogWriter.SupportedSubsystems(), " ",
|
2019-09-20 16:22:02 +02:00
|
|
|
),
|
2017-01-15 03:16:53 +01:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
rpcsLog.Infof("[debuglevel] changing debug level to: %v", req.LevelSpec)
|
|
|
|
|
|
|
|
// Otherwise, we'll attempt to set the logging level using the
|
|
|
|
// specified level spec.
|
2020-05-25 18:22:41 +02:00
|
|
|
err := build.ParseAndSetDebugLevels(req.LevelSpec, r.cfg.LogWriter)
|
2019-09-20 16:22:02 +02:00
|
|
|
if err != nil {
|
2017-01-15 03:16:53 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &lnrpc.DebugLevelResponse{}, nil
|
|
|
|
}
|
2017-01-17 22:39:30 +01:00
|
|
|
|
|
|
|
// DecodePayReq takes an encoded payment request string and attempts to decode
|
|
|
|
// it, returning a full description of the conditions encoded within the
|
|
|
|
// payment request.
|
|
|
|
func (r *rpcServer) DecodePayReq(ctx context.Context,
|
|
|
|
req *lnrpc.PayReqString) (*lnrpc.PayReq, error) {
|
2017-08-22 08:25:41 +02:00
|
|
|
|
|
|
|
rpcsLog.Tracef("[decodepayreq] decoding: %v", req.PayReq)
|
|
|
|
|
2017-01-17 22:39:30 +01:00
|
|
|
// Fist we'll attempt to decode the payment request string, if the
|
|
|
|
// request is invalid or the checksum doesn't match, then we'll exit
|
|
|
|
// here with an error.
|
2020-08-04 20:56:31 +02:00
|
|
|
payReq, err := zpay32.Decode(req.PayReq, r.cfg.ActiveNetParams.Params)
|
2017-01-17 22:39:30 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-09-05 18:08:02 +02:00
|
|
|
// Let the fields default to empty strings.
|
|
|
|
desc := ""
|
|
|
|
if payReq.Description != nil {
|
|
|
|
desc = *payReq.Description
|
|
|
|
}
|
|
|
|
|
|
|
|
descHash := []byte("")
|
|
|
|
if payReq.DescriptionHash != nil {
|
|
|
|
descHash = payReq.DescriptionHash[:]
|
|
|
|
}
|
|
|
|
|
|
|
|
fallbackAddr := ""
|
|
|
|
if payReq.FallbackAddr != nil {
|
|
|
|
fallbackAddr = payReq.FallbackAddr.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Expiry time will default to 3600 seconds if not specified
|
|
|
|
// explicitly.
|
|
|
|
expiry := int64(payReq.Expiry().Seconds())
|
|
|
|
|
2018-03-28 06:01:21 +02:00
|
|
|
// Convert between the `lnrpc` and `routing` types.
|
2019-01-03 19:13:08 +01:00
|
|
|
routeHints := invoicesrpc.CreateRPCRouteHints(payReq.RouteHints)
|
2018-03-28 06:01:21 +02:00
|
|
|
|
2024-04-10 12:59:41 +02:00
|
|
|
blindedPaymentPaths, err := invoicesrpc.CreateRPCBlindedPayments(
|
|
|
|
payReq.BlindedPaymentPaths,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-12-07 00:48:30 +01:00
|
|
|
var amtSat, amtMsat int64
|
2017-09-05 18:08:02 +02:00
|
|
|
if payReq.MilliSat != nil {
|
2019-12-07 00:48:30 +01:00
|
|
|
amtSat = int64(payReq.MilliSat.ToSatoshis())
|
|
|
|
amtMsat = int64(*payReq.MilliSat)
|
2017-09-05 18:08:02 +02:00
|
|
|
}
|
|
|
|
|
2019-12-05 16:59:47 +01:00
|
|
|
// Extract the payment address from the payment request, if present.
|
|
|
|
var paymentAddr []byte
|
|
|
|
if payReq.PaymentAddr != nil {
|
|
|
|
paymentAddr = payReq.PaymentAddr[:]
|
|
|
|
}
|
|
|
|
|
2017-01-17 22:39:30 +01:00
|
|
|
dest := payReq.Destination.SerializeCompressed()
|
|
|
|
return &lnrpc.PayReq{
|
2017-09-05 18:08:02 +02:00
|
|
|
Destination: hex.EncodeToString(dest),
|
|
|
|
PaymentHash: hex.EncodeToString(payReq.PaymentHash[:]),
|
2019-12-07 00:48:30 +01:00
|
|
|
NumSatoshis: amtSat,
|
|
|
|
NumMsat: amtMsat,
|
2017-09-05 18:08:02 +02:00
|
|
|
Timestamp: payReq.Timestamp.Unix(),
|
|
|
|
Description: desc,
|
|
|
|
DescriptionHash: hex.EncodeToString(descHash[:]),
|
|
|
|
FallbackAddr: fallbackAddr,
|
|
|
|
Expiry: expiry,
|
2017-10-19 07:16:40 +02:00
|
|
|
CltvExpiry: int64(payReq.MinFinalCLTVExpiry()),
|
2018-03-28 06:01:21 +02:00
|
|
|
RouteHints: routeHints,
|
2024-04-10 12:59:41 +02:00
|
|
|
BlindedPaths: blindedPaymentPaths,
|
2019-12-05 16:59:47 +01:00
|
|
|
PaymentAddr: paymentAddr,
|
2019-12-12 01:37:02 +01:00
|
|
|
Features: invoicesrpc.CreateRPCFeatures(payReq.Features),
|
2017-01-17 22:39:30 +01:00
|
|
|
}, nil
|
|
|
|
}
|
2017-08-22 09:09:43 +02:00
|
|
|
|
|
|
|
// feeBase is the fixed point that fee rate computation are performed over.
|
|
|
|
// Nodes on the network advertise their fee rate using this point as a base.
|
|
|
|
// This means that the minimal possible fee rate if 1e-6, or 0.000001, or
|
|
|
|
// 0.0001%.
|
2021-09-11 23:43:35 +02:00
|
|
|
const feeBase float64 = 1000000
|
2017-08-22 09:09:43 +02:00
|
|
|
|
|
|
|
// FeeReport allows the caller to obtain a report detailing the current fee
|
|
|
|
// schedule enforced by the node globally for each channel.
|
|
|
|
func (r *rpcServer) FeeReport(ctx context.Context,
|
|
|
|
_ *lnrpc.FeeReportRequest) (*lnrpc.FeeReportResponse, error) {
|
|
|
|
|
2021-08-03 09:57:26 +02:00
|
|
|
channelGraph := r.server.graphDB
|
2017-08-22 09:09:43 +02:00
|
|
|
selfNode, err := channelGraph.SourceNode()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var feeReports []*lnrpc.ChannelFeeReport
|
2024-06-15 02:34:53 +02:00
|
|
|
err = channelGraph.ForEachNodeChannel(selfNode.PubKeyBytes,
|
2023-11-08 10:18:45 +01:00
|
|
|
func(_ kvdb.RTx, chanInfo *models.ChannelEdgeInfo,
|
|
|
|
edgePolicy, _ *models.ChannelEdgePolicy) error {
|
2023-10-23 13:44:40 +02:00
|
|
|
|
|
|
|
// Self node should always have policies for its
|
|
|
|
// channels.
|
|
|
|
if edgePolicy == nil {
|
|
|
|
return fmt.Errorf("no policy for outgoing "+
|
|
|
|
"channel %v ", chanInfo.ChannelID)
|
|
|
|
}
|
2017-08-22 09:09:43 +02:00
|
|
|
|
2023-10-23 13:44:40 +02:00
|
|
|
// We'll compute the effective fee rate by converting
|
|
|
|
// from a fixed point fee rate to a floating point fee
|
|
|
|
// rate. The fee rate field in the database the amount
|
|
|
|
// of mSAT charged per 1mil mSAT sent, so will divide by
|
|
|
|
// this to get the proper fee rate.
|
|
|
|
feeRateFixedPoint :=
|
|
|
|
edgePolicy.FeeProportionalMillionths
|
|
|
|
feeRate := float64(feeRateFixedPoint) / feeBase
|
|
|
|
|
2022-09-21 14:05:53 +02:00
|
|
|
// Decode inbound fee from extra data.
|
|
|
|
var inboundFee lnwire.Fee
|
|
|
|
_, err := edgePolicy.ExtraOpaqueData.ExtractRecords(
|
|
|
|
&inboundFee,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-10-23 13:44:40 +02:00
|
|
|
// TODO(roasbeef): also add stats for revenue for each
|
|
|
|
// channel
|
|
|
|
feeReports = append(feeReports, &lnrpc.ChannelFeeReport{
|
|
|
|
ChanId: chanInfo.ChannelID,
|
|
|
|
ChannelPoint: chanInfo.ChannelPoint.String(),
|
|
|
|
BaseFeeMsat: int64(edgePolicy.FeeBaseMSat),
|
|
|
|
FeePerMil: int64(feeRateFixedPoint),
|
|
|
|
FeeRate: feeRate,
|
2022-09-21 14:05:53 +02:00
|
|
|
|
|
|
|
InboundBaseFeeMsat: inboundFee.BaseFee,
|
|
|
|
InboundFeePerMil: inboundFee.FeeRate,
|
2023-10-23 13:44:40 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
)
|
2017-08-22 09:09:43 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-09-21 19:18:17 +02:00
|
|
|
fwdEventLog := r.server.miscDB.ForwardingLog()
|
2018-02-28 07:23:09 +01:00
|
|
|
|
|
|
|
// computeFeeSum is a helper function that computes the total fees for
|
|
|
|
// a particular time slice described by a forwarding event query.
|
|
|
|
computeFeeSum := func(query channeldb.ForwardingEventQuery) (lnwire.MilliSatoshi, error) {
|
|
|
|
|
|
|
|
var totalFees lnwire.MilliSatoshi
|
|
|
|
|
|
|
|
// We'll continue to fetch the next query and accumulate the
|
|
|
|
// fees until the next query returns no events.
|
|
|
|
for {
|
|
|
|
timeSlice, err := fwdEventLog.Query(query)
|
|
|
|
if err != nil {
|
2019-09-13 09:48:43 +02:00
|
|
|
return 0, err
|
2018-02-28 07:23:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// If the timeslice is empty, then we'll return as
|
|
|
|
// we've retrieved all the entries in this range.
|
|
|
|
if len(timeSlice.ForwardingEvents) == 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, we'll tally up an accumulate the total
|
|
|
|
// fees for this time slice.
|
|
|
|
for _, event := range timeSlice.ForwardingEvents {
|
|
|
|
fee := event.AmtIn - event.AmtOut
|
|
|
|
totalFees += fee
|
|
|
|
}
|
|
|
|
|
|
|
|
// We'll now take the last offset index returned as
|
|
|
|
// part of this response, and modify our query to start
|
|
|
|
// at this index. This has a pagination effect in the
|
|
|
|
// case that our query bounds has more than 100k
|
|
|
|
// entries.
|
|
|
|
query.IndexOffset = timeSlice.LastIndexOffset
|
|
|
|
}
|
|
|
|
|
|
|
|
return totalFees, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
now := time.Now()
|
|
|
|
|
|
|
|
// Before we perform the queries below, we'll instruct the switch to
|
|
|
|
// flush any pending events to disk. This ensure we get a complete
|
|
|
|
// snapshot at this particular time.
|
2019-03-11 21:23:21 +01:00
|
|
|
if err := r.server.htlcSwitch.FlushForwardingEvents(); err != nil {
|
2018-02-28 07:23:09 +01:00
|
|
|
return nil, fmt.Errorf("unable to flush forwarding "+
|
|
|
|
"events: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// In addition to returning the current fee schedule for each channel.
|
|
|
|
// We'll also perform a series of queries to obtain the total fees
|
|
|
|
// earned over the past day, week, and month.
|
|
|
|
dayQuery := channeldb.ForwardingEventQuery{
|
|
|
|
StartTime: now.Add(-time.Hour * 24),
|
|
|
|
EndTime: now,
|
|
|
|
NumMaxEvents: 1000,
|
|
|
|
}
|
|
|
|
dayFees, err := computeFeeSum(dayQuery)
|
|
|
|
if err != nil {
|
2024-02-26 12:19:38 +01:00
|
|
|
return nil, fmt.Errorf("unable to retrieve day fees: %w", err)
|
2018-02-28 07:23:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
weekQuery := channeldb.ForwardingEventQuery{
|
|
|
|
StartTime: now.Add(-time.Hour * 24 * 7),
|
|
|
|
EndTime: now,
|
|
|
|
NumMaxEvents: 1000,
|
|
|
|
}
|
|
|
|
weekFees, err := computeFeeSum(weekQuery)
|
|
|
|
if err != nil {
|
2024-02-26 12:19:38 +01:00
|
|
|
return nil, fmt.Errorf("unable to retrieve day fees: %w", err)
|
2018-02-28 07:23:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
monthQuery := channeldb.ForwardingEventQuery{
|
|
|
|
StartTime: now.Add(-time.Hour * 24 * 30),
|
|
|
|
EndTime: now,
|
|
|
|
NumMaxEvents: 1000,
|
|
|
|
}
|
|
|
|
monthFees, err := computeFeeSum(monthQuery)
|
|
|
|
if err != nil {
|
2024-02-26 12:19:38 +01:00
|
|
|
return nil, fmt.Errorf("unable to retrieve day fees: %w", err)
|
2018-02-28 07:23:09 +01:00
|
|
|
}
|
|
|
|
|
2017-08-22 10:00:07 +02:00
|
|
|
return &lnrpc.FeeReportResponse{
|
|
|
|
ChannelFees: feeReports,
|
2018-02-28 07:23:09 +01:00
|
|
|
DayFeeSum: uint64(dayFees.ToSatoshis()),
|
|
|
|
WeekFeeSum: uint64(weekFees.ToSatoshis()),
|
|
|
|
MonthFeeSum: uint64(monthFees.ToSatoshis()),
|
2017-08-22 10:00:07 +02:00
|
|
|
}, nil
|
2017-08-22 09:09:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// minFeeRate is the smallest permitted fee rate within the network. This is
|
2018-02-07 04:11:11 +01:00
|
|
|
// derived by the fact that fee rates are computed using a fixed point of
|
2017-08-22 09:09:43 +02:00
|
|
|
// 1,000,000. As a result, the smallest representable fee rate is 1e-6, or
|
|
|
|
// 0.000001, or 0.0001%.
|
|
|
|
const minFeeRate = 1e-6
|
|
|
|
|
2017-12-16 23:14:58 +01:00
|
|
|
// UpdateChannelPolicy allows the caller to update the channel forwarding policy
|
|
|
|
// for all channels globally, or a particular channel.
|
|
|
|
func (r *rpcServer) UpdateChannelPolicy(ctx context.Context,
|
|
|
|
req *lnrpc.PolicyUpdateRequest) (*lnrpc.PolicyUpdateResponse, error) {
|
2017-08-22 09:09:43 +02:00
|
|
|
|
|
|
|
var targetChans []wire.OutPoint
|
|
|
|
switch scope := req.Scope.(type) {
|
|
|
|
// If the request is targeting all active channels, then we don't need
|
|
|
|
// target any channels by their channel point.
|
2017-12-16 23:14:58 +01:00
|
|
|
case *lnrpc.PolicyUpdateRequest_Global:
|
2017-08-22 09:09:43 +02:00
|
|
|
|
|
|
|
// Otherwise, we're targeting an individual channel by its channel
|
|
|
|
// point.
|
2017-12-16 23:14:58 +01:00
|
|
|
case *lnrpc.PolicyUpdateRequest_ChanPoint:
|
2021-02-13 09:05:33 +01:00
|
|
|
txid, err := lnrpc.GetChanPointFundingTxid(scope.ChanPoint)
|
2017-08-22 09:09:43 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
targetChans = append(targetChans, wire.OutPoint{
|
|
|
|
Hash: *txid,
|
|
|
|
Index: scope.ChanPoint.OutputIndex,
|
|
|
|
})
|
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("unknown scope: %v", scope)
|
|
|
|
}
|
|
|
|
|
2021-09-11 23:43:35 +02:00
|
|
|
var feeRateFixed uint32
|
|
|
|
|
2019-05-29 23:52:42 +02:00
|
|
|
switch {
|
2021-09-11 23:43:35 +02:00
|
|
|
// The request should use either the fee rate in percent, or the new
|
|
|
|
// ppm rate, but not both.
|
|
|
|
case req.FeeRate != 0 && req.FeeRatePpm != 0:
|
|
|
|
errMsg := "cannot set both FeeRate and FeeRatePpm at the " +
|
|
|
|
"same time"
|
|
|
|
|
|
|
|
return nil, status.Errorf(codes.InvalidArgument, errMsg)
|
|
|
|
|
|
|
|
// If the request is using fee_rate.
|
|
|
|
case req.FeeRate != 0:
|
|
|
|
// As a sanity check, if the fee isn't zero, we'll ensure that
|
|
|
|
// the passed fee rate is below 1e-6, or the lowest allowed
|
|
|
|
// non-zero fee rate expressible within the protocol.
|
|
|
|
if req.FeeRate != 0 && req.FeeRate < minFeeRate {
|
|
|
|
return nil, fmt.Errorf("fee rate of %v is too "+
|
|
|
|
"small, min fee rate is %v", req.FeeRate,
|
|
|
|
minFeeRate)
|
|
|
|
}
|
|
|
|
|
|
|
|
// We'll also need to convert the floating point fee rate we
|
|
|
|
// accept over RPC to the fixed point rate that we use within
|
|
|
|
// the protocol. We do this by multiplying the passed fee rate
|
|
|
|
// by the fee base. This gives us the fixed point, scaled by 1
|
|
|
|
// million that's used within the protocol.
|
|
|
|
//
|
|
|
|
// Because of the inaccurate precision of the IEEE 754
|
|
|
|
// standard, we need to round the product of feerate and
|
|
|
|
// feebase.
|
|
|
|
feeRateFixed = uint32(math.Round(req.FeeRate * feeBase))
|
|
|
|
|
|
|
|
// Otherwise, we use the fee_rate_ppm parameter.
|
|
|
|
case req.FeeRatePpm != 0:
|
|
|
|
feeRateFixed = req.FeeRatePpm
|
|
|
|
}
|
2017-08-22 09:09:43 +02:00
|
|
|
|
2019-05-29 23:52:42 +02:00
|
|
|
// We'll also ensure that the user isn't setting a CLTV delta that
|
|
|
|
// won't give outgoing HTLCs enough time to fully resolve if needed.
|
2021-09-11 23:43:35 +02:00
|
|
|
if req.TimeLockDelta < minTimeLockDelta {
|
2017-12-16 23:14:58 +01:00
|
|
|
return nil, fmt.Errorf("time lock delta of %v is too small, "+
|
|
|
|
"minimum supported is %v", req.TimeLockDelta,
|
|
|
|
minTimeLockDelta)
|
2023-02-18 04:53:27 +01:00
|
|
|
} else if req.TimeLockDelta > uint32(MaxTimeLockDelta) {
|
|
|
|
return nil, fmt.Errorf("time lock delta of %v is too big, "+
|
|
|
|
"maximum supported is %v", req.TimeLockDelta,
|
|
|
|
MaxTimeLockDelta)
|
2017-12-16 23:14:58 +01:00
|
|
|
}
|
|
|
|
|
2024-04-06 18:45:04 +02:00
|
|
|
// By default, positive inbound fees are rejected.
|
2024-05-14 00:21:26 +02:00
|
|
|
if !r.cfg.AcceptPositiveInboundFees && req.InboundFee != nil {
|
|
|
|
if req.InboundFee.BaseFeeMsat > 0 {
|
2024-04-06 18:45:04 +02:00
|
|
|
return nil, fmt.Errorf("positive values for inbound "+
|
|
|
|
"base fee msat are not supported: %v",
|
2024-05-14 00:21:26 +02:00
|
|
|
req.InboundFee.BaseFeeMsat)
|
2024-04-06 18:45:04 +02:00
|
|
|
}
|
2024-05-14 00:21:26 +02:00
|
|
|
if req.InboundFee.FeeRatePpm > 0 {
|
2024-04-06 18:45:04 +02:00
|
|
|
return nil, fmt.Errorf("positive values for inbound "+
|
|
|
|
"fee rate ppm are not supported: %v",
|
2024-05-14 00:21:26 +02:00
|
|
|
req.InboundFee.FeeRatePpm)
|
2024-04-06 18:45:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-14 00:21:26 +02:00
|
|
|
// If no inbound fees have been specified, we indicate with an empty
|
|
|
|
// option that the previous inbound fee should be retained during the
|
|
|
|
// edge update.
|
|
|
|
inboundFee := fn.None[models.InboundFee]()
|
|
|
|
if req.InboundFee != nil {
|
|
|
|
inboundFee = fn.Some(models.InboundFee{
|
|
|
|
Base: req.InboundFee.BaseFeeMsat,
|
|
|
|
Rate: req.InboundFee.FeeRatePpm,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-08-22 09:09:43 +02:00
|
|
|
baseFeeMsat := lnwire.MilliSatoshi(req.BaseFeeMsat)
|
|
|
|
feeSchema := routing.FeeSchema{
|
2024-05-14 00:21:26 +02:00
|
|
|
BaseFee: baseFeeMsat,
|
|
|
|
FeeRate: feeRateFixed,
|
|
|
|
InboundFee: inboundFee,
|
2017-08-22 09:09:43 +02:00
|
|
|
}
|
|
|
|
|
2019-11-15 11:24:58 +01:00
|
|
|
maxHtlc := lnwire.MilliSatoshi(req.MaxHtlcMsat)
|
|
|
|
var minHtlc *lnwire.MilliSatoshi
|
|
|
|
if req.MinHtlcMsatSpecified {
|
|
|
|
min := lnwire.MilliSatoshi(req.MinHtlcMsat)
|
|
|
|
minHtlc = &min
|
|
|
|
}
|
|
|
|
|
2017-12-16 23:14:58 +01:00
|
|
|
chanPolicy := routing.ChannelPolicy{
|
|
|
|
FeeSchema: feeSchema,
|
|
|
|
TimeLockDelta: req.TimeLockDelta,
|
2019-11-15 11:24:58 +01:00
|
|
|
MaxHTLC: maxHtlc,
|
|
|
|
MinHTLC: minHtlc,
|
2017-12-16 23:14:58 +01:00
|
|
|
}
|
|
|
|
|
2023-08-11 13:04:03 +02:00
|
|
|
rpcsLog.Debugf("[updatechanpolicy] updating channel policy "+
|
|
|
|
"base_fee=%v, rate_fixed=%v, time_lock_delta: %v, "+
|
2019-11-15 11:24:58 +01:00
|
|
|
"min_htlc=%v, max_htlc=%v, targets=%v",
|
2021-09-11 23:43:35 +02:00
|
|
|
req.BaseFeeMsat, feeRateFixed, req.TimeLockDelta,
|
2019-11-15 11:24:58 +01:00
|
|
|
minHtlc, maxHtlc,
|
2017-08-22 09:09:43 +02:00
|
|
|
spew.Sdump(targetChans))
|
|
|
|
|
2019-09-19 11:02:46 +02:00
|
|
|
// With the scope resolved, we'll now send this to the local channel
|
|
|
|
// manager so it can propagate the new policy for our target channel(s).
|
2021-06-19 03:47:06 +02:00
|
|
|
failedUpdates, err := r.server.localChanMgr.UpdatePolicy(chanPolicy,
|
|
|
|
targetChans...)
|
2017-08-22 09:09:43 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-06-19 03:47:06 +02:00
|
|
|
return &lnrpc.PolicyUpdateResponse{
|
|
|
|
FailedUpdates: failedUpdates,
|
|
|
|
}, nil
|
2017-08-22 09:09:43 +02:00
|
|
|
}
|
2018-02-28 07:23:27 +01:00
|
|
|
|
|
|
|
// ForwardingHistory allows the caller to query the htlcswitch for a record of
|
|
|
|
// all HTLC's forwarded within the target time range, and integer offset within
|
|
|
|
// that time range. If no time-range is specified, then the first chunk of the
|
|
|
|
// past 24 hrs of forwarding history are returned.
|
|
|
|
|
|
|
|
// A list of forwarding events are returned. The size of each forwarding event
|
|
|
|
// is 40 bytes, and the max message size able to be returned in gRPC is 4 MiB.
|
|
|
|
// In order to safely stay under this max limit, we'll return 50k events per
|
|
|
|
// response. Each response has the index offset of the last entry. The index
|
|
|
|
// offset can be provided to the request to allow the caller to skip a series
|
|
|
|
// of records.
|
|
|
|
func (r *rpcServer) ForwardingHistory(ctx context.Context,
|
2022-10-11 17:02:50 +02:00
|
|
|
req *lnrpc.ForwardingHistoryRequest) (*lnrpc.ForwardingHistoryResponse,
|
|
|
|
error) {
|
2018-02-28 07:23:27 +01:00
|
|
|
|
|
|
|
// Before we perform the queries below, we'll instruct the switch to
|
|
|
|
// flush any pending events to disk. This ensure we get a complete
|
|
|
|
// snapshot at this particular time.
|
|
|
|
if err := r.server.htlcSwitch.FlushForwardingEvents(); err != nil {
|
|
|
|
return nil, fmt.Errorf("unable to flush forwarding "+
|
|
|
|
"events: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
startTime, endTime time.Time
|
|
|
|
|
|
|
|
numEvents uint32
|
|
|
|
)
|
|
|
|
|
2022-10-11 17:02:50 +02:00
|
|
|
// startTime defaults to the Unix epoch (0 unixtime, or
|
|
|
|
// midnight 01-01-1970).
|
2019-10-04 00:12:21 +02:00
|
|
|
startTime = time.Unix(int64(req.StartTime), 0)
|
2019-07-09 00:18:35 +02:00
|
|
|
|
|
|
|
// If the end time wasn't specified, assume a default end time of now.
|
|
|
|
if req.EndTime == 0 {
|
|
|
|
now := time.Now()
|
|
|
|
endTime = now
|
|
|
|
} else {
|
2018-02-28 07:23:27 +01:00
|
|
|
endTime = time.Unix(int64(req.EndTime), 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the number of events wasn't specified, then we'll default to
|
|
|
|
// returning the last 100 events.
|
|
|
|
numEvents = req.NumMaxEvents
|
|
|
|
if numEvents == 0 {
|
|
|
|
numEvents = 100
|
|
|
|
}
|
|
|
|
|
2019-07-09 00:18:35 +02:00
|
|
|
// Next, we'll map the proto request into a format that is understood by
|
2018-02-28 07:23:27 +01:00
|
|
|
// the forwarding log.
|
|
|
|
eventQuery := channeldb.ForwardingEventQuery{
|
|
|
|
StartTime: startTime,
|
|
|
|
EndTime: endTime,
|
|
|
|
IndexOffset: req.IndexOffset,
|
|
|
|
NumMaxEvents: numEvents,
|
|
|
|
}
|
2021-09-21 19:18:17 +02:00
|
|
|
timeSlice, err := r.server.miscDB.ForwardingLog().Query(eventQuery)
|
2018-02-28 07:23:27 +01:00
|
|
|
if err != nil {
|
2024-03-07 13:19:28 +01:00
|
|
|
return nil, fmt.Errorf("unable to query forwarding log: %w",
|
2022-10-11 17:02:50 +02:00
|
|
|
err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// chanToPeerAlias caches previously looked up channel information.
|
|
|
|
chanToPeerAlias := make(map[lnwire.ShortChannelID]string)
|
|
|
|
|
|
|
|
// Helper function to extract a peer's node alias given its SCID.
|
|
|
|
getRemoteAlias := func(chanID lnwire.ShortChannelID) (string, error) {
|
|
|
|
// If we'd previously seen this chanID then return the cached
|
|
|
|
// peer alias.
|
|
|
|
if peerAlias, ok := chanToPeerAlias[chanID]; ok {
|
|
|
|
return peerAlias, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Else call the server to look up the peer alias.
|
|
|
|
edge, err := r.GetChanInfo(ctx, &lnrpc.ChanInfoRequest{
|
|
|
|
ChanId: chanID.ToUint64(),
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
remotePub := edge.Node1Pub
|
|
|
|
if r.selfNode.String() == edge.Node1Pub {
|
|
|
|
remotePub = edge.Node2Pub
|
|
|
|
}
|
|
|
|
|
|
|
|
vertex, err := route.NewVertexFromStr(remotePub)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2024-06-15 02:29:26 +02:00
|
|
|
peer, err := r.server.graphDB.FetchLightningNode(vertex)
|
2022-10-11 17:02:50 +02:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cache the peer alias.
|
|
|
|
chanToPeerAlias[chanID] = peer.Alias
|
|
|
|
|
|
|
|
return peer.Alias, nil
|
2018-02-28 07:23:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO(roasbeef): add settlement latency?
|
|
|
|
// * use FPE on all records?
|
|
|
|
|
|
|
|
// With the events retrieved, we'll now map them into the proper proto
|
|
|
|
// response.
|
|
|
|
//
|
|
|
|
// TODO(roasbeef): show in ns for the outside?
|
2022-10-11 17:02:50 +02:00
|
|
|
fwdingEvents := make(
|
|
|
|
[]*lnrpc.ForwardingEvent, len(timeSlice.ForwardingEvents),
|
|
|
|
)
|
2018-02-28 07:23:27 +01:00
|
|
|
resp := &lnrpc.ForwardingHistoryResponse{
|
2022-10-11 17:02:50 +02:00
|
|
|
ForwardingEvents: fwdingEvents,
|
2018-02-28 07:23:27 +01:00
|
|
|
LastOffsetIndex: timeSlice.LastIndexOffset,
|
|
|
|
}
|
|
|
|
for i, event := range timeSlice.ForwardingEvents {
|
2019-10-16 19:50:58 +02:00
|
|
|
amtInMsat := event.AmtIn
|
|
|
|
amtOutMsat := event.AmtOut
|
2018-11-13 18:22:12 +01:00
|
|
|
feeMsat := event.AmtIn - event.AmtOut
|
2018-02-28 07:23:27 +01:00
|
|
|
|
|
|
|
resp.ForwardingEvents[i] = &lnrpc.ForwardingEvent{
|
2021-01-24 16:25:24 +01:00
|
|
|
Timestamp: uint64(event.Timestamp.Unix()),
|
|
|
|
TimestampNs: uint64(event.Timestamp.UnixNano()),
|
|
|
|
ChanIdIn: event.IncomingChanID.ToUint64(),
|
|
|
|
ChanIdOut: event.OutgoingChanID.ToUint64(),
|
|
|
|
AmtIn: uint64(amtInMsat.ToSatoshis()),
|
|
|
|
AmtOut: uint64(amtOutMsat.ToSatoshis()),
|
|
|
|
Fee: uint64(feeMsat.ToSatoshis()),
|
|
|
|
FeeMsat: uint64(feeMsat),
|
|
|
|
AmtInMsat: uint64(amtInMsat),
|
|
|
|
AmtOutMsat: uint64(amtOutMsat),
|
2018-02-28 07:23:27 +01:00
|
|
|
}
|
2022-10-11 17:02:50 +02:00
|
|
|
|
2022-10-25 11:51:44 +02:00
|
|
|
if req.PeerAliasLookup {
|
2022-10-11 17:02:50 +02:00
|
|
|
aliasIn, err := getRemoteAlias(event.IncomingChanID)
|
|
|
|
if err != nil {
|
2023-03-01 23:38:08 +01:00
|
|
|
aliasIn = fmt.Sprintf("unable to lookup peer "+
|
2022-10-11 17:02:50 +02:00
|
|
|
"alias: %v", err)
|
|
|
|
}
|
|
|
|
aliasOut, err := getRemoteAlias(event.OutgoingChanID)
|
|
|
|
if err != nil {
|
2023-03-01 23:38:08 +01:00
|
|
|
aliasOut = fmt.Sprintf("unable to lookup peer"+
|
2022-10-11 17:02:50 +02:00
|
|
|
"alias: %v", err)
|
|
|
|
}
|
2023-03-01 23:38:08 +01:00
|
|
|
resp.ForwardingEvents[i].PeerAliasIn = aliasIn
|
2022-10-11 17:02:50 +02:00
|
|
|
resp.ForwardingEvents[i].PeerAliasOut = aliasOut
|
|
|
|
}
|
2018-02-28 07:23:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return resp, nil
|
|
|
|
}
|
2018-12-10 05:12:24 +01:00
|
|
|
|
|
|
|
// ExportChannelBackup attempts to return an encrypted static channel backup
|
|
|
|
// for the target channel identified by it channel point. The backup is
|
|
|
|
// encrypted with a key generated from the aezeed seed of the user. The
|
|
|
|
// returned backup can either be restored using the RestoreChannelBackup method
|
|
|
|
// once lnd is running, or via the InitWallet and UnlockWallet methods from the
|
|
|
|
// WalletUnlocker service.
|
|
|
|
func (r *rpcServer) ExportChannelBackup(ctx context.Context,
|
2019-03-11 01:47:06 +01:00
|
|
|
in *lnrpc.ExportChannelBackupRequest) (*lnrpc.ChannelBackup, error) {
|
2018-12-10 05:12:24 +01:00
|
|
|
|
|
|
|
// First, we'll convert the lnrpc channel point into a wire.OutPoint
|
|
|
|
// that we can manipulate.
|
2021-02-13 09:05:33 +01:00
|
|
|
txid, err := lnrpc.GetChanPointFundingTxid(in.ChanPoint)
|
2018-12-10 05:12:24 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
chanPoint := wire.OutPoint{
|
|
|
|
Hash: *txid,
|
2019-03-11 01:47:06 +01:00
|
|
|
Index: in.ChanPoint.OutputIndex,
|
2018-12-10 05:12:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Next, we'll attempt to fetch a channel backup for this channel from
|
|
|
|
// the database. If this channel has been closed, or the outpoint is
|
|
|
|
// unknown, then we'll return an error
|
|
|
|
unpackedBackup, err := chanbackup.FetchBackupForChan(
|
2021-09-21 19:18:16 +02:00
|
|
|
chanPoint, r.server.chanStateDB, r.server.addrSource,
|
2018-12-10 05:12:24 +01:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// At this point, we have an unpacked backup (plaintext) so we'll now
|
|
|
|
// attempt to serialize and encrypt it in order to create a packed
|
|
|
|
// backup.
|
|
|
|
packedBackups, err := chanbackup.PackStaticChanBackups(
|
|
|
|
[]chanbackup.Single{*unpackedBackup},
|
2020-10-06 17:03:42 +02:00
|
|
|
r.server.cc.KeyRing,
|
2018-12-10 05:12:24 +01:00
|
|
|
)
|
|
|
|
if err != nil {
|
2024-02-26 12:19:38 +01:00
|
|
|
return nil, fmt.Errorf("packing of back ups failed: %w", err)
|
2018-12-10 05:12:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Before we proceed, we'll ensure that we received a backup for this
|
|
|
|
// channel, otherwise, we'll bail out.
|
|
|
|
packedBackup, ok := packedBackups[chanPoint]
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("expected single backup for "+
|
|
|
|
"ChannelPoint(%v), got %v", chanPoint,
|
|
|
|
len(packedBackup))
|
|
|
|
}
|
|
|
|
|
|
|
|
return &lnrpc.ChannelBackup{
|
2019-03-11 01:47:06 +01:00
|
|
|
ChanPoint: in.ChanPoint,
|
2018-12-10 05:12:24 +01:00
|
|
|
ChanBackup: packedBackup,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2019-04-05 00:52:31 +02:00
|
|
|
// VerifyChanBackup allows a caller to verify the integrity of a channel backup
|
|
|
|
// snapshot. This method will accept both either a packed Single or a packed
|
|
|
|
// Multi. Specifying both will result in an error.
|
2019-03-11 01:54:49 +01:00
|
|
|
func (r *rpcServer) VerifyChanBackup(ctx context.Context,
|
|
|
|
in *lnrpc.ChanBackupSnapshot) (*lnrpc.VerifyChanBackupResponse, error) {
|
|
|
|
|
2019-04-05 00:52:31 +02:00
|
|
|
switch {
|
|
|
|
// If neither a Single or Multi has been specified, then we have nothing
|
|
|
|
// to verify.
|
|
|
|
case in.GetSingleChanBackups() == nil && in.GetMultiChanBackup() == nil:
|
|
|
|
return nil, errors.New("either a Single or Multi channel " +
|
2019-03-11 01:54:49 +01:00
|
|
|
"backup must be specified")
|
|
|
|
|
2019-04-05 00:52:31 +02:00
|
|
|
// Either a Single or a Multi must be specified, but not both.
|
|
|
|
case in.GetSingleChanBackups() != nil && in.GetMultiChanBackup() != nil:
|
|
|
|
return nil, errors.New("either a Single or Multi channel " +
|
|
|
|
"backup must be specified, but not both")
|
2019-03-11 01:54:49 +01:00
|
|
|
|
2019-04-05 00:52:31 +02:00
|
|
|
// If a Single is specified then we'll only accept one of them to allow
|
|
|
|
// the caller to map the valid/invalid state for each individual Single.
|
|
|
|
case in.GetSingleChanBackups() != nil:
|
|
|
|
chanBackupsProtos := in.GetSingleChanBackups().ChanBackups
|
|
|
|
if len(chanBackupsProtos) != 1 {
|
|
|
|
return nil, errors.New("only one Single is accepted " +
|
|
|
|
"at a time")
|
|
|
|
}
|
2019-03-11 01:54:49 +01:00
|
|
|
|
2019-04-05 00:52:31 +02:00
|
|
|
// First, we'll convert the raw byte slice into a type we can
|
2019-03-11 01:54:49 +01:00
|
|
|
// work with a bit better.
|
|
|
|
chanBackup := chanbackup.PackedSingles(
|
|
|
|
[][]byte{chanBackupsProtos[0].ChanBackup},
|
|
|
|
)
|
|
|
|
|
|
|
|
// With our PackedSingles created, we'll attempt to unpack the
|
2019-04-05 00:52:31 +02:00
|
|
|
// backup. If this fails, then we know the backup is invalid for
|
|
|
|
// some reason.
|
2020-10-06 17:03:42 +02:00
|
|
|
_, err := chanBackup.Unpack(r.server.cc.KeyRing)
|
2019-04-05 00:52:31 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("invalid single channel "+
|
|
|
|
"backup: %v", err)
|
|
|
|
}
|
2019-03-11 01:54:49 +01:00
|
|
|
|
2019-04-05 00:52:31 +02:00
|
|
|
case in.GetMultiChanBackup() != nil:
|
|
|
|
// We'll convert the raw byte slice into a PackedMulti that we
|
|
|
|
// can easily work with.
|
2019-03-11 01:54:49 +01:00
|
|
|
packedMultiBackup := in.GetMultiChanBackup().MultiChanBackup
|
|
|
|
packedMulti := chanbackup.PackedMulti(packedMultiBackup)
|
|
|
|
|
2019-04-05 00:52:31 +02:00
|
|
|
// We'll now attempt to unpack the Multi. If this fails, then we
|
|
|
|
// know it's invalid.
|
2020-10-06 17:03:42 +02:00
|
|
|
_, err := packedMulti.Unpack(r.server.cc.KeyRing)
|
2019-04-05 00:52:31 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("invalid multi channel backup: "+
|
|
|
|
"%v", err)
|
|
|
|
}
|
2019-03-11 01:54:49 +01:00
|
|
|
}
|
|
|
|
|
2019-04-05 00:52:31 +02:00
|
|
|
return &lnrpc.VerifyChanBackupResponse{}, nil
|
2019-03-11 01:54:49 +01:00
|
|
|
}
|
|
|
|
|
2018-12-10 05:12:24 +01:00
|
|
|
// createBackupSnapshot converts the passed Single backup into a snapshot which
|
|
|
|
// contains individual packed single backups, as well as a single packed multi
|
|
|
|
// backup.
|
|
|
|
func (r *rpcServer) createBackupSnapshot(backups []chanbackup.Single) (
|
|
|
|
*lnrpc.ChanBackupSnapshot, error) {
|
|
|
|
|
|
|
|
// Once we have the set of back ups, we'll attempt to pack them all
|
|
|
|
// into a series of single channel backups.
|
|
|
|
singleChanPackedBackups, err := chanbackup.PackStaticChanBackups(
|
2020-10-06 17:03:42 +02:00
|
|
|
backups, r.server.cc.KeyRing,
|
2018-12-10 05:12:24 +01:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("unable to pack set of chan "+
|
|
|
|
"backups: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now that we have our set of single packed backups, we'll morph that
|
|
|
|
// into a form that the proto response requires.
|
|
|
|
numBackups := len(singleChanPackedBackups)
|
|
|
|
singleBackupResp := &lnrpc.ChannelBackups{
|
|
|
|
ChanBackups: make([]*lnrpc.ChannelBackup, 0, numBackups),
|
|
|
|
}
|
|
|
|
for chanPoint, singlePackedBackup := range singleChanPackedBackups {
|
|
|
|
txid := chanPoint.Hash
|
|
|
|
rpcChanPoint := &lnrpc.ChannelPoint{
|
|
|
|
FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
|
|
|
|
FundingTxidBytes: txid[:],
|
|
|
|
},
|
|
|
|
OutputIndex: chanPoint.Index,
|
|
|
|
}
|
|
|
|
|
|
|
|
singleBackupResp.ChanBackups = append(
|
|
|
|
singleBackupResp.ChanBackups,
|
|
|
|
&lnrpc.ChannelBackup{
|
|
|
|
ChanPoint: rpcChanPoint,
|
|
|
|
ChanBackup: singlePackedBackup,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// In addition, to the set of single chan backups, we'll also create a
|
|
|
|
// single multi-channel backup which can be serialized into a single
|
|
|
|
// file for safe storage.
|
|
|
|
var b bytes.Buffer
|
|
|
|
unpackedMultiBackup := chanbackup.Multi{
|
|
|
|
StaticBackups: backups,
|
|
|
|
}
|
2020-10-06 17:03:42 +02:00
|
|
|
err = unpackedMultiBackup.PackToWriter(&b, r.server.cc.KeyRing)
|
2018-12-10 05:12:24 +01:00
|
|
|
if err != nil {
|
2024-02-26 12:19:38 +01:00
|
|
|
return nil, fmt.Errorf("unable to multi-pack backups: %w", err)
|
2018-12-10 05:12:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
multiBackupResp := &lnrpc.MultiChanBackup{
|
|
|
|
MultiChanBackup: b.Bytes(),
|
|
|
|
}
|
|
|
|
for _, singleBackup := range singleBackupResp.ChanBackups {
|
|
|
|
multiBackupResp.ChanPoints = append(
|
|
|
|
multiBackupResp.ChanPoints, singleBackup.ChanPoint,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &lnrpc.ChanBackupSnapshot{
|
|
|
|
SingleChanBackups: singleBackupResp,
|
|
|
|
MultiChanBackup: multiBackupResp,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ExportAllChannelBackups returns static channel backups for all existing
|
|
|
|
// channels known to lnd. A set of regular singular static channel backups for
|
|
|
|
// each channel are returned. Additionally, a multi-channel backup is returned
|
|
|
|
// as well, which contains a single encrypted blob containing the backups of
|
|
|
|
// each channel.
|
|
|
|
func (r *rpcServer) ExportAllChannelBackups(ctx context.Context,
|
|
|
|
in *lnrpc.ChanBackupExportRequest) (*lnrpc.ChanBackupSnapshot, error) {
|
|
|
|
|
|
|
|
// First, we'll attempt to read back ups for ALL currently opened
|
|
|
|
// channels from disk.
|
|
|
|
allUnpackedBackups, err := chanbackup.FetchStaticChanBackups(
|
2021-09-21 19:18:16 +02:00
|
|
|
r.server.chanStateDB, r.server.addrSource,
|
2018-12-10 05:12:24 +01:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("unable to fetch all static chan "+
|
|
|
|
"backups: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// With the backups assembled, we'll create a full snapshot.
|
|
|
|
return r.createBackupSnapshot(allUnpackedBackups)
|
|
|
|
}
|
|
|
|
|
|
|
|
// RestoreChannelBackups accepts a set of singular channel backups, or a single
|
|
|
|
// encrypted multi-chan backup and attempts to recover any funds remaining
|
|
|
|
// within the channel. If we're able to unpack the backup, then the new channel
|
|
|
|
// will be shown under listchannels, as well as pending channels.
|
|
|
|
func (r *rpcServer) RestoreChannelBackups(ctx context.Context,
|
|
|
|
in *lnrpc.RestoreChanBackupRequest) (*lnrpc.RestoreBackupResponse, error) {
|
|
|
|
|
2021-08-17 20:56:09 +02:00
|
|
|
// The server hasn't yet started, so it won't be able to service any of
|
|
|
|
// our requests, so we'll bail early here.
|
|
|
|
if !r.server.Started() {
|
|
|
|
return nil, ErrServerNotActive
|
|
|
|
}
|
|
|
|
|
2018-12-10 05:12:24 +01:00
|
|
|
// First, we'll make our implementation of the
|
|
|
|
// chanbackup.ChannelRestorer interface which we'll use to properly
|
|
|
|
// restore either a set of chanbackup.Single or chanbackup.Multi
|
|
|
|
// backups.
|
|
|
|
chanRestorer := &chanDBRestorer{
|
2021-08-03 09:57:26 +02:00
|
|
|
db: r.server.chanStateDB,
|
2020-10-06 17:03:42 +02:00
|
|
|
secretKeys: r.server.cc.KeyRing,
|
2018-12-10 05:12:24 +01:00
|
|
|
chainArb: r.server.chainArb,
|
|
|
|
}
|
|
|
|
|
|
|
|
// We'll accept either a list of Single backups, or a single Multi
|
|
|
|
// backup which contains several single backups.
|
|
|
|
switch {
|
|
|
|
case in.GetChanBackups() != nil:
|
|
|
|
chanBackupsProtos := in.GetChanBackups()
|
|
|
|
|
|
|
|
// Now that we know what type of backup we're working with,
|
|
|
|
// we'll parse them all out into a more suitable format.
|
|
|
|
packedBackups := make([][]byte, 0, len(chanBackupsProtos.ChanBackups))
|
|
|
|
for _, chanBackup := range chanBackupsProtos.ChanBackups {
|
|
|
|
packedBackups = append(
|
|
|
|
packedBackups, chanBackup.ChanBackup,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// With our backups obtained, we'll now restore them which will
|
|
|
|
// write the new backups to disk, and then attempt to connect
|
|
|
|
// out to any peers that we know of which were our prior
|
|
|
|
// channel peers.
|
|
|
|
err := chanbackup.UnpackAndRecoverSingles(
|
|
|
|
chanbackup.PackedSingles(packedBackups),
|
2020-10-06 17:03:42 +02:00
|
|
|
r.server.cc.KeyRing, chanRestorer, r.server,
|
2018-12-10 05:12:24 +01:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("unable to unpack single "+
|
|
|
|
"backups: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
case in.GetMultiChanBackup() != nil:
|
|
|
|
packedMultiBackup := in.GetMultiChanBackup()
|
|
|
|
|
|
|
|
// With our backups obtained, we'll now restore them which will
|
|
|
|
// write the new backups to disk, and then attempt to connect
|
|
|
|
// out to any peers that we know of which were our prior
|
|
|
|
// channel peers.
|
|
|
|
packedMulti := chanbackup.PackedMulti(packedMultiBackup)
|
|
|
|
err := chanbackup.UnpackAndRecoverMulti(
|
2020-10-06 17:03:42 +02:00
|
|
|
packedMulti, r.server.cc.KeyRing, chanRestorer,
|
2018-12-10 05:12:24 +01:00
|
|
|
r.server,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("unable to unpack chan "+
|
|
|
|
"backup: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return &lnrpc.RestoreBackupResponse{}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// SubscribeChannelBackups allows a client to sub-subscribe to the most up to
|
|
|
|
// date information concerning the state of all channel back ups. Each time a
|
|
|
|
// new channel is added, we return the new set of channels, along with a
|
|
|
|
// multi-chan backup containing the backup info for all channels. Each time a
|
|
|
|
// channel is closed, we send a new update, which contains new new chan back
|
|
|
|
// ups, but the updated set of encrypted multi-chan backups with the closed
|
|
|
|
// channel(s) removed.
|
|
|
|
func (r *rpcServer) SubscribeChannelBackups(req *lnrpc.ChannelBackupSubscription,
|
|
|
|
updateStream lnrpc.Lightning_SubscribeChannelBackupsServer) error {
|
|
|
|
|
2019-02-09 04:44:35 +01:00
|
|
|
// First, we'll subscribe to the primary channel notifier so we can
|
2020-02-12 13:41:48 +01:00
|
|
|
// obtain events for new pending/opened/closed channels.
|
2019-02-09 04:44:35 +01:00
|
|
|
chanSubscription, err := r.server.channelNotifier.SubscribeChannelEvents()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
defer chanSubscription.Cancel()
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
// A new event has been sent by the channel notifier, we'll
|
|
|
|
// assemble, then sling out a new event to the client.
|
|
|
|
case e := <-chanSubscription.Updates():
|
|
|
|
// TODO(roasbeef): batch dispatch ntnfs
|
2018-12-10 05:12:24 +01:00
|
|
|
|
2019-02-09 04:44:35 +01:00
|
|
|
switch e.(type) {
|
2018-12-10 05:12:24 +01:00
|
|
|
|
2019-02-09 04:44:35 +01:00
|
|
|
// We only care about new/closed channels, so we'll
|
2020-02-12 13:41:48 +01:00
|
|
|
// skip any events for active/inactive channels.
|
|
|
|
// To make the subscription behave the same way as the
|
|
|
|
// synchronous call and the file based backup, we also
|
|
|
|
// include pending channels in the update.
|
2019-02-09 04:44:35 +01:00
|
|
|
case channelnotifier.ActiveChannelEvent:
|
|
|
|
continue
|
|
|
|
case channelnotifier.InactiveChannelEvent:
|
|
|
|
continue
|
2020-03-07 04:42:58 +01:00
|
|
|
case channelnotifier.ActiveLinkEvent:
|
|
|
|
continue
|
2022-11-23 23:57:53 +01:00
|
|
|
case channelnotifier.InactiveLinkEvent:
|
|
|
|
continue
|
2019-02-09 04:44:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Now that we know the channel state has changed,
|
|
|
|
// we'll obtains the current set of single channel
|
|
|
|
// backups from disk.
|
|
|
|
chanBackups, err := chanbackup.FetchStaticChanBackups(
|
2021-09-21 19:18:16 +02:00
|
|
|
r.server.chanStateDB, r.server.addrSource,
|
2019-02-09 04:44:35 +01:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to fetch all "+
|
|
|
|
"static chan backups: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// With our backups obtained, we'll pack them into a
|
|
|
|
// snapshot and send them back to the client.
|
|
|
|
backupSnapshot, err := r.createBackupSnapshot(
|
|
|
|
chanBackups,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = updateStream.Send(backupSnapshot)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-09-06 19:08:21 +02:00
|
|
|
// The response stream's context for whatever reason has been
|
|
|
|
// closed. If context is closed by an exceeded deadline we will
|
|
|
|
// return an error.
|
2020-07-18 22:41:11 +02:00
|
|
|
case <-updateStream.Context().Done():
|
2021-09-06 19:08:21 +02:00
|
|
|
if errors.Is(updateStream.Context().Err(), context.Canceled) {
|
|
|
|
return nil
|
|
|
|
}
|
2020-07-18 22:41:11 +02:00
|
|
|
return updateStream.Context().Err()
|
|
|
|
|
2019-02-09 04:44:35 +01:00
|
|
|
case <-r.quit:
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
2018-12-10 05:12:24 +01:00
|
|
|
}
|
2019-08-08 04:14:53 +02:00
|
|
|
|
2019-08-08 04:17:50 +02:00
|
|
|
// ChannelAcceptor dispatches a bi-directional streaming RPC in which
|
|
|
|
// OpenChannel requests are sent to the client and the client responds with
|
|
|
|
// a boolean that tells LND whether or not to accept the channel. This allows
|
|
|
|
// node operators to specify their own criteria for accepting inbound channels
|
|
|
|
// through a single persistent connection.
|
2019-08-08 04:14:53 +02:00
|
|
|
func (r *rpcServer) ChannelAcceptor(stream lnrpc.Lightning_ChannelAcceptorServer) error {
|
2019-08-08 04:17:50 +02:00
|
|
|
chainedAcceptor := r.chanPredicate
|
|
|
|
|
2020-11-09 08:34:49 +01:00
|
|
|
// Create a new RPCAcceptor which will send requests into the
|
|
|
|
// newRequests channel when it receives them.
|
|
|
|
rpcAcceptor := chanacceptor.NewRPCAcceptor(
|
2020-11-09 08:34:52 +01:00
|
|
|
stream.Recv, stream.Send, r.cfg.AcceptorTimeout,
|
|
|
|
r.cfg.ActiveNetParams.Params, r.quit,
|
2020-11-09 08:34:49 +01:00
|
|
|
)
|
2019-08-08 04:17:50 +02:00
|
|
|
|
|
|
|
// Add the RPCAcceptor to the ChainedAcceptor and defer its removal.
|
|
|
|
id := chainedAcceptor.AddAcceptor(rpcAcceptor)
|
|
|
|
defer chainedAcceptor.RemoveAcceptor(id)
|
|
|
|
|
2020-11-09 08:34:49 +01:00
|
|
|
// Run the rpc acceptor, which will accept requests for channel
|
|
|
|
// acceptance decisions from our chained acceptor, send them to the
|
|
|
|
// channel acceptor and listen for and report responses. This function
|
|
|
|
// blocks, and will exit if the rpcserver receives the instruction to
|
|
|
|
// shutdown, or the client cancels.
|
|
|
|
return rpcAcceptor.Run()
|
2019-08-08 04:14:53 +02:00
|
|
|
}
|
2019-10-23 13:28:17 +02:00
|
|
|
|
|
|
|
// BakeMacaroon allows the creation of a new macaroon with custom read and write
|
|
|
|
// permissions. No first-party caveats are added since this can be done offline.
|
2021-05-17 08:13:38 +02:00
|
|
|
// If the --allow-external-permissions flag is set, the RPC will allow
|
|
|
|
// external permissions that LND is not aware of.
|
2019-10-23 13:28:17 +02:00
|
|
|
func (r *rpcServer) BakeMacaroon(ctx context.Context,
|
|
|
|
req *lnrpc.BakeMacaroonRequest) (*lnrpc.BakeMacaroonResponse, error) {
|
|
|
|
|
|
|
|
// If the --no-macaroons flag is used to start lnd, the macaroon service
|
|
|
|
// is not initialized. Therefore we can't bake new macaroons.
|
|
|
|
if r.macService == nil {
|
2020-07-23 18:37:13 +02:00
|
|
|
return nil, errMacaroonDisabled
|
2019-10-23 13:28:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
helpMsg := fmt.Sprintf("supported actions are %v, supported entities "+
|
|
|
|
"are %v", validActions, validEntities)
|
|
|
|
|
|
|
|
// Don't allow empty permission list as it doesn't make sense to have
|
|
|
|
// a macaroon that is not allowed to access any RPC.
|
|
|
|
if len(req.Permissions) == 0 {
|
|
|
|
return nil, fmt.Errorf("permission list cannot be empty. "+
|
|
|
|
"specify at least one action/entity pair. %s", helpMsg)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validate and map permission struct used by gRPC to the one used by
|
2021-05-17 08:13:38 +02:00
|
|
|
// the bakery. If the --allow-external-permissions flag is set, we
|
|
|
|
// will not validate, but map.
|
2019-10-23 13:28:17 +02:00
|
|
|
requestedPermissions := make([]bakery.Op, len(req.Permissions))
|
|
|
|
for idx, op := range req.Permissions {
|
2021-05-17 08:13:38 +02:00
|
|
|
if req.AllowExternalPermissions {
|
|
|
|
requestedPermissions[idx] = bakery.Op{
|
|
|
|
Entity: op.Entity,
|
|
|
|
Action: op.Action,
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2019-10-23 13:28:17 +02:00
|
|
|
if !stringInSlice(op.Entity, validEntities) {
|
|
|
|
return nil, fmt.Errorf("invalid permission entity. %s",
|
|
|
|
helpMsg)
|
|
|
|
}
|
|
|
|
|
2020-09-04 09:22:36 +02:00
|
|
|
// Either we have the special entity "uri" which specifies a
|
|
|
|
// full gRPC URI or we have one of the pre-defined actions.
|
|
|
|
if op.Entity == macaroons.PermissionEntityCustomURI {
|
2020-10-13 11:24:40 +02:00
|
|
|
allPermissions := r.interceptorChain.Permissions()
|
|
|
|
_, ok := allPermissions[op.Action]
|
2020-09-04 09:22:36 +02:00
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("invalid permission " +
|
|
|
|
"action, must be an existing URI in " +
|
|
|
|
"the format /package.Service/" +
|
|
|
|
"MethodName")
|
|
|
|
}
|
|
|
|
} else if !stringInSlice(op.Action, validActions) {
|
|
|
|
return nil, fmt.Errorf("invalid permission action. %s",
|
|
|
|
helpMsg)
|
|
|
|
}
|
|
|
|
|
2019-10-23 13:28:17 +02:00
|
|
|
requestedPermissions[idx] = bakery.Op{
|
|
|
|
Entity: op.Entity,
|
|
|
|
Action: op.Action,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-23 18:37:13 +02:00
|
|
|
// Convert root key id from uint64 to bytes. Because the
|
|
|
|
// DefaultRootKeyID is a digit 0 expressed in a byte slice of a string
|
|
|
|
// "0", we will keep the IDs in the same format - all must be numeric,
|
|
|
|
// and must be a byte slice of string value of the digit, e.g.,
|
|
|
|
// uint64(123) to string(123).
|
2020-07-23 18:36:42 +02:00
|
|
|
rootKeyID := []byte(strconv.FormatUint(req.RootKeyId, 10))
|
|
|
|
|
2019-10-23 13:28:17 +02:00
|
|
|
// Bake new macaroon with the given permissions and send it binary
|
|
|
|
// serialized and hex encoded to the client.
|
2020-07-23 18:36:42 +02:00
|
|
|
newMac, err := r.macService.NewMacaroon(
|
|
|
|
ctx, rootKeyID, requestedPermissions...,
|
2019-10-23 13:28:17 +02:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
newMacBytes, err := newMac.M().MarshalBinary()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
resp := &lnrpc.BakeMacaroonResponse{}
|
|
|
|
resp.Macaroon = hex.EncodeToString(newMacBytes)
|
|
|
|
|
|
|
|
return resp, nil
|
|
|
|
}
|
lnrpc: add ability to provide chan point shims for funding, new funding modifiers
In this commit, we start to expose some of the new external funding
functionality over the RPC interface.
First, we add a new `funding_shim` field to the regular `OpenChannel`
method. This can be used by a caller to express that certain parameters
of the funding flow have already been negotiated outside the protocol,
and should be used instead. For example, a shim can be provided to use a
particular key for the commitment key (ideally cold) rather than use one
this is generated by the wallet as normal, or signal that signing will
be carried out in an interactive manner (PSBT based).
Next, we add a brand new method: `FundingStateStep`. FundingStateStep is
an advanced funding related call that allows the caller to either
execute some preparatory steps for a funding workflow, or manually
progress a funding workflow. The primary way a funding flow is
identified is via its pending channel ID. As an example, this method can
be used to specify that we're expecting a funding flow for a particular
pending channel ID, for which we need to use specific parameters.
Alternatively, this can be used to interactively drive PSBT signing for
funding for partially complete funding transactions.
The new transition methods (funding state machine modifiers) in this
commit allow a party to register a funding intent that should be used
for a specified incoming pending channel ID. The "responder" to the
external channel flow should use this to prep lnd to be able to handle
the channel flow properly.
2019-11-14 05:54:34 +01:00
|
|
|
|
2020-07-23 18:36:42 +02:00
|
|
|
// ListMacaroonIDs returns a list of macaroon root key IDs in use.
|
|
|
|
func (r *rpcServer) ListMacaroonIDs(ctx context.Context,
|
2020-07-23 18:37:13 +02:00
|
|
|
req *lnrpc.ListMacaroonIDsRequest) (
|
|
|
|
*lnrpc.ListMacaroonIDsResponse, error) {
|
2020-07-23 18:36:42 +02:00
|
|
|
|
|
|
|
// If the --no-macaroons flag is used to start lnd, the macaroon service
|
|
|
|
// is not initialized. Therefore we can't show any IDs.
|
|
|
|
if r.macService == nil {
|
2020-07-23 18:37:13 +02:00
|
|
|
return nil, errMacaroonDisabled
|
2020-07-23 18:36:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
rootKeyIDByteSlice, err := r.macService.ListMacaroonIDs(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var rootKeyIDs []uint64
|
|
|
|
for _, value := range rootKeyIDByteSlice {
|
|
|
|
// Convert bytes into uint64.
|
|
|
|
id, err := strconv.ParseUint(string(value), 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
rootKeyIDs = append(rootKeyIDs, id)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &lnrpc.ListMacaroonIDsResponse{RootKeyIds: rootKeyIDs}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// DeleteMacaroonID removes a specific macaroon ID.
|
|
|
|
func (r *rpcServer) DeleteMacaroonID(ctx context.Context,
|
2020-07-23 18:37:13 +02:00
|
|
|
req *lnrpc.DeleteMacaroonIDRequest) (
|
|
|
|
*lnrpc.DeleteMacaroonIDResponse, error) {
|
2020-07-23 18:36:42 +02:00
|
|
|
|
|
|
|
// If the --no-macaroons flag is used to start lnd, the macaroon service
|
2020-07-23 18:37:13 +02:00
|
|
|
// is not initialized. Therefore we can't delete any IDs.
|
2020-07-23 18:36:42 +02:00
|
|
|
if r.macService == nil {
|
2020-07-23 18:37:13 +02:00
|
|
|
return nil, errMacaroonDisabled
|
2020-07-23 18:36:42 +02:00
|
|
|
}
|
|
|
|
|
2020-07-23 18:37:13 +02:00
|
|
|
// Convert root key id from uint64 to bytes. Because the
|
|
|
|
// DefaultRootKeyID is a digit 0 expressed in a byte slice of a string
|
|
|
|
// "0", we will keep the IDs in the same format - all must be digit, and
|
|
|
|
// must be a byte slice of string value of the digit.
|
2020-07-23 18:36:42 +02:00
|
|
|
rootKeyID := []byte(strconv.FormatUint(req.RootKeyId, 10))
|
|
|
|
deletedIDBytes, err := r.macService.DeleteMacaroonID(ctx, rootKeyID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &lnrpc.DeleteMacaroonIDResponse{
|
2020-07-23 18:37:13 +02:00
|
|
|
// If the root key ID doesn't exist, it won't be deleted. We
|
|
|
|
// will return a response with deleted = false, otherwise true.
|
2020-07-23 18:36:42 +02:00
|
|
|
Deleted: deletedIDBytes != nil,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2020-09-04 09:22:38 +02:00
|
|
|
// ListPermissions lists all RPC method URIs and their required macaroon
|
|
|
|
// permissions to access them.
|
|
|
|
func (r *rpcServer) ListPermissions(_ context.Context,
|
|
|
|
_ *lnrpc.ListPermissionsRequest) (*lnrpc.ListPermissionsResponse,
|
|
|
|
error) {
|
|
|
|
|
|
|
|
permissionMap := make(map[string]*lnrpc.MacaroonPermissionList)
|
2020-10-13 11:24:40 +02:00
|
|
|
for uri, perms := range r.interceptorChain.Permissions() {
|
2020-09-04 09:22:38 +02:00
|
|
|
rpcPerms := make([]*lnrpc.MacaroonPermission, len(perms))
|
|
|
|
for idx, perm := range perms {
|
|
|
|
rpcPerms[idx] = &lnrpc.MacaroonPermission{
|
|
|
|
Entity: perm.Entity,
|
|
|
|
Action: perm.Action,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
permissionMap[uri] = &lnrpc.MacaroonPermissionList{
|
|
|
|
Permissions: rpcPerms,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return &lnrpc.ListPermissionsResponse{
|
|
|
|
MethodPermissions: permissionMap,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2021-05-17 08:19:30 +02:00
|
|
|
// CheckMacaroonPermissions checks the caveats and permissions of a macaroon.
|
|
|
|
func (r *rpcServer) CheckMacaroonPermissions(ctx context.Context,
|
|
|
|
req *lnrpc.CheckMacPermRequest) (*lnrpc.CheckMacPermResponse, error) {
|
|
|
|
|
|
|
|
// Turn grpc macaroon permission into bakery.Op for the server to
|
|
|
|
// process.
|
|
|
|
permissions := make([]bakery.Op, len(req.Permissions))
|
|
|
|
for idx, perm := range req.Permissions {
|
|
|
|
permissions[idx] = bakery.Op{
|
|
|
|
Entity: perm.Entity,
|
|
|
|
Action: perm.Action,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
err := r.macService.CheckMacAuth(
|
2021-08-12 16:07:15 +02:00
|
|
|
ctx, req.Macaroon, permissions, req.FullMethod,
|
2021-05-17 08:19:30 +02:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, status.Error(codes.InvalidArgument, err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
return &lnrpc.CheckMacPermResponse{
|
|
|
|
Valid: true,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
lnrpc: add ability to provide chan point shims for funding, new funding modifiers
In this commit, we start to expose some of the new external funding
functionality over the RPC interface.
First, we add a new `funding_shim` field to the regular `OpenChannel`
method. This can be used by a caller to express that certain parameters
of the funding flow have already been negotiated outside the protocol,
and should be used instead. For example, a shim can be provided to use a
particular key for the commitment key (ideally cold) rather than use one
this is generated by the wallet as normal, or signal that signing will
be carried out in an interactive manner (PSBT based).
Next, we add a brand new method: `FundingStateStep`. FundingStateStep is
an advanced funding related call that allows the caller to either
execute some preparatory steps for a funding workflow, or manually
progress a funding workflow. The primary way a funding flow is
identified is via its pending channel ID. As an example, this method can
be used to specify that we're expecting a funding flow for a particular
pending channel ID, for which we need to use specific parameters.
Alternatively, this can be used to interactively drive PSBT signing for
funding for partially complete funding transactions.
The new transition methods (funding state machine modifiers) in this
commit allow a party to register a funding intent that should be used
for a specified incoming pending channel ID. The "responder" to the
external channel flow should use this to prep lnd to be able to handle
the channel flow properly.
2019-11-14 05:54:34 +01:00
|
|
|
// FundingStateStep is an advanced funding related call that allows the caller
|
|
|
|
// to either execute some preparatory steps for a funding workflow, or manually
|
|
|
|
// progress a funding workflow. The primary way a funding flow is identified is
|
|
|
|
// via its pending channel ID. As an example, this method can be used to
|
|
|
|
// specify that we're expecting a funding flow for a particular pending channel
|
|
|
|
// ID, for which we need to use specific parameters. Alternatively, this can
|
|
|
|
// be used to interactively drive PSBT signing for funding for partially
|
|
|
|
// complete funding transactions.
|
2019-11-14 06:00:30 +01:00
|
|
|
func (r *rpcServer) FundingStateStep(ctx context.Context,
|
|
|
|
in *lnrpc.FundingTransitionMsg) (*lnrpc.FundingStateStepResp, error) {
|
lnrpc: add ability to provide chan point shims for funding, new funding modifiers
In this commit, we start to expose some of the new external funding
functionality over the RPC interface.
First, we add a new `funding_shim` field to the regular `OpenChannel`
method. This can be used by a caller to express that certain parameters
of the funding flow have already been negotiated outside the protocol,
and should be used instead. For example, a shim can be provided to use a
particular key for the commitment key (ideally cold) rather than use one
this is generated by the wallet as normal, or signal that signing will
be carried out in an interactive manner (PSBT based).
Next, we add a brand new method: `FundingStateStep`. FundingStateStep is
an advanced funding related call that allows the caller to either
execute some preparatory steps for a funding workflow, or manually
progress a funding workflow. The primary way a funding flow is
identified is via its pending channel ID. As an example, this method can
be used to specify that we're expecting a funding flow for a particular
pending channel ID, for which we need to use specific parameters.
Alternatively, this can be used to interactively drive PSBT signing for
funding for partially complete funding transactions.
The new transition methods (funding state machine modifiers) in this
commit allow a party to register a funding intent that should be used
for a specified incoming pending channel ID. The "responder" to the
external channel flow should use this to prep lnd to be able to handle
the channel flow properly.
2019-11-14 05:54:34 +01:00
|
|
|
|
2020-03-31 09:13:17 +02:00
|
|
|
var pendingChanID [32]byte
|
2019-11-14 06:00:30 +01:00
|
|
|
switch {
|
|
|
|
// If this is a message to register a new shim that is an external
|
|
|
|
// channel point, then we'll contact the wallet to register this new
|
|
|
|
// shim. A user will use this method to register a new channel funding
|
|
|
|
// workflow which has already been partially negotiated outside of the
|
|
|
|
// core protocol.
|
|
|
|
case in.GetShimRegister() != nil &&
|
|
|
|
in.GetShimRegister().GetChanPointShim() != nil:
|
|
|
|
|
|
|
|
rpcShimIntent := in.GetShimRegister().GetChanPointShim()
|
|
|
|
|
|
|
|
// Using the rpc shim as a template, we'll construct a new
|
|
|
|
// chanfunding.Assembler that is able to express proper
|
|
|
|
// formulation of this expected channel.
|
|
|
|
shimAssembler, err := newFundingShimAssembler(
|
2020-10-06 17:03:42 +02:00
|
|
|
rpcShimIntent, false, r.server.cc.KeyRing,
|
2019-11-14 06:00:30 +01:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
req := &chanfunding.Request{
|
|
|
|
RemoteAmt: btcutil.Amount(rpcShimIntent.Amt),
|
|
|
|
}
|
|
|
|
shimIntent, err := shimAssembler.ProvisionChannel(req)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Once we have the intent, we'll register it with the wallet.
|
|
|
|
// Once we receive an incoming funding request that uses this
|
|
|
|
// pending channel ID, then this shim will be dispatched in
|
|
|
|
// place of our regular funding workflow.
|
|
|
|
copy(pendingChanID[:], rpcShimIntent.PendingChanId)
|
2020-10-06 17:03:42 +02:00
|
|
|
err = r.server.cc.Wallet.RegisterFundingIntent(
|
2019-11-14 06:00:30 +01:00
|
|
|
pendingChanID, shimIntent,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-03-31 09:13:17 +02:00
|
|
|
// There is no need to register a PSBT shim before opening the channel,
|
|
|
|
// even though our RPC message structure allows for it. Inform the user
|
|
|
|
// by returning a proper error instead of just doing nothing.
|
|
|
|
case in.GetShimRegister() != nil &&
|
|
|
|
in.GetShimRegister().GetPsbtShim() != nil:
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("PSBT shim must only be sent when " +
|
|
|
|
"opening a channel")
|
|
|
|
|
2019-11-14 06:00:30 +01:00
|
|
|
// If this is a transition to cancel an existing shim, then we'll pass
|
2020-03-31 09:13:17 +02:00
|
|
|
// this message along to the wallet, informing it that the intent no
|
|
|
|
// longer needs to be considered and should be cleaned up.
|
2019-11-14 06:00:30 +01:00
|
|
|
case in.GetShimCancel() != nil:
|
2020-03-31 09:13:17 +02:00
|
|
|
rpcsLog.Debugf("Canceling funding shim for pending_id=%x",
|
|
|
|
in.GetShimCancel().PendingChanId)
|
2019-11-14 06:00:30 +01:00
|
|
|
|
2020-03-31 09:13:17 +02:00
|
|
|
copy(pendingChanID[:], in.GetShimCancel().PendingChanId)
|
2020-10-06 17:03:42 +02:00
|
|
|
err := r.server.cc.Wallet.CancelFundingIntent(pendingChanID)
|
2019-11-14 06:00:30 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-03-31 09:13:17 +02:00
|
|
|
|
|
|
|
// If this is a transition to verify the PSBT for an existing shim,
|
|
|
|
// we'll do so and then store the verified PSBT for later so we can
|
|
|
|
// compare it to the final, signed one.
|
|
|
|
case in.GetPsbtVerify() != nil:
|
|
|
|
rpcsLog.Debugf("Verifying PSBT for pending_id=%x",
|
|
|
|
in.GetPsbtVerify().PendingChanId)
|
|
|
|
|
|
|
|
copy(pendingChanID[:], in.GetPsbtVerify().PendingChanId)
|
|
|
|
packet, err := psbt.NewFromRawBytes(
|
|
|
|
bytes.NewReader(in.GetPsbtVerify().FundedPsbt), false,
|
|
|
|
)
|
|
|
|
if err != nil {
|
2024-02-26 12:19:38 +01:00
|
|
|
return nil, fmt.Errorf("error parsing psbt: %w", err)
|
2020-03-31 09:13:17 +02:00
|
|
|
}
|
|
|
|
|
2020-10-06 17:03:42 +02:00
|
|
|
err = r.server.cc.Wallet.PsbtFundingVerify(
|
2021-06-07 11:16:38 +02:00
|
|
|
pendingChanID, packet, in.GetPsbtVerify().SkipFinalize,
|
2020-03-31 09:13:17 +02:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// If this is a transition to finalize the PSBT funding flow, we compare
|
|
|
|
// the final PSBT to the previously verified one and if nothing
|
|
|
|
// unexpected was changed, continue the channel opening process.
|
|
|
|
case in.GetPsbtFinalize() != nil:
|
2020-09-07 18:02:46 +02:00
|
|
|
msg := in.GetPsbtFinalize()
|
2020-03-31 09:13:17 +02:00
|
|
|
rpcsLog.Debugf("Finalizing PSBT for pending_id=%x",
|
2020-09-07 18:02:46 +02:00
|
|
|
msg.PendingChanId)
|
2020-03-31 09:13:17 +02:00
|
|
|
|
|
|
|
copy(pendingChanID[:], in.GetPsbtFinalize().PendingChanId)
|
2020-09-07 18:02:46 +02:00
|
|
|
|
|
|
|
var (
|
|
|
|
packet *psbt.Packet
|
|
|
|
rawTx *wire.MsgTx
|
|
|
|
err error
|
2020-03-31 09:13:17 +02:00
|
|
|
)
|
2020-09-07 18:02:46 +02:00
|
|
|
|
|
|
|
// Either the signed PSBT or the raw transaction need to be set
|
|
|
|
// but not both at the same time.
|
|
|
|
switch {
|
|
|
|
case len(msg.SignedPsbt) > 0 && len(msg.FinalRawTx) > 0:
|
|
|
|
return nil, fmt.Errorf("cannot set both signed PSBT " +
|
|
|
|
"and final raw TX at the same time")
|
|
|
|
|
|
|
|
case len(msg.SignedPsbt) > 0:
|
|
|
|
packet, err = psbt.NewFromRawBytes(
|
|
|
|
bytes.NewReader(in.GetPsbtFinalize().SignedPsbt),
|
|
|
|
false,
|
|
|
|
)
|
|
|
|
if err != nil {
|
2024-03-07 13:19:28 +01:00
|
|
|
return nil, fmt.Errorf("error parsing psbt: %w",
|
2020-09-07 18:02:46 +02:00
|
|
|
err)
|
|
|
|
}
|
|
|
|
|
|
|
|
case len(msg.FinalRawTx) > 0:
|
|
|
|
rawTx = &wire.MsgTx{}
|
|
|
|
err = rawTx.Deserialize(bytes.NewReader(msg.FinalRawTx))
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("error parsing final "+
|
|
|
|
"raw TX: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("PSBT or raw transaction to " +
|
|
|
|
"finalize missing")
|
2020-03-31 09:13:17 +02:00
|
|
|
}
|
|
|
|
|
2020-10-06 17:03:42 +02:00
|
|
|
err = r.server.cc.Wallet.PsbtFundingFinalize(
|
2020-09-07 18:02:46 +02:00
|
|
|
pendingChanID, packet, rawTx,
|
2020-03-31 09:13:17 +02:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-11-14 06:00:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO(roasbeef): extend PendingChannels to also show shims
|
|
|
|
|
|
|
|
// TODO(roasbeef): return resulting state? also add a method to query
|
|
|
|
// current state?
|
|
|
|
return &lnrpc.FundingStateStepResp{}, nil
|
lnrpc: add ability to provide chan point shims for funding, new funding modifiers
In this commit, we start to expose some of the new external funding
functionality over the RPC interface.
First, we add a new `funding_shim` field to the regular `OpenChannel`
method. This can be used by a caller to express that certain parameters
of the funding flow have already been negotiated outside the protocol,
and should be used instead. For example, a shim can be provided to use a
particular key for the commitment key (ideally cold) rather than use one
this is generated by the wallet as normal, or signal that signing will
be carried out in an interactive manner (PSBT based).
Next, we add a brand new method: `FundingStateStep`. FundingStateStep is
an advanced funding related call that allows the caller to either
execute some preparatory steps for a funding workflow, or manually
progress a funding workflow. The primary way a funding flow is
identified is via its pending channel ID. As an example, this method can
be used to specify that we're expecting a funding flow for a particular
pending channel ID, for which we need to use specific parameters.
Alternatively, this can be used to interactively drive PSBT signing for
funding for partially complete funding transactions.
The new transition methods (funding state machine modifiers) in this
commit allow a party to register a funding intent that should be used
for a specified incoming pending channel ID. The "responder" to the
external channel flow should use this to prep lnd to be able to handle
the channel flow properly.
2019-11-14 05:54:34 +01:00
|
|
|
}
|
2021-08-12 16:07:24 +02:00
|
|
|
|
|
|
|
// RegisterRPCMiddleware adds a new gRPC middleware to the interceptor chain. A
|
|
|
|
// gRPC middleware is software component external to lnd that aims to add
|
|
|
|
// additional business logic to lnd by observing/intercepting/validating
|
|
|
|
// incoming gRPC client requests and (if needed) replacing/overwriting outgoing
|
|
|
|
// messages before they're sent to the client. When registering the middleware
|
|
|
|
// must identify itself and indicate what custom macaroon caveats it wants to
|
|
|
|
// be responsible for. Only requests that contain a macaroon with that specific
|
|
|
|
// custom caveat are then sent to the middleware for inspection. As a security
|
|
|
|
// measure, _no_ middleware can intercept requests made with _unencumbered_
|
|
|
|
// macaroons!
|
|
|
|
func (r *rpcServer) RegisterRPCMiddleware(
|
|
|
|
stream lnrpc.Lightning_RegisterRPCMiddlewareServer) error {
|
|
|
|
|
|
|
|
// This is a security critical functionality and needs to be enabled
|
|
|
|
// specifically by the user.
|
|
|
|
if !r.cfg.RPCMiddleware.Enable {
|
|
|
|
return fmt.Errorf("RPC middleware not enabled in config")
|
|
|
|
}
|
|
|
|
|
|
|
|
// When registering a middleware the first message being sent from the
|
|
|
|
// middleware must be a registration message containing its name and the
|
|
|
|
// custom caveat it wants to register for.
|
|
|
|
var (
|
2022-07-19 18:03:14 +02:00
|
|
|
registerChan = make(chan *lnrpc.MiddlewareRegistration, 1)
|
|
|
|
registerDoneChan = make(chan struct{})
|
|
|
|
errChan = make(chan error, 1)
|
2021-08-12 16:07:24 +02:00
|
|
|
)
|
|
|
|
ctxc, cancel := context.WithTimeout(
|
|
|
|
stream.Context(), r.cfg.RPCMiddleware.InterceptTimeout,
|
|
|
|
)
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
// Read the first message in a goroutine because the Recv method blocks
|
|
|
|
// until the message arrives.
|
|
|
|
go func() {
|
|
|
|
msg, err := stream.Recv()
|
|
|
|
if err != nil {
|
|
|
|
errChan <- err
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
registerChan <- msg.GetRegister()
|
|
|
|
}()
|
|
|
|
|
|
|
|
// Wait for the initial message to arrive or time out if it takes too
|
|
|
|
// long.
|
|
|
|
var registerMsg *lnrpc.MiddlewareRegistration
|
|
|
|
select {
|
|
|
|
case registerMsg = <-registerChan:
|
|
|
|
if registerMsg == nil {
|
|
|
|
return fmt.Errorf("invalid initial middleware " +
|
|
|
|
"registration message")
|
|
|
|
}
|
|
|
|
|
|
|
|
case err := <-errChan:
|
|
|
|
return fmt.Errorf("error receiving initial middleware "+
|
|
|
|
"registration message: %v", err)
|
|
|
|
|
|
|
|
case <-ctxc.Done():
|
|
|
|
return ctxc.Err()
|
|
|
|
|
|
|
|
case <-r.quit:
|
|
|
|
return ErrServerShuttingDown
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure the registration is valid.
|
|
|
|
const nameMinLength = 5
|
|
|
|
if len(registerMsg.MiddlewareName) < nameMinLength {
|
|
|
|
return fmt.Errorf("invalid middleware name, use descriptive "+
|
|
|
|
"name of at least %d characters", nameMinLength)
|
|
|
|
}
|
|
|
|
|
|
|
|
readOnly := registerMsg.ReadOnlyMode
|
|
|
|
caveatName := registerMsg.CustomMacaroonCaveatName
|
|
|
|
switch {
|
|
|
|
case readOnly && len(caveatName) > 0:
|
|
|
|
return fmt.Errorf("cannot set read-only and custom caveat " +
|
|
|
|
"name at the same time")
|
|
|
|
|
|
|
|
case !readOnly && len(caveatName) < nameMinLength:
|
|
|
|
return fmt.Errorf("need to set either custom caveat name "+
|
|
|
|
"of at least %d characters or read-only mode",
|
|
|
|
nameMinLength)
|
|
|
|
}
|
|
|
|
|
|
|
|
middleware := rpcperms.NewMiddlewareHandler(
|
|
|
|
registerMsg.MiddlewareName,
|
|
|
|
caveatName, readOnly, stream.Recv, stream.Send,
|
|
|
|
r.cfg.RPCMiddleware.InterceptTimeout,
|
|
|
|
r.cfg.ActiveNetParams.Params, r.quit,
|
|
|
|
)
|
|
|
|
|
|
|
|
// Add the RPC middleware to the interceptor chain and defer its
|
|
|
|
// removal.
|
|
|
|
if err := r.interceptorChain.RegisterMiddleware(middleware); err != nil {
|
2024-02-26 12:19:38 +01:00
|
|
|
return fmt.Errorf("error registering middleware: %w", err)
|
2021-08-12 16:07:24 +02:00
|
|
|
}
|
|
|
|
defer r.interceptorChain.RemoveMiddleware(registerMsg.MiddlewareName)
|
|
|
|
|
2022-07-19 18:03:14 +02:00
|
|
|
// Send a message to the client to indicate that the registration has
|
|
|
|
// successfully completed.
|
|
|
|
regCompleteMsg := &lnrpc.RPCMiddlewareRequest{
|
|
|
|
InterceptType: &lnrpc.RPCMiddlewareRequest_RegComplete{
|
|
|
|
RegComplete: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send the message in a goroutine because the Send method blocks until
|
|
|
|
// the message is read by the client.
|
|
|
|
go func() {
|
|
|
|
err := stream.Send(regCompleteMsg)
|
|
|
|
if err != nil {
|
|
|
|
errChan <- err
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
close(registerDoneChan)
|
|
|
|
}()
|
|
|
|
|
|
|
|
select {
|
|
|
|
case err := <-errChan:
|
|
|
|
return fmt.Errorf("error sending middleware registration "+
|
|
|
|
"complete message: %v", err)
|
|
|
|
|
|
|
|
case <-ctxc.Done():
|
|
|
|
return ctxc.Err()
|
|
|
|
|
|
|
|
case <-r.quit:
|
|
|
|
return ErrServerShuttingDown
|
|
|
|
|
|
|
|
case <-registerDoneChan:
|
|
|
|
}
|
|
|
|
|
2021-08-12 16:07:24 +02:00
|
|
|
return middleware.Run()
|
|
|
|
}
|
2021-05-31 10:03:47 +02:00
|
|
|
|
|
|
|
// SendCustomMessage sends a custom peer message.
|
|
|
|
func (r *rpcServer) SendCustomMessage(ctx context.Context, req *lnrpc.SendCustomMessageRequest) (
|
|
|
|
*lnrpc.SendCustomMessageResponse, error) {
|
|
|
|
|
|
|
|
peer, err := route.NewVertexFromBytes(req.Peer)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = r.server.SendCustomMessage(
|
|
|
|
peer, lnwire.MessageType(req.Type), req.Data,
|
|
|
|
)
|
|
|
|
switch {
|
|
|
|
case err == ErrPeerNotConnected:
|
|
|
|
return nil, status.Error(codes.NotFound, err.Error())
|
|
|
|
case err != nil:
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &lnrpc.SendCustomMessageResponse{}, nil
|
|
|
|
}
|
2021-05-31 12:06:48 +02:00
|
|
|
|
|
|
|
// SubscribeCustomMessages subscribes to a stream of incoming custom peer
|
|
|
|
// messages.
|
|
|
|
func (r *rpcServer) SubscribeCustomMessages(req *lnrpc.SubscribeCustomMessagesRequest,
|
|
|
|
server lnrpc.Lightning_SubscribeCustomMessagesServer) error {
|
|
|
|
|
|
|
|
client, err := r.server.SubscribeCustomMessages()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer client.Cancel()
|
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-client.Quit():
|
|
|
|
return errors.New("shutdown")
|
|
|
|
|
|
|
|
case <-server.Context().Done():
|
|
|
|
return server.Context().Err()
|
|
|
|
|
|
|
|
case update := <-client.Updates():
|
|
|
|
customMsg := update.(*CustomMessage)
|
|
|
|
|
|
|
|
err := server.Send(&lnrpc.CustomMessage{
|
|
|
|
Peer: customMsg.Peer[:],
|
|
|
|
Data: customMsg.Msg.Data,
|
|
|
|
Type: uint32(customMsg.Msg.Type),
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-01-07 09:38:51 +01:00
|
|
|
|
2022-07-14 19:51:48 +02:00
|
|
|
// ListAliases returns the set of all aliases we have ever allocated along with
|
|
|
|
// their base SCID's and possibly a separate confirmed SCID in the case of
|
|
|
|
// zero-conf.
|
|
|
|
func (r *rpcServer) ListAliases(ctx context.Context,
|
|
|
|
in *lnrpc.ListAliasesRequest) (*lnrpc.ListAliasesResponse, error) {
|
|
|
|
|
|
|
|
// Fetch the map of all aliases.
|
|
|
|
mapAliases := r.server.aliasMgr.ListAliases()
|
|
|
|
|
|
|
|
// Fill out the response. This does not include the zero-conf confirmed
|
|
|
|
// SCID. Doing so would require more database lookups and it can be
|
|
|
|
// cross-referenced with the output of listchannels/closedchannels.
|
|
|
|
resp := &lnrpc.ListAliasesResponse{
|
|
|
|
AliasMaps: make([]*lnrpc.AliasMap, 0),
|
|
|
|
}
|
|
|
|
|
|
|
|
for base, set := range mapAliases {
|
|
|
|
rpcMap := &lnrpc.AliasMap{
|
|
|
|
BaseScid: base.ToUint64(),
|
|
|
|
}
|
|
|
|
for _, alias := range set {
|
|
|
|
rpcMap.Aliases = append(
|
|
|
|
rpcMap.Aliases, alias.ToUint64(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
resp.AliasMaps = append(resp.AliasMaps, rpcMap)
|
|
|
|
}
|
|
|
|
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
2022-01-07 09:38:51 +01:00
|
|
|
// rpcInitiator returns the correct lnrpc initiator for channels where we have
|
|
|
|
// a record of the opening channel.
|
|
|
|
func rpcInitiator(isInitiator bool) lnrpc.Initiator {
|
|
|
|
if isInitiator {
|
|
|
|
return lnrpc.Initiator_INITIATOR_LOCAL
|
|
|
|
}
|
|
|
|
|
|
|
|
return lnrpc.Initiator_INITIATOR_REMOTE
|
|
|
|
}
|