Browse Source

feat(lnd): use free port for lnd

Select free ports for lnd from a pool of options. By default we will
use the standard lnd ports (10009 and 9735) but if those are not
available then we will search for free ports in a nearby range.

This allows us to have multiple instances of the wallet running and
avoids possible conflicts with other instances of lnd that the user
may be running.
renovate/lint-staged-8.x
Tom Kirkpatrick 6 years ago
parent
commit
52cefd7cdf
No known key found for this signature in database GPG Key ID: 72203A8EC5967EA8
  1. 28
      app/lib/lnd/neutrino.js
  2. 2
      app/lib/zap/controller.js
  3. 1
      package.json
  4. 12
      test/unit/lnd/neutrino.spec.js
  5. 4
      yarn.lock

28
app/lib/lnd/neutrino.js

@ -3,6 +3,7 @@
import split2 from 'split2'
import { spawn } from 'child_process'
import EventEmitter from 'events'
import getPort from 'get-port'
import { mainLog, lndLog, lndLogGetLevel } from '../utils/log'
import { fetchBlockHeight } from './util'
import LndConfig from './config'
@ -63,9 +64,11 @@ class Neutrino extends EventEmitter {
* Start the Lnd process in Neutrino mode.
* @return {Number} PID of the Lnd process that was started.
*/
start() {
async start() {
if (this.process) {
throw new Error('Neutrino process with PID ${this.process.pid} already exists.')
return Promise.reject(
new Error('Neutrino process with PID ${this.process.pid} already exists.')
)
}
mainLog.info('Starting lnd in neutrino mode')
@ -75,13 +78,30 @@ class Neutrino extends EventEmitter {
mainLog.info(' > cert:', this.lndConfig.cert)
mainLog.info(' > macaroon:', this.lndConfig.macaroon)
// Get a free port to use as the rpc listen address.
const rpcListen = await getPort({
host: 'localhost',
port: [10009, 10008, 10007, 10006, 10005, 10004, 10003, 10002, 10001]
})
this.lndConfig.host = `localhost:${rpcListen}`
// Get a free port to use as the p2p listen address.
const p2pListen = await getPort({
host: '0.0.0.0',
port: [9735, 9734, 9733, 9732, 9731, 9736, 9737, 9738, 9739]
})
//Configure lnd.
const neutrinoArgs = [
`--configfile=${this.lndConfig.configPath}`,
`--lnddir=${this.lndConfig.lndDir}`,
`--listen=0.0.0.0:${p2pListen}`,
`--rpclisten=localhost:${rpcListen}`,
`${this.lndConfig.autopilot ? '--autopilot.active' : ''}`,
`${this.lndConfig.alias ? `--alias=${this.lndConfig.alias}` : ''}`
]
// Configure neutrino backend.
if (this.lndConfig.network === 'mainnet') {
neutrinoArgs.push('--neutrino.connect=mainnet1-btcd.zaphq.io')
// neutrinoArgs.push('--neutrino.connect=mainnet2-btcd.zaphq.io')
@ -90,12 +110,14 @@ class Neutrino extends EventEmitter {
// neutrinoArgs.push('--neutrino.connect=testnet2-btcd.zaphq.io')
}
// Log the final config.
mainLog.info(
'Spawning Neutrino process: %s %s',
this.lndConfig.binaryPath,
neutrinoArgs.join(' ')
neutrinoArgs.filter(v => v != '').join(' ')
)
// Spawn lnd process.
this.process = spawn(this.lndConfig.binaryPath, neutrinoArgs)
.on('error', error => {
mainLog.debug('Neutrino process received "error" event with error: %s', error)

2
app/lib/zap/controller.js

@ -353,7 +353,7 @@ class ZapController {
this.sendMessage('lndCfilterHeight', Number(height))
})
this.neutrino.start()
return this.neutrino.start()
}
/**

1
package.json

@ -292,6 +292,7 @@
"electron-is-dev": "^0.3.0",
"electron-store": "^2.0.0",
"font-awesome": "^4.7.0",
"get-port": "^4.0.0",
"history": "^4.7.2",
"javascript-state-machine": "^3.1.0",
"jstimezonedetect": "^1.0.6",

12
test/unit/lnd/neutrino.spec.js

@ -179,9 +179,9 @@ describe('Neutrino', function() {
describe('.start', () => {
describe('called when neutrino is not running', () => {
beforeEach(() => {
beforeEach(async () => {
this.neutrino = new Neutrino(new LndConfig())
this.neutrino.start()
await this.neutrino.start()
})
it('should set the subprocess object on the `process` property', () => {
expect(this.neutrino.process.pid).toBeDefined()
@ -193,10 +193,10 @@ describe('Neutrino', function() {
this.neutrino = new Neutrino(new LndConfig())
this.neutrino.process = mockSpawn()
})
it('should throw an error', () => {
expect(() => {
this.neutrino.start()
}).toThrow()
it('should throw an error', async () => {
await expect(this.neutrino.start()).rejects.toThrowErrorMatchingInlineSnapshot(
`"Neutrino process with PID \${this.process.pid} already exists."`
)
})
})
})

4
yarn.lock

@ -5571,6 +5571,10 @@ get-port@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc"
get-port@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/get-port/-/get-port-4.0.0.tgz#373c85960138ee20027c070e3cb08019fea29816"
get-stdin@5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398"

Loading…
Cancel
Save