Repository: lightningnetwork/lnd (language: go)

Issue:
[bug]: NodePubkey is not set (gRPC BatchOpenChannel)
### Background

using `BatchOpenChannel` via lightning.proto in NodeJS, fails with `Error: 2 UNKNOWN: batch funding failed: error parsing channel 0: NodePubkey is not set` even though the payload has `node_pubkey` set on each channel using `bytebuffer` as recommended in these docs: https://dev.lightning.community/guides/javascript-grpc/ 

Note: if node_pubkey is set as a regular hex string, the error changes to: `error parsing channel 0: malformed public key: invalid length: 49`

base64 encoding the node_pubkey results in `malformed public key: invalid length: 66`

### Your environment

* version of `lnd`: `LND 0.17.3-beta`

### Steps to reproduce

attempt batch channel open using gRPC over Node.js using [lightning.proto](https://github.com/lightningnetwork/lnd/blob/master/lnrpc/lightning.proto):

Here's the code demodularized into one execution path for clarity:

```
const ByteBuffer = require("bytebuffer");
const fs = require("fs");
const grpc = require("@grpc/grpc-js");
const protoLoader = require("@grpc/proto-loader");
const { promisify } = require("util");

const { LND_DIR, GRPC_HOST, MACAROON_PATH } = process.env;
const TLS_PATH = `${LND_DIR}/tls.cert`;
const loaderOptions = {
  keepCase: true,
  longs: String,
  enums: String,
  defaults: true,
  oneofs: true,
};
const packageDefinition = protoLoader.loadSync(
  "lightning.proto",
  loaderOptions
);
const lnrpc = grpc.loadPackageDefinition(packageDefinition).lnrpc;
process.env.GRPC_SSL_CIPHER_SUITES = "HIGH+ECDSA";
const tlsCert = fs.readFileSync(TLS_PATH);
const sslCreds = grpc.credentials.createSsl(tlsCert);
const macaroon = fs.readFileSync(`${LND_DIR}${MACAROON_PATH}`).toString("hex");
const macaroonCreds = grpc.credentials.createFromMetadataGenerator(function (
  args,
  callback
) {
  const metadata = new grpc.Metadata();
  metadata.add("macaroon", macaroon);
  callback(null, metadata);
});
const creds = grpc.credentials.combineChannelCredentials(
  sslCreds,
  macaroonCreds
);
const client = new lnrpc.Lightning(GRPC_HOST, creds);

const batchOpenChannel = promisify(client.batchOpenChannel).bind(client);

// sample channel structure (real channel list comes from db file)
const channels = [
  {
    pubkey: "pubkeystring...",
    size: 10000000,
  },
...
];

const payload = {
  channels: channels.map((c) => {
    const channel = {
      node_pubkey: ByteBuffer.fromHex(c.pubkey),
      local_funding_amount: c.size,
    };
    return channel;
  }),
  sat_per_vbyte: 60,
};

(async () => {
  await batchOpenChannel(payload).catch(console.error).then(console.log);
})();

```


### Expected behaviour

channels are opened, no error

### Actual behaviour

```
Error: 2 UNKNOWN: batch funding failed: error parsing channel 0: NodePubkey is not set
```



Discussion / hints:
This should work... I checked the code and `node_pubkey` should indeed just be the raw bytes.
Can you perhaps log the full payload before sending it? Maybe something is getting lost?
The example code you used is quite old, so maybe the `bytebuffer` package isn't working correctly anymore? Meaning that `ByteBuffer.fromHex(c.pubkey),` would encode an empty buffer for some reason?
full payload:
```
{
  channels: [
    { 
      node_pubkey: ByteBuffer {
        buffer: <Buffer 02 1c 97 a9 0a 41 1f f2 b1 0d c2 a8 e3 2d e2 f2 9d 2f a4 9d 41 bf bb 52 bd 41 6e 46 0d b0 74 7d 0d>,
        offset: 0,
        markedOffset: -1,
        limit: 33,
        littleEndian: false,
        noAssert: true
      }, 
      local_funding_amount: 30000000 
    },
    { 
      node_pubkey: ByteBuffer {
        buffer: <Buffer 03 cc 1d 09 32 bb 99 b0 69 7f 5b 5e 59 61 b8 3a b7 fd 66 f1 ef c4 c9 f5 c7 ba d6 6c 1b cb e7 8f 02>,
        offset: 0,
        markedOffset: -1,
        limit: 33,
        littleEndian: false,
        noAssert: true
      }, 
      local_funding_amount: 10000000 
    }
  ],
  sat_per_vbyte: 60
}
```

reducing the payload args to JSON, using hex string pubkey, and passing to lncli batchopenchannel worked perfectly without error BTW: 

```
lncli batchopenchannel --sat_per_vbyte=60 '[{"node_pubkey":"021c97a90a411ff2b10dc2a8e32de2f29d2fa49d41bfbb52bd416e460db0747d0d","local_funding_amount":30000000},{"node_pubkey":"03cc1d0932bb99b0697f5b5e5961b83ab7fd66f1efc4c9f5c7bad66c1bcbe78f02","local_funding_amount":10000000}]'
```
It looks like `node_pubkey` and `node_pubkey_string` were unified for the REST side but not for gRPC: https://github.com/lightningnetwork/lnd/issues/4041
Okay, it is as I expected. The `ByteBuffer` class doesn't work correctly for some reason. If you replace `node_pubkey: ByteBuffer.fromHex(c.pubkey),` with `node_pubkey: new Buffer(c.pubkey, "hex"),` it works.

I'm going to create a PR to update the example docs.

Fix patch:
diff --git a/docs/grpc/javascript.md b/docs/grpc/javascript.md
index f05b7eb947..9cf65f637e 100644
--- a/docs/grpc/javascript.md
+++ b/docs/grpc/javascript.md
@@ -123,7 +123,7 @@ invoice.
 
 This example has a few dependencies:
 ```shell
-$  npm install --save async lodash bytebuffer
+$  npm install --save async lodash
 ```
 
 You can run the following in your shell or put it in a program and run it like
@@ -133,10 +133,9 @@ You can run the following in your shell or put it in a program and run it like
 // Load some libraries specific to this example
 const async = require('async');
 const _ = require('lodash');
-const ByteBuffer = require('bytebuffer');
 
 let dest_pubkey = <RECEIVER_ID_PUBKEY>;
-let dest_pubkey_bytes = ByteBuffer.fromHex(dest_pubkey);
+let dest_pubkey_bytes = new Buffer(dest_pubkey, "hex");
 
 // Set a listener on the bidirectional stream
 let call = lightning.sendPayment();


Test patch: