separate node logic

This commit is contained in:
Jesse de Wit
2023-08-14 13:10:46 +02:00
parent 25d205e05c
commit ddba2a114c
4 changed files with 171 additions and 134 deletions

View File

@@ -15,6 +15,7 @@ import (
"github.com/breez/lspd/interceptor"
"github.com/breez/lspd/lightning"
lspdrpc "github.com/breez/lspd/rpc"
"github.com/breez/lspd/shared"
ecies "github.com/ecies/go/v2"
"github.com/golang/protobuf/proto"
"google.golang.org/grpc/codes"
@@ -54,26 +55,26 @@ func (s *channelOpenerServer) ChannelInformation(ctx context.Context, in *lspdrp
}
return &lspdrpc.ChannelInformationReply{
Name: node.nodeConfig.Name,
Pubkey: node.nodeConfig.NodePubkey,
Host: node.nodeConfig.Host,
ChannelCapacity: int64(node.nodeConfig.PublicChannelAmount),
TargetConf: int32(node.nodeConfig.TargetConf),
MinHtlcMsat: int64(node.nodeConfig.MinHtlcMsat),
BaseFeeMsat: int64(node.nodeConfig.BaseFeeMsat),
FeeRate: node.nodeConfig.FeeRate,
TimeLockDelta: node.nodeConfig.TimeLockDelta,
ChannelFeePermyriad: int64(node.nodeConfig.ChannelFeePermyriad),
ChannelMinimumFeeMsat: int64(node.nodeConfig.ChannelMinimumFeeMsat),
LspPubkey: node.publicKey.SerializeCompressed(), // TODO: Is the publicKey different from the ecies public key?
MaxInactiveDuration: int64(node.nodeConfig.MaxInactiveDuration),
Name: node.NodeConfig.Name,
Pubkey: node.NodeConfig.NodePubkey,
Host: node.NodeConfig.Host,
ChannelCapacity: int64(node.NodeConfig.PublicChannelAmount),
TargetConf: int32(node.NodeConfig.TargetConf),
MinHtlcMsat: int64(node.NodeConfig.MinHtlcMsat),
BaseFeeMsat: int64(node.NodeConfig.BaseFeeMsat),
FeeRate: node.NodeConfig.FeeRate,
TimeLockDelta: node.NodeConfig.TimeLockDelta,
ChannelFeePermyriad: int64(node.NodeConfig.ChannelFeePermyriad),
ChannelMinimumFeeMsat: int64(node.NodeConfig.ChannelMinimumFeeMsat),
LspPubkey: node.PublicKey.SerializeCompressed(), // TODO: Is the publicKey different from the ecies public key?
MaxInactiveDuration: int64(node.NodeConfig.MaxInactiveDuration),
OpeningFeeParamsMenu: params,
}, nil
}
func (s *channelOpenerServer) createOpeningParamsMenu(
ctx context.Context,
node *node,
node *shared.Node,
token string,
) ([]*lspdrpc.OpeningFeeParams, error) {
var menu []*lspdrpc.OpeningFeeParams
@@ -136,13 +137,13 @@ func paramsHash(params *lspdrpc.OpeningFeeParams) ([]byte, error) {
return hash[:], nil
}
func createPromise(node *node, params *lspdrpc.OpeningFeeParams) (*string, error) {
func createPromise(node *shared.Node, params *lspdrpc.OpeningFeeParams) (*string, error) {
hash, err := paramsHash(params)
if err != nil {
return nil, err
}
// Sign the hash with the private key of the LSP id.
sig, err := ecdsa.SignCompact(node.privateKey, hash[:], true)
sig, err := ecdsa.SignCompact(node.PrivateKey, hash[:], true)
if err != nil {
log.Printf("createPromise: SignCompact error: %v", err)
return nil, err
@@ -151,7 +152,7 @@ func createPromise(node *node, params *lspdrpc.OpeningFeeParams) (*string, error
return &promise, nil
}
func verifyPromise(node *node, params *lspdrpc.OpeningFeeParams) error {
func verifyPromise(node *shared.Node, params *lspdrpc.OpeningFeeParams) error {
hash, err := paramsHash(params)
if err != nil {
return err
@@ -166,14 +167,14 @@ func verifyPromise(node *node, params *lspdrpc.OpeningFeeParams) error {
log.Printf("verifyPromise: RecoverCompact(%x) error: %v", sig, err)
return err
}
if !node.publicKey.IsEqual(pub) {
if !node.PublicKey.IsEqual(pub) {
log.Print("verifyPromise: not signed by us", err)
return fmt.Errorf("invalid promise")
}
return nil
}
func validateOpeningFeeParams(node *node, params *lspdrpc.OpeningFeeParams) bool {
func validateOpeningFeeParams(node *shared.Node, params *lspdrpc.OpeningFeeParams) bool {
if params == nil {
return false
}
@@ -206,10 +207,10 @@ func (s *channelOpenerServer) RegisterPayment(
return nil, err
}
data, err := ecies.Decrypt(node.eciesPrivateKey, in.Blob)
data, err := ecies.Decrypt(node.EciesPrivateKey, in.Blob)
if err != nil {
log.Printf("ecies.Decrypt(%x) error: %v", in.Blob, err)
data, err = btceclegacy.Decrypt(node.privateKey, in.Blob)
data, err = btceclegacy.Decrypt(node.PrivateKey, in.Blob)
if err != nil {
log.Printf("btcec.Decrypt(%x) error: %v", in.Blob, err)
return nil, fmt.Errorf("btcec.Decrypt(%x) error: %w", in.Blob, err)
@@ -247,10 +248,10 @@ func (s *channelOpenerServer) RegisterPayment(
} else {
log.Printf("DEPRECATED: RegisterPayment with deprecated fee mechanism.")
pi.OpeningFeeParams = &lspdrpc.OpeningFeeParams{
MinMsat: uint64(node.nodeConfig.ChannelMinimumFeeMsat),
Proportional: uint32(node.nodeConfig.ChannelFeePermyriad * 100),
MinMsat: uint64(node.NodeConfig.ChannelMinimumFeeMsat),
Proportional: uint32(node.NodeConfig.ChannelFeePermyriad * 100),
ValidUntil: time.Now().UTC().Add(time.Duration(time.Hour * 24)).Format(basetypes.TIME_FORMAT),
MaxIdleTime: uint32(node.nodeConfig.MaxInactiveDuration / 600),
MaxIdleTime: uint32(node.NodeConfig.MaxInactiveDuration / 600),
MaxClientToSelfDelay: uint32(10000),
}
}
@@ -282,25 +283,25 @@ func (s *channelOpenerServer) OpenChannel(ctx context.Context, in *lspdrpc.OpenC
return nil, err
}
r, err, _ := node.openChannelReqGroup.Do(in.Pubkey, func() (interface{}, error) {
r, err, _ := node.OpenChannelReqGroup.Do(in.Pubkey, func() (interface{}, error) {
pubkey, err := hex.DecodeString(in.Pubkey)
if err != nil {
return nil, err
}
channelCount, err := node.client.GetNodeChannelCount(pubkey)
channelCount, err := node.Client.GetNodeChannelCount(pubkey)
if err != nil {
return nil, err
}
var outPoint *wire.OutPoint
if channelCount == 0 {
outPoint, err = node.client.OpenChannel(&lightning.OpenChannelRequest{
CapacitySat: node.nodeConfig.ChannelAmount,
outPoint, err = node.Client.OpenChannel(&lightning.OpenChannelRequest{
CapacitySat: node.NodeConfig.ChannelAmount,
Destination: pubkey,
TargetConf: &node.nodeConfig.TargetConf,
MinHtlcMsat: node.nodeConfig.MinHtlcMsat,
IsPrivate: node.nodeConfig.ChannelPrivate,
TargetConf: &node.NodeConfig.TargetConf,
MinHtlcMsat: node.NodeConfig.MinHtlcMsat,
IsPrivate: node.NodeConfig.ChannelPrivate,
})
if err != nil {
@@ -320,13 +321,13 @@ func (s *channelOpenerServer) OpenChannel(ctx context.Context, in *lspdrpc.OpenC
return r.(*lspdrpc.OpenChannelReply), err
}
func (n *node) getSignedEncryptedData(in *lspdrpc.Encrypted) (string, []byte, bool, error) {
func getSignedEncryptedData(n *shared.Node, in *lspdrpc.Encrypted) (string, []byte, bool, error) {
usedEcies := true
signedBlob, err := ecies.Decrypt(n.eciesPrivateKey, in.Data)
signedBlob, err := ecies.Decrypt(n.EciesPrivateKey, in.Data)
if err != nil {
log.Printf("ecies.Decrypt(%x) error: %v", in.Data, err)
usedEcies = false
signedBlob, err = btceclegacy.Decrypt(n.privateKey, in.Data)
signedBlob, err = btceclegacy.Decrypt(n.PrivateKey, in.Data)
if err != nil {
log.Printf("btcec.Decrypt(%x) error: %v", in.Data, err)
return "", nil, usedEcies, fmt.Errorf("btcec.Decrypt(%x) error: %w", in.Data, err)
@@ -366,7 +367,7 @@ func (s *channelOpenerServer) CheckChannels(ctx context.Context, in *lspdrpc.Enc
return nil, err
}
nodeID, data, usedEcies, err := node.getSignedEncryptedData(in)
nodeID, data, usedEcies, err := getSignedEncryptedData(node, in)
if err != nil {
log.Printf("getSignedEncryptedData error: %v", err)
return nil, fmt.Errorf("getSignedEncryptedData error: %v", err)
@@ -377,7 +378,7 @@ func (s *channelOpenerServer) CheckChannels(ctx context.Context, in *lspdrpc.Enc
log.Printf("proto.Unmarshal(%x) error: %v", data, err)
return nil, fmt.Errorf("proto.Unmarshal(%x) error: %w", data, err)
}
closedChannels, err := node.client.GetClosedChannels(nodeID, checkChannelsRequest.WaitingCloseChannels)
closedChannels, err := node.Client.GetClosedChannels(nodeID, checkChannelsRequest.WaitingCloseChannels)
if err != nil {
log.Printf("GetClosedChannels(%v) error: %v", checkChannelsRequest.FakeChannels, err)
return nil, fmt.Errorf("GetClosedChannels(%v) error: %w", checkChannelsRequest.FakeChannels, err)
@@ -399,7 +400,7 @@ func (s *channelOpenerServer) CheckChannels(ctx context.Context, in *lspdrpc.Enc
var encrypted []byte
if usedEcies {
encrypted, err = ecies.Encrypt(node.eciesPublicKey, dataReply)
encrypted, err = ecies.Encrypt(node.EciesPublicKey, dataReply)
if err != nil {
log.Printf("ecies.Encrypt() error: %v", err)
return nil, fmt.Errorf("ecies.Encrypt() error: %w", err)
@@ -415,7 +416,7 @@ func (s *channelOpenerServer) CheckChannels(ctx context.Context, in *lspdrpc.Enc
return &lspdrpc.Encrypted{Data: encrypted}, nil
}
func (s *channelOpenerServer) getNode(ctx context.Context) (*node, string, error) {
func (s *channelOpenerServer) getNode(ctx context.Context) (*shared.Node, string, error) {
nd := ctx.Value(contextKey("node"))
if nd == nil {
return nil, "", status.Errorf(codes.PermissionDenied, "Not authorized")

View File

@@ -3,23 +3,16 @@ package main
import (
"context"
"crypto/tls"
"encoding/hex"
"fmt"
"log"
"net"
"strings"
"github.com/breez/lspd/cln"
"github.com/breez/lspd/config"
"github.com/breez/lspd/lightning"
"github.com/breez/lspd/lnd"
"github.com/breez/lspd/notifications"
lspdrpc "github.com/breez/lspd/rpc"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/breez/lspd/shared"
"github.com/caddyserver/certmagic"
ecies "github.com/ecies/go/v2"
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
"golang.org/x/sync/singleflight"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
@@ -27,119 +20,38 @@ import (
)
type grpcServer struct {
nodesService shared.NodesService
address string
certmagicDomain string
lis net.Listener
s *grpc.Server
nodes map[string]*node
c lspdrpc.ChannelOpenerServer
n notifications.NotificationsServer
}
type nodeContext struct {
token string
node *node
}
type node struct {
client lightning.Client
nodeConfig *config.NodeConfig
privateKey *btcec.PrivateKey
publicKey *btcec.PublicKey
eciesPrivateKey *ecies.PrivateKey
eciesPublicKey *ecies.PublicKey
openChannelReqGroup singleflight.Group
node *shared.Node
}
func NewGrpcServer(
configs []*config.NodeConfig,
nodesService shared.NodesService,
address string,
certmagicDomain string,
c lspdrpc.ChannelOpenerServer,
n notifications.NotificationsServer,
) (*grpcServer, error) {
if len(configs) == 0 {
return nil, fmt.Errorf("no nodes supplied")
}
nodes := make(map[string]*node)
for _, config := range configs {
pk, err := hex.DecodeString(config.LspdPrivateKey)
if err != nil {
return nil, fmt.Errorf("hex.DecodeString(config.lspdPrivateKey=%v) error: %v", config.LspdPrivateKey, err)
}
eciesPrivateKey := ecies.NewPrivateKeyFromBytes(pk)
eciesPublicKey := eciesPrivateKey.PublicKey
privateKey, publicKey := btcec.PrivKeyFromBytes(pk)
node := &node{
nodeConfig: config,
privateKey: privateKey,
publicKey: publicKey,
eciesPrivateKey: eciesPrivateKey,
eciesPublicKey: eciesPublicKey,
}
if config.Lnd == nil && config.Cln == nil {
return nil, fmt.Errorf("node has to be either cln or lnd")
}
if config.Lnd != nil && config.Cln != nil {
return nil, fmt.Errorf("node cannot be both cln and lnd")
}
if config.Lnd != nil {
node.client, err = lnd.NewLndClient(config.Lnd)
if err != nil {
return nil, err
}
}
if config.Cln != nil {
node.client, err = cln.NewClnClient(config.Cln.SocketPath)
if err != nil {
return nil, err
}
}
for _, token := range config.Tokens {
_, exists := nodes[token]
if exists {
return nil, fmt.Errorf("cannot have multiple nodes with the same token")
}
nodes[token] = node
}
}
return &grpcServer{
nodesService: nodesService,
address: address,
certmagicDomain: certmagicDomain,
nodes: nodes,
c: c,
n: n,
}, nil
}
func (s *grpcServer) Start() error {
// Make sure all nodes are available and set name and pubkey if not set
// in config.
for _, n := range s.nodes {
info, err := n.client.GetInfo()
if err != nil {
return fmt.Errorf("failed to get info from host %s", n.nodeConfig.Host)
}
if n.nodeConfig.Name == "" {
n.nodeConfig.Name = info.Alias
}
if n.nodeConfig.NodePubkey == "" {
n.nodeConfig.NodePubkey = info.Pubkey
}
}
var lis net.Listener
if s.certmagicDomain == "" {
var err error
@@ -167,8 +79,8 @@ func (s *grpcServer) Start() error {
}
token := strings.Replace(auth, "Bearer ", "", 1)
node, ok := s.nodes[token]
if !ok {
node, err := s.nodesService.GetNode(token)
if err != nil {
continue
}

View File

@@ -20,6 +20,7 @@ import (
"github.com/breez/lspd/mempool"
"github.com/breez/lspd/notifications"
"github.com/breez/lspd/postgresql"
"github.com/breez/lspd/shared"
"github.com/btcsuite/btcd/btcec/v2"
)
@@ -44,6 +45,11 @@ func main() {
log.Fatalf("need at least one node configured in NODES.")
}
nodesService, err := shared.NewNodesService(nodes)
if err != nil {
log.Fatalf("failed to create nodes service: %v", err)
}
mempoolUrl := os.Getenv("MEMPOOL_API_BASE_URL")
if mempoolUrl == "" {
log.Fatalf("No mempool url configured.")
@@ -136,7 +142,7 @@ func main() {
certMagicDomain := os.Getenv("CERTMAGIC_DOMAIN")
cs := NewChannelOpenerServer(interceptStore)
ns := notifications.NewNotificationsServer(notificationsStore)
s, err := NewGrpcServer(nodes, address, certMagicDomain, cs, ns)
s, err := NewGrpcServer(nodesService, address, certMagicDomain, cs, ns)
if err != nil {
log.Fatalf("failed to initialize grpc server: %v", err)
}

118
shared/nodes_service.go Normal file
View File

@@ -0,0 +1,118 @@
package shared
import (
"encoding/hex"
"errors"
"fmt"
"github.com/breez/lspd/cln"
"github.com/breez/lspd/config"
"github.com/breez/lspd/lightning"
"github.com/breez/lspd/lnd"
"github.com/btcsuite/btcd/btcec/v2"
ecies "github.com/ecies/go/v2"
"golang.org/x/sync/singleflight"
)
type Node struct {
Client lightning.Client
NodeConfig *config.NodeConfig
PrivateKey *btcec.PrivateKey
PublicKey *btcec.PublicKey
EciesPrivateKey *ecies.PrivateKey
EciesPublicKey *ecies.PublicKey
OpenChannelReqGroup singleflight.Group
}
type NodesService interface {
GetNode(token string) (*Node, error)
}
type nodesService struct {
nodes map[string]*Node
}
func NewNodesService(configs []*config.NodeConfig) (NodesService, error) {
if len(configs) == 0 {
return nil, fmt.Errorf("no nodes supplied")
}
nodes := make(map[string]*Node)
for _, config := range configs {
pk, err := hex.DecodeString(config.LspdPrivateKey)
if err != nil {
return nil, fmt.Errorf("hex.DecodeString(config.lspdPrivateKey=%v) error: %v", config.LspdPrivateKey, err)
}
eciesPrivateKey := ecies.NewPrivateKeyFromBytes(pk)
eciesPublicKey := eciesPrivateKey.PublicKey
privateKey, publicKey := btcec.PrivKeyFromBytes(pk)
node := &Node{
NodeConfig: config,
PrivateKey: privateKey,
PublicKey: publicKey,
EciesPrivateKey: eciesPrivateKey,
EciesPublicKey: eciesPublicKey,
}
if config.Lnd == nil && config.Cln == nil {
return nil, fmt.Errorf("node has to be either cln or lnd")
}
if config.Lnd != nil && config.Cln != nil {
return nil, fmt.Errorf("node cannot be both cln and lnd")
}
if config.Lnd != nil {
node.Client, err = lnd.NewLndClient(config.Lnd)
if err != nil {
return nil, err
}
}
if config.Cln != nil {
node.Client, err = cln.NewClnClient(config.Cln.SocketPath)
if err != nil {
return nil, err
}
}
// Make sure the nodes is available and set name and pubkey if not set
// in config.
info, err := node.Client.GetInfo()
if err != nil {
return nil, fmt.Errorf("failed to get info from host %s", node.NodeConfig.Host)
}
if node.NodeConfig.Name == "" {
node.NodeConfig.Name = info.Alias
}
if node.NodeConfig.NodePubkey == "" {
node.NodeConfig.NodePubkey = info.Pubkey
}
for _, token := range config.Tokens {
_, exists := nodes[token]
if exists {
return nil, fmt.Errorf("cannot have multiple nodes with the same token")
}
nodes[token] = node
}
}
return &nodesService{
nodes: nodes,
}, nil
}
var ErrNodeNotFound = errors.New("node not found")
func (s *nodesService) GetNode(token string) (*Node, error) {
node, ok := s.nodes[token]
if !ok {
return nil, ErrNodeNotFound
}
return node, nil
}