Files
lspd/server.go
Roei Erez b83019e5c6 Remove invalid fake channels check.
We no longer need to check for confirmed channels
so now we return empty map instead to satisfy the client.
This is untill we will release a new client that doesn't use this endpoint at all.
2023-01-18 10:31:49 +02:00

395 lines
12 KiB
Go

package main
import (
"context"
"crypto/tls"
"encoding/hex"
"fmt"
"log"
"net"
"strings"
"github.com/breez/lspd/btceclegacy"
lspdrpc "github.com/breez/lspd/rpc"
ecies "github.com/ecies/go/v2"
"github.com/golang/protobuf/proto"
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"
"google.golang.org/grpc/status"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/caddyserver/certmagic"
"github.com/lightningnetwork/lnd/lnwire"
)
type server struct {
address string
certmagicDomain string
lis net.Listener
s *grpc.Server
nodes map[string]*node
}
type node struct {
client LightningClient
nodeConfig *NodeConfig
privateKey *btcec.PrivateKey
publicKey *btcec.PublicKey
eciesPrivateKey *ecies.PrivateKey
eciesPublicKey *ecies.PublicKey
openChannelReqGroup singleflight.Group
}
func (s *server) ChannelInformation(ctx context.Context, in *lspdrpc.ChannelInformationRequest) (*lspdrpc.ChannelInformationReply, error) {
node, err := getNode(ctx)
if err != nil {
return nil, err
}
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),
}, nil
}
func (s *server) RegisterPayment(ctx context.Context, in *lspdrpc.RegisterPaymentRequest) (*lspdrpc.RegisterPaymentReply, error) {
node, err := getNode(ctx)
if err != nil {
return nil, err
}
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)
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)
}
}
var pi lspdrpc.PaymentInformation
err = proto.Unmarshal(data, &pi)
if err != nil {
log.Printf("proto.Unmarshal(%x) error: %v", data, err)
return nil, fmt.Errorf("proto.Unmarshal(%x) error: %w", data, err)
}
log.Printf("RegisterPayment - Destination: %x, pi.PaymentHash: %x, pi.PaymentSecret: %x, pi.IncomingAmountMsat: %v, pi.OutgoingAmountMsat: %v",
pi.Destination, pi.PaymentHash, pi.PaymentSecret, pi.IncomingAmountMsat, pi.OutgoingAmountMsat)
err = checkPayment(node.nodeConfig, pi.IncomingAmountMsat, pi.OutgoingAmountMsat)
if err != nil {
log.Printf("checkPayment(%v, %v) error: %v", pi.IncomingAmountMsat, pi.OutgoingAmountMsat, err)
return nil, fmt.Errorf("checkPayment(%v, %v) error: %v", pi.IncomingAmountMsat, pi.OutgoingAmountMsat, err)
}
err = registerPayment(pi.Destination, pi.PaymentHash, pi.PaymentSecret, pi.IncomingAmountMsat, pi.OutgoingAmountMsat)
if err != nil {
log.Printf("RegisterPayment() error: %v", err)
return nil, fmt.Errorf("RegisterPayment() error: %w", err)
}
return &lspdrpc.RegisterPaymentReply{}, nil
}
func (s *server) OpenChannel(ctx context.Context, in *lspdrpc.OpenChannelRequest) (*lspdrpc.OpenChannelReply, error) {
node, err := getNode(ctx)
if err != nil {
return nil, err
}
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)
if err != nil {
return nil, err
}
var outPoint *wire.OutPoint
if channelCount == 0 {
outPoint, err = node.client.OpenChannel(&OpenChannelRequest{
CapacitySat: node.nodeConfig.ChannelAmount,
Destination: pubkey,
TargetConf: node.nodeConfig.TargetConf,
MinHtlcMsat: node.nodeConfig.MinHtlcMsat,
IsPrivate: node.nodeConfig.ChannelPrivate,
})
if err != nil {
log.Printf("Error in OpenChannel: %v", err)
return nil, err
}
log.Printf("Response from OpenChannel: (TX: %v)", outPoint.String())
}
return &lspdrpc.OpenChannelReply{TxHash: outPoint.Hash.String(), OutputIndex: outPoint.Index}, nil
})
if err != nil {
return nil, err
}
return r.(*lspdrpc.OpenChannelReply), err
}
func (n *node) getSignedEncryptedData(in *lspdrpc.Encrypted) (string, []byte, bool, error) {
usedEcies := true
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)
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)
}
}
var signed lspdrpc.Signed
err = proto.Unmarshal(signedBlob, &signed)
if err != nil {
log.Printf("proto.Unmarshal(%x) error: %v", signedBlob, err)
return "", nil, usedEcies, fmt.Errorf("proto.Unmarshal(%x) error: %w", signedBlob, err)
}
pubkey, err := btcec.ParsePubKey(signed.Pubkey)
if err != nil {
log.Printf("unable to parse pubkey: %v", err)
return "", nil, usedEcies, fmt.Errorf("unable to parse pubkey: %w", err)
}
wireSig, err := lnwire.NewSigFromRawSignature(signed.Signature)
if err != nil {
return "", nil, usedEcies, fmt.Errorf("failed to decode signature: %v", err)
}
sig, err := wireSig.ToSignature()
if err != nil {
return "", nil, usedEcies, fmt.Errorf("failed to convert from wire format: %v",
err)
}
// The signature is over the sha256 hash of the message.
digest := chainhash.HashB(signed.Data)
if !sig.Verify(digest, pubkey) {
return "", nil, usedEcies, fmt.Errorf("invalid signature")
}
return hex.EncodeToString(signed.Pubkey), signed.Data, usedEcies, nil
}
func (s *server) CheckChannels(ctx context.Context, in *lspdrpc.Encrypted) (*lspdrpc.Encrypted, error) {
node, err := getNode(ctx)
if err != nil {
return nil, err
}
nodeID, data, usedEcies, err := node.getSignedEncryptedData(in)
if err != nil {
log.Printf("getSignedEncryptedData error: %v", err)
return nil, fmt.Errorf("getSignedEncryptedData error: %v", err)
}
var checkChannelsRequest lspdrpc.CheckChannelsRequest
err = proto.Unmarshal(data, &checkChannelsRequest)
if err != nil {
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)
if err != nil {
log.Printf("GetClosedChannels(%v) error: %v", checkChannelsRequest.FakeChannels, err)
return nil, fmt.Errorf("GetClosedChannels(%v) error: %w", checkChannelsRequest.FakeChannels, err)
}
checkChannelsReply := lspdrpc.CheckChannelsReply{
NotFakeChannels: make(map[string]uint64),
ClosedChannels: closedChannels,
}
dataReply, err := proto.Marshal(&checkChannelsReply)
if err != nil {
log.Printf("proto.Marshall() error: %v", err)
return nil, fmt.Errorf("proto.Marshal() error: %w", err)
}
pubkey, err := btcec.ParsePubKey(checkChannelsRequest.EncryptPubkey)
if err != nil {
log.Printf("unable to parse pubkey: %v", err)
return nil, fmt.Errorf("unable to parse pubkey: %w", err)
}
var encrypted []byte
if usedEcies {
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)
}
} else {
encrypted, err = btceclegacy.Encrypt(pubkey, dataReply)
if err != nil {
log.Printf("btcec.Encrypt() error: %v", err)
return nil, fmt.Errorf("btcec.Encrypt() error: %w", err)
}
}
return &lspdrpc.Encrypted{Data: encrypted}, nil
}
func NewGrpcServer(configs []*NodeConfig, address string, certmagicDomain string) (*server, 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 = NewLndClient(config.Lnd)
if err != nil {
return nil, err
}
}
if config.Cln != nil {
node.client, err = NewClnClient(config.Cln.SocketPath)
if err != nil {
return nil, err
}
}
_, exists := nodes[config.Token]
if exists {
return nil, fmt.Errorf("cannot have multiple nodes with the same token")
}
nodes[config.Token] = node
}
return &server{
address: address,
certmagicDomain: certmagicDomain,
nodes: nodes,
}, nil
}
func (s *server) 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
lis, err = net.Listen("tcp", s.address)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
} else {
tlsConfig, err := certmagic.TLS([]string{s.certmagicDomain})
if err != nil {
log.Fatalf("failed to run certmagic: %v", err)
}
lis, err = tls.Listen("tcp", s.address, tlsConfig)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
}
srv := grpc.NewServer(
grpc_middleware.WithUnaryServerChain(func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
if md, ok := metadata.FromIncomingContext(ctx); ok {
for _, auth := range md.Get("authorization") {
if !strings.HasPrefix(auth, "Bearer ") {
continue
}
token := strings.Replace(auth, "Bearer ", "", 1)
node, ok := s.nodes[token]
if !ok {
continue
}
return handler(context.WithValue(ctx, "node", node), req)
}
}
return nil, status.Errorf(codes.PermissionDenied, "Not authorized")
}),
)
lspdrpc.RegisterChannelOpenerServer(srv, &server{})
s.s = srv
s.lis = lis
if err := srv.Serve(lis); err != nil {
return fmt.Errorf("failed to serve: %v", err)
}
return nil
}
func (s *server) Stop() {
srv := s.s
if srv != nil {
srv.GracefulStop()
}
}
func getNode(ctx context.Context) (*node, error) {
n := ctx.Value("node")
if n == nil {
return nil, status.Errorf(codes.PermissionDenied, "Not authorized")
}
node, ok := n.(*node)
if !ok || node == nil {
return nil, status.Errorf(codes.PermissionDenied, "Not authorized")
}
return node, nil
}