From 15f960aec1a19a3bec9a4a84cdb8ab7912343f4b Mon Sep 17 00:00:00 2001 From: William O'Beirne Date: Thu, 28 Mar 2019 11:35:45 -0400 Subject: [PATCH] Blockchain watcher safety adjustments (#411) * Fix a failed block potentially being skipped. Up retry sleep to 2 minutes, up number of failed requests to 10 to give blockchain more time to recover. * Fix non-transaction vouts causing issues. * Wrap whole scan function in try. Reduce retry time to 1 minute. --- blockchain/src/node.ts | 4 +-- blockchain/src/webhooks/index.ts | 36 +++++++++---------- .../webhooks/notifiers/contribution/index.ts | 5 +++ 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/blockchain/src/node.ts b/blockchain/src/node.ts index 82c1935a..17c09e21 100644 --- a/blockchain/src/node.ts +++ b/blockchain/src/node.ts @@ -17,9 +17,9 @@ export interface BlockChainInfo { export interface ScriptPubKey { asm: string; hex: string; - reqSigs: number; type: string; - addresses: string[]; + reqSigs?: number; + addresses?: string[]; } export interface VIn { diff --git a/blockchain/src/webhooks/index.ts b/blockchain/src/webhooks/index.ts index 434e406d..ab4c19a1 100755 --- a/blockchain/src/webhooks/index.ts +++ b/blockchain/src/webhooks/index.ts @@ -11,7 +11,7 @@ import log from "../log"; let blockScanTimeout: any = null; let notifiers = [] as Notifier[]; let consecutiveBlockFailures = 0; -const MAXIMUM_BLOCK_FAILURES = 5; +const MAXIMUM_BLOCK_FAILURES = 10; const MIN_BLOCK_CONF = parseInt(env.MINIMUM_BLOCK_CONFIRMATIONS, 10); export async function start() { @@ -38,7 +38,7 @@ function initScan() { store.subscribe(() => { const { startingBlockHeight } = store.getState(); if (startingBlockHeight !== null && prevHeight !== startingBlockHeight) { - console.info(`Starting block scan at block ${startingBlockHeight}`); + log.info(`Starting block scan at block ${startingBlockHeight}`); clearTimeout(blockScanTimeout); scanBlock(startingBlockHeight); prevHeight = startingBlockHeight; @@ -47,22 +47,23 @@ function initScan() { } async function scanBlock(height: number) { - const highestBlock = await node.getblockcount(); - - // Try again in 5 seconds if the next block isn't ready - if (height > highestBlock - MIN_BLOCK_CONF) { - blockScanTimeout = setTimeout(() => { - scanBlock(height); - }, 5000); - return; - } - - // Process the block try { + // Fetch the current block height, try again in 5 seconds if the next + // block doesn't meet our confirmation requirement + const highestBlock = await node.getblockcount(); + if (height > highestBlock - MIN_BLOCK_CONF) { + blockScanTimeout = setTimeout(() => { + scanBlock(height); + }, 5000); + return; + } + + // Process the block, then try the next one const block = await node.getblock(String(height), 2); // 2 == full blocks log.info(`Processing block #${block.height}...`); notifiers.forEach(n => n.onNewBlock && n.onNewBlock(block)); consecutiveBlockFailures = 0; + scanBlock(height + 1); } catch(err) { log.warn(`Failed to fetch block ${height}: ${extractErrMessage(err)}`); consecutiveBlockFailures++; @@ -74,13 +75,12 @@ async function scanBlock(height: number) { process.exit(1); } else { - log.warn('Attempting to fetch again shortly...'); - await sleep(5000); + log.warn('Attempting to fetch again in 60 seconds...'); + await sleep(60000); } + // Try same block again + scanBlock(height); } - - // Try next block - scanBlock(height + 1); } function initNotifiers() { diff --git a/blockchain/src/webhooks/notifiers/contribution/index.ts b/blockchain/src/webhooks/notifiers/contribution/index.ts index a8e21b20..b84ccace 100644 --- a/blockchain/src/webhooks/notifiers/contribution/index.ts +++ b/blockchain/src/webhooks/notifiers/contribution/index.ts @@ -42,6 +42,11 @@ export default class ContributionNotifier implements Notifier { block.tx.forEach(tx => { tx.vout.forEach(vout => { + // Some vouts are not transactions with addresses, ignore those + if (!vout.scriptPubKey.addresses) { + return; + } + // Addresses is an array because of multisigs, but we'll never // generate one, so all of our addresses will only have addresses[0] const to = vout.scriptPubKey.addresses[0];