|
|
@ -5,20 +5,37 @@ import config from '../config' |
|
|
|
import { mainLog, lndLog, lndLogGetLevel } from '../../utils/log' |
|
|
|
import { fetchBlockHeight } from './util' |
|
|
|
|
|
|
|
// Sync status is currenty pending.
|
|
|
|
const NEUTRINO_SYNC_STATUS_PENDING = 'chain-sync-pending' |
|
|
|
|
|
|
|
// Waiting for chain backend to finish synchronizing.
|
|
|
|
const NEUTRINO_SYNC_STATUS_WAITING = 'chain-sync-waiting' |
|
|
|
|
|
|
|
// Initial sync is currently in progress.
|
|
|
|
const NEUTRINO_SYNC_STATUS_IN_PROGRESS = 'chain-sync-started' |
|
|
|
|
|
|
|
// Initial sync has completed.
|
|
|
|
const NEUTRINO_SYNC_STATUS_COMPLETE = 'chain-sync-finished' |
|
|
|
|
|
|
|
/** |
|
|
|
* Wrapper class for Lnd to run and monitor it in Neutrino mode. |
|
|
|
* @extends EventEmitter |
|
|
|
*/ |
|
|
|
class Neutrino extends EventEmitter { |
|
|
|
constructor(alias, autopilot) { |
|
|
|
super() |
|
|
|
this.alias = alias |
|
|
|
this.autopilot = autopilot |
|
|
|
this.process = null |
|
|
|
this.state = { |
|
|
|
grpcProxyStarted: false, |
|
|
|
walletOpened: false, |
|
|
|
chainSyncStarted: false, |
|
|
|
chainSyncFinished: false |
|
|
|
} |
|
|
|
this.grpcProxyStarted = false |
|
|
|
this.walletOpened = false |
|
|
|
this.chainSyncStatus = NEUTRINO_SYNC_STATUS_PENDING |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Start the Lnd process in Neutrino mode. |
|
|
|
* @return {Number} PID of the Lnd process that was started. |
|
|
|
*/ |
|
|
|
start() { |
|
|
|
if (this.process) { |
|
|
|
throw new Error('Neutrino process with PID ${this.process.pid} already exists.') |
|
|
@ -59,28 +76,43 @@ class Neutrino extends EventEmitter { |
|
|
|
} |
|
|
|
|
|
|
|
// gRPC started.
|
|
|
|
if (!this.state.grpcProxyStarted) { |
|
|
|
if (!this.grpcProxyStarted) { |
|
|
|
if (line.includes('gRPC proxy started') && line.includes('password')) { |
|
|
|
this.state.grpcProxyStarted = true |
|
|
|
this.grpcProxyStarted = true |
|
|
|
this.emit('grpc-proxy-started') |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Wallet opened.
|
|
|
|
if (!this.state.walletOpened) { |
|
|
|
if (!this.walletOpened) { |
|
|
|
if (line.includes('gRPC proxy started') && !line.includes('password')) { |
|
|
|
this.state.walletOpened = true |
|
|
|
this.walletOpened = true |
|
|
|
this.emit('wallet-opened') |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// LND syncing has started.
|
|
|
|
if (!this.state.chainSyncStarted) { |
|
|
|
// If the sync has already completed then we don't need to do anythibng else.
|
|
|
|
if (this.is(NEUTRINO_SYNC_STATUS_COMPLETE)) { |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
// Lnd waiting for backend to finish syncing.
|
|
|
|
if (this.is(NEUTRINO_SYNC_STATUS_PENDING) || this.is(NEUTRINO_SYNC_STATUS_IN_PROGRESS)) { |
|
|
|
if ( |
|
|
|
line.includes('No sync peer candidates available') || |
|
|
|
line.includes('Unable to synchronize wallet to chain') || |
|
|
|
line.includes('Waiting for chain backend to finish sync') |
|
|
|
) { |
|
|
|
this.setState(NEUTRINO_SYNC_STATUS_WAITING) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Lnd syncing has started or resumed.
|
|
|
|
if (this.is(NEUTRINO_SYNC_STATUS_PENDING) || this.is(NEUTRINO_SYNC_STATUS_WAITING)) { |
|
|
|
const match = line.match(/Syncing to block height (\d+)/) |
|
|
|
if (match) { |
|
|
|
// Notify that chhain syncronisation has now started.
|
|
|
|
this.state.chainSyncStarted = true |
|
|
|
this.emit('chain-sync-started') |
|
|
|
this.setState(NEUTRINO_SYNC_STATUS_IN_PROGRESS) |
|
|
|
|
|
|
|
// This is the latest block that BTCd is aware of.
|
|
|
|
const btcdHeight = Number(match[1]) |
|
|
@ -98,25 +130,27 @@ class Neutrino extends EventEmitter { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// LND syncing has completed.
|
|
|
|
if (!this.state.chainSyncFinished) { |
|
|
|
if (line.includes('Chain backend is fully synced')) { |
|
|
|
this.state.chainSyncFinished = true |
|
|
|
this.emit('chain-sync-finished') |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Pass current block height progress to front end for loading state UX
|
|
|
|
if (this.state.chainSyncStarted) { |
|
|
|
// Lnd as received some updated block data.
|
|
|
|
if (this.is(NEUTRINO_SYNC_STATUS_WAITING) || this.is(NEUTRINO_SYNC_STATUS_IN_PROGRESS)) { |
|
|
|
let height |
|
|
|
let match |
|
|
|
if ((match = line.match(/Downloading headers for blocks (\d+) to \d+/))) { |
|
|
|
this.emit('got-lnd-block-height', match[1]) |
|
|
|
} else if ((match = line.match(/Rescanned through block.+\(height (\d+)/))) { |
|
|
|
this.emit('got-lnd-block-height', match[1]) |
|
|
|
|
|
|
|
if ((match = line.match(/Rescanned through block.+\(height (\d+)/))) { |
|
|
|
height = match[1] |
|
|
|
} else if ((match = line.match(/Caught up to height (\d+)/))) { |
|
|
|
this.emit('got-lnd-block-height', match[1]) |
|
|
|
height = match[1] |
|
|
|
} else if ((match = line.match(/Processed \d* blocks? in the last.+\(height (\d+)/))) { |
|
|
|
this.emit('got-lnd-block-height', match[1]) |
|
|
|
height = match[1] |
|
|
|
} |
|
|
|
|
|
|
|
if (height) { |
|
|
|
this.setState(NEUTRINO_SYNC_STATUS_IN_PROGRESS) |
|
|
|
this.emit('got-lnd-block-height', height) |
|
|
|
} |
|
|
|
|
|
|
|
// Lnd syncing has completed.
|
|
|
|
if (line.includes('Chain backend is fully synced')) { |
|
|
|
this.setState(NEUTRINO_SYNC_STATUS_COMPLETE) |
|
|
|
} |
|
|
|
} |
|
|
|
}) |
|
|
@ -124,12 +158,35 @@ class Neutrino extends EventEmitter { |
|
|
|
return this.process |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Stop the Lnd process. |
|
|
|
*/ |
|
|
|
stop() { |
|
|
|
if (this.process) { |
|
|
|
this.process.kill() |
|
|
|
this.process = null |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Check if the current state matches the passted in state. |
|
|
|
* @param {String} state State to compare against the current state. |
|
|
|
* @return {Boolean} Boolean indicating if the current state matches the passed in state. |
|
|
|
*/ |
|
|
|
is(state) { |
|
|
|
return this.chainSyncStatus === state |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Set the current state and emit an event to notify others if te state as canged. |
|
|
|
* @param {String} state Target state. |
|
|
|
*/ |
|
|
|
setState(state) { |
|
|
|
if (state !== this.chainSyncStatus) { |
|
|
|
this.chainSyncStatus = state |
|
|
|
this.emit(state) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
export default Neutrino |
|
|
|