|
|
@ -1,23 +1,54 @@ |
|
|
|
import fs from 'fs' |
|
|
|
// @flow
|
|
|
|
|
|
|
|
import grpc from 'grpc' |
|
|
|
import { loadSync } from '@grpc/proto-loader' |
|
|
|
import walletUnlockerMethods from './walletUnlockerMethods' |
|
|
|
import StateMachine from 'javascript-state-machine' |
|
|
|
import LndConfig from './config' |
|
|
|
import { getDeadline, validateHost, createSslCreds, createMacaroonCreds } from './util' |
|
|
|
import methods from './walletUnlockerMethods' |
|
|
|
import { mainLog } from '../utils/log' |
|
|
|
|
|
|
|
export const initWalletUnlocker = lndConfig => { |
|
|
|
const walletUnlockerObj = walletUnlocker(lndConfig) |
|
|
|
const walletUnlockerMethodsCallback = (event, msg, data) => |
|
|
|
walletUnlockerMethods(lndConfig, walletUnlockerObj, mainLog, event, msg, data) |
|
|
|
/** |
|
|
|
* Creates an LND grpc client lightning service. |
|
|
|
* @returns {WalletUnlocker} |
|
|
|
*/ |
|
|
|
class WalletUnlocker { |
|
|
|
service: any |
|
|
|
lndConfig: LndConfig |
|
|
|
_fsm: StateMachine |
|
|
|
|
|
|
|
return walletUnlockerMethodsCallback |
|
|
|
} |
|
|
|
// Transitions provided by the state machine.
|
|
|
|
connect: any |
|
|
|
disconnect: any |
|
|
|
terminate: any |
|
|
|
is: any |
|
|
|
can: any |
|
|
|
state: string |
|
|
|
|
|
|
|
constructor(lndConfig: LndConfig) { |
|
|
|
this.service = null |
|
|
|
this.lndConfig = lndConfig |
|
|
|
|
|
|
|
// Initialize the state machine.
|
|
|
|
this._fsm() |
|
|
|
} |
|
|
|
|
|
|
|
export const walletUnlocker = lndConfig => { |
|
|
|
const lndCert = fs.readFileSync(lndConfig.cert) |
|
|
|
const credentials = grpc.credentials.createSsl(lndCert) |
|
|
|
// ------------------------------------
|
|
|
|
// FSM Callbacks
|
|
|
|
// ------------------------------------
|
|
|
|
|
|
|
|
/** |
|
|
|
* Connect to the gRPC interface and verify it is functional. |
|
|
|
* @return {Promise<rpc.lnrpc.WalletUnlocker>} |
|
|
|
*/ |
|
|
|
async onBeforeConnect() { |
|
|
|
mainLog.info('Connecting to WalletUnlocker gRPC service') |
|
|
|
const { rpcProtoPath, host, cert, macaroon } = this.lndConfig |
|
|
|
|
|
|
|
// Verify that the host is valid before creating a gRPC client that is connected to it.
|
|
|
|
return await validateHost(host).then(async () => { |
|
|
|
// Load the gRPC proto file.
|
|
|
|
// The following options object closely approximates the existing behavior of grpc.load
|
|
|
|
// The following options object closely approximates the existing behavior of grpc.load.
|
|
|
|
// See https://github.com/grpc/grpc-node/blob/master/packages/grpc-protobufjs/README.md
|
|
|
|
const options = { |
|
|
|
keepCase: true, |
|
|
@ -26,11 +57,61 @@ export const walletUnlocker = lndConfig => { |
|
|
|
defaults: true, |
|
|
|
oneofs: true |
|
|
|
} |
|
|
|
const packageDefinition = loadSync(lndConfig.rpcProtoPath, options) |
|
|
|
const packageDefinition = loadSync(rpcProtoPath, options) |
|
|
|
|
|
|
|
// Load gRPC package definition as a gRPC object hierarchy.
|
|
|
|
const rpc = grpc.loadPackageDefinition(packageDefinition) |
|
|
|
|
|
|
|
// Instantiate a new connection to the WalletUnlocker interface.
|
|
|
|
return new rpc.lnrpc.WalletUnlocker(lndConfig.host, credentials) |
|
|
|
// Create ssl and macaroon credentials to use with the gRPC client.
|
|
|
|
const [sslCreds, macaroonCreds] = await Promise.all([ |
|
|
|
createSslCreds(cert), |
|
|
|
createMacaroonCreds(macaroon) |
|
|
|
]) |
|
|
|
const credentials = grpc.credentials.combineChannelCredentials(sslCreds, macaroonCreds) |
|
|
|
|
|
|
|
// Create a new gRPC client instance.
|
|
|
|
this.service = new rpc.lnrpc.WalletUnlocker(host, credentials) |
|
|
|
|
|
|
|
// Wait for the gRPC connection to be established.
|
|
|
|
return new Promise((resolve, reject) => { |
|
|
|
grpc.waitForClientReady(this.service, getDeadline(2), err => { |
|
|
|
if (err) { |
|
|
|
return reject(err) |
|
|
|
} |
|
|
|
return resolve() |
|
|
|
}) |
|
|
|
}) |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Discomnnect the gRPC service. |
|
|
|
*/ |
|
|
|
onBeforeDisconnect() { |
|
|
|
mainLog.info('Disconnecting from WalletUnlocker gRPC service') |
|
|
|
if (this.service) { |
|
|
|
this.service.close() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// ------------------------------------
|
|
|
|
// Helpers
|
|
|
|
// ------------------------------------
|
|
|
|
|
|
|
|
/** |
|
|
|
* Hook up lnd restful methods. |
|
|
|
*/ |
|
|
|
registerMethods(event: Event, msg: string, data: any) { |
|
|
|
return methods(this.service, mainLog, event, msg, data, this.lndConfig) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
StateMachine.factory(WalletUnlocker, { |
|
|
|
init: 'ready', |
|
|
|
transitions: [ |
|
|
|
{ name: 'connect', from: 'ready', to: 'connected' }, |
|
|
|
{ name: 'disconnect', from: 'connected', to: 'ready' } |
|
|
|
] |
|
|
|
}) |
|
|
|
|
|
|
|
export default WalletUnlocker |
|
|
|