diff --git a/lib/logger.js b/lib/logger.js index c0bb48e0..78c84525 100644 --- a/lib/logger.js +++ b/lib/logger.js @@ -66,6 +66,9 @@ Logger.prototype._log = function(color) { args[0] = '[' + date.toISOString() + ']' + ' ' + typeString + ' ' + args[0]; } var fn = console.log; + if (level === 'error') { + fn = console.error; + } fn.apply(console, args); }; diff --git a/lib/node.js b/lib/node.js index a376cdb8..31f95101 100644 --- a/lib/node.js +++ b/lib/node.js @@ -3,9 +3,8 @@ var util = require('util'); var EventEmitter = require('events').EventEmitter; var async = require('async'); +var assert = require('assert'); var bitcore = require('bitcore-lib'); -var Networks = bitcore.Networks; -var $ = bitcore.util.preconditions; var _ = bitcore.deps._; var index = require('./'); var log = index.log; @@ -25,7 +24,6 @@ function Node(config) { } if (config.services) { - $.checkArgument(Array.isArray(config.services)); this._unloadedServices = config.services; } } @@ -49,15 +47,7 @@ Node.prototype._init = function(config) { }; Node.prototype._setNetwork = function(config) { - if (config.network === 'testnet') { - this.network = Networks.get('testnet'); - } else if (config.network === 'regtest') { - Networks.enableRegtest(); - this.network = Networks.get('regtest'); - } else { - this.network = Networks.defaultNetwork; - } - $.checkState(this.network, 'Unrecognized network'); + this.network = config.network; }; Node.prototype.openBus = function(options) { @@ -107,7 +97,7 @@ Node.prototype._getServiceOrder = function(services) { var name = names[i]; var service = servicesByName[name]; - $.checkState(service, 'Required dependency "' + name + '" not available.'); + assert(service, 'Required dependency "' + name + '" not available.'); addToStack(service.module.dependencies); @@ -131,9 +121,9 @@ Node.prototype._startService = function(serviceInfo, callback) { var config; if (serviceInfo.config) { - $.checkState(_.isObject(serviceInfo.config)); - $.checkState(!serviceInfo.config.node); - $.checkState(!serviceInfo.config.name); + assert(_.isObject(serviceInfo.config)); + assert(serviceInfo.config.node); + assert(serviceInfo.config.name); config = serviceInfo.config; } else { config = {}; diff --git a/lib/services/address/index.js b/lib/services/address/index.js index d86ca036..ab5f8881 100644 --- a/lib/services/address/index.js +++ b/lib/services/address/index.js @@ -371,7 +371,7 @@ AddressService.prototype._onReorg = function(commonAncestorHeader, oldBlockList) var address; for(var k = 0; k < tx.inputs.length; k++) { var input = tx.inputs[k]; - address = utils.getAddressString({ tx: tx, item: input, network: this._network }); + address = input.address; if (!address) { continue; @@ -386,7 +386,7 @@ AddressService.prototype._onReorg = function(commonAncestorHeader, oldBlockList) //outputs for(k = 0; k < tx.outputs.length; k++) { var output = tx.outputs[k]; - address = utils.getAddressString({ tx: tx, item: output, network: this._network }); + address = output.address; if (!address) { continue; @@ -431,7 +431,7 @@ AddressService.prototype.onBlock = function(block, callback) { AddressService.prototype._processInput = function(tx, input, opts) { - var address = utils.getAddressString({ item: input }); + var address = input.address; if(!address) { return; @@ -464,7 +464,7 @@ AddressService.prototype._processInput = function(tx, input, opts) { AddressService.prototype._processOutput = function(tx, output, index, opts) { - var address = utils.getAddressString({ item: output }); + var address = output.address; if(!address) { return; diff --git a/lib/services/block/index.js b/lib/services/block/index.js index 0b108077..3e6dba82 100644 --- a/lib/services/block/index.js +++ b/lib/services/block/index.js @@ -317,6 +317,7 @@ BlockService.prototype._handleReorg = function(hash, allHeaders) { this._reorging = false; }); + }; // get the blocks from our current tip to the given hash, non-inclusive diff --git a/lib/services/db/index.js b/lib/services/db/index.js index d0b3bb50..a86757a1 100644 --- a/lib/services/db/index.js +++ b/lib/services/db/index.js @@ -6,9 +6,6 @@ var async = require('async'); var levelup = require('levelup'); var leveldown = require('leveldown'); var mkdirp = require('mkdirp'); -var bitcore = require('bitcore-lib'); -var Networks = bitcore.Networks; -var $ = bitcore.util.preconditions; var Service = require('../../service'); var constants = require('../../constants'); var log = require('../../index').log; @@ -38,10 +35,10 @@ function DB(options) { this.subscriptions = {}; - this.GENESIS_HASH = constants.BITCOIN_GENESIS_HASH[this.node.getNetworkName()]; + this.GENESIS_HASH = constants.BITCOIN_GENESIS_HASH[this.node.network]; this.node.on('stopping', function() { - log.warn('Node is stopping, gently closing the database.'); + log.warn('Node is stopping, gently closing the database. Please wait, this could take a while.'); }); } @@ -52,19 +49,16 @@ DB.dependencies = []; DB.prototype._onError = function(err) { log.error('Db Service: error: ' + err); this.node.stop(); - process.exit(-1); }; DB.prototype._setDataPath = function() { - $.checkState(this.node.datadir, 'Node is expected to have a "datadir" property'); - if (this.node.network === Networks.livenet) { + assert(fs.existsSync(this.node.datadir), 'Node is expected to have a "datadir" property'); + if (this.node.network === 'livenet' || this.node.network === 'mainnet') { this.dataPath = this.node.datadir + '/bitcore-node.db'; - } else if (this.node.network === Networks.testnet) { - if (this.node.network.regtestEnabled) { + } else if (this.node.network === 'regtest') { this.dataPath = this.node.datadir + '/regtest/bitcore-node.db'; - } else { - this.dataPath = this.node.datadir + '/testnet3/bitcore-node.db'; - } + } else if (this.node.network === 'testnet') { + this.dataPath = this.node.datadir + '/testnet/bitcore-node.db'; } else { throw new Error('Unknown network: ' + this.network); } @@ -77,13 +71,12 @@ DB.prototype._setVersion = function(callback) { }; DB.prototype.start = function(callback) { - var self = this; - if (!fs.existsSync(self.dataPath)) { + if (!fs.existsSync(this.dataPath)) { mkdirp.sync(this.dataPath); } - self._store = levelup(self.dataPath, { db: self.levelupStore, keyEncoding: 'binary', valueEncoding: 'binary'}); + this._store = levelup(this.dataPath, { db: this.levelupStore, keyEncoding: 'binary', valueEncoding: 'binary'}); setImmediate(callback); @@ -135,7 +128,7 @@ DB.prototype.put = function(key, value, callback) { var self = this; if (self._stopping) { - return; + callback(); } self._store.put(key, value, callback); @@ -195,6 +188,7 @@ DB.prototype.close = function(callback) { if (this._store && this._store.isOpen()) { this._store.close(callback); } + setImmediate(callback); }; DB.prototype.getAPIMethods = function() { diff --git a/lib/services/mempool/index.js b/lib/services/mempool/index.js index 1d75893b..5b94cdf9 100644 --- a/lib/services/mempool/index.js +++ b/lib/services/mempool/index.js @@ -16,7 +16,7 @@ MempoolService.dependencies = ['db', 'block']; MempoolService.prototype.getAPIMethods = function() { var methods = [ - ['getMempoolTransaction', this, this.getTransaction, 1] + ['getMempoolTransaction', this, this.getMempoolTransaction, 1] ]; return methods; }; diff --git a/lib/services/transaction/index.js b/lib/services/transaction/index.js index e2dadbe2..8b9f4829 100644 --- a/lib/services/transaction/index.js +++ b/lib/services/transaction/index.js @@ -7,6 +7,7 @@ var utils = require('../../utils'); var _ = require('lodash'); var LRU = require('lru-cache'); var Unit = require('bitcore-lib').Unit; +var log = require('../../index').log; function TransactionService(options) { BaseService.call(this, options); @@ -93,7 +94,6 @@ TransactionService.prototype.sendTransaction = function(tx, callback) { TransactionService.prototype.start = function(callback) { var self = this; - self._setListeners(); self._db.getPrefix(self.name, function(err, prefix) { @@ -161,12 +161,6 @@ TransactionService.prototype.onBlock = function(block, callback) { return self._processTransaction(tx, { block: block }); }); - if (operations && operations.length > 0) { - - self._db.batch(operations); - - } - setImmediate(function() { callback(null, operations); }); @@ -175,13 +169,14 @@ TransactionService.prototype.onBlock = function(block, callback) { TransactionService.prototype._onReorg = function(commonAncestorHeader, oldBlockList) { + var self = this; // if the common ancestor block height is greater than our own, then nothing to do for the reorg - if (this._tip.height <= commonAncestorHeader.height) { + if (self._tip.height <= commonAncestorHeader.height) { return; } // set the tip to the common ancestor in case something goes wrong with the reorg - var tipOps = utils.encodeTip({ hash: commonAncestorHeader.hash, height: commonAncestorHeader.height }, this.name); + var tipOps = utils.encodeTip({ hash: commonAncestorHeader.hash, height: commonAncestorHeader.height }, self.name); var removalOps = [{ type: 'put', @@ -195,12 +190,18 @@ TransactionService.prototype._onReorg = function(commonAncestorHeader, oldBlockL var tx = block.transactions[j]; removalOps.push({ type: 'del', - key: this._encoding.encodeTransactionKey(tx.id) + key: self._encoding.encodeTransactionKey(tx.id) }); } } - this._db.batch(removalOps); + self._db.batch(removalOps, function(err) { + if (err) { + log.error('Transaction Service: error removing operations during reorg.' + err); + self.node.stop(); + return; + } + }); }; @@ -227,14 +228,6 @@ TransactionService.prototype._processTransaction = function(tx, opts) { }; -TransactionService.prototype._setListeners = function() { - - var self = this; - - self.on('reorg', self._onReorg.bind(self)); - -}; - TransactionService.prototype._startSubscriptions = function() { if (this._subscribed) { diff --git a/lib/utils.js b/lib/utils.js index 70a37c15..4aaa1683 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,36 +1,22 @@ 'use strict'; -var bitcore = require('bitcore-lib'); -var BufferUtil = bitcore.util.buffer; -var MAX_SAFE_INTEGER = 0x1fffffffffffff; // 2 ^ 53 - 1 -var crypto = require('crypto'); var _ = require('lodash'); var constants = require('./constants'); +var BN = require('bn.js'); var utils = {}; -utils.isHash = function(value) { - return typeof value === 'string' && value.length === 64 && /^[0-9a-fA-F]+$/.test(value); -}; - -utils.isSafeNatural = function(value) { - return typeof value === 'number' && - isFinite(value) && - Math.floor(value) === value && - value >= 0 && - value <= MAX_SAFE_INTEGER; -}; - -utils.startAtZero = function(obj, key) { - if (!obj.hasOwnProperty(key)) { - obj[key] = 0; - } + +utils.isHeight = function(blockArg) { + return _.isNumber(blockArg) || (blockArg.length < 40 && /^[0-9]+$/.test(blockArg)); }; +//start utils.isAbsolutePath = require('path').isAbsolute; if (!utils.isAbsolutePath) { utils.isAbsolutePath = require('path-is-absolute'); } +//main utils.parseParamsWithJSON = function(paramsArg) { var params = paramsArg.map(function(paramArg) { var param; @@ -45,9 +31,12 @@ utils.parseParamsWithJSON = function(paramsArg) { }; utils.getTerminalKey = function(startKey) { - var endKey = Buffer.from(startKey); - endKey.writeUInt8(startKey.readUInt8(startKey.length - 1) + 1, startKey.length - 1); - return endKey; + if (!startKey || !Buffer.isBuffer(startKey)) { + return; + } + var bn = new BN(startKey); + var endBN = bn.iaddn(1); + return endBN.toBuffer(); }; utils.diffTime = function(time) { @@ -55,27 +44,6 @@ utils.diffTime = function(time) { return (diff[0] * 1E9 + diff[1])/(1E9 * 1.0); }; -utils.reverseBufferToString = function(buf) { - if (_.isString(buf)) { - buf = new Buffer(buf, 'hex'); - } - return BufferUtil.reverse(buf).toString('hex'); -}; - -utils.getAddressString = function(opts) { - - if (!opts.item) { - return; - } - - // TODO: this does not handle P2PK correctly, it uses the address and not the - // pubkey - if (opts.address) { - return address.toString(); - } - -}; - utils.sendError = function(err, res) { if (err.statusCode) { res.status(err.statusCode).send(err.message); @@ -85,108 +53,6 @@ utils.sendError = function(err, res) { } }; -utils.getWalletId = exports.generateJobId = function() { - return crypto.randomBytes(16).toString('hex'); -}; - -utils.getWalletId = exports.generateJobId = function() { - return crypto.randomBytes(16).toString('hex'); -}; - -utils.toJSONL = function(obj) { - var str = JSON.stringify(obj); - str = str.replace(/\n/g, ''); - return str + '\n'; -}; - -utils.normalizeTimeStamp = function(addressArg) { - var addresses = [addressArg]; - if (Array.isArray(addressArg)) { - addresses = addressArg; - } - return addresses; -}; - -utils.normalizeTimeStamp = function(value) { - if (value > 0xffffffff) { - value = Math.round(value/1000); - } - return value; -}; - -utils.delimitedStringParse = function(delim, str) { - function tryJSONparse(str) { - try { - return JSON.parse(str); - } catch(e) { - return false; - } - } - var ret = []; - - if (delim === null) { - return tryJSONparse(str); - } - - var list = str.split(delim); - for(var i = 0; i < list.length; i++) { - ret.push(tryJSONparse(list[i])); - } - ret = _.compact(ret); - return ret.length === 0 ? false : ret; - -}; - -utils.toIntIfNumberLike = function(a) { - if (!/[^\d]+/.test(a)) { - return parseInt(a); - } - return a; -}; - -utils.getBlockInfoString = function(tip, best) { - - var diff = best - tip; - var astr = diff + ' blocks behind.'; - - if (diff === -1) { - astr = Math.abs(diff) + ' block ahead. Peer may be syncing or we may need to reorganize our chain after new blocks arrive.'; - } else if (diff < 1) { - astr = Math.abs(diff) + ' blocks ahead. Peer may be syncing or we may need to reorganize our chain after new blocks arrive.'; - } else if (diff === 1) { - astr = diff + ' block behind.'; - } - -}; -utils.joinListsOnIndex = function(indexList, list1, list2, direction) { - - var newChains = []; - - indexList.forEach(function(index) { - var otherList = list2[index]; - if (direction && direction === 'reverse') { - newChains.push(otherList.concat(list1)); - } else { - newChains.push(list1.concat(otherList)); - } - }); - - return newChains; - - -}; - -utils.removeItemsByIndexList = function(indexList, list) { - var ret = []; - for(var i = 0; i < list.length; i++) { - var item = list[i]; - if (indexList.indexOf(i) === -1) { - ret.push(item); - } - } - return ret; -}; - utils.encodeTip = function(tip, name) { var key = Buffer.concat([ constants.DB_PREFIX, new Buffer('tip-' + name, 'utf8') ]); @@ -199,10 +65,6 @@ utils.encodeTip = function(tip, name) { }; -utils.isHeight = function(blockArg) { - return _.isNumber(blockArg) || (blockArg.length < 40 && /^[0-9]+$/.test(blockArg)) -}; - utils.SimpleMap = function SimpleMap() { var object = {}; var array = []; diff --git a/test/services/db/index.unit.js b/test/services/db/index.unit.js index 37731ef7..60b7ba96 100644 --- a/test/services/db/index.unit.js +++ b/test/services/db/index.unit.js @@ -1,56 +1,154 @@ 'use strict'; -var expect = require('chai').expect; -var bitcore = require('bitcore-lib'); -var DB = require('../../../lib/services/db'); +var chai = require('chai'); +var should = chai.should(); +var assert = chai.assert; +var expect = chai.expect; +var DBService = require('../../../lib/services/db'); +var sinon = require('sinon'); +var Levelup = require('levelup'); describe('DB', function() { - describe('Reorg', function() { + var dbService; - before(function() { - this.db = new DB({ - node: { - network: bitcore.Networks.testnet, - datadir: '/tmp', - services: '' - } + var sandbox; + beforeEach(function() { + sandbox = sinon.sandbox.create(); + dbService = new DBService({ + node: { + services: [], + datadir: '/tmp', + network: 'regtest', + on: sinon.stub() + } + }); + }); + + afterEach(function() { + sandbox.restore(); + }); + + describe('#start', function() { + it('should start the db service by creating a db dir, ' + + ' if necessary, and setting the store', function(done) { + dbService.start(function() { + dbService._store.should.be.instanceOf(Levelup); + done(); + }); + }); + }); + + describe('#stop', function() { + it('should stop if store not open', function(done) { + dbService.stop(function() { + var close = sandbox.stub().callsArg(0); + dbService._store = { close: close }; + dbService._stopping.should.be.true; + done(); }); - this.db.tip = { hash: 'ff', height: 444 }; }); - it('should detect a reorg from a common ancenstor that is in our set', function() { - var block1 = { hash: '11', header: { prevHash: new Buffer('ff', 'hex') } }; - var block2 = { hash: '22', header: { prevHash: new Buffer('11', 'hex') } }; - var block3 = { hash: '33', header: { prevHash: new Buffer('22', 'hex') } }; - var block4 = { hash: '44', header: { prevHash: new Buffer('22', 'hex') } }; - //blocks must be passed in the order that they are received. - var blocks = [ block3, block2, block1, block4 ]; - expect(this.db.detectReorg(blocks)).to.deep.equal(block3); - + it('should stop if store open', function(done) { + dbService.stop(function() { + var close = sandbox.stub().callsArg(0); + dbService._store = { close: close, isOpen: sinon.stub().returns(true) }; + dbService._stopping.should.be.true; + done(); + }); }); + }); - it('should detect a reorg from a common ancenstor that is not in our set', function() { - var block1 = { hash: '11', header: { prevHash: new Buffer('ff', 'hex') } }; - var block2 = { hash: '22', header: { prevHash: new Buffer('11', 'hex') } }; - var block3 = { hash: '33', header: { prevHash: new Buffer('22', 'hex') } }; - var block4 = { hash: '44', header: { prevHash: new Buffer('ee', 'hex') } }; - var blocks = [ block3, block2, block1, block4 ]; - expect(this.db.detectReorg(blocks)).to.deep.equal(block4); - + describe('#_onError', function() { + it('should stop the db', function() { + var stop = sandbox.stub(); + dbService.node = { stop: stop }; + dbService._onError(new Error('some error')); + stop.should.be.calledOnce; }); + }); - it('should not detect a reorg', function() { - this.db.reorgTipHash = null; - var block1 = { hash: '11', header: { prevHash: new Buffer('ff', 'hex') } }; - var block2 = { hash: '22', header: { prevHash: new Buffer('11', 'hex') } }; - var block3 = { hash: '33', header: { prevHash: new Buffer('22', 'hex') } }; - var block4 = { hash: '44', header: { prevHash: new Buffer('33', 'hex') } }; - var blocks = [ block3, block2, block1, block4 ]; - var actual = this.db.detectReorg(blocks); - expect(actual).to.be.undefined; + describe('#_setDataPath', function() { + + it('should set the data path', function() { + dbService._setDataPath(); + dbService.dataPath.should.equal('/tmp/regtest/bitcore-node.db'); }); }); + + describe('#_setVersion', function() { + it('should set the version', function(done) { + var put = sandbox.stub(dbService, 'put').callsArgWith(2, null); + dbService._setVersion(function(err) { + put.should.be.calledOnce; + put.args[0][0].toString('hex').should.deep.equal('ffff76657273696f6e'); + put.args[0][1].toString('hex').should.deep.equal('00000001'); + done(); + }); + }); + }); + + describe('#get', function() { + it('should get a value from the db', function(done) { + var get = sandbox.stub().callsArgWith(2, null, 'data'); + dbService._store = { get: get }; + dbService.get('key', function(err, value) { + if (err) { + return done(err); + } + value.should.equal('data'); + done(); + }); + }); + + it('should not get a value while the node is shutting down', function(done) { + dbService._stopping = true; + dbService.get('key', function(err, value) { + err.message.should.equal('Shutdown sequence underway, not able to complete the query'); + done(); + }); + }); + + }); + + describe('#put', function() { + it('should put a value in the db', function(done) { + var put = sandbox.stub().callsArgWith(2, null); + dbService._store = { put: put }; + dbService.put(new Buffer('key'), new Buffer('value'), function(err) { + if (err) { + return done(err); + } + put.should.be.calledOnce; + done(); + }); + }); + + it('should not allow an operation while the node is shutting down', function(done) { + dbService._stopping = true; + dbService.put(new Buffer('key'), new Buffer('value'), function(err) { + done(); + }); + }); + }); + + describe('#batch', function() { + }); + + describe('#createReadStream', function() { + }); + + describe('#createKeyStream', function() { + }); + + describe('#close', function() { + }); + + describe('#getServiceTip', function() { + }); + + describe('#getPrefix', function() { + }); }); diff --git a/test/services/db/reorg.integration.js b/test/services/db/reorg.integration.js deleted file mode 100644 index 8f80a9fc..00000000 --- a/test/services/db/reorg.integration.js +++ /dev/null @@ -1,56 +0,0 @@ -'use strict'; - -var should = require('chai').should(); -var sinon = require('sinon'); -var bitcore = require('bitcore-lib'); -var BufferUtil = bitcore.util.buffer; -var DB = require('../../../lib/services/db'); -var Networks = bitcore.Networks; -var EventEmitter = require('events').EventEmitter; -var rimraf = require('rimraf'); -var mkdirp = require('mkdirp'); -var blocks = require('../../data/blocks.json'); - - -describe('DB', function() { - - var bitcoind = { - on: function(event, callback) { - }, - genesisBuffer: blocks.genesis - }; - - var node = { - network: Networks.testnet, - datadir: '/tmp/datadir', - services: { bitcoind: bitcoind }, - on: sinon.stub(), - once: sinon.stub() - }; - - before(function(done) { - - var self = this; - - rimraf(node.datadir, function(err) { - if(err) { - return done(err); - } - mkdirp(node.datadir, done); - }); - - this.db = new DB({node: node}); - this.emitter = new EventEmitter(); - - }); - - - describe('Reorg', function() { - - it('should start db service', function(done) { - this.db.start(done); - }); - - }); -}); - diff --git a/test/services/db/reorg.unit.js b/test/services/db/reorg.unit.js deleted file mode 100644 index c49891f3..00000000 --- a/test/services/db/reorg.unit.js +++ /dev/null @@ -1,135 +0,0 @@ -'use strict'; - -var should = require('chai').should(); -var sinon = require('sinon'); -var bitcore = require('bitcore-lib'); -var BufferUtil = bitcore.util.buffer; -var Reorg = require('../../../lib/services/db/reorg'); - -describe('Reorg', function() { - describe('full-test', function() { - before(function() { - sinon.stub(BufferUtil, 'reverse', function(input) { - return { - toString: function() { - return input; - } - }; - }); - }); - - after(function() { - BufferUtil.reverse.restore(); - }); - - it('should handle a reorg correctly', function(done) { - var tipBlocks = [ - {hash: 'main0', header: {prevHash: null}}, - {hash: 'main1', header: {prevHash: 'main0'}}, - {hash: 'fork1', header: {prevHash: 'main1'}}, - {hash: 'fork2', header: {prevHash: 'fork1'}} - ]; - - var concurrentBlocks = [ - {hash: 'main0', header: {prevHash: null}}, - {hash: 'main1', header: {prevHash: 'main0'}}, - {hash: 'fork1', header: {prevHash: 'main1'}}, - {hash: 'fork2', header: {prevHash: 'fork1'}}, - {hash: 'fork3', header: {prevHash: 'fork2'}} - ]; - - var bitcoindBlocks = [ - {hash: 'main0', header: {prevHash: null}}, - {hash: 'main1', header: {prevHash: 'main0'}}, - {hash: 'main2', header: {prevHash: 'main1'}}, - {hash: 'main3', header: {prevHash: 'main2'}} - ]; - - var allBlocks = tipBlocks.concat(concurrentBlocks, bitcoindBlocks); - - var db = { - tip: tipBlocks[3], - concurrentTip: concurrentBlocks[4], - store: { - batch: sinon.stub().callsArg(1) - }, - getConcurrentBlockOperations: sinon.stub().callsArgWith(2, null, []), - getSerialBlockOperations: sinon.stub().callsArgWith(2, null, []), - getConcurrentTipOperation: sinon.stub().returns(null), - getTipOperation: sinon.stub().returns(null) - }; - - var node = { - services: { - bitcoind: { - getBlock: function(hash, callback) { - var block; - for(var i = 0; i < allBlocks.length; i++) { - if(allBlocks[i].hash === hash) { - block = allBlocks[i]; - } - } - - setImmediate(function() { - if(!block) { - return callback(new Error('Block not found: ' + hash)); - } - - callback(null, block); - }); - }, - getBlockHeader: function(hash, callback) { - var header; - for(var i = 0; i < allBlocks.length; i++) { - if(allBlocks[i].hash === hash) { - header = allBlocks[i].header; - } - } - - setImmediate(function() { - if(!header) { - return callback(new Error('Block header not found: ' + hash)); - } - - callback(null, header); - }); - } - } - } - }; - - var reorg = new Reorg(node, db); - - reorg.handleReorg(bitcoindBlocks[3].hash, function(err) { - should.not.exist(err); - - db.tip.hash.should.equal('main3'); - db.concurrentTip.hash.should.equal('main3'); - - db.getConcurrentBlockOperations.callCount.should.equal(5); - db.getConcurrentBlockOperations.args[0][0].should.equal(concurrentBlocks[4]); - db.getConcurrentBlockOperations.args[0][1].should.equal(false); - db.getConcurrentBlockOperations.args[1][0].should.equal(concurrentBlocks[3]); - db.getConcurrentBlockOperations.args[1][1].should.equal(false); - db.getConcurrentBlockOperations.args[2][0].should.equal(concurrentBlocks[2]); - db.getConcurrentBlockOperations.args[2][1].should.equal(false); - db.getConcurrentBlockOperations.args[3][0].should.equal(bitcoindBlocks[2]); - db.getConcurrentBlockOperations.args[3][1].should.equal(true); - db.getConcurrentBlockOperations.args[4][0].should.equal(bitcoindBlocks[3]); - db.getConcurrentBlockOperations.args[4][1].should.equal(true); - - db.getSerialBlockOperations.callCount.should.equal(4); - db.getSerialBlockOperations.args[0][0].should.deep.equal(tipBlocks[3]); - db.getSerialBlockOperations.args[0][1].should.equal(false); - db.getSerialBlockOperations.args[1][0].should.deep.equal(tipBlocks[2]); - db.getSerialBlockOperations.args[1][1].should.equal(false); - db.getSerialBlockOperations.args[2][0].should.deep.equal(bitcoindBlocks[2]); - db.getSerialBlockOperations.args[2][1].should.equal(true); - db.getSerialBlockOperations.args[3][0].should.deep.equal(bitcoindBlocks[3]); - db.getSerialBlockOperations.args[3][1].should.equal(true); - - done(); - }); - }); - }); -});