mirror of
https://github.com/getAlby/hub.git
synced 2026-08-13 12:33:39 +02:00
feat: add CLN backend (#2026)
* feat: add CLN as a lnclient backend * feat: add hold invoice support for CLN backend * fix: reduce CLN form to just addresses and lightning dir * feat: add README for CLN grpc go code generation * fix: cln backend does not support keysend with given preimages * fix: hold invoice notifications in CLN backend * fix: remove dead code in CLN backend from ListTransactions * fix: env example CLN_ADDRESS_HOLD with different port to show it's a different service * fix: cleanup of CLN ressources in all cases * fix: cln backend's GetNetworkGraph only fetches specified nodeId's * fix: cln backend: only advertise hold methods for nip47 if hold plugin enabled * fix: cln's Shutdown should not stop CLN itself * fix: relax the LND README line regarding env configuration * fix: more nil checks in clnInvoiceToTransaction * fix: prevent feerate overflow in CLN's RedeemOnchainFunds * fix: don't access nil errors for empty reponses of certain CLN methods * fix: nil instead of empty string in cln's GetNetworkGraph return types * fix: nil checks for created_at in cln's clnInvoiceToTransaction * fix: set minimum tls version to 1.2 for cln backend grpc connections * fix: cln's subscribeOpenHoldInvoices doesn't give up as fast * fix: deduplicate graph edges in cln's GetNetworkGraph * fix: print the error string, not pointer address, in cln's ListChannels * fix: remove cln's ListTransactions completely * fix: use ListPeers instead of ListPeerChannels in cln's ListPeers * feat: cln's MakeHoldInvoice supports minCltvExpiryDelta * fix: use named return err in NewCLNService * fix: cln listpeers log message * fix: incorrect import * fix: compile errors after rename --------- Co-authored-by: Roland Bewick <roland.bewick@gmail.com>
This commit is contained in:
parent
fe1f338ed3
commit
28706ecf31
21 changed files with 58303 additions and 3 deletions
11
.env.example
11
.env.example
|
|
@ -40,4 +40,13 @@ FRONTEND_URL=http://localhost:5173
|
|||
|
||||
# Boltz API
|
||||
#BOLTZ_API=https://api.testnet.boltz.exchange
|
||||
#NETWORK=testnet
|
||||
#NETWORK=testnet
|
||||
|
||||
# CLN Backend
|
||||
#LN_BACKEND_TYPE=CLN
|
||||
# CLN's grpc-host:grpc-port
|
||||
#CLN_ADDRESS=127.0.0.1:9737
|
||||
# CLN's lightning directory containing the grpc certificates, usually ~/.lightning/<network>/
|
||||
#CLN_LIGHTNING_DIR=/path/to/.lightning/bitcoin
|
||||
# CLN's hold plugin https://github.com/BoltzExchange/hold gRPC address
|
||||
#CLN_ADDRESS_HOLD=127.0.0.1:9738
|
||||
|
|
|
|||
16
README.md
16
README.md
|
|
@ -29,6 +29,7 @@ By default Alby Hub uses the embedded LDK based lightning node. Optionally it ca
|
|||
- LND
|
||||
- Phoenixd
|
||||
- Cashu
|
||||
- CLN
|
||||
- want more? please open an issue.
|
||||
|
||||
## Development
|
||||
|
|
@ -210,9 +211,22 @@ Migration of the database is currently experimental. Please make a backup before
|
|||
|
||||
- `ENABLE_ADVANCED_SETUP`: set to `false` to force a specific backend type (combined with backend parameters below)
|
||||
|
||||
### CLN Backend parameters
|
||||
|
||||
Can be configured via env or the UI
|
||||
|
||||
- `LN_BACKEND_TYPE`: CLN
|
||||
- `CLN_ADDRESS`: the CLN grpc address (grpc-host and grpc-port), e.g. `127.0.0.1:9737`
|
||||
- `CLN_LIGHTNING_DIR`: CLN's lightning directory containing the grpc certificates, usually `~/.lightning/<network>`
|
||||
|
||||
Optional for hold invoice methods support:
|
||||
- `CLN_ADDRESS_HOLD`: the CLN hold plugin grpc address (grpc-host and grpc-port), e.g. `127.0.0.1:9738`
|
||||
|
||||
If you are copying the certificates to another machine make sure you get the `ca.pem`, `client.pem` and `client-key.pem` from the lightning directory and optionally from the `hold` directory inside the lightning directory and keep the sub-directory structure of the hold directory.
|
||||
|
||||
### LND Backend parameters
|
||||
|
||||
Currently only LND can be configured via env. Other node types must be configured via the UI.
|
||||
LND can be configured via env. Other node types may need to be configured via the UI.
|
||||
|
||||
_To configure via env, the following parameters must be provided:_
|
||||
|
||||
|
|
|
|||
24
api/api.go
24
api/api.go
|
|
@ -1644,6 +1644,30 @@ func (api *api) Setup(ctx context.Context, setupRequest *SetupRequest) error {
|
|||
}
|
||||
}
|
||||
|
||||
if setupRequest.CLNAddress != "" {
|
||||
err = api.cfg.SetUpdate("CLNAddress", setupRequest.CLNAddress, setupRequest.UnlockPassword)
|
||||
if err != nil {
|
||||
logger.Logger.WithError(err).Error("Failed to save CLN address")
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if setupRequest.CLNLightningDir != "" {
|
||||
err = api.cfg.SetUpdate("CLNLightningDir", setupRequest.CLNLightningDir, setupRequest.UnlockPassword)
|
||||
if err != nil {
|
||||
logger.Logger.WithError(err).Error("Failed to save CLN Lightning directory path")
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if setupRequest.CLNAddressHold != "" {
|
||||
err = api.cfg.SetUpdate("CLNAddressHold", setupRequest.CLNAddressHold, setupRequest.UnlockPassword)
|
||||
if err != nil {
|
||||
logger.Logger.WithError(err).Error("Failed to save cln hold plugin address")
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -277,6 +277,11 @@ type SetupRequest struct {
|
|||
|
||||
// Cashu fields
|
||||
CashuMintUrl string `json:"cashuMintUrl"`
|
||||
|
||||
// CLN fields
|
||||
CLNAddress string `json:"clnAddress"`
|
||||
CLNLightningDir string `json:"clnLightningDir"`
|
||||
CLNAddressHold string `json:"clnAddressHold"`
|
||||
}
|
||||
|
||||
type CreateAppResponse struct {
|
||||
|
|
|
|||
|
|
@ -112,6 +112,26 @@ func (cfg *config) init(env *AppConfig) error {
|
|||
}
|
||||
}
|
||||
|
||||
// CLN specific to support env variables
|
||||
if cfg.Env.CLNAddress != "" {
|
||||
err := cfg.SetUpdate("CLNAddress", cfg.Env.CLNAddress, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if cfg.Env.CLNLightningDir != "" {
|
||||
err := cfg.SetUpdate("CLNLightningDir", cfg.Env.CLNLightningDir, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if cfg.Env.CLNAddressHold != "" {
|
||||
err := cfg.SetUpdate("CLNAddressHold", cfg.Env.CLNAddressHold, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ const (
|
|||
LDKBackendType = "LDK"
|
||||
PhoenixBackendType = "PHOENIX"
|
||||
CashuBackendType = "CASHU"
|
||||
CLNBackendType = "CLN"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -59,6 +60,9 @@ type AppConfig struct {
|
|||
LogDBQueries bool `envconfig:"LOG_DB_QUERIES" default:"false"`
|
||||
BoltzApi string `envconfig:"BOLTZ_API" default:"https://api.boltz.exchange"`
|
||||
HideUpdateBanner bool `envconfig:"HIDE_UPDATE_BANNER" default:"false"`
|
||||
CLNAddress string `envconfig:"CLN_ADDRESS"`
|
||||
CLNLightningDir string `envconfig:"CLN_LIGHTNING_DIR"`
|
||||
CLNAddressHold string `envconfig:"CLN_ADDRESS_HOLD"`
|
||||
}
|
||||
|
||||
func (c *AppConfig) IsDefaultClientId() bool {
|
||||
|
|
|
|||
BIN
frontend/src/assets/images/node/cln.png
Normal file
BIN
frontend/src/assets/images/node/cln.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
|
|
@ -27,4 +27,9 @@ export const backendTypeConfigs: Record<BackendType, BackendTypeConfig> = {
|
|||
hasChannelManagement: false,
|
||||
hasNodeBackup: false,
|
||||
},
|
||||
CLN: {
|
||||
hasMnemonic: false,
|
||||
hasChannelManagement: true,
|
||||
hasNodeBackup: false,
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ import { SetupFinish } from "src/screens/setup/SetupFinish";
|
|||
import { SetupNode } from "src/screens/setup/SetupNode";
|
||||
import { SetupPassword } from "src/screens/setup/SetupPassword";
|
||||
import { SetupSecurity } from "src/screens/setup/SetupSecurity";
|
||||
import { CLNForm } from "src/screens/setup/node/CLNForm";
|
||||
import { CashuForm } from "src/screens/setup/node/CashuForm";
|
||||
import { LDKForm } from "src/screens/setup/node/LDKForm";
|
||||
import { LNDForm } from "src/screens/setup/node/LNDForm";
|
||||
|
|
@ -548,6 +549,10 @@ const routes: RouteObject[] = [
|
|||
path: "ldk",
|
||||
element: <LDKForm />,
|
||||
},
|
||||
{
|
||||
path: "cln",
|
||||
element: <CLNForm />,
|
||||
},
|
||||
{
|
||||
path: "preset",
|
||||
element: <PresetNodeForm />,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import { cn } from "src/lib/utils";
|
|||
import { BackendType } from "src/types";
|
||||
|
||||
import cashu from "src/assets/images/node/cashu.png";
|
||||
import cln from "src/assets/images/node/cln.png";
|
||||
import lnd from "src/assets/images/node/lnd.png";
|
||||
import { backendTypeConfigs } from "src/lib/backendType";
|
||||
import useSetupStore from "src/state/SetupStore";
|
||||
|
|
@ -37,6 +38,10 @@ const backendTypeDisplayConfigs: Partial<
|
|||
title: "Cashu Mint",
|
||||
icon: <img src={cashu} />,
|
||||
},
|
||||
CLN: {
|
||||
title: "CLN",
|
||||
icon: <img src={cln} />,
|
||||
},
|
||||
};
|
||||
|
||||
const backendTypeDisplayConfigList = Object.entries(
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ export function SetupSecurity() {
|
|||
</span>
|
||||
</div>
|
||||
{store.nodeInfo.backendType === "LND" ||
|
||||
store.nodeInfo.backendType === "CLN" ||
|
||||
store.nodeInfo.backendType === "PHOENIX" ? (
|
||||
<div className="flex gap-3 items-center">
|
||||
<div className="shrink-0">
|
||||
|
|
|
|||
86
frontend/src/screens/setup/node/CLNForm.tsx
Normal file
86
frontend/src/screens/setup/node/CLNForm.tsx
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
import React from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
import Container from "src/components/Container";
|
||||
import TwoColumnLayoutHeader from "src/components/TwoColumnLayoutHeader";
|
||||
import { Button } from "src/components/ui/button";
|
||||
import { Input } from "src/components/ui/input";
|
||||
import { Label } from "src/components/ui/label";
|
||||
import useSetupStore from "src/state/SetupStore";
|
||||
|
||||
export function CLNForm() {
|
||||
const navigate = useNavigate();
|
||||
const setupStore = useSetupStore();
|
||||
const [clnAddress, setClnAddress] = React.useState<string>(
|
||||
setupStore.nodeInfo.clnAddress || ""
|
||||
);
|
||||
const [clnLightningDir, setClnLightningDir] = React.useState<string>(
|
||||
setupStore.nodeInfo.clnLightningDir || ""
|
||||
);
|
||||
const [clnAddressHold, setClnAddressHold] = React.useState<string>(
|
||||
setupStore.nodeInfo.clnAddressHold || ""
|
||||
);
|
||||
|
||||
// TODO: proper onboarding
|
||||
function onSubmit(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
handleSubmit({
|
||||
clnAddress,
|
||||
clnLightningDir,
|
||||
clnAddressHold,
|
||||
});
|
||||
}
|
||||
|
||||
async function handleSubmit(data: object) {
|
||||
setupStore.updateNodeInfo({
|
||||
backendType: "CLN",
|
||||
...data,
|
||||
});
|
||||
navigate("/setup/security");
|
||||
}
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<TwoColumnLayoutHeader
|
||||
title="Configure CLN"
|
||||
description="Fill out wallet details to finish setup."
|
||||
/>
|
||||
<form className="w-full grid gap-5 mt-6" onSubmit={onSubmit}>
|
||||
<div className="grid gap-1.5">
|
||||
<Label htmlFor="cln-address">CLN Address (GRPC)</Label>
|
||||
<Input
|
||||
required
|
||||
name="cln-address"
|
||||
onChange={(e) => setClnAddress(e.target.value)}
|
||||
value={clnAddress}
|
||||
id="cln-address"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-1.5">
|
||||
<Label htmlFor="cln-lightning-dir">
|
||||
CLN Lightning directory (full path)
|
||||
</Label>
|
||||
<Input
|
||||
required
|
||||
name="cln-lightning-dir"
|
||||
onChange={(e) => setClnLightningDir(e.target.value)}
|
||||
value={clnLightningDir}
|
||||
type="text"
|
||||
id="cln-lightning-dir"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-1.5">
|
||||
<Label htmlFor="cln-address-hold">
|
||||
(optional) CLN hold plugin Address (GRPC)
|
||||
</Label>
|
||||
<Input
|
||||
name="cln-address-hold"
|
||||
onChange={(e) => setClnAddressHold(e.target.value)}
|
||||
value={clnAddressHold}
|
||||
id="cln-address-hold"
|
||||
/>
|
||||
</div>
|
||||
<Button>Next</Button>
|
||||
</form>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@ import {
|
|||
WalletMinimalIcon,
|
||||
} from "lucide-react";
|
||||
|
||||
export type BackendType = "LND" | "LDK" | "PHOENIX" | "CASHU";
|
||||
export type BackendType = "LND" | "LDK" | "PHOENIX" | "CASHU" | "CLN";
|
||||
|
||||
export type Nip47RequestMethod =
|
||||
| "get_info"
|
||||
|
|
@ -457,6 +457,10 @@ export type SetupNodeInfo = Partial<{
|
|||
|
||||
phoenixdAddress?: string;
|
||||
phoenixdAuthorization?: string;
|
||||
|
||||
clnAddress?: string;
|
||||
clnLightningDir?: string;
|
||||
clnAddressHold?: string;
|
||||
}>;
|
||||
|
||||
export type LSPType = "LSPS1";
|
||||
|
|
|
|||
75
lnclient/cln/README.md
Normal file
75
lnclient/cln/README.md
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
## Generating Go gRPC Code
|
||||
|
||||
The Go gRPC bindings for Core Lightning (CLN) are generated from proto files that live
|
||||
in the **lightning** repository (`cln-grpc`) and in the **hold** plugin repository.
|
||||
|
||||
The generated Go files are written into the **hub** repository under `lnclient/cln/clngrpc` and `lnclient/cln/clngrpc_hold`.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Make sure the following tools are installed:
|
||||
|
||||
```bash
|
||||
# protoc (>= 3.20 recommended)
|
||||
protoc --version
|
||||
|
||||
# Go plugins
|
||||
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
|
||||
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
|
||||
```
|
||||
|
||||
Ensure `$GOPATH/bin` is in your `PATH`:
|
||||
|
||||
```bash
|
||||
export PATH="$PATH:$(go env GOPATH)/bin"
|
||||
```
|
||||
|
||||
This guide assumes the following directory structure:
|
||||
|
||||
```
|
||||
~/dev/
|
||||
├── lightning/
|
||||
│ └── cln-grpc/
|
||||
│ └── proto/
|
||||
│ ├── node.proto
|
||||
│ ├── primitives.proto
|
||||
│ └── ...
|
||||
├── hub/
|
||||
| └── lnclient/
|
||||
| └── cln/
|
||||
| └── clngrpc/
|
||||
| └── clngrpc_hold/
|
||||
└── hold/
|
||||
└── protos/
|
||||
└── hold.proto
|
||||
```
|
||||
|
||||
### Generating Go code
|
||||
|
||||
From the hub repository root, run:
|
||||
|
||||
```bash
|
||||
protoc \
|
||||
--proto_path=../lightning/cln-grpc/proto \
|
||||
--go_out=./lnclient/cln/clngrpc \
|
||||
--go_opt=paths=source_relative \
|
||||
--go_opt=Mprimitives.proto=github.com/getAlby/hub/lnclient/cln/clngrpc \
|
||||
--go_opt=Mnode.proto=github.com/getAlby/hub/lnclient/cln/clngrpc \
|
||||
--go-grpc_out=./lnclient/cln/clngrpc \
|
||||
--go-grpc_opt=paths=source_relative \
|
||||
../lightning/cln-grpc/proto/node.proto \
|
||||
../lightning/cln-grpc/proto/primitives.proto
|
||||
```
|
||||
|
||||
and if you have the hold plugin repo:
|
||||
|
||||
```bash
|
||||
protoc \
|
||||
--proto_path=../hold/protos \
|
||||
--go_out=./lnclient/cln/clngrpc_hold \
|
||||
--go_opt=paths=source_relative \
|
||||
--go_opt=Mhold.proto=github.com/getAlby/hub/lnclient/cln/clngrpc_hold \
|
||||
--go-grpc_out=./lnclient/cln/clngrpc_hold \
|
||||
--go-grpc_opt=paths=source_relative \
|
||||
../hold/protos/hold.proto
|
||||
```
|
||||
1939
lnclient/cln/cln.go
Normal file
1939
lnclient/cln/cln.go
Normal file
File diff suppressed because it is too large
Load diff
46401
lnclient/cln/clngrpc/node.pb.go
Normal file
46401
lnclient/cln/clngrpc/node.pb.go
Normal file
File diff suppressed because it is too large
Load diff
5726
lnclient/cln/clngrpc/node_grpc.pb.go
Normal file
5726
lnclient/cln/clngrpc/node_grpc.pb.go
Normal file
File diff suppressed because it is too large
Load diff
1500
lnclient/cln/clngrpc/primitives.pb.go
Normal file
1500
lnclient/cln/clngrpc/primitives.pb.go
Normal file
File diff suppressed because it is too large
Load diff
2005
lnclient/cln/clngrpc_hold/hold.pb.go
Normal file
2005
lnclient/cln/clngrpc_hold/hold.pb.go
Normal file
File diff suppressed because it is too large
Load diff
466
lnclient/cln/clngrpc_hold/hold_grpc.pb.go
Normal file
466
lnclient/cln/clngrpc_hold/hold_grpc.pb.go
Normal file
|
|
@ -0,0 +1,466 @@
|
|||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.0
|
||||
// - protoc v3.21.12
|
||||
// source: hold.proto
|
||||
|
||||
package clngrpc_hold
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.64.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
Hold_GetInfo_FullMethodName = "/hold.Hold/GetInfo"
|
||||
Hold_Invoice_FullMethodName = "/hold.Hold/Invoice"
|
||||
Hold_Inject_FullMethodName = "/hold.Hold/Inject"
|
||||
Hold_List_FullMethodName = "/hold.Hold/List"
|
||||
Hold_Settle_FullMethodName = "/hold.Hold/Settle"
|
||||
Hold_Cancel_FullMethodName = "/hold.Hold/Cancel"
|
||||
Hold_Clean_FullMethodName = "/hold.Hold/Clean"
|
||||
Hold_Track_FullMethodName = "/hold.Hold/Track"
|
||||
Hold_TrackAll_FullMethodName = "/hold.Hold/TrackAll"
|
||||
Hold_OnionMessages_FullMethodName = "/hold.Hold/OnionMessages"
|
||||
)
|
||||
|
||||
// HoldClient is the client API for Hold service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type HoldClient interface {
|
||||
GetInfo(ctx context.Context, in *GetInfoRequest, opts ...grpc.CallOption) (*GetInfoResponse, error)
|
||||
Invoice(ctx context.Context, in *InvoiceRequest, opts ...grpc.CallOption) (*InvoiceResponse, error)
|
||||
Inject(ctx context.Context, in *InjectRequest, opts ...grpc.CallOption) (*InjectResponse, error)
|
||||
List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error)
|
||||
Settle(ctx context.Context, in *SettleRequest, opts ...grpc.CallOption) (*SettleResponse, error)
|
||||
Cancel(ctx context.Context, in *CancelRequest, opts ...grpc.CallOption) (*CancelResponse, error)
|
||||
// Cleans cancelled invoices
|
||||
Clean(ctx context.Context, in *CleanRequest, opts ...grpc.CallOption) (*CleanResponse, error)
|
||||
Track(ctx context.Context, in *TrackRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[TrackResponse], error)
|
||||
TrackAll(ctx context.Context, in *TrackAllRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[TrackAllResponse], error)
|
||||
OnionMessages(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[OnionMessageResponse, OnionMessage], error)
|
||||
}
|
||||
|
||||
type holdClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewHoldClient(cc grpc.ClientConnInterface) HoldClient {
|
||||
return &holdClient{cc}
|
||||
}
|
||||
|
||||
func (c *holdClient) GetInfo(ctx context.Context, in *GetInfoRequest, opts ...grpc.CallOption) (*GetInfoResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GetInfoResponse)
|
||||
err := c.cc.Invoke(ctx, Hold_GetInfo_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *holdClient) Invoice(ctx context.Context, in *InvoiceRequest, opts ...grpc.CallOption) (*InvoiceResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(InvoiceResponse)
|
||||
err := c.cc.Invoke(ctx, Hold_Invoice_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *holdClient) Inject(ctx context.Context, in *InjectRequest, opts ...grpc.CallOption) (*InjectResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(InjectResponse)
|
||||
err := c.cc.Invoke(ctx, Hold_Inject_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *holdClient) List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ListResponse)
|
||||
err := c.cc.Invoke(ctx, Hold_List_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *holdClient) Settle(ctx context.Context, in *SettleRequest, opts ...grpc.CallOption) (*SettleResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(SettleResponse)
|
||||
err := c.cc.Invoke(ctx, Hold_Settle_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *holdClient) Cancel(ctx context.Context, in *CancelRequest, opts ...grpc.CallOption) (*CancelResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(CancelResponse)
|
||||
err := c.cc.Invoke(ctx, Hold_Cancel_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *holdClient) Clean(ctx context.Context, in *CleanRequest, opts ...grpc.CallOption) (*CleanResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(CleanResponse)
|
||||
err := c.cc.Invoke(ctx, Hold_Clean_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *holdClient) Track(ctx context.Context, in *TrackRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[TrackResponse], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &Hold_ServiceDesc.Streams[0], Hold_Track_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[TrackRequest, TrackResponse]{ClientStream: stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type Hold_TrackClient = grpc.ServerStreamingClient[TrackResponse]
|
||||
|
||||
func (c *holdClient) TrackAll(ctx context.Context, in *TrackAllRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[TrackAllResponse], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &Hold_ServiceDesc.Streams[1], Hold_TrackAll_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[TrackAllRequest, TrackAllResponse]{ClientStream: stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type Hold_TrackAllClient = grpc.ServerStreamingClient[TrackAllResponse]
|
||||
|
||||
func (c *holdClient) OnionMessages(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[OnionMessageResponse, OnionMessage], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &Hold_ServiceDesc.Streams[2], Hold_OnionMessages_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[OnionMessageResponse, OnionMessage]{ClientStream: stream}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type Hold_OnionMessagesClient = grpc.BidiStreamingClient[OnionMessageResponse, OnionMessage]
|
||||
|
||||
// HoldServer is the server API for Hold service.
|
||||
// All implementations must embed UnimplementedHoldServer
|
||||
// for forward compatibility.
|
||||
type HoldServer interface {
|
||||
GetInfo(context.Context, *GetInfoRequest) (*GetInfoResponse, error)
|
||||
Invoice(context.Context, *InvoiceRequest) (*InvoiceResponse, error)
|
||||
Inject(context.Context, *InjectRequest) (*InjectResponse, error)
|
||||
List(context.Context, *ListRequest) (*ListResponse, error)
|
||||
Settle(context.Context, *SettleRequest) (*SettleResponse, error)
|
||||
Cancel(context.Context, *CancelRequest) (*CancelResponse, error)
|
||||
// Cleans cancelled invoices
|
||||
Clean(context.Context, *CleanRequest) (*CleanResponse, error)
|
||||
Track(*TrackRequest, grpc.ServerStreamingServer[TrackResponse]) error
|
||||
TrackAll(*TrackAllRequest, grpc.ServerStreamingServer[TrackAllResponse]) error
|
||||
OnionMessages(grpc.BidiStreamingServer[OnionMessageResponse, OnionMessage]) error
|
||||
mustEmbedUnimplementedHoldServer()
|
||||
}
|
||||
|
||||
// UnimplementedHoldServer must be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedHoldServer struct{}
|
||||
|
||||
func (UnimplementedHoldServer) GetInfo(context.Context, *GetInfoRequest) (*GetInfoResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetInfo not implemented")
|
||||
}
|
||||
func (UnimplementedHoldServer) Invoice(context.Context, *InvoiceRequest) (*InvoiceResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method Invoice not implemented")
|
||||
}
|
||||
func (UnimplementedHoldServer) Inject(context.Context, *InjectRequest) (*InjectResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method Inject not implemented")
|
||||
}
|
||||
func (UnimplementedHoldServer) List(context.Context, *ListRequest) (*ListResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method List not implemented")
|
||||
}
|
||||
func (UnimplementedHoldServer) Settle(context.Context, *SettleRequest) (*SettleResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method Settle not implemented")
|
||||
}
|
||||
func (UnimplementedHoldServer) Cancel(context.Context, *CancelRequest) (*CancelResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method Cancel not implemented")
|
||||
}
|
||||
func (UnimplementedHoldServer) Clean(context.Context, *CleanRequest) (*CleanResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method Clean not implemented")
|
||||
}
|
||||
func (UnimplementedHoldServer) Track(*TrackRequest, grpc.ServerStreamingServer[TrackResponse]) error {
|
||||
return status.Error(codes.Unimplemented, "method Track not implemented")
|
||||
}
|
||||
func (UnimplementedHoldServer) TrackAll(*TrackAllRequest, grpc.ServerStreamingServer[TrackAllResponse]) error {
|
||||
return status.Error(codes.Unimplemented, "method TrackAll not implemented")
|
||||
}
|
||||
func (UnimplementedHoldServer) OnionMessages(grpc.BidiStreamingServer[OnionMessageResponse, OnionMessage]) error {
|
||||
return status.Error(codes.Unimplemented, "method OnionMessages not implemented")
|
||||
}
|
||||
func (UnimplementedHoldServer) mustEmbedUnimplementedHoldServer() {}
|
||||
func (UnimplementedHoldServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafeHoldServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to HoldServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeHoldServer interface {
|
||||
mustEmbedUnimplementedHoldServer()
|
||||
}
|
||||
|
||||
func RegisterHoldServer(s grpc.ServiceRegistrar, srv HoldServer) {
|
||||
// If the following call panics, it indicates UnimplementedHoldServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&Hold_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _Hold_GetInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetInfoRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(HoldServer).GetInfo(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Hold_GetInfo_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(HoldServer).GetInfo(ctx, req.(*GetInfoRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Hold_Invoice_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(InvoiceRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(HoldServer).Invoice(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Hold_Invoice_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(HoldServer).Invoice(ctx, req.(*InvoiceRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Hold_Inject_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(InjectRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(HoldServer).Inject(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Hold_Inject_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(HoldServer).Inject(ctx, req.(*InjectRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Hold_List_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(HoldServer).List(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Hold_List_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(HoldServer).List(ctx, req.(*ListRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Hold_Settle_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SettleRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(HoldServer).Settle(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Hold_Settle_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(HoldServer).Settle(ctx, req.(*SettleRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Hold_Cancel_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CancelRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(HoldServer).Cancel(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Hold_Cancel_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(HoldServer).Cancel(ctx, req.(*CancelRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Hold_Clean_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CleanRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(HoldServer).Clean(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Hold_Clean_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(HoldServer).Clean(ctx, req.(*CleanRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Hold_Track_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(TrackRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(HoldServer).Track(m, &grpc.GenericServerStream[TrackRequest, TrackResponse]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type Hold_TrackServer = grpc.ServerStreamingServer[TrackResponse]
|
||||
|
||||
func _Hold_TrackAll_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(TrackAllRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(HoldServer).TrackAll(m, &grpc.GenericServerStream[TrackAllRequest, TrackAllResponse]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type Hold_TrackAllServer = grpc.ServerStreamingServer[TrackAllResponse]
|
||||
|
||||
func _Hold_OnionMessages_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
return srv.(HoldServer).OnionMessages(&grpc.GenericServerStream[OnionMessageResponse, OnionMessage]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type Hold_OnionMessagesServer = grpc.BidiStreamingServer[OnionMessageResponse, OnionMessage]
|
||||
|
||||
// Hold_ServiceDesc is the grpc.ServiceDesc for Hold service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var Hold_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "hold.Hold",
|
||||
HandlerType: (*HoldServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "GetInfo",
|
||||
Handler: _Hold_GetInfo_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Invoice",
|
||||
Handler: _Hold_Invoice_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Inject",
|
||||
Handler: _Hold_Inject_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "List",
|
||||
Handler: _Hold_List_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Settle",
|
||||
Handler: _Hold_Settle_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Cancel",
|
||||
Handler: _Hold_Cancel_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Clean",
|
||||
Handler: _Hold_Clean_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "Track",
|
||||
Handler: _Hold_Track_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
{
|
||||
StreamName: "TrackAll",
|
||||
Handler: _Hold_TrackAll_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
{
|
||||
StreamName: "OnionMessages",
|
||||
Handler: _Hold_OnionMessages_Handler,
|
||||
ServerStreams: true,
|
||||
ClientStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "hold.proto",
|
||||
}
|
||||
|
|
@ -22,6 +22,7 @@ import (
|
|||
"github.com/getAlby/hub/events"
|
||||
"github.com/getAlby/hub/lnclient"
|
||||
"github.com/getAlby/hub/lnclient/cashu"
|
||||
"github.com/getAlby/hub/lnclient/cln"
|
||||
"github.com/getAlby/hub/lnclient/ldk"
|
||||
"github.com/getAlby/hub/lnclient/lnd"
|
||||
"github.com/getAlby/hub/lnclient/phoenixd"
|
||||
|
|
@ -372,6 +373,11 @@ func (svc *service) launchLNBackend(ctx context.Context, encryptionKey string) e
|
|||
cashuWorkdir := path.Join(svc.cfg.GetEnv().Workdir, "cashu")
|
||||
|
||||
lnClient, err = cashu.NewCashuService(svc.cfg, cashuWorkdir, mnemonic, cashuMintUrl)
|
||||
case config.CLNBackendType:
|
||||
CLNAddress, _ := svc.cfg.Get("CLNAddress", encryptionKey)
|
||||
CLNLightningDir, _ := svc.cfg.Get("CLNLightningDir", encryptionKey)
|
||||
CLNAddressHold, _ := svc.cfg.Get("CLNAddressHold", encryptionKey)
|
||||
lnClient, err = cln.NewCLNService(ctx, svc.eventPublisher, CLNAddress, CLNLightningDir, CLNAddressHold)
|
||||
default:
|
||||
logger.Logger.WithField("backend_type", lnBackend).Error("Unsupported LNBackendType")
|
||||
return fmt.Errorf("unsupported backend type: %s", lnBackend)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue