Browse Source

feat(sync): show lnd block sync progress

Show the current and total block counts to provide better feedback to
users about what is happening during the sync process.

Fix #517
renovate/lint-staged-8.x
Tom Kirkpatrick 7 years ago
parent
commit
d617dd2657
No known key found for this signature in database GPG Key ID: 72203A8EC5967EA8
  1. 22
      app/components/Onboarding/Syncing.js
  2. 6
      app/components/Onboarding/Syncing.scss
  3. 5
      app/containers/Root.js
  4. 20
      app/lnd/lib/neutrino.js
  5. 4
      app/reducers/ipc.js
  6. 39
      app/reducers/lnd.js
  7. 12
      app/zap.js

22
app/components/Onboarding/Syncing.js

@ -16,8 +16,7 @@ class Syncing extends Component {
} }
render() { render() {
const { syncPercentage, address } = this.props const { syncPercentage, address, blockHeight, lndBlockHeight } = this.props
const copyClicked = () => { const copyClicked = () => {
copy(address) copy(address)
showNotification('Noice', 'Successfully copied to clipboard') showNotification('Noice', 'Successfully copied to clipboard')
@ -66,14 +65,21 @@ class Syncing extends Component {
<div className={styles.progressBar}> <div className={styles.progressBar}>
<div <div
className={styles.progress} className={styles.progress}
style={{ width: Number.isNaN(syncPercentage) ? 0 : `${syncPercentage}%` }} style={{ width: syncPercentage ? `${syncPercentage}%` : 0 }}
/> />
</div> </div>
<h4> <h4>
{Number.isNaN(parseInt(syncPercentage, 10)) || syncPercentage.toString().length === 0 {typeof syncPercentage === 'undefined' && 'Preparing...'}
? '' {Boolean(syncPercentage >= 0 && syncPercentage < 99) && `${syncPercentage}%`}
: `${syncPercentage}%`} {Boolean(syncPercentage >= 99) && 'Finalizing...'}
</h4> </h4>
{Boolean(syncPercentage >= 0 && syncPercentage < 99) && (
<span className={styles.progressCounter}>
{Boolean(!blockHeight || !lndBlockHeight) && 'starting...'}
{Boolean(blockHeight && lndBlockHeight) &&
`${lndBlockHeight.toLocaleString()} of ${blockHeight.toLocaleString()}`}
</span>
)}
</section> </section>
</div> </div>
</div> </div>
@ -85,7 +91,9 @@ Syncing.propTypes = {
fetchBlockHeight: PropTypes.func.isRequired, fetchBlockHeight: PropTypes.func.isRequired,
newAddress: PropTypes.func.isRequired, newAddress: PropTypes.func.isRequired,
address: PropTypes.string.isRequired, address: PropTypes.string.isRequired,
syncPercentage: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired syncPercentage: PropTypes.number,
blockHeight: PropTypes.number,
lndBlockHeight: PropTypes.number
} }
export default Syncing export default Syncing

6
app/components/Onboarding/Syncing.scss

@ -94,6 +94,12 @@
color: $gold; color: $gold;
margin-top: 10px; margin-top: 10px;
} }
.progressCounter {
color: $gold;
font-size: 12px;
margin-top: 10px;
}
} }
.spinner { .spinner {

5
app/containers/Root.js

@ -82,8 +82,9 @@ const mapStateToProps = state => ({
const mergeProps = (stateProps, dispatchProps, ownProps) => { const mergeProps = (stateProps, dispatchProps, ownProps) => {
const syncingProps = { const syncingProps = {
fetchBlockHeight: dispatchProps.fetchBlockHeight, fetchBlockHeight: dispatchProps.fetchBlockHeight,
blockHeight: stateProps.lnd.blockHeight,
lndBlockHeight: stateProps.lnd.lndBlockHeight,
newAddress: dispatchProps.newAddress, newAddress: dispatchProps.newAddress,
syncPercentage: stateProps.syncPercentage, syncPercentage: stateProps.syncPercentage,
address: stateProps.address.address address: stateProps.address.address
} }
@ -214,7 +215,7 @@ const Root = ({
} }
// If we are syncing show the syncing screen // If we are syncing show the syncing screen
if (lnd.syncing) { if (lnd.grpcStarted && lnd.syncing) {
return <Syncing {...syncingProps} /> return <Syncing {...syncingProps} />
} }

20
app/lnd/lib/neutrino.js

@ -61,14 +61,26 @@ class Neutrino extends EventEmitter {
this.emit('wallet-opened') this.emit('wallet-opened')
} }
// LND is all caught up to the blockchain. // LND syncing has started.
if (line.includes('Waiting for chain backend to finish sync')) {
this.emit('chain-sync-started')
}
// LND syncing has completed.
if (line.includes('Chain backend is fully synced')) { if (line.includes('Chain backend is fully synced')) {
this.emit('fully-synced') this.emit('chain-sync-finished')
} }
// Pass current block height progress to front end for loading state UX // Pass current block height progress to front end for loading state UX
if (line.includes('Caught up to height') || line.includes('Catching up block hashes')) { if (line.includes('Rescanned through block')) {
this.emit('got-block-height', line) const height = line.match(/Rescanned through block.+\(height (\d*)/)[1]
this.emit('got-block-height', height)
} else if (line.includes('Caught up to height')) {
const height = line.match(/Caught up to height (\d*)/)[1]
this.emit('got-block-height', height)
} else if (line.includes('in the last')) {
const height = line.match(/Processed \d* blocks? in the last.+\(height (\d*)/)[1]
this.emit('got-block-height', height)
} }
}) })
return this.process return this.process

4
app/reducers/ipc.js

@ -1,5 +1,5 @@
import createIpc from 'redux-electron-ipc' import createIpc from 'redux-electron-ipc'
import { lndSyncing, lndSynced, lndStdout, grpcDisconnected, grpcConnected } from './lnd' import { lndSyncing, lndSynced, lndBlockHeight, grpcDisconnected, grpcConnected } from './lnd'
import { receiveInfo } from './info' import { receiveInfo } from './info'
import { receiveAddress } from './address' import { receiveAddress } from './address'
import { receiveCryptocurrency } from './ticker' import { receiveCryptocurrency } from './ticker'
@ -53,7 +53,7 @@ import {
const ipc = createIpc({ const ipc = createIpc({
lndSyncing, lndSyncing,
lndSynced, lndSynced,
lndStdout, lndBlockHeight,
grpcDisconnected, grpcDisconnected,
grpcConnected, grpcConnected,

39
app/reducers/lnd.js

@ -10,10 +10,9 @@ import { showNotification } from '../notifications'
export const START_SYNCING = 'START_SYNCING' export const START_SYNCING = 'START_SYNCING'
export const STOP_SYNCING = 'STOP_SYNCING' export const STOP_SYNCING = 'STOP_SYNCING'
export const RECEIVE_LINE = 'RECEIVE_LINE'
export const GET_BLOCK_HEIGHT = 'GET_BLOCK_HEIGHT' export const GET_BLOCK_HEIGHT = 'GET_BLOCK_HEIGHT'
export const RECEIVE_BLOCK_HEIGHT = 'RECEIVE_BLOCK_HEIGHT' export const RECEIVE_BLOCK_HEIGHT = 'RECEIVE_BLOCK_HEIGHT'
export const RECEIVE_BLOCK = 'RECEIVE_BLOCK'
export const GRPC_DISCONNECTED = 'GRPC_DISCONNECTED' export const GRPC_DISCONNECTED = 'GRPC_DISCONNECTED'
export const GRPC_CONNECTED = 'GRPC_CONNECTED' export const GRPC_CONNECTED = 'GRPC_CONNECTED'
@ -43,29 +42,14 @@ export const lndSynced = () => dispatch => {
export const grpcDisconnected = () => dispatch => dispatch({ type: GRPC_DISCONNECTED }) export const grpcDisconnected = () => dispatch => dispatch({ type: GRPC_DISCONNECTED })
export const grpcConnected = () => dispatch => dispatch({ type: GRPC_CONNECTED }) export const grpcConnected = () => dispatch => {
dispatch(fetchInfo())
dispatch({ type: GRPC_CONNECTED })
}
// Receive IPC event for LND streaming a line // Receive IPC event for LND streaming a line
export const lndStdout = (event, line) => dispatch => { export const lndBlockHeight = (event, height) => dispatch => {
let height dispatch({ type: RECEIVE_BLOCK, lndBlockHeight: height })
let trimmed
if (line.includes('Caught up to height')) {
trimmed = line.slice(line.indexOf('Caught up to height') + 'Caught up to height'.length).trim()
height = trimmed.split(' ')[0].split(/(\r\n|\n|\r)/gm)[0]
}
if (line.includes('Catching up block hashes to height')) {
trimmed = line
.slice(
line.indexOf('Catching up block hashes to height') +
'Catching up block hashes to height'.length
)
.trim()
height = trimmed.match(/[-]{0,1}[\d.]*[\d]+/g)[0]
}
dispatch({ type: RECEIVE_LINE, lndBlockHeight: height })
} }
export function getBlockHeight() { export function getBlockHeight() {
@ -95,14 +79,13 @@ const ACTION_HANDLERS = {
[START_SYNCING]: state => ({ ...state, syncing: true }), [START_SYNCING]: state => ({ ...state, syncing: true }),
[STOP_SYNCING]: state => ({ ...state, syncing: false }), [STOP_SYNCING]: state => ({ ...state, syncing: false }),
[RECEIVE_LINE]: (state, { lndBlockHeight }) => ({ ...state, lndBlockHeight }),
[GET_BLOCK_HEIGHT]: state => ({ ...state, fetchingBlockHeight: true }), [GET_BLOCK_HEIGHT]: state => ({ ...state, fetchingBlockHeight: true }),
[RECEIVE_BLOCK_HEIGHT]: (state, { blockHeight }) => ({ [RECEIVE_BLOCK_HEIGHT]: (state, { blockHeight }) => ({
...state, ...state,
blockHeight, blockHeight,
fetchingBlockHeight: false fetchingBlockHeight: false
}), }),
[RECEIVE_BLOCK]: (state, { lndBlockHeight }) => ({ ...state, lndBlockHeight }),
[GRPC_DISCONNECTED]: state => ({ ...state, grpcStarted: false }), [GRPC_DISCONNECTED]: state => ({ ...state, grpcStarted: false }),
[GRPC_CONNECTED]: state => ({ ...state, grpcStarted: true }) [GRPC_CONNECTED]: state => ({ ...state, grpcStarted: true })
@ -133,11 +116,11 @@ lndSelectors.syncPercentage = createSelector(
(blockHeight, lndBlockHeight) => { (blockHeight, lndBlockHeight) => {
const percentage = Math.floor((lndBlockHeight / blockHeight) * 100) const percentage = Math.floor((lndBlockHeight / blockHeight) * 100)
if (percentage === Infinity) { if (percentage === Infinity || Number.isNaN(percentage)) {
return '' return undefined
} }
return percentage return parseInt(percentage, 10)
} }
) )

12
app/zap.js

@ -158,16 +158,20 @@ class ZapController {
this.neutrino.on('wallet-opened', () => { this.neutrino.on('wallet-opened', () => {
mainLog.info('Wallet opened') mainLog.info('Wallet opened')
this.startGrpc() this.startGrpc()
})
this.neutrino.on('chain-sync-started', () => {
mainLog.info('Neutrino sync started')
this.sendMessage('lndSyncing') this.sendMessage('lndSyncing')
}) })
this.neutrino.on('fully-synced', () => { this.neutrino.on('chain-sync-finished', () => {
mainLog.info('Neutrino fully synced') mainLog.info('Neutrino sync finished')
this.sendMessage('lndSynced') this.sendMessage('lndSynced')
}) })
this.neutrino.on('got-block-height', line => { this.neutrino.on('got-block-height', height => {
this.sendMessage('lndStdout', line) this.sendMessage('lndBlockHeight', Number(height))
}) })
this.neutrino.start() this.neutrino.start()

Loading…
Cancel
Save