diff --git a/server.go b/channel_opener_server.go similarity index 72% rename from server.go rename to channel_opener_server.go index db35c4a..56d2bbb 100644 --- a/server.go +++ b/channel_opener_server.go @@ -3,64 +3,46 @@ package main import ( "context" "crypto/sha256" - "crypto/tls" "encoding/hex" "encoding/json" "fmt" "log" - "net" "sort" - "strings" "time" "github.com/breez/lspd/basetypes" "github.com/breez/lspd/btceclegacy" - "github.com/breez/lspd/cln" - "github.com/breez/lspd/config" "github.com/breez/lspd/interceptor" "github.com/breez/lspd/lightning" - "github.com/breez/lspd/lnd" 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/btcec/v2/ecdsa" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" - "github.com/caddyserver/certmagic" "github.com/lightningnetwork/lnd/lnwire" ) -type server struct { +type channelOpenerServer struct { lspdrpc.ChannelOpenerServer - address string - certmagicDomain string - lis net.Listener - s *grpc.Server - nodes map[string]*node - store interceptor.InterceptStore + store interceptor.InterceptStore } -type node struct { - client lightning.Client - nodeConfig *config.NodeConfig - privateKey *btcec.PrivateKey - publicKey *btcec.PublicKey - eciesPrivateKey *ecies.PrivateKey - eciesPublicKey *ecies.PublicKey - openChannelReqGroup singleflight.Group +func NewChannelOpenerServer( + store interceptor.InterceptStore, +) *channelOpenerServer { + return &channelOpenerServer{ + store: store, + } } type contextKey string -func (s *server) ChannelInformation(ctx context.Context, in *lspdrpc.ChannelInformationRequest) (*lspdrpc.ChannelInformationReply, error) { +func (s *channelOpenerServer) ChannelInformation(ctx context.Context, in *lspdrpc.ChannelInformationRequest) (*lspdrpc.ChannelInformationReply, error) { node, token, err := s.getNode(ctx) if err != nil { return nil, err @@ -89,7 +71,7 @@ func (s *server) ChannelInformation(ctx context.Context, in *lspdrpc.ChannelInfo }, nil } -func (s *server) createOpeningParamsMenu( +func (s *channelOpenerServer) createOpeningParamsMenu( ctx context.Context, node *node, token string, @@ -207,7 +189,7 @@ func validateOpeningFeeParams(node *node, params *lspdrpc.OpeningFeeParams) bool return true } -func (s *server) RegisterPayment( +func (s *channelOpenerServer) RegisterPayment( ctx context.Context, in *lspdrpc.RegisterPaymentRequest, ) (*lspdrpc.RegisterPaymentReply, error) { @@ -286,7 +268,7 @@ func (s *server) RegisterPayment( return &lspdrpc.RegisterPaymentReply{}, nil } -func (s *server) OpenChannel(ctx context.Context, in *lspdrpc.OpenChannelRequest) (*lspdrpc.OpenChannelReply, error) { +func (s *channelOpenerServer) OpenChannel(ctx context.Context, in *lspdrpc.OpenChannelRequest) (*lspdrpc.OpenChannelReply, error) { node, _, err := s.getNode(ctx) if err != nil { return nil, err @@ -370,7 +352,7 @@ func (n *node) getSignedEncryptedData(in *lspdrpc.Encrypted) (string, []byte, bo return hex.EncodeToString(signed.Pubkey), signed.Data, usedEcies, nil } -func (s *server) CheckChannels(ctx context.Context, in *lspdrpc.Encrypted) (*lspdrpc.Encrypted, error) { +func (s *channelOpenerServer) CheckChannels(ctx context.Context, in *lspdrpc.Encrypted) (*lspdrpc.Encrypted, error) { node, _, err := s.getNode(ctx) if err != nil { return nil, err @@ -425,164 +407,18 @@ func (s *server) CheckChannels(ctx context.Context, in *lspdrpc.Encrypted) (*lsp return &lspdrpc.Encrypted{Data: encrypted}, nil } -func NewGrpcServer( - configs []*config.NodeConfig, - address string, - certmagicDomain string, - store interceptor.InterceptStore, -) (*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 = 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 &server{ - address: address, - certmagicDomain: certmagicDomain, - nodes: nodes, - store: store, - }, 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) - _, ok := s.nodes[token] - if !ok { - continue - } - - return handler(context.WithValue(ctx, contextKey("token"), token), req) - } - } - return nil, status.Errorf(codes.PermissionDenied, "Not authorized") - }), - ) - lspdrpc.RegisterChannelOpenerServer(srv, s) - - 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 (s *server) getNode(ctx context.Context) (*node, string, error) { - tok := ctx.Value(contextKey("token")) - if tok == nil { +func (s *channelOpenerServer) getNode(ctx context.Context) (*node, string, error) { + nd := ctx.Value(contextKey("node")) + if nd == nil { return nil, "", status.Errorf(codes.PermissionDenied, "Not authorized") } - token, ok := tok.(string) + nodeContext, ok := nd.(*nodeContext) if !ok { return nil, "", status.Errorf(codes.PermissionDenied, "Not authorized") } - node, ok := s.nodes[token] - if !ok { - return nil, "", status.Errorf(codes.PermissionDenied, "Not authorized") - } - return node, token, nil + + return nodeContext.node, nodeContext.token, nil } func checkPayment(params *lspdrpc.OpeningFeeParams, incomingAmountMsat, outgoingAmountMsat int64) error { diff --git a/grpc_server.go b/grpc_server.go new file mode 100644 index 0000000..4c45bc9 --- /dev/null +++ b/grpc_server.go @@ -0,0 +1,196 @@ +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" + lspdrpc "github.com/breez/lspd/rpc" + "github.com/btcsuite/btcd/btcec/v2" + "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" + "google.golang.org/grpc/status" +) + +type grpcServer struct { + address string + certmagicDomain string + lis net.Listener + s *grpc.Server + nodes map[string]*node + c lspdrpc.ChannelOpenerServer +} + +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 +} + +func NewGrpcServer( + configs []*config.NodeConfig, + address string, + certmagicDomain string, + c lspdrpc.ChannelOpenerServer, +) (*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{ + address: address, + certmagicDomain: certmagicDomain, + nodes: nodes, + c: c, + }, 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 + 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, contextKey("node"), &nodeContext{ + token: token, + node: node, + }), req) + } + } + return nil, status.Errorf(codes.PermissionDenied, "Not authorized") + }), + ) + lspdrpc.RegisterChannelOpenerServer(srv, s.c) + + 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 *grpcServer) Stop() { + srv := s.s + if srv != nil { + srv.GracefulStop() + } +} diff --git a/main.go b/main.go index 2e3df49..9c20127 100644 --- a/main.go +++ b/main.go @@ -117,7 +117,8 @@ func main() { address := os.Getenv("LISTEN_ADDRESS") certMagicDomain := os.Getenv("CERTMAGIC_DOMAIN") - s, err := NewGrpcServer(nodes, address, certMagicDomain, interceptStore) + cs := NewChannelOpenerServer(interceptStore) + s, err := NewGrpcServer(nodes, address, certMagicDomain, cs) if err != nil { log.Fatalf("failed to initialize grpc server: %v", err) }