From a5800d10569185d30fc80c085146126b2feee20f Mon Sep 17 00:00:00 2001 From: Tom Kirkpatrick Date: Thu, 13 Sep 2018 17:38:26 +0200 Subject: [PATCH 1/2] feat(lnd): update lnd to v0.5-beta-6-g48d016bc --- app/reducers/invoice.js | 2 +- package.json | 2 +- resources/lnd.conf | 23 +++++++++++++---------- resources/rpc.proto | 29 +++++++++++++++++++++-------- 4 files changed, 36 insertions(+), 20 deletions(-) diff --git a/app/reducers/invoice.js b/app/reducers/invoice.js index 095d7f81..536637bd 100644 --- a/app/reducers/invoice.js +++ b/app/reducers/invoice.js @@ -40,7 +40,7 @@ export const UPDATE_INVOICE = 'UPDATE_INVOICE' // Decorate invoice object with custom/computed properties. const decorateInvoice = invoice => { - invoice.finalAmount = invoice.amt_paid ? invoice.amt_paid : invoice.value + invoice.finalAmount = invoice.amt_paid_sat ? invoice.amt_paid_sat : invoice.value return invoice } diff --git a/package.json b/package.json index f7907494..6553c189 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "config": { "style_paths": "app/styles/*.scss app/components/**/*.scss", "lnd-binary": { - "binaryVersion": "0.5-beta-rc2-41-g4dd4f7cf", + "binaryVersion": "0.5-beta-6-g48d016bc", "binarySite": "https://github.com/LN-Zap/lnd/releases/download" } }, diff --git a/resources/lnd.conf b/resources/lnd.conf index 76e1c555..0a61f59c 100644 --- a/resources/lnd.conf +++ b/resources/lnd.conf @@ -42,15 +42,23 @@ ; Path to write the admin macaroon for lnd's RPC and REST services if it ; doesn't exist. This can be set if one wishes to store the admin macaroon in a -; distinct location. By default, it is stored within lnd's main home directory. -; Applications that are able to read this file, gains admin macaroon access -; adminmacaroonpath=~/.lnd/admin.macaroon +; distinct location. By default, it is stored within lnd's network directory. +; Applications that are able to read this file, gain admin macaroon access. +; adminmacaroonpath=~/.lnd/data/chain/bitcoin/simnet/admin.macaroon ; Path to write the read-only macaroon for lnd's RPC and REST services if it ; doesn't exist. This can be set if one wishes to store the read-only macaroon ; in a distinct location. The read only macaroon allows users which can read -; the file to access RPCs which don't modify the state of the daemon. -; readonlymacaroonpath=~/.lnd/readonly.macaroon +; the file to access RPCs which don't modify the state of the daemon. By +; default, it is stored within lnd's network directory. +; readonlymacaroonpath=~/.lnd/data/chain/bitcoin/simnet/readonly.macaroon + +; Path to write the invoice macaroon for lnd's RPC and REST services if it +; doesn't exist. This can be set if one wishes to store the invoice macaroon in +; a distinct location. By default, it is stored within lnd's network directory. +; The invoice macaroon allows users which can read the file to gain read and +; write access to all invoice related RPCs. +; invoicemacaroonpath=~/.lnd/data/chain/bitcoin/simnet/invoice.macaroon ; Specify the interfaces to listen on for p2p connections. One listen @@ -128,11 +136,6 @@ debuglevel=debug ; network. ; nobootstrap=1 -; If set, your wallet will be encrypted with the default passphrase. This isn't -; recommend, as if an attacker gains access to your wallet file, they'll be able -; to decrypt it. This value is ONLY to be used in testing environments. -; noencryptwallet=1 - ; The alias your node will use, which can be up to 32 UTF-8 characters in ; length. ; alias=My Lightning ☇ diff --git a/resources/rpc.proto b/resources/rpc.proto index 6d6e45c8..acd6a9e5 100644 --- a/resources/rpc.proto +++ b/resources/rpc.proto @@ -1,4 +1,4 @@ -// Imported from https://github.com/lightningnetwork/lnd/blob/baee07ef480c15a731e0c87dc98d27269c5a80fa/lnrpc/rpc.proto +// Imported from https://github.com/lightningnetwork/lnd/blob/48d016bc78e9f338742b10d936e52e7655fd8770/lnrpc/rpc.proto syntax = "proto3"; // import "google/api/annotations.proto"; @@ -1670,15 +1670,28 @@ message Invoice { */ uint64 settle_index = 17 [json_name = "settle_index"]; + /// Deprecated, use amt_paid_sat or amt_paid_msat. + int64 amt_paid = 18 [json_name = "amt_paid", deprecated = true]; + + /** + The amount that was accepted for this invoice, in satoshis. This will ONLY + be set if this invoice has been settled. We provide this field as if the + invoice was created with a zero value, then we need to record what amount + was ultimately accepted. Additionally, it's possible that the sender paid + MORE that was specified in the original invoice. So we'll record that here + as well. + */ + int64 amt_paid_sat = 19 [json_name = "amt_paid_sat"]; + /** - The amount that was accepted for this invoice. This will ONLY be set if - this invoice has been settled. We provide this field as if the invoice was - created with a zero value, then we need to record what amount was - ultimately accepted. Additionally, it's possible that the sender paid MORE - that was specified in the original invoice. So we'll record that here as - well. + The amount that was accepted for this invoice, in millisatoshis. This will + ONLY be set if this invoice has been settled. We provide this field as if + the invoice was created with a zero value, then we need to record what + amount was ultimately accepted. Additionally, it's possible that the sender + paid MORE that was specified in the original invoice. So we'll record that + here as well. */ - int64 amt_paid = 18 [json_name = "amt_paid"]; + int64 amt_paid_msat = 20 [json_name = "amt_paid_msat"]; } message AddInvoiceResponse { bytes r_hash = 1 [json_name = "r_hash"]; From 3326db1e55fba05a18157600348da63711e543d4 Mon Sep 17 00:00:00 2001 From: Tom Kirkpatrick Date: Fri, 14 Sep 2018 11:15:57 +0200 Subject: [PATCH 2/2] fix(lnd): send SIGINT to lnd to terminate --- app/lib/lnd/neutrino.js | 27 ++++++++--- app/lib/zap/controller.js | 85 +++++++++++++--------------------- test/unit/lnd/neutrino.spec.js | 15 ------ 3 files changed, 53 insertions(+), 74 deletions(-) diff --git a/app/lib/lnd/neutrino.js b/app/lib/lnd/neutrino.js index dd2c55f3..68c60f50 100644 --- a/app/lib/lnd/neutrino.js +++ b/app/lib/lnd/neutrino.js @@ -15,7 +15,7 @@ const CHAIN_SYNC_COMPLETE = 'chain-sync-finished' // Events const ERROR = 'error' -const CLOSE = 'close' +const EXIT = 'exit' const WALLET_UNLOCKER_GRPC_ACTIVE = 'wallet-unlocker-grpc-active' const LIGHTNING_GRPC_ACTIVE = 'lightning-grpc-active' const GOT_CURRENT_BLOCK_HEIGHT = 'got-current-block-height' @@ -90,11 +90,25 @@ class Neutrino extends EventEmitter { // neutrinoArgs.push('--neutrino.connect=testnet2-btcd.zaphq.io') } + mainLog.info( + 'Spawning Neutrino process: %s %s', + this.lndConfig.binaryPath, + neutrinoArgs.join(' ') + ) + this.process = spawn(this.lndConfig.binaryPath, neutrinoArgs) - .on('error', error => this.emit(ERROR, error)) - .on('close', code => { - this.emit(CLOSE, code, this.lastError) + .on('error', error => { + mainLog.debug('Neutrino process received "error" event with error: %s', error) + this.emit(ERROR, error, this.lastError) + }) + .on('exit', (code, signal) => { + mainLog.debug( + 'Neutrino process received "exit" event with code %s and signal %s', + code, + signal + ) this.process = null + this.emit(EXIT, code, signal, this.lastError) }) // Listen for when neutrino prints data to stderr. @@ -230,11 +244,10 @@ class Neutrino extends EventEmitter { /** * Stop the Lnd process. */ - kill() { + kill(signalName: string = 'SIGINT') { if (this.process) { mainLog.info('Killing Neutrino process...') - this.process.kill() - this.process = null + this.process.kill(signalName) } } diff --git a/app/lib/zap/controller.js b/app/lib/zap/controller.js index 8f1255ce..368005e2 100644 --- a/app/lib/zap/controller.js +++ b/app/lib/zap/controller.js @@ -288,11 +288,8 @@ class ZapController { } } - /** /** * Starts the LND node and attach event listeners. - * @param {string} alias Alias to assign to the lnd node. - * @param {boolean} autopilot True if autopilot should be enabled. * @return {Neutrino} Neutrino instance. */ startNeutrino() { @@ -307,12 +304,12 @@ class ZapController { }) }) - this.neutrino.on('close', (code, lastError) => { - mainLog.info(`Lnd process has shut down (code ${code})`) + this.neutrino.on('exit', (code, signal, lastError) => { + mainLog.info(`Lnd process has shut down (code: ${code}, signal: ${signal})`) if (this.is('running') || this.is('connected')) { dialog.showMessageBox({ type: 'error', - message: `Lnd has unexpectedly quit: ${lastError}` + message: `Lnd has unexpectedly quit:\n\nError code: ${code}\nExit signal: ${signal}\nLast error: ${lastError}` }) this.terminate() } @@ -358,61 +355,45 @@ class ZapController { */ async shutdownNeutrino() { // We only want to shut down LND if we are running it locally. - if (this.lndConfig.type !== 'local') { + if (this.lndConfig.type !== 'local' || !this.neutrino || !this.neutrino.process) { return Promise.resolve() } - // Attempt a graceful shutdown if we can. - if (this.lightning && this.lightning.can('terminate')) { - mainLog.info('Shutting down Neutrino...') - - return new Promise(resolve => { - // HACK: Sometimes there are errors during the shutdown process that prevent the daeming from shutting down at - // all. If we haven't received notification of the process closing within 10 seconds, kill it. - // See https://github.com/lightningnetwork/lnd/pull/1781 - // See https://github.com/lightningnetwork/lnd/pull/1783 - const shutdownTimeout = setTimeout(() => { - this.neutrino.removeListener('close', closeHandler) - if (this.neutrino) { - mainLog.warn('Graceful shutdown failed to complete within 10 seconds.') - this.neutrino.kill() - resolve() - } - }, 1000 * 10) - - // HACK: The Lightning.stopDaemon` call returns before lnd has actually fully completed the shutdown process - // so we add a listener on the close event so that we can wrap things up once the process has been fully closed - // out. - const closeHandler = function() { - mainLog.info('Neutrino shutdown complete.') - clearTimeout(shutdownTimeout) + mainLog.info('Shutting down Neutrino...') + + return new Promise(async resolve => { + // HACK: Sometimes there are errors during the shutdown process that prevent the daeming from shutting down at + // all. If we haven't received notification of the process closing within 10 seconds, kill it. + // See https://github.com/lightningnetwork/lnd/pull/1781 + // See https://github.com/lightningnetwork/lnd/pull/1783 + const shutdownTimeout = setTimeout(() => { + this.neutrino.removeListener('exit', exitHandler) + if (this.neutrino) { + mainLog.warn('Graceful shutdown failed to complete within 10 seconds.') + this.neutrino.kill('SIGTERM') resolve() } - this.neutrino.once('close', closeHandler) - - this.lightning - .terminate() - .then(() => mainLog.info('Neutrino Daemon shutdown complete')) - .catch(err => { - mainLog.error('Unable to gracefully shutdown LND: %o', err) - // Kill the process ourselves here to ensure that we don't leave hanging processes. - if (this.neutrino) { - this.neutrino.kill() - resolve() - } - }) - }) - } + }, 1000 * 10) + + const exitHandler = () => { + clearTimeout(shutdownTimeout) + resolve() + } + this.neutrino.once('exit', exitHandler) - // The Lightning service is only active after the wallet has been unlocked and a gRPC connection has been - // established. In this case, kill the Neutrino process to ensure that we don't leave hanging process. - // FIXME: This currencly doesn't do a graceful shutdown as LND does not properly handle SIGTERM. - // See https://github.com/lightningnetwork/lnd/issues/1028 - else if (this.neutrino) { + // The Lightning service is only active once the wallet has been unlocked and a gRPC connection has been made. + // If it is active, disconnect from it before we terminate neutrino. + if (this.lightning && this.lightning.can('terminate')) { + await this.lightning.disconnect() + } + // Kill the Neutrino process (sends SIGINT to Neutrino process) this.neutrino.kill() - } + }).then(() => mainLog.info('Neutrino shutdown complete')) } + /** + * Start or connect to lnd process after onboarding has been completed by the app. + */ finishOnboarding(options: onboardingOptions) { mainLog.info('Finishing onboarding') // Save the lnd config options that we got from the renderer. diff --git a/test/unit/lnd/neutrino.spec.js b/test/unit/lnd/neutrino.spec.js index 7c645ee6..4c80d918 100644 --- a/test/unit/lnd/neutrino.spec.js +++ b/test/unit/lnd/neutrino.spec.js @@ -200,19 +200,4 @@ describe('Neutrino', function() { }) }) }) - - describe('.kill', () => { - describe('called when neutrino is already running', () => { - beforeEach(() => { - this.neutrino = new Neutrino(new LndConfig()) - this.neutrino.process = { - kill: jest.fn() - } - this.neutrino.kill() - }) - it('should kill the neutrino process', () => { - expect(this.neutrino.process).toBeNull() - }) - }) - }) })