Initial mobilev2 implementation

This commit is contained in:
djkazic 2024-01-15 10:22:29 -05:00
parent 7bd2f1a93c
commit 565b05b227
No known key found for this signature in database
GPG key ID: 511B7BDA931A770F
32 changed files with 12532 additions and 22 deletions

6
.gitignore vendored
View file

@ -8,9 +8,13 @@ litcli-debug
# MacOS junk
.DS_Store
# Files from mobile build.
mobile/build
mobile/*_generated.go
itest/btcd-itest
itest/litd-itest
itest/lnd-itest
itest/itest.test
itest/.logs
itest/*.log
itest/*.log

View file

@ -1,4 +1,5 @@
PKG := github.com/lightninglabs/lightning-terminal
MOBILE_PKG := $(PKG)/mobile
ESCPKG := github.com\/lightninglabs\/lightning-terminal
LND_PKG := github.com/lightningnetwork/lnd
LOOP_PKG := github.com/lightninglabs/loop
@ -13,6 +14,13 @@ TOOLS_DIR := tools
GO_BIN := ${GOPATH}/bin
GOACC_BIN := $(GO_BIN)/go-acc
GOIMPORTS_BIN := $(GO_BIN)/gosimports
GOMOBILE_BIN := GO111MODULE=off $(GO_BIN)/gomobile
MOBILE_BUILD_DIR :=${GOPATH}/src/$(MOBILE_PKG)/build
IOS_BUILD_DIR := $(MOBILE_BUILD_DIR)/ios
IOS_BUILD := $(IOS_BUILD_DIR)/Litdmobile.xcframework
ANDROID_BUILD_DIR := $(MOBILE_BUILD_DIR)/android
ANDROID_BUILD := $(ANDROID_BUILD_DIR)/Litdmobile.aar
COMMIT := $(shell git describe --abbrev=40 --dirty --tags)
COMMIT_HASH := $(shell git rev-parse HEAD)
@ -247,3 +255,36 @@ clean:
$(RM) ./litcli-debug
$(RM) ./litd-debug
$(RM) coverage.txt
# =============
# MOBILE
# =============
mobile-rpc:
@$(call print, "Creating mobile RPC from protos.")
cd ./litrpc; COMPILE_MOBILE=1 SUBSERVER_PREFIX=1 ./gen_protos_docker.sh
vendor:
@$(call print, "Re-creating vendor directory.")
rm -r vendor/; go mod vendor
apple: vendor mobile-rpc
@$(call print, "Building iOS and macOS cxframework ($(IOS_BUILD)).")
mkdir -p $(IOS_BUILD_DIR)
$(GOMOBILE_BIN) bind -target=ios,iossimulator,macos -tags="mobile $(RELEASE_LDFLAGS)" -v -o $(IOS_BUILD) $(MOBILE_PKG)
ios: vendor mobile-rpc
@$(call print, "Building iOS cxframework ($(IOS_BUILD)).")
mkdir -p $(IOS_BUILD_DIR)
$(GOMOBILE_BIN) bind -target=ios,iossimulator -tags="mobile $(RELEASE_LDFLAGS)" -v -o $(IOS_BUILD) $(MOBILE_PKG)
macos: vendor mobile-rpc
@$(call print, "Building macOS cxframework ($(IOS_BUILD)).")
mkdir -p $(IOS_BUILD_DIR)
$(GOMOBILE_BIN) bind -target=macos -tags="mobile $(RELEASE_LDFLAGS)" -v -o $(IOS_BUILD) $(MOBILE_PKG)
android: vendor mobile-rpc
@$(call print, "Building Android library ($(ANDROID_BUILD)).")
mkdir -p $(ANDROID_BUILD_DIR)
$(GOMOBILE_BIN) bind -target=android -androidapi 21 -tags="mobile $(LND_RELEASE_TAGS)" -v -o $(ANDROID_BUILD) $(MOBILE_PKG)
mobile: ios android

View file

@ -11,7 +11,7 @@ import (
// main starts the lightning-terminal application.
func main() {
err := terminal.New().Run()
err := terminal.New(nil, nil).Run()
var flagErr *flags.Error
isFlagErr := errors.As(err, &flagErr)
if err != nil && (!isFlagErr || flagErr.Type != flags.ErrHelp) {

View file

@ -161,8 +161,9 @@ type Config struct {
LetsEncryptDir string `long:"letsencryptdir" description:"The directory where the Let's Encrypt library will store its key and certificate."`
LetsEncryptListen string `long:"letsencryptlisten" description:"The IP:port on which LiT will listen for Let's Encrypt challenges. Let's Encrypt will always try to contact on port 80. Often non-root processes are not allowed to bind to ports lower than 1024. This configuration option allows a different port to be used, but must be used in combination with port forwarding from port 80. This configuration can also be used to specify another IP address to listen on, for example an IPv6 address."`
TLSCertPath string `long:"tlscertpath" description:"Path to write the self signed TLS certificate for LiT's RPC and REST proxy service (if Let's Encrypt is not used). This only applies to the HTTPSListen port."`
TLSKeyPath string `long:"tlskeypath" description:"Path to write the self signed TLS private key for LiT's RPC and REST proxy service (if Let's Encrypt is not used). This only applies to the HTTPSListen port."`
TLSCertPath string `long:"tlscertpath" description:"Path to write the self signed TLS certificate for LiT's RPC and REST proxy service (if Let's Encrypt is not used). This only applies to the HTTPSListen port."`
TLSKeyPath string `long:"tlskeypath" description:"Path to write the self signed TLS private key for LiT's RPC and REST proxy service (if Let's Encrypt is not used). This only applies to the HTTPSListen port."`
TLSDisableAutofill bool `long:"tlsdisableautofill" description:"Do not include the interface IPs or the system hostname in TLS certificate"`
LitDir string `long:"lit-dir" description:"The main directory where LiT looks for its configuration file. If LiT is running in 'remote' lnd mode, this is also the directory where the TLS certificates and log files are stored by default."`
ConfigFile string `long:"configfile" description:"Path to LiT's configuration file."`
@ -826,13 +827,15 @@ func buildTLSConfigForHttp2(config *Config) (*tls.Config, error) {
} else {
tlsCertPath := config.TLSCertPath
tlsKeyPath := config.TLSKeyPath
tlsDisableAutoFill := config.TLSDisableAutofill
if !lnrpc.FileExists(tlsCertPath) &&
!lnrpc.FileExists(tlsKeyPath) {
//TODO(kevin): make this a config option, not a hardcoded flag
certBytes, keyBytes, err := cert.GenCertPair(
defaultSelfSignedCertOrganization, nil, nil,
false, DefaultAutogenValidity,
tlsDisableAutoFill, DefaultAutogenValidity,
)
if err != nil {
return nil, fmt.Errorf("failed creating "+

View file

@ -19,6 +19,7 @@ RUN cd /tmp \
&& go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@${PROTOC_GEN_GO_GRPC_VERSION} \
&& go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@${GRPC_GATEWAY_VERSION} \
&& go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2@${GRPC_GATEWAY_VERSION} \
&& go install golang.org/x/tools/cmd/goimports@v0.1.7 \
&& go install github.com/lightninglabs/falafel@${FALAFEL_VERSION}
WORKDIR /build

View file

@ -59,4 +59,10 @@ popd
pushd autopilotserverrpc
format
generate no-rest
popd
popd
if [[ "$COMPILE_MOBILE" == "1" ]]; then
pushd mobile
./gen_bindings.sh $FALAFEL_VERSION
popd
fi

View file

@ -19,5 +19,7 @@ docker run \
--rm \
--user $UID:$UID \
-e UID=$UID \
-e COMPILE_MOBILE \
-e SUBSERVER_PREFIX \
-v "$DIR/../:/build" \
lit-protobuf-builder

239
mobile/README.md Normal file
View file

@ -0,0 +1,239 @@
# Building mobile libraries
## Prerequisites
To build for iOS, you need to run macOS with either
[Command Line Tools](https://developer.apple.com/download/all/?q=command%20line%20tools)
or [Xcode](https://apps.apple.com/app/xcode/id497799835) installed.
To build for Android, you need either
[Android Studio](https://developer.android.com/studio) or
[Command Line Tools](https://developer.android.com/studio#downloads) installed, which in turn must be used to install [Android NDK](https://developer.android.com/ndk/).
### Go and Go mobile
First, follow the instructions to [install Go](https://github.com/lightningnetwork/lnd/blob/master/docs/INSTALL.md#building-a-development-version-from-source).
Then, install [Go mobile](https://github.com/golang/go/wiki/Mobile) and
initialize it:
```shell
⛰ go install golang.org/x/mobile/cmd/gomobile@latest
⛰ go mod download golang.org/x/mobile
⛰ gomobile init
```
### Docker
Install and run [Docker](https://www.docker.com/products/docker-desktop).
### Make
Check that `make` is available by running the following command without errors:
```shell
⛰ make --version
```
## Building the libraries
Note that `gomobile` only supports building projects from `$GOPATH` at this
point. However, with the introduction of Go modules, the source code files are
no longer installed there by default.
To be able to do so, we must turn off module and using the now deprecated
`go get` command before turning modules back on again.
```shell
⛰ go env -w GO111MODULE="off"
⛰ go get github.com/lightninglabs/lightning-terminal
⛰ go get golang.org/x/mobile/bind
⛰ go env -w GO111MODULE="on"
```
Finally, lets change directory to the newly created lightning-terminal folder inside `$GOPATH`:
```shell
⛰ cd $GOPATH/src/github.com/lightninglabs/lightning-terminal
```
It is not recommended building from the master branch for mainnet. To checkout
the latest tagged release of Lightning Terminal, run
```shell
⛰ git checkout $(git describe --match "v[0-9]*" --abbrev=0)
```
### iOS
```shell
⛰ make ios
```
The Xcode framework file will be found in `mobile/build/ios/Litdmobile.xcframework`.
### Android
```shell
⛰ make android
```
The AAR library file will be found in `mobile/build/android/Litdmobile.aar`.
---
Tip: `make mobile` will build both iOS and Android libraries.
---
## Generating proto definitions
In order to call the methods in the generated library, the serialized proto for
the given RPC call must be provided. Similarly, the response will be a
serialized proto.
### iOS
In order to generate protobuf definitions for iOS, add `--swift_out=.` to the
first `protoc` invocation found in [ `gen_protos.sh` ](../lnrpc/gen_protos.sh).
Then, some changes to [Dockerfile](../lnrpc/Dockerfile) need to be done in
order to use the [Swift protobuf](https://github.com/apple/swift-protobuf)
plugin with protoc:
1. Replace the base image with `FROM swift:focal` so that Swift can be used.
2. `clang-format='1:7.0*'` is unavailable in Ubuntu Focal. Change that to
`clang-format='1:10.0*'`.
3. On the next line, install Go and set the environment variables by adding the
following commands:
```
RUN apt-get install -y wget \
&& wget -c https://golang.org/dl/go1.17.6.linux-amd64.tar.gz -O - \
| tar -xz -C /usr/local
ENV GOPATH=/go
ENV PATH=$PATH:/usr/local/go/bin:/go/bin
```
4. At the end of the file, just above `CMD`, add the following `RUN` command.
This will download and compile the latest tagged release of Swift protobuf.
```
RUN git clone https://github.com/apple/swift-protobuf.git \
&& cd swift-protobuf \
&& git checkout $(git describe --tags --abbrev=0) \
&& swift build -c release \
&& mv .build/release/protoc-gen-swift /bin
```
Finally, run `make rpc`.
Tip: The generated Swift files will be found in various folders. If youd like
to move them to the same folder as the framework file, run
```shell
⛰ find . -name "*.swift" -print0 | xargs -0 -I {} mv {} mobile/build/ios
```
`Litdmobile.xcframework` and all Swift files should now be added to your Xcode
project. You will also need to add [Swift Protobuf](https://github.com/apple/swift-protobuf)
to your project to support the generated code.
### Android
#### First option:
In order to generate protobuf definitions for Android, add `--java_out=.`
to the first `protoc` invocation found in
[ `gen_protos.sh` ](../lnrpc/gen_protos.sh). Then, run `make rpc`.
#### Second option (preferable):
- You have to install the profobuf plugin to your Android application.
Please, follow this link https://github.com/google/protobuf-gradle-plugin.
- Add this line to your `app build.gradle` file.
```shell
classpath "com.google.protobuf:protobuf-gradle-plugin:0.8.17"
```
- Create a `proto` folder under the `main` folder.
![proto_folder](docs/proto_folder.png)
- Add `aar` file to libs folder.
- After that add these lines to your `module's` `build.gradle` file:
```shell
plugins {
id "com.google.protobuf"
}
android {
sourceSets {
main {
proto {
}
}
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "com.google.protobuf:protobuf-javalite:${rootProject.ext.javalite_version}"
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:${rootProject.ext.protoc_version}"
}
generateProtoTasks {
all().each { task ->
task.builtins {
java {
option "lite"
}
}
}
}
}
```
- Then, copy all the proto files from `lnd/lnrpc` to your `proto` folder, saving the structure.
- Build the project and wait until you see the generated Java proto files in the `build` folder.
---
*Note:*
If Android Studio tells you that the `aar` file cannot be included into the `app-bundle`, this is a workaround:
1. Create a separate gradle module
2. Remove everything from there and leave only `aar` and `build.gradle`.
![separate_gradle_module](docs/separate_gradle_module.png)
3. Gradle file should contain only these lines:
```shell
configurations.maybeCreate("default")
artifacts.add("default", file('Litdmobile.aar'))
```
4. In `dependencies` add this line instead of depending on `libs` folder:
```shell
implementation project(":litdmobile", { "default" })
```
---
## Calling the API
In LND v0.15+ all API methods have prefixed the generated methods with the subserver name. This is required to support subservers with name conflicts.
eg. `QueryScores` is now `AutopilotQueryScores`. `GetBlockHeader` is now `NeutrinoKitGetBlockHeader`.
## API docs
- [LND gRPC API Reference](https://api.lightning.community)
- [LND Builders Guide](https://docs.lightning.engineering)

151
mobile/bindings.go Normal file
View file

@ -0,0 +1,151 @@
//go:build mobile
// +build mobile
package litdmobile
import (
"errors"
"fmt"
"os"
"strings"
"sync/atomic"
"github.com/jessevdk/go-flags"
terminal "github.com/lightninglabs/lightning-terminal"
"github.com/lightningnetwork/lnd"
"google.golang.org/grpc"
)
// litdStarted will be used atomically to ensure only a single lnd instance is
// attempted to be started at once.
var litdStarted int32
// Start starts lnd in a new goroutine.
//
// extraArgs can be used to pass command line arguments to lnd that will
// override what is found in the config file. Example:
//
// extraArgs = "--bitcoin.testnet --lnddir=\"/tmp/folder name/\" --profile=5050"
//
// The rpcReady is called lnd is ready to accept RPC calls.
//
// NOTE: On mobile platforms the '--lnddir` argument should be set to the
// current app directory in order to ensure lnd has the permissions needed to
// write to it.
func Start(extraArgs string, rpcReady Callback) {
// We only support a single litd instance at a time (singleton) for now,
// so we make sure to return immediately if it has already been
// started.
if !atomic.CompareAndSwapInt32(&litdStarted, 0, 1) {
err := errors.New("litd already started")
rpcReady.OnError(err)
return
}
// (Re-)initialize the in-mem gRPC listeners we're going to give to lnd.
// This is required each time lnd is started, because when lnd shuts
// down, the in-mem listeners are closed.
RecreateListeners()
var splitArgs []string
for _, a := range strings.Split(extraArgs, "--") {
// Trim any whitespace space, and ignore empty params.
a := strings.TrimSpace(a)
if a == "" {
continue
}
// Finally we prefix any non-empty string with -- to mimic the
// regular command line arguments.
splitArgs = append(splitArgs, "--"+a)
}
// Add the extra arguments to os.Args, as that will be parsed in
// LoadConfig below.
os.Args = append(os.Args, splitArgs...)
var (
rpcListening = make(chan struct{})
term *terminal.LightningTerminal
)
go func() {
term = terminal.New(lightningLis, rpcListening)
err := term.Run()
if err != nil {
if e, ok := err.(*flags.Error); ok &&
e.Type == flags.ErrHelp {
} else {
fmt.Fprintln(os.Stderr, err)
}
rpcReady.OnError(err)
return
}
}()
// By this point, we should be started, call rpcReady
go func() {
select {
case <-rpcListening:
cfg := term.GetConfig()
adminMacCfg := &lnd.Config{
TLSCertPath: cfg.TLSCertPath,
NoMacaroons: false,
AdminMacPath: cfg.Lnd.AdminMacPath,
}
proxyCfg := &lnd.Config{}
swapCfg := &lnd.Config{
TLSCertPath: cfg.TLSCertPath,
AdminMacPath: cfg.Loop.MacaroonPath,
}
poolCfg := &lnd.Config{
TLSCertPath: cfg.TLSCertPath,
AdminMacPath: cfg.Pool.MacaroonPath,
}
tapCfg := &lnd.Config{
TLSCertPath: cfg.TLSCertPath,
AdminMacPath: cfg.TaprootAssets.RpcConf.MacaroonPath,
}
// By default we'll apply the admin auth options, which will include
// macaroons.
setDefaultDialOption(
func() ([]grpc.DialOption, error) {
return lnd.AdminAuthOptions(adminMacCfg, false)
},
)
// For WalletUnlocker, StateService, and Proxy, the macaroons might not be
// available yet when called, so we use a more restricted set of
// options that don't include them.
setWalletUnlockerDialOption(
func() ([]grpc.DialOption, error) {
return lnd.AdminAuthOptions(adminMacCfg, true)
},
)
setStateDialOption(
func() ([]grpc.DialOption, error) {
return lnd.AdminAuthOptions(adminMacCfg, true)
},
)
setProxyDialOption(
func() ([]grpc.DialOption, error) {
return lnd.AdminAuthOptions(proxyCfg, true)
},
)
setSwapClientDialOption(
func() ([]grpc.DialOption, error) {
return lnd.AdminAuthOptions(swapCfg, false)
},
)
setTraderDialOption(
func() ([]grpc.DialOption, error) {
return lnd.AdminAuthOptions(poolCfg, false)
},
)
setTaprootAssetsDialOption(
func() ([]grpc.DialOption, error) {
return lnd.AdminAuthOptions(tapCfg, false)
},
)
}
rpcReady.OnResponse([]byte{})
}()
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

282
mobile/gen_bindings.sh Executable file
View file

@ -0,0 +1,282 @@
#!/bin/sh
mkdir -p build
# Check falafel version.
falafelVersion=$1
if [ -z $falafelVersion ]
then
echo "falafel version not set"
exit 1
fi
falafel=$(which falafel)
if [ $falafel ]
then
version="v$($falafel -v)"
if [ $version != $falafelVersion ]
then
echo "falafel version $falafelVersion required, had $version"
exit 1
fi
echo "Using plugin $falafel $version"
else
echo "falafel not found"
exit 1
fi
# Name of the package for the generated APIs.
pkg="litdmobile"
# The package where the protobuf definitions originally are found.
litd_target_pkg="github.com/lightninglabs/lightning-terminal/litrpc"
lnd_target_pkg="github.com/lightningnetwork/lnd/lnrpc"
autopilot_target_pkg="github.com/lightningnetwork/lnd/lnrpc/autopilotrpc"
chain_target_pkg="github.com/lightningnetwork/lnd/lnrpc/chainrpc"
invoices_target_pkg="github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
neutrino_target_pkg="github.com/lightningnetwork/lnd/lnrpc/neutrinorpc"
router_target_pkg="github.com/lightningnetwork/lnd/lnrpc/routerrpc"
signer_target_pkg="github.com/lightningnetwork/lnd/lnrpc/signrpc"
wallet_target_pkg="github.com/lightningnetwork/lnd/lnrpc/walletrpc"
watchtower_target_pkg="github.com/lightningnetwork/lnd/lnrpc/watchtowerrpc"
watchtower_client_target_pkg="github.com/lightningnetwork/lnd/lnrpc/wtclientrpc"
loop_target_pkg="github.com/lightninglabs/loop/looprpc"
pool_target_pkg="github.com/lightninglabs/pool/poolrpc"
tapd_target_pkg="github.com/lightninglabs/taproot-assets/taprpc"
assetwallet_target_pkg="github.com/lightninglabs/taproot-assets/taprpc/assetwalletrpc"
# A mapping from grpc service to name of the custom listeners. The grpc server
# must be configured to listen on these.
listeners="accounts=lightningLis sessions=lightningLis proxy=lightningLis lightning=lightningLis walletunlocker=lightningLis state=lightningLis autopilot=lightningLis chainnotifier=lightningLis invoices=lightningLis neutrinokit=lightningLis peers=lightningLis router=lightningLis signer=lightningLis versioner=lightningLis walletkit=lightningLis watchtower=lightningLis watchtowerclient=lightningLis swapclient=lightningLis swapserver=lightningLis trader=lightningLis channelauctioneer=lightningLis taprootassets=lightningLis assetwallet=lightningLis"
# Set to 1 to create boiler plate grpc client code and listeners. If more than
# one proto file is being parsed, it should only be done once.
mem_rpc=1
LITD_PROTOS="lit-accounts.proto lit-sessions.proto proxy.proto"
LND_PROTOS="lnd.proto stateservice.proto walletunlocker.proto"
AUTOPILOT_PROTOS="autopilotrpc/autopilot.proto"
CHAIN_PROTOS="chainrpc/chainnotifier.proto"
INVOICES_PROTOS="invoicesrpc/invoices.proto"
NEUTRINO_PROTOS="neutrinorpc/neutrino.proto"
ROUTER_PROTOS="routerrpc/router.proto"
SIGNER_PROTOS="signrpc/signer.proto"
WALLET_PROTOS="walletrpc/walletkit.proto"
WATCHTOWER_PROTOS="watchtowerrpc/watchtower.proto"
WATCHTOWER_CLIENT_PROTOS="wtclientrpc/wtclient.proto"
LOOP_PROTOS="loop.proto looprpc/client.proto swapserverrpc/common.proto"
POOL_PROTOS="trader.proto auctioneerrpc/auctioneer.proto"
TAPD_PROTOS="taprootassets.proto assetwalletrpc/assetwallet.proto"
# If prefix=1 is specified, prefix the generated methods with subserver name.
# This must be enabled to support subservers with name conflicts.
use_prefix="0"
if [ "$SUBSERVER_PREFIX" = "1" ]
then
echo "Prefixing methods with subserver name"
use_prefix="1"
fi
litd_opts="package_name=$pkg,target_package=$litd_target_pkg,api_prefix=$use_prefix,listeners=$listeners,mem_rpc=$mem_rpc"
lnd_opts="package_name=$pkg,target_package=$lnd_target_pkg,api_prefix=0,listeners=$listeners,mem_rpc=$mem_rpc"
autopilot_opts="package_name=$pkg,target_package=$autopilot_target_pkg,api_prefix=$use_prefix,listeners=$listeners,mem_rpc=$mem_rpc"
chain_opts="package_name=$pkg,target_package=$chain_target_pkg,api_prefix=$use_prefix,listeners=$listeners,mem_rpc=$mem_rpc"
invoices_opts="package_name=$pkg,target_package=$invoices_target_pkg,api_prefix=$use_prefix,listeners=$listeners,mem_rpc=$mem_rpc"
neutrino_opts="package_name=$pkg,target_package=$neutrino_target_pkg,api_prefix=$use_prefix,listeners=$listeners,mem_rpc=$mem_rpc"
router_opts="package_name=$pkg,target_package=$router_target_pkg,api_prefix=$use_prefix,listeners=$listeners,mem_rpc=$mem_rpc"
signer_opts="package_name=$pkg,target_package=$signer_target_pkg,api_prefix=$use_prefix,listeners=$listeners,mem_rpc=$mem_rpc"
wallet_opts="package_name=$pkg,target_package=$wallet_target_pkg,api_prefix=$use_prefix,listeners=$listeners,mem_rpc=$mem_rpc"
watchtower_opts="package_name=$pkg,target_package=$watchtower_target_pkg,api_prefix=$use_prefix,listeners=$listeners,mem_rpc=$mem_rpc"
watchtower_client_opts="package_name=$pkg,target_package=$watchtower_client_target_pkg,api_prefix=$use_prefix,listeners=$listeners,mem_rpc=$mem_rpc"
loop_opts="package_name=$pkg,target_package=$loop_target_pkg,api_prefix=$use_prefix,listeners=$listeners,mem_rpc=$mem_rpc"
pool_opts="package_name=$pkg,target_package=$pool_target_pkg,api_prefix=$use_prefix,listeners=$listeners,mem_rpc=$mem_rpc"
auctioneer_opts="package_name=$pkg,target_package=$auctioneer_target_pkg,api_prefix=$use_prefix,listeners=$listeners,mem_rpc=$mem_rpc"
tapd_opts="package_name=$pkg,target_package=$tapd_target_pkg,api_prefix=$use_prefix,listeners=$listeners,mem_rpc=$mem_rpc"
assetwallet_opts="package_name=$pkg,target_package=$assetwallet_target_pkg,api_prefix=$use_prefix,listeners=$listeners,mem_rpc=$mem_rpc"
for file in $LITD_PROTOS; do
echo "Generating mobile protos from ${file}"
protoc -I/usr/local/include -I. \
--plugin=protoc-gen-custom=$falafel\
--custom_out=./build \
--custom_opt="$litd_opts" \
--proto_path=../proto \
"${file}"
done
for file in $LND_PROTOS; do
echo "Generating mobile protos from ${file}"
protoc -I/usr/local/include -I. \
--plugin=protoc-gen-custom=$falafel\
--custom_out=./build \
--custom_opt="$lnd_opts" \
--proto_path=../proto \
"${file}"
done
for file in $AUTOPILOT_PROTOS; do
echo "Generating mobile protos from ${file}"
protoc -I/usr/local/include -I. \
--plugin=protoc-gen-custom=$falafel\
--custom_out=./build \
--custom_opt="$autopilot_opts" \
--proto_path=../proto \
"${file}"
done
for file in $CHAIN_PROTOS; do
echo "Generating mobile protos from ${file}"
protoc -I/usr/local/include -I. \
--plugin=protoc-gen-custom=$falafel\
--custom_out=./build \
--custom_opt="$chain_opts" \
--proto_path=../proto \
"${file}"
done
for file in $INVOICES_PROTOS; do
echo "Generating mobile protos from ${file}"
protoc -I/usr/local/include -I. \
--plugin=protoc-gen-custom=$falafel\
--custom_out=./build \
--custom_opt="$invoices_opts" \
--proto_path=../proto \
"${file}"
done
for file in $NEUTRINO_PROTOS; do
echo "Generating mobile protos from ${file}"
protoc -I/usr/local/include -I. \
--plugin=protoc-gen-custom=$falafel\
--custom_out=./build \
--custom_opt="$neutrino_opts" \
--proto_path=../proto \
"${file}"
done
for file in $ROUTER_PROTOS; do
echo "Generating mobile protos from ${file}"
protoc -I/usr/local/include -I. \
--plugin=protoc-gen-custom=$falafel\
--custom_out=./build \
--custom_opt="$router_opts" \
--proto_path=../proto \
"${file}"
done
for file in $SIGNER_PROTOS; do
echo "Generating mobile protos from ${file}"
protoc -I/usr/local/include -I. \
--plugin=protoc-gen-custom=$falafel\
--custom_out=./build \
--custom_opt="$signer_opts" \
--proto_path=../proto \
"${file}"
done
for file in $WALLET_PROTOS; do
echo "Generating mobile protos from ${file}"
protoc -I/usr/local/include -I. \
--plugin=protoc-gen-custom=$falafel\
--custom_out=./build \
--custom_opt="$wallet_opts" \
--proto_path=../proto \
"${file}"
done
for file in $WATCHTOWER_PROTOS; do
echo "Generating mobile protos from ${file}"
protoc -I/usr/local/include -I. \
--plugin=protoc-gen-custom=$falafel\
--custom_out=./build \
--custom_opt="$watchtower_opts" \
--proto_path=../proto \
"${file}"
done
for file in $WATCHTOWER_CLIENT_PROTOS; do
echo "Generating mobile protos from ${file}"
protoc -I/usr/local/include -I. \
--plugin=protoc-gen-custom=$falafel\
--custom_out=./build \
--custom_opt="$watchtower_client_opts" \
--proto_path=../proto \
"${file}"
done
for file in $LOOP_PROTOS; do
echo "Generating mobile protos from ${file}"
protoc -I/usr/local/include -I. \
--plugin=protoc-gen-custom=$falafel\
--custom_out=./build \
--custom_opt="$loop_opts" \
--proto_path=../proto \
"${file}"
done
for file in $POOL_PROTOS; do
echo "Generating mobile protos from ${file}"
protoc -I/usr/local/include -I. \
--plugin=protoc-gen-custom=$falafel\
--custom_out=./build \
--custom_opt="$pool_opts" \
--proto_path=../proto \
"${file}"
done
# for file in $TAPD_PROTOS; do
echo "Generating mobile protos from taprootassets.proto"
protoc -I/usr/local/include -I. \
--plugin=protoc-gen-custom=$falafel\
--custom_out=./build \
--custom_opt="$tapd_opts" \
--proto_path=../proto \
taprootassets.proto
echo "Generating mobile protos from assetwalletrpc/assetwallet.proto"
protoc -I/usr/local/include -I. \
--plugin=protoc-gen-custom=$falafel\
--custom_out=./build \
--custom_opt="$assetwallet_opts" \
--proto_path=../proto \
assetwalletrpc/assetwallet.proto
# done
# TODO update protoc command to avoid this replace hack
sed -i '12i "github.com/lightningnetwork/lnd/lnrpc/signrpc"' ./walletkit_api_generated.go
sed -i '12i "github.com/lightninglabs/pool/auctioneerrpc"' ./trader_api_generated.go
sed -i '13i "github.com/lightninglabs/taproot-assets/taprpc/assetwalletrpc"' ./assetwallet_api_generated.go
trader_replacements="BatchSnapshotRequest BatchSnapshotsRequest"
for call in $trader_replacements; do
echo "Making replacements for trader_api_generated.go: ${call}"
sed -i "s/poolrpc.${call}/auctioneerrpc.${call}/g" ./trader_api_generated.go
done
echo "Making replacements for channelauctioneer_api_generated.go"
sed -i "s/poolrpc/auctioneerrpc/g" ./channelauctioneer_api_generated.go
# Run goimports to resolve any dependencies among the sub-servers.
goimports -w ./*_generated.go

3
mobile/go.mod Normal file
View file

@ -0,0 +1,3 @@
module mobile
go 1.18

14
mobile/sample_lnd.conf Normal file
View file

@ -0,0 +1,14 @@
[Application Options]
debuglevel=info
maxbackoff=2s
nolisten=1
norest=1
tlsdisableautofill=1
[Routing]
routing.assumechanvalid=1
[Bitcoin]
bitcoin.active=1
bitcoin.testnet=1
bitcoin.node=neutrino

View file

@ -0,0 +1,213 @@
syntax = "proto3";
import "taprootassets.proto";
package assetwalletrpc;
option go_package = "github.com/lightninglabs/taproot-assets/taprpc/assetwalletrpc";
service AssetWallet {
/*
FundVirtualPsbt selects inputs from the available asset commitments to fund
a virtual transaction matching the template.
*/
rpc FundVirtualPsbt (FundVirtualPsbtRequest)
returns (FundVirtualPsbtResponse);
/*
SignVirtualPsbt signs the inputs of a virtual transaction and prepares the
commitments of the inputs and outputs.
*/
rpc SignVirtualPsbt (SignVirtualPsbtRequest)
returns (SignVirtualPsbtResponse);
/*
AnchorVirtualPsbts merges and then commits multiple virtual transactions in
a single BTC level anchor transaction.
TODO(guggero): Actually implement accepting and merging multiple
transactions.
*/
rpc AnchorVirtualPsbts (AnchorVirtualPsbtsRequest)
returns (taprpc.SendAssetResponse);
/*
NextInternalKey derives the next internal key for the given key family and
stores it as an internal key in the database to make sure it is identified
as a local key later on when importing proofs. While an internal key can
also be used as the internal key of a script key, it is recommended to use
the NextScriptKey RPC instead, to make sure the tweaked Taproot output key
is also recognized as a local key.
*/
rpc NextInternalKey (NextInternalKeyRequest)
returns (NextInternalKeyResponse);
/*
NextScriptKey derives the next script key (and its corresponding internal
key) and stores them both in the database to make sure they are identified
as local keys later on when importing proofs.
*/
rpc NextScriptKey (NextScriptKeyRequest) returns (NextScriptKeyResponse);
/* tapcli: `proofs proveownership`
ProveAssetOwnership creates an ownership proof embedded in an asset
transition proof. That ownership proof is a signed virtual transaction
spending the asset with a valid witness to prove the prover owns the keys
that can spend the asset.
*/
rpc ProveAssetOwnership (ProveAssetOwnershipRequest)
returns (ProveAssetOwnershipResponse);
/* tapcli: `proofs verifyownership`
VerifyAssetOwnership verifies the asset ownership proof embedded in the
given transition proof of an asset and returns true if the proof is valid.
*/
rpc VerifyAssetOwnership (VerifyAssetOwnershipRequest)
returns (VerifyAssetOwnershipResponse);
/*
RemoveUTXOLease removes the lease/lock/reservation of the given managed
UTXO.
*/
rpc RemoveUTXOLease (RemoveUTXOLeaseRequest)
returns (RemoveUTXOLeaseResponse);
}
message FundVirtualPsbtRequest {
oneof template {
/*
Use an existing PSBT packet as the template for the funded PSBT.
TODO(guggero): Actually implement this. We can't use the "reserved"
keyword here because we're in a oneof, so we add the field but implement
it later.
*/
bytes psbt = 1;
/*
Use the asset outputs and optional asset inputs from this raw template.
*/
TxTemplate raw = 2;
}
}
message FundVirtualPsbtResponse {
/*
The funded but not yet signed PSBT packet.
*/
bytes funded_psbt = 1;
/*
The index of the added change output or -1 if no change was left over.
*/
int32 change_output_index = 2;
}
message TxTemplate {
/*
An optional list of inputs to use. Every input must be an asset UTXO known
to the wallet. The sum of all inputs must be greater than or equal to the
sum of all outputs.
If no inputs are specified, asset coin selection will be performed instead
and inputs of sufficient value will be added to the resulting PSBT.
*/
repeated PrevId inputs = 1;
/*
A map of all Taproot Asset addresses mapped to the anchor transaction's
output index that should be sent to.
*/
map<string, uint64> recipients = 2;
}
message PrevId {
/*
The bitcoin anchor output on chain that contains the input asset.
*/
taprpc.OutPoint outpoint = 1;
/*
The asset ID of the previous asset tree.
*/
bytes id = 2;
/*
The tweaked Taproot output key committing to the possible spending
conditions of the asset.
*/
bytes script_key = 3;
}
message SignVirtualPsbtRequest {
/*
The PSBT of the virtual transaction that should be signed. The PSBT must
contain all required inputs, outputs, UTXO data and custom fields required
to identify the signing key.
*/
bytes funded_psbt = 1;
}
message SignVirtualPsbtResponse {
/*
The signed virtual transaction in PSBT format.
*/
bytes signed_psbt = 1;
/*
The indices of signed inputs.
*/
repeated uint32 signed_inputs = 2;
}
message AnchorVirtualPsbtsRequest {
/*
The list of virtual transactions that should be merged and committed to in
the BTC level anchor transaction.
*/
repeated bytes virtual_psbts = 1;
}
message NextInternalKeyRequest {
uint32 key_family = 1;
}
message NextInternalKeyResponse {
taprpc.KeyDescriptor internal_key = 1;
}
message NextScriptKeyRequest {
uint32 key_family = 1;
}
message NextScriptKeyResponse {
taprpc.ScriptKey script_key = 1;
}
message ProveAssetOwnershipRequest {
bytes asset_id = 1;
bytes script_key = 2;
taprpc.OutPoint outpoint = 3;
}
message ProveAssetOwnershipResponse {
bytes proof_with_witness = 1;
}
message VerifyAssetOwnershipRequest {
bytes proof_with_witness = 1;
}
message VerifyAssetOwnershipResponse {
bool valid_proof = 1;
}
message RemoveUTXOLeaseRequest {
// The outpoint of the UTXO to remove the lease for.
taprpc.OutPoint outpoint = 1;
}
message RemoveUTXOLeaseResponse {
}

View file

@ -0,0 +1,80 @@
syntax = "proto3";
package autopilotrpc;
option go_package = "github.com/lightningnetwork/lnd/lnrpc/autopilotrpc";
// Autopilot is a service that can be used to get information about the current
// state of the daemon's autopilot agent, and also supply it with information
// that can be used when deciding where to open channels.
service Autopilot {
/*
Status returns whether the daemon's autopilot agent is active.
*/
rpc Status (StatusRequest) returns (StatusResponse);
/*
ModifyStatus is used to modify the status of the autopilot agent, like
enabling or disabling it.
*/
rpc ModifyStatus (ModifyStatusRequest) returns (ModifyStatusResponse);
/*
QueryScores queries all available autopilot heuristics, in addition to any
active combination of these heruristics, for the scores they would give to
the given nodes.
*/
rpc QueryScores (QueryScoresRequest) returns (QueryScoresResponse);
/*
SetScores attempts to set the scores used by the running autopilot agent,
if the external scoring heuristic is enabled.
*/
rpc SetScores (SetScoresRequest) returns (SetScoresResponse);
}
message StatusRequest {
}
message StatusResponse {
// Indicates whether the autopilot is active or not.
bool active = 1;
}
message ModifyStatusRequest {
// Whether the autopilot agent should be enabled or not.
bool enable = 1;
}
message ModifyStatusResponse {
}
message QueryScoresRequest {
repeated string pubkeys = 1;
// If set, we will ignore the local channel state when calculating scores.
bool ignore_local_state = 2;
}
message QueryScoresResponse {
message HeuristicResult {
string heuristic = 1;
map<string, double> scores = 2;
}
repeated HeuristicResult results = 1;
}
message SetScoresRequest {
// The name of the heuristic to provide scores to.
string heuristic = 1;
/*
A map from hex-encoded public keys to scores. Scores must be in the range
[0.0, 1.0].
*/
map<string, double> scores = 2;
}
message SetScoresResponse {
}

View file

@ -0,0 +1,200 @@
syntax = "proto3";
package chainrpc;
option go_package = "github.com/lightningnetwork/lnd/lnrpc/chainrpc";
// ChainNotifier is a service that can be used to get information about the
// chain backend by registering notifiers for chain events.
service ChainNotifier {
/*
RegisterConfirmationsNtfn is a synchronous response-streaming RPC that
registers an intent for a client to be notified once a confirmation request
has reached its required number of confirmations on-chain.
A confirmation request must have a valid output script. It is also possible
to give a transaction ID. If the transaction ID is not set, a notification
is sent once the output script confirms. If the transaction ID is also set,
a notification is sent once the output script confirms in the given
transaction.
*/
rpc RegisterConfirmationsNtfn (ConfRequest) returns (stream ConfEvent);
/*
RegisterSpendNtfn is a synchronous response-streaming RPC that registers an
intent for a client to be notification once a spend request has been spent
by a transaction that has confirmed on-chain.
A client can specify whether the spend request should be for a particular
outpoint or for an output script by specifying a zero outpoint.
*/
rpc RegisterSpendNtfn (SpendRequest) returns (stream SpendEvent);
/*
RegisterBlockEpochNtfn is a synchronous response-streaming RPC that
registers an intent for a client to be notified of blocks in the chain. The
stream will return a hash and height tuple of a block for each new/stale
block in the chain. It is the client's responsibility to determine whether
the tuple returned is for a new or stale block in the chain.
A client can also request a historical backlog of blocks from a particular
point. This allows clients to be idempotent by ensuring that they do not
missing processing a single block within the chain.
*/
rpc RegisterBlockEpochNtfn (BlockEpoch) returns (stream BlockEpoch);
}
message ConfRequest {
/*
The transaction hash for which we should request a confirmation notification
for. If set to a hash of all zeros, then the confirmation notification will
be requested for the script instead.
*/
bytes txid = 1;
/*
An output script within a transaction with the hash above which will be used
by light clients to match block filters. If the transaction hash is set to a
hash of all zeros, then a confirmation notification will be requested for
this script instead.
*/
bytes script = 2;
/*
The number of desired confirmations the transaction/output script should
reach before dispatching a confirmation notification.
*/
uint32 num_confs = 3;
/*
The earliest height in the chain for which the transaction/output script
could have been included in a block. This should in most cases be set to the
broadcast height of the transaction/output script.
*/
uint32 height_hint = 4;
/*
If true, then the block that mines the specified txid/script will be
included in eventual the notification event.
*/
bool include_block = 5;
}
message ConfDetails {
// The raw bytes of the confirmed transaction.
bytes raw_tx = 1;
// The hash of the block in which the confirmed transaction was included in.
bytes block_hash = 2;
// The height of the block in which the confirmed transaction was included
// in.
uint32 block_height = 3;
// The index of the confirmed transaction within the block.
uint32 tx_index = 4;
/*
The raw bytes of the block that mined the transaction. Only included if
include_block was set in the request.
*/
bytes raw_block = 5;
}
message Reorg {
// TODO(wilmer): need to know how the client will use this first.
}
message ConfEvent {
oneof event {
/*
An event that includes the confirmation details of the request
(txid/ouput script).
*/
ConfDetails conf = 1;
/*
An event send when the transaction of the request is reorged out of the
chain.
*/
Reorg reorg = 2;
}
}
message Outpoint {
// The hash of the transaction.
bytes hash = 1;
// The index of the output within the transaction.
uint32 index = 2;
}
message SpendRequest {
/*
The outpoint for which we should request a spend notification for. If set to
a zero outpoint, then the spend notification will be requested for the
script instead. A zero or nil outpoint is not supported for Taproot spends
because the output script cannot reliably be computed from the witness alone
and the spent output script is not always available in the rescan context.
So an outpoint must _always_ be specified when registering a spend
notification for a Taproot output.
*/
Outpoint outpoint = 1;
/*
The output script for the outpoint above. This will be used by light clients
to match block filters. If the outpoint is set to a zero outpoint, then a
spend notification will be requested for this script instead.
*/
bytes script = 2;
/*
The earliest height in the chain for which the outpoint/output script could
have been spent. This should in most cases be set to the broadcast height of
the outpoint/output script.
*/
uint32 height_hint = 3;
// TODO(wilmer): extend to support num confs on spending tx.
}
message SpendDetails {
// The outpoint was that spent.
Outpoint spending_outpoint = 1;
// The raw bytes of the spending transaction.
bytes raw_spending_tx = 2;
// The hash of the spending transaction.
bytes spending_tx_hash = 3;
// The input of the spending transaction that fulfilled the spend request.
uint32 spending_input_index = 4;
// The height at which the spending transaction was included in a block.
uint32 spending_height = 5;
}
message SpendEvent {
oneof event {
/*
An event that includes the details of the spending transaction of the
request (outpoint/output script).
*/
SpendDetails spend = 1;
/*
An event sent when the spending transaction of the request was
reorged out of the chain.
*/
Reorg reorg = 2;
}
}
message BlockEpoch {
// The hash of the block.
bytes hash = 1;
// The height of the block.
uint32 height = 2;
}

View file

@ -13,5 +13,6 @@ docker run \
--rm \
--user $UID:$UID \
-e UID=$UID \
-e SUBSERVER_PREFIX \
-v "$DIR/../:/build" \
lit-protobuf-builder

View file

@ -0,0 +1,175 @@
syntax = "proto3";
import "lightning.proto";
package invoicesrpc;
option go_package = "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc";
// Invoices is a service that can be used to create, accept, settle and cancel
// invoices.
service Invoices {
/*
SubscribeSingleInvoice returns a uni-directional stream (server -> client)
to notify the client of state transitions of the specified invoice.
Initially the current invoice state is always sent out.
*/
rpc SubscribeSingleInvoice (SubscribeSingleInvoiceRequest)
returns (stream lnrpc.Invoice);
/*
CancelInvoice cancels a currently open invoice. If the invoice is already
canceled, this call will succeed. If the invoice is already settled, it will
fail.
*/
rpc CancelInvoice (CancelInvoiceMsg) returns (CancelInvoiceResp);
/*
AddHoldInvoice creates a hold invoice. It ties the invoice to the hash
supplied in the request.
*/
rpc AddHoldInvoice (AddHoldInvoiceRequest) returns (AddHoldInvoiceResp);
/*
SettleInvoice settles an accepted invoice. If the invoice is already
settled, this call will succeed.
*/
rpc SettleInvoice (SettleInvoiceMsg) returns (SettleInvoiceResp);
/*
LookupInvoiceV2 attempts to look up at invoice. An invoice can be refrenced
using either its payment hash, payment address, or set ID.
*/
rpc LookupInvoiceV2 (LookupInvoiceMsg) returns (lnrpc.Invoice);
}
message CancelInvoiceMsg {
// Hash corresponding to the (hold) invoice to cancel. When using
// REST, this field must be encoded as base64.
bytes payment_hash = 1;
}
message CancelInvoiceResp {
}
message AddHoldInvoiceRequest {
/*
An optional memo to attach along with the invoice. Used for record keeping
purposes for the invoice's creator, and will also be set in the description
field of the encoded payment request if the description_hash field is not
being used.
*/
string memo = 1;
// The hash of the preimage
bytes hash = 2;
/*
The value of this invoice in satoshis
The fields value and value_msat are mutually exclusive.
*/
int64 value = 3;
/*
The value of this invoice in millisatoshis
The fields value and value_msat are mutually exclusive.
*/
int64 value_msat = 10;
/*
Hash (SHA-256) of a description of the payment. Used if the description of
payment (memo) is too long to naturally fit within the description field
of an encoded payment request.
*/
bytes description_hash = 4;
// Payment request expiry time in seconds. Default is 86400 (24 hours).
int64 expiry = 5;
// Fallback on-chain address.
string fallback_addr = 6;
// Delta to use for the time-lock of the CLTV extended to the final hop.
uint64 cltv_expiry = 7;
/*
Route hints that can each be individually used to assist in reaching the
invoice's destination.
*/
repeated lnrpc.RouteHint route_hints = 8;
// Whether this invoice should include routing hints for private channels.
bool private = 9;
}
message AddHoldInvoiceResp {
/*
A bare-bones invoice for a payment within the Lightning Network. With the
details of the invoice, the sender has all the data necessary to send a
payment to the recipient.
*/
string payment_request = 1;
/*
The "add" index of this invoice. Each newly created invoice will increment
this index making it monotonically increasing. Callers to the
SubscribeInvoices call can use this to instantly get notified of all added
invoices with an add_index greater than this one.
*/
uint64 add_index = 2;
/*
The payment address of the generated invoice. This value should be used
in all payments for this invoice as we require it for end to end
security.
*/
bytes payment_addr = 3;
}
message SettleInvoiceMsg {
// Externally discovered pre-image that should be used to settle the hold
// invoice.
bytes preimage = 1;
}
message SettleInvoiceResp {
}
message SubscribeSingleInvoiceRequest {
reserved 1;
// Hash corresponding to the (hold) invoice to subscribe to. When using
// REST, this field must be encoded as base64url.
bytes r_hash = 2;
}
enum LookupModifier {
// The default look up modifier, no look up behavior is changed.
DEFAULT = 0;
/*
Indicates that when a look up is done based on a set_id, then only that set
of HTLCs related to that set ID should be returned.
*/
HTLC_SET_ONLY = 1;
/*
Indicates that when a look up is done using a payment_addr, then no HTLCs
related to the payment_addr should be returned. This is useful when one
wants to be able to obtain the set of associated setIDs with a given
invoice, then look up the sub-invoices "projected" by that set ID.
*/
HTLC_SET_BLANK = 2;
}
message LookupInvoiceMsg {
oneof invoice_ref {
// When using REST, this field must be encoded as base64.
bytes payment_hash = 1;
bytes payment_addr = 2;
bytes set_id = 3;
}
LookupModifier lookup_modifier = 4;
}

4957
proto/lightning.proto Normal file

File diff suppressed because it is too large Load diff

1245
proto/looprpc/client.proto Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,228 @@
syntax = "proto3";
package neutrinorpc;
option go_package = "github.com/lightningnetwork/lnd/lnrpc/neutrinorpc";
// NeutrinoKit is a service that can be used to get information about the
// current state of the neutrino instance, fetch blocks and add/remove peers.
service NeutrinoKit {
/*
Status returns the status of the light client neutrino instance,
along with height and hash of the best block, and a list of connected
peers.
*/
rpc Status (StatusRequest) returns (StatusResponse);
/*
AddPeer adds a new peer that has already been connected to the server.
*/
rpc AddPeer (AddPeerRequest) returns (AddPeerResponse);
/*
DisconnectPeer disconnects a peer by target address. Both outbound and
inbound nodes will be searched for the target node. An error message will
be returned if the peer was not found.
*/
rpc DisconnectPeer (DisconnectPeerRequest) returns (DisconnectPeerResponse);
/*
IsBanned returns true if the peer is banned, otherwise false.
*/
rpc IsBanned (IsBannedRequest) returns (IsBannedResponse);
/*
GetBlockHeader returns a block header with a particular block hash.
*/
rpc GetBlockHeader (GetBlockHeaderRequest) returns (GetBlockHeaderResponse);
/*
GetBlock returns a block with a particular block hash.
*/
rpc GetBlock (GetBlockRequest) returns (GetBlockResponse);
/*
GetCFilter returns a compact filter from a block.
*/
rpc GetCFilter (GetCFilterRequest) returns (GetCFilterResponse);
/*
Deprecated, use chainrpc.GetBlockHash instead.
GetBlockHash returns the header hash of a block at a given height.
*/
rpc GetBlockHash (GetBlockHashRequest) returns (GetBlockHashResponse) {
option deprecated = true;
}
}
message StatusRequest {
}
message StatusResponse {
// Indicates whether the neutrino backend is active or not.
bool active = 1;
// Is fully synced.
bool synced = 2;
// Best block height.
int32 block_height = 3;
// Best block hash.
string block_hash = 4;
// Connected peers.
repeated string peers = 5;
}
message AddPeerRequest {
// Peer to add.
string peer_addrs = 1;
}
message AddPeerResponse {
}
message DisconnectPeerRequest {
// Peer to disconnect.
string peer_addrs = 1;
}
message DisconnectPeerResponse {
}
message IsBannedRequest {
// Peer to lookup.
string peer_addrs = 1;
}
message IsBannedResponse {
bool banned = 1;
}
message GetBlockHeaderRequest {
// Block hash in hex notation.
string hash = 1;
}
message GetBlockHeaderResponse {
// The block hash (same as provided).
string hash = 1;
// The number of confirmations.
int64 confirmations = 2;
// The block size excluding witness data.
int64 stripped_size = 3;
// The block size (bytes).
int64 size = 4;
// The block weight as defined in BIP 141.
int64 weight = 5;
// The block height or index.
int32 height = 6;
// The block version.
int32 version = 7;
// The block version.
string version_hex = 8;
// The merkle root.
string merkleroot = 9;
// The block time in seconds since epoch (Jan 1 1970 GMT).
int64 time = 10;
// The nonce.
uint32 nonce = 11;
// The bits in hex notation.
string bits = 12;
// The number of transactions in the block.
int32 ntx = 13;
// The hash of the previous block.
string previous_block_hash = 14;
// The raw hex of the block.
bytes raw_hex = 15;
}
message GetBlockRequest {
// Block hash in hex notation.
string hash = 1;
}
message GetBlockResponse {
// The block hash (same as provided).
string hash = 1;
// The number of confirmations.
int64 confirmations = 2;
// The block size excluding witness data.
int64 stripped_size = 3;
// The block size (bytes).
int64 size = 4;
// The block weight as defined in BIP 141.
int64 weight = 5;
// The block height or index.
int32 height = 6;
// The block version.
int32 version = 7;
// The block version.
string version_hex = 8;
// The merkle root.
string merkleroot = 9;
// List of transaction ids.
repeated string tx = 10;
// The block time in seconds since epoch (Jan 1 1970 GMT).
int64 time = 11;
// The nonce.
uint32 nonce = 12;
// The bits in hex notation.
string bits = 13;
// The number of transactions in the block.
int32 ntx = 14;
// The hash of the previous block.
string previous_block_hash = 15;
// The raw hex of the block.
bytes raw_hex = 16;
}
message GetCFilterRequest {
// Block hash in hex notation.
string hash = 1;
}
message GetCFilterResponse {
// GCS filter.
bytes filter = 1;
}
message GetBlockHashRequest {
// The block height or index.
int32 height = 1;
}
message GetBlockHashResponse {
// The block hash.
string hash = 1;
}

View file

@ -0,0 +1,952 @@
syntax = "proto3";
import "lightning.proto";
package routerrpc;
option go_package = "github.com/lightningnetwork/lnd/lnrpc/routerrpc";
// Router is a service that offers advanced interaction with the router
// subsystem of the daemon.
service Router {
/*
SendPaymentV2 attempts to route a payment described by the passed
PaymentRequest to the final destination. The call returns a stream of
payment updates.
*/
rpc SendPaymentV2 (SendPaymentRequest) returns (stream lnrpc.Payment);
/*
TrackPaymentV2 returns an update stream for the payment identified by the
payment hash.
*/
rpc TrackPaymentV2 (TrackPaymentRequest) returns (stream lnrpc.Payment);
/*
TrackPayments returns an update stream for every payment that is not in a
terminal state. Note that if payments are in-flight while starting a new
subscription, the start of the payment stream could produce out-of-order
and/or duplicate events. In order to get updates for every in-flight
payment attempt make sure to subscribe to this method before initiating any
payments.
*/
rpc TrackPayments (TrackPaymentsRequest) returns (stream lnrpc.Payment);
/*
EstimateRouteFee allows callers to obtain a lower bound w.r.t how much it
may cost to send an HTLC to the target end destination.
*/
rpc EstimateRouteFee (RouteFeeRequest) returns (RouteFeeResponse);
/*
Deprecated, use SendToRouteV2. SendToRoute attempts to make a payment via
the specified route. This method differs from SendPayment in that it
allows users to specify a full route manually. This can be used for
things like rebalancing, and atomic swaps. It differs from the newer
SendToRouteV2 in that it doesn't return the full HTLC information.
*/
rpc SendToRoute (SendToRouteRequest) returns (SendToRouteResponse) {
option deprecated = true;
}
/*
SendToRouteV2 attempts to make a payment via the specified route. This
method differs from SendPayment in that it allows users to specify a full
route manually. This can be used for things like rebalancing, and atomic
swaps.
*/
rpc SendToRouteV2 (SendToRouteRequest) returns (lnrpc.HTLCAttempt);
/*
ResetMissionControl clears all mission control state and starts with a clean
slate.
*/
rpc ResetMissionControl (ResetMissionControlRequest)
returns (ResetMissionControlResponse);
/*
QueryMissionControl exposes the internal mission control state to callers.
It is a development feature.
*/
rpc QueryMissionControl (QueryMissionControlRequest)
returns (QueryMissionControlResponse);
/*
XImportMissionControl is an experimental API that imports the state provided
to the internal mission control's state, using all results which are more
recent than our existing values. These values will only be imported
in-memory, and will not be persisted across restarts.
*/
rpc XImportMissionControl (XImportMissionControlRequest)
returns (XImportMissionControlResponse);
/*
GetMissionControlConfig returns mission control's current config.
*/
rpc GetMissionControlConfig (GetMissionControlConfigRequest)
returns (GetMissionControlConfigResponse);
/*
SetMissionControlConfig will set mission control's config, if the config
provided is valid.
*/
rpc SetMissionControlConfig (SetMissionControlConfigRequest)
returns (SetMissionControlConfigResponse);
/*
Deprecated. QueryProbability returns the current success probability
estimate for a given node pair and amount. The call returns a zero success
probability if no channel is available or if the amount violates min/max
HTLC constraints.
*/
rpc QueryProbability (QueryProbabilityRequest)
returns (QueryProbabilityResponse);
/*
BuildRoute builds a fully specified route based on a list of hop public
keys. It retrieves the relevant channel policies from the graph in order to
calculate the correct fees and time locks.
*/
rpc BuildRoute (BuildRouteRequest) returns (BuildRouteResponse);
/*
SubscribeHtlcEvents creates a uni-directional stream from the server to
the client which delivers a stream of htlc events.
*/
rpc SubscribeHtlcEvents (SubscribeHtlcEventsRequest)
returns (stream HtlcEvent);
/*
Deprecated, use SendPaymentV2. SendPayment attempts to route a payment
described by the passed PaymentRequest to the final destination. The call
returns a stream of payment status updates.
*/
rpc SendPayment (SendPaymentRequest) returns (stream PaymentStatus) {
option deprecated = true;
}
/*
Deprecated, use TrackPaymentV2. TrackPayment returns an update stream for
the payment identified by the payment hash.
*/
rpc TrackPayment (TrackPaymentRequest) returns (stream PaymentStatus) {
option deprecated = true;
}
/**
HtlcInterceptor dispatches a bi-directional streaming RPC in which
Forwarded HTLC requests are sent to the client and the client responds with
a boolean that tells LND if this htlc should be intercepted.
In case of interception, the htlc can be either settled, cancelled or
resumed later by using the ResolveHoldForward endpoint.
*/
rpc HtlcInterceptor (stream ForwardHtlcInterceptResponse)
returns (stream ForwardHtlcInterceptRequest);
/*
UpdateChanStatus attempts to manually set the state of a channel
(enabled, disabled, or auto). A manual "disable" request will cause the
channel to stay disabled until a subsequent manual request of either
"enable" or "auto".
*/
rpc UpdateChanStatus (UpdateChanStatusRequest)
returns (UpdateChanStatusResponse);
}
message SendPaymentRequest {
// The identity pubkey of the payment recipient
bytes dest = 1;
/*
Number of satoshis to send.
The fields amt and amt_msat are mutually exclusive.
*/
int64 amt = 2;
/*
Number of millisatoshis to send.
The fields amt and amt_msat are mutually exclusive.
*/
int64 amt_msat = 12;
// The hash to use within the payment's HTLC
bytes payment_hash = 3;
/*
The CLTV delta from the current height that should be used to set the
timelock for the final hop.
*/
int32 final_cltv_delta = 4;
// An optional payment addr to be included within the last hop of the route.
bytes payment_addr = 20;
/*
A bare-bones invoice for a payment within the Lightning Network. With the
details of the invoice, the sender has all the data necessary to send a
payment to the recipient. The amount in the payment request may be zero. In
that case it is required to set the amt field as well. If no payment request
is specified, the following fields are required: dest, amt and payment_hash.
*/
string payment_request = 5;
/*
An upper limit on the amount of time we should spend when attempting to
fulfill the payment. This is expressed in seconds. If we cannot make a
successful payment within this time frame, an error will be returned.
This field must be non-zero.
*/
int32 timeout_seconds = 6;
/*
The maximum number of satoshis that will be paid as a fee of the payment.
If this field is left to the default value of 0, only zero-fee routes will
be considered. This usually means single hop routes connecting directly to
the destination. To send the payment without a fee limit, use max int here.
The fields fee_limit_sat and fee_limit_msat are mutually exclusive.
*/
int64 fee_limit_sat = 7;
/*
The maximum number of millisatoshis that will be paid as a fee of the
payment. If this field is left to the default value of 0, only zero-fee
routes will be considered. This usually means single hop routes connecting
directly to the destination. To send the payment without a fee limit, use
max int here.
The fields fee_limit_sat and fee_limit_msat are mutually exclusive.
*/
int64 fee_limit_msat = 13;
/*
Deprecated, use outgoing_chan_ids. The channel id of the channel that must
be taken to the first hop. If zero, any channel may be used (unless
outgoing_chan_ids are set).
*/
uint64 outgoing_chan_id = 8 [jstype = JS_STRING, deprecated = true];
/*
The channel ids of the channels are allowed for the first hop. If empty,
any channel may be used.
*/
repeated uint64 outgoing_chan_ids = 19;
/*
The pubkey of the last hop of the route. If empty, any hop may be used.
*/
bytes last_hop_pubkey = 14;
/*
An optional maximum total time lock for the route. This should not exceed
lnd's `--max-cltv-expiry` setting. If zero, then the value of
`--max-cltv-expiry` is enforced.
*/
int32 cltv_limit = 9;
/*
Optional route hints to reach the destination through private channels.
*/
repeated lnrpc.RouteHint route_hints = 10;
/*
An optional field that can be used to pass an arbitrary set of TLV records
to a peer which understands the new records. This can be used to pass
application specific data during the payment attempt. Record types are
required to be in the custom range >= 65536. When using REST, the values
must be encoded as base64.
*/
map<uint64, bytes> dest_custom_records = 11;
// If set, circular payments to self are permitted.
bool allow_self_payment = 15;
/*
Features assumed to be supported by the final node. All transitive feature
dependencies must also be set properly. For a given feature bit pair, either
optional or remote may be set, but not both. If this field is nil or empty,
the router will try to load destination features from the graph as a
fallback.
*/
repeated lnrpc.FeatureBit dest_features = 16;
/*
The maximum number of partial payments that may be use to complete the full
amount.
*/
uint32 max_parts = 17;
/*
If set, only the final payment update is streamed back. Intermediate updates
that show which htlcs are still in flight are suppressed.
*/
bool no_inflight_updates = 18;
/*
The largest payment split that should be attempted when making a payment if
splitting is necessary. Setting this value will effectively cause lnd to
split more aggressively, vs only when it thinks it needs to. Note that this
value is in milli-satoshis.
*/
uint64 max_shard_size_msat = 21;
/*
If set, an AMP-payment will be attempted.
*/
bool amp = 22;
/*
The time preference for this payment. Set to -1 to optimize for fees
only, to 1 to optimize for reliability only or a value inbetween for a mix.
*/
double time_pref = 23;
}
message TrackPaymentRequest {
// The hash of the payment to look up.
bytes payment_hash = 1;
/*
If set, only the final payment update is streamed back. Intermediate updates
that show which htlcs are still in flight are suppressed.
*/
bool no_inflight_updates = 2;
}
message TrackPaymentsRequest {
/*
If set, only the final payment updates are streamed back. Intermediate
updates that show which htlcs are still in flight are suppressed.
*/
bool no_inflight_updates = 1;
}
message RouteFeeRequest {
/*
The destination once wishes to obtain a routing fee quote to.
*/
bytes dest = 1;
/*
The amount one wishes to send to the target destination.
*/
int64 amt_sat = 2;
}
message RouteFeeResponse {
/*
A lower bound of the estimated fee to the target destination within the
network, expressed in milli-satoshis.
*/
int64 routing_fee_msat = 1;
/*
An estimate of the worst case time delay that can occur. Note that callers
will still need to factor in the final CLTV delta of the last hop into this
value.
*/
int64 time_lock_delay = 2;
}
message SendToRouteRequest {
// The payment hash to use for the HTLC.
bytes payment_hash = 1;
// Route that should be used to attempt to complete the payment.
lnrpc.Route route = 2;
/*
Whether the payment should be marked as failed when a temporary error is
returned from the given route. Set it to true so the payment won't be
failed unless a terminal error is occurred, such as payment timeout, no
routes, incorrect payment details, or insufficient funds.
*/
bool skip_temp_err = 3;
}
message SendToRouteResponse {
// The preimage obtained by making the payment.
bytes preimage = 1;
// The failure message in case the payment failed.
lnrpc.Failure failure = 2;
}
message ResetMissionControlRequest {
}
message ResetMissionControlResponse {
}
message QueryMissionControlRequest {
}
// QueryMissionControlResponse contains mission control state.
message QueryMissionControlResponse {
reserved 1;
// Node pair-level mission control state.
repeated PairHistory pairs = 2;
}
message XImportMissionControlRequest {
// Node pair-level mission control state to be imported.
repeated PairHistory pairs = 1;
// Whether to force override MC pair history. Note that even with force
// override the failure pair is imported before the success pair and both
// still clamp existing failure/success amounts.
bool force = 2;
}
message XImportMissionControlResponse {
}
// PairHistory contains the mission control state for a particular node pair.
message PairHistory {
// The source node pubkey of the pair.
bytes node_from = 1;
// The destination node pubkey of the pair.
bytes node_to = 2;
reserved 3, 4, 5, 6;
PairData history = 7;
}
message PairData {
// Time of last failure.
int64 fail_time = 1;
/*
Lowest amount that failed to forward rounded to whole sats. This may be
set to zero if the failure is independent of amount.
*/
int64 fail_amt_sat = 2;
/*
Lowest amount that failed to forward in millisats. This may be
set to zero if the failure is independent of amount.
*/
int64 fail_amt_msat = 4;
reserved 3;
// Time of last success.
int64 success_time = 5;
// Highest amount that we could successfully forward rounded to whole sats.
int64 success_amt_sat = 6;
// Highest amount that we could successfully forward in millisats.
int64 success_amt_msat = 7;
}
message GetMissionControlConfigRequest {
}
message GetMissionControlConfigResponse {
/*
Mission control's currently active config.
*/
MissionControlConfig config = 1;
}
message SetMissionControlConfigRequest {
/*
The config to set for mission control. Note that all values *must* be set,
because the full config will be applied.
*/
MissionControlConfig config = 1;
}
message SetMissionControlConfigResponse {
}
message MissionControlConfig {
/*
Deprecated, use AprioriParameters. The amount of time mission control will
take to restore a penalized node or channel back to 50% success probability,
expressed in seconds. Setting this value to a higher value will penalize
failures for longer, making mission control less likely to route through
nodes and channels that we have previously recorded failures for.
*/
uint64 half_life_seconds = 1 [deprecated = true];
/*
Deprecated, use AprioriParameters. The probability of success mission
control should assign to hop in a route where it has no other information
available. Higher values will make mission control more willing to try hops
that we have no information about, lower values will discourage trying these
hops.
*/
float hop_probability = 2 [deprecated = true];
/*
Deprecated, use AprioriParameters. The importance that mission control
should place on historical results, expressed as a value in [0;1]. Setting
this value to 1 will ignore all historical payments and just use the hop
probability to assess the probability of success for each hop. A zero value
ignores hop probability completely and relies entirely on historical
results, unless none are available.
*/
float weight = 3 [deprecated = true];
/*
The maximum number of payment results that mission control will store.
*/
uint32 maximum_payment_results = 4;
/*
The minimum time that must have passed since the previously recorded failure
before we raise the failure amount.
*/
uint64 minimum_failure_relax_interval = 5;
enum ProbabilityModel {
APRIORI = 0;
BIMODAL = 1;
}
/*
ProbabilityModel defines which probability estimator should be used in
pathfinding. Note that the bimodal estimator is experimental.
*/
ProbabilityModel model = 6;
/*
EstimatorConfig is populated dependent on the estimator type.
*/
oneof EstimatorConfig {
AprioriParameters apriori = 7;
BimodalParameters bimodal = 8;
}
}
message BimodalParameters {
/*
NodeWeight defines how strongly other previous forwardings on channels of a
router should be taken into account when computing a channel's probability
to route. The allowed values are in the range [0, 1], where a value of 0
means that only direct information about a channel is taken into account.
*/
double node_weight = 1;
/*
ScaleMsat describes the scale over which channels statistically have some
liquidity left. The value determines how quickly the bimodal distribution
drops off from the edges of a channel. A larger value (compared to typical
channel capacities) means that the drop off is slow and that channel
balances are distributed more uniformly. A small value leads to the
assumption of very unbalanced channels.
*/
uint64 scale_msat = 2;
/*
DecayTime describes the information decay of knowledge about previous
successes and failures in channels. The smaller the decay time, the quicker
we forget about past forwardings.
*/
uint64 decay_time = 3;
}
message AprioriParameters {
/*
The amount of time mission control will take to restore a penalized node
or channel back to 50% success probability, expressed in seconds. Setting
this value to a higher value will penalize failures for longer, making
mission control less likely to route through nodes and channels that we
have previously recorded failures for.
*/
uint64 half_life_seconds = 1;
/*
The probability of success mission control should assign to hop in a route
where it has no other information available. Higher values will make mission
control more willing to try hops that we have no information about, lower
values will discourage trying these hops.
*/
double hop_probability = 2;
/*
The importance that mission control should place on historical results,
expressed as a value in [0;1]. Setting this value to 1 will ignore all
historical payments and just use the hop probability to assess the
probability of success for each hop. A zero value ignores hop probability
completely and relies entirely on historical results, unless none are
available.
*/
double weight = 3;
/*
The fraction of a channel's capacity that we consider to have liquidity. For
amounts that come close to or exceed the fraction, an additional penalty is
applied. A value of 1.0 disables the capacity factor. Allowed values are in
[0.75, 1.0].
*/
double capacity_fraction = 4;
}
message QueryProbabilityRequest {
// The source node pubkey of the pair.
bytes from_node = 1;
// The destination node pubkey of the pair.
bytes to_node = 2;
// The amount for which to calculate a probability.
int64 amt_msat = 3;
}
message QueryProbabilityResponse {
// The success probability for the requested pair.
double probability = 1;
// The historical data for the requested pair.
PairData history = 2;
}
message BuildRouteRequest {
/*
The amount to send expressed in msat. If set to zero, the minimum routable
amount is used.
*/
int64 amt_msat = 1;
/*
CLTV delta from the current height that should be used for the timelock
of the final hop
*/
int32 final_cltv_delta = 2;
/*
The channel id of the channel that must be taken to the first hop. If zero,
any channel may be used.
*/
uint64 outgoing_chan_id = 3 [jstype = JS_STRING];
/*
A list of hops that defines the route. This does not include the source hop
pubkey.
*/
repeated bytes hop_pubkeys = 4;
// An optional payment addr to be included within the last hop of the route.
bytes payment_addr = 5;
}
message BuildRouteResponse {
/*
Fully specified route that can be used to execute the payment.
*/
lnrpc.Route route = 1;
}
message SubscribeHtlcEventsRequest {
}
/*
HtlcEvent contains the htlc event that was processed. These are served on a
best-effort basis; events are not persisted, delivery is not guaranteed
(in the event of a crash in the switch, forward events may be lost) and
some events may be replayed upon restart. Events consumed from this package
should be de-duplicated by the htlc's unique combination of incoming and
outgoing channel id and htlc id. [EXPERIMENTAL]
*/
message HtlcEvent {
/*
The short channel id that the incoming htlc arrived at our node on. This
value is zero for sends.
*/
uint64 incoming_channel_id = 1;
/*
The short channel id that the outgoing htlc left our node on. This value
is zero for receives.
*/
uint64 outgoing_channel_id = 2;
/*
Incoming id is the index of the incoming htlc in the incoming channel.
This value is zero for sends.
*/
uint64 incoming_htlc_id = 3;
/*
Outgoing id is the index of the outgoing htlc in the outgoing channel.
This value is zero for receives.
*/
uint64 outgoing_htlc_id = 4;
/*
The time in unix nanoseconds that the event occurred.
*/
uint64 timestamp_ns = 5;
enum EventType {
UNKNOWN = 0;
SEND = 1;
RECEIVE = 2;
FORWARD = 3;
}
/*
The event type indicates whether the htlc was part of a send, receive or
forward.
*/
EventType event_type = 6;
oneof event {
ForwardEvent forward_event = 7;
ForwardFailEvent forward_fail_event = 8;
SettleEvent settle_event = 9;
LinkFailEvent link_fail_event = 10;
SubscribedEvent subscribed_event = 11;
FinalHtlcEvent final_htlc_event = 12;
}
}
message HtlcInfo {
// The timelock on the incoming htlc.
uint32 incoming_timelock = 1;
// The timelock on the outgoing htlc.
uint32 outgoing_timelock = 2;
// The amount of the incoming htlc.
uint64 incoming_amt_msat = 3;
// The amount of the outgoing htlc.
uint64 outgoing_amt_msat = 4;
}
message ForwardEvent {
// Info contains details about the htlc that was forwarded.
HtlcInfo info = 1;
}
message ForwardFailEvent {
}
message SettleEvent {
// The revealed preimage.
bytes preimage = 1;
}
message FinalHtlcEvent {
bool settled = 1;
bool offchain = 2;
}
message SubscribedEvent {
}
message LinkFailEvent {
// Info contains details about the htlc that we failed.
HtlcInfo info = 1;
// FailureCode is the BOLT error code for the failure.
lnrpc.Failure.FailureCode wire_failure = 2;
/*
FailureDetail provides additional information about the reason for the
failure. This detail enriches the information provided by the wire message
and may be 'no detail' if the wire message requires no additional metadata.
*/
FailureDetail failure_detail = 3;
// A string representation of the link failure.
string failure_string = 4;
}
enum FailureDetail {
UNKNOWN = 0;
NO_DETAIL = 1;
ONION_DECODE = 2;
LINK_NOT_ELIGIBLE = 3;
ON_CHAIN_TIMEOUT = 4;
HTLC_EXCEEDS_MAX = 5;
INSUFFICIENT_BALANCE = 6;
INCOMPLETE_FORWARD = 7;
HTLC_ADD_FAILED = 8;
FORWARDS_DISABLED = 9;
INVOICE_CANCELED = 10;
INVOICE_UNDERPAID = 11;
INVOICE_EXPIRY_TOO_SOON = 12;
INVOICE_NOT_OPEN = 13;
MPP_INVOICE_TIMEOUT = 14;
ADDRESS_MISMATCH = 15;
SET_TOTAL_MISMATCH = 16;
SET_TOTAL_TOO_LOW = 17;
SET_OVERPAID = 18;
UNKNOWN_INVOICE = 19;
INVALID_KEYSEND = 20;
MPP_IN_PROGRESS = 21;
CIRCULAR_ROUTE = 22;
}
enum PaymentState {
/*
Payment is still in flight.
*/
IN_FLIGHT = 0;
/*
Payment completed successfully.
*/
SUCCEEDED = 1;
/*
There are more routes to try, but the payment timeout was exceeded.
*/
FAILED_TIMEOUT = 2;
/*
All possible routes were tried and failed permanently. Or were no
routes to the destination at all.
*/
FAILED_NO_ROUTE = 3;
/*
A non-recoverable error has occurred.
*/
FAILED_ERROR = 4;
/*
Payment details incorrect (unknown hash, invalid amt or
invalid final cltv delta)
*/
FAILED_INCORRECT_PAYMENT_DETAILS = 5;
/*
Insufficient local balance.
*/
FAILED_INSUFFICIENT_BALANCE = 6;
}
message PaymentStatus {
// Current state the payment is in.
PaymentState state = 1;
/*
The pre-image of the payment when state is SUCCEEDED.
*/
bytes preimage = 2;
reserved 3;
/*
The HTLCs made in attempt to settle the payment [EXPERIMENTAL].
*/
repeated lnrpc.HTLCAttempt htlcs = 4;
}
message CircuitKey {
/// The id of the channel that the is part of this circuit.
uint64 chan_id = 1;
/// The index of the incoming htlc in the incoming channel.
uint64 htlc_id = 2;
}
message ForwardHtlcInterceptRequest {
/*
The key of this forwarded htlc. It defines the incoming channel id and
the index in this channel.
*/
CircuitKey incoming_circuit_key = 1;
// The incoming htlc amount.
uint64 incoming_amount_msat = 5;
// The incoming htlc expiry.
uint32 incoming_expiry = 6;
/*
The htlc payment hash. This value is not guaranteed to be unique per
request.
*/
bytes payment_hash = 2;
// The requested outgoing channel id for this forwarded htlc. Because of
// non-strict forwarding, this isn't necessarily the channel over which the
// packet will be forwarded eventually. A different channel to the same peer
// may be selected as well.
uint64 outgoing_requested_chan_id = 7;
// The outgoing htlc amount.
uint64 outgoing_amount_msat = 3;
// The outgoing htlc expiry.
uint32 outgoing_expiry = 4;
// Any custom records that were present in the payload.
map<uint64, bytes> custom_records = 8;
// The onion blob for the next hop
bytes onion_blob = 9;
// The block height at which this htlc will be auto-failed to prevent the
// channel from force-closing.
int32 auto_fail_height = 10;
}
/**
ForwardHtlcInterceptResponse enables the caller to resolve a previously hold
forward. The caller can choose either to:
- `Resume`: Execute the default behavior (usually forward).
- `Reject`: Fail the htlc backwards.
- `Settle`: Settle this htlc with a given preimage.
*/
message ForwardHtlcInterceptResponse {
/**
The key of this forwarded htlc. It defines the incoming channel id and
the index in this channel.
*/
CircuitKey incoming_circuit_key = 1;
// The resolve action for this intercepted htlc.
ResolveHoldForwardAction action = 2;
// The preimage in case the resolve action is Settle.
bytes preimage = 3;
// Encrypted failure message in case the resolve action is Fail.
//
// If failure_message is specified, the failure_code field must be set
// to zero.
bytes failure_message = 4;
// Return the specified failure code in case the resolve action is Fail. The
// message data fields are populated automatically.
//
// If a non-zero failure_code is specified, failure_message must not be set.
//
// For backwards-compatibility reasons, TEMPORARY_CHANNEL_FAILURE is the
// default value for this field.
lnrpc.Failure.FailureCode failure_code = 5;
}
enum ResolveHoldForwardAction {
SETTLE = 0;
FAIL = 1;
RESUME = 2;
}
message UpdateChanStatusRequest {
lnrpc.ChannelPoint chan_point = 1;
ChanStatusAction action = 2;
}
enum ChanStatusAction {
ENABLE = 0;
DISABLE = 1;
AUTO = 2;
}
message UpdateChanStatusResponse {
}

697
proto/signrpc/signer.proto Normal file
View file

@ -0,0 +1,697 @@
syntax = "proto3";
package signrpc;
option go_package = "github.com/lightningnetwork/lnd/lnrpc/signrpc";
// Signer is a service that gives access to the signing functionality of the
// daemon's wallet.
service Signer {
/*
SignOutputRaw is a method that can be used to generated a signature for a
set of inputs/outputs to a transaction. Each request specifies details
concerning how the outputs should be signed, which keys they should be
signed with, and also any optional tweaks. The return value is a fixed
64-byte signature (the same format as we use on the wire in Lightning).
If we are unable to sign using the specified keys, then an error will be
returned.
*/
rpc SignOutputRaw (SignReq) returns (SignResp);
/*
ComputeInputScript generates a complete InputIndex for the passed
transaction with the signature as defined within the passed SignDescriptor.
This method should be capable of generating the proper input script for both
regular p2wkh/p2tr outputs and p2wkh outputs nested within a regular p2sh
output.
Note that when using this method to sign inputs belonging to the wallet,
the only items of the SignDescriptor that need to be populated are pkScript
in the TxOut field, the value in that same field, and finally the input
index.
*/
rpc ComputeInputScript (SignReq) returns (InputScriptResp);
/*
SignMessage signs a message with the key specified in the key locator. The
returned signature is fixed-size LN wire format encoded.
The main difference to SignMessage in the main RPC is that a specific key is
used to sign the message instead of the node identity private key.
*/
rpc SignMessage (SignMessageReq) returns (SignMessageResp);
/*
VerifyMessage verifies a signature over a message using the public key
provided. The signature must be fixed-size LN wire format encoded.
The main difference to VerifyMessage in the main RPC is that the public key
used to sign the message does not have to be a node known to the network.
*/
rpc VerifyMessage (VerifyMessageReq) returns (VerifyMessageResp);
/*
DeriveSharedKey returns a shared secret key by performing Diffie-Hellman key
derivation between the ephemeral public key in the request and the node's
key specified in the key_desc parameter. Either a key locator or a raw
public key is expected in the key_desc, if neither is supplied, defaults to
the node's identity private key:
P_shared = privKeyNode * ephemeralPubkey
The resulting shared public key is serialized in the compressed format and
hashed with sha256, resulting in the final key length of 256bit.
*/
rpc DeriveSharedKey (SharedKeyRequest) returns (SharedKeyResponse);
/*
MuSig2CombineKeys (experimental!) is a stateless helper RPC that can be used
to calculate the combined MuSig2 public key from a list of all participating
signers' public keys. This RPC is completely stateless and deterministic and
does not create any signing session. It can be used to determine the Taproot
public key that should be put in an on-chain output once all public keys are
known. A signing session is only needed later when that output should be
_spent_ again.
NOTE: The MuSig2 BIP is not final yet and therefore this API must be
considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
releases. Backward compatibility is not guaranteed!
*/
rpc MuSig2CombineKeys (MuSig2CombineKeysRequest)
returns (MuSig2CombineKeysResponse);
/*
MuSig2CreateSession (experimental!) creates a new MuSig2 signing session
using the local key identified by the key locator. The complete list of all
public keys of all signing parties must be provided, including the public
key of the local signing key. If nonces of other parties are already known,
they can be submitted as well to reduce the number of RPC calls necessary
later on.
NOTE: The MuSig2 BIP is not final yet and therefore this API must be
considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
releases. Backward compatibility is not guaranteed!
*/
rpc MuSig2CreateSession (MuSig2SessionRequest)
returns (MuSig2SessionResponse);
/*
MuSig2RegisterNonces (experimental!) registers one or more public nonces of
other signing participants for a session identified by its ID. This RPC can
be called multiple times until all nonces are registered.
NOTE: The MuSig2 BIP is not final yet and therefore this API must be
considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
releases. Backward compatibility is not guaranteed!
*/
rpc MuSig2RegisterNonces (MuSig2RegisterNoncesRequest)
returns (MuSig2RegisterNoncesResponse);
/*
MuSig2Sign (experimental!) creates a partial signature using the local
signing key that was specified when the session was created. This can only
be called when all public nonces of all participants are known and have been
registered with the session. If this node isn't responsible for combining
all the partial signatures, then the cleanup flag should be set, indicating
that the session can be removed from memory once the signature was produced.
NOTE: The MuSig2 BIP is not final yet and therefore this API must be
considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
releases. Backward compatibility is not guaranteed!
*/
rpc MuSig2Sign (MuSig2SignRequest) returns (MuSig2SignResponse);
/*
MuSig2CombineSig (experimental!) combines the given partial signature(s)
with the local one, if it already exists. Once a partial signature of all
participants is registered, the final signature will be combined and
returned.
NOTE: The MuSig2 BIP is not final yet and therefore this API must be
considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
releases. Backward compatibility is not guaranteed!
*/
rpc MuSig2CombineSig (MuSig2CombineSigRequest)
returns (MuSig2CombineSigResponse);
/*
MuSig2Cleanup (experimental!) allows a caller to clean up a session early in
cases where it's obvious that the signing session won't succeed and the
resources can be released.
NOTE: The MuSig2 BIP is not final yet and therefore this API must be
considered to be HIGHLY EXPERIMENTAL and subject to change in upcoming
releases. Backward compatibility is not guaranteed!
*/
rpc MuSig2Cleanup (MuSig2CleanupRequest) returns (MuSig2CleanupResponse);
}
message KeyLocator {
// The family of key being identified.
int32 key_family = 1;
// The precise index of the key being identified.
int32 key_index = 2;
}
message KeyDescriptor {
/*
The raw bytes of the public key in the key pair being identified. Either
this or the KeyLocator must be specified.
*/
bytes raw_key_bytes = 1;
/*
The key locator that identifies which private key to use for signing.
Either this or the raw bytes of the target public key must be specified.
*/
KeyLocator key_loc = 2;
}
message TxOut {
// The value of the output being spent.
int64 value = 1;
// The script of the output being spent.
bytes pk_script = 2;
}
enum SignMethod {
/*
Specifies that a SegWit v0 (p2wkh, np2wkh, p2wsh) input script should be
signed.
*/
SIGN_METHOD_WITNESS_V0 = 0;
/*
Specifies that a SegWit v1 (p2tr) input should be signed by using the
BIP0086 method (commit to internal key only).
*/
SIGN_METHOD_TAPROOT_KEY_SPEND_BIP0086 = 1;
/*
Specifies that a SegWit v1 (p2tr) input should be signed by using a given
taproot hash to commit to in addition to the internal key.
*/
SIGN_METHOD_TAPROOT_KEY_SPEND = 2;
/*
Specifies that a SegWit v1 (p2tr) input should be spent using the script
path and that a specific leaf script should be signed for.
*/
SIGN_METHOD_TAPROOT_SCRIPT_SPEND = 3;
}
message SignDescriptor {
/*
A descriptor that precisely describes *which* key to use for signing. This
may provide the raw public key directly, or require the Signer to re-derive
the key according to the populated derivation path.
Note that if the key descriptor was obtained through walletrpc.DeriveKey,
then the key locator MUST always be provided, since the derived keys are not
persisted unlike with DeriveNextKey.
*/
KeyDescriptor key_desc = 1;
/*
A scalar value that will be added to the private key corresponding to the
above public key to obtain the private key to be used to sign this input.
This value is typically derived via the following computation:
* derivedKey = privkey + sha256(perCommitmentPoint || pubKey) mod N
*/
bytes single_tweak = 2;
/*
A private key that will be used in combination with its corresponding
private key to derive the private key that is to be used to sign the target
input. Within the Lightning protocol, this value is typically the
commitment secret from a previously revoked commitment transaction. This
value is in combination with two hash values, and the original private key
to derive the private key to be used when signing.
* k = (privKey*sha256(pubKey || tweakPub) +
tweakPriv*sha256(tweakPub || pubKey)) mod N
*/
bytes double_tweak = 3;
/*
The 32 byte input to the taproot tweak derivation that is used to derive
the output key from an internal key: outputKey = internalKey +
tagged_hash("tapTweak", internalKey || tapTweak).
When doing a BIP 86 spend, this field can be an empty byte slice.
When doing a normal key path spend, with the output key committing to an
actual script root, then this field should be: the tapscript root hash.
*/
bytes tap_tweak = 10;
/*
The full script required to properly redeem the output. This field will
only be populated if a p2tr, p2wsh or a p2sh output is being signed. If a
taproot script path spend is being attempted, then this should be the raw
leaf script.
*/
bytes witness_script = 4;
/*
A description of the output being spent. The value and script MUST be
provided.
*/
TxOut output = 5;
/*
The target sighash type that should be used when generating the final
sighash, and signature.
*/
uint32 sighash = 7;
/*
The target input within the transaction that should be signed.
*/
int32 input_index = 8;
/*
The sign method specifies how the input should be signed. Depending on the
method, either the tap_tweak, witness_script or both need to be specified.
Defaults to SegWit v0 signing to be backward compatible with older RPC
clients.
*/
SignMethod sign_method = 9;
}
message SignReq {
// The raw bytes of the transaction to be signed.
bytes raw_tx_bytes = 1;
// A set of sign descriptors, for each input to be signed.
repeated SignDescriptor sign_descs = 2;
/*
The full list of UTXO information for each of the inputs being spent. This
is required when spending one or more taproot (SegWit v1) outputs.
*/
repeated TxOut prev_outputs = 3;
}
message SignResp {
/*
A set of signatures realized in a fixed 64-byte format ordered in ascending
input order.
*/
repeated bytes raw_sigs = 1;
}
message InputScript {
// The serializes witness stack for the specified input.
repeated bytes witness = 1;
/*
The optional sig script for the specified witness that will only be set if
the input specified is a nested p2sh witness program.
*/
bytes sig_script = 2;
}
message InputScriptResp {
// The set of fully valid input scripts requested.
repeated InputScript input_scripts = 1;
}
message SignMessageReq {
/*
The message to be signed. When using REST, this field must be encoded as
base64.
*/
bytes msg = 1;
// The key locator that identifies which key to use for signing.
KeyLocator key_loc = 2;
// Double-SHA256 hash instead of just the default single round.
bool double_hash = 3;
/*
Use the compact (pubkey recoverable) format instead of the raw lnwire
format. This option cannot be used with Schnorr signatures.
*/
bool compact_sig = 4;
/*
Use Schnorr signature. This option cannot be used with compact format.
*/
bool schnorr_sig = 5;
/*
The optional Taproot tweak bytes to apply to the private key before creating
a Schnorr signature. The private key is tweaked as described in BIP-341:
privKey + h_tapTweak(internalKey || tapTweak)
*/
bytes schnorr_sig_tap_tweak = 6;
}
message SignMessageResp {
/*
The signature for the given message in the fixed-size LN wire format.
*/
bytes signature = 1;
}
message VerifyMessageReq {
// The message over which the signature is to be verified. When using
// REST, this field must be encoded as base64.
bytes msg = 1;
/*
The fixed-size LN wire encoded signature to be verified over the given
message. When using REST, this field must be encoded as base64.
*/
bytes signature = 2;
/*
The public key the signature has to be valid for. When using REST, this
field must be encoded as base64. If the is_schnorr_sig option is true, then
the public key is expected to be in the 32-byte x-only serialization
according to BIP-340.
*/
bytes pubkey = 3;
/*
Specifies if the signature is a Schnorr signature.
*/
bool is_schnorr_sig = 4;
}
message VerifyMessageResp {
// Whether the signature was valid over the given message.
bool valid = 1;
}
message SharedKeyRequest {
// The ephemeral public key to use for the DH key derivation.
bytes ephemeral_pubkey = 1;
/*
Deprecated. The optional key locator of the local key that should be used.
If this parameter is not set then the node's identity private key will be
used.
*/
KeyLocator key_loc = 2 [deprecated = true];
/*
A key descriptor describes the key used for performing ECDH. Either a key
locator or a raw public key is expected, if neither is supplied, defaults to
the node's identity private key.
*/
KeyDescriptor key_desc = 3;
}
message SharedKeyResponse {
// The shared public key, hashed with sha256.
bytes shared_key = 1;
}
message TweakDesc {
/*
Tweak is the 32-byte value that will modify the public key.
*/
bytes tweak = 1;
/*
Specifies if the target key should be converted to an x-only public key
before tweaking. If true, then the public key will be mapped to an x-only
key before the tweaking operation is applied.
*/
bool is_x_only = 2;
}
message TaprootTweakDesc {
/*
The root hash of the tapscript tree if a script path is committed to. If
the MuSig2 key put on chain doesn't also commit to a script path (BIP-0086
key spend only), then this needs to be empty and the key_spend_only field
below must be set to true. This is required because gRPC cannot
differentiate between a zero-size byte slice and a nil byte slice (both
would be serialized the same way). So the extra boolean is required.
*/
bytes script_root = 1;
/*
Indicates that the above script_root is expected to be empty because this
is a BIP-0086 key spend only commitment where only the internal key is
committed to instead of also including a script root hash.
*/
bool key_spend_only = 2;
}
enum MuSig2Version {
/*
The default value on the RPC is zero for enums so we need to represent an
invalid/undefined version by default to make sure clients upgrade their
software to set the version explicitly.
*/
MUSIG2_VERSION_UNDEFINED = 0;
/*
The version of MuSig2 that lnd 0.15.x shipped with, which corresponds to the
version v0.4.0 of the MuSig2 BIP draft.
*/
MUSIG2_VERSION_V040 = 1;
/*
The current version of MuSig2 which corresponds to the version v1.0.0rc2 of
the MuSig2 BIP draft.
*/
MUSIG2_VERSION_V100RC2 = 2;
}
message MuSig2CombineKeysRequest {
/*
A list of all public keys (serialized in 32-byte x-only format for v0.4.0
and 33-byte compressed format for v1.0.0rc2!) participating in the signing
session. The list will always be sorted lexicographically internally. This
must include the local key which is described by the above key_loc.
*/
repeated bytes all_signer_pubkeys = 1;
/*
A series of optional generic tweaks to be applied to the the aggregated
public key.
*/
repeated TweakDesc tweaks = 2;
/*
An optional taproot specific tweak that must be specified if the MuSig2
combined key will be used as the main taproot key of a taproot output
on-chain.
*/
TaprootTweakDesc taproot_tweak = 3;
/*
The mandatory version of the MuSig2 BIP draft to use. This is necessary to
differentiate between the changes that were made to the BIP while this
experimental RPC was already released. Some of those changes affect how the
combined key and nonces are created.
*/
MuSig2Version version = 4;
}
message MuSig2CombineKeysResponse {
/*
The combined public key (in the 32-byte x-only format) with all tweaks
applied to it. If a taproot tweak is specified, this corresponds to the
taproot key that can be put into the on-chain output.
*/
bytes combined_key = 1;
/*
The raw combined public key (in the 32-byte x-only format) before any tweaks
are applied to it. If a taproot tweak is specified, this corresponds to the
internal key that needs to be put into the witness if the script spend path
is used.
*/
bytes taproot_internal_key = 2;
/*
The version of the MuSig2 BIP that was used to combine the keys.
*/
MuSig2Version version = 4;
}
message MuSig2SessionRequest {
/*
The key locator that identifies which key to use for signing.
*/
KeyLocator key_loc = 1;
/*
A list of all public keys (serialized in 32-byte x-only format for v0.4.0
and 33-byte compressed format for v1.0.0rc2!) participating in the signing
session. The list will always be sorted lexicographically internally. This
must include the local key which is described by the above key_loc.
*/
repeated bytes all_signer_pubkeys = 2;
/*
An optional list of all public nonces of other signing participants that
might already be known.
*/
repeated bytes other_signer_public_nonces = 3;
/*
A series of optional generic tweaks to be applied to the the aggregated
public key.
*/
repeated TweakDesc tweaks = 4;
/*
An optional taproot specific tweak that must be specified if the MuSig2
combined key will be used as the main taproot key of a taproot output
on-chain.
*/
TaprootTweakDesc taproot_tweak = 5;
/*
The mandatory version of the MuSig2 BIP draft to use. This is necessary to
differentiate between the changes that were made to the BIP while this
experimental RPC was already released. Some of those changes affect how the
combined key and nonces are created.
*/
MuSig2Version version = 6;
/*
A set of pre generated secret local nonces to use in the musig2 session.
This field is optional. This can be useful for protocols that need to send
nonces ahead of time before the set of signer keys are known. This value
MUST be 97 bytes and be the concatenation of two CSPRNG generated 32 byte
values and local public key used for signing as specified in the key_loc
field.
*/
bytes pregenerated_local_nonce = 7;
}
message MuSig2SessionResponse {
/*
The unique ID that represents this signing session. A session can be used
for producing a signature a single time. If the signing fails for any
reason, a new session with the same participants needs to be created.
*/
bytes session_id = 1;
/*
The combined public key (in the 32-byte x-only format) with all tweaks
applied to it. If a taproot tweak is specified, this corresponds to the
taproot key that can be put into the on-chain output.
*/
bytes combined_key = 2;
/*
The raw combined public key (in the 32-byte x-only format) before any tweaks
are applied to it. If a taproot tweak is specified, this corresponds to the
internal key that needs to be put into the witness if the script spend path
is used.
*/
bytes taproot_internal_key = 3;
/*
The two public nonces the local signer uses, combined into a single value
of 66 bytes. Can be split into the two 33-byte points to get the individual
nonces.
*/
bytes local_public_nonces = 4;
/*
Indicates whether all nonces required to start the signing process are known
now.
*/
bool have_all_nonces = 5;
/*
The version of the MuSig2 BIP that was used to create the session.
*/
MuSig2Version version = 6;
}
message MuSig2RegisterNoncesRequest {
/*
The unique ID of the signing session those nonces should be registered with.
*/
bytes session_id = 1;
/*
A list of all public nonces of other signing participants that should be
registered.
*/
repeated bytes other_signer_public_nonces = 3;
}
message MuSig2RegisterNoncesResponse {
/*
Indicates whether all nonces required to start the signing process are known
now.
*/
bool have_all_nonces = 1;
}
message MuSig2SignRequest {
/*
The unique ID of the signing session to use for signing.
*/
bytes session_id = 1;
/*
The 32-byte SHA256 digest of the message to sign.
*/
bytes message_digest = 2;
/*
Cleanup indicates that after signing, the session state can be cleaned up,
since another participant is going to be responsible for combining the
partial signatures.
*/
bool cleanup = 3;
}
message MuSig2SignResponse {
/*
The partial signature created by the local signer.
*/
bytes local_partial_signature = 1;
}
message MuSig2CombineSigRequest {
/*
The unique ID of the signing session to combine the signatures for.
*/
bytes session_id = 1;
/*
The list of all other participants' partial signatures to add to the current
session.
*/
repeated bytes other_partial_signatures = 2;
}
message MuSig2CombineSigResponse {
/*
Indicates whether all partial signatures required to create a final, full
signature are known yet. If this is true, then the final_signature field is
set, otherwise it is empty.
*/
bool have_all_signatures = 1;
/*
The final, full signature that is valid for the combined public key.
*/
bytes final_signature = 2;
}
message MuSig2CleanupRequest {
/*
The unique ID of the signing session that should be removed/cleaned up.
*/
bytes session_id = 1;
}
message MuSig2CleanupResponse {
}

73
proto/stateservice.proto Normal file
View file

@ -0,0 +1,73 @@
syntax = "proto3";
package lnrpc;
option go_package = "github.com/lightningnetwork/lnd/lnrpc";
/*
* Comments in this file will be directly parsed into the API
* Documentation as descriptions of the associated method, message, or field.
* These descriptions should go right above the definition of the object, and
* can be in either block or // comment format.
*
* An RPC method can be matched to an lncli command by placing a line in the
* beginning of the description in exactly the following format:
* lncli: `methodname`
*
* Failure to specify the exact name of the command will cause documentation
* generation to fail.
*
* More information on how exactly the gRPC documentation is generated from
* this proto file can be found here:
* https://github.com/lightninglabs/lightning-api
*/
// State service is a always running service that exposes the current state of
// the wallet and RPC server.
service State {
// SubscribeState subscribes to the state of the wallet. The current wallet
// state will always be delivered immediately.
rpc SubscribeState (SubscribeStateRequest)
returns (stream SubscribeStateResponse);
// GetState returns the current wallet state without streaming further
// changes.
rpc GetState (GetStateRequest) returns (GetStateResponse);
}
enum WalletState {
// NON_EXISTING means that the wallet has not yet been initialized.
NON_EXISTING = 0;
// LOCKED means that the wallet is locked and requires a password to unlock.
LOCKED = 1;
// UNLOCKED means that the wallet was unlocked successfully, but RPC server
// isn't ready.
UNLOCKED = 2;
// RPC_ACTIVE means that the lnd server is active but not fully ready for
// calls.
RPC_ACTIVE = 3;
// SERVER_ACTIVE means that the lnd server is ready to accept calls.
SERVER_ACTIVE = 4;
// WAITING_TO_START means that node is waiting to become the leader in a
// cluster and is not started yet.
WAITING_TO_START = 255;
}
message SubscribeStateRequest {
}
message SubscribeStateResponse {
WalletState state = 1;
}
message GetStateRequest {
}
message GetStateResponse {
WalletState state = 1;
}

1078
proto/taprootassets.proto Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

338
proto/walletunlocker.proto Normal file
View file

@ -0,0 +1,338 @@
syntax = "proto3";
import "lightning.proto";
package lnrpc;
option go_package = "github.com/lightningnetwork/lnd/lnrpc";
/*
* Comments in this file will be directly parsed into the API
* Documentation as descriptions of the associated method, message, or field.
* These descriptions should go right above the definition of the object, and
* can be in either block or // comment format.
*
* An RPC method can be matched to an lncli command by placing a line in the
* beginning of the description in exactly the following format:
* lncli: `methodname`
*
* Failure to specify the exact name of the command will cause documentation
* generation to fail.
*
* More information on how exactly the gRPC documentation is generated from
* this proto file can be found here:
* https://github.com/lightninglabs/lightning-api
*/
// WalletUnlocker is a service that is used to set up a wallet password for
// lnd at first startup, and unlock a previously set up wallet.
service WalletUnlocker {
/*
GenSeed is the first method that should be used to instantiate a new lnd
instance. This method allows a caller to generate a new aezeed cipher seed
given an optional passphrase. If provided, the passphrase will be necessary
to decrypt the cipherseed to expose the internal wallet seed.
Once the cipherseed is obtained and verified by the user, the InitWallet
method should be used to commit the newly generated seed, and create the
wallet.
*/
rpc GenSeed (GenSeedRequest) returns (GenSeedResponse);
/*
InitWallet is used when lnd is starting up for the first time to fully
initialize the daemon and its internal wallet. At the very least a wallet
password must be provided. This will be used to encrypt sensitive material
on disk.
In the case of a recovery scenario, the user can also specify their aezeed
mnemonic and passphrase. If set, then the daemon will use this prior state
to initialize its internal wallet.
Alternatively, this can be used along with the GenSeed RPC to obtain a
seed, then present it to the user. Once it has been verified by the user,
the seed can be fed into this RPC in order to commit the new wallet.
*/
rpc InitWallet (InitWalletRequest) returns (InitWalletResponse);
/* lncli: `unlock`
UnlockWallet is used at startup of lnd to provide a password to unlock
the wallet database.
*/
rpc UnlockWallet (UnlockWalletRequest) returns (UnlockWalletResponse);
/* lncli: `changepassword`
ChangePassword changes the password of the encrypted wallet. This will
automatically unlock the wallet database if successful.
*/
rpc ChangePassword (ChangePasswordRequest) returns (ChangePasswordResponse);
}
message GenSeedRequest {
/*
aezeed_passphrase is an optional user provided passphrase that will be used
to encrypt the generated aezeed cipher seed. When using REST, this field
must be encoded as base64.
*/
bytes aezeed_passphrase = 1;
/*
seed_entropy is an optional 16-bytes generated via CSPRNG. If not
specified, then a fresh set of randomness will be used to create the seed.
When using REST, this field must be encoded as base64.
*/
bytes seed_entropy = 2;
}
message GenSeedResponse {
/*
cipher_seed_mnemonic is a 24-word mnemonic that encodes a prior aezeed
cipher seed obtained by the user. This field is optional, as if not
provided, then the daemon will generate a new cipher seed for the user.
Otherwise, then the daemon will attempt to recover the wallet state linked
to this cipher seed.
*/
repeated string cipher_seed_mnemonic = 1;
/*
enciphered_seed are the raw aezeed cipher seed bytes. This is the raw
cipher text before run through our mnemonic encoding scheme.
*/
bytes enciphered_seed = 2;
}
message InitWalletRequest {
/*
wallet_password is the passphrase that should be used to encrypt the
wallet. This MUST be at least 8 chars in length. After creation, this
password is required to unlock the daemon. When using REST, this field
must be encoded as base64.
*/
bytes wallet_password = 1;
/*
cipher_seed_mnemonic is a 24-word mnemonic that encodes a prior aezeed
cipher seed obtained by the user. This may have been generated by the
GenSeed method, or be an existing seed.
*/
repeated string cipher_seed_mnemonic = 2;
/*
aezeed_passphrase is an optional user provided passphrase that will be used
to encrypt the generated aezeed cipher seed. When using REST, this field
must be encoded as base64.
*/
bytes aezeed_passphrase = 3;
/*
recovery_window is an optional argument specifying the address lookahead
when restoring a wallet seed. The recovery window applies to each
individual branch of the BIP44 derivation paths. Supplying a recovery
window of zero indicates that no addresses should be recovered, such after
the first initialization of the wallet.
*/
int32 recovery_window = 4;
/*
channel_backups is an optional argument that allows clients to recover the
settled funds within a set of channels. This should be populated if the
user was unable to close out all channels and sweep funds before partial or
total data loss occurred. If specified, then after on-chain recovery of
funds, lnd begin to carry out the data loss recovery protocol in order to
recover the funds in each channel from a remote force closed transaction.
*/
ChanBackupSnapshot channel_backups = 5;
/*
stateless_init is an optional argument instructing the daemon NOT to create
any *.macaroon files in its filesystem. If this parameter is set, then the
admin macaroon returned in the response MUST be stored by the caller of the
RPC as otherwise all access to the daemon will be lost!
*/
bool stateless_init = 6;
/*
extended_master_key is an alternative to specifying cipher_seed_mnemonic and
aezeed_passphrase. Instead of deriving the master root key from the entropy
of an aezeed cipher seed, the given extended master root key is used
directly as the wallet's master key. This allows users to import/use a
master key from another wallet. When doing so, lnd still uses its default
SegWit only (BIP49/84) derivation paths and funds from custom/non-default
derivation paths will not automatically appear in the on-chain wallet. Using
an 'xprv' instead of an aezeed also has the disadvantage that the wallet's
birthday is not known as that is an information that's only encoded in the
aezeed, not the xprv. Therefore a birthday needs to be specified in
extended_master_key_birthday_timestamp or a "safe" default value will be
used.
*/
string extended_master_key = 7;
/*
extended_master_key_birthday_timestamp is the optional unix timestamp in
seconds to use as the wallet's birthday when using an extended master key
to restore the wallet. lnd will only start scanning for funds in blocks that
are after the birthday which can speed up the process significantly. If the
birthday is not known, this should be left at its default value of 0 in
which case lnd will start scanning from the first SegWit block (481824 on
mainnet).
*/
uint64 extended_master_key_birthday_timestamp = 8;
/*
watch_only is the third option of initializing a wallet: by importing
account xpubs only and therefore creating a watch-only wallet that does not
contain any private keys. That means the wallet won't be able to sign for
any of the keys and _needs_ to be run with a remote signer that has the
corresponding private keys and can serve signing RPC requests.
*/
WatchOnly watch_only = 9;
/*
macaroon_root_key is an optional 32 byte macaroon root key that can be
provided when initializing the wallet rather than letting lnd generate one
on its own.
*/
bytes macaroon_root_key = 10;
}
message InitWalletResponse {
/*
The binary serialized admin macaroon that can be used to access the daemon
after creating the wallet. If the stateless_init parameter was set to true,
this is the ONLY copy of the macaroon and MUST be stored safely by the
caller. Otherwise a copy of this macaroon is also persisted on disk by the
daemon, together with other macaroon files.
*/
bytes admin_macaroon = 1;
}
message WatchOnly {
/*
The unix timestamp in seconds of when the master key was created. lnd will
only start scanning for funds in blocks that are after the birthday which
can speed up the process significantly. If the birthday is not known, this
should be left at its default value of 0 in which case lnd will start
scanning from the first SegWit block (481824 on mainnet).
*/
uint64 master_key_birthday_timestamp = 1;
/*
The fingerprint of the root key (also known as the key with derivation path
m/) from which the account public keys were derived from. This may be
required by some hardware wallets for proper identification and signing. The
bytes must be in big-endian order.
*/
bytes master_key_fingerprint = 2;
/*
The list of accounts to import. There _must_ be an account for all of lnd's
main key scopes: BIP49/BIP84 (m/49'/0'/0', m/84'/0'/0', note that the
coin type is always 0, even for testnet/regtest) and lnd's internal key
scope (m/1017'/<coin_type>'/<account>'), where account is the key family as
defined in `keychain/derivation.go` (currently indices 0 to 9).
*/
repeated WatchOnlyAccount accounts = 3;
}
message WatchOnlyAccount {
/*
Purpose is the first number in the derivation path, must be either 49, 84
or 1017.
*/
uint32 purpose = 1;
/*
Coin type is the second number in the derivation path, this is _always_ 0
for purposes 49 and 84. It only needs to be set to 1 for purpose 1017 on
testnet or regtest.
*/
uint32 coin_type = 2;
/*
Account is the third number in the derivation path. For purposes 49 and 84
at least the default account (index 0) needs to be created but optional
additional accounts are allowed. For purpose 1017 there needs to be exactly
one account for each of the key families defined in `keychain/derivation.go`
(currently indices 0 to 9)
*/
uint32 account = 3;
/*
The extended public key at depth 3 for the given account.
*/
string xpub = 4;
}
message UnlockWalletRequest {
/*
wallet_password should be the current valid passphrase for the daemon. This
will be required to decrypt on-disk material that the daemon requires to
function properly. When using REST, this field must be encoded as base64.
*/
bytes wallet_password = 1;
/*
recovery_window is an optional argument specifying the address lookahead
when restoring a wallet seed. The recovery window applies to each
individual branch of the BIP44 derivation paths. Supplying a recovery
window of zero indicates that no addresses should be recovered, such after
the first initialization of the wallet.
*/
int32 recovery_window = 2;
/*
channel_backups is an optional argument that allows clients to recover the
settled funds within a set of channels. This should be populated if the
user was unable to close out all channels and sweep funds before partial or
total data loss occurred. If specified, then after on-chain recovery of
funds, lnd begin to carry out the data loss recovery protocol in order to
recover the funds in each channel from a remote force closed transaction.
*/
ChanBackupSnapshot channel_backups = 3;
/*
stateless_init is an optional argument instructing the daemon NOT to create
any *.macaroon files in its file system.
*/
bool stateless_init = 4;
}
message UnlockWalletResponse {
}
message ChangePasswordRequest {
/*
current_password should be the current valid passphrase used to unlock the
daemon. When using REST, this field must be encoded as base64.
*/
bytes current_password = 1;
/*
new_password should be the new passphrase that will be needed to unlock the
daemon. When using REST, this field must be encoded as base64.
*/
bytes new_password = 2;
/*
stateless_init is an optional argument instructing the daemon NOT to create
any *.macaroon files in its filesystem. If this parameter is set, then the
admin macaroon returned in the response MUST be stored by the caller of the
RPC as otherwise all access to the daemon will be lost!
*/
bool stateless_init = 3;
/*
new_macaroon_root_key is an optional argument instructing the daemon to
rotate the macaroon root key when set to true. This will invalidate all
previously generated macaroons.
*/
bool new_macaroon_root_key = 4;
}
message ChangePasswordResponse {
/*
The binary serialized admin macaroon that can be used to access the daemon
after rotating the macaroon root key. If both the stateless_init and
new_macaroon_root_key parameter were set to true, this is the ONLY copy of
the macaroon that was created from the new root key and MUST be stored
safely by the caller. Otherwise a copy of this macaroon is also persisted on
disk by the daemon, together with other macaroon files.
*/
bytes admin_macaroon = 1;
}

View file

@ -0,0 +1,30 @@
syntax = "proto3";
package watchtowerrpc;
option go_package = "github.com/lightningnetwork/lnd/lnrpc/watchtowerrpc";
// Watchtower is a service that grants access to the watchtower server
// functionality of the daemon.
service Watchtower {
/* lncli: tower info
GetInfo returns general information concerning the companion watchtower
including its public key and URIs where the server is currently
listening for clients.
*/
rpc GetInfo (GetInfoRequest) returns (GetInfoResponse);
}
message GetInfoRequest {
}
message GetInfoResponse {
// The public key of the watchtower.
bytes pubkey = 1;
// The listening addresses of the watchtower.
repeated string listeners = 2;
// The URIs of the watchtower.
repeated string uris = 3;
}

View file

@ -0,0 +1,224 @@
syntax = "proto3";
package wtclientrpc;
option go_package = "github.com/lightningnetwork/lnd/lnrpc/wtclientrpc";
// WatchtowerClient is a service that grants access to the watchtower client
// functionality of the daemon.
service WatchtowerClient {
/*
AddTower adds a new watchtower reachable at the given address and
considers it for new sessions. If the watchtower already exists, then
any new addresses included will be considered when dialing it for
session negotiations and backups.
*/
rpc AddTower (AddTowerRequest) returns (AddTowerResponse);
/*
RemoveTower removes a watchtower from being considered for future session
negotiations and from being used for any subsequent backups until it's added
again. If an address is provided, then this RPC only serves as a way of
removing the address from the watchtower instead.
*/
rpc RemoveTower (RemoveTowerRequest) returns (RemoveTowerResponse);
// ListTowers returns the list of watchtowers registered with the client.
rpc ListTowers (ListTowersRequest) returns (ListTowersResponse);
// GetTowerInfo retrieves information for a registered watchtower.
rpc GetTowerInfo (GetTowerInfoRequest) returns (Tower);
// Stats returns the in-memory statistics of the client since startup.
rpc Stats (StatsRequest) returns (StatsResponse);
// Policy returns the active watchtower client policy configuration.
rpc Policy (PolicyRequest) returns (PolicyResponse);
}
message AddTowerRequest {
// The identifying public key of the watchtower to add.
bytes pubkey = 1;
// A network address the watchtower is reachable over.
string address = 2;
}
message AddTowerResponse {
}
message RemoveTowerRequest {
// The identifying public key of the watchtower to remove.
bytes pubkey = 1;
/*
If set, then the record for this address will be removed, indicating that is
is stale. Otherwise, the watchtower will no longer be used for future
session negotiations and backups.
*/
string address = 2;
}
message RemoveTowerResponse {
}
message GetTowerInfoRequest {
// The identifying public key of the watchtower to retrieve information for.
bytes pubkey = 1;
// Whether we should include sessions with the watchtower in the response.
bool include_sessions = 2;
// Whether to exclude exhausted sessions in the response info. This option
// is only meaningful if include_sessions is true.
bool exclude_exhausted_sessions = 3;
}
message TowerSession {
/*
The total number of successful backups that have been made to the
watchtower session.
*/
uint32 num_backups = 1;
/*
The total number of backups in the session that are currently pending to be
acknowledged by the watchtower.
*/
uint32 num_pending_backups = 2;
// The maximum number of backups allowed by the watchtower session.
uint32 max_backups = 3;
/*
Deprecated, use sweep_sat_per_vbyte.
The fee rate, in satoshis per vbyte, that will be used by the watchtower for
the justice transaction in the event of a channel breach.
*/
uint32 sweep_sat_per_byte = 4 [deprecated = true];
/*
The fee rate, in satoshis per vbyte, that will be used by the watchtower for
the justice transaction in the event of a channel breach.
*/
uint32 sweep_sat_per_vbyte = 5;
}
message Tower {
// The identifying public key of the watchtower.
bytes pubkey = 1;
// The list of addresses the watchtower is reachable over.
repeated string addresses = 2;
// Deprecated, use the active_session_candidate field under the
// correct identifier in the client_type map.
// Whether the watchtower is currently a candidate for new sessions.
bool active_session_candidate = 3 [deprecated = true];
// Deprecated, use the num_sessions field under the correct identifier
// in the client_type map.
// The number of sessions that have been negotiated with the watchtower.
uint32 num_sessions = 4 [deprecated = true];
// Deprecated, use the sessions field under the correct identifier in the
// client_type map.
// The list of sessions that have been negotiated with the watchtower.
repeated TowerSession sessions = 5 [deprecated = true];
// A list sessions held with the tower.
repeated TowerSessionInfo session_info = 6;
}
message TowerSessionInfo {
// Whether the watchtower is currently a candidate for new sessions.
bool active_session_candidate = 1;
// The number of sessions that have been negotiated with the watchtower.
uint32 num_sessions = 2;
// The list of sessions that have been negotiated with the watchtower.
repeated TowerSession sessions = 3;
// The session's policy type.
PolicyType policy_type = 4;
}
message ListTowersRequest {
// Whether we should include sessions with the watchtower in the response.
bool include_sessions = 1;
// Whether to exclude exhausted sessions in the response info. This option
// is only meaningful if include_sessions is true.
bool exclude_exhausted_sessions = 2;
}
message ListTowersResponse {
// The list of watchtowers available for new backups.
repeated Tower towers = 1;
}
message StatsRequest {
}
message StatsResponse {
/*
The total number of backups made to all active and exhausted watchtower
sessions.
*/
uint32 num_backups = 1;
/*
The total number of backups that are pending to be acknowledged by all
active and exhausted watchtower sessions.
*/
uint32 num_pending_backups = 2;
/*
The total number of backups that all active and exhausted watchtower
sessions have failed to acknowledge.
*/
uint32 num_failed_backups = 3;
// The total number of new sessions made to watchtowers.
uint32 num_sessions_acquired = 4;
// The total number of watchtower sessions that have been exhausted.
uint32 num_sessions_exhausted = 5;
}
enum PolicyType {
// Selects the policy from the legacy tower client.
LEGACY = 0;
// Selects the policy from the anchor tower client.
ANCHOR = 1;
}
message PolicyRequest {
/*
The client type from which to retrieve the active offering policy.
*/
PolicyType policy_type = 1;
}
message PolicyResponse {
/*
The maximum number of updates each session we negotiate with watchtowers
should allow.
*/
uint32 max_updates = 1;
/*
Deprecated, use sweep_sat_per_vbyte.
The fee rate, in satoshis per vbyte, that will be used by watchtowers for
justice transactions in response to channel breaches.
*/
uint32 sweep_sat_per_byte = 2 [deprecated = true];
/*
The fee rate, in satoshis per vbyte, that will be used by watchtowers for
justice transactions in response to channel breaches.
*/
uint32 sweep_sat_per_vbyte = 3;
}

View file

@ -55,9 +55,13 @@ type subServer struct {
// newSubServer constructs a new subServer.
func newSubServer(disabled bool, opts ...SubServerOption) *subServer {
return &subServer{
s := &subServer{
disabled: disabled,
}
for _, opt := range opts {
opt(s)
}
return s
}
// Manager manages the status of any sub-server registered to it. It is also an

View file

@ -196,15 +196,24 @@ type LightningTerminal struct {
restHandler http.Handler
restCancel func()
mobileListener *bufconn.Listener
mobileReadyChan chan struct{}
}
// New creates a new instance of the lightning-terminal daemon.
func New() *LightningTerminal {
func New(listener *bufconn.Listener, readyChan chan struct{}) *LightningTerminal {
return &LightningTerminal{
statusMgr: status.NewStatusManager(),
statusMgr: status.NewStatusManager(),
mobileListener: listener,
mobileReadyChan: readyChan,
}
}
func (g *LightningTerminal) GetConfig() *Config {
return g.cfg
}
// Run starts everything and then blocks until either the application is shut
// down or a critical error happens.
func (g *LightningTerminal) Run() error {
@ -244,7 +253,7 @@ func (g *LightningTerminal) Run() error {
// URI as soon as LND can accept the call, i.e. when the lnd sub-server
// is in the "Wallet Ready" state.
lndOverride := func(uri, manualStatus string) (bool, bool) {
if uri != "/lnrpc.State/GetState" {
if uri != "/lnrpc.State/GetState" && !strings.HasPrefix(uri, "/lnrpc.WalletUnlocker") {
return false, false
}
@ -283,19 +292,34 @@ func (g *LightningTerminal) Run() error {
litrpc.RegisterProxyServer(g.rpcProxy.grpcServer, g.rpcProxy)
litrpc.RegisterStatusServer(g.rpcProxy.grpcServer, g.statusMgr)
// Start the main web server that dispatches requests either to the
// static UI file server or the RPC proxy. This makes it possible to
// unlock lnd through the UI.
if err := g.startMainWebServer(); err != nil {
return fmt.Errorf("error starting main proxy HTTP server: %v",
err)
}
// Set up the in-memory listener
if g.mobileListener != nil && g.mobileReadyChan != nil {
log.Info("Setting up mobile listener")
tlsConfig, err := buildTLSConfigForHttp2(g.cfg)
if err != nil {
log.Infof("Failed to create mobileListener tlsConfig: %v", err)
os.Exit(1)
}
go func() {
tlsListener := tls.NewListener(g.mobileListener, tlsConfig)
close(g.mobileReadyChan)
_ = g.rpcProxy.grpcServer.Serve(tlsListener)
}()
} else {
// Start the main web server that dispatches requests either to the
// static UI file server or the RPC proxy. This makes it possible to
// unlock lnd through the UI.
if err := g.startMainWebServer(); err != nil {
return fmt.Errorf("error starting main proxy HTTP server: %v",
err)
}
// We'll also create a REST proxy that'll convert any REST calls to gRPC
// calls and forward them to the internal listener.
if g.cfg.EnableREST {
if err := g.createRESTProxy(); err != nil {
return fmt.Errorf("error creating REST proxy: %v", err)
// We'll also create a REST proxy that'll convert any REST calls to gRPC
// calls and forward them to the internal listener.
if g.cfg.EnableREST {
if err := g.createRESTProxy(); err != nil {
return fmt.Errorf("error creating REST proxy: %v", err)
}
}
}