GithubHelp home page GithubHelp logo

lnrpc's Introduction

@radar/lnrpc

CircleCI Known Vulnerabilities NPM Version License

A Typescript gRPC client for LND with support for all LND sub-servers. Originally forked from Matt-Jensen/lnrpc.

Features

  • Auto-generates lnd/lnrpc clients and Typescript type definitions using a target release tag
  • Supports all LND sub-servers
  • Wraps requests in promises
  • Easily setup SSL and Macaroons
  • Instantiates all gRPC services
  • uint64/int64 types cast to string to prevent precision loss

Installation

npm install @radar/lnrpc
# OR
yarn add @radar/lnrpc

Notes:

  • Ensure you have an lnd instance running with --no-macaroons, unless you provide macaroon authentication to your lnrpc instance when created.
  • If you want to interact with the LND sub-servers, ensure that LND was compiled with the necessary sub-server build tags.
  • If the following error is thrown in the consuming application, run npm rebuild:
    Error: Failed to load gRPC binary module because it was not installed for the current system
    

Usage

This package exports a create function for the main gRPC server as well as each sub-server:

import {
  createAutopilotRpc,
  createChainRpc,
  createInvoicesRpc,
  createLnRpc,
  createRouterRpc,
  createSignRpc,
  createWalletRpc,
  createWatchtowerRpc,
  createWtClientRpc,
} from '@radar/lnrpc';

You can also import the create function for the main gRPC server using the default import:

import createLnRpc from '@radar/lnrpc';

If you want to interact with all servers, wrap the functions in a class or object for easy initialization:

import createLnRpc, {
  AutopilotRpc,
  ChainRpc,
  createAutopilotRpc,
  createChainRpc,
  createInvoicesRpc,
  createRouterRpc,
  createSignRpc,
  createWalletRpc,
  createWatchtowerRpc,
  createWtClientRpc,
  InvoicesRpc,
  LnRpc,
  RouterRpc,
  RpcClientConfig,
  SignRpc,
  WalletRpc,
  WatchtowerRpc,
  WtClientRpc,
} from '@radar/lnrpc';

export class Lightning {
  public static lnrpc: LnRpc;
  public static autopilotrpc: AutopilotRpc;
  public static chainrpc: ChainRpc;
  public static invoicesrpc: InvoicesRpc;
  public static routerrpc: RouterRpc;
  public static signrpc: SignRpc;
  public static walletrpc: WalletRpc;
  public static watchtowerrpc: WatchtowerRpc;
  public static wtclientrpc: WtClientRpc;

  /**
   * Initialize gRPC clients for the main server and all sub-servers
   * @param config The RPC client connection configuration
   */
  public static async init(config: RpcClientConfig): Promise<void> {
    this.lnrpc = await createLnRpc(config);
    this.autopilotrpc = await createAutopilotRpc(config);
    this.chainrpc = await createChainRpc(config);
    this.invoicesrpc = await createInvoicesRpc(config);
    this.routerrpc = await createRouterRpc(config);
    this.signrpc = await createSignRpc(config);
    this.walletrpc = await createWalletRpc(config);
    this.watchtowerrpc = await createWatchtowerRpc(config);
    this.wtclientrpc = await createWtClientRpc(config);
  }
}

Usage Example - Main Server

Connecting to an lnd instance at localhost:10001.

import createLnRpc from '@radar/lnrpc';

(async () => {
  const lnRpcClient = await createLnRpc(config);

  // All requests are promisified and typed
  const { confirmedBalance } = await lnRpcClient.walletBalance();

  // ...and you're off!
  console.log(confirmedBalance);

  // subscribe to LND server events
  const subscriber = await lnRpcClient.subscribeInvoices();
  subscriber.on('data', invoice => {
    console.log(invoice); // do something with invoice event
  });
})();

Options Example - Main Server

import createLnRpc from '@radar/lnrpc';

(async () => {
  const lnRpcClient = await createLnRpc({
    /*
     * By default lnrpc connects to `localhost:10001`,
     * however we can point to any host.
     */
    server: '173.239.209.2:3001',

    /*
     * By default  lnrpc looks for your tls certificate at:
     * `~/.lnd/tls.cert`, unless it detects you're using macOS and
     * defaults to `~/Library/Application\ Support/Lnd/tls.cert`
     * however you can configure your own SSL certificate path like:
     */
    tls: './path/to/tls.cert',

    /*
     * You can also provide a TLS certificate directly as a string
     * (Just make sure you don't commit this to git).
     * Overwrites: `tls`
     */
    cert: process.env.MY_SSL_CERT,

    /*
     * Optional path to configure macaroon authentication
     * from LND generated macaroon file.
     */
    macaroonPath: './path/to/data/admin.macaroon',

    /*
     * Optional way to configure macaroon authentication by
     * passing a hex encoded string of your macaroon file.
     * Encoding: `xxd -ps -u -c 1000 ./path/to/data/admin.macaroon`
     * Details: https://github.com/lightningnetwork/lnd/blob/dc3db4b/docs/macaroons.md#using-macaroons-with-grpc-clients
     */
    macaroon: process.env.MY_MACAROON_HEX,
  });

  try {
    const getInfoResponse = await lnRpcClient.getInfo();
    console.log(getInfoResponse);
  } catch (error) {
    console.error(error);
  }
})();

API Reference

All main server (lnrpc) methods documentation can be found here.

Usage With BTCPayServer

By default lnrpc assumes SSl certificate pinning. In order to use lnrpc with a service (like BTCPayServer) which manages your certification, you'll have to opt to disable certificate pinning by passing { tls: false } within your lnrpc configuration.

Contributing

Clone Repository & Install Dependencies

git clone [email protected]:RadarTech/lnrpc.git && cd $_

npm install
# OR
yarn

Change LND gRPC release version

To change the gRPC definitions used for all auto-generated types and RPC methods edit the config.lnd_release_tag value in package.json to the desired LND release tag and run the following:

npm run update-protos
# OR
yarn update-protos

# AND

npm run generate
# OR
yarn generate

Newly generated type definitions will be available in ./generated. You can now delete the old proto file inside the lnd directory. Use the generated type definitions to update the types in src/types/rpc. Any added streaming methods must be included in the subscriptionMethods array that's into the createServiceClient function. This prevents streaming methods from being promisified.

License

This project is licensed under the MIT License.

lnrpc's People

Contributors

ajcrowe avatar cavanmflynn avatar ctebbe avatar d4nte avatar danielemiliogarcia avatar dennisreimann avatar dependabot[bot] avatar jamaljsr avatar kaybesee avatar leon-do avatar lightningk0ala avatar matt-jensen avatar snyk-bot avatar wbobeirne avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

lnrpc's Issues

Types don't present useful errors

Hey all, loving what you're providing to the TypeScript + Lightning ecosystem, hope I can help out going forward. Just started using lnrpc, and I'm finding the type errors to be really hard to grok. Take for instance this line of code that I'm converting over from using ln-service which has a slightly different api and nomenclature for arguments:

lnRpc.addInvoice({
  description: 'My item',
  tokens: '10000',
  expires_at: expires.toISOString(),
})

This results in the following error:

Argument of type '{ description: string; tokens: number; expires_at: string; }' is not assignable to parameter of type 'Partial<AsObject>'.
  Object literal may only specify known properties, and 'description' does not exist in type 'Partial<AsObject>'.

It looks like due to the AsObject wrapping that the types have, you don't really get useful information out of the errors. It's only by drilling down into the types that I can find the definition for AsObject. Not sure how the types are generated, and if there's a better way to surface them, but it'd definitely improve the developer experience!

Incomplete invoice data in lookupInvoice

Hi, I am trying to retrieve invoices from lnd node using lnrpc ():

import { Invoice, Readable } from '@radar/lnrpc'; import env from './env'; import { node, initNode } from './node';
...
const invoice = await node.lookupInvoice({ rHashStr: paymenthash, }); console.log(invoice)

I get back the invoice, however it misses many of the data items that I can get for the same invoice through command line lncli lookupinvoice (status, settled, etc):

{ memo: 'my invoice description', rPreimage: <Buffer 64 70 c9 c0 35 1a d0 d2 df 1c 9e 7a 41 48 81 f2 8b 57 61 db 41 32 50 b9 27 ae 75 8f cb bd 6a 7a>, rHash: <Buffer 34 31 2a bd d3 aa 33 39 ae 03 3d 1e 58 da fa 94 e0 42 be 37 68 aa 93 cc de 2e b6 15 9f 12 b7 63>, value: '100000', creationDate: '1556265359', paymentRequest: 'lnbc1m1pwv9wv0pp5xscj40wn4genntsr85093kh6jnsy903hdz4f8nx796mpt8cjka3sdzv2fjkyctvv9hxxefqdanzqcmgv9hxuetvypmkjargypy5ggpkxyersvfsxccrvde4xcerjwphx5eqcqzpgtpvqa3z3qwjt24qekwq5cgxe3udalvuq2ahg2xfjvxg8t9zhczz4cx7jm0hygwzs9skued9qfxprr45xgzc0xkxx7tr3evlwpljzm3cqyxn4j4', expiry: '3600', cltvExpiry: '40', addIndex: '433' }
Is there any reason for such behavior? Thank you

SubscribeInvoice endpoint not returning InvoiceState

When an invoice is added in LND the invoice state field and other empty fields are not populated in to the response. I'm wondering if empty fields are excluded, and whether InvoiceState::OPEN being value 0 is the cause.

Error: 2 UNKNOWN: Stream removed

Hello,

Version used: 0.6.0-beta.3

I have an issue with SubscribeInvoices. After 10 to 15 minutes, the connection ends with Error: 2 UNKNOWN: Stream removed.
I found a similar issue here grpc/grpc-node#708

Someone on the LND slack has suggested to increase connection_idle, connection_age and the keepalive settings for the grpc channel.
The parameters should be added here https://github.com/RadarTech/lnrpc/blob/master/lib/lightning.js#L49
The grpc documentation: https://grpc.github.io/grpc/cpp/group__grpc__arg__keys.html

Currently I get around the issue with a setInterval monitoring and retrying connection upon error. But I would be interested to have your opinion about the issue and suggested fix.
Maybe allowing the end-user of lnrpc to set those parameters in the createLnRpc options object would be nice. What do you think?

Thank you

SendPaymentV2 not recognising destCustomRecord for keysend

Trying to send a keysend payment with sendPaymentV2 as below.

    const response = await rpc.sendPaymentV2({
      dest: Buffer.from(pubKey, "hex"),
      amt: 210,
      allowSelfPayment: true,
      timeoutSeconds: 30,
      paymentHash: preimage.toString("base64"),
      destCustomRecords: [[5482373484, Buffer.from(preimage)]],
    });

I am using the key 5482373484 but getting back an error:

no custom records with types below 65536 allowed

subscribeInvoice type incorrect, returns Readable not Promise

This is the subscribeInvoice type currently provided:

LnRpc.subscribeInvoices(args?: InvoiceSubscription | undefined): Promise<Readable<Invoice>>

However the following code:

node.subscribeInvoices().then(stream => {
  stream.on('data', chunk => {
    console.log(chunk);
  });
});

results in the following error:

UnhandledPromiseRejectionWarning: TypeError: node_1.node.subscribeInvoices(...).then is not a function

Doing a quick log of the call:

const stream = node.subscribeInvoices();
console.log(stream);

Prints this, indicating it returns a readable, no promise wrap:

ClientReadableStream {
  _readableState:
    ReadableState {
      ...

I'm not sure if it's just the type that's wrong, or the non-promise behavior that's wrong, but I currently have to work around this by doing:

import { Invoice, Readable } from '@radar/lnrpc';

const stream = node.subscribeInvoices() as any as Readable<Invoice>;
stream.on('data', chunk => {
  console.log(chunk);
});

sendPayment returns rpc error:code = Canceled desc = context canceled

Trying to send a payment with sendPayment as shown bellow.

      let call = rpc.sendPayment({
        dest: Buffer.from(peerpub, "hex"),
        amt: '22022',
        destCustomRecords: [[34349334, Buffer.from(messagew1)]]
      });

No matter what parameters I provide I always see the same response in node logs.

[ERR] RPCS: Failed receiving from stream: rpc error: code = Canceled desc = context canceled
[ERR] RPCS: [/lnrpc.Lightning/SendPayment]: rpc error: code = Canceled desc = context canceled

Not working on Raspberry Pi Zero

Hello,

I wanted to run a web app that uses this library on a Raspberry Pi Zero but I have issues. I got a Illegal instruction error. Apparently it is because of grpc.
So this past night I runned npm rebuild --build-from-source=grpc, but still got an error ...node_modules/grpc/src/node/extension_binary/node-v64-linux-arm-glibc/grpc_node.node: undefined symbol: __atomic_store_8.
I don't think there is a good solution for this, I am just writing this issue in case someone faces it.

Hanging on subscribeHtlcEvents()

Not sure why but cannot await this endpoint on the router rpc, node just hangs. subscribeInvoices() on invoices rpc works fine. Please advise.

FYI: rpc.proto missing error in packaged electron app

First off, just want to say thank you for the excellent library. It's been a pleasure to work with.

I am building an electron app which communicates with local LND nodes. When packaging my app, electron-builder stores all of the node_modules in an app.asar (zip) archive on OSX. This causes a problem with your library, because the createLnRpc function attempts to write the rpc.proto file to node_modules/@radar/lnrpc/ at runtime. See lnrpc.js. Since the rpc.proto file does not exist, my app is unable to connect to the LND nodes. This is the error I receive:

ENOENT, node_modules/@radar/lnrpc/rpc.proto not found in /Users/jamal/dev/polar/dist/mac/Polar.app/Contents/Resources/app.asar

It works fine when developing locally, but not when using the production app packaged on a CI server with fresh node_modules. I have worked around this issue for now by running a script that copies the rpc.proto file prior to packaging. The logic is basically the same as what you have in createLnRpc.

// lnrpcProto.js
const lnrpcPkgJson = require('@radar/lnrpc/package.json');
const { join } = require('path');
const { outputFile, pathExists, readFile } = require('fs-extra');

const copyProto = async () => {
  console.log('Copying lnrpc rpc.proto file');
  const lnrpcPath = join('node_modules', '@radar', 'lnrpc');
  const protoSrc = join(
    lnrpcPath,
    `lnd/${lnrpcPkgJson.config['lnd-release-tag']}/rpc.proto`,
  );
  const protoDest = join(lnrpcPath, 'rpc.proto');

  console.log(` - src: ${protoSrc}`);
  console.log(` - dest: ${protoDest}`);
  if (!(await pathExists(protoDest))) {
    let grpcSrc = await readFile(protoSrc, 'utf8');
    // remove google annotations causing parse error on `grpc.load()`
    grpcSrc = grpcSrc.replace('import "google/api/annotations.proto";', '');
    await outputFile(protoDest, grpcSrc);
    console.log(' -> copied!');
  } else {
    console.log(' -> file already exists');
  }
};

copyProto();

I run this script as a yarn command before running electron-builder and my app then works as expected afterwards.

It's not completely clear to me why it is necessary for your library to copy the proto file at runtime instead of putting it in the correct location during your build process. I assume there is a good reason to do it this way.

So I am just creating this issue to make you aware of this problem that prevents your library from being easily used in electron apps out of the box. I came up with a workaround that I can live with if this cannot be fixed on your end.

Thanks again. I hope this feedback is helpful.

grpcPkgObj.lnrpc.WalletUnlocker is not a constructor

I'm trying to connect to my local Lightning Network node started with Polar, but I can't create the LN gRPC client.
I tried with cert, macaroon config (hex and base64) and with tls, macaroonPath config.
All of them result in this error:

import { createLnRpc } from "@radar/lnrpc"

async function test(): Promise<void> {
	try {
		const client = await createLnRpc({
			server: "127.0.0.1:10001",
			tls: "/home/bkiac/.polar/networks/1/volumes/lnd/alice/tls.cert",
			macaroonPath:
				"/home/bkiac/.polar/networks/1/volumes/lnd/alice/data/chain/bitcoin/regtest/admin.macaroon",
		})
	} catch (error: unknown) {
		console.error(error)
	}
}

void test()
TypeError: grpcPkgObj.lnrpc.WalletUnlocker is not a constructor
    at Object.createWalletUnlocker (/node_modules/@radar/lnrpc/dist/src/services/wallet-unlocker.js:22:30)
    at Object.<anonymous> (/node_modules/@radar/lnrpc/dist/src/rpc/ln-rpc.js:102:65)
    at step (/node_modules/@radar/lnrpc/dist/src/rpc/ln-rpc.js:44:23)
    at Object.next (/node_modules/@radar/lnrpc/dist/src/rpc/ln-rpc.js:25:53)
    at fulfilled (/node_modules/@radar/lnrpc/dist/src/rpc/ln-rpc.js:16:58) {
  code: 'GRPC_WALLET_UNLOCKER_SERVICE_ERR'
}

I previously used ln-service, I had no problem connecting with that library.

I'm not sure what I'm missing, appreciate the help!

Typed subscribe stream event data

It would be excellent of methods like subscribeInvoices or subscribeTransactions had typed data for their events. It looks like the default Readable class doesn't have a way of providing types for instances of it, so I'm not sure how this is done, but it'd certainly make working with these streams a lot easier.

Event for disconnection of lnd ?

Hi,

I was wondering if there was some kind of discconection event / connection event so that we know when lnd is down ?

i.e. after this

(async () => {
  const lnRpcClient = await createLnRpc(config);

Is there a subscription that informs me that the lnRpc is down - hence the rpc is down, so I can re-act inside of my code ?

Specifically I want to work with typescript, but i was wondering if something existed in another library I found (but only javasript) - that gives the following

// Do something cool if we detect that the wallet is locked.
grpc.on(`locked`, () => console.log('wallet locked!'))

// Do something cool when the wallet gets unlocked.
grpc.on(`active`, () => console.log('wallet unlocked!'))

// Do something cool when the connection gets disconnected.
grpc.on(`disconnected`, () => console.log('disconnected from lnd!'))

Thanks in advance

QueryRoutesRequest requires optional fields

Was just using queryRoutes, and was finding it was asking me for a bunch of fields I believe are optional. Here's the current type for it:

export interface QueryRoutesRequest {
  pubKey: string;
  amt?: string;
  numRoutes?: number;
  finalCltvDelta?: number;
  feeLimit?: FeeLimit;
  ignoredNodes: Buffer[] | string[];
  ignoredEdges: EdgeLocator[];
  sourcePubKey: string;
}

The docs say this about sourcePubKey:

The source node where the request route should originated from. If empty, self is assumed.

I'm pretty sure ignoredNodes and ignoredEdges are optional as well, since the call works fine without them.

I wasn't sure if these types were hand-written or not, so I didn't want to make a PR changing it if they're generated from something that needs to be fixed upstream.

Add lightning-network as a topic to this repository

Hello,

This repository is listed on the Awesome Lightning-Network list, which is a collection of useful lightning-network projects. However, it seems that the 'lightning-network' topic is missing from this repository's topics.

Adding the 'lightning-network' topic will help users discover your project more easily and recognize its relevance to the lightning-network ecosystem. To add the topic, please follow these steps:

  1. Navigate to the main page of the repository.
  2. Click on the gear icon next to "About" on the right side of the page.
  3. In the "Topics" section, type 'lightning-network' and press Enter.
  4. Click "Save changes."

Thank you for your attention and for contributing to the lightning-network community!

CloseChannel requires both bytes and str txid

The current argument interface for closing a channel is

export interface ChannelPoint {
  fundingTxidBytes: Buffer | string;
  fundingTxidStr: string;
  outputIndex: number;
}

This should probably be either bytes or string, not both.

Missing annotations.proto on Windows 10

This issue is related to #56.

I've upgraded my app to use v0.8.0-beta.3. Everything works great on OSX, but on Windows, there is still an issue loading the google proto files. I get this error when calling createLnRpc:

ENOENT: no such file or directory, open 'C:\\...\\node_modules\\@radar\\lnrpc\\lnd\\v0.8.0-beta\\google\\api\\annotations.proto'

I went digging into my node_modules and noticed the google dir is missing.There's only the rpc.proto file there. I went ahead and copied the google dir from the root lnrpc dir.

$ tree lnd/
lnd/
└── v0.8.0-beta
    ├── google
    │   └── api
    │       ├── annotations.proto
    │       └── http.proto
    └── rpc.proto

Now when running my app, I get a slightly different error:

ENOENT: no such file or directory, open 'C:\\...\\node_modules\\@radar\\lnrpc\\lnd\\v0.8.0-beta\\google\\api\\google\\api\\http.proto'

Its looking for a google dir inside of the google dir. So I created a nested copy.

$ tree lnd/
lnd/
└── v0.8.0-beta
    ├── google
    │   └── api
    │       ├── annotations.proto
    │       ├── google
    │       │   └── api
    │       │       ├── annotations.proto
    │       │       └── http.proto
    │       └── http.proto
    └── rpc.proto

Now when I run my app, I get another error:

ENOENT: no such file or directory, open 'C:\\...\\node_modules\\@radar\\lnrpc\\lnd\\v0.8.0-beta\\google\\api\\google\\protobuf\\descriptor.proto'

Its looking for a descriptor.proto file now. I was able to find it here. I saved it into the path, so now I have:

$ tree lnd/
lnd/
└── v0.8.0-beta
    ├── google
    │   └── api
    │       ├── annotations.proto
    │       ├── google
    │       │   ├── api
    │       │   │   ├── annotations.proto
    │       │   │   └── http.proto
    │       │   └── protobuf
    │       │       └── descriptor.proto
    │       └── http.proto
    └── rpc.proto

Finally, my app is able to connect to the LND node.

Just to be certain this wasn't being caused by a configuration issue in my app, I created a simple nodejs app. It produces the same error message about missing annotations.proto.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.