RadarTech / lnrpc

A Typescript gRPC client for LND with support for all LND sub-servers
MIT License
43 stars 13 forks source link

FYI: rpc.proto missing error in packaged electron app #56

Closed jamaljsr closed 4 years ago

jamaljsr commented 4 years ago

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.

cavanmflynn commented 4 years ago

Actually, I don't see a strong reason to perform this logic at runtime. import "google/api/annotations.proto" is required in the proto file for type generation, but we can implement a different fix for grpc.load() without re-writing the file at runtime.

The runtime logic hasn't been removed because we didn't realize it caused any issues. Thanks for letting us know, @jamaljsr!

cavanmflynn commented 4 years ago

A fix has been published in v0.8.0-beta.1.

jamaljsr commented 4 years ago

Hey @cavanmflynn

I have tested v0.8.0-beta.1 and confirmed the problem no longer exists. So I can get rid of my workaround now. Thanks for making this change so quickly.