github.com/decred/dcrlnd@v0.7.6/lnrpc/invoicesrpc/invoices_server.go (about)

     1  //go:build !no_invoicesrpc
     2  // +build !no_invoicesrpc
     3  
     4  package invoicesrpc
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"os"
    11  	"path/filepath"
    12  
    13  	"google.golang.org/grpc"
    14  	"google.golang.org/grpc/codes"
    15  	"google.golang.org/grpc/status"
    16  	"gopkg.in/macaroon-bakery.v2/bakery"
    17  
    18  	"github.com/decred/dcrlnd/channeldb"
    19  	"github.com/decred/dcrlnd/lnrpc"
    20  	"github.com/decred/dcrlnd/lntypes"
    21  	"github.com/decred/dcrlnd/macaroons"
    22  	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
    23  )
    24  
    25  const (
    26  	// subServerName is the name of the sub rpc server. We'll use this name
    27  	// to register ourselves, and we also require that the main
    28  	// SubServerConfigDispatcher instance recognize it as the name of our
    29  	// RPC service.
    30  	subServerName = "InvoicesRPC"
    31  )
    32  
    33  var (
    34  	// macaroonOps are the set of capabilities that our minted macaroon (if
    35  	// it doesn't already exist) will have.
    36  	macaroonOps = []bakery.Op{
    37  		{
    38  			Entity: "invoices",
    39  			Action: "write",
    40  		},
    41  		{
    42  			Entity: "invoices",
    43  			Action: "read",
    44  		},
    45  	}
    46  
    47  	// macPermissions maps RPC calls to the permissions they require.
    48  	macPermissions = map[string][]bakery.Op{
    49  		"/invoicesrpc.Invoices/SubscribeSingleInvoice": {{
    50  			Entity: "invoices",
    51  			Action: "read",
    52  		}},
    53  		"/invoicesrpc.Invoices/SettleInvoice": {{
    54  			Entity: "invoices",
    55  			Action: "write",
    56  		}},
    57  		"/invoicesrpc.Invoices/CancelInvoice": {{
    58  			Entity: "invoices",
    59  			Action: "write",
    60  		}},
    61  		"/invoicesrpc.Invoices/AddHoldInvoice": {{
    62  			Entity: "invoices",
    63  			Action: "write",
    64  		}},
    65  		"/invoicesrpc.Invoices/LookupInvoiceV2": {{
    66  			Entity: "invoices",
    67  			Action: "write",
    68  		}},
    69  	}
    70  
    71  	// DefaultInvoicesMacFilename is the default name of the invoices
    72  	// macaroon that we expect to find via a file handle within the main
    73  	// configuration file in this package.
    74  	DefaultInvoicesMacFilename = "invoices.macaroon"
    75  )
    76  
    77  // ServerShell is a shell struct holding a reference to the actual sub-server.
    78  // It is used to register the gRPC sub-server with the root server before we
    79  // have the necessary dependencies to populate the actual sub-server.
    80  type ServerShell struct {
    81  	InvoicesServer
    82  }
    83  
    84  // Server is a sub-server of the main RPC server: the invoices RPC. This sub
    85  // RPC server allows external callers to access the status of the invoices
    86  // currently active within lnd, as well as configuring it at runtime.
    87  type Server struct {
    88  	quit chan struct{}
    89  
    90  	cfg *Config
    91  }
    92  
    93  // A compile time check to ensure that Server fully implements the
    94  // InvoicesServer gRPC service.
    95  var _ InvoicesServer = (*Server)(nil)
    96  
    97  // New returns a new instance of the invoicesrpc Invoices sub-server. We also
    98  // return the set of permissions for the macaroons that we may create within
    99  // this method. If the macaroons we need aren't found in the filepath, then
   100  // we'll create them on start up. If we're unable to locate, or create the
   101  // macaroons we need, then we'll return with an error.
   102  func New(cfg *Config) (*Server, lnrpc.MacaroonPerms, error) {
   103  	// If the path of the invoices macaroon wasn't specified, then we'll
   104  	// assume that it's found at the default network directory.
   105  	macFilePath := filepath.Join(
   106  		cfg.NetworkDir, DefaultInvoicesMacFilename,
   107  	)
   108  
   109  	// Now that we know the full path of the invoices macaroon, we can
   110  	// check to see if we need to create it or not. If stateless_init is set
   111  	// then we don't write the macaroons.
   112  	if cfg.MacService != nil && !cfg.MacService.StatelessInit &&
   113  		!lnrpc.FileExists(macFilePath) {
   114  
   115  		log.Infof("Baking macaroons for invoices RPC Server at: %v",
   116  			macFilePath)
   117  
   118  		// At this point, we know that the invoices macaroon doesn't
   119  		// yet, exist, so we need to create it with the help of the
   120  		// main macaroon service.
   121  		invoicesMac, err := cfg.MacService.NewMacaroon(
   122  			context.Background(), macaroons.DefaultRootKeyID,
   123  			macaroonOps...,
   124  		)
   125  		if err != nil {
   126  			return nil, nil, err
   127  		}
   128  		invoicesMacBytes, err := invoicesMac.M().MarshalBinary()
   129  		if err != nil {
   130  			return nil, nil, err
   131  		}
   132  		err = ioutil.WriteFile(macFilePath, invoicesMacBytes, 0644)
   133  		if err != nil {
   134  			_ = os.Remove(macFilePath)
   135  			return nil, nil, err
   136  		}
   137  	}
   138  
   139  	server := &Server{
   140  		cfg:  cfg,
   141  		quit: make(chan struct{}, 1),
   142  	}
   143  
   144  	return server, macPermissions, nil
   145  }
   146  
   147  // Start launches any helper goroutines required for the Server to function.
   148  //
   149  // NOTE: This is part of the lnrpc.SubServer interface.
   150  func (s *Server) Start() error {
   151  	return nil
   152  }
   153  
   154  // Stop signals any active goroutines for a graceful closure.
   155  //
   156  // NOTE: This is part of the lnrpc.SubServer interface.
   157  func (s *Server) Stop() error {
   158  	close(s.quit)
   159  
   160  	return nil
   161  }
   162  
   163  // Name returns a unique string representation of the sub-server. This can be
   164  // used to identify the sub-server and also de-duplicate them.
   165  //
   166  // NOTE: This is part of the lnrpc.SubServer interface.
   167  func (s *Server) Name() string {
   168  	return subServerName
   169  }
   170  
   171  // RegisterWithRootServer will be called by the root gRPC server to direct a sub
   172  // RPC server to register itself with the main gRPC root server. Until this is
   173  // called, each sub-server won't be able to have requests routed towards it.
   174  //
   175  // NOTE: This is part of the lnrpc.GrpcHandler interface.
   176  func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
   177  	// We make sure that we register it with the main gRPC server to ensure
   178  	// all our methods are routed properly.
   179  	RegisterInvoicesServer(grpcServer, r)
   180  
   181  	log.Debugf("Invoices RPC server successfully registered with root " +
   182  		"gRPC server")
   183  
   184  	return nil
   185  }
   186  
   187  // RegisterWithRestServer will be called by the root REST mux to direct a sub
   188  // RPC server to register itself with the main REST mux server. Until this is
   189  // called, each sub-server won't be able to have requests routed towards it.
   190  //
   191  // NOTE: This is part of the lnrpc.GrpcHandler interface.
   192  func (r *ServerShell) RegisterWithRestServer(ctx context.Context,
   193  	mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error {
   194  
   195  	// We make sure that we register it with the main REST server to ensure
   196  	// all our methods are routed properly.
   197  	err := RegisterInvoicesHandlerFromEndpoint(ctx, mux, dest, opts)
   198  	if err != nil {
   199  		log.Errorf("Could not register Invoices REST server "+
   200  			"with root REST server: %v", err)
   201  		return err
   202  	}
   203  
   204  	log.Debugf("Invoices REST server successfully registered with " +
   205  		"root REST server")
   206  	return nil
   207  }
   208  
   209  // CreateSubServer populates the subserver's dependencies using the passed
   210  // SubServerConfigDispatcher. This method should fully initialize the
   211  // sub-server instance, making it ready for action. It returns the macaroon
   212  // permissions that the sub-server wishes to pass on to the root server for all
   213  // methods routed towards it.
   214  //
   215  // NOTE: This is part of the lnrpc.GrpcHandler interface.
   216  func (r *ServerShell) CreateSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (
   217  	lnrpc.SubServer, lnrpc.MacaroonPerms, error) {
   218  
   219  	subServer, macPermissions, err := createNewSubServer(configRegistry)
   220  	if err != nil {
   221  		return nil, nil, err
   222  	}
   223  
   224  	r.InvoicesServer = subServer
   225  	return subServer, macPermissions, nil
   226  }
   227  
   228  // SubscribeSingleInvoice returns a uni-directional stream (server -> client)
   229  // for notifying the client of state changes for a specified invoice.
   230  func (s *Server) SubscribeSingleInvoice(req *SubscribeSingleInvoiceRequest,
   231  	updateStream Invoices_SubscribeSingleInvoiceServer) error {
   232  
   233  	hash, err := lntypes.MakeHash(req.RHash)
   234  	if err != nil {
   235  		return err
   236  	}
   237  
   238  	invoiceClient, err := s.cfg.InvoiceRegistry.SubscribeSingleInvoice(hash)
   239  	if err != nil {
   240  		return err
   241  	}
   242  	defer invoiceClient.Cancel()
   243  
   244  	for {
   245  		select {
   246  		case newInvoice := <-invoiceClient.Updates:
   247  			rpcInvoice, err := CreateRPCInvoice(
   248  				newInvoice, s.cfg.ChainParams,
   249  			)
   250  			if err != nil {
   251  				return err
   252  			}
   253  
   254  			if err := updateStream.Send(rpcInvoice); err != nil {
   255  				return err
   256  			}
   257  
   258  			// If we have reached a terminal state, close the
   259  			// stream with no error.
   260  			if newInvoice.State.IsFinal() {
   261  				return nil
   262  			}
   263  
   264  		case <-updateStream.Context().Done():
   265  			return updateStream.Context().Err()
   266  
   267  		case <-s.quit:
   268  			return nil
   269  		}
   270  	}
   271  }
   272  
   273  // SettleInvoice settles an accepted invoice. If the invoice is already settled,
   274  // this call will succeed.
   275  func (s *Server) SettleInvoice(ctx context.Context,
   276  	in *SettleInvoiceMsg) (*SettleInvoiceResp, error) {
   277  
   278  	preimage, err := lntypes.MakePreimage(in.Preimage)
   279  	if err != nil {
   280  		return nil, err
   281  	}
   282  
   283  	err = s.cfg.InvoiceRegistry.SettleHodlInvoice(preimage)
   284  	if err != nil && err != channeldb.ErrInvoiceAlreadySettled {
   285  		return nil, err
   286  	}
   287  
   288  	return &SettleInvoiceResp{}, nil
   289  }
   290  
   291  // CancelInvoice cancels a currently open invoice. If the invoice is already
   292  // canceled, this call will succeed. If the invoice is already settled, it will
   293  // fail.
   294  func (s *Server) CancelInvoice(ctx context.Context,
   295  	in *CancelInvoiceMsg) (*CancelInvoiceResp, error) {
   296  
   297  	paymentHash, err := lntypes.MakeHash(in.PaymentHash)
   298  	if err != nil {
   299  		return nil, err
   300  	}
   301  
   302  	err = s.cfg.InvoiceRegistry.CancelInvoice(paymentHash)
   303  	if err != nil {
   304  		return nil, err
   305  	}
   306  
   307  	log.Infof("Canceled invoice %v", paymentHash)
   308  
   309  	return &CancelInvoiceResp{}, nil
   310  }
   311  
   312  // AddHoldInvoice attempts to add a new hold invoice to the invoice database.
   313  // Any duplicated invoices are rejected, therefore all invoices *must* have a
   314  // unique payment hash.
   315  func (s *Server) AddHoldInvoice(ctx context.Context,
   316  	invoice *AddHoldInvoiceRequest) (*AddHoldInvoiceResp, error) {
   317  
   318  	addInvoiceCfg := &AddInvoiceConfig{
   319  		AddInvoice:            s.cfg.InvoiceRegistry.AddInvoice,
   320  		IsChannelActive:       s.cfg.IsChannelActive,
   321  		ChainParams:           s.cfg.ChainParams,
   322  		NodeSigner:            s.cfg.NodeSigner,
   323  		DefaultCLTVExpiry:     s.cfg.DefaultCLTVExpiry,
   324  		ChanDB:                s.cfg.ChanStateDB,
   325  		Graph:                 s.cfg.GraphDB,
   326  		GenInvoiceFeatures:    s.cfg.GenInvoiceFeatures,
   327  		GenAmpInvoiceFeatures: s.cfg.GenAmpInvoiceFeatures,
   328  	}
   329  
   330  	hash, err := lntypes.MakeHash(invoice.Hash)
   331  	if err != nil {
   332  		return nil, err
   333  	}
   334  
   335  	value, err := lnrpc.UnmarshallAmt(invoice.Value, invoice.ValueMAtoms)
   336  	if err != nil {
   337  		return nil, err
   338  	}
   339  
   340  	// Convert the passed routing hints to the required format.
   341  	routeHints, err := CreateZpay32HopHints(invoice.RouteHints)
   342  	if err != nil {
   343  		return nil, err
   344  	}
   345  	addInvoiceData := &AddInvoiceData{
   346  		Memo:            invoice.Memo,
   347  		Hash:            &hash,
   348  		Value:           value,
   349  		DescriptionHash: invoice.DescriptionHash,
   350  		Expiry:          invoice.Expiry,
   351  		FallbackAddr:    invoice.FallbackAddr,
   352  		CltvExpiry:      invoice.CltvExpiry,
   353  		Private:         invoice.Private,
   354  		HodlInvoice:     true,
   355  		Preimage:        nil,
   356  		RouteHints:      routeHints,
   357  	}
   358  
   359  	_, dbInvoice, err := AddInvoice(ctx, addInvoiceCfg, addInvoiceData)
   360  	if err != nil {
   361  		return nil, err
   362  	}
   363  
   364  	return &AddHoldInvoiceResp{
   365  		AddIndex:       dbInvoice.AddIndex,
   366  		PaymentRequest: string(dbInvoice.PaymentRequest),
   367  		PaymentAddr:    dbInvoice.Terms.PaymentAddr[:],
   368  	}, nil
   369  }
   370  
   371  // LookupInvoiceV2 attempts to look up at invoice. An invoice can be referenced
   372  // using either its payment hash, payment address, or set ID.
   373  func (s *Server) LookupInvoiceV2(ctx context.Context,
   374  	req *LookupInvoiceMsg) (*lnrpc.Invoice, error) {
   375  
   376  	var invoiceRef channeldb.InvoiceRef
   377  
   378  	// First, we'll attempt to parse out the invoice ref from the proto
   379  	// oneof.  If none of the three currently supported types was
   380  	// specified, then we'll exit with an error.
   381  	switch {
   382  	case req.GetPaymentHash() != nil:
   383  		payHash, err := lntypes.MakeHash(req.GetPaymentHash())
   384  		if err != nil {
   385  			return nil, status.Error(
   386  				codes.InvalidArgument,
   387  				fmt.Sprintf("unable to parse pay hash: %v", err),
   388  			)
   389  		}
   390  
   391  		invoiceRef = channeldb.InvoiceRefByHash(payHash)
   392  
   393  	case req.GetPaymentAddr() != nil &&
   394  		req.LookupModifier == LookupModifier_HTLC_SET_BLANK:
   395  
   396  		var payAddr [32]byte
   397  		copy(payAddr[:], req.GetPaymentAddr())
   398  
   399  		invoiceRef = channeldb.InvoiceRefByAddrBlankHtlc(payAddr)
   400  
   401  	case req.GetPaymentAddr() != nil:
   402  		var payAddr [32]byte
   403  		copy(payAddr[:], req.GetPaymentAddr())
   404  
   405  		invoiceRef = channeldb.InvoiceRefByAddr(payAddr)
   406  
   407  	case req.GetSetId() != nil &&
   408  		req.LookupModifier == LookupModifier_HTLC_SET_ONLY:
   409  
   410  		var setID [32]byte
   411  		copy(setID[:], req.GetSetId())
   412  
   413  		invoiceRef = channeldb.InvoiceRefBySetIDFiltered(setID)
   414  
   415  	case req.GetSetId() != nil:
   416  		var setID [32]byte
   417  		copy(setID[:], req.GetSetId())
   418  
   419  		invoiceRef = channeldb.InvoiceRefBySetID(setID)
   420  
   421  	default:
   422  		return nil, status.Error(codes.InvalidArgument,
   423  			"invoice ref must be set")
   424  	}
   425  
   426  	// Attempt to locate the invoice, returning a nice "not found" error if
   427  	// we can't find it in the database.
   428  	invoice, err := s.cfg.InvoiceRegistry.LookupInvoiceByRef(invoiceRef)
   429  	switch {
   430  	case err == channeldb.ErrInvoiceNotFound:
   431  		return nil, status.Error(codes.NotFound, err.Error())
   432  	case err != nil:
   433  		return nil, err
   434  	}
   435  
   436  	return CreateRPCInvoice(&invoice, s.cfg.ChainParams)
   437  }