<repo_name>NewYorkCoinNYC/blockchain-account-monitor<file_sep>/test/poll_and_log_payments.js
const BlockchainAccountMonitor = require(__dirname+'/../lib/blockchain_account_monitor.js');
const BlockchainClient = require(__dirname+'/../lib/blockchain_client.js');
const config = require(__dirname+'/../lib/config.js');

const monitor = new BlockchainAccountMonitor({
  blockchainClient:  new BlockchainClient({
    host: config.get('RPC_HOST'),
    port: config.get('RPC_PORT'),
    user: config.get('RPC_USER'),
    pass: config.get('RPC_PASSWORD'),
    confirmations: config.get('RPC_CONFIRMATIONS'),
    type: config.get('RPC_TYPE')
  }),
  onBlock: function(block, next) {
    console.log('FOUND '+block.length+ ' transactions');
    console.log('block', block);
    next();
  },
  timeout: 1000
});

monitor.lastBlockHash = config.get('LAST_BLOCK_HASH');

monitor.start();

<file_sep>/lib/client.js
const _ = require('underscore');
const coinDaemon = require('node-newyorkcoin');

process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0" 

function BlockchainClient(options) {  
  this.confirmations = options.confirmations || 2;
  this.coinDaemon = coinDaemon({
    host: options.host,
    port: options.port,
    user: options.user,
    pass: options.pass,
    https: (options.https === false) ? false : true
  });
}

BlockchainClient.prototype = {

  listNextBlock: function(previousBlockHash, callback) {
    var _this = this;
    _this.coinDaemon.exec('listsinceblock', previousBlockHash, _this.confirmations,
    function(error, response) {
      if (error) {
        return callback(error, null);
      }
      var transactions = response.transactions; 
      if (!transactions || transactions.length === 0) {
        callback();
      } else {
        var confirmed = _this._filterByMinimumConfirmations(transactions, _this.confirmations);
        if (confirmed.length > 0) {
          var sorted = _this._sortTransactionsByTime(confirmed);
          var nextBlockHash = sorted[0].blockhash;
          callback(null, _this._transactionsInBlock(sorted, nextBlockHash));
        } else {
          callback();
        }
      }
    });
  },
  
  _sortTransactionsByTime: function(transactions) {
    return _.sortBy(transactions, function(transaction){
      return transaction.blocktime;
    }); 
  },

  _transactionsInBlock: function(transactions, blockHash) {
    return _.filter(transactions, function(transaction){
      return transaction.blockhash === blockHash;
    });
  },
  
  _filterByMinimumConfirmations: function(transactions, confirmations) {
    return _.filter(transactions, function(transaction){
      return parseInt(transaction.confirmations) >= confirmations;
    });
  }
}

module.exports = BlockchainClient;

<file_sep>/README.md

## Blockchain Monitor

## Installation

    npm install --save blockchain-account-monitor

## Usage

    const blockchain = require('blockchain-account-monitor');

    const monitor = new blockchain.AccountMonitor({
      blockchainClient:  new blockchain.Client({
        host: "You.Daemon.Host.here",
        port: 18823,
        user: "someRpcUser",
        pass: "<PASSWORD>",
        confirmations: 3,
        type: 'newyorkcoin'
      }),
      onBlock: function(block, next) {
        console.log('FOUND '+block.length+ ' transactions');
        console.log('block', block);
        next();
      },  
      timeout: 1000
    });

    monitor.lastBlockHash = '23e1b5d9c4258352e6d34728816cde2d46968072cf8d8545184b832149bdb94b';

    monitor.start();

<file_sep>/test/blockchain_poller.js
const BlockchainPoller = require(__dirname+'/../lib/blockchain_poller.js');
const BlockchainClient = require(__dirname+'/../lib/blockchain_client.js');
const config = require(__dirname+'/../lib/config.js');

describe('Blockchain Poller', function() {

  before(function() {
    blockchainPoller = new BlockchainPoller({
      lastBlockHash: config.get('lastBlockHash'),
      blockchainClient:  new BlockchainClient()
    });
  });

  it('accepts a function to call when a block with new transactions is discovered', function(done) {
  
    blockchainPoller.pollForBlocks(function(block, next) {
      config.set('lastBlockHash', block.hash);
      next();
      done();
    });
  });

  it('should not advance if an error is received', function(callback) {
    blockchainPoller.pollForBlocks(function(block, next, done) {
      delete block;
      next(new Error('AccidentalBlockDeletionError'));
      callback();
    });
  });

  it('#stop should stop the polling behavior', function() {
    var blocks = 0;
    blockchainPoller.pollForBlocks(function(block, next) {
      if (blocks+=1 > 3) {
        return blockchainPoller.stopPollingForBlocks();
      }
      next();
    });
  });

});

<file_sep>/lib/account_monitor.js

function BlockchainAccountMonitor(options) {
  this.lastBlockHash = options.lastBlockHash;
  this.blockchainClient = options.blockchainClient;
  this.timeout = options.timeout || 2000;
  this.onBlock = options.onBlock;
  this.onError = options.onError || function(error) {
    console.log('BlockchainAccountMonitor::Error', error);
  }
}

BlockchainAccountMonitor.prototype = {
  _getNextBlockWithTransactions: function getNextBlockWithTransactions() {
    var _this = this;
    _this.blockchainClient.listNextBlock(_this.lastBlockHash, function(error, block) {
      if (error) {
        _this.onError(error);
        return setTimeout(_this._getNextBlockWithTransactions.bind(_this), _this.timeout);
      }
      if (!block) {
        return setTimeout(_this._getNextBlockWithTransactions.bind(_this), _this.timeout);
      } else {
        return _this.onBlock(block, function() {
          _this.lastBlockHash = block[0].blockhash;
          _this._getNextBlockWithTransactions();
        });
      }
    });
  },
  
  start: function() {
    var _this = this;
    _this._getNextBlockWithTransactions();
  }

}

module.exports = BlockchainAccountMonitor;

