@ -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