gcash / bchd

An alternative full node bitcoin cash implementation written in Go (golang)
ISC License
279 stars 101 forks source link

Simplest option for accessing bchd remotely #511

Closed mariusk closed 2 years ago

mariusk commented 2 years ago

The project speaks highly about grpc. But I can't find a simple way to authenticate the grpc session easily using a username and password (similar to the bchctl examples shown many places). Yes, the documentation hints that it is somewhat intentionally, that username / password combo shouldn't be used in production. Yeah, sure. But it sure would be handy just to test that everything else is set up correctly while still in development.

So my second idea was to run bchctl to access a remote bchd node. You know what bchctl requires? The config file and a certificate that bchd itself sets up. I'm sure I can get those pieces in the right place as well, although I do not yet understand why those pieces are necessary. It looks like bchctl supports remote access so why does it expect to find bchd configuration locally?

Any easier / simpler options to get going accessing a remotely running bchd instance?

mariusk commented 2 years ago

So I managed to get it going using the auto generated certificates generated by the bchd installation itself. Here's a demonstration which demonstrates wiring up the connection details and making the connection. Plus making the traditional grpc callback function a promise, and call it as a callback, promise and promise using the await pattern:

#!/usr/bin/env node
const fs = require('fs');
const util = require('util');

const protoLoader = require('@grpc/proto-loader');
const grpcLibrary = require('@grpc/grpc-js');
const packageDefinition = protoLoader.loadSync('./bchrpc.proto');
const packageObject = grpcLibrary.loadPackageDefinition(packageDefinition);
const {pb} = packageObject;
const {bchrpc} = pb;
const rootCert = fs.readFileSync('/Users/marius/.bchd/rpc.cert');
const bchdClient = new bchrpc('localhost:8335', grpcLibrary.credentials.createSsl(rootCert));
//const bchdClient = new bchrpc('192.168.10.190:8335', grpcLibrary.credentials.createSsl(rootCert));
const req = pb.GetMempoolInfoRequest;

bchdClient.GetMempoolInfo(req, (err, resp) => {
  console.log('callback:', resp);
});

const testFunc = util.promisify(bchdClient.GetMempoolInfo).bind(bchdClient);
testFunc(req)
  .then(txt => console.log('promise:', txt))
  .catch(err => console.error('promise error:', err));

async function test() {
  const txt = await testFunc(req);
  console.log('await:', txt);
}

test();

This code will output something like:

callback: { size: 532, bytes: 443734 }
promise: { size: 532, bytes: 443734 }
await: { size: 532, bytes: 443734 }

While figuring this stuff out, there were a couple of snags which is useful to mention.

The settings in bchd.conf named rpccert and rpckey contain file names with ~ and possibly paths with spaces like something ..Application Support.. and similar. Those did not work out of the box on Macos, so making sure these files exist in "easier" paths might be useful. I created a symlink and used absolute links instead of "user relative" paths like they originally contained (a symlink like /Users/marius/.bchd -> /Users/marius/Library/Application Support/Bchd and paths like rpccert=/Users/marius/.bchd/rpc.cert.

In my original post I mentioned the lack of an easy way to test simple authentication with grpc over the wire. Yes, "no authentication" could probably be easiest, but that would only work for localhost AFAIK. With the localhost reference in the code I posted everything works fine without any warnings. But if you want to test this on a local network where you actually access a different computer (e.g. you develop on your laptop but want to hit another computer running the bchd node) you need to use an IP address instead (localhost only works locally on the same computer). If you do that it will still work, BUT node will issue the following warning:

(node:8270) [DEP0123] DeprecationWarning: Setting the TLS ServerName to an IP address is not permitted by RFC 6066. This will be ignored in a future version.

This is most likely a generic issue related to TLS security. But it is also another way where these complex setups gets even harder to test. But at least for now it actually seems to work just fine.

Anyway, hopefully this will be helpful to others. Or myself in six months.