From 5a6d8760b710e65f9f7fef10fba32dd68b08f9de Mon Sep 17 00:00:00 2001 From: Chris Kleeschulte Date: Tue, 18 Jul 2017 09:54:00 -0400 Subject: [PATCH] wip --- .gitignore | 2 +- livetest/index.js | 93 - regtest/bitcoind.js | 485 -- regtest/block.js | 231 - regtest/cluster.js | 183 - regtest/data/.gitignore | 3 - regtest/data/bitcoin.conf | 12 - regtest/data/bitcoind.crt | 14 - regtest/data/bitcoind_no_pass.key | 15 - regtest/data/node1/bitcoin.conf | 16 - regtest/data/node2/bitcoin.conf | 16 - regtest/data/node3/bitcoin.conf | 16 - regtest/db.js | 228 - regtest/node.js | 758 ---- regtest/p2p.js | 417 -- regtest/test_bus.js | 168 - regtest/test_web.js | 69 - regtest/timestamp.js | 199 - regtest/utils.js | 428 -- regtest/utxo.js | 270 -- regtest/v4/bitcoind.js | 467 -- regtest/wallet.js | 268 -- test/services/bitcoind/bitcoind.unit.js | 5036 --------------------- test/services/wallet-api/encoding.unit.js | 198 - test/services/wallet-api/utils.js | 15 - 25 files changed, 1 insertion(+), 9606 deletions(-) delete mode 100644 livetest/index.js delete mode 100644 regtest/bitcoind.js delete mode 100644 regtest/block.js delete mode 100644 regtest/cluster.js delete mode 100644 regtest/data/.gitignore delete mode 100644 regtest/data/bitcoin.conf delete mode 100644 regtest/data/bitcoind.crt delete mode 100644 regtest/data/bitcoind_no_pass.key delete mode 100644 regtest/data/node1/bitcoin.conf delete mode 100644 regtest/data/node2/bitcoin.conf delete mode 100644 regtest/data/node3/bitcoin.conf delete mode 100644 regtest/db.js delete mode 100644 regtest/node.js delete mode 100644 regtest/p2p.js delete mode 100644 regtest/test_bus.js delete mode 100644 regtest/test_web.js delete mode 100644 regtest/timestamp.js delete mode 100644 regtest/utils.js delete mode 100644 regtest/utxo.js delete mode 100644 regtest/v4/bitcoind.js delete mode 100644 regtest/wallet.js delete mode 100644 test/services/bitcoind/bitcoind.unit.js delete mode 100644 test/services/wallet-api/encoding.unit.js delete mode 100644 test/services/wallet-api/utils.js diff --git a/.gitignore b/.gitignore index 131a3220..6d32fe91 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,6 @@ bin/SHA256SUMS.asc regtest/data/node1/regtest regtest/data/node2/regtest regtest/data/node3/regtest -bitcore-node.json +bitcore-node.json* *.bak *.orig diff --git a/livetest/index.js b/livetest/index.js deleted file mode 100644 index b5af5c75..00000000 --- a/livetest/index.js +++ /dev/null @@ -1,93 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var async = require('async'); -var BitcoreNode = require('../'); -var db = require('../lib/services/db'); -var config = { - "network": "livenet", - "port": 3001, - "datadir": "/Users/chrisk/.bwdb/", - "services": [ - { - "name": "bitcoind", - "config": { - "connect": [ - { - "rpcport": 8332, - "rpcuser": "bitcoin", - "rpcpassword": "local321", - "zmqpubrawtx": "tcp://127.0.0.1:28332" - } - ] - }, - "module": require('../lib/services/bitcoind') - }, - { - "name": "db", - "config": {}, - "module": db - }, - { - "name": "transaction", - "config": {}, - "module": require('../lib/services/transaction') - }, - { - "name": "address", - "config": {}, - "module": require('../lib/services/address') - }, - { - "name": "timestamp", - "config": {}, - "module": require('../lib/services/timestamp') - } - ], - "servicesConfig": { - "bitcoind": { - "connect": [ - { - "rpcport": 8332, - "rpcuser": "bitcoin", - "rpcpassword": "local321", - "zmqpubrawtx": "tcp://127.0.0.1:28332" - } - ] - } - }, - "path": "/Users/chrisk/source/zzbitcore_node/bitcore-node.json" -} -db.prototype.sync = function(){}; -var node = new BitcoreNode.Node(config); -node.start(function(err) { - if(err) { - throw err; - } - var addresses = [ '1MfDRRVVKXUe5KNVZzu8CBzUZDHTTYZM94' ]; - async.series([function(next) { - node.services.address.getUnspentOutputs(addresses, false, function(err, results) { - if(err) { - throw err; - } - console.log(results); - next(); - }); - }, function(next) { - node.services.address.getAddressHistory(addresses, false, function(err, results) { - if(err) { - return callback(err); - } - console.log(results); - next(); - }); - }], function(err) { - node.stop(function(err) { - if(err) { - return callback(err); - } - process.exit(0); - }); - }); - -}); diff --git a/regtest/bitcoind.js b/regtest/bitcoind.js deleted file mode 100644 index 13346416..00000000 --- a/regtest/bitcoind.js +++ /dev/null @@ -1,485 +0,0 @@ -'use strict'; - -// To run the tests: $ mocha -R spec regtest/bitcoind.js - -var path = require('path'); -var index = require('..'); -var log = index.log; - -var chai = require('chai'); -var bitcore = require('bitcore-lib'); -var BN = bitcore.crypto.BN; -var async = require('async'); -var rimraf = require('rimraf'); -var bitcoind; - -/* jshint unused: false */ -var should = chai.should(); -var assert = chai.assert; -var sinon = require('sinon'); -var BitcoinRPC = require('bitcoind-rpc'); -var transactionData = []; -var blockHashes = []; -var utxos; -var client; -var coinbasePrivateKey; -var privateKey = bitcore.PrivateKey(); -var destKey = bitcore.PrivateKey(); - -describe('Bitcoind Functionality', function() { - - before(function(done) { - this.timeout(60000); - - // Add the regtest network - bitcore.Networks.enableRegtest(); - var regtestNetwork = bitcore.Networks.get('regtest'); - - var datadir = __dirname + '/data'; - - rimraf(datadir + '/regtest', function(err) { - - if (err) { - throw err; - } - - bitcoind = require('../').services.Bitcoin({ - spawn: { - datadir: datadir, - exec: path.resolve(__dirname, '../bin/bitcoind') - }, - node: { - network: regtestNetwork, - getNetworkName: function() { - return 'regtest'; - } - } - }); - - bitcoind.on('error', function(err) { - log.error('error="%s"', err.message); - }); - - log.info('Waiting for Bitcoin Core to initialize...'); - - bitcoind.start(function() { - log.info('Bitcoind started'); - - client = new BitcoinRPC({ - protocol: 'http', - host: '127.0.0.1', - port: 30331, - user: 'bitcoin', - pass: 'local321', - rejectUnauthorized: false - }); - - log.info('Generating 100 blocks...'); - - // Generate enough blocks so that the initial coinbase transactions - // can be spent. - - setImmediate(function() { - client.generate(150, function(err, response) { - if (err) { - throw err; - } - blockHashes = response.result; - - log.info('Preparing test data...'); - - // Get all of the unspent outputs - client.listUnspent(0, 150, function(err, response) { - utxos = response.result; - - async.mapSeries(utxos, function(utxo, next) { - async.series([ - function(finished) { - // Load all of the transactions for later testing - client.getTransaction(utxo.txid, function(err, txresponse) { - if (err) { - throw err; - } - // add to the list of transactions for testing later - transactionData.push(txresponse.result.hex); - finished(); - }); - }, - function(finished) { - // Get the private key for each utxo - client.dumpPrivKey(utxo.address, function(err, privresponse) { - if (err) { - throw err; - } - utxo.privateKeyWIF = privresponse.result; - finished(); - }); - } - ], next); - }, function(err) { - if (err) { - throw err; - } - done(); - }); - }); - }); - }); - }); - }); - }); - - after(function(done) { - this.timeout(60000); - bitcoind.node.stopping = true; - bitcoind.stop(function(err, result) { - done(); - }); - }); - - describe('get blocks by hash', function() { - - [0,1,2,3,5,6,7,8,9].forEach(function(i) { - it('generated block ' + i, function(done) { - bitcoind.getBlock(blockHashes[i], function(err, block) { - if (err) { - throw err; - } - should.exist(block); - block.hash.should.equal(blockHashes[i]); - done(); - }); - }); - }); - }); - - describe('get blocks as buffers', function() { - [0,1,2,3,5,6,7,8,9].forEach(function(i) { - it('generated block ' + i, function(done) { - bitcoind.getRawBlock(blockHashes[i], function(err, block) { - if (err) { - throw err; - } - should.exist(block); - (block instanceof Buffer).should.equal(true); - done(); - }); - }); - }); - }); - - describe('get errors as error instances', function() { - it('will wrap an rpc into a javascript error', function(done) { - bitcoind.client.getBlock(1000000000, function(err, response) { - var error = bitcoind._wrapRPCError(err); - (error instanceof Error).should.equal(true); - error.message.should.equal(err.message); - error.code.should.equal(err.code); - should.exist(error.stack); - done(); - }); - }); - }); - - describe('get blocks by height', function() { - - [0,1,2,3,4,5,6,7,8,9].forEach(function(i) { - it('generated block ' + i, function(done) { - // add the genesis block - var height = i + 1; - bitcoind.getBlock(i + 1, function(err, block) { - if (err) { - throw err; - } - should.exist(block); - block.hash.should.equal(blockHashes[i]); - done(); - }); - }); - }); - - it('will get error with number greater than tip', function(done) { - bitcoind.getBlock(1000000000, function(err, response) { - should.exist(err); - err.code.should.equal(-8); - done(); - }); - }); - - }); - - describe('get transactions by hash', function() { - [0,1,2,3,4,5,6,7,8,9].forEach(function(i) { - it('for tx ' + i, function(done) { - var txhex = transactionData[i]; - var tx = new bitcore.Transaction(); - tx.fromString(txhex); - bitcoind.getTransaction(tx.hash, function(err, response) { - if (err) { - throw err; - } - assert(response.toString('hex') === txhex, 'incorrect tx data result'); - done(); - }); - }); - }); - - it('will return error if the transaction does not exist', function(done) { - var txid = '6226c407d0e9705bdd7158e60983e37d0f5d23529086d6672b07d9238d5aa618'; - bitcoind.getTransaction(txid, function(err, response) { - should.exist(err); - done(); - }); - }); - }); - - describe('get transactions as buffers', function() { - [0,1,2,3,4,5,6,7,8,9].forEach(function(i) { - it('for tx ' + i, function(done) { - var txhex = transactionData[i]; - var tx = new bitcore.Transaction(); - tx.fromString(txhex); - bitcoind.getRawTransaction(tx.hash, function(err, response) { - if (err) { - throw err; - } - response.should.be.instanceOf(Buffer); - assert(response.toString('hex') === txhex, 'incorrect tx data result'); - done(); - }); - }); - }); - - it('will return error if the transaction does not exist', function(done) { - var txid = '6226c407d0e9705bdd7158e60983e37d0f5d23529086d6672b07d9238d5aa618'; - bitcoind.getRawTransaction(txid, function(err, response) { - should.exist(err); - done(); - }); - }); - }); - - describe('get block header', function() { - var expectedWork = new BN(6); - [1,2,3,4,5,6,7,8,9].forEach(function(i) { - it('generate block ' + i, function(done) { - bitcoind.getBlockHeader(blockHashes[i], function(err, blockIndex) { - if (err) { - return done(err); - } - should.exist(blockIndex); - should.exist(blockIndex.chainWork); - var work = new BN(blockIndex.chainWork, 'hex'); - work.toString(16).should.equal(expectedWork.toString(16)); - expectedWork = expectedWork.add(new BN(2)); - should.exist(blockIndex.prevHash); - blockIndex.hash.should.equal(blockHashes[i]); - blockIndex.prevHash.should.equal(blockHashes[i - 1]); - blockIndex.height.should.equal(i + 1); - done(); - }); - }); - }); - it('will get null prevHash for the genesis block', function(done) { - bitcoind.getBlockHeader(0, function(err, header) { - if (err) { - return done(err); - } - should.exist(header); - should.equal(header.prevHash, undefined); - done(); - }); - }); - it('will get error for block not found', function(done) { - bitcoind.getBlockHeader('notahash', function(err, header) { - should.exist(err); - done(); - }); - }); - }); - - describe('get block index by height', function() { - var expectedWork = new BN(6); - [2,3,4,5,6,7,8,9].forEach(function(i) { - it('generate block ' + i, function() { - bitcoind.getBlockHeader(i, function(err, header) { - should.exist(header); - should.exist(header.chainWork); - var work = new BN(header.chainWork, 'hex'); - work.toString(16).should.equal(expectedWork.toString(16)); - expectedWork = expectedWork.add(new BN(2)); - should.exist(header.prevHash); - header.hash.should.equal(blockHashes[i - 1]); - header.prevHash.should.equal(blockHashes[i - 2]); - header.height.should.equal(i); - }); - }); - }); - it('will get error with number greater than tip', function(done) { - bitcoind.getBlockHeader(100000, function(err, header) { - should.exist(err); - done(); - }); - }); - }); - - describe('send transaction functionality', function() { - - it('will not error and return the transaction hash', function(done) { - - // create and sign the transaction - var tx = bitcore.Transaction(); - tx.from(utxos[0]); - tx.change(privateKey.toAddress()); - tx.to(destKey.toAddress(), utxos[0].amount * 1e8 - 1000); - tx.sign(bitcore.PrivateKey.fromWIF(utxos[0].privateKeyWIF)); - - // test sending the transaction - bitcoind.sendTransaction(tx.serialize(), function(err, hash) { - if (err) { - return done(err); - } - hash.should.equal(tx.hash); - done(); - }); - - }); - - it('will throw an error if an unsigned transaction is sent', function(done) { - var tx = bitcore.Transaction(); - tx.from(utxos[1]); - tx.change(privateKey.toAddress()); - tx.to(destKey.toAddress(), utxos[1].amount * 1e8 - 1000); - bitcoind.sendTransaction(tx.uncheckedSerialize(), function(err, hash) { - should.exist(err); - (err instanceof Error).should.equal(true); - should.not.exist(hash); - done(); - }); - }); - - it('will throw an error for unexpected types (tx decode failed)', function(done) { - var garbage = new Buffer('abcdef', 'hex'); - bitcoind.sendTransaction(garbage, function(err, hash) { - should.exist(err); - should.not.exist(hash); - var num = 23; - bitcoind.sendTransaction(num, function(err, hash) { - should.exist(err); - (err instanceof Error).should.equal(true); - should.not.exist(hash); - done(); - }); - }); - }); - - it('will emit "tx" events', function(done) { - var tx = bitcore.Transaction(); - tx.from(utxos[2]); - tx.change(privateKey.toAddress()); - tx.to(destKey.toAddress(), utxos[2].amount * 1e8 - 1000); - tx.sign(bitcore.PrivateKey.fromWIF(utxos[2].privateKeyWIF)); - - var serialized = tx.serialize(); - - bitcoind.once('tx', function(buffer) { - buffer.toString('hex').should.equal(serialized); - done(); - }); - bitcoind.sendTransaction(serialized, function(err, hash) { - if (err) { - return done(err); - } - should.exist(hash); - }); - }); - - }); - - describe('fee estimation', function() { - it('will estimate fees', function(done) { - bitcoind.estimateFee(1, function(err, fees) { - if (err) { - return done(err); - } - fees.should.equal(-1); - done(); - }); - }); - }); - - describe('tip updates', function() { - it('will get an event when the tip is new', function(done) { - this.timeout(4000); - bitcoind.on('tip', function(height) { - if (height === 151) { - done(); - } - }); - client.generate(1, function(err, response) { - if (err) { - throw err; - } - }); - }); - }); - - describe('get detailed transaction', function() { - it('should include details for coinbase tx', function(done) { - bitcoind.getDetailedTransaction(utxos[0].txid, function(err, tx) { - if (err) { - return done(err); - } - should.exist(tx.height); - tx.height.should.be.a('number'); - should.exist(tx.blockTimestamp); - should.exist(tx.blockHash); - tx.coinbase.should.equal(true); - tx.version.should.equal(1); - tx.hex.should.be.a('string'); - tx.locktime.should.equal(0); - tx.feeSatoshis.should.equal(0); - tx.outputSatoshis.should.equal(50 * 1e8); - tx.inputSatoshis.should.equal(0); - tx.inputs.length.should.equal(1); - tx.outputs.length.should.equal(1); - should.equal(tx.inputs[0].prevTxId, null); - should.equal(tx.inputs[0].outputIndex, null); - tx.inputs[0].script.should.be.a('string'); - should.equal(tx.inputs[0].scriptAsm, null); - should.equal(tx.inputs[0].address, null); - should.equal(tx.inputs[0].satoshis, null); - tx.outputs[0].satoshis.should.equal(50 * 1e8); - tx.outputs[0].script.should.be.a('string'); - tx.outputs[0].scriptAsm.should.be.a('string'); - tx.outputs[0].spentTxId.should.be.a('string'); - tx.outputs[0].spentIndex.should.equal(0); - tx.outputs[0].spentHeight.should.be.a('number'); - tx.outputs[0].address.should.be.a('string'); - done(); - }); - }); - }); - - describe('#getInfo', function() { - it('will get information', function(done) { - bitcoind.getInfo(function(err, info) { - if (err) { - return done(err); - } - info.network.should.equal('regtest'); - should.exist(info); - should.exist(info.version); - should.exist(info.blocks); - should.exist(info.timeOffset); - should.exist(info.connections); - should.exist(info.difficulty); - should.exist(info.testnet); - should.exist(info.relayFee); - should.exist(info.errors); - done(); - }); - }); - }); - -}); diff --git a/regtest/block.js b/regtest/block.js deleted file mode 100644 index 801b50fe..00000000 --- a/regtest/block.js +++ /dev/null @@ -1,231 +0,0 @@ -'use strict'; - -var chai = require('chai'); -var expect = chai.expect; -var async = require('async'); -var BitcoinRPC = require('bitcoind-rpc'); -var path = require('path'); -var Utils = require('./utils'); -var constants = require('../lib/constants'); -var zmq = require('zmq'); - -var debug = true; -var extraDebug = true; -var bitcoreDataDir = '/tmp/testtmpfs/bitcore'; -var bitcoinDataDir = '/tmp/testtmpfs/bitcoin'; - -var rpcConfig = { - protocol: 'http', - user: 'bitcoin', - pass: 'local321', - host: '127.0.0.1', - port: '58332', - rejectUnauthorized: false -}; - -var bitcoin = { - args: { - datadir: bitcoinDataDir, - listen: 1, - regtest: 1, - server: 1, - rpcuser: rpcConfig.user, - rpcpassword: rpcConfig.pass, - rpcport: rpcConfig.port - }, - datadir: bitcoinDataDir, - exec: 'bitcoind', //if this isn't on your PATH, then provide the absolute path, e.g. /usr/local/bin/bitcoind - process: null -}; - -var bitcore = { - configFile: { - file: bitcoreDataDir + '/bitcore-node.json', - conf: { - network: 'regtest', - port: 53001, - datadir: bitcoreDataDir, - services: [ - 'p2p', - 'db', - 'web', - 'block', - 'timestamp', - 'block-test' - ], - servicesConfig: { - p2p: { - peers: [ - { - ip: { v4: '127.0.0.1' } - } - ] - }, - 'block-test': { - requirePath: path.resolve(__dirname + '/test_bus.js') - } - } - } - }, - httpOpts: { - protocol: 'http:', - hostname: 'localhost', - port: 53001, - }, - opts: { cwd: bitcoreDataDir }, - datadir: bitcoreDataDir, - exec: path.resolve(__dirname, '../bin/bitcore-node'), - args: ['start'], - process: null -}; - -var opts = { - debug: debug, - bitcore: bitcore, - bitcoin: bitcoin, - bitcoinDataDir: bitcoinDataDir, - bitcoreDataDir: bitcoreDataDir, - rpc: new BitcoinRPC(rpcConfig), - blockHeight: 0, - initialHeight: 150, - path: '/test/info', - errorFilter: function(req, res) { - try { - var info = JSON.parse(res); - if (info.result) { - return; - } - } catch(e) { - return e; - } - } -}; - -var utils = new Utils(opts); - -var subSocket; -var blocks = []; - -function processMessages(topic, message) { - var topicStr = topic.toString(); - if (topicStr === 'block/block') { - return blocks.push(message); - } -} - -function setupZmqSubscriber(callback) { - - subSocket = zmq.socket('sub'); - subSocket.on('connect', function(fd, endPoint) { - if (debug) { - console.log('ZMQ connected to:', endPoint); - } - }); - - subSocket.on('disconnect', function(fd, endPoint) { - if (debug) { - console.log('ZMQ disconnect:', endPoint); - } - }); - - subSocket.monitor(100, 0); - subSocket.connect('tcp://127.0.0.1:38332'); - subSocket.subscribe('block'); - subSocket.on('message', processMessages); - callback(); -} - -describe('Block Operations', function() { - - this.timeout(60000); - - describe('Sync Block Headers', function() { - - var self = this; - - after(function(done) { - utils.cleanup(done); - }); - - before(function(done) { - async.series([ - utils.startBitcoind.bind(utils), - utils.waitForBitcoinReady.bind(utils), - utils.startBitcoreNode.bind(utils), - utils.waitForBitcoreNode.bind(utils), - setupZmqSubscriber - ], done); - }); - - it.only('should be able to get historical blocks from the network', function(done) { - var filter = { startHash: constants.BITCOIN_GENESIS_HASH.regtest }; - utils.queryBitcoreNode(Object.assign({ - path: '/test/p2p/blocks?filter=' + JSON.stringify(filter), - }, bitcore.httpOpts), function(err) { - - if(err) { - return done(err); - } - - setTimeout(function() { - expect(blocks.length).to.equal(150); - done(); - }, 2000); - - - }); - }); - - it('should sync block hashes as keys and heights as values', function(done) { - - //async.timesLimit(opts.initialHeight, 12, function(n, next) { - // utils.queryBitcoreNode(Object.assign({ - // path: '/test/block/hash/' + n - // }, bitcore.httpOpts), function(err, res) { - - // if(err) { - // return done(err); - // } - // res = JSON.parse(res); - // expect(res.height).to.equal(n); - // expect(res.hash.length).to.equal(64); - // next(null, res.hash); - // }); - //}, function(err, hashes) { - - // if(err) { - // return done(err); - // } - // self.hashes = hashes; - // done(); - - //}); - }); - - it('should sync block heights as keys and hashes as values', function(done) { - async.timesLimit(opts.initialHeight, 12, function(n, next) { - utils.queryBitcoreNode(Object.assign({ - path: '/test/block/height/' + self.hashes[n] - }, bitcore.httpOpts), function(err, res) { - - if(err) { - return done(err); - } - res = JSON.parse(res); - expect(res.height).to.equal(n); - expect(res.hash).to.equal(self.hashes[n]); - next(); - }); - }, function(err) { - - if(err) { - return done(err); - } - done(); - - }); - - }); - }); - -}); diff --git a/regtest/cluster.js b/regtest/cluster.js deleted file mode 100644 index c8ab8754..00000000 --- a/regtest/cluster.js +++ /dev/null @@ -1,183 +0,0 @@ -'use strict'; - -var path = require('path'); -var async = require('async'); -var spawn = require('child_process').spawn; - -var BitcoinRPC = require('bitcoind-rpc'); -var rimraf = require('rimraf'); -var bitcore = require('bitcore-lib'); -var chai = require('chai'); -var should = chai.should(); - -var index = require('..'); -var log = index.log; -log.debug = function() {}; -var BitcoreNode = index.Node; -var BitcoinService = index.services.Bitcoin; - -describe('Bitcoin Cluster', function() { - var node; - var daemons = []; - var execPath = path.resolve(__dirname, '../bin/bitcoind'); - var nodesConf = [ - { - datadir: path.resolve(__dirname, './data/node1'), - conf: path.resolve(__dirname, './data/node1/bitcoin.conf'), - rpcuser: 'bitcoin', - rpcpassword: 'local321', - rpcport: 30521, - zmqpubrawtx: 'tcp://127.0.0.1:30611', - zmqpubhashblock: 'tcp://127.0.0.1:30611' - }, - { - datadir: path.resolve(__dirname, './data/node2'), - conf: path.resolve(__dirname, './data/node2/bitcoin.conf'), - rpcuser: 'bitcoin', - rpcpassword: 'local321', - rpcport: 30522, - zmqpubrawtx: 'tcp://127.0.0.1:30622', - zmqpubhashblock: 'tcp://127.0.0.1:30622' - }, - { - datadir: path.resolve(__dirname, './data/node3'), - conf: path.resolve(__dirname, './data/node3/bitcoin.conf'), - rpcuser: 'bitcoin', - rpcpassword: 'local321', - rpcport: 30523, - zmqpubrawtx: 'tcp://127.0.0.1:30633', - zmqpubhashblock: 'tcp://127.0.0.1:30633' - } - ]; - - before(function(done) { - log.info('Starting 3 bitcoind daemons'); - this.timeout(60000); - async.each(nodesConf, function(nodeConf, next) { - var opts = [ - '--regtest', - '--datadir=' + nodeConf.datadir, - '--conf=' + nodeConf.conf - ]; - - rimraf(path.resolve(nodeConf.datadir, './regtest'), function(err) { - if (err) { - return done(err); - } - - var process = spawn(execPath, opts, {stdio: 'inherit'}); - - var client = new BitcoinRPC({ - protocol: 'http', - host: '127.0.0.1', - port: nodeConf.rpcport, - user: nodeConf.rpcuser, - pass: nodeConf.rpcpassword - }); - - daemons.push(process); - - async.retry({times: 10, interval: 5000}, function(ready) { - client.getInfo(ready); - }, next); - - }); - - }, done); - }); - - after(function(done) { - this.timeout(10000); - setTimeout(function() { - async.each(daemons, function(process, next) { - process.once('exit', next); - process.kill('SIGINT'); - }, done); - }, 1000); - }); - - it('step 1: will connect to three bitcoind daemons', function(done) { - this.timeout(20000); - var configuration = { - network: 'regtest', - services: [ - { - name: 'bitcoind', - module: BitcoinService, - config: { - connect: [ - { - rpchost: '127.0.0.1', - rpcport: 30521, - rpcuser: 'bitcoin', - rpcpassword: 'local321', - zmqpubrawtx: 'tcp://127.0.0.1:30611' - }, - { - rpchost: '127.0.0.1', - rpcport: 30522, - rpcuser: 'bitcoin', - rpcpassword: 'local321', - zmqpubrawtx: 'tcp://127.0.0.1:30622' - }, - { - rpchost: '127.0.0.1', - rpcport: 30523, - rpcuser: 'bitcoin', - rpcpassword: 'local321', - zmqpubrawtx: 'tcp://127.0.0.1:30633' - } - ] - } - } - ] - }; - - var regtest = bitcore.Networks.get('regtest'); - should.exist(regtest); - - node = new BitcoreNode(configuration); - - node.on('error', function(err) { - log.error(err); - }); - - node.on('ready', function() { - done(); - }); - - node.start(function(err) { - if (err) { - return done(err); - } - }); - - }); - - it('step 2: receive block events', function(done) { - this.timeout(10000); - node.services.bitcoind.once('tip', function(height) { - height.should.equal(1); - done(); - }); - node.generateBlock(1, function(err, hashes) { - if (err) { - return done(err); - } - should.exist(hashes); - }); - }); - - it('step 3: get blocks', function(done) { - async.times(3, function(n, next) { - node.getBlock(1, function(err, block) { - if (err) { - return next(err); - } - should.exist(block); - next(); - }); - }, done); - }); - -}); diff --git a/regtest/data/.gitignore b/regtest/data/.gitignore deleted file mode 100644 index df8377f9..00000000 --- a/regtest/data/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -.lock -blocks -regtest diff --git a/regtest/data/bitcoin.conf b/regtest/data/bitcoin.conf deleted file mode 100644 index 95cf0afb..00000000 --- a/regtest/data/bitcoin.conf +++ /dev/null @@ -1,12 +0,0 @@ -server=1 -whitelist=127.0.0.1 -txindex=1 -addressindex=1 -timestampindex=1 -spentindex=1 -zmqpubrawtx=tcp://127.0.0.1:30332 -zmqpubhashblock=tcp://127.0.0.1:30332 -rpcallowip=127.0.0.1 -rpcport=30331 -rpcuser=bitcoin -rpcpassword=local321 diff --git a/regtest/data/bitcoind.crt b/regtest/data/bitcoind.crt deleted file mode 100644 index 280edc50..00000000 --- a/regtest/data/bitcoind.crt +++ /dev/null @@ -1,14 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICDTCCAXYCCQCsGf/7CM97gDANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJV -UzELMAkGA1UECBMCR0ExDDAKBgNVBAcTA2ZvbzEhMB8GA1UEChMYSW50ZXJuZXQg -V2lkZ2l0cyBQdHkgTHRkMB4XDTE1MDgyNjE3NTAwOFoXDTE1MDkyNTE3NTAwOFow -SzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkdBMQwwCgYDVQQHEwNmb28xITAfBgNV -BAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCBnzANBgkqhkiG9w0BAQEFAAOB -jQAwgYkCgYEA7SXnPpKk+0qXTTa42jzvu/C/gdGby0arY50bE2A+epI7FlI5YqKd -hQWoNRpuehF3jH6Ij3mbeeImtTA7TaUYlgHKpn63xfJ0cRj55+6vqq09nDxf0Lm9 -IpTbgllu1l+SHtSuzFBVtGuNRSqObf8gD5XCD5lWK1vXHQ6PFSnAakMCAwEAATAN -BgkqhkiG9w0BAQUFAAOBgQBNARLDgsw7NCBVkn57AEgwZptxeyvFWlGZCd0BmYIX -ZFk7T1OQDwn7GlHry2IBswI0QRi076RQ4oJq+fg2O3XdFvEYV0cyypW7AxrnYTHP -m1h2xr6Y5vhxFKP8DxpAxST27DHbR18YvTD+IGtp2UjLj646587N0MWxt8vmaU3c -og== ------END CERTIFICATE----- diff --git a/regtest/data/bitcoind_no_pass.key b/regtest/data/bitcoind_no_pass.key deleted file mode 100644 index 353f5af2..00000000 --- a/regtest/data/bitcoind_no_pass.key +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIICXwIBAAKBgQDtJec+kqT7SpdNNrjaPO+78L+B0ZvLRqtjnRsTYD56kjsWUjli -op2FBag1Gm56EXeMfoiPeZt54ia1MDtNpRiWAcqmfrfF8nRxGPnn7q+qrT2cPF/Q -ub0ilNuCWW7WX5Ie1K7MUFW0a41FKo5t/yAPlcIPmVYrW9cdDo8VKcBqQwIDAQAB -AoGBAOzYFxyCPu2OMI/4ICQuCcwtBEa2Ph+Fo/Rn2ru+OogV9Zc0ZYWiHSnWXYkz -rbSSL1CMqvyIGoRfHgOFeSTxxxtVyRo5LayI6Ce6V04yUFua16uo6hMX5bfGKZ9d -uq/HCDmdQvgifxUFpTpoSencuxwVCSYstMqjGfpobc5nxN2RAkEA+23BWNyA2qcq -yqSplgTQO0laXox9ksr1k2mJB2HtG3GNs1kapP+Z8AVtn/Mf9Va0hgbSnqpF9fll -1xpBqfidSQJBAPF1rizAm7xP7AeRRET36YKjZCVayA/g4rp7kecrNJTTupJIuxHr -JUlOTtXEWjIVcutSM7bP7bytPv4SAaApBysCQQDEZhqnCC+bHQO/IVrbNc1W0ljG -DGY22VV1HfYND0CAtHXkx9CZXJPpusPEMs0e/uiq3P9/MzDNEFCt8vOiCvMJAkEA -65oDIKGzk/R7/wpMjetEyva5AgXpjizFrmZigCjVPp61voT/G8XQ9Q1WuRjFVXc+ -UcU8tpV+iIqXG3vgYDGITwJBANb0NFFF8QsygbENtad1tw1C/hNabHk8n9hu+Z8+ -OSzEMlP7SsvddPaqusGydhTUxazoG3s4kEh5WmCWKgZGKO0= ------END RSA PRIVATE KEY----- diff --git a/regtest/data/node1/bitcoin.conf b/regtest/data/node1/bitcoin.conf deleted file mode 100644 index 54ed2aec..00000000 --- a/regtest/data/node1/bitcoin.conf +++ /dev/null @@ -1,16 +0,0 @@ -server=1 -whitelist=127.0.0.1 -txindex=1 -addressindex=1 -timestampindex=1 -spentindex=1 -addnode=127.0.0.1:30432 -addnode=127.0.0.1:30433 -port=30431 -rpcport=30521 -zmqpubrawtx=tcp://127.0.0.1:30611 -zmqpubhashblock=tcp://127.0.0.1:30611 -rpcallowip=127.0.0.1 -rpcuser=bitcoin -rpcpassword=local321 -keypool=3 diff --git a/regtest/data/node2/bitcoin.conf b/regtest/data/node2/bitcoin.conf deleted file mode 100644 index bcd09fe6..00000000 --- a/regtest/data/node2/bitcoin.conf +++ /dev/null @@ -1,16 +0,0 @@ -server=1 -whitelist=127.0.0.1 -txindex=1 -addressindex=1 -timestampindex=1 -spentindex=1 -addnode=127.0.0.1:30431 -addnode=127.0.0.1:30433 -port=30432 -rpcport=30522 -zmqpubrawtx=tcp://127.0.0.1:30622 -zmqpubhashblock=tcp://127.0.0.1:30622 -rpcallowip=127.0.0.1 -rpcuser=bitcoin -rpcpassword=local321 -keypool=3 diff --git a/regtest/data/node3/bitcoin.conf b/regtest/data/node3/bitcoin.conf deleted file mode 100644 index 8be13ef3..00000000 --- a/regtest/data/node3/bitcoin.conf +++ /dev/null @@ -1,16 +0,0 @@ -server=1 -whitelist=127.0.0.1 -txindex=1 -addressindex=1 -timestampindex=1 -spentindex=1 -addnode=127.0.0.1:30431 -addnode=127.0.0.1:30432 -port=30433 -rpcport=30523 -zmqpubrawtx=tcp://127.0.0.1:30633 -zmqpubhashblock=tcp://127.0.0.1:30633 -rpcallowip=127.0.0.1 -rpcuser=bitcoin -rpcpassword=local321 -keypool=3 diff --git a/regtest/db.js b/regtest/db.js deleted file mode 100644 index f10b3157..00000000 --- a/regtest/db.js +++ /dev/null @@ -1,228 +0,0 @@ -'use strict'; - -var chai = require('chai'); -var expect = chai.expect; -var async = require('async'); -var path = require('path'); -var Utils = require('./utils'); -var zmq = require('zmq'); -var http = require('http'); -var blocks = require('../test/data/blocks.json'); -var bitcore = require('bitcore-lib'); -var Block = bitcore.Block; -var BufferUtil = bitcore.util.buffer; - -/* - Bitcoind does not need to be started or run -*/ - -var debug = true; -var bitcoreDataDir = '/tmp/bitcore'; -var pubSocket; -var rpcServer; - -function setupFakeRpcServer() { - rpcServer = http.createServer(); - rpcServer.listen(48332, '127.0.0.1'); -} - -function setupFakeZmq() { - pubSocket = zmq.socket('pub'); - pubSocket.bind('tcp://127.0.0.1:38332'); -} - -var bitcore = { - configFile: { - file: bitcoreDataDir + '/bitcore-node.json', - conf: { - network: 'regtest', - port: 53001, - datadir: bitcoreDataDir, - services: [ - 'bitcoind', - 'db', - 'web', - 'block', - 'reorg-test', - 'timestamp' - ], - servicesConfig: { - bitcoind: { - connect: [ - { - rpcconnect: '127.0.0.1', - rpcport: 48332, - rpcuser: 'bitcoin', - rpcpassword: 'local321', - zmqpubrawtx: 'tcp://127.0.0.1:38332' - } - ] - }, - 'reorg-test': { requirePath: path.resolve(__dirname + '/test_web.js') } - } - } - }, - httpOpts: { - protocol: 'http:', - hostname: 'localhost', - port: 53001, - }, - opts: { cwd: bitcoreDataDir }, - datadir: bitcoreDataDir, - exec: path.resolve(__dirname, '../bin/bitcore-node'), - args: ['start'], - process: null -}; - -var opts = { - debug: debug, - bitcore: bitcore, - bitcoreDataDir: bitcoreDataDir, - blockHeight: 0 -}; -var utils = new Utils(opts); - -var genesis = new Block(new Buffer(blocks.genesis, 'hex')); -var block1 = new Block(new Buffer(blocks.block1a, 'hex')); -var block2 = new Block(new Buffer(blocks.block1b, 'hex')); -var rawGenesis = blocks.genesis; -var rawBlock1 = blocks.block1a; -var rawBlock2 = blocks.block1b; -var genesisHash = genesis.hash; -var genesisHeader = { - height: 0, - hash: genesis.hash, - previousblockhash: new Array(65).join('0') -}; -var block1Header = { - height: 1, - hash: block1.header.hash, - previousblockhash: BufferUtil.reverse(block1.header.prevHash).toString('hex') -}; -var block2Header = { - height: 1, - hash: block2.header.hash, - previousblockhash: BufferUtil.reverse(block2.header.prevHash).toString('hex') -}; - - -function publishBlockHash(rawBlockHex, callback) { - - pubSocket.send([ 'rawblock', new Buffer(rawBlockHex, 'hex') ]); - - var httpOpts = utils.getHttpOpts({ path: '/info' }); - - // we don't know exactly when all the blockhandlers will complete after the "tip" event - // so we must wait an indeterminate time to check on the current tip - setTimeout(function() { - - utils.queryBitcoreNode(httpOpts, function(err, res) { - - if(err) { - return callback(err); - } - - var block = Block.fromString(rawBlockHex); - expect(block.hash).equal(JSON.parse(res).dbhash); - callback(); - - }); - - }, 2000); -} - -describe('DB Operations', function() { - - this.timeout(60000); - - describe('DB Reorg', function() { - - var self = this; - - var responses = [ - genesisHash, - genesisHeader, - rawGenesis, - block1Header, - block2Header - ]; - - after(function(done) { - pubSocket.close(); - rpcServer.close(); - bitcore.process.kill(); - setTimeout(done, 1000); - }); - - - before(function(done) { - - var responseCount = 0; - - setupFakeRpcServer(); - - rpcServer.on('request', function(req, res) { - var data = ''; - - req.on('data', function(chunk) { - data += chunk.toString(); - }); - - req.on('end', function() { - var body = JSON.parse(data); - if (debug) { - console.log('request', body); - } - var response = JSON.stringify({ result: responses[responseCount++], count: responseCount }); - if (debug) { - console.log('response', response, 'id: ', body.id); - } - res.write(response); - res.end(); - }); - - }); - - setupFakeZmq(); - - utils.startBitcoreNode(function() { - utils.waitForBitcoreNode(done); - }); - - }); - - it('should reorg when needed', function(done) { - - /* - _______________________________________________________ - | | | | | - | Genesis | Block 1a | Block 1b | Result | - | _______ ________ | - | | |_____| |___________________ORPHANED | - | |_______| |________| | - | | ________ ________ | - | |______________________| |____| | | - | |________| |________| | - |_______________________________________________________| - - */ - - async.series([ - - publishBlockHash.bind(self, rawBlock1), - publishBlockHash.bind(self, rawBlock2), - function(next) { - utils.opts.blockHeight++; - next(); - }, - utils.waitForBitcoreNode.bind(utils) - - ], done); - - }); - }); - -}); - - - diff --git a/regtest/node.js b/regtest/node.js deleted file mode 100644 index 2e8539ab..00000000 --- a/regtest/node.js +++ /dev/null @@ -1,758 +0,0 @@ -'use strict'; - -// To run the tests: $ mocha -R spec regtest/node.js - -var path = require('path'); -var index = require('..'); -var async = require('async'); -var log = index.log; -log.debug = function() {}; - -var chai = require('chai'); -var bitcore = require('bitcore-lib'); -var rimraf = require('rimraf'); -var node; - -var should = chai.should(); - -var BitcoinRPC = require('bitcoind-rpc'); -var index = require('..'); -var Transaction = bitcore.Transaction; -var BitcoreNode = index.Node; -var BitcoinService = index.services.Bitcoin; -var testWIF = 'cSdkPxkAjA4HDr5VHgsebAPDEh9Gyub4HK8UJr2DFGGqKKy4K5sG'; -var testKey; -var client; - -var outputForIsSpentTest1; -var unspentOutputSpentTxId; - -describe('Node Functionality', function() { - - var regtest; - - before(function(done) { - this.timeout(20000); - - var datadir = __dirname + '/data'; - - testKey = bitcore.PrivateKey(testWIF); - - rimraf(datadir + '/regtest', function(err) { - - if (err) { - throw err; - } - - var configuration = { - network: 'regtest', - services: [ - { - name: 'bitcoind', - module: BitcoinService, - config: { - spawn: { - datadir: datadir, - exec: path.resolve(__dirname, '../bin/bitcoind') - } - } - } - ] - }; - - node = new BitcoreNode(configuration); - - regtest = bitcore.Networks.get('regtest'); - should.exist(regtest); - - node.on('error', function(err) { - log.error(err); - }); - - node.start(function(err) { - if (err) { - return done(err); - } - - client = new BitcoinRPC({ - protocol: 'http', - host: '127.0.0.1', - port: 30331, - user: 'bitcoin', - pass: 'local321', - rejectUnauthorized: false - }); - - var syncedHandler = function() { - if (node.services.bitcoind.height === 150) { - node.services.bitcoind.removeListener('synced', syncedHandler); - done(); - } - }; - - node.services.bitcoind.on('synced', syncedHandler); - - client.generate(150, function(err) { - if (err) { - throw err; - } - }); - - }); - - - }); - }); - - after(function(done) { - this.timeout(20000); - node.stop(function(err, result) { - if(err) { - throw err; - } - done(); - }); - }); - - describe('Bus Functionality', function() { - it('subscribes and unsubscribes to an event on the bus', function(done) { - var bus = node.openBus(); - var blockExpected; - var blockReceived; - bus.subscribe('bitcoind/hashblock'); - bus.on('bitcoind/hashblock', function(data) { - bus.unsubscribe('bitcoind/hashblock'); - if (blockExpected) { - data.should.be.equal(blockExpected); - done(); - } else { - blockReceived = data; - } - }); - client.generate(1, function(err, response) { - if (err) { - throw err; - } - if (blockReceived) { - blockReceived.should.be.equal(response.result[0]); - done(); - } else { - blockExpected = response.result[0]; - } - }); - }); - }); - - describe('Address Functionality', function() { - var address; - var unspentOutput; - before(function(done) { - this.timeout(10000); - address = testKey.toAddress(regtest).toString(); - var startHeight = node.services.bitcoind.height; - node.services.bitcoind.on('tip', function(height) { - if (height === startHeight + 3) { - done(); - } - }); - client.sendToAddress(testKey.toAddress(regtest).toString(), 10, function(err) { - if (err) { - throw err; - } - client.generate(3, function(err) { - if (err) { - throw err; - } - }); - }); - }); - it('should be able to get the balance of the test address', function(done) { - node.getAddressBalance(address, false, function(err, data) { - if (err) { - throw err; - } - data.balance.should.equal(10 * 1e8); - done(); - }); - }); - it('can get unspent outputs for address', function(done) { - node.getAddressUnspentOutputs(address, false, function(err, results) { - if (err) { - throw err; - } - results.length.should.equal(1); - unspentOutput = outputForIsSpentTest1 = results[0]; - done(); - }); - }); - it('correctly give the history for the address', function(done) { - var options = { - from: 0, - to: 10, - queryMempool: false - }; - node.getAddressHistory(address, options, function(err, results) { - if (err) { - throw err; - } - var items = results.items; - items.length.should.equal(1); - var info = items[0]; - should.exist(info.addresses[address]); - info.addresses[address].outputIndexes.length.should.equal(1); - info.addresses[address].outputIndexes[0].should.be.within(0, 1); - info.addresses[address].inputIndexes.should.deep.equal([]); - info.satoshis.should.equal(10 * 1e8); - info.confirmations.should.equal(3); - info.tx.blockTimestamp.should.be.a('number'); - info.tx.feeSatoshis.should.be.within(950, 4000); - done(); - }); - }); - it('correctly give the summary for the address', function(done) { - var options = { - queryMempool: false - }; - node.getAddressSummary(address, options, function(err, results) { - if (err) { - throw err; - } - results.totalReceived.should.equal(1000000000); - results.totalSpent.should.equal(0); - results.balance.should.equal(1000000000); - should.not.exist(results.unconfirmedBalance); - results.appearances.should.equal(1); - should.not.exist(results.unconfirmedAppearances); - results.txids.length.should.equal(1); - done(); - }); - }); - describe('History', function() { - - this.timeout(20000); - - var testKey2; - var address2; - var testKey3; - var address3; - var testKey4; - var address4; - var testKey5; - var address5; - var testKey6; - var address6; - var tx2Amount; - var tx2Hash; - - before(function(done) { - /* jshint maxstatements: 50 */ - - // Finished once all blocks have been mined - var startHeight = node.services.bitcoind.height; - node.services.bitcoind.on('tip', function(height) { - if (height === startHeight + 5) { - done(); - } - }); - - testKey2 = bitcore.PrivateKey.fromWIF('cNfF4jXiLHQnFRsxaJyr2YSGcmtNYvxQYSakNhuDGxpkSzAwn95x'); - address2 = testKey2.toAddress(regtest).toString(); - - testKey3 = bitcore.PrivateKey.fromWIF('cVTYQbaFNetiZcvxzXcVMin89uMLC43pEBMy2etgZHbPPxH5obYt'); - address3 = testKey3.toAddress(regtest).toString(); - - testKey4 = bitcore.PrivateKey.fromWIF('cPNQmfE31H2oCUFqaHpfSqjDibkt7XoT2vydLJLDHNTvcddCesGw'); - address4 = testKey4.toAddress(regtest).toString(); - - testKey5 = bitcore.PrivateKey.fromWIF('cVrzm9gCmnzwEVMGeCxY6xLVPdG3XWW97kwkFH3H3v722nb99QBF'); - address5 = testKey5.toAddress(regtest).toString(); - - testKey6 = bitcore.PrivateKey.fromWIF('cPfMesNR2gsQEK69a6xe7qE44CZEZavgMUak5hQ74XDgsRmmGBYF'); - address6 = testKey6.toAddress(regtest).toString(); - - var tx = new Transaction(); - tx.from(unspentOutput); - tx.to(address, 1 * 1e8); - tx.to(address, 2 * 1e8); - tx.to(address, 0.5 * 1e8); - tx.to(address, 3 * 1e8); - tx.fee(10000); - tx.change(address); - tx.sign(testKey); - - unspentOutputSpentTxId = tx.id; - - function mineBlock(next) { - client.generate(1, function(err, response) { - if (err) { - throw err; - } - should.exist(response); - next(); - }); - } - - node.sendTransaction(tx.serialize(), function(err, hash) { - if (err) { - return done(err); - } - - client.generate(1, function(err, response) { - if (err) { - throw err; - } - should.exist(response); - - node.getAddressUnspentOutputs(address, false, function(err, results) { - /* jshint maxstatements: 50 */ - if (err) { - throw err; - } - results.length.should.equal(5); - - async.series([ - function(next) { - var tx2 = new Transaction(); - tx2Amount = results[0].satoshis - 10000; - tx2.from(results[0]); - tx2.to(address2, tx2Amount); - tx2.change(address); - tx2.sign(testKey); - tx2Hash = tx2.hash; - node.sendTransaction(tx2.serialize(), function(err) { - if (err) { - return next(err); - } - mineBlock(next); - }); - }, function(next) { - var tx3 = new Transaction(); - tx3.from(results[1]); - tx3.to(address3, results[1].satoshis - 10000); - tx3.change(address); - tx3.sign(testKey); - node.sendTransaction(tx3.serialize(), function(err) { - if (err) { - return next(err); - } - mineBlock(next); - }); - }, function(next) { - var tx4 = new Transaction(); - tx4.from(results[2]); - tx4.to(address4, results[2].satoshis - 10000); - tx4.change(address); - tx4.sign(testKey); - node.sendTransaction(tx4.serialize(), function(err) { - if (err) { - return next(err); - } - mineBlock(next); - }); - }, function(next) { - var tx5 = new Transaction(); - tx5.from(results[3]); - tx5.from(results[4]); - tx5.to(address5, results[3].satoshis - 10000); - tx5.to(address6, results[4].satoshis - 10000); - tx5.change(address); - tx5.sign(testKey); - node.sendTransaction(tx5.serialize(), function(err) { - if (err) { - return next(err); - } - mineBlock(next); - }); - } - ], function(err) { - if (err) { - throw err; - } - }); - }); - - }); - - }); - - }); - - it('five addresses', function(done) { - var addresses = [ - address2, - address3, - address4, - address5, - address6 - ]; - var options = {}; - node.getAddressHistory(addresses, options, function(err, results) { - if (err) { - throw err; - } - results.totalCount.should.equal(4); - var history = results.items; - history.length.should.equal(4); - history[0].tx.height.should.equal(159); - history[0].confirmations.should.equal(1); - history[1].tx.height.should.equal(158); - should.exist(history[1].addresses[address4]); - history[2].tx.height.should.equal(157); - should.exist(history[2].addresses[address3]); - history[3].tx.height.should.equal(156); - should.exist(history[3].addresses[address2]); - history[3].satoshis.should.equal(tx2Amount); - history[3].tx.hash.should.equal(tx2Hash); - history[3].confirmations.should.equal(4); - done(); - }); - }); - - it('five addresses (limited by height)', function(done) { - var addresses = [ - address2, - address3, - address4, - address5, - address6 - ]; - var options = { - start: 158, - end: 157 - }; - node.getAddressHistory(addresses, options, function(err, results) { - if (err) { - throw err; - } - results.totalCount.should.equal(2); - var history = results.items; - history.length.should.equal(2); - history[0].tx.height.should.equal(158); - history[0].confirmations.should.equal(2); - history[1].tx.height.should.equal(157); - should.exist(history[1].addresses[address3]); - done(); - }); - }); - - it('five addresses (limited by height 155 to 154)', function(done) { - var addresses = [ - address2, - address3, - address4, - address5, - address6 - ]; - var options = { - start: 157, - end: 156 - }; - node.getAddressHistory(addresses, options, function(err, results) { - if (err) { - throw err; - } - results.totalCount.should.equal(2); - var history = results.items; - history.length.should.equal(2); - history[0].tx.height.should.equal(157); - history[1].tx.height.should.equal(156); - done(); - }); - }); - - it('five addresses (paginated by index)', function(done) { - var addresses = [ - address2, - address3, - address4, - address5, - address6 - ]; - var options = { - from: 0, - to: 3 - }; - node.getAddressHistory(addresses, options, function(err, results) { - if (err) { - throw err; - } - results.totalCount.should.equal(4); - var history = results.items; - history.length.should.equal(3); - history[0].tx.height.should.equal(159); - history[0].confirmations.should.equal(1); - history[1].tx.height.should.equal(158); - should.exist(history[1].addresses[address4]); - done(); - }); - }); - - it('one address with sending and receiving', function(done) { - var addresses = [ - address - ]; - var options = {}; - node.getAddressHistory(addresses, options, function(err, results) { - if (err) { - throw err; - } - results.totalCount.should.equal(6); - var history = results.items; - history.length.should.equal(6); - history[0].tx.height.should.equal(159); - history[0].addresses[address].inputIndexes.should.deep.equal([0, 1]); - history[0].addresses[address].outputIndexes.should.deep.equal([2]); - history[0].confirmations.should.equal(1); - history[1].tx.height.should.equal(158); - history[2].tx.height.should.equal(157); - history[3].tx.height.should.equal(156); - history[4].tx.height.should.equal(155); - history[4].satoshis.should.equal(-10000); - history[4].addresses[address].outputIndexes.should.deep.equal([0, 1, 2, 3, 4]); - history[4].addresses[address].inputIndexes.should.deep.equal([0]); - history[5].tx.height.should.equal(152); - history[5].satoshis.should.equal(10 * 1e8); - done(); - }); - }); - - it('summary for an address (sending and receiving)', function(done) { - node.getAddressSummary(address, {}, function(err, results) { - if (err) { - throw err; - } - results.totalReceived.should.equal(2000000000); - results.totalSpent.should.equal(1999990000); - results.balance.should.equal(10000); - results.unconfirmedBalance.should.equal(0); - results.appearances.should.equal(6); - results.unconfirmedAppearances.should.equal(0); - results.txids.length.should.equal(6); - done(); - }); - }); - - - it('total transaction count (sending and receiving)', function(done) { - var addresses = [ - address - ]; - var options = {}; - node.getAddressHistory(addresses, options, function(err, results) { - if (err) { - throw err; - } - results.totalCount.should.equal(6); - done(); - }); - }); - - describe('Pagination', function() { - it('from 0 to 1', function(done) { - var options = { - from: 0, - to: 1 - }; - node.getAddressHistory(address, options, function(err, results) { - if (err) { - throw err; - } - var history = results.items; - history.length.should.equal(1); - history[0].tx.height.should.equal(159); - done(); - }); - }); - it('from 1 to 2', function(done) { - var options = { - from: 1, - to: 2 - }; - node.getAddressHistory(address, options, function(err, results) { - if (err) { - throw err; - } - var history = results.items; - history.length.should.equal(1); - history[0].tx.height.should.equal(158); - done(); - }); - }); - it('from 2 to 3', function(done) { - var options = { - from: 2, - to: 3 - }; - node.getAddressHistory(address, options, function(err, results) { - if (err) { - throw err; - } - var history = results.items; - history.length.should.equal(1); - history[0].tx.height.should.equal(157); - done(); - }); - }); - it('from 3 to 4', function(done) { - var options = { - from: 3, - to: 4 - }; - node.getAddressHistory(address, options, function(err, results) { - if (err) { - throw err; - } - var history = results.items; - history.length.should.equal(1); - history[0].tx.height.should.equal(156); - done(); - }); - }); - it('from 4 to 5', function(done) { - var options = { - from: 4, - to: 5 - }; - node.getAddressHistory(address, options, function(err, results) { - if (err) { - throw err; - } - var history = results.items; - history.length.should.equal(1); - history[0].tx.height.should.equal(155); - history[0].satoshis.should.equal(-10000); - history[0].addresses[address].outputIndexes.should.deep.equal([0, 1, 2, 3, 4]); - history[0].addresses[address].inputIndexes.should.deep.equal([0]); - done(); - }); - }); - it('from 5 to 6', function(done) { - var options = { - from: 5, - to: 6 - }; - node.getAddressHistory(address, options, function(err, results) { - if (err) { - throw err; - } - var history = results.items; - history.length.should.equal(1); - history[0].tx.height.should.equal(152); - history[0].satoshis.should.equal(10 * 1e8); - done(); - }); - }); - - }); - }); - - describe('Mempool Index', function() { - var unspentOutput; - before(function(done) { - node.getAddressUnspentOutputs(address, false, function(err, results) { - if (err) { - throw err; - } - results.length.should.equal(1); - unspentOutput = results[0]; - done(); - }); - }); - - it('will update the mempool index after new tx', function(done) { - var memAddress = bitcore.PrivateKey().toAddress(node.network).toString(); - var tx = new Transaction(); - tx.from(unspentOutput); - tx.to(memAddress, unspentOutput.satoshis - 1000); - tx.fee(1000); - tx.sign(testKey); - - node.services.bitcoind.sendTransaction(tx.serialize(), function(err, hash) { - node.getAddressTxids(memAddress, {}, function(err, txids) { - if (err) { - return done(err); - } - txids.length.should.equal(1); - txids[0].should.equal(hash); - done(); - }); - }); - }); - - }); - - }); - - describe('Orphaned Transactions', function() { - this.timeout(8000); - var orphanedTransaction; - - before(function(done) { - var count; - var invalidatedBlockHash; - - async.series([ - function(next) { - client.sendToAddress(testKey.toAddress(regtest).toString(), 10, function(err) { - if (err) { - return next(err); - } - client.generate(1, next); - }); - }, - function(next) { - client.getBlockCount(function(err, response) { - if (err) { - return next(err); - } - count = response.result; - next(); - }); - }, - function(next) { - client.getBlockHash(count, function(err, response) { - if (err) { - return next(err); - } - invalidatedBlockHash = response.result; - next(); - }); - }, - function(next) { - client.getBlock(invalidatedBlockHash, function(err, response) { - if (err) { - return next(err); - } - orphanedTransaction = response.result.tx[1]; - should.exist(orphanedTransaction); - next(); - }); - }, - function(next) { - client.invalidateBlock(invalidatedBlockHash, next); - } - ], function(err) { - if (err) { - throw err; - } - done(); - }); - }); - - it('will not show confirmation count for orphaned transaction', function(done) { - // This test verifies that in the situation that the transaction is not in the mempool and - // is included in an orphaned block transaction index that the confirmation count will be unconfirmed. - node.getDetailedTransaction(orphanedTransaction, function(err, data) { - if (err) { - return done(err); - } - should.exist(data); - should.exist(data.height); - data.height.should.equal(-1); - done(); - }); - }); - - }); - -}); diff --git a/regtest/p2p.js b/regtest/p2p.js deleted file mode 100644 index 6f98899d..00000000 --- a/regtest/p2p.js +++ /dev/null @@ -1,417 +0,0 @@ -'use strict'; - -var chai = require('chai'); -var expect = chai.expect; -var async = require('async'); -var BitcoinRPC = require('bitcoind-rpc'); -var path = require('path'); -var Utils = require('./utils'); -var crypto = require('crypto'); -var zmq = require('zmq'); -var bitcore = require('bitcore-lib'); -var Transaction = bitcore.Transaction; -var Block = bitcore.Block; -var BlockHeader = bitcore.BlockHeader; -var constants = require('../lib/constants'); -var debug = true; -var extraDebug = true; -//advisable to use a tmpfs here, much easier on NAND-based disks and good for performance -var bitcoreDataDir = '/tmp/testtmpfs/bitcore'; -// to do this on Linux: sudo mount -t tmpfs -o size=512m tmpfs /tmp/testtmpfs -var bitcoinDataDir = '/tmp/testtmpfs/bitcoin'; - -var rpcConfig = { - protocol: 'http', - user: 'bitcoin', - pass: 'local321', - host: '127.0.0.1', - port: '58332', - rejectUnauthorized: false -}; - -var bitcoin = { - args: { - datadir: bitcoinDataDir, - listen: 1, - regtest: 1, - server: 1, - listenonion: 0, - whitelist: '127.0.0.1', - rpcuser: rpcConfig.user, - rpcpassword: rpcConfig.pass, - rpcport: rpcConfig.port - }, - datadir: bitcoinDataDir, - exec: 'bitcoind', //if this isn't on your PATH, then provide the absolute path, e.g. /usr/local/bin/bitcoind - process: null -}; - -var bitcore = { - configFile: { - file: bitcoreDataDir + '/bitcore-node.json', - conf: { - network: 'regtest', - port: 53001, - datadir: bitcoreDataDir, - services: ['p2p', 'test-p2p', 'web'], - servicesConfig: { - p2p: { - peers: [ - { - ip: { v4: '127.0.0.1' } - } - ] - }, - 'test-p2p': { - requirePath: path.resolve(__dirname + '/test_bus.js') - } - } - } - }, - httpOpts: { - protocol: 'http:', - hostname: 'localhost', - port: 53001, - }, - opts: { cwd: bitcoreDataDir }, - datadir: bitcoreDataDir, - exec: path.resolve(__dirname, '../bin/bitcore-node'), - args: ['start'], - process: null -}; - -if (debug && extraDebug) { - bitcoin.args.printtoconsole = 1; - bitcoin.args.debug = 1; - bitcoin.args.logips = 1; -} - -var opts = { - debug: debug, - bitcore: bitcore, - bitcoin: bitcoin, - bitcoinDataDir: bitcoinDataDir, - bitcoreDataDir: bitcoreDataDir, - rpc: new BitcoinRPC(rpcConfig), - walletPassphrase: 'test', - txCount: 0, - blockHeight: 0, - walletPrivKeys: [], - initialTxs: [], - fee: 100000, - feesReceived: 0, - satoshisSent: 0, - walletId: crypto.createHash('sha256').update('test').digest('hex'), - satoshisReceived: 0, - initialHeight: 110, - path: '/test/info', - errorFilter: function(err, res) { - try { - var info = JSON.parse(res); - if (info.result) { - return; - } - } catch(e) { - return e; - } - } -}; - -var utils = new Utils(opts); - -var subSocket; -var txs = []; -var blocks = []; -var headers = []; -var startingBlockHash; -var count = 0; -function processMessages(topic, message) { - var topicStr = topic.toString(); - if (topicStr === 'transaction') { - return txs.push(message); - } else if (topicStr === 'block') { - count++; - return blocks.push(message); - } else if (topicStr === 'headers') { - return headers.push(message); - } -} - -function setupZmqSubscriber(callback) { - - subSocket = zmq.socket('sub'); - subSocket.on('connect', function(fd, endPoint) { - if (debug) { - console.log('ZMQ connected to:', endPoint); - } - }); - - subSocket.on('disconnect', function(fd, endPoint) { - if (debug) { - console.log('ZMQ disconnect:', endPoint); - } - }); - - subSocket.monitor(100, 0); - subSocket.connect('tcp://127.0.0.1:38332'); - subSocket.subscribe('transaction'); - subSocket.subscribe('block'); - subSocket.subscribe('headers'); - subSocket.on('message', processMessages); - callback(); -} - -describe('P2P Operations', function() { - - this.timeout(60000); - - after(function(done) { - utils.cleanup(done); - }); - - before(function(done) { - async.series([ - utils.startBitcoind.bind(utils), - utils.waitForBitcoinReady.bind(utils), - utils.unlockWallet.bind(utils), - utils.setupInitialTxs.bind(utils), - utils.startBitcoreNode.bind(utils), - utils.waitForBitcoreNode.bind(utils), - setupZmqSubscriber, - utils.sendTxs.bind(utils, false) - ], done); - }); - - describe('Mempool', function() { - it('should send new transactions as they are broadcasted by our trusted peer (unsoliticted)', function(done) { - - var initialTxs = {}; - - utils.opts.initialTxs.map(function(tx) { - initialTxs[tx.hash] = true; - return; - }); - - - var i = 0; - for(; i < utils.opts.initialTxs.length; i++) { - var tx = new Transaction(txs[i]); - expect(initialTxs[tx.hash]).to.equal(true); - } - - expect(txs.length).to.equal(i); - done(); - - }); - - it('should connect to the p2p network and stream the mempool to clients', function(done) { - // this tricky because if the p2p service has already asked for the data - // from a particular peer, it will not ask again until the inv hash is dropped - // from its lru cache. So, to fake this out, I will clear this cache manually - txs.length = 0; - utils.queryBitcoreNode(Object.assign({ - path: '/test/mempool', - }, bitcore.httpOpts), function(err) { - - if(err) { - return done(err); - } - - setTimeout(function() { - var initialTxs = {}; - - utils.opts.initialTxs.map(function(tx) { - initialTxs[tx.hash] = true; - return; - }); - - var i = 0; - for(; i < utils.opts.initialTxs.length; i++) { - var tx = new Transaction(txs[i]); - expect(initialTxs[tx.hash]).to.equal(true); - } - - expect(txs.length).to.equal(i); - - done(); - }, 2000); - }); - }); - - it('should be able to set a mempool filter and only get back what is NOT in the filter', function(done) { - var newTx = txs.shift(); - newTx = new Transaction(newTx); - var argTxs = txs.map(function(rawTx) { - var tx = new Transaction(rawTx); - return tx.hash; - }); - txs.length = 0; - utils.queryBitcoreNode(Object.assign({ - path: '/test/mempool?filter=' + JSON.stringify(argTxs) - }, bitcore.httpOpts), function(err) { - - if (err) { - return done(err); - } - - setTimeout(function() { - - var tx = new Transaction(txs[0]); - expect(newTx.hash).to.equal(tx.hash); - expect(txs.length).to.equal(1); - done(); - }, 2000); - }); - }); - }); - - describe('Block', function() { - - it('should get blocks when they are relayed to us', function(done) { - opts.rpc.generate(1, function(err, res) { - if(err) { - return done(err); - } - startingBlockHash = res.result[0]; - setTimeout(function() { - expect(blocks.length).to.equal(1); - var block = new Block(blocks[0]); - expect(startingBlockHash).to.equal(block.hash); - done(); - }, 2000); - }); - }); - - it('should be able to get historical blocks', function(done) { - - blocks.length = 0; - var filter = { startHash: constants.BITCOIN_GENESIS_HASH.regtest }; - utils.queryBitcoreNode(Object.assign({ - path: '/test/blocks?filter=' + JSON.stringify(filter), - }, bitcore.httpOpts), function(err) { - - if(err) { - return done(err); - } - - setTimeout(function() { - expect(blocks.length).to.equal(utils.opts.blockHeight + 1); - var lastBlock = new Block(blocks[blocks.length - 1]); - expect(startingBlockHash).to.equal(lastBlock.hash); - done(); - }, 2000); - - - }); - - }); - - - }); - - describe('Block Headers', function() { - - it('should be able to get historical block headers', function(done) { - - var filter = { startHash: constants.BITCOIN_GENESIS_HASH.regtest }; - utils.queryBitcoreNode(Object.assign({ - path: '/test/headers?filter=' + JSON.stringify(filter), - }, bitcore.httpOpts), function(err) { - - if(err) { - return done(err); - } - - setTimeout(function() { - - expect(headers.length).to.equal(utils.opts.blockHeight + 1); - var lastBlockHeader = new BlockHeader(blocks[blocks.length - 1]); - expect(startingBlockHash).to.equal(lastBlockHeader.hash); - done(); - - }, 2000); - - - }); - }); - - it('should return up to 2000 headers in a single call to getHeaders', function(done) { - - // p2p note: when asking for a series of headers, your peer will always follow up - // with an additional inventory message after delivering the initial data. - - // after getHeaders: an inv message for the block matching the latest header you received. - // remember: getHeaders message does not respond with an inventory message like getBlocks does, - // instead it responds with the header message, but THEN will respond with a single inventory - // message representing the block of the last header delievered. - - // For example: if there exists 4 blocks with block hashes a,b,c,d: - // getHeaders({ starts: 'a', stop: 0 }) should receive headers for b,c,d and an inv message for block d. - var additionalBlockCount = 2000 - 111; - headers.length = 0; - opts.rpc.generate(additionalBlockCount, function(err) { - - if(err) { - return done(err); - } - - var filter = { startHash: constants.BITCOIN_GENESIS_HASH.regtest }; - - utils.queryBitcoreNode(Object.assign({ - path: '/test/headers?filter=' + JSON.stringify(filter), - }, bitcore.httpOpts), function(err) { - - if(err) { - return done(err); - } - - setTimeout(function() { - - expect(headers.length).to.equal(2000); - done(); - - }, 2000); - - - }); - - }); - }); - - it('should return up to 500 blocks in a single call to getBlocks', function(done) { - - // p2p note: when asking for a series of headers, your peer will always follow up - // with an additional inventory message after delivering the initial data. - - // after getBlocks: an inv message for the block immediately following the last one you received, if - // there more blocks to retrieve. Since there is a 500 block limit in the initial inventory message response, - // when receiving 500 blocks, an additional inventory message will tell you what the next block is and that - // are more blocks to be retrieved. - - blocks.length = 0; - count = 0; - var filter = { startHash: constants.BITCOIN_GENESIS_HASH.regtest }; - - utils.queryBitcoreNode(Object.assign({ - path: '/test/blocks?filter=' + JSON.stringify(filter), - }, bitcore.httpOpts), function(err) { - - if(err) { - return done(err); - } - - setTimeout(function() { - - expect(blocks.length).to.equal(501); - done(); - - }, 2000); - - }); - }); - - }); - - -}); - diff --git a/regtest/test_bus.js b/regtest/test_bus.js deleted file mode 100644 index 1739daad..00000000 --- a/regtest/test_bus.js +++ /dev/null @@ -1,168 +0,0 @@ -'use strict'; - -var BaseService = require('../lib/service'); -var inherits = require('util').inherits; -var zmq = require('zmq'); -var index = require('../lib'); -var log = index.log; - -var TestBusService = function(options) { - BaseService.call(this, options); - this._cache = { transaction: [], block: [], headers: [] }; -}; - -inherits(TestBusService, BaseService); - -TestBusService.dependencies = ['p2p', 'web', 'block', 'timestamp']; - -TestBusService.prototype.start = function(callback) { - - var self = this; - self.pubSocket = zmq.socket('pub'); - - log.info('zmq bound to port: 38332'); - - self.pubSocket.bind('tcp://127.0.0.1:38332'); - - self.bus = self.node.openBus({ remoteAddress: 'localhost' }); - - self.bus.on('p2p/transaction', function(tx) { - self._cache.transaction.push(tx); - if (self._ready) { - while(self._cache.transaction.length > 0) { - var transaction = self._cache.transaction.shift(); - self.pubSocket.send([ 'transaction', new Buffer(transaction.uncheckedSerialize(), 'hex') ]); - } - } - }); - - self.node.services.p2p.on('bestHeight', function(height) { - self._bestHeight = height; - }); - - self.bus.on('p2p/block', function(block) { - self._cache.block.push(block); - if (self._ready) { - while(self._cache.block.length > 0) { - var blk = self._cache.block.shift(); - self.pubSocket.send([ 'p2p/block', blk.toBuffer() ]); - } - } - }); - - self.bus.on('p2p/headers', function(headers) { - headers.forEach(function(header) { - self._cache.headers.push(header); - }); - - if (self._ready) { - while(self._cache.headers.length > 0) { - var hdr = self._cache.headers.shift(); - self.pubSocket.send([ 'headers', hdr.toBuffer() ]); - } - } - }); - - self.bus.on('block/block', function(block) { - self._cache.block.push(block); - if (self._ready) { - while(self._cache.block.length > 0) { - var blk = self._cache.block.shift(); - self.pubSocket.send([ 'block/block', blk.toBuffer() ]); - } - } - }); - - self.bus.subscribe('p2p/transaction'); - self.bus.subscribe('p2p/block'); - self.bus.subscribe('p2p/headers'); - self.bus.subscribe('block/block'); - - self.node.on('ready', function() { - - self._ready = true; - - }); - - callback(); -}; - -TestBusService.prototype.setupRoutes = function(app) { - - var self = this; - - app.get('/p2p/mempool', function(req, res) { - self.node.services.p2p.clearInventoryCache(); - var filter; - if (req.query.filter) { - filter = JSON.parse(req.query.filter); - } - self.node.services.p2p.getMempool(filter); - res.status(200).end(); - }); - - app.get('/p2p/blocks', function(req, res) { - self.node.services.p2p.clearInventoryCache(); - var filter; - if (req.query.filter) { - filter = JSON.parse(req.query.filter); - } - self.node.services.p2p.getBlocks(filter); - res.status(200).end(); - }); - - app.get('/p2p/headers', function(req, res) { - var filter; - if (req.query.filter) { - filter = JSON.parse(req.query.filter); - } - self.node.services.p2p.getHeaders(filter); - res.status(200).end(); - }); - - app.get('/info', function(req, res) { - res.status(200).jsonp({ result: (self._ready && (self._bestHeight >= 0))}); - }); - - app.get('/block/hash/:height', function(req, res) { - self.node.services.block.getBlockHash(req.params.height, function(err, hash) { - res.status(200).jsonp({ hash: hash, height: parseInt(req.params.height) }); - }); - }); - - app.get('/block/height/:hash', function(req, res) { - self.node.services.block.getBlockHeight(req.params.hash, function(err, height) { - res.status(200).jsonp({ hash: req.params.hash, height: height }); - }); - }); - - app.get('/timestamp/time/:hash', function(req, res) { - self.node.services.timestamp.getTimestamp(req.params.hash, function(err, timestamp) { - res.status(200).jsonp({ hash: req.params.hash, timestamp: timestamp }); - }); - }); - - app.get('/timestamp/hash/:time', function(req, res) { - self.node.services.timestamp.getHash(req.params.time, function(err, hash) { - res.status(200).jsonp({ hash: hash, timestamp: parseInt(req.params.time) }); - }); - }); - - app.get('/utxo/:address', function(req, res) { - self.node.services.utxo.getUtxosForAddress(req.params.address, function(err, utxos) { - res.status(200).jsonp({ utxos: utxos }); - }); - }); - -}; - -TestBusService.prototype.getRoutePrefix = function() { - return 'test'; -}; - -TestBusService.prototype.stop = function(callback) { - callback(); -}; - -module.exports = TestBusService; - diff --git a/regtest/test_web.js b/regtest/test_web.js deleted file mode 100644 index 0e365ddd..00000000 --- a/regtest/test_web.js +++ /dev/null @@ -1,69 +0,0 @@ -'use strict'; - -var BaseService = require('../lib/service'); -var inherits = require('util').inherits; - -var TestWebService = function(options) { - BaseService.call(this, options); -}; - -inherits(TestWebService, BaseService); - -TestWebService.dependencies = ['web', 'block', 'timestamp']; - -TestWebService.prototype.start = function(callback) { - callback(); -}; - -TestWebService.prototype.stop = function(callback) { - callback(); -}; - -TestWebService.prototype.setupRoutes = function(app) { - - var self = this; - - app.get('/block/hash/:height', function(req, res) { - self.node.services.block.getBlockHash(req.params.height, function(err, hash) { - res.status(200).jsonp({ hash: hash, height: parseInt(req.params.height) }); - }); - }); - - app.get('/block/height/:hash', function(req, res) { - self.node.services.block.getBlockHeight(req.params.hash, function(err, height) { - res.status(200).jsonp({ hash: req.params.hash, height: height }); - }); - }); - - app.get('/timestamp/time/:hash', function(req, res) { - self.node.services.timestamp.getTimestamp(req.params.hash, function(err, timestamp) { - res.status(200).jsonp({ hash: req.params.hash, timestamp: timestamp }); - }); - }); - - app.get('/timestamp/hash/:time', function(req, res) { - self.node.services.timestamp.getHash(req.params.time, function(err, hash) { - res.status(200).jsonp({ hash: hash, timestamp: parseInt(req.params.time) }); - }); - }); - - app.get('/utxo/:address', function(req, res) { - self.node.services.utxo.getUtxosForAddress(req.params.address, function(err, utxos) { - res.status(200).jsonp({ utxos: utxos }); - }); - }); - - app.get('/info', function(req, res) { - var tip = self.node.services.block.tip; - if (tip) { - return res.status(200).jsonp({ tip: JSON.stringify(tip) }); - } - return res.status(503).end(); - }); -}; - -TestWebService.prototype.getRoutePrefix = function() { - return 'test'; -}; - -module.exports = TestWebService; diff --git a/regtest/timestamp.js b/regtest/timestamp.js deleted file mode 100644 index 857fb8d6..00000000 --- a/regtest/timestamp.js +++ /dev/null @@ -1,199 +0,0 @@ -'use strict'; - -var chai = require('chai'); -var expect = chai.expect; -var async = require('async'); -var BitcoinRPC = require('bitcoind-rpc'); -var path = require('path'); -var utils = require('./utils'); - -var debug = true; -var bitcoreDataDir = '/tmp/bitcore'; -var bitcoinDataDir = '/tmp/bitcoin'; - -var rpcConfig = { - protocol: 'http', - user: 'bitcoin', - pass: 'local321', - host: '127.0.0.1', - port: '58332', - rejectUnauthorized: false -}; - -var bitcoin = { - args: { - datadir: bitcoinDataDir, - listen: 0, - regtest: 1, - server: 1, - rpcuser: rpcConfig.user, - rpcpassword: rpcConfig.pass, - rpcport: rpcConfig.port, - zmqpubrawtx: 'tcp://127.0.0.1:38332', - zmqpubhashblock: 'tcp://127.0.0.1:38332' - }, - datadir: bitcoinDataDir, - exec: 'bitcoind', //if this isn't on your PATH, then provide the absolute path, e.g. /usr/local/bin/bitcoind - process: null -}; - -var bitcore = { - configFile: { - file: bitcoreDataDir + '/bitcore-node.json', - conf: { - network: 'regtest', - port: 53001, - datadir: bitcoreDataDir, - services: [ - 'bitcoind', - 'web', - 'db', - 'timestamp', - 'block', - 'test-timestamp' - ], - servicesConfig: { - bitcoind: { - connect: [ - { - rpcconnect: rpcConfig.host, - rpcport: rpcConfig.port, - rpcuser: rpcConfig.user, - rpcpassword: rpcConfig.pass, - zmqpubrawtx: bitcoin.args.zmqpubrawtx - } - ] - }, - 'test-timestamp': { - requirePath: path.resolve(__dirname + '/test_web.js') - } - } - } - }, - httpOpts: { - protocol: 'http:', - hostname: 'localhost', - port: 53001, - }, - opts: { cwd: bitcoreDataDir }, - datadir: bitcoreDataDir, - exec: path.resolve(__dirname, '../bin/bitcore-node'), - args: ['start'], - process: null -}; - -var opts = { - debug: debug, - bitcore: bitcore, - bitcoin: bitcoin, - bitcoinDataDir: bitcoinDataDir, - bitcoreDataDir: bitcoreDataDir, - rpc: new BitcoinRPC(rpcConfig), - blockHeight: 0, - initialHeight: 150 -}; - -describe('Timestamp Index', function() { - - var self = this; - self.timeout(60000); - - after(function(done) { - utils.cleanup(self.opts, done); - }); - - before(function(done) { - self.opts = Object.assign({}, opts); - async.series([ - utils.startBitcoind.bind(utils, self.opts), - utils.waitForBitcoinReady.bind(utils, self.opts), - utils.startBitcoreNode.bind(utils, self.opts), - utils.waitForBitcoreNode.bind(utils, self.opts), - function(next) { - - async.timesLimit(opts.initialHeight, 12, function(n, next) { - utils.queryBitcoreNode(Object.assign({ - path: '/test/block/hash/' + n - }, bitcore.httpOpts), function(err, res) { - - if(err) { - return done(err); - } - res = JSON.parse(res); - expect(res.height).to.equal(n); - expect(res.hash.length).to.equal(64); - next(null, res.hash); - }); - }, function(err, hashes) { - - if(err) { - return next(err); - } - self.hashes = hashes; - next(); - - }); - - } - ], done); - }); - - it('should sync block hashes as keys and timestamps as values', function(done) { - - var lastTimestamp = 0; - async.mapLimit(self.hashes, 12, function(hash, next) { - - utils.queryBitcoreNode(Object.assign({ - path: '/test/timestamp/time/' + hash - }, bitcore.httpOpts), function(err, res) { - - if(err) { - return next(err); - } - - res = JSON.parse(res); - next(null, res.timestamp); - }); - }, function(err, timestamps) { - - if(err) { - return done(err); - } - timestamps.forEach(function(timestamp) { - expect(timestamp).to.be.above(lastTimestamp); - lastTimestamp = timestamp; - }); - self.timestamps = timestamps; - done(); - - }); - }); - - it('should sync block timestamps as keys and block hashes as values', function(done) { - - async.eachOfLimit(self.timestamps, 12, function(timestamp, index, next) { - utils.queryBitcoreNode(Object.assign({ - path: '/test/timestamp/hash/' + timestamp - }, bitcore.httpOpts), function(err, res) { - - if(err) { - return done(err); - } - - res = JSON.parse(res); - expect(res.hash).to.equal(self.hashes[index]); - expect(res.timestamp).to.equal(timestamp); - next(); - }); - }, function(err) { - - if(err) { - return done(err); - } - done(); - - }); - - }); - -}); diff --git a/regtest/utils.js b/regtest/utils.js deleted file mode 100644 index 2206b009..00000000 --- a/regtest/utils.js +++ /dev/null @@ -1,428 +0,0 @@ -'use strict'; - -var bitcore = require('bitcore-lib'); -var _ = require('lodash'); -var mkdirp = require('mkdirp'); -var rimraf = require('rimraf'); -var fs = require('fs'); -var async = require('async'); -var spawn = require('child_process').spawn; -var http = require('http'); -var Unit = bitcore.Unit; -var Transaction = bitcore.Transaction; -var PrivateKey = bitcore.PrivateKey; -var assert = require('assert'); - -var Utils = function(opts) { - this.opts = opts; -}; - -Utils.prototype.writeConfigFile = function(fileStr, obj) { - fs.writeFileSync(fileStr, JSON.stringify(obj)); -}; - -Utils.prototype.toArgs = function(opts) { - return Object.keys(opts).map(function(key) { - return '-' + key + '=' + opts[key]; - }); -}; - -Utils.prototype.waitForService = function(task, callback) { - var retryOpts = { times: 20, interval: 1000 }; - async.retry(retryOpts, task, callback); -}; - -Utils.prototype.queryBitcoreNode = function(httpOpts, callback) { - var error; - var request = http.request(httpOpts, function(res) { - - if (res.statusCode !== 200 && res.statusCode !== 201) { - if (error) { - return; - } - return callback(res.statusCode); - } - - var resError; - var resData = ''; - - res.on('error', function(e) { - resError = e; - }); - - res.on('data', function(data) { - resData += data; - }); - - res.on('end', function() { - if (error) { - return; - } - if (httpOpts.errorFilter) { - return callback(httpOpts.errorFilter(resError, resData)); - } - callback(resError, resData); - }); - - }); - - request.on('error', function(e) { - error = e; - callback(error); - }); - - request.write(httpOpts.body || ''); - request.end(); -}; - -Utils.prototype.waitForBitcoreNode = function(callback) { - - var self = this; - - var errorFilter = self.opts.errorFilter; - if (!errorFilter) { - errorFilter = function(err, res) { - try { - var info = JSON.parse(res); - if (info.dbheight === self.opts.blockHeight && - info.dbheight === info.bitcoindheight && - info.bitcoindhash === info.dbhash) { - return; - } - return res; - } catch(e) { - return e; - } - }; - } - var httpOpts = self.getHttpOpts({ path: self.opts.path || '/info', errorFilter: errorFilter }); - - self.waitForService(self.queryBitcoreNode.bind(self, httpOpts), callback); -}; - -Utils.prototype.waitForBitcoinReady = function(callback) { - - var self = this; - self.waitForService(function(callback) { - - self.opts.rpc.generate(self.opts.initialHeight, function(err, res) { - - if (err || (res && res.error)) { - return callback('keep trying'); - } - self.opts.blockHeight += self.opts.initialHeight; - callback(); - }); - }, function(err) { - - if(err) { - return callback(err); - } - - callback(); - - }, callback); - -}; - -Utils.prototype.initializeAndStartService = function(opts, callback) { - - var self = this; - - rimraf(opts.datadir, function(err) { - - if(err) { - return callback(err); - } - - mkdirp(opts.datadir, function(err) { - - if(err) { - return callback(err); - } - - if (opts.configFile) { - self.writeConfigFile(opts.configFile.file, opts.configFile.conf); - } - - var args = _.isArray(opts.args) ? opts.args : self.toArgs(opts.args); - opts.process = spawn(opts.exec, args, opts.opts); - callback(); - - }); - }); - -}; - -Utils.prototype.startBitcoreNode = function(callback) { - var self = this; - this.initializeAndStartService(self.opts.bitcore, function(err) { - - if(err) { - return callback(err); - } - - self.opts.bitcore.process.stdout.on('data', function(data) { - if (self.opts.debug) { - process.stdout.write(data.toString()); - } - }); - - self.opts.bitcore.process.stderr.on('data', function(data) { - process.stdout.write(data.toString()); - }); - - - callback(); - - }); -}; - -Utils.prototype.startBitcoind = function(callback) { - var self = this; - self.initializeAndStartService(self.opts.bitcoin, function() { - - // in case you choose to -printtoconsole - self.opts.bitcoin.process.stdout.on('data', function(data) { - if (self.opts.debug) { - process.stdout.write(data.toString()); - } - }); - - self.opts.bitcoin.process.stderr.on('data', function(data) { - process.stdout.write(data.toString()); - }); - - callback(); - }); -}; - -Utils.prototype.unlockWallet = function(callback) { - this.opts.rpc.walletPassPhrase(this.opts.walletPassphrase, 3000, function(err) { - if(err && err.code !== -15) { - return callback(err); - } - callback(); - }); -}; - -Utils.prototype.getPrivateKeysWithABalance = function(callback) { - - var self = this; - self.opts.rpc.listUnspent(function(err, res) { - - if(err) { - return callback(err); - } - - var utxos = []; - for(var i = 0; i < res.result.length; i++) { - if (res.result[i].amount > 1) { - utxos.push(res.result[i]); - } - } - if (utxos.length <= 0) { - return callback(new Error('no utxos available')); - } - async.mapLimit(utxos, 8, function(utxo, callback) { - - self.opts.rpc.dumpPrivKey(utxo.address, function(err, res) { - if(err) { - return callback(err); - } - var privKey = res.result; - callback(null, { utxo: utxo, privKey: privKey }); - }); - - }, function(err, utxos) { - if(err) { - return callback(err); - } - callback(null, utxos); - }); - }); - -}; - -Utils.prototype.generateSpendingTxs = function(utxos) { - - var self = this; - return utxos.map(function(utxo) { - - var toPrivKey = new PrivateKey('testnet'); //external addresses - var changePrivKey = new PrivateKey('testnet'); //our wallet keys - var utxoSatoshis = Unit.fromBTC(utxo.utxo.amount).satoshis; - var satsToPrivKey = Math.round(utxoSatoshis / 2); - var tx = new Transaction(); - - tx.from(utxo.utxo); - tx.to(toPrivKey.toAddress().toString(), satsToPrivKey); - tx.fee(self.opts.fee); - tx.change(changePrivKey.toAddress().toString()); - tx.sign(utxo.privKey); - - self.opts.walletPrivKeys.push(changePrivKey); - self.opts.satoshisReceived += Unit.fromBTC(utxo.utxo.amount).toSatoshis() - (satsToPrivKey + self.opts.fee); - return tx; - }); - -}; - -Utils.prototype.setupInitialTxs = function(callback) { - - var self = this; - self.getPrivateKeysWithABalance(function(err, utxos) { - - if(err) { - return callback(err); - } - self.opts.initialTxs = self.generateSpendingTxs(utxos); - callback(); - }); - -}; - -Utils.prototype.sendTxs = function(generateBlockAfterEach, callback) { - var self = this; - if (typeof generateBlockAfterEach !== 'function') { - return async.eachSeries(this.opts.initialTxs, function(tx, next) { - self.sendTx(tx, generateBlockAfterEach, next); - }, callback); - } - async.eachOfSeries(this.opts.initialTxs, this.sendTx.bind(this), callback); -}; - -Utils.prototype.sendTx = function(tx, index, callback) { - - var self = this; - // sending these too quickly will prevent them from being relayed over the - // p2p network - self.opts.rpc.sendRawTransaction(tx.serialize(), function(err, res) { - if (err) { - return callback(err); - } - assert(res.result === tx.hash, 'sendTx: provided hash did not match returned hash'); - var mod = index % 2; - setTimeout(function() { - if (mod === 1) { - self.opts.blockHeight++; - self.opts.rpc.generate(1, callback); - } else { - callback(); - } - }, 200); - }); - -}; - -Utils.prototype.getHttpOpts = function(httpOpts) { - return Object.assign({ - path: httpOpts.path, - method: httpOpts.method || 'GET', - body: httpOpts.body, - headers: { - 'Content-Type': 'application/json', - 'Content-Length': httpOpts.length || 0 - }, - errorFilter: httpOpts.errorFilter - }, this.opts.bitcore.httpOpts); -}; - -Utils.prototype.registerWallet = function(callback) { - - var httpOpts = this.getHttpOpts(this.opts, { path: '/wallet-api/wallets/' + this.opts.walletId, method: 'POST' }); - this.queryBitcoreNode(httpOpts, callback); - -}; - -Utils.prototype.uploadWallet = function(callback) { - - var self = this; - var addresses = JSON.stringify(self.opts.walletPrivKeys.map(function(privKey) { - if (privKey.privKey) { - return privKey.pubKey.toString(); - } - return privKey.toAddress().toString(); - })); - - var httpOpts = self.getHttpOpts(self.opts, { - path: '/wallet-api/wallets/' + self.opts.walletId + '/addresses', - method: 'POST', - body: addresses, - length: addresses.length - }); - - async.waterfall([ self.queryBitcoreNode.bind(self, httpOpts) ], function(err, res) { - if (err) { - return callback(err); - } - var job = JSON.parse(res); - - Object.keys(job).should.deep.equal(['jobId']); - - var httpOpts = self.getHttpOpts(self.opts, { path: '/wallet-api/jobs/' + job.jobId }); - - async.retry({ times: 10, interval: 1000 }, function(next) { - self.queryBitcoreNode(httpOpts, function(err, res) { - if (err) { - return next(err); - } - var result = JSON.parse(res); - if (result.status === 'complete') { - return next(); - } - next(res); - }); - - }, function(err) { - if(err) { - return callback(err); - } - callback(); - }); - }); - -}; - -Utils.prototype.getListOfTxs = function(callback) { - - var self = this; - var end = Date.now() + 86400000; - var httpOpts = self.getHttpOpts(self.opts, { - path: '/wallet-api/wallets/' + self.opts.walletId + '/transactions?start=0&end=' + end }); - - self.queryBitcoreNode(httpOpts, function(err, res) { - if(err) { - return callback(err); - } - var results = []; - res.split('\n').forEach(function(result) { - - if (result.length > 0) { - return results.push(JSON.parse(result)); - } - - }); - - var map = self.opts.initialTxs.map(function(tx) { - return tx.serialize(); - }); - - results.forEach(function(result) { - var tx = new Transaction(result); - map.splice(map.indexOf(tx.uncheckedSerialize()), 1); - }); - - map.length.should.equal(0); - results.length.should.equal(self.opts.initialTxs.length); - callback(); - }); -}; - -Utils.prototype.cleanup = function(callback) { - this.opts.bitcore.process.kill(); - this.opts.bitcoin.process.kill(); - setTimeout(callback, 2000); -}; - -module.exports = Utils; diff --git a/regtest/utxo.js b/regtest/utxo.js deleted file mode 100644 index 14533431..00000000 --- a/regtest/utxo.js +++ /dev/null @@ -1,270 +0,0 @@ -'use strict'; - -var chai = require('chai'); -var expect = chai.expect; -var async = require('async'); -var BitcoinRPC = require('bitcoind-rpc'); -var path = require('path'); -var Utils = require('./utils'); -var crypto = require('crypto'); -var bitcore = require('bitcore-lib'); -var PrivateKey = bitcore.PrivateKey; -var Transaction = bitcore.Transaction; -var Output = bitcore.Transaction.Output; -var Script = bitcore.Script; -var _ = require('lodash'); - -var debug = false; -var bitcoreDataDir = '/tmp/bitcore'; -var bitcoinDataDir = '/tmp/bitcoin'; - -var rpcConfig = { - protocol: 'http', - user: 'bitcoin', - pass: 'local321', - host: '127.0.0.1', - port: '58332', - rejectUnauthorized: false -}; - -var bitcoin = { - args: { - datadir: bitcoinDataDir, - listen: 0, - regtest: 1, - server: 1, - rpcuser: rpcConfig.user, - rpcpassword: rpcConfig.pass, - rpcport: rpcConfig.port, - zmqpubrawtx: 'tcp://127.0.0.1:38332', - zmqpubrawblock: 'tcp://127.0.0.1:38332' - }, - datadir: bitcoinDataDir, - exec: 'bitcoind', //if this isn't on your PATH, then provide the absolute path, e.g. /usr/local/bin/bitcoind - process: null -}; - -var bitcore = { - configFile: { - file: bitcoreDataDir + '/bitcore-node.json', - conf: { - network: 'regtest', - port: 53001, - datadir: bitcoreDataDir, - services: [ - 'bitcoind', - 'db', - 'timestamp', - 'web', - 'block', - 'utxo', - 'utxo-test' - ], - servicesConfig: { - bitcoind: { - connect: [ - { - rpcconnect: rpcConfig.host, - rpcport: rpcConfig.port, - rpcuser: rpcConfig.user, - rpcpassword: rpcConfig.pass, - zmqpubrawtx: bitcoin.args.zmqpubrawtx - } - ] - }, - 'utxo-test': { - requirePath: path.resolve(__dirname + '/test_web.js') - } - } - } - }, - httpOpts: { - protocol: 'http:', - hostname: 'localhost', - port: 53001, - }, - opts: { cwd: bitcoreDataDir }, - datadir: bitcoreDataDir, - exec: path.resolve(__dirname, '../bin/bitcore-node'), - args: ['start'], - process: null -}; - -var opts = { - debug: debug, - bitcore: bitcore, - bitcoin: bitcoin, - bitcoinDataDir: bitcoinDataDir, - bitcoreDataDir: bitcoreDataDir, - rpc: new BitcoinRPC(rpcConfig), - walletPassphrase: 'test', - txCount: 0, - blockHeight: 0, - walletPrivKeys: [], - initialTxs: [], - fee: 100000, - feesReceived: 0, - satoshisSent: 0, - walletId: crypto.createHash('sha256').update('test').digest('hex'), - satoshisReceived: 0, - initialHeight: 150 -}; - -var utils = new Utils(opts); - -describe('Utxo Operations', function() { - - this.timeout(60000); - - var self = this; - - - after(function(done) { - utils.cleanup(done); - }); - - before(function(done) { - async.series([ - utils.startBitcoind.bind(utils), - utils.waitForBitcoinReady.bind(utils), - utils.unlockWallet.bind(utils), - utils.setupInitialTxs.bind(utils), - utils.sendTxs.bind(utils), - utils.startBitcoreNode.bind(utils), - utils.waitForBitcoreNode.bind(utils) - ], done); - }); - - it('should index utxos', function(done) { - async.mapSeries(opts.walletPrivKeys, function(privKey, next) { - - var address = privKey.toAddress().toString(); - - var httpOpts = Object.assign({ - path: '/test/utxo/' + address - }, bitcore.httpOpts); - - utils.queryBitcoreNode(httpOpts, function(err, res) { - - if(err) { - return next(err); - } - - res = JSON.parse(res); - expect(res.utxos.length).equal(1); - expect(res.utxos[0].address).to.equal(address); - expect(Object.keys(res.utxos[0])).to.deep.equal([ - 'address', - 'txId', - 'outputIndex', - 'height', - 'satoshis', - 'script' ]); - next(null, res.utxos); - - }); - }, function(err, results) { - - if(err) { - return done(err); - } - - self.utxos = _.flatten(results); - - done(); - - }); - }); - - it('should store p2pk and p2pkh utxos', function(done) { - - var pk1 = new PrivateKey('testnet'); - var pk2 = new PrivateKey('testnet'); - - var satoshis = 100000000; - var utxo = self.utxos[0]; - - var tx = new Transaction(); - - tx.from(utxo); - - tx.addOutput(new Output({ - satoshis: satoshis, - script: Script.buildPublicKeyOut(pk1.publicKey) - })); - - tx.change(pk2.toAddress().toString()); - tx.sign(opts.walletPrivKeys[0]); - - async.series([ - - function(next) { - utils.sendTx(tx, 1, function(err) { - - if (err) { - return next(err); - } - - next(); - }); - }, - - function(next) { - - utils.waitForBitcoreNode(function(err) { - - if (err) { - return next(err); - } - - next(); - - }); - - }, - - function(next) { - - var address = pk1.publicKey.toString('hex'); - var httpOpts = Object.assign({ - path: '/test/utxo/' + address - }, bitcore.httpOpts); - - utils.queryBitcoreNode(httpOpts, function(err, res) { - - if(err) { - return next(err); - } - - res = JSON.parse(res); - expect(res.utxos.length).to.equal(1); - expect(res.utxos[0].address).to.equal(address); - expect(res.utxos[0].satoshis).to.equal(satoshis); - expect(Object.keys(res.utxos[0])).to.deep.equal([ - 'address', - 'txId', - 'outputIndex', - 'height', - 'satoshis', - 'script' ]); - - next(); - - }); - } - - ], function(err) { - - if(err) { - return done(err); - } - - - done(); - - }); - - }); - -}); - diff --git a/regtest/v4/bitcoind.js b/regtest/v4/bitcoind.js deleted file mode 100644 index 1df2c507..00000000 --- a/regtest/v4/bitcoind.js +++ /dev/null @@ -1,467 +0,0 @@ -'use strict'; - -// To run the tests: $ mocha -R spec regtest/bitcoind.js - -var path = require('path');; -var index = require('../..'); -var log = index.log; - -var chai = require('chai'); -var bitcore = require('bitcore-lib'); -var BN = bitcore.crypto.BN; -var async = require('async'); -var rimraf = require('rimraf'); -var bitcoind; - -/* jshint unused: false */ -var should = chai.should(); -var assert = chai.assert; -var sinon = require('sinon'); -var BitcoinRPC = require('bitcoind-rpc'); -var transactionData = []; -var blockHashes = []; -var utxos; -var client; -var coinbasePrivateKey; -var privateKey = bitcore.PrivateKey(); -var destKey = bitcore.PrivateKey(); - -describe('Bitcoind Functionality', function() { - - before(function(done) { - this.timeout(60000); - - // Add the regtest network - bitcore.Networks.enableRegtest(); - var regtestNetwork = bitcore.Networks.get('regtest'); - var config = { rpcprotocol: 'http', rpcport: 18332, rpcuser: 'bitcoin', rpcpassword: 'local321', zmqpubrawtx: 'tcp://127.0.0.1:38332' }; - - bitcoind = require('../..').services.Bitcoin({ - connect: [ config ], - node: { - network: regtestNetwork, - getNetworkName: function() { - return 'regtest'; - } - } - }); - - bitcoind.on('error', function(err) { - log.error('error="%s"', err.message); - }); - - bitcoind.start(function() { - log.info('Bitcoind started'); - - client = new BitcoinRPC({ - protocol: config.rpcprotocol || 'http', - host: config.rpchost || '127.0.0.1', - port: config.rpcport, - user: config.rpcuser, - pass: config.rpcpassword, - rejectUnauthorized: true - }); - - setImmediate(function() { - client.generate(150, function(err, response) { - if (err) { - throw err; - } - blockHashes = response.result; - - log.info('Preparing test data...'); - - // Get all of the unspent outputs - client.listUnspent(0, 150, function(err, response) { - utxos = response.result; - - async.mapSeries(utxos, function(utxo, next) { - async.series([ - function(finished) { - // Load all of the transactions for later testing - client.getTransaction(utxo.txid, function(err, txresponse) { - if (err) { - throw err; - } - // add to the list of transactions for testing later - transactionData.push(txresponse.result.hex); - finished(); - }); - }, - function(finished) { - // Get the private key for each utxo - client.dumpPrivKey(utxo.address, function(err, privresponse) { - if (err) { - throw err; - } - utxo.privateKeyWIF = privresponse.result; - finished(); - }); - } - ], next); - }, function(err) { - if (err) { - throw err; - } - done(); - }); - }); - }); - }); - }); - }); - - after(function(done) { - this.timeout(60000); - bitcoind.node.stopping = true; - bitcoind.stop(function(err, result) { - done(); - }); - }); - - describe('get blocks by hash', function() { - - [0,1,2,3,5,6,7,8,9].forEach(function(i) { - it('generated block ' + i, function(done) { - bitcoind.getBlock(blockHashes[i], function(err, block) { - if (err) { - throw err; - } - should.exist(block); - block.hash.should.equal(blockHashes[i]); - done(); - }); - }); - }); - }); - - describe('get blocks as buffers', function() { - [0,1,2,3,5,6,7,8,9].forEach(function(i) { - it('generated block ' + i, function(done) { - bitcoind.getRawBlock(blockHashes[i], function(err, block) { - if (err) { - throw err; - } - should.exist(block); - (block instanceof Buffer).should.equal(true); - done(); - }); - }); - }); - }); - - describe('get errors as error instances', function() { - it('will wrap an rpc into a javascript error', function(done) { - bitcoind.client.getBlock(1000000000, function(err, response) { - var error = bitcoind._wrapRPCError(err); - (error instanceof Error).should.equal(true); - error.message.should.equal(err.message); - error.code.should.equal(err.code); - should.exist(error.stack); - done(); - }); - }); - }); - - describe('get blocks by height', function() { - - [0,1,2,3,4,5,6,7,8,9].forEach(function(i) { - it('generated block ' + i, function(done) { - // add the genesis block - var height = i + 1; - bitcoind.getBlock(i + 1, function(err, block) { - if (err) { - throw err; - } - should.exist(block); - block.hash.should.equal(blockHashes[i]); - done(); - }); - }); - }); - - it('will get error with number greater than tip', function(done) { - bitcoind.getBlock(1000000000, function(err, response) { - should.exist(err); - err.code.should.equal(-8); - done(); - }); - }); - - }); - - describe('get transactions by hash', function() { - [0,1,2,3,4,5,6,7,8,9].forEach(function(i) { - it('for tx ' + i, function(done) { - var txhex = transactionData[i]; - var tx = new bitcore.Transaction(); - tx.fromString(txhex); - bitcoind.getTransaction(tx.hash, function(err, response) { - if (err) { - throw err; - } - assert(response.toString('hex') === txhex, 'incorrect tx data result'); - done(); - }); - }); - }); - - it('will return error if the transaction does not exist', function(done) { - var txid = '6226c407d0e9705bdd7158e60983e37d0f5d23529086d6672b07d9238d5aa618'; - bitcoind.getTransaction(txid, function(err, response) { - should.exist(err); - done(); - }); - }); - }); - - describe('get transactions as buffers', function() { - [0,1,2,3,4,5,6,7,8,9].forEach(function(i) { - it('for tx ' + i, function(done) { - var txhex = transactionData[i]; - var tx = new bitcore.Transaction(); - tx.fromString(txhex); - bitcoind.getRawTransaction(tx.hash, function(err, response) { - if (err) { - throw err; - } - response.should.be.instanceOf(Buffer); - assert(response.toString('hex') === txhex, 'incorrect tx data result'); - done(); - }); - }); - }); - - it('will return error if the transaction does not exist', function(done) { - var txid = '6226c407d0e9705bdd7158e60983e37d0f5d23529086d6672b07d9238d5aa618'; - bitcoind.getRawTransaction(txid, function(err, response) { - should.exist(err); - done(); - }); - }); - }); - - describe('get block header', function() { - var expectedWork = new BN(6); - [1,2,3,4,5,6,7,8,9].forEach(function(i) { - it('generate block ' + i, function(done) { - bitcoind.getBlockHeader(blockHashes[i], function(err, blockIndex) { - if (err) { - return done(err); - } - should.exist(blockIndex); - should.exist(blockIndex.chainWork); - var work = new BN(blockIndex.chainWork, 'hex'); - work.toString(16).should.equal(expectedWork.toString(16)); - expectedWork = expectedWork.add(new BN(2)); - should.exist(blockIndex.prevHash); - blockIndex.hash.should.equal(blockHashes[i]); - blockIndex.prevHash.should.equal(blockHashes[i - 1]); - blockIndex.height.should.equal(i + 1); - done(); - }); - }); - }); - it('will get null prevHash for the genesis block', function(done) { - bitcoind.getBlockHeader(0, function(err, header) { - if (err) { - return done(err); - } - should.exist(header); - should.equal(header.prevHash, undefined); - done(); - }); - }); - it('will get error for block not found', function(done) { - bitcoind.getBlockHeader('notahash', function(err, header) { - should.exist(err); - done(); - }); - }); - }); - - describe('get block index by height', function() { - var expectedWork = new BN(6); - [2,3,4,5,6,7,8,9].forEach(function(i) { - it('generate block ' + i, function() { - bitcoind.getBlockHeader(i, function(err, header) { - should.exist(header); - should.exist(header.chainWork); - var work = new BN(header.chainWork, 'hex'); - work.toString(16).should.equal(expectedWork.toString(16)); - expectedWork = expectedWork.add(new BN(2)); - should.exist(header.prevHash); - header.hash.should.equal(blockHashes[i - 1]); - header.prevHash.should.equal(blockHashes[i - 2]); - header.height.should.equal(i); - }); - }); - }); - it('will get error with number greater than tip', function(done) { - bitcoind.getBlockHeader(100000, function(err, header) { - should.exist(err); - done(); - }); - }); - }); - - describe('send transaction functionality', function() { - - it('will not error and return the transaction hash', function(done) { - - // create and sign the transaction - var tx = bitcore.Transaction(); - tx.from(utxos[0]); - tx.change(privateKey.toAddress()); - tx.to(destKey.toAddress(), utxos[0].amount * 1e8 - 1000); - tx.sign(bitcore.PrivateKey.fromWIF(utxos[0].privateKeyWIF)); - - // test sending the transaction - bitcoind.sendTransaction(tx.serialize(), function(err, hash) { - if (err) { - return done(err); - } - hash.should.equal(tx.hash); - done(); - }); - - }); - - it('will throw an error if an unsigned transaction is sent', function(done) { - var tx = bitcore.Transaction(); - tx.from(utxos[1]); - tx.change(privateKey.toAddress()); - tx.to(destKey.toAddress(), utxos[1].amount * 1e8 - 1000); - bitcoind.sendTransaction(tx.uncheckedSerialize(), function(err, hash) { - should.exist(err); - (err instanceof Error).should.equal(true); - should.not.exist(hash); - done(); - }); - }); - - it('will throw an error for unexpected types (tx decode failed)', function(done) { - var garbage = new Buffer('abcdef', 'hex'); - bitcoind.sendTransaction(garbage, function(err, hash) { - should.exist(err); - should.not.exist(hash); - var num = 23; - bitcoind.sendTransaction(num, function(err, hash) { - should.exist(err); - (err instanceof Error).should.equal(true); - should.not.exist(hash); - done(); - }); - }); - }); - - it('will emit "tx" events', function(done) { - var tx = bitcore.Transaction(); - tx.from(utxos[2]); - tx.change(privateKey.toAddress()); - tx.to(destKey.toAddress(), utxos[2].amount * 1e8 - 1000); - tx.sign(bitcore.PrivateKey.fromWIF(utxos[2].privateKeyWIF)); - - var serialized = tx.serialize(); - - bitcoind.once('tx', function(buffer) { - buffer.toString('hex').should.equal(serialized); - done(); - }); - bitcoind.sendTransaction(serialized, function(err, hash) { - if (err) { - return done(err); - } - should.exist(hash); - }); - }); - - }); - - describe('fee estimation', function() { - it('will estimate fees', function(done) { - bitcoind.estimateFee(1, function(err, fees) { - if (err) { - return done(err); - } - fees.should.equal(-1); - done(); - }); - }); - }); - - describe('tip updates', function() { - it('will get an event when the tip is new', function(done) { - this.timeout(4000); - bitcoind.on('tip', function(height) { - if (height === 151) { - done(); - } - }); - client.generate(1, function(err, response) { - if (err) { - throw err; - } - }); - }); - }); - - describe('get detailed transaction', function() { - it('should include details for coinbase tx', function(done) { - bitcoind.getDetailedTransaction(utxos[0].txid, function(err, tx) { - if (err) { - return done(err); - } - should.exist(tx.height); - tx.height.should.be.a('number'); - should.exist(tx.blockTimestamp); - should.exist(tx.blockHash); - tx.coinbase.should.equal(true); - tx.version.should.equal(1); - tx.hex.should.be.a('string'); - tx.locktime.should.equal(0); - tx.feeSatoshis.should.equal(0); - tx.outputSatoshis.should.equal(50 * 1e8); - tx.inputSatoshis.should.equal(0); - tx.inputs.length.should.equal(1); - tx.outputs.length.should.equal(1); - should.equal(tx.inputs[0].prevTxId, null); - should.equal(tx.inputs[0].outputIndex, null); - tx.inputs[0].script.should.be.a('string'); - should.equal(tx.inputs[0].scriptAsm, null); - should.equal(tx.inputs[0].address, null); - should.equal(tx.inputs[0].satoshis, null); - tx.outputs[0].satoshis.should.equal(50 * 1e8); - tx.outputs[0].script.should.be.a('string'); - tx.outputs[0].scriptAsm.should.be.a('string'); - tx.outputs[0].spentTxId.should.be.a('string'); - tx.outputs[0].spentIndex.should.equal(0); - tx.outputs[0].spentHeight.should.be.a('number'); - tx.outputs[0].address.should.be.a('string'); - done(); - }); - }); - }); - - describe('#getInfo', function() { - it('will get information', function(done) { - bitcoind.getInfo(function(err, info) { - if (err) { - return done(err); - } - info.network.should.equal('regtest'); - should.exist(info); - should.exist(info.version); - should.exist(info.blocks); - should.exist(info.timeOffset); - should.exist(info.connections); - should.exist(info.difficulty); - should.exist(info.testnet); - should.exist(info.relayFee); - should.exist(info.errors); - done(); - }); - }); - }); - -}); diff --git a/regtest/wallet.js b/regtest/wallet.js deleted file mode 100644 index e84a1fe8..00000000 --- a/regtest/wallet.js +++ /dev/null @@ -1,268 +0,0 @@ -'use strict'; - -var chai = require('chai'); -var should = chai.should(); -var async = require('async'); -var BitcoinRPC = require('bitcoind-rpc'); -var path = require('path'); -var utils = require('./utils'); -var crypto = require('crypto'); - -var debug = true; -var bitcoreDataDir = '/tmp/bitcore'; -var bitcoinDataDir = '/tmp/bitcoin'; - -var rpcConfig = { - protocol: 'http', - user: 'bitcoin', - pass: 'local321', - host: '127.0.0.1', - port: '58332', - rejectUnauthorized: false -}; - -var bitcoin = { - args: { - datadir: bitcoinDataDir, - listen: 0, - regtest: 1, - server: 1, - rpcuser: rpcConfig.user, - rpcpassword: rpcConfig.pass, - rpcport: rpcConfig.port, - zmqpubrawtx: 'tcp://127.0.0.1:38332', - zmqpubhashblock: 'tcp://127.0.0.1:38332' - }, - datadir: bitcoinDataDir, - exec: 'bitcoind', //if this isn't on your PATH, then provide the absolute path, e.g. /usr/local/bin/bitcoind - process: null -}; - -var bitcore = { - configFile: { - file: bitcoreDataDir + '/bitcore-node.json', - conf: { - network: 'regtest', - port: 53001, - datadir: bitcoreDataDir, - services: [ - 'bitcoind', - 'db', - 'transaction', - 'timestamp', - 'address', - 'mempool', - 'wallet-api', - 'web', - 'block' - ], - servicesConfig: { - bitcoind: { - connect: [ - { - rpcconnect: rpcConfig.host, - rpcport: rpcConfig.port, - rpcuser: rpcConfig.user, - rpcpassword: rpcConfig.pass, - zmqpubrawtx: bitcoin.args.zmqpubrawtx - } - ] - } - } - } - }, - httpOpts: { - protocol: 'http:', - hostname: 'localhost', - port: 53001, - }, - opts: { cwd: bitcoreDataDir }, - datadir: bitcoreDataDir, - exec: path.resolve(__dirname, '../bin/bitcore-node'), - args: ['start'], - process: null -}; - -var opts = { - debug: debug, - bitcore: bitcore, - bitcoin: bitcoin, - bitcoinDataDir: bitcoinDataDir, - bitcoreDataDir: bitcoreDataDir, - rpc: new BitcoinRPC(rpcConfig), - walletPassphrase: 'test', - txCount: 0, - blockHeight: 0, - walletPrivKeys: [], - initialTxs: [], - fee: 100000, - feesReceived: 0, - satoshisSent: 0, - walletId: crypto.createHash('sha256').update('test').digest('hex'), - satoshisReceived: 0, - initialHeight: 150 -}; - -describe('Wallet Operations', function() { - - this.timeout(60000); - - describe('Register, Upload, GetTransactions', function() { - - var self = this; - - after(function(done) { - utils.cleanup(self.opts, done); - }); - - before(function(done) { - self.opts = Object.assign({}, opts); - async.series([ - utils.startBitcoind.bind(utils, self.opts), - utils.waitForBitcoinReady.bind(utils, self.opts), - utils.unlockWallet.bind(utils, self.opts), - utils.setupInitialTxs.bind(utils, self.opts), - utils.startBitcoreNode.bind(utils, self.opts), - utils.waitForBitcoreNode.bind(utils, self.opts) - ], done); - }); - - it('should register wallet', function(done) { - - utils.registerWallet.call(utils, self.opts, function(err, res) { - - if (err) { - return done(err); - } - - res.should.deep.equal(JSON.stringify({ - walletId: '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08' - })); - done(); - }); - }); - - it('should upload a wallet', function(done) { - - utils.uploadWallet.call(utils, self.opts, done); - - }); - - it('should get a list of transactions', function(done) { - - //the wallet should be fully uploaded and indexed by the time this happens - utils.sendTxs.call(utils, self.opts, function(err) { - - if(err) { - return done(err); - } - utils.waitForBitcoreNode.call(utils, self.opts, function(err) { - - if(err) { - return done(err); - } - utils.getListOfTxs.call(utils, self.opts, done); - }); - }); - - }); - - }); - - describe('Load addresses after syncing the blockchain', function() { - - var self = this; - - self.opts = Object.assign({}, opts); - - after(utils.cleanup.bind(utils, self.opts)); - - before(function(done) { - async.series([ - utils.startBitcoind.bind(utils, self.opts), - utils.waitForBitcoinReady.bind(utils, self.opts), - utils.unlockWallet.bind(utils, self.opts), - utils.setupInitialTxs.bind(utils, self.opts), - utils.sendTxs.bind(utils, self.opts), - utils.startBitcoreNode.bind(utils, self.opts), - utils.waitForBitcoreNode.bind(utils, self.opts), - utils.registerWallet.bind(utils, self.opts), - utils.uploadWallet.bind(utils, self.opts) - ], done); - }); - - it('should get list of transactions', function(done) { - - utils.getListOfTxs.call(utils, self.opts, done); - - }); - - it('should get the balance of a wallet', function(done) { - - var httpOpts = utils.getHttpOpts.call( - utils, - self.opts, - { path: '/wallet-api/wallets/' + self.opts.walletId + '/balance' }); - - utils.queryBitcoreNode.call(utils, httpOpts, function(err, res) { - if(err) { - return done(err); - } - var results = JSON.parse(res); - results.satoshis.should.equal(self.opts.satoshisReceived); - done(); - }); - - }); - - it('should get the set of utxos for the wallet', function(done) { - - var httpOpts = utils.getHttpOpts.call( - utils, - self.opts, - { path: '/wallet-api/wallets/' + opts.walletId + '/utxos' }); - - utils.queryBitcoreNode.call(utils, httpOpts, function(err, res) { - - if(err) { - return done(err); - } - - var results = JSON.parse(res); - var balance = 0; - - results.utxos.forEach(function(utxo) { - balance += utxo.satoshis; - }); - - results.height.should.equal(self.opts.blockHeight); - balance.should.equal(self.opts.satoshisReceived); - done(); - }); - }); - - it('should get the list of jobs', function(done) { - var httpOpts = utils.getHttpOpts.call(utils, self.opts, { path: '/wallet-api/jobs' }); - utils.queryBitcoreNode.call(utils, httpOpts, function(err, res) { - if(err) { - return done(err); - } - var results = JSON.parse(res); - results.jobCount.should.equal(1); - done(); - }); - }); - - it('should remove all wallets', function(done) { - var httpOpts = utils.getHttpOpts.call(utils, self.opts, { path: '/wallet-api/wallets', method: 'DELETE' }); - utils.queryBitcoreNode.call(utils, httpOpts, function(err, res) { - if(err) { - return done(err); - } - var results = JSON.parse(res); - results.numberRemoved.should.equal(152); - done(); - }); - }); - }); -}); diff --git a/test/services/bitcoind/bitcoind.unit.js b/test/services/bitcoind/bitcoind.unit.js deleted file mode 100644 index 8d565877..00000000 --- a/test/services/bitcoind/bitcoind.unit.js +++ /dev/null @@ -1,5036 +0,0 @@ -'use strict'; - -/* jshint sub: true */ - -var path = require('path'); -var EventEmitter = require('events').EventEmitter; -var should = require('chai').should(); -var crypto = require('crypto'); -var bitcore = require('bitcore-lib'); -var _ = bitcore.deps._; -var sinon = require('sinon'); -var proxyquire = require('proxyquire'); -var fs = require('fs'); -var sinon = require('sinon'); - -var index = require('../../../lib'); -var log = index.log; -var errors = index.errors; - -var Transaction = bitcore.Transaction; -var readFileSync = sinon.stub().returns(fs.readFileSync(path.resolve(__dirname, '../../data/bitcoin.conf'))); -var BitcoinService = proxyquire('../../../lib/services/bitcoind', { - fs: { - readFileSync: readFileSync - } -}); -var defaultBitcoinConf = fs.readFileSync(path.resolve(__dirname, '../../data/default.bitcoin.conf'), 'utf8'); - -describe('Bitcoin Service', function() { - var txhex = '01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0104ffffffff0100f2052a0100000043410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac00000000'; - - var baseConfig = { - node: { - network: bitcore.Networks.testnet - }, - spawn: { - datadir: 'testdir', - exec: 'testpath' - } - }; - - describe('@constructor', function() { - it('will create an instance', function() { - var bitcoind = new BitcoinService(baseConfig); - should.exist(bitcoind); - }); - it('will create an instance without `new`', function() { - var bitcoind = BitcoinService(baseConfig); - should.exist(bitcoind); - }); - it('will init caches', function() { - var bitcoind = new BitcoinService(baseConfig); - should.exist(bitcoind.utxosCache); - should.exist(bitcoind.txidsCache); - should.exist(bitcoind.balanceCache); - should.exist(bitcoind.summaryCache); - should.exist(bitcoind.transactionDetailedCache); - - should.exist(bitcoind.transactionCache); - should.exist(bitcoind.rawTransactionCache); - should.exist(bitcoind.blockCache); - should.exist(bitcoind.rawBlockCache); - should.exist(bitcoind.blockHeaderCache); - should.exist(bitcoind.zmqKnownTransactions); - should.exist(bitcoind.zmqKnownBlocks); - should.exist(bitcoind.lastTip); - should.exist(bitcoind.lastTipTimeout); - }); - it('will init clients', function() { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.nodes.should.deep.equal([]); - bitcoind.nodesIndex.should.equal(0); - bitcoind.nodes.push({client: sinon.stub()}); - should.exist(bitcoind.client); - }); - it('will set subscriptions', function() { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.subscriptions.should.deep.equal({ - address: {}, - rawtransaction: [], - hashblock: [] - }); - }); - }); - - describe('#_initDefaults', function() { - it('will set transaction concurrency', function() { - var bitcoind = new BitcoinService(baseConfig); - bitcoind._initDefaults({transactionConcurrency: 10}); - bitcoind.transactionConcurrency.should.equal(10); - bitcoind._initDefaults({}); - bitcoind.transactionConcurrency.should.equal(5); - }); - }); - - describe('@dependencies', function() { - it('will have no dependencies', function() { - BitcoinService.dependencies.should.deep.equal([]); - }); - }); - - describe('#getAPIMethods', function() { - it('will return spec', function() { - var bitcoind = new BitcoinService(baseConfig); - var methods = bitcoind.getAPIMethods(); - should.exist(methods); - methods.length.should.equal(21); - }); - }); - - describe('#getPublishEvents', function() { - it('will return spec', function() { - var bitcoind = new BitcoinService(baseConfig); - var events = bitcoind.getPublishEvents(); - should.exist(events); - events.length.should.equal(3); - events[0].name.should.equal('bitcoind/rawtransaction'); - events[0].scope.should.equal(bitcoind); - events[0].subscribe.should.be.a('function'); - events[0].unsubscribe.should.be.a('function'); - events[1].name.should.equal('bitcoind/hashblock'); - events[1].scope.should.equal(bitcoind); - events[1].subscribe.should.be.a('function'); - events[1].unsubscribe.should.be.a('function'); - events[2].name.should.equal('bitcoind/addresstxid'); - events[2].scope.should.equal(bitcoind); - events[2].subscribe.should.be.a('function'); - events[2].unsubscribe.should.be.a('function'); - }); - it('will call subscribe/unsubscribe with correct args', function() { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.subscribe = sinon.stub(); - bitcoind.unsubscribe = sinon.stub(); - var events = bitcoind.getPublishEvents(); - - events[0].subscribe('test'); - bitcoind.subscribe.args[0][0].should.equal('rawtransaction'); - bitcoind.subscribe.args[0][1].should.equal('test'); - - events[0].unsubscribe('test'); - bitcoind.unsubscribe.args[0][0].should.equal('rawtransaction'); - bitcoind.unsubscribe.args[0][1].should.equal('test'); - - events[1].subscribe('test'); - bitcoind.subscribe.args[1][0].should.equal('hashblock'); - bitcoind.subscribe.args[1][1].should.equal('test'); - - events[1].unsubscribe('test'); - bitcoind.unsubscribe.args[1][0].should.equal('hashblock'); - bitcoind.unsubscribe.args[1][1].should.equal('test'); - }); - }); - - describe('#subscribe', function() { - var sandbox = sinon.sandbox.create(); - beforeEach(function() { - sandbox.stub(log, 'info'); - }); - afterEach(function() { - sandbox.restore(); - }); - it('will push to subscriptions', function() { - var bitcoind = new BitcoinService(baseConfig); - var emitter = {}; - bitcoind.subscribe('hashblock', emitter); - bitcoind.subscriptions.hashblock[0].should.equal(emitter); - - var emitter2 = {}; - bitcoind.subscribe('rawtransaction', emitter2); - bitcoind.subscriptions.rawtransaction[0].should.equal(emitter2); - }); - }); - - describe('#unsubscribe', function() { - var sandbox = sinon.sandbox.create(); - beforeEach(function() { - sandbox.stub(log, 'info'); - }); - afterEach(function() { - sandbox.restore(); - }); - it('will remove item from subscriptions', function() { - var bitcoind = new BitcoinService(baseConfig); - var emitter1 = {}; - var emitter2 = {}; - var emitter3 = {}; - var emitter4 = {}; - var emitter5 = {}; - bitcoind.subscribe('hashblock', emitter1); - bitcoind.subscribe('hashblock', emitter2); - bitcoind.subscribe('hashblock', emitter3); - bitcoind.subscribe('hashblock', emitter4); - bitcoind.subscribe('hashblock', emitter5); - bitcoind.subscriptions.hashblock.length.should.equal(5); - - bitcoind.unsubscribe('hashblock', emitter3); - bitcoind.subscriptions.hashblock.length.should.equal(4); - bitcoind.subscriptions.hashblock[0].should.equal(emitter1); - bitcoind.subscriptions.hashblock[1].should.equal(emitter2); - bitcoind.subscriptions.hashblock[2].should.equal(emitter4); - bitcoind.subscriptions.hashblock[3].should.equal(emitter5); - }); - it('will not remove item an already unsubscribed item', function() { - var bitcoind = new BitcoinService(baseConfig); - var emitter1 = {}; - var emitter3 = {}; - bitcoind.subscriptions.hashblock= [emitter1]; - bitcoind.unsubscribe('hashblock', emitter3); - bitcoind.subscriptions.hashblock.length.should.equal(1); - bitcoind.subscriptions.hashblock[0].should.equal(emitter1); - }); - }); - - describe('#subscribeAddress', function() { - var sandbox = sinon.sandbox.create(); - beforeEach(function() { - sandbox.stub(log, 'info'); - }); - afterEach(function() { - sandbox.restore(); - }); - it('will not an invalid address', function() { - var bitcoind = new BitcoinService(baseConfig); - var emitter = new EventEmitter(); - bitcoind.subscribeAddress(emitter, ['invalidaddress']); - should.not.exist(bitcoind.subscriptions.address['invalidaddress']); - }); - it('will add a valid address', function() { - var bitcoind = new BitcoinService(baseConfig); - var emitter = new EventEmitter(); - bitcoind.subscribeAddress(emitter, ['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br']); - should.exist(bitcoind.subscriptions.address['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br']); - }); - it('will handle multiple address subscribers', function() { - var bitcoind = new BitcoinService(baseConfig); - var emitter1 = new EventEmitter(); - var emitter2 = new EventEmitter(); - bitcoind.subscribeAddress(emitter1, ['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br']); - bitcoind.subscribeAddress(emitter2, ['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br']); - should.exist(bitcoind.subscriptions.address['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br']); - bitcoind.subscriptions.address['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br'].length.should.equal(2); - }); - it('will not add the same emitter twice', function() { - var bitcoind = new BitcoinService(baseConfig); - var emitter1 = new EventEmitter(); - bitcoind.subscribeAddress(emitter1, ['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br']); - bitcoind.subscribeAddress(emitter1, ['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br']); - should.exist(bitcoind.subscriptions.address['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br']); - bitcoind.subscriptions.address['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br'].length.should.equal(1); - }); - }); - - describe('#unsubscribeAddress', function() { - var sandbox = sinon.sandbox.create(); - beforeEach(function() { - sandbox.stub(log, 'info'); - }); - afterEach(function() { - sandbox.restore(); - }); - it('it will remove a subscription', function() { - var bitcoind = new BitcoinService(baseConfig); - var emitter1 = new EventEmitter(); - var emitter2 = new EventEmitter(); - bitcoind.subscribeAddress(emitter1, ['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br']); - bitcoind.subscribeAddress(emitter2, ['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br']); - should.exist(bitcoind.subscriptions.address['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br']); - bitcoind.subscriptions.address['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br'].length.should.equal(2); - bitcoind.unsubscribeAddress(emitter1, ['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br']); - bitcoind.subscriptions.address['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br'].length.should.equal(1); - }); - it('will unsubscribe subscriptions for an emitter', function() { - var bitcoind = new BitcoinService(baseConfig); - var emitter1 = new EventEmitter(); - var emitter2 = new EventEmitter(); - bitcoind.subscriptions.address['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br'] = [emitter1, emitter2]; - bitcoind.unsubscribeAddress(emitter1); - bitcoind.subscriptions.address['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br'].length.should.equal(1); - }); - it('will NOT unsubscribe subscription with missing address', function() { - var bitcoind = new BitcoinService(baseConfig); - var emitter1 = new EventEmitter(); - var emitter2 = new EventEmitter(); - bitcoind.subscriptions.address['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br'] = [emitter1, emitter2]; - bitcoind.unsubscribeAddress(emitter1, ['1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo']); - bitcoind.subscriptions.address['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br'].length.should.equal(2); - }); - it('will NOT unsubscribe subscription with missing emitter', function() { - var bitcoind = new BitcoinService(baseConfig); - var emitter1 = new EventEmitter(); - var emitter2 = new EventEmitter(); - bitcoind.subscriptions.address['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br'] = [emitter2]; - bitcoind.unsubscribeAddress(emitter1, ['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br']); - bitcoind.subscriptions.address['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br'].length.should.equal(1); - bitcoind.subscriptions.address['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br'][0].should.equal(emitter2); - }); - it('will remove empty addresses', function() { - var bitcoind = new BitcoinService(baseConfig); - var emitter1 = new EventEmitter(); - var emitter2 = new EventEmitter(); - bitcoind.subscriptions.address['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br'] = [emitter1, emitter2]; - bitcoind.unsubscribeAddress(emitter1, ['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br']); - bitcoind.unsubscribeAddress(emitter2, ['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br']); - should.not.exist(bitcoind.subscriptions.address['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br']); - }); - it('will unsubscribe emitter for all addresses', function() { - var bitcoind = new BitcoinService(baseConfig); - var emitter1 = new EventEmitter(); - var emitter2 = new EventEmitter(); - bitcoind.subscriptions.address['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br'] = [emitter1, emitter2]; - bitcoind.subscriptions.address['1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo'] = [emitter1, emitter2]; - sinon.spy(bitcoind, 'unsubscribeAddressAll'); - bitcoind.unsubscribeAddress(emitter1); - bitcoind.unsubscribeAddressAll.callCount.should.equal(1); - bitcoind.subscriptions.address['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br'].length.should.equal(1); - bitcoind.subscriptions.address['1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo'].length.should.equal(1); - }); - }); - - describe('#unsubscribeAddressAll', function() { - var sandbox = sinon.sandbox.create(); - beforeEach(function() { - sandbox.stub(log, 'info'); - }); - afterEach(function() { - sandbox.restore(); - }); - it('will unsubscribe emitter for all addresses', function() { - var bitcoind = new BitcoinService(baseConfig); - var emitter1 = new EventEmitter(); - var emitter2 = new EventEmitter(); - bitcoind.subscriptions.address['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br'] = [emitter1, emitter2]; - bitcoind.subscriptions.address['1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo'] = [emitter1, emitter2]; - bitcoind.subscriptions.address['mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW'] = [emitter2]; - bitcoind.subscriptions.address['3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou'] = [emitter1]; - bitcoind.unsubscribeAddress(emitter1); - bitcoind.subscriptions.address['2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br'].length.should.equal(1); - bitcoind.subscriptions.address['1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo'].length.should.equal(1); - bitcoind.subscriptions.address['mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW'].length.should.equal(1); - should.not.exist(bitcoind.subscriptions.address['3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou']); - }); - }); - - describe('#_getDefaultConfig', function() { - it('will generate config file from defaults', function() { - var bitcoind = new BitcoinService(baseConfig); - var config = bitcoind._getDefaultConfig(); - config.should.equal(defaultBitcoinConf); - }); - }); - - describe('#_loadSpawnConfiguration', function() { - var sandbox = sinon.sandbox.create(); - beforeEach(function() { - sandbox.stub(log, 'info'); - }); - afterEach(function() { - sandbox.restore(); - }); - it('will parse a bitcoin.conf file', function() { - var TestBitcoin = proxyquire('../../lib/services/bitcoind', { - fs: { - readFileSync: readFileSync, - existsSync: sinon.stub().returns(true), - writeFileSync: sinon.stub() - }, - mkdirp: { - sync: sinon.stub() - } - }); - var bitcoind = new TestBitcoin(baseConfig); - bitcoind.options.spawn.datadir = '/tmp/.bitcoin'; - var node = {}; - bitcoind._loadSpawnConfiguration(node); - should.exist(bitcoind.spawn.config); - bitcoind.spawn.config.should.deep.equal({ - addressindex: 1, - checkblocks: 144, - dbcache: 8192, - maxuploadtarget: 1024, - port: 20000, - rpcport: 50001, - rpcallowip: '127.0.0.1', - rpcuser: 'bitcoin', - rpcpassword: 'local321', - server: 1, - spentindex: 1, - timestampindex: 1, - txindex: 1, - upnp: 0, - whitelist: '127.0.0.1', - zmqpubhashblock: 'tcp://127.0.0.1:28332', - zmqpubrawtx: 'tcp://127.0.0.1:28332' - }); - }); - it('will expand relative datadir to absolute path', function() { - var TestBitcoin = proxyquire('../../lib/services/bitcoind', { - fs: { - readFileSync: readFileSync, - existsSync: sinon.stub().returns(true), - writeFileSync: sinon.stub() - }, - mkdirp: { - sync: sinon.stub() - } - }); - var config = { - node: { - network: bitcore.Networks.testnet, - configPath: '/tmp/.bitcore/bitcore-node.json' - }, - spawn: { - datadir: './data', - exec: 'testpath' - } - }; - var bitcoind = new TestBitcoin(config); - bitcoind.options.spawn.datadir = './data'; - var node = {}; - bitcoind._loadSpawnConfiguration(node); - bitcoind.options.spawn.datadir.should.equal('/tmp/.bitcore/data'); - }); - it('should throw an exception if txindex isn\'t enabled in the configuration', function() { - var TestBitcoin = proxyquire('../../lib/services/bitcoind', { - fs: { - readFileSync: sinon.stub().returns(fs.readFileSync(__dirname + '/../data/badbitcoin.conf')), - existsSync: sinon.stub().returns(true), - }, - mkdirp: { - sync: sinon.stub() - } - }); - var bitcoind = new TestBitcoin(baseConfig); - (function() { - bitcoind._loadSpawnConfiguration({datadir: './test'}); - }).should.throw(bitcore.errors.InvalidState); - }); - it('should NOT set https options if node https options are set', function() { - var writeFileSync = function(path, config) { - config.should.equal(defaultBitcoinConf); - }; - var TestBitcoin = proxyquire('../../lib/services/bitcoind', { - fs: { - writeFileSync: writeFileSync, - readFileSync: readFileSync, - existsSync: sinon.stub().returns(false) - }, - mkdirp: { - sync: sinon.stub() - } - }); - var config = { - node: { - network: { - name: 'regtest' - }, - https: true, - httpsOptions: { - key: 'key.pem', - cert: 'cert.pem' - } - }, - spawn: { - datadir: 'testdir', - exec: 'testexec' - } - }; - var bitcoind = new TestBitcoin(config); - bitcoind.options.spawn.datadir = '/tmp/.bitcoin'; - var node = {}; - bitcoind._loadSpawnConfiguration(node); - }); - }); - - describe('#_checkConfigIndexes', function() { - var sandbox = sinon.sandbox.create(); - beforeEach(function() { - sandbox.stub(log, 'warn'); - }); - afterEach(function() { - sandbox.restore(); - }); - it('should warn the user if reindex is set to 1 in the bitcoin.conf file', function() { - var bitcoind = new BitcoinService(baseConfig); - var config = { - txindex: 1, - addressindex: 1, - spentindex: 1, - server: 1, - zmqpubrawtx: 1, - zmqpubhashblock: 1, - reindex: 1 - }; - var node = {}; - bitcoind._checkConfigIndexes(config, node); - log.warn.callCount.should.equal(1); - node._reindex.should.equal(true); - }); - it('should warn if zmq port and hosts do not match', function() { - var bitcoind = new BitcoinService(baseConfig); - var config = { - txindex: 1, - addressindex: 1, - spentindex: 1, - server: 1, - zmqpubrawtx: 'tcp://127.0.0.1:28332', - zmqpubhashblock: 'tcp://127.0.0.1:28331', - reindex: 1 - }; - var node = {}; - (function() { - bitcoind._checkConfigIndexes(config, node); - }).should.throw('"zmqpubrawtx" and "zmqpubhashblock"'); - }); - }); - - describe('#_resetCaches', function() { - it('will reset LRU caches', function() { - var bitcoind = new BitcoinService(baseConfig); - var keys = []; - for (var i = 0; i < 10; i++) { - keys.push(crypto.randomBytes(32)); - bitcoind.transactionDetailedCache.set(keys[i], {}); - bitcoind.utxosCache.set(keys[i], {}); - bitcoind.txidsCache.set(keys[i], {}); - bitcoind.balanceCache.set(keys[i], {}); - bitcoind.summaryCache.set(keys[i], {}); - } - bitcoind._resetCaches(); - should.equal(bitcoind.transactionDetailedCache.get(keys[0]), undefined); - should.equal(bitcoind.utxosCache.get(keys[0]), undefined); - should.equal(bitcoind.txidsCache.get(keys[0]), undefined); - should.equal(bitcoind.balanceCache.get(keys[0]), undefined); - should.equal(bitcoind.summaryCache.get(keys[0]), undefined); - }); - }); - - describe('#_tryAllClients', function() { - it('will retry for each node client', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.tryAllInterval = 1; - bitcoind.nodes.push({ - client: { - getInfo: sinon.stub().callsArgWith(0, new Error('test')) - } - }); - bitcoind.nodes.push({ - client: { - getInfo: sinon.stub().callsArgWith(0, new Error('test')) - } - }); - bitcoind.nodes.push({ - client: { - getInfo: sinon.stub().callsArg(0) - } - }); - bitcoind._tryAllClients(function(client, next) { - client.getInfo(next); - }, function(err) { - if (err) { - return done(err); - } - bitcoind.nodes[0].client.getInfo.callCount.should.equal(1); - bitcoind.nodes[1].client.getInfo.callCount.should.equal(1); - bitcoind.nodes[2].client.getInfo.callCount.should.equal(1); - done(); - }); - }); - it('will start using the current node index (round-robin)', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.tryAllInterval = 1; - bitcoind.nodes.push({ - client: { - getInfo: sinon.stub().callsArgWith(0, new Error('2')) - } - }); - bitcoind.nodes.push({ - client: { - getInfo: sinon.stub().callsArgWith(0, new Error('3')) - } - }); - bitcoind.nodes.push({ - client: { - getInfo: sinon.stub().callsArgWith(0, new Error('1')) - } - }); - bitcoind.nodesIndex = 2; - bitcoind._tryAllClients(function(client, next) { - client.getInfo(next); - }, function(err) { - err.should.be.instanceOf(Error); - err.message.should.equal('3'); - bitcoind.nodes[0].client.getInfo.callCount.should.equal(1); - bitcoind.nodes[1].client.getInfo.callCount.should.equal(1); - bitcoind.nodes[2].client.getInfo.callCount.should.equal(1); - bitcoind.nodesIndex.should.equal(2); - done(); - }); - }); - it('will get error if all clients fail', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.tryAllInterval = 1; - bitcoind.nodes.push({ - client: { - getInfo: sinon.stub().callsArgWith(0, new Error('test')) - } - }); - bitcoind.nodes.push({ - client: { - getInfo: sinon.stub().callsArgWith(0, new Error('test')) - } - }); - bitcoind.nodes.push({ - client: { - getInfo: sinon.stub().callsArgWith(0, new Error('test')) - } - }); - bitcoind._tryAllClients(function(client, next) { - client.getInfo(next); - }, function(err) { - should.exist(err); - err.should.be.instanceOf(Error); - err.message.should.equal('test'); - done(); - }); - }); - }); - - describe('#_wrapRPCError', function() { - it('will convert bitcoind-rpc error object into JavaScript error', function() { - var bitcoind = new BitcoinService(baseConfig); - var error = bitcoind._wrapRPCError({message: 'Test error', code: -1}); - error.should.be.an.instanceof(errors.RPCError); - error.code.should.equal(-1); - error.message.should.equal('Test error'); - }); - }); - - describe('#_initChain', function() { - var sandbox = sinon.sandbox.create(); - beforeEach(function() { - sandbox.stub(log, 'info'); - }); - afterEach(function() { - sandbox.restore(); - }); - it('will set height and genesis buffer', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var genesisBuffer = new Buffer([]); - bitcoind.getRawBlock = sinon.stub().callsArgWith(1, null, genesisBuffer); - bitcoind.nodes.push({ - client: { - getBestBlockHash: function(callback) { - callback(null, { - result: 'bestblockhash' - }); - }, - getBlock: function(hash, callback) { - if (hash === 'bestblockhash') { - callback(null, { - result: { - height: 5000 - } - }); - } - }, - getBlockHash: function(num, callback) { - callback(null, { - result: 'genesishash' - }); - } - } - }); - bitcoind._initChain(function() { - log.info.callCount.should.equal(1); - bitcoind.getRawBlock.callCount.should.equal(1); - bitcoind.getRawBlock.args[0][0].should.equal('genesishash'); - bitcoind.height.should.equal(5000); - bitcoind.genesisBuffer.should.equal(genesisBuffer); - done(); - }); - }); - it('it will handle error from getBestBlockHash', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getBestBlockHash = sinon.stub().callsArgWith(0, {code: -1, message: 'error'}); - bitcoind.nodes.push({ - client: { - getBestBlockHash: getBestBlockHash - } - }); - bitcoind._initChain(function(err) { - err.should.be.instanceOf(Error); - done(); - }); - }); - it('it will handle error from getBlock', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getBestBlockHash = sinon.stub().callsArgWith(0, null, {}); - var getBlock = sinon.stub().callsArgWith(1, {code: -1, message: 'error'}); - bitcoind.nodes.push({ - client: { - getBestBlockHash: getBestBlockHash, - getBlock: getBlock - } - }); - bitcoind._initChain(function(err) { - err.should.be.instanceOf(Error); - done(); - }); - }); - it('it will handle error from getBlockHash', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getBestBlockHash = sinon.stub().callsArgWith(0, null, {}); - var getBlock = sinon.stub().callsArgWith(1, null, { - result: { - height: 10 - } - }); - var getBlockHash = sinon.stub().callsArgWith(1, {code: -1, message: 'error'}); - bitcoind.nodes.push({ - client: { - getBestBlockHash: getBestBlockHash, - getBlock: getBlock, - getBlockHash: getBlockHash - } - }); - bitcoind._initChain(function(err) { - err.should.be.instanceOf(Error); - done(); - }); - }); - it('it will handle error from getRawBlock', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getBestBlockHash = sinon.stub().callsArgWith(0, null, {}); - var getBlock = sinon.stub().callsArgWith(1, null, { - result: { - height: 10 - } - }); - var getBlockHash = sinon.stub().callsArgWith(1, null, {}); - bitcoind.nodes.push({ - client: { - getBestBlockHash: getBestBlockHash, - getBlock: getBlock, - getBlockHash: getBlockHash - } - }); - bitcoind.getRawBlock = sinon.stub().callsArgWith(1, new Error('test')); - bitcoind._initChain(function(err) { - err.should.be.instanceOf(Error); - done(); - }); - }); - }); - - describe('#_getDefaultConf', function() { - afterEach(function() { - bitcore.Networks.disableRegtest(); - baseConfig.node.network = bitcore.Networks.testnet; - }); - it('will get default rpc port for livenet', function() { - var config = { - node: { - network: bitcore.Networks.livenet - }, - spawn: { - datadir: 'testdir', - exec: 'testpath' - } - }; - var bitcoind = new BitcoinService(config); - bitcoind._getDefaultConf().rpcport.should.equal(8332); - }); - it('will get default rpc port for testnet', function() { - var config = { - node: { - network: bitcore.Networks.testnet - }, - spawn: { - datadir: 'testdir', - exec: 'testpath' - } - }; - var bitcoind = new BitcoinService(config); - bitcoind._getDefaultConf().rpcport.should.equal(18332); - }); - it('will get default rpc port for regtest', function() { - bitcore.Networks.enableRegtest(); - var config = { - node: { - network: bitcore.Networks.testnet - }, - spawn: { - datadir: 'testdir', - exec: 'testpath' - } - }; - var bitcoind = new BitcoinService(config); - bitcoind._getDefaultConf().rpcport.should.equal(18332); - }); - }); - - describe('#_getNetworkConfigPath', function() { - afterEach(function() { - bitcore.Networks.disableRegtest(); - baseConfig.node.network = bitcore.Networks.testnet; - }); - it('will get default config path for livenet', function() { - var config = { - node: { - network: bitcore.Networks.livenet - }, - spawn: { - datadir: 'testdir', - exec: 'testpath' - } - }; - var bitcoind = new BitcoinService(config); - should.equal(bitcoind._getNetworkConfigPath(), undefined); - }); - it('will get default rpc port for testnet', function() { - var config = { - node: { - network: bitcore.Networks.testnet - }, - spawn: { - datadir: 'testdir', - exec: 'testpath' - } - }; - var bitcoind = new BitcoinService(config); - bitcoind._getNetworkConfigPath().should.equal('testnet3/bitcoin.conf'); - }); - it('will get default rpc port for regtest', function() { - bitcore.Networks.enableRegtest(); - var config = { - node: { - network: bitcore.Networks.testnet - }, - spawn: { - datadir: 'testdir', - exec: 'testpath' - } - }; - var bitcoind = new BitcoinService(config); - bitcoind._getNetworkConfigPath().should.equal('regtest/bitcoin.conf'); - }); - }); - - describe('#_getNetworkOption', function() { - afterEach(function() { - bitcore.Networks.disableRegtest(); - baseConfig.node.network = bitcore.Networks.testnet; - }); - it('return --testnet for testnet', function() { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.node.network = bitcore.Networks.testnet; - bitcoind._getNetworkOption().should.equal('--testnet'); - }); - it('return --regtest for testnet', function() { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.node.network = bitcore.Networks.testnet; - bitcore.Networks.enableRegtest(); - bitcoind._getNetworkOption().should.equal('--regtest'); - }); - it('return undefined for livenet', function() { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.node.network = bitcore.Networks.livenet; - bitcore.Networks.enableRegtest(); - should.equal(bitcoind._getNetworkOption(), undefined); - }); - }); - - describe('#_zmqBlockHandler', function() { - it('will emit block', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var node = {}; - var message = new Buffer('00000000002e08fc7ae9a9aa5380e95e2adcdc5752a4a66a7d3a22466bd4e6aa', 'hex'); - bitcoind._rapidProtectedUpdateTip = sinon.stub(); - bitcoind.on('block', function(block) { - block.should.equal(message); - done(); - }); - bitcoind._zmqBlockHandler(node, message); - }); - it('will not emit same block twice', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var node = {}; - var message = new Buffer('00000000002e08fc7ae9a9aa5380e95e2adcdc5752a4a66a7d3a22466bd4e6aa', 'hex'); - bitcoind._rapidProtectedUpdateTip = sinon.stub(); - bitcoind.on('block', function(block) { - block.should.equal(message); - done(); - }); - bitcoind._zmqBlockHandler(node, message); - bitcoind._zmqBlockHandler(node, message); - }); - it('will call function to update tip', function() { - var bitcoind = new BitcoinService(baseConfig); - var node = {}; - var message = new Buffer('00000000002e08fc7ae9a9aa5380e95e2adcdc5752a4a66a7d3a22466bd4e6aa', 'hex'); - bitcoind._rapidProtectedUpdateTip = sinon.stub(); - bitcoind._zmqBlockHandler(node, message); - bitcoind._rapidProtectedUpdateTip.callCount.should.equal(1); - bitcoind._rapidProtectedUpdateTip.args[0][0].should.equal(node); - bitcoind._rapidProtectedUpdateTip.args[0][1].should.equal(message); - }); - it('will emit to subscribers', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var node = {}; - var message = new Buffer('00000000002e08fc7ae9a9aa5380e95e2adcdc5752a4a66a7d3a22466bd4e6aa', 'hex'); - bitcoind._rapidProtectedUpdateTip = sinon.stub(); - var emitter = new EventEmitter(); - bitcoind.subscriptions.hashblock.push(emitter); - emitter.on('bitcoind/hashblock', function(blockHash) { - blockHash.should.equal(message.toString('hex')); - done(); - }); - bitcoind._zmqBlockHandler(node, message); - }); - }); - - describe('#_rapidProtectedUpdateTip', function() { - it('will limit tip updates with rapid calls', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var callCount = 0; - bitcoind._updateTip = function() { - callCount++; - callCount.should.be.within(1, 2); - if (callCount > 1) { - done(); - } - }; - var node = {}; - var message = new Buffer('00000000002e08fc7ae9a9aa5380e95e2adcdc5752a4a66a7d3a22466bd4e6aa', 'hex'); - var count = 0; - function repeat() { - bitcoind._rapidProtectedUpdateTip(node, message); - count++; - if (count < 50) { - repeat(); - } - } - repeat(); - }); - }); - - describe('#_updateTip', function() { - var sandbox = sinon.sandbox.create(); - var message = new Buffer('00000000002e08fc7ae9a9aa5380e95e2adcdc5752a4a66a7d3a22466bd4e6aa', 'hex'); - beforeEach(function() { - sandbox.stub(log, 'error'); - sandbox.stub(log, 'info'); - }); - afterEach(function() { - sandbox.restore(); - }); - it('log and emit rpc error from get block', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.syncPercentage = sinon.stub(); - bitcoind.on('error', function(err) { - err.code.should.equal(-1); - err.message.should.equal('Test error'); - log.error.callCount.should.equal(1); - done(); - }); - var node = { - client: { - getBlock: sinon.stub().callsArgWith(1, {message: 'Test error', code: -1}) - } - }; - bitcoind._updateTip(node, message); - }); - it('emit synced if percentage is 100', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.syncPercentage = sinon.stub().callsArgWith(0, null, 100); - bitcoind.on('synced', function() { - done(); - }); - var node = { - client: { - getBlock: sinon.stub() - } - }; - bitcoind._updateTip(node, message); - }); - it('NOT emit synced if percentage is less than 100', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.syncPercentage = sinon.stub().callsArgWith(0, null, 99); - bitcoind.on('synced', function() { - throw new Error('Synced called'); - }); - var node = { - client: { - getBlock: sinon.stub() - } - }; - bitcoind._updateTip(node, message); - log.info.callCount.should.equal(1); - done(); - }); - it('log and emit error from syncPercentage', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.syncPercentage = sinon.stub().callsArgWith(0, new Error('test')); - bitcoind.on('error', function(err) { - log.error.callCount.should.equal(1); - err.message.should.equal('test'); - done(); - }); - var node = { - client: { - getBlock: sinon.stub() - } - }; - bitcoind._updateTip(node, message); - }); - it('reset caches and set height', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.syncPercentage = sinon.stub(); - bitcoind._resetCaches = sinon.stub(); - bitcoind.on('tip', function(height) { - bitcoind._resetCaches.callCount.should.equal(1); - height.should.equal(10); - bitcoind.height.should.equal(10); - done(); - }); - var node = { - client: { - getBlock: sinon.stub().callsArgWith(1, null, { - result: { - height: 10 - } - }) - } - }; - bitcoind._updateTip(node, message); - }); - it('will NOT update twice for the same hash', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.syncPercentage = sinon.stub(); - bitcoind._resetCaches = sinon.stub(); - bitcoind.on('tip', function() { - done(); - }); - var node = { - client: { - getBlock: sinon.stub().callsArgWith(1, null, { - result: { - height: 10 - } - }) - } - }; - bitcoind._updateTip(node, message); - bitcoind._updateTip(node, message); - }); - it('will not call syncPercentage if node is stopping', function(done) { - var config = { - node: { - network: bitcore.Networks.testnet - }, - spawn: { - datadir: 'testdir', - exec: 'testpath' - } - }; - var bitcoind = new BitcoinService(config); - bitcoind.syncPercentage = sinon.stub(); - bitcoind._resetCaches = sinon.stub(); - bitcoind.node.stopping = true; - var node = { - client: { - getBlock: sinon.stub().callsArgWith(1, null, { - result: { - height: 10 - } - }) - } - }; - bitcoind.on('tip', function() { - bitcoind.syncPercentage.callCount.should.equal(0); - done(); - }); - bitcoind._updateTip(node, message); - }); - }); - - describe('#_getAddressesFromTransaction', function() { - it('will get results using bitcore.Transaction', function() { - var bitcoind = new BitcoinService(baseConfig); - var wif = 'L2Gkw3kKJ6N24QcDuH4XDqt9cTqsKTVNDGz1CRZhk9cq4auDUbJy'; - var privkey = bitcore.PrivateKey.fromWIF(wif); - var inputAddress = privkey.toAddress(bitcore.Networks.testnet); - var outputAddress = bitcore.Address('2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br'); - var tx = bitcore.Transaction(); - tx.from({ - txid: '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b', - outputIndex: 0, - script: bitcore.Script(inputAddress), - address: inputAddress.toString(), - satoshis: 5000000000 - }); - tx.to(outputAddress, 5000000000); - tx.sign(privkey); - var addresses = bitcoind._getAddressesFromTransaction(tx); - addresses.length.should.equal(2); - addresses[0].should.equal(inputAddress.toString()); - addresses[1].should.equal(outputAddress.toString()); - }); - it('will handle non-standard script types', function() { - var bitcoind = new BitcoinService(baseConfig); - var tx = bitcore.Transaction(); - tx.addInput(bitcore.Transaction.Input({ - prevTxId: '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b', - script: bitcore.Script('OP_TRUE'), - outputIndex: 1, - output: { - script: bitcore.Script('OP_TRUE'), - satoshis: 5000000000 - } - })); - tx.addOutput(bitcore.Transaction.Output({ - script: bitcore.Script('OP_TRUE'), - satoshis: 5000000000 - })); - var addresses = bitcoind._getAddressesFromTransaction(tx); - addresses.length.should.equal(0); - }); - it('will handle unparsable script types or missing input script', function() { - var bitcoind = new BitcoinService(baseConfig); - var tx = bitcore.Transaction(); - tx.addOutput(bitcore.Transaction.Output({ - script: new Buffer('4c', 'hex'), - satoshis: 5000000000 - })); - var addresses = bitcoind._getAddressesFromTransaction(tx); - addresses.length.should.equal(0); - }); - it('will return unique values', function() { - var bitcoind = new BitcoinService(baseConfig); - var tx = bitcore.Transaction(); - var address = bitcore.Address('2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br'); - tx.addOutput(bitcore.Transaction.Output({ - script: bitcore.Script(address), - satoshis: 5000000000 - })); - tx.addOutput(bitcore.Transaction.Output({ - script: bitcore.Script(address), - satoshis: 5000000000 - })); - var addresses = bitcoind._getAddressesFromTransaction(tx); - addresses.length.should.equal(1); - }); - }); - - describe('#_notifyAddressTxidSubscribers', function() { - it('will emit event if matching addresses', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var address = '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo'; - bitcoind._getAddressesFromTransaction = sinon.stub().returns([address]); - var emitter = new EventEmitter(); - bitcoind.subscriptions.address[address] = [emitter]; - var txid = '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0'; - var transaction = {}; - emitter.on('bitcoind/addresstxid', function(data) { - data.address.should.equal(address); - data.txid.should.equal(txid); - done(); - }); - sinon.spy(emitter, 'emit'); - bitcoind._notifyAddressTxidSubscribers(txid, transaction); - emitter.emit.callCount.should.equal(1); - }); - it('will NOT emit event without matching addresses', function() { - var bitcoind = new BitcoinService(baseConfig); - var address = '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo'; - bitcoind._getAddressesFromTransaction = sinon.stub().returns([address]); - var emitter = new EventEmitter(); - var txid = '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0'; - var transaction = {}; - emitter.emit = sinon.stub(); - bitcoind._notifyAddressTxidSubscribers(txid, transaction); - emitter.emit.callCount.should.equal(0); - }); - }); - - describe('#_zmqTransactionHandler', function() { - it('will emit to subscribers', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var expectedBuffer = new Buffer(txhex, 'hex'); - var emitter = new EventEmitter(); - bitcoind.subscriptions.rawtransaction.push(emitter); - emitter.on('bitcoind/rawtransaction', function(hex) { - hex.should.be.a('string'); - hex.should.equal(expectedBuffer.toString('hex')); - done(); - }); - var node = {}; - bitcoind._zmqTransactionHandler(node, expectedBuffer); - }); - it('will NOT emit to subscribers more than once for the same tx', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var expectedBuffer = new Buffer(txhex, 'hex'); - var emitter = new EventEmitter(); - bitcoind.subscriptions.rawtransaction.push(emitter); - emitter.on('bitcoind/rawtransaction', function() { - done(); - }); - var node = {}; - bitcoind._zmqTransactionHandler(node, expectedBuffer); - bitcoind._zmqTransactionHandler(node, expectedBuffer); - }); - it('will emit "tx" event', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var expectedBuffer = new Buffer(txhex, 'hex'); - bitcoind.on('tx', function(buffer) { - buffer.should.be.instanceof(Buffer); - buffer.toString('hex').should.equal(expectedBuffer.toString('hex')); - done(); - }); - var node = {}; - bitcoind._zmqTransactionHandler(node, expectedBuffer); - }); - it('will NOT emit "tx" event more than once for the same tx', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var expectedBuffer = new Buffer(txhex, 'hex'); - bitcoind.on('tx', function() { - done(); - }); - var node = {}; - bitcoind._zmqTransactionHandler(node, expectedBuffer); - bitcoind._zmqTransactionHandler(node, expectedBuffer); - }); - }); - - describe('#_checkSyncedAndSubscribeZmqEvents', function() { - var sandbox = sinon.sandbox.create(); - before(function() { - sandbox.stub(log, 'error'); - }); - after(function() { - sandbox.restore(); - }); - it('log errors, update tip and subscribe to zmq events', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind._updateTip = sinon.stub(); - bitcoind._subscribeZmqEvents = sinon.stub(); - var blockEvents = 0; - bitcoind.on('block', function() { - blockEvents++; - }); - var getBestBlockHash = sinon.stub().callsArgWith(0, null, { - result: '00000000000000001bb82a7f5973618cfd3185ba1ded04dd852a653f92a27c45' - }); - getBestBlockHash.onCall(0).callsArgWith(0, {code: -1 , message: 'Test error'}); - var progress = 0.90; - function getProgress() { - progress = progress + 0.01; - return progress; - } - var info = {}; - Object.defineProperty(info, 'result', { - get: function() { - return { - verificationprogress: getProgress() - }; - } - }); - var getBlockchainInfo = sinon.stub().callsArgWith(0, null, info); - getBlockchainInfo.onCall(0).callsArgWith(0, {code: -1, message: 'Test error'}); - var node = { - _reindex: true, - _reindexWait: 1, - _tipUpdateInterval: 1, - client: { - getBestBlockHash: getBestBlockHash, - getBlockchainInfo: getBlockchainInfo - } - }; - bitcoind._checkSyncedAndSubscribeZmqEvents(node); - setTimeout(function() { - log.error.callCount.should.equal(2); - blockEvents.should.equal(11); - bitcoind._updateTip.callCount.should.equal(11); - bitcoind._subscribeZmqEvents.callCount.should.equal(1); - done(); - }, 200); - }); - it('it will clear interval if node is stopping', function(done) { - var config = { - node: { - network: bitcore.Networks.testnet - }, - spawn: { - datadir: 'testdir', - exec: 'testpath' - } - }; - var bitcoind = new BitcoinService(config); - var getBestBlockHash = sinon.stub().callsArgWith(0, {code: -1, message: 'error'}); - var node = { - _tipUpdateInterval: 1, - client: { - getBestBlockHash: getBestBlockHash - } - }; - bitcoind._checkSyncedAndSubscribeZmqEvents(node); - setTimeout(function() { - bitcoind.node.stopping = true; - var count = getBestBlockHash.callCount; - setTimeout(function() { - getBestBlockHash.callCount.should.equal(count); - done(); - }, 100); - }, 100); - }); - it('will not set interval if synced is true', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind._updateTip = sinon.stub(); - bitcoind._subscribeZmqEvents = sinon.stub(); - var getBestBlockHash = sinon.stub().callsArgWith(0, null, { - result: '00000000000000001bb82a7f5973618cfd3185ba1ded04dd852a653f92a27c45' - }); - var info = { - result: { - verificationprogress: 1.00 - } - }; - var getBlockchainInfo = sinon.stub().callsArgWith(0, null, info); - var node = { - _tipUpdateInterval: 1, - client: { - getBestBlockHash: getBestBlockHash, - getBlockchainInfo: getBlockchainInfo - } - }; - bitcoind._checkSyncedAndSubscribeZmqEvents(node); - setTimeout(function() { - getBestBlockHash.callCount.should.equal(1); - getBlockchainInfo.callCount.should.equal(1); - done(); - }, 200); - }); - }); - - describe('#_subscribeZmqEvents', function() { - it('will call subscribe on zmq socket', function() { - var bitcoind = new BitcoinService(baseConfig); - var node = { - zmqSubSocket: { - subscribe: sinon.stub(), - on: sinon.stub() - } - }; - bitcoind._subscribeZmqEvents(node); - node.zmqSubSocket.subscribe.callCount.should.equal(2); - node.zmqSubSocket.subscribe.args[0][0].should.equal('hashblock'); - node.zmqSubSocket.subscribe.args[1][0].should.equal('rawtx'); - }); - it('will call relevant handler for rawtx topics', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind._zmqTransactionHandler = sinon.stub(); - var node = { - zmqSubSocket: new EventEmitter() - }; - node.zmqSubSocket.subscribe = sinon.stub(); - bitcoind._subscribeZmqEvents(node); - node.zmqSubSocket.on('message', function() { - bitcoind._zmqTransactionHandler.callCount.should.equal(1); - done(); - }); - var topic = new Buffer('rawtx', 'utf8'); - var message = new Buffer('abcdef', 'hex'); - node.zmqSubSocket.emit('message', topic, message); - }); - it('will call relevant handler for hashblock topics', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind._zmqBlockHandler = sinon.stub(); - var node = { - zmqSubSocket: new EventEmitter() - }; - node.zmqSubSocket.subscribe = sinon.stub(); - bitcoind._subscribeZmqEvents(node); - node.zmqSubSocket.on('message', function() { - bitcoind._zmqBlockHandler.callCount.should.equal(1); - done(); - }); - var topic = new Buffer('hashblock', 'utf8'); - var message = new Buffer('abcdef', 'hex'); - node.zmqSubSocket.emit('message', topic, message); - }); - it('will ignore unknown topic types', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind._zmqBlockHandler = sinon.stub(); - bitcoind._zmqTransactionHandler = sinon.stub(); - var node = { - zmqSubSocket: new EventEmitter() - }; - node.zmqSubSocket.subscribe = sinon.stub(); - bitcoind._subscribeZmqEvents(node); - node.zmqSubSocket.on('message', function() { - bitcoind._zmqBlockHandler.callCount.should.equal(0); - bitcoind._zmqTransactionHandler.callCount.should.equal(0); - done(); - }); - var topic = new Buffer('unknown', 'utf8'); - var message = new Buffer('abcdef', 'hex'); - node.zmqSubSocket.emit('message', topic, message); - }); - }); - - describe('#_initZmqSubSocket', function() { - it('will setup zmq socket', function() { - var socket = new EventEmitter(); - socket.monitor = sinon.stub(); - socket.connect = sinon.stub(); - var socketFunc = function() { - return socket; - }; - var BitcoinService = proxyquire('../../lib/services/bitcoind', { - zmq: { - socket: socketFunc - } - }); - var bitcoind = new BitcoinService(baseConfig); - var node = {}; - bitcoind._initZmqSubSocket(node, 'url'); - node.zmqSubSocket.should.equal(socket); - socket.connect.callCount.should.equal(1); - socket.connect.args[0][0].should.equal('url'); - socket.monitor.callCount.should.equal(1); - socket.monitor.args[0][0].should.equal(500); - socket.monitor.args[0][1].should.equal(0); - }); - }); - - describe('#_checkReindex', function() { - var sandbox = sinon.sandbox.create(); - before(function() { - sandbox.stub(log, 'info'); - }); - after(function() { - sandbox.restore(); - }); - it('give error from client getblockchaininfo', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var node = { - _reindex: true, - _reindexWait: 1, - client: { - getBlockchainInfo: sinon.stub().callsArgWith(0, {code: -1 , message: 'Test error'}) - } - }; - bitcoind._checkReindex(node, function(err) { - should.exist(err); - err.should.be.instanceof(errors.RPCError); - done(); - }); - }); - it('will wait until sync is 100 percent', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var percent = 0.89; - var node = { - _reindex: true, - _reindexWait: 1, - client: { - getBlockchainInfo: function(callback) { - percent += 0.01; - callback(null, { - result: { - verificationprogress: percent - } - }); - } - } - }; - bitcoind._checkReindex(node, function() { - node._reindex.should.equal(false); - log.info.callCount.should.equal(11); - done(); - }); - }); - it('will call callback if reindex is not enabled', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var node = { - _reindex: false - }; - bitcoind._checkReindex(node, function() { - node._reindex.should.equal(false); - done(); - }); - }); - }); - - describe('#_loadTipFromNode', function() { - var sandbox = sinon.sandbox.create(); - beforeEach(function() { - sandbox.stub(log, 'warn'); - }); - afterEach(function() { - sandbox.restore(); - }); - it('will give rpc from client getbestblockhash', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getBestBlockHash = sinon.stub().callsArgWith(0, {code: -1, message: 'Test error'}); - var node = { - client: { - getBestBlockHash: getBestBlockHash - } - }; - bitcoind._loadTipFromNode(node, function(err) { - err.should.be.instanceof(Error); - log.warn.callCount.should.equal(0); - done(); - }); - }); - it('will give rpc from client getblock', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getBestBlockHash = sinon.stub().callsArgWith(0, null, { - result: '00000000000000001bb82a7f5973618cfd3185ba1ded04dd852a653f92a27c45' - }); - var getBlock = sinon.stub().callsArgWith(1, new Error('Test error')); - var node = { - client: { - getBestBlockHash: getBestBlockHash, - getBlock: getBlock - } - }; - bitcoind._loadTipFromNode(node, function(err) { - getBlock.args[0][0].should.equal('00000000000000001bb82a7f5973618cfd3185ba1ded04dd852a653f92a27c45'); - err.should.be.instanceof(Error); - log.warn.callCount.should.equal(0); - done(); - }); - }); - it('will log when error is RPC_IN_WARMUP', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getBestBlockHash = sinon.stub().callsArgWith(0, {code: -28, message: 'Verifying blocks...'}); - var node = { - client: { - getBestBlockHash: getBestBlockHash - } - }; - bitcoind._loadTipFromNode(node, function(err) { - err.should.be.instanceof(Error); - log.warn.callCount.should.equal(1); - done(); - }); - }); - it('will set height and emit tip', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getBestBlockHash = sinon.stub().callsArgWith(0, null, { - result: '00000000000000001bb82a7f5973618cfd3185ba1ded04dd852a653f92a27c45' - }); - var getBlock = sinon.stub().callsArgWith(1, null, { - result: { - height: 100 - } - }); - var node = { - client: { - getBestBlockHash: getBestBlockHash, - getBlock: getBlock - } - }; - bitcoind.on('tip', function(height) { - height.should.equal(100); - bitcoind.height.should.equal(100); - done(); - }); - bitcoind._loadTipFromNode(node, function(err) { - if (err) { - return done(err); - } - }); - }); - }); - - describe('#_stopSpawnedProcess', function() { - var sandbox = sinon.sandbox.create(); - beforeEach(function() { - sandbox.stub(log, 'warn'); - }); - afterEach(function() { - sandbox.restore(); - }); - it('it will kill process and resume', function(done) { - var readFile = sandbox.stub(); - readFile.onCall(0).callsArgWith(2, null, '4321'); - var error = new Error('Test error'); - error.code = 'ENOENT'; - readFile.onCall(1).callsArgWith(2, error); - var TestBitcoinService = proxyquire('../../lib/services/bitcoind', { - fs: { - readFile: readFile - } - }); - var bitcoind = new TestBitcoinService(baseConfig); - bitcoind.spawnStopTime = 1; - bitcoind._process = {}; - bitcoind._process.kill = sinon.stub(); - bitcoind._stopSpawnedBitcoin(function(err) { - if (err) { - return done(err); - } - bitcoind._process.kill.callCount.should.equal(1); - log.warn.callCount.should.equal(1); - done(); - }); - }); - it('it will attempt to kill process and resume', function(done) { - var readFile = sandbox.stub(); - readFile.onCall(0).callsArgWith(2, null, '4321'); - var error = new Error('Test error'); - error.code = 'ENOENT'; - readFile.onCall(1).callsArgWith(2, error); - var TestBitcoinService = proxyquire('../../lib/services/bitcoind', { - fs: { - readFile: readFile - } - }); - var bitcoind = new TestBitcoinService(baseConfig); - bitcoind.spawnStopTime = 1; - bitcoind._process = {}; - var error2 = new Error('Test error'); - error2.code = 'ESRCH'; - bitcoind._process.kill = sinon.stub().throws(error2); - bitcoind._stopSpawnedBitcoin(function(err) { - if (err) { - return done(err); - } - bitcoind._process.kill.callCount.should.equal(1); - log.warn.callCount.should.equal(2); - done(); - }); - }); - it('it will attempt to kill process with NaN', function(done) { - var readFile = sandbox.stub(); - readFile.onCall(0).callsArgWith(2, null, ' '); - var TestBitcoinService = proxyquire('../../lib/services/bitcoind', { - fs: { - readFile: readFile - } - }); - var bitcoind = new TestBitcoinService(baseConfig); - bitcoind.spawnStopTime = 1; - bitcoind._process = {}; - bitcoind._process.kill = sinon.stub(); - bitcoind._stopSpawnedBitcoin(function(err) { - if (err) { - return done(err); - } - done(); - }); - }); - it('it will attempt to kill process without pid', function(done) { - var readFile = sandbox.stub(); - readFile.onCall(0).callsArgWith(2, null, ''); - var TestBitcoinService = proxyquire('../../lib/services/bitcoind', { - fs: { - readFile: readFile - } - }); - var bitcoind = new TestBitcoinService(baseConfig); - bitcoind.spawnStopTime = 1; - bitcoind._process = {}; - bitcoind._process.kill = sinon.stub(); - bitcoind._stopSpawnedBitcoin(function(err) { - if (err) { - return done(err); - } - done(); - }); - }); - }); - - describe('#_spawnChildProcess', function() { - var sandbox = sinon.sandbox.create(); - beforeEach(function() { - sandbox.stub(log, 'info'); - sandbox.stub(log, 'warn'); - sandbox.stub(log, 'error'); - }); - afterEach(function() { - sandbox.restore(); - }); - it('will give error from spawn config', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind._loadSpawnConfiguration = sinon.stub(); - bitcoind._loadSpawnConfiguration = sinon.stub().throws(new Error('test')); - bitcoind._spawnChildProcess(function(err) { - err.should.be.instanceof(Error); - err.message.should.equal('test'); - done(); - }); - }); - it('will give error from stopSpawnedBitcoin', function() { - var bitcoind = new BitcoinService(baseConfig); - bitcoind._loadSpawnConfiguration = sinon.stub(); - bitcoind._stopSpawnedBitcoin = sinon.stub().callsArgWith(0, new Error('test')); - bitcoind._spawnChildProcess(function(err) { - err.should.be.instanceOf(Error); - err.message.should.equal('test'); - }); - }); - it('will exit spawn if shutdown', function() { - var config = { - node: { - network: bitcore.Networks.testnet - }, - spawn: { - datadir: 'testdir', - exec: 'testpath' - } - }; - var process = new EventEmitter(); - var spawn = sinon.stub().returns(process); - var TestBitcoinService = proxyquire('../../lib/services/bitcoind', { - fs: { - readFileSync: readFileSync - }, - child_process: { - spawn: spawn - } - }); - var bitcoind = new TestBitcoinService(config); - bitcoind.spawn = {}; - bitcoind._loadSpawnConfiguration = sinon.stub(); - bitcoind._stopSpawnedBitcoin = sinon.stub().callsArgWith(0, null); - bitcoind.node.stopping = true; - bitcoind._spawnChildProcess(function(err) { - err.should.be.instanceOf(Error); - err.message.should.match(/Stopping while trying to spawn/); - }); - }); - it('will include network with spawn command and init zmq/rpc on node', function(done) { - var process = new EventEmitter(); - var spawn = sinon.stub().returns(process); - var TestBitcoinService = proxyquire('../../lib/services/bitcoind', { - fs: { - readFileSync: readFileSync - }, - child_process: { - spawn: spawn - } - }); - var bitcoind = new TestBitcoinService(baseConfig); - - bitcoind._loadSpawnConfiguration = sinon.stub(); - bitcoind.spawn = {}; - bitcoind.spawn.exec = 'testexec'; - bitcoind.spawn.configPath = 'testdir/bitcoin.conf'; - bitcoind.spawn.datadir = 'testdir'; - bitcoind.spawn.config = {}; - bitcoind.spawn.config.rpcport = 20001; - bitcoind.spawn.config.rpcuser = 'bitcoin'; - bitcoind.spawn.config.rpcpassword = 'password'; - bitcoind.spawn.config.zmqpubrawtx = 'tcp://127.0.0.1:30001'; - - bitcoind._loadTipFromNode = sinon.stub().callsArgWith(1, null); - bitcoind._initZmqSubSocket = sinon.stub(); - bitcoind._checkSyncedAndSubscribeZmqEvents = sinon.stub(); - bitcoind._checkReindex = sinon.stub().callsArgWith(1, null); - bitcoind._spawnChildProcess(function(err, node) { - should.not.exist(err); - spawn.callCount.should.equal(1); - spawn.args[0][0].should.equal('testexec'); - spawn.args[0][1].should.deep.equal([ - '--conf=testdir/bitcoin.conf', - '--datadir=testdir', - '--testnet' - ]); - spawn.args[0][2].should.deep.equal({ - stdio: 'inherit' - }); - bitcoind._loadTipFromNode.callCount.should.equal(1); - bitcoind._initZmqSubSocket.callCount.should.equal(1); - should.exist(bitcoind._initZmqSubSocket.args[0][0].client); - bitcoind._initZmqSubSocket.args[0][1].should.equal('tcp://127.0.0.1:30001'); - bitcoind._checkSyncedAndSubscribeZmqEvents.callCount.should.equal(1); - should.exist(bitcoind._checkSyncedAndSubscribeZmqEvents.args[0][0].client); - should.exist(node); - should.exist(node.client); - done(); - }); - }); - it('will respawn bitcoind spawned process', function(done) { - var process = new EventEmitter(); - var spawn = sinon.stub().returns(process); - var TestBitcoinService = proxyquire('../../lib/services/bitcoind', { - fs: { - readFileSync: readFileSync - }, - child_process: { - spawn: spawn - } - }); - var bitcoind = new TestBitcoinService(baseConfig); - bitcoind._loadSpawnConfiguration = sinon.stub(); - bitcoind.spawn = {}; - bitcoind.spawn.exec = 'bitcoind'; - bitcoind.spawn.datadir = '/tmp/bitcoin'; - bitcoind.spawn.configPath = '/tmp/bitcoin/bitcoin.conf'; - bitcoind.spawn.config = {}; - bitcoind.spawnRestartTime = 1; - bitcoind._loadTipFromNode = sinon.stub().callsArg(1); - bitcoind._initZmqSubSocket = sinon.stub(); - bitcoind._checkReindex = sinon.stub().callsArg(1); - bitcoind._checkSyncedAndSubscribeZmqEvents = sinon.stub(); - bitcoind._stopSpawnedBitcoin = sinon.stub().callsArg(0); - sinon.spy(bitcoind, '_spawnChildProcess'); - bitcoind._spawnChildProcess(function(err) { - if (err) { - return done(err); - } - process.once('exit', function() { - setTimeout(function() { - bitcoind._spawnChildProcess.callCount.should.equal(2); - done(); - }, 5); - }); - process.emit('exit', 1); - }); - }); - it('will emit error during respawn', function(done) { - var process = new EventEmitter(); - var spawn = sinon.stub().returns(process); - var TestBitcoinService = proxyquire('../../lib/services/bitcoind', { - fs: { - readFileSync: readFileSync - }, - child_process: { - spawn: spawn - } - }); - var bitcoind = new TestBitcoinService(baseConfig); - bitcoind._loadSpawnConfiguration = sinon.stub(); - bitcoind.spawn = {}; - bitcoind.spawn.exec = 'bitcoind'; - bitcoind.spawn.datadir = '/tmp/bitcoin'; - bitcoind.spawn.configPath = '/tmp/bitcoin/bitcoin.conf'; - bitcoind.spawn.config = {}; - bitcoind.spawnRestartTime = 1; - bitcoind._loadTipFromNode = sinon.stub().callsArg(1); - bitcoind._initZmqSubSocket = sinon.stub(); - bitcoind._checkReindex = sinon.stub().callsArg(1); - bitcoind._checkSyncedAndSubscribeZmqEvents = sinon.stub(); - bitcoind._stopSpawnedBitcoin = sinon.stub().callsArg(0); - sinon.spy(bitcoind, '_spawnChildProcess'); - bitcoind._spawnChildProcess(function(err) { - if (err) { - return done(err); - } - bitcoind._spawnChildProcess = sinon.stub().callsArgWith(0, new Error('test')); - bitcoind.on('error', function(err) { - err.should.be.instanceOf(Error); - err.message.should.equal('test'); - done(); - }); - process.emit('exit', 1); - }); - }); - it('will NOT respawn bitcoind spawned process if shutting down', function(done) { - var process = new EventEmitter(); - var spawn = sinon.stub().returns(process); - var TestBitcoinService = proxyquire('../../lib/services/bitcoind', { - fs: { - readFileSync: readFileSync - }, - child_process: { - spawn: spawn - } - }); - var config = { - node: { - network: bitcore.Networks.testnet - }, - spawn: { - datadir: 'testdir', - exec: 'testpath' - } - }; - var bitcoind = new TestBitcoinService(config); - bitcoind._loadSpawnConfiguration = sinon.stub(); - bitcoind.spawn = {}; - bitcoind.spawn.exec = 'bitcoind'; - bitcoind.spawn.datadir = '/tmp/bitcoin'; - bitcoind.spawn.configPath = '/tmp/bitcoin/bitcoin.conf'; - bitcoind.spawn.config = {}; - bitcoind.spawnRestartTime = 1; - bitcoind._loadTipFromNode = sinon.stub().callsArg(1); - bitcoind._initZmqSubSocket = sinon.stub(); - bitcoind._checkReindex = sinon.stub().callsArg(1); - bitcoind._checkSyncedAndSubscribeZmqEvents = sinon.stub(); - bitcoind._stopSpawnedBitcoin = sinon.stub().callsArg(0); - sinon.spy(bitcoind, '_spawnChildProcess'); - bitcoind._spawnChildProcess(function(err) { - if (err) { - return done(err); - } - bitcoind.node.stopping = true; - process.once('exit', function() { - setTimeout(function() { - bitcoind._spawnChildProcess.callCount.should.equal(1); - done(); - }, 5); - }); - process.emit('exit', 1); - }); - }); - it('will give error after 60 retries', function(done) { - var process = new EventEmitter(); - var spawn = sinon.stub().returns(process); - var TestBitcoinService = proxyquire('../../lib/services/bitcoind', { - fs: { - readFileSync: readFileSync - }, - child_process: { - spawn: spawn - } - }); - var bitcoind = new TestBitcoinService(baseConfig); - bitcoind.startRetryInterval = 1; - bitcoind._loadSpawnConfiguration = sinon.stub(); - bitcoind.spawn = {}; - bitcoind.spawn.exec = 'testexec'; - bitcoind.spawn.configPath = 'testdir/bitcoin.conf'; - bitcoind.spawn.datadir = 'testdir'; - bitcoind.spawn.config = {}; - bitcoind.spawn.config.rpcport = 20001; - bitcoind.spawn.config.rpcuser = 'bitcoin'; - bitcoind.spawn.config.rpcpassword = 'password'; - bitcoind.spawn.config.zmqpubrawtx = 'tcp://127.0.0.1:30001'; - bitcoind._loadTipFromNode = sinon.stub().callsArgWith(1, new Error('test')); - bitcoind._spawnChildProcess(function(err) { - bitcoind._loadTipFromNode.callCount.should.equal(60); - err.should.be.instanceof(Error); - done(); - }); - }); - it('will give error from check reindex', function(done) { - var process = new EventEmitter(); - var spawn = sinon.stub().returns(process); - var TestBitcoinService = proxyquire('../../lib/services/bitcoind', { - fs: { - readFileSync: readFileSync - }, - child_process: { - spawn: spawn - } - }); - var bitcoind = new TestBitcoinService(baseConfig); - - bitcoind._loadSpawnConfiguration = sinon.stub(); - bitcoind.spawn = {}; - bitcoind.spawn.exec = 'testexec'; - bitcoind.spawn.configPath = 'testdir/bitcoin.conf'; - bitcoind.spawn.datadir = 'testdir'; - bitcoind.spawn.config = {}; - bitcoind.spawn.config.rpcport = 20001; - bitcoind.spawn.config.rpcuser = 'bitcoin'; - bitcoind.spawn.config.rpcpassword = 'password'; - bitcoind.spawn.config.zmqpubrawtx = 'tcp://127.0.0.1:30001'; - - bitcoind._loadTipFromNode = sinon.stub().callsArgWith(1, null); - bitcoind._initZmqSubSocket = sinon.stub(); - bitcoind._checkSyncedAndSubscribeZmqEvents = sinon.stub(); - bitcoind._checkReindex = sinon.stub().callsArgWith(1, new Error('test')); - - bitcoind._spawnChildProcess(function(err) { - err.should.be.instanceof(Error); - done(); - }); - }); - }); - - describe('#_connectProcess', function() { - it('will give error if connecting while shutting down', function(done) { - var config = { - node: { - network: bitcore.Networks.testnet - }, - spawn: { - datadir: 'testdir', - exec: 'testpath' - } - }; - var bitcoind = new BitcoinService(config); - bitcoind.node.stopping = true; - bitcoind.startRetryInterval = 100; - bitcoind._loadTipFromNode = sinon.stub(); - bitcoind._connectProcess({}, function(err) { - err.should.be.instanceof(Error); - err.message.should.match(/Stopping while trying to connect/); - bitcoind._loadTipFromNode.callCount.should.equal(0); - done(); - }); - }); - it('will give error from loadTipFromNode after 60 retries', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind._loadTipFromNode = sinon.stub().callsArgWith(1, new Error('test')); - bitcoind.startRetryInterval = 1; - var config = {}; - bitcoind._connectProcess(config, function(err) { - err.should.be.instanceof(Error); - bitcoind._loadTipFromNode.callCount.should.equal(60); - done(); - }); - }); - it('will init zmq/rpc on node', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind._initZmqSubSocket = sinon.stub(); - bitcoind._subscribeZmqEvents = sinon.stub(); - bitcoind._loadTipFromNode = sinon.stub().callsArgWith(1, null); - var config = {}; - bitcoind._connectProcess(config, function(err, node) { - should.not.exist(err); - bitcoind._loadTipFromNode.callCount.should.equal(1); - bitcoind._initZmqSubSocket.callCount.should.equal(1); - bitcoind._loadTipFromNode.callCount.should.equal(1); - should.exist(node); - should.exist(node.client); - done(); - }); - }); - }); - - describe('#start', function() { - var sandbox = sinon.sandbox.create(); - beforeEach(function() { - sandbox.stub(log, 'info'); - }); - afterEach(function() { - sandbox.restore(); - }); - it('will give error if "spawn" and "connect" are both not configured', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.options = {}; - bitcoind.start(function(err) { - err.should.be.instanceof(Error); - err.message.should.match(/Bitcoin configuration options/); - }); - done(); - }); - it('will give error from spawnChildProcess', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind._spawnChildProcess = sinon.stub().callsArgWith(0, new Error('test')); - bitcoind.options = { - spawn: {} - }; - bitcoind.start(function(err) { - err.should.be.instanceof(Error); - err.message.should.equal('test'); - done(); - }); - }); - it('will give error from connectProcess', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind._connectProcess = sinon.stub().callsArgWith(1, new Error('test')); - bitcoind.options = { - connect: [ - {} - ] - }; - bitcoind.start(function(err) { - bitcoind._connectProcess.callCount.should.equal(1); - err.should.be.instanceof(Error); - err.message.should.equal('test'); - done(); - }); - }); - it('will push node from spawnChildProcess', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var node = {}; - bitcoind._initChain = sinon.stub().callsArg(0); - bitcoind._spawnChildProcess = sinon.stub().callsArgWith(0, null, node); - bitcoind.options = { - spawn: {} - }; - bitcoind.start(function(err) { - should.not.exist(err); - bitcoind.nodes.length.should.equal(1); - done(); - }); - }); - it('will push node from connectProcess', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind._initChain = sinon.stub().callsArg(0); - var nodes = [{}]; - bitcoind._connectProcess = sinon.stub().callsArgWith(1, null, nodes); - bitcoind.options = { - connect: [ - {} - ] - }; - bitcoind.start(function(err) { - should.not.exist(err); - bitcoind._connectProcess.callCount.should.equal(1); - bitcoind.nodes.length.should.equal(1); - done(); - }); - }); - }); - - describe('#isSynced', function() { - it('will give error from syncPercentage', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.syncPercentage = sinon.stub().callsArgWith(0, new Error('test')); - bitcoind.isSynced(function(err) { - should.exist(err); - err.message.should.equal('test'); - done(); - }); - }); - it('will give "true" if percentage is 100.00', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.syncPercentage = sinon.stub().callsArgWith(0, null, 100.00); - bitcoind.isSynced(function(err, synced) { - if (err) { - return done(err); - } - synced.should.equal(true); - done(); - }); - }); - it('will give "true" if percentage is 99.98', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.syncPercentage = sinon.stub().callsArgWith(0, null, 99.98); - bitcoind.isSynced(function(err, synced) { - if (err) { - return done(err); - } - synced.should.equal(true); - done(); - }); - }); - it('will give "false" if percentage is 99.49', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.syncPercentage = sinon.stub().callsArgWith(0, null, 99.49); - bitcoind.isSynced(function(err, synced) { - if (err) { - return done(err); - } - synced.should.equal(false); - done(); - }); - }); - it('will give "false" if percentage is 1', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.syncPercentage = sinon.stub().callsArgWith(0, null, 1); - bitcoind.isSynced(function(err, synced) { - if (err) { - return done(err); - } - synced.should.equal(false); - done(); - }); - }); - }); - - describe('#syncPercentage', function() { - it('will give rpc error', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getBlockchainInfo = sinon.stub().callsArgWith(0, {message: 'error', code: -1}); - bitcoind.nodes.push({ - client: { - getBlockchainInfo: getBlockchainInfo - } - }); - bitcoind.syncPercentage(function(err) { - should.exist(err); - err.should.be.an.instanceof(errors.RPCError); - done(); - }); - }); - it('will call client getInfo and give result', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getBlockchainInfo = sinon.stub().callsArgWith(0, null, { - result: { - verificationprogress: '0.983821387' - } - }); - bitcoind.nodes.push({ - client: { - getBlockchainInfo: getBlockchainInfo - } - }); - bitcoind.syncPercentage(function(err, percentage) { - if (err) { - return done(err); - } - percentage.should.equal(98.3821387); - done(); - }); - }); - }); - - describe('#_normalizeAddressArg', function() { - it('will turn single address into array', function() { - var bitcoind = new BitcoinService(baseConfig); - var args = bitcoind._normalizeAddressArg('address'); - args.should.deep.equal(['address']); - }); - it('will keep an array as an array', function() { - var bitcoind = new BitcoinService(baseConfig); - var args = bitcoind._normalizeAddressArg(['address', 'address']); - args.should.deep.equal(['address', 'address']); - }); - }); - - describe('#getAddressBalance', function() { - it('will give rpc error', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.nodes.push({ - client: { - getAddressBalance: sinon.stub().callsArgWith(1, {code: -1, message: 'Test error'}) - } - }); - var address = '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo'; - var options = {}; - bitcoind.getAddressBalance(address, options, function(err) { - err.should.be.instanceof(Error); - done(); - }); - }); - it('will give balance', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getAddressBalance = sinon.stub().callsArgWith(1, null, { - result: { - received: 100000, - balance: 10000 - } - }); - bitcoind.nodes.push({ - client: { - getAddressBalance: getAddressBalance - } - }); - var address = '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo'; - var options = {}; - bitcoind.getAddressBalance(address, options, function(err, data) { - if (err) { - return done(err); - } - data.balance.should.equal(10000); - data.received.should.equal(100000); - bitcoind.getAddressBalance(address, options, function(err, data2) { - if (err) { - return done(err); - } - data2.balance.should.equal(10000); - data2.received.should.equal(100000); - getAddressBalance.callCount.should.equal(1); - done(); - }); - }); - }); - }); - - describe('#getAddressUnspentOutputs', function() { - it('will give rpc error', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.nodes.push({ - client: { - getAddressUtxos: sinon.stub().callsArgWith(1, {code: -1, message: 'Test error'}) - } - }); - var options = { - queryMempool: false - }; - var address = '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo'; - bitcoind.getAddressUnspentOutputs(address, options, function(err) { - should.exist(err); - err.should.be.instanceof(errors.RPCError); - done(); - }); - }); - it('will give results from client getaddressutxos', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var expectedUtxos = [ - { - address: '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo', - txid: '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0', - outputIndex: 1, - script: '76a914f399b4b8894f1153b96fce29f05e6e116eb4c21788ac', - satoshis: 7679241, - height: 207111 - } - ]; - bitcoind.nodes.push({ - client: { - getAddressUtxos: sinon.stub().callsArgWith(1, null, { - result: expectedUtxos - }) - } - }); - var options = { - queryMempool: false - }; - var address = '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo'; - bitcoind.getAddressUnspentOutputs(address, options, function(err, utxos) { - if (err) { - return done(err); - } - utxos.length.should.equal(1); - utxos.should.deep.equal(expectedUtxos); - done(); - }); - }); - it('will use cache', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var expectedUtxos = [ - { - address: '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo', - txid: '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0', - outputIndex: 1, - script: '76a914f399b4b8894f1153b96fce29f05e6e116eb4c21788ac', - satoshis: 7679241, - height: 207111 - } - ]; - var getAddressUtxos = sinon.stub().callsArgWith(1, null, { - result: expectedUtxos - }); - bitcoind.nodes.push({ - client: { - getAddressUtxos: getAddressUtxos - } - }); - var options = { - queryMempool: false - }; - var address = '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo'; - bitcoind.getAddressUnspentOutputs(address, options, function(err, utxos) { - if (err) { - return done(err); - } - utxos.length.should.equal(1); - utxos.should.deep.equal(expectedUtxos); - getAddressUtxos.callCount.should.equal(1); - bitcoind.getAddressUnspentOutputs(address, options, function(err, utxos) { - if (err) { - return done(err); - } - utxos.length.should.equal(1); - utxos.should.deep.equal(expectedUtxos); - getAddressUtxos.callCount.should.equal(1); - done(); - }); - }); - }); - it('will update with mempool results', function(done) { - var deltas = [ - { - txid: 'e9dcf22807db77ac0276b03cc2d3a8b03c4837db8ac6650501ef45af1c807cce', - satoshis: -7679241, - address: '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo', - index: 0, - timestamp: 1461342707725, - prevtxid: '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0', - prevout: 1 - }, - { - txid: 'f637384e9f81f18767ea50e00bce58fc9848b6588a1130529eebba22a410155f', - satoshis: 100000, - address: '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo', - index: 0, - timestamp: 1461342833133 - }, - { - txid: 'f71bccef3a8f5609c7f016154922adbfe0194a96fb17a798c24077c18d0a9345', - satoshis: 400000, - address: '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo', - index: 1, - timestamp: 1461342954813 - } - ]; - var bitcoind = new BitcoinService(baseConfig); - var confirmedUtxos = [ - { - address: '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo', - txid: '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0', - outputIndex: 1, - script: '76a914f399b4b8894f1153b96fce29f05e6e116eb4c21788ac', - satoshis: 7679241, - height: 207111 - } - ]; - var expectedUtxos = [ - { - address: '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo', - outputIndex: 1, - satoshis: 400000, - script: '76a914809dc14496f99b6deb722cf46d89d22f4beb8efd88ac', - timestamp: 1461342954813, - txid: 'f71bccef3a8f5609c7f016154922adbfe0194a96fb17a798c24077c18d0a9345' - }, - { - address: '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo', - outputIndex: 0, - satoshis: 100000, - script: '76a914809dc14496f99b6deb722cf46d89d22f4beb8efd88ac', - timestamp: 1461342833133, - txid: 'f637384e9f81f18767ea50e00bce58fc9848b6588a1130529eebba22a410155f' - } - ]; - bitcoind.nodes.push({ - client: { - getAddressUtxos: sinon.stub().callsArgWith(1, null, { - result: confirmedUtxos - }), - getAddressMempool: sinon.stub().callsArgWith(1, null, { - result: deltas - }) - } - }); - var options = { - queryMempool: true - }; - var address = '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo'; - bitcoind.getAddressUnspentOutputs(address, options, function(err, utxos) { - if (err) { - return done(err); - } - utxos.length.should.equal(2); - utxos.should.deep.equal(expectedUtxos); - done(); - }); - }); - it('will update with mempool results with multiple outputs', function(done) { - var deltas = [ - { - txid: 'e9dcf22807db77ac0276b03cc2d3a8b03c4837db8ac6650501ef45af1c807cce', - satoshis: -7679241, - address: '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo', - index: 0, - timestamp: 1461342707725, - prevtxid: '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0', - prevout: 1 - }, - { - txid: 'e9dcf22807db77ac0276b03cc2d3a8b03c4837db8ac6650501ef45af1c807cce', - satoshis: -7679241, - address: '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo', - index: 1, - timestamp: 1461342707725, - prevtxid: '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0', - prevout: 2 - } - ]; - var bitcoind = new BitcoinService(baseConfig); - var confirmedUtxos = [ - { - address: '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo', - txid: '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0', - outputIndex: 1, - script: '76a914f399b4b8894f1153b96fce29f05e6e116eb4c21788ac', - satoshis: 7679241, - height: 207111 - }, - { - address: '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo', - txid: '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0', - outputIndex: 2, - script: '76a914f399b4b8894f1153b96fce29f05e6e116eb4c21788ac', - satoshis: 7679241, - height: 207111 - } - ]; - bitcoind.nodes.push({ - client: { - getAddressUtxos: sinon.stub().callsArgWith(1, null, { - result: confirmedUtxos - }), - getAddressMempool: sinon.stub().callsArgWith(1, null, { - result: deltas - }) - } - }); - var options = { - queryMempool: true - }; - var address = '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo'; - bitcoind.getAddressUnspentOutputs(address, options, function(err, utxos) { - if (err) { - return done(err); - } - utxos.length.should.equal(0); - done(); - }); - }); - it('three confirmed utxos -> one utxo after mempool', function(done) { - var deltas = [ - { - txid: 'e9dcf22807db77ac0276b03cc2d3a8b03c4837db8ac6650501ef45af1c807cce', - satoshis: -7679241, - address: '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo', - index: 0, - timestamp: 1461342707725, - prevtxid: '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0', - prevout: 0 - }, - { - txid: 'e9dcf22807db77ac0276b03cc2d3a8b03c4837db8ac6650501ef45af1c807cce', - satoshis: -7679241, - address: '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo', - index: 0, - timestamp: 1461342707725, - prevtxid: '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0', - prevout: 1 - }, - { - txid: 'e9dcf22807db77ac0276b03cc2d3a8b03c4837db8ac6650501ef45af1c807cce', - satoshis: -7679241, - address: '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo', - index: 1, - timestamp: 1461342707725, - prevtxid: '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0', - prevout: 2 - }, - { - txid: 'e9dcf22807db77ac0276b03cc2d3a8b03c4837db8ac6650501ef45af1c807cce', - satoshis: 100000, - address: '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo', - index: 1, - script: '76a914809dc14496f99b6deb722cf46d89d22f4beb8efd88ac', - timestamp: 1461342833133 - } - ]; - var bitcoind = new BitcoinService(baseConfig); - var confirmedUtxos = [ - { - address: '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo', - txid: '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0', - outputIndex: 0, - script: '76a914f399b4b8894f1153b96fce29f05e6e116eb4c21788ac', - satoshis: 7679241, - height: 207111 - }, - { - address: '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo', - txid: '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0', - outputIndex: 1, - script: '76a914f399b4b8894f1153b96fce29f05e6e116eb4c21788ac', - satoshis: 7679241, - height: 207111 - }, - { - address: '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo', - txid: '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0', - outputIndex: 2, - script: '76a914f399b4b8894f1153b96fce29f05e6e116eb4c21788ac', - satoshis: 7679241, - height: 207111 - } - ]; - bitcoind.nodes.push({ - client: { - getAddressUtxos: sinon.stub().callsArgWith(1, null, { - result: confirmedUtxos - }), - getAddressMempool: sinon.stub().callsArgWith(1, null, { - result: deltas - }) - } - }); - var options = { - queryMempool: true - }; - var address = '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo'; - bitcoind.getAddressUnspentOutputs(address, options, function(err, utxos) { - if (err) { - return done(err); - } - utxos.length.should.equal(1); - done(); - }); - }); - it('spending utxos in the mempool', function(done) { - var deltas = [ - { - txid: '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0', - satoshis: 7679241, - address: '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo', - index: 0, - timestamp: 1461342707724 - }, - { - txid: '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0', - satoshis: 7679241, - address: '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo', - index: 1, - timestamp: 1461342707724 - }, - { - txid: '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0', - satoshis: 7679241, - address: '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo', - timestamp: 1461342707724, - index: 2, - }, - { - txid: 'e9dcf22807db77ac0276b03cc2d3a8b03c4837db8ac6650501ef45af1c807cce', - satoshis: -7679241, - address: '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo', - index: 0, - timestamp: 1461342707725, - prevtxid: '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0', - prevout: 0 - }, - { - txid: 'e9dcf22807db77ac0276b03cc2d3a8b03c4837db8ac6650501ef45af1c807cce', - satoshis: -7679241, - address: '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo', - index: 0, - timestamp: 1461342707725, - prevtxid: '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0', - prevout: 1 - }, - { - txid: 'e9dcf22807db77ac0276b03cc2d3a8b03c4837db8ac6650501ef45af1c807cce', - satoshis: -7679241, - address: '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo', - index: 1, - timestamp: 1461342707725, - prevtxid: '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0', - prevout: 2 - }, - { - txid: 'e9dcf22807db77ac0276b03cc2d3a8b03c4837db8ac6650501ef45af1c807cce', - satoshis: 100000, - address: '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo', - index: 1, - timestamp: 1461342833133 - } - ]; - var bitcoind = new BitcoinService(baseConfig); - var confirmedUtxos = []; - bitcoind.nodes.push({ - client: { - getAddressUtxos: sinon.stub().callsArgWith(1, null, { - result: confirmedUtxos - }), - getAddressMempool: sinon.stub().callsArgWith(1, null, { - result: deltas - }) - } - }); - var options = { - queryMempool: true - }; - var address = '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo'; - bitcoind.getAddressUnspentOutputs(address, options, function(err, utxos) { - if (err) { - return done(err); - } - utxos.length.should.equal(1); - utxos[0].address.should.equal(address); - utxos[0].txid.should.equal('e9dcf22807db77ac0276b03cc2d3a8b03c4837db8ac6650501ef45af1c807cce'); - utxos[0].outputIndex.should.equal(1); - utxos[0].script.should.equal('76a914809dc14496f99b6deb722cf46d89d22f4beb8efd88ac'); - utxos[0].timestamp.should.equal(1461342833133); - done(); - }); - }); - it('will update with mempool results spending zero value output (likely never to happen)', function(done) { - var deltas = [ - { - txid: 'e9dcf22807db77ac0276b03cc2d3a8b03c4837db8ac6650501ef45af1c807cce', - satoshis: 0, - address: '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo', - index: 0, - timestamp: 1461342707725, - prevtxid: '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0', - prevout: 1 - } - ]; - var bitcoind = new BitcoinService(baseConfig); - var confirmedUtxos = [ - { - address: '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo', - txid: '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0', - outputIndex: 1, - script: '76a914f399b4b8894f1153b96fce29f05e6e116eb4c21788ac', - satoshis: 0, - height: 207111 - } - ]; - bitcoind.nodes.push({ - client: { - getAddressUtxos: sinon.stub().callsArgWith(1, null, { - result: confirmedUtxos - }), - getAddressMempool: sinon.stub().callsArgWith(1, null, { - result: deltas - }) - } - }); - var options = { - queryMempool: true - }; - var address = '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo'; - bitcoind.getAddressUnspentOutputs(address, options, function(err, utxos) { - if (err) { - return done(err); - } - utxos.length.should.equal(0); - done(); - }); - }); - it('will not filter results if mempool is not spending', function(done) { - var deltas = [ - { - txid: 'e9dcf22807db77ac0276b03cc2d3a8b03c4837db8ac6650501ef45af1c807cce', - satoshis: 10000, - address: '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo', - index: 0, - timestamp: 1461342707725 - } - ]; - var bitcoind = new BitcoinService(baseConfig); - var confirmedUtxos = [ - { - address: '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo', - txid: '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0', - outputIndex: 1, - script: '76a914f399b4b8894f1153b96fce29f05e6e116eb4c21788ac', - satoshis: 0, - height: 207111 - } - ]; - bitcoind.nodes.push({ - client: { - getAddressUtxos: sinon.stub().callsArgWith(1, null, { - result: confirmedUtxos - }), - getAddressMempool: sinon.stub().callsArgWith(1, null, { - result: deltas - }) - } - }); - var options = { - queryMempool: true - }; - var address = '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo'; - bitcoind.getAddressUnspentOutputs(address, options, function(err, utxos) { - if (err) { - return done(err); - } - utxos.length.should.equal(2); - done(); - }); - }); - it('it will handle error from getAddressMempool', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.nodes.push({ - client: { - getAddressMempool: sinon.stub().callsArgWith(1, {code: -1, message: 'test'}) - } - }); - var options = { - queryMempool: true - }; - var address = '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo'; - bitcoind.getAddressUnspentOutputs(address, options, function(err) { - err.should.be.instanceOf(Error); - done(); - }); - }); - it('should set query mempool if undefined', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getAddressMempool = sinon.stub().callsArgWith(1, {code: -1, message: 'test'}); - bitcoind.nodes.push({ - client: { - getAddressMempool: getAddressMempool - } - }); - var options = {}; - var address = '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo'; - bitcoind.getAddressUnspentOutputs(address, options, function(err) { - getAddressMempool.callCount.should.equal(1); - done(); - }); - }); - }); - - describe('#_getBalanceFromMempool', function() { - it('will sum satoshis', function() { - var bitcoind = new BitcoinService(baseConfig); - var deltas = [ - { - satoshis: -1000, - }, - { - satoshis: 2000, - }, - { - satoshis: -10, - } - ]; - var sum = bitcoind._getBalanceFromMempool(deltas); - sum.should.equal(990); - }); - }); - - describe('#_getTxidsFromMempool', function() { - it('will filter to txids', function() { - var bitcoind = new BitcoinService(baseConfig); - var deltas = [ - { - txid: 'txid0', - }, - { - txid: 'txid1', - }, - { - txid: 'txid2', - } - ]; - var txids = bitcoind._getTxidsFromMempool(deltas); - txids.length.should.equal(3); - txids[0].should.equal('txid0'); - txids[1].should.equal('txid1'); - txids[2].should.equal('txid2'); - }); - it('will not include duplicates', function() { - var bitcoind = new BitcoinService(baseConfig); - var deltas = [ - { - txid: 'txid0', - }, - { - txid: 'txid0', - }, - { - txid: 'txid1', - } - ]; - var txids = bitcoind._getTxidsFromMempool(deltas); - txids.length.should.equal(2); - txids[0].should.equal('txid0'); - txids[1].should.equal('txid1'); - }); - }); - - describe('#_getHeightRangeQuery', function() { - it('will detect range query', function() { - var bitcoind = new BitcoinService(baseConfig); - var options = { - start: 20, - end: 0 - }; - var rangeQuery = bitcoind._getHeightRangeQuery(options); - rangeQuery.should.equal(true); - }); - it('will get range properties', function() { - var bitcoind = new BitcoinService(baseConfig); - var options = { - start: 20, - end: 0 - }; - var clone = {}; - bitcoind._getHeightRangeQuery(options, clone); - clone.end.should.equal(20); - clone.start.should.equal(0); - }); - it('will throw error with invalid range', function() { - var bitcoind = new BitcoinService(baseConfig); - var options = { - start: 0, - end: 20 - }; - (function() { - bitcoind._getHeightRangeQuery(options); - }).should.throw('"end" is expected'); - }); - }); - - describe('#getAddressTxids', function() { - it('will give error from _getHeightRangeQuery', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind._getHeightRangeQuery = sinon.stub().throws(new Error('test')); - bitcoind.getAddressTxids('address', {}, function(err) { - err.should.be.instanceOf(Error); - err.message.should.equal('test'); - done(); - }); - }); - it('will give rpc error from mempool query', function() { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.nodes.push({ - client: { - getAddressMempool: sinon.stub().callsArgWith(1, {code: -1, message: 'Test error'}) - } - }); - var options = {}; - var address = '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo'; - bitcoind.getAddressTxids(address, options, function(err) { - should.exist(err); - err.should.be.instanceof(errors.RPCError); - }); - }); - it('will give rpc error from txids query', function() { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.nodes.push({ - client: { - getAddressTxids: sinon.stub().callsArgWith(1, {code: -1, message: 'Test error'}) - } - }); - var options = { - queryMempool: false - }; - var address = '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo'; - bitcoind.getAddressTxids(address, options, function(err) { - should.exist(err); - err.should.be.instanceof(errors.RPCError); - }); - }); - it('will get txid results', function(done) { - var expectedTxids = [ - 'e9dcf22807db77ac0276b03cc2d3a8b03c4837db8ac6650501ef45af1c807cce', - 'f637384e9f81f18767ea50e00bce58fc9848b6588a1130529eebba22a410155f', - 'f3c1ba3ef86a0420d6102e40e2cfc8682632ab95d09d86a27f5d466b9fa9da47', - '56fafeb01961831b926558d040c246b97709fd700adcaa916541270583e8e579', - 'bc992ad772eb02864db07ef248d31fb3c6826d25f1153ebf8c79df9b7f70fcf2', - 'f71bccef3a8f5609c7f016154922adbfe0194a96fb17a798c24077c18d0a9345', - 'f35e7e2a2334e845946f3eaca76890d9a68f4393ccc9fe37a0c2fb035f66d2e9', - 'edc080f2084eed362aa488ccc873a24c378dc0979aa29b05767517b70569414a', - 'ed11a08e3102f9610bda44c80c46781d97936a4290691d87244b1b345b39a693', - 'ec94d845c603f292a93b7c829811ac624b76e52b351617ca5a758e9d61a11681' - ]; - var bitcoind = new BitcoinService(baseConfig); - bitcoind.nodes.push({ - client: { - getAddressTxids: sinon.stub().callsArgWith(1, null, { - result: expectedTxids.reverse() - }) - } - }); - var options = { - queryMempool: false - }; - var address = '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo'; - bitcoind.getAddressTxids(address, options, function(err, txids) { - if (err) { - return done(err); - } - txids.length.should.equal(expectedTxids.length); - txids.should.deep.equal(expectedTxids); - done(); - }); - }); - it('will get txid results from cache', function(done) { - var expectedTxids = [ - 'e9dcf22807db77ac0276b03cc2d3a8b03c4837db8ac6650501ef45af1c807cce' - ]; - var bitcoind = new BitcoinService(baseConfig); - var getAddressTxids = sinon.stub().callsArgWith(1, null, { - result: expectedTxids.reverse() - }); - bitcoind.nodes.push({ - client: { - getAddressTxids: getAddressTxids - } - }); - var options = { - queryMempool: false - }; - var address = '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo'; - bitcoind.getAddressTxids(address, options, function(err, txids) { - if (err) { - return done(err); - } - getAddressTxids.callCount.should.equal(1); - txids.should.deep.equal(expectedTxids); - - bitcoind.getAddressTxids(address, options, function(err, txids) { - if (err) { - return done(err); - } - getAddressTxids.callCount.should.equal(1); - txids.should.deep.equal(expectedTxids); - done(); - }); - }); - }); - it('will get txid results WITHOUT cache if rangeQuery and exclude mempool', function(done) { - var expectedTxids = [ - 'e9dcf22807db77ac0276b03cc2d3a8b03c4837db8ac6650501ef45af1c807cce' - ]; - var bitcoind = new BitcoinService(baseConfig); - var getAddressMempool = sinon.stub(); - var getAddressTxids = sinon.stub().callsArgWith(1, null, { - result: expectedTxids.reverse() - }); - bitcoind.nodes.push({ - client: { - getAddressTxids: getAddressTxids, - getAddressMempool: getAddressMempool - } - }); - var options = { - queryMempool: true, // start and end will exclude mempool - start: 4, - end: 2 - }; - var address = '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo'; - bitcoind.getAddressTxids(address, options, function(err, txids) { - if (err) { - return done(err); - } - getAddressTxids.callCount.should.equal(1); - getAddressMempool.callCount.should.equal(0); - txids.should.deep.equal(expectedTxids); - - bitcoind.getAddressTxids(address, options, function(err, txids) { - if (err) { - return done(err); - } - getAddressTxids.callCount.should.equal(2); - getAddressMempool.callCount.should.equal(0); - txids.should.deep.equal(expectedTxids); - done(); - }); - }); - }); - it('will get txid results from cache and live mempool', function(done) { - var expectedTxids = [ - 'e9dcf22807db77ac0276b03cc2d3a8b03c4837db8ac6650501ef45af1c807cce' - ]; - var bitcoind = new BitcoinService(baseConfig); - var getAddressTxids = sinon.stub().callsArgWith(1, null, { - result: expectedTxids.reverse() - }); - var getAddressMempool = sinon.stub().callsArgWith(1, null, { - result: [ - { - txid: 'bc992ad772eb02864db07ef248d31fb3c6826d25f1153ebf8c79df9b7f70fcf2' - }, - { - txid: 'f71bccef3a8f5609c7f016154922adbfe0194a96fb17a798c24077c18d0a9345' - }, - { - txid: 'f35e7e2a2334e845946f3eaca76890d9a68f4393ccc9fe37a0c2fb035f66d2e9' - } - ] - }); - bitcoind.nodes.push({ - client: { - getAddressTxids: getAddressTxids, - getAddressMempool: getAddressMempool - } - }); - var address = '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo'; - bitcoind.getAddressTxids(address, {queryMempool: false}, function(err, txids) { - if (err) { - return done(err); - } - getAddressTxids.callCount.should.equal(1); - txids.should.deep.equal(expectedTxids); - - bitcoind.getAddressTxids(address, {queryMempool: true}, function(err, txids) { - if (err) { - return done(err); - } - getAddressTxids.callCount.should.equal(1); - txids.should.deep.equal([ - 'f35e7e2a2334e845946f3eaca76890d9a68f4393ccc9fe37a0c2fb035f66d2e9', // mempool - 'f71bccef3a8f5609c7f016154922adbfe0194a96fb17a798c24077c18d0a9345', // mempool - 'bc992ad772eb02864db07ef248d31fb3c6826d25f1153ebf8c79df9b7f70fcf2', // mempool - 'e9dcf22807db77ac0276b03cc2d3a8b03c4837db8ac6650501ef45af1c807cce' // confirmed - ]); - - bitcoind.getAddressTxids(address, {queryMempoolOnly: true}, function(err, txids) { - if (err) { - return done(err); - } - getAddressTxids.callCount.should.equal(1); - txids.should.deep.equal([ - 'f35e7e2a2334e845946f3eaca76890d9a68f4393ccc9fe37a0c2fb035f66d2e9', // mempool - 'f71bccef3a8f5609c7f016154922adbfe0194a96fb17a798c24077c18d0a9345', // mempool - 'bc992ad772eb02864db07ef248d31fb3c6826d25f1153ebf8c79df9b7f70fcf2', // mempool - ]); - done(); - }); - }); - }); - }); - }); - - describe('#_getConfirmationDetail', function() { - var sandbox = sinon.sandbox.create(); - beforeEach(function() { - sandbox.stub(log, 'warn'); - }); - afterEach(function() { - sandbox.restore(); - }); - it('should get 0 confirmation', function() { - var tx = new Transaction(txhex); - tx.height = -1; - var bitcoind = new BitcoinService(baseConfig); - bitcoind.height = 10; - var confirmations = bitcoind._getConfirmationsDetail(tx); - confirmations.should.equal(0); - }); - it('should get 1 confirmation', function() { - var tx = new Transaction(txhex); - tx.height = 10; - var bitcoind = new BitcoinService(baseConfig); - bitcoind.height = 10; - var confirmations = bitcoind._getConfirmationsDetail(tx); - confirmations.should.equal(1); - }); - it('should get 2 confirmation', function() { - var bitcoind = new BitcoinService(baseConfig); - var tx = new Transaction(txhex); - bitcoind.height = 11; - tx.height = 10; - var confirmations = bitcoind._getConfirmationsDetail(tx); - confirmations.should.equal(2); - }); - it('should get 0 confirmation with overflow', function() { - var bitcoind = new BitcoinService(baseConfig); - var tx = new Transaction(txhex); - bitcoind.height = 3; - tx.height = 10; - var confirmations = bitcoind._getConfirmationsDetail(tx); - log.warn.callCount.should.equal(1); - confirmations.should.equal(0); - }); - it('should get 1000 confirmation', function() { - var bitcoind = new BitcoinService(baseConfig); - var tx = new Transaction(txhex); - bitcoind.height = 1000; - tx.height = 1; - var confirmations = bitcoind._getConfirmationsDetail(tx); - confirmations.should.equal(1000); - }); - }); - - describe('#_getAddressDetailsForInput', function() { - it('will return if missing an address', function() { - var bitcoind = new BitcoinService(baseConfig); - var result = {}; - bitcoind._getAddressDetailsForInput({}, 0, result, []); - should.not.exist(result.addresses); - should.not.exist(result.satoshis); - }); - it('will only add address if it matches', function() { - var bitcoind = new BitcoinService(baseConfig); - var result = {}; - bitcoind._getAddressDetailsForInput({ - address: 'address1' - }, 0, result, ['address2']); - should.not.exist(result.addresses); - should.not.exist(result.satoshis); - }); - it('will instantiate if outputIndexes not defined', function() { - var bitcoind = new BitcoinService(baseConfig); - var result = { - addresses: {} - }; - bitcoind._getAddressDetailsForInput({ - address: 'address1' - }, 0, result, ['address1']); - should.exist(result.addresses); - result.addresses['address1'].inputIndexes.should.deep.equal([0]); - result.addresses['address1'].outputIndexes.should.deep.equal([]); - }); - it('will push to inputIndexes', function() { - var bitcoind = new BitcoinService(baseConfig); - var result = { - addresses: { - 'address1': { - inputIndexes: [1] - } - } - }; - bitcoind._getAddressDetailsForInput({ - address: 'address1' - }, 2, result, ['address1']); - should.exist(result.addresses); - result.addresses['address1'].inputIndexes.should.deep.equal([1, 2]); - }); - }); - - describe('#_getAddressDetailsForOutput', function() { - it('will return if missing an address', function() { - var bitcoind = new BitcoinService(baseConfig); - var result = {}; - bitcoind._getAddressDetailsForOutput({}, 0, result, []); - should.not.exist(result.addresses); - should.not.exist(result.satoshis); - }); - it('will only add address if it matches', function() { - var bitcoind = new BitcoinService(baseConfig); - var result = {}; - bitcoind._getAddressDetailsForOutput({ - address: 'address1' - }, 0, result, ['address2']); - should.not.exist(result.addresses); - should.not.exist(result.satoshis); - }); - it('will instantiate if outputIndexes not defined', function() { - var bitcoind = new BitcoinService(baseConfig); - var result = { - addresses: {} - }; - bitcoind._getAddressDetailsForOutput({ - address: 'address1' - }, 0, result, ['address1']); - should.exist(result.addresses); - result.addresses['address1'].inputIndexes.should.deep.equal([]); - result.addresses['address1'].outputIndexes.should.deep.equal([0]); - }); - it('will push if outputIndexes defined', function() { - var bitcoind = new BitcoinService(baseConfig); - var result = { - addresses: { - 'address1': { - outputIndexes: [0] - } - } - }; - bitcoind._getAddressDetailsForOutput({ - address: 'address1' - }, 1, result, ['address1']); - should.exist(result.addresses); - result.addresses['address1'].outputIndexes.should.deep.equal([0, 1]); - }); - }); - - describe('#_getAddressDetailsForTransaction', function() { - it('will calculate details for the transaction', function(done) { - /* jshint sub:true */ - var tx = { - inputs: [ - { - satoshis: 1000000000, - address: 'mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW' - } - ], - outputs: [ - { - satoshis: 100000000, - address: 'mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW' - }, - { - satoshis: 200000000, - address: 'mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW' - }, - { - satoshis: 50000000, - address: 'mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW' - }, - { - satoshis: 300000000, - address: 'mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW' - }, - { - satoshis: 349990000, - address: 'mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW' - } - ], - locktime: 0 - }; - var bitcoind = new BitcoinService(baseConfig); - var addresses = ['mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW']; - var details = bitcoind._getAddressDetailsForTransaction(tx, addresses); - should.exist(details.addresses['mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW']); - details.addresses['mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW'].inputIndexes.should.deep.equal([0]); - details.addresses['mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW'].outputIndexes.should.deep.equal([ - 0, 1, 2, 3, 4 - ]); - details.satoshis.should.equal(-10000); - done(); - }); - }); - - describe('#_getAddressDetailedTransaction', function() { - it('will get detailed transaction info', function(done) { - var txid = '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0'; - var tx = { - height: 20, - }; - var bitcoind = new BitcoinService(baseConfig); - bitcoind.getDetailedTransaction = sinon.stub().callsArgWith(1, null, tx); - bitcoind.height = 300; - var addresses = {}; - bitcoind._getAddressDetailsForTransaction = sinon.stub().returns({ - addresses: addresses, - satoshis: 1000, - }); - bitcoind._getAddressDetailedTransaction(txid, {}, function(err, details) { - if (err) { - return done(err); - } - details.addresses.should.equal(addresses); - details.satoshis.should.equal(1000); - details.confirmations.should.equal(281); - details.tx.should.equal(tx); - done(); - }); - }); - it('give error from getDetailedTransaction', function(done) { - var txid = '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0'; - var bitcoind = new BitcoinService(baseConfig); - bitcoind.getDetailedTransaction = sinon.stub().callsArgWith(1, new Error('test')); - bitcoind._getAddressDetailedTransaction(txid, {}, function(err) { - err.should.be.instanceof(Error); - done(); - }); - }); - }); - - describe('#_getAddressStrings', function() { - it('will get address strings from bitcore addresses', function() { - var addresses = [ - bitcore.Address('1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i'), - bitcore.Address('3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou'), - ]; - var bitcoind = new BitcoinService(baseConfig); - var strings = bitcoind._getAddressStrings(addresses); - strings[0].should.equal('1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i'); - strings[1].should.equal('3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou'); - }); - it('will get address strings from strings', function() { - var addresses = [ - '1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i', - '3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou', - ]; - var bitcoind = new BitcoinService(baseConfig); - var strings = bitcoind._getAddressStrings(addresses); - strings[0].should.equal('1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i'); - strings[1].should.equal('3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou'); - }); - it('will get address strings from mixture of types', function() { - var addresses = [ - bitcore.Address('1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i'), - '3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou', - ]; - var bitcoind = new BitcoinService(baseConfig); - var strings = bitcoind._getAddressStrings(addresses); - strings[0].should.equal('1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i'); - strings[1].should.equal('3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou'); - }); - it('will give error with unknown', function() { - var addresses = [ - bitcore.Address('1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i'), - 0, - ]; - var bitcoind = new BitcoinService(baseConfig); - (function() { - bitcoind._getAddressStrings(addresses); - }).should.throw(TypeError); - }); - }); - - describe('#_paginateTxids', function() { - it('slice txids based on "from" and "to" (3 to 13)', function() { - var bitcoind = new BitcoinService(baseConfig); - var txids = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - var paginated = bitcoind._paginateTxids(txids, 3, 13); - paginated.should.deep.equal([3, 4, 5, 6, 7, 8, 9, 10]); - }); - it('slice txids based on "from" and "to" (0 to 3)', function() { - var bitcoind = new BitcoinService(baseConfig); - var txids = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - var paginated = bitcoind._paginateTxids(txids, 0, 3); - paginated.should.deep.equal([0, 1, 2]); - }); - it('slice txids based on "from" and "to" (0 to 1)', function() { - var bitcoind = new BitcoinService(baseConfig); - var txids = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - var paginated = bitcoind._paginateTxids(txids, 0, 1); - paginated.should.deep.equal([0]); - }); - it('will throw error if "from" is greater than "to"', function() { - var bitcoind = new BitcoinService(baseConfig); - var txids = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - (function() { - bitcoind._paginateTxids(txids, 1, 0); - }).should.throw('"from" (1) is expected to be less than "to"'); - }); - it('will handle string numbers', function() { - var bitcoind = new BitcoinService(baseConfig); - var txids = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - var paginated = bitcoind._paginateTxids(txids, '1', '3'); - paginated.should.deep.equal([1, 2]); - }); - }); - - describe('#getAddressHistory', function() { - var address = '12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX'; - it('will give error with "from" and "to" range that exceeds max size', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.getAddressHistory(address, {from: 0, to: 51}, function(err) { - should.exist(err); - err.message.match(/^\"from/); - done(); - }); - }); - it('will give error with "from" and "to" order is reversed', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.getAddressTxids = sinon.stub().callsArgWith(2, null, []); - bitcoind.getAddressHistory(address, {from: 51, to: 0}, function(err) { - should.exist(err); - err.message.match(/^\"from/); - done(); - }); - }); - it('will give error from _getAddressDetailedTransaction', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.getAddressTxids = sinon.stub().callsArgWith(2, null, ['txid']); - bitcoind._getAddressDetailedTransaction = sinon.stub().callsArgWith(2, new Error('test')); - bitcoind.getAddressHistory(address, {}, function(err) { - should.exist(err); - err.message.should.equal('test'); - done(); - }); - }); - it('will give an error if length of addresses is too long', function(done) { - var addresses = []; - for (var i = 0; i < 101; i++) { - addresses.push(address); - } - var bitcoind = new BitcoinService(baseConfig); - bitcoind.maxAddressesQuery = 100; - bitcoind.getAddressHistory(addresses, {}, function(err) { - should.exist(err); - err.message.match(/Maximum/); - done(); - }); - }); - it('give error from getAddressTxids', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.getAddressTxids = sinon.stub().callsArgWith(2, new Error('test')); - bitcoind.getAddressHistory('address', {}, function(err) { - should.exist(err); - err.should.be.instanceof(Error); - err.message.should.equal('test'); - done(); - }); - }); - it('will paginate', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind._getAddressDetailedTransaction = function(txid, options, callback) { - callback(null, txid); - }; - var txids = ['one', 'two', 'three', 'four']; - bitcoind.getAddressTxids = sinon.stub().callsArgWith(2, null, txids); - bitcoind.getAddressHistory('address', {from: 1, to: 3}, function(err, data) { - if (err) { - return done(err); - } - data.items.length.should.equal(2); - data.items.should.deep.equal(['two', 'three']); - done(); - }); - }); - }); - - describe('#getAddressSummary', function() { - var txid1 = '70d9d441d7409aace8e0ffe24ff0190407b2fcb405799a266e0327017288d1f8'; - var txid2 = '35fafaf572341798b2ce2858755afa7c8800bb6b1e885d3e030b81255b5e172d'; - var txid3 = '57b7842afc97a2b46575b490839df46e9273524c6ea59ba62e1e86477cf25247'; - var memtxid1 = 'b1bfa8dbbde790cb46b9763ef3407c1a21c8264b67bfe224f462ec0e1f569e92'; - var memtxid2 = 'e9dcf22807db77ac0276b03cc2d3a8b03c4837db8ac6650501ef45af1c807cce'; - it('will handle error from getAddressTxids', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.nodes.push({ - client: { - getAddressMempool: sinon.stub().callsArgWith(1, null, { - result: [ - { - txid: '70d9d441d7409aace8e0ffe24ff0190407b2fcb405799a266e0327017288d1f8', - } - ] - }) - } - }); - bitcoind.getAddressTxids = sinon.stub().callsArgWith(2, new Error('test')); - bitcoind.getAddressBalance = sinon.stub().callsArgWith(2, null, {}); - var address = ''; - var options = {}; - bitcoind.getAddressSummary(address, options, function(err) { - should.exist(err); - err.should.be.instanceof(Error); - err.message.should.equal('test'); - done(); - }); - }); - it('will handle error from getAddressBalance', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.nodes.push({ - client: { - getAddressMempool: sinon.stub().callsArgWith(1, null, { - result: [ - { - txid: '70d9d441d7409aace8e0ffe24ff0190407b2fcb405799a266e0327017288d1f8', - } - ] - }) - } - }); - bitcoind.getAddressTxids = sinon.stub().callsArgWith(2, null, {}); - bitcoind.getAddressBalance = sinon.stub().callsArgWith(2, new Error('test'), {}); - var address = ''; - var options = {}; - bitcoind.getAddressSummary(address, options, function(err) { - should.exist(err); - err.should.be.instanceof(Error); - err.message.should.equal('test'); - done(); - }); - }); - it('will handle error from client getAddressMempool', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.nodes.push({ - client: { - getAddressMempool: sinon.stub().callsArgWith(1, {code: -1, message: 'Test error'}) - } - }); - bitcoind.getAddressTxids = sinon.stub().callsArgWith(2, null, {}); - bitcoind.getAddressBalance = sinon.stub().callsArgWith(2, null, {}); - var address = ''; - var options = {}; - bitcoind.getAddressSummary(address, options, function(err) { - should.exist(err); - err.should.be.instanceof(Error); - err.message.should.equal('Test error'); - done(); - }); - }); - it('should set all properties', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.nodes.push({ - client: { - getAddressMempool: sinon.stub().callsArgWith(1, null, { - result: [ - { - txid: memtxid1, - satoshis: -1000000 - }, - { - txid: memtxid2, - satoshis: 99999 - } - ] - }) - } - }); - sinon.spy(bitcoind, '_paginateTxids'); - bitcoind.getAddressTxids = sinon.stub().callsArgWith(2, null, [txid1, txid2, txid3]); - bitcoind.getAddressBalance = sinon.stub().callsArgWith(2, null, { - received: 30 * 1e8, - balance: 20 * 1e8 - }); - var address = '3NbU8XzUgKyuCgYgZEKsBtUvkTm2r7Xgwj'; - var options = {}; - bitcoind.getAddressSummary(address, options, function(err, summary) { - bitcoind._paginateTxids.callCount.should.equal(1); - bitcoind._paginateTxids.args[0][1].should.equal(0); - bitcoind._paginateTxids.args[0][2].should.equal(1000); - summary.appearances.should.equal(3); - summary.totalReceived.should.equal(3000000000); - summary.totalSpent.should.equal(1000000000); - summary.balance.should.equal(2000000000); - summary.unconfirmedAppearances.should.equal(2); - summary.unconfirmedBalance.should.equal(-900001); - summary.txids.should.deep.equal([ - 'e9dcf22807db77ac0276b03cc2d3a8b03c4837db8ac6650501ef45af1c807cce', - 'b1bfa8dbbde790cb46b9763ef3407c1a21c8264b67bfe224f462ec0e1f569e92', - '70d9d441d7409aace8e0ffe24ff0190407b2fcb405799a266e0327017288d1f8', - '35fafaf572341798b2ce2858755afa7c8800bb6b1e885d3e030b81255b5e172d', - '57b7842afc97a2b46575b490839df46e9273524c6ea59ba62e1e86477cf25247' - ]); - done(); - }); - }); - it('will give error with "from" and "to" range that exceeds max size', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.nodes.push({ - client: { - getAddressMempool: sinon.stub().callsArgWith(1, null, { - result: [ - { - txid: memtxid1, - satoshis: -1000000 - }, - { - txid: memtxid2, - satoshis: 99999 - } - ] - }) - } - }); - bitcoind.getAddressTxids = sinon.stub().callsArgWith(2, null, [txid1, txid2, txid3]); - bitcoind.getAddressBalance = sinon.stub().callsArgWith(2, null, { - received: 30 * 1e8, - balance: 20 * 1e8 - }); - var address = '3NbU8XzUgKyuCgYgZEKsBtUvkTm2r7Xgwj'; - var options = { - from: 0, - to: 1001 - }; - bitcoind.getAddressSummary(address, options, function(err) { - should.exist(err); - err.message.match(/^\"from/); - done(); - }); - }); - it('will get from cache with noTxList', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.nodes.push({ - client: { - getAddressMempool: sinon.stub().callsArgWith(1, null, { - result: [ - { - txid: memtxid1, - satoshis: -1000000 - }, - { - txid: memtxid2, - satoshis: 99999 - } - ] - }) - } - }); - bitcoind.getAddressTxids = sinon.stub().callsArgWith(2, null, [txid1, txid2, txid3]); - bitcoind.getAddressBalance = sinon.stub().callsArgWith(2, null, { - received: 30 * 1e8, - balance: 20 * 1e8 - }); - var address = '3NbU8XzUgKyuCgYgZEKsBtUvkTm2r7Xgwj'; - var options = { - noTxList: true - }; - function checkSummary(summary) { - summary.appearances.should.equal(3); - summary.totalReceived.should.equal(3000000000); - summary.totalSpent.should.equal(1000000000); - summary.balance.should.equal(2000000000); - summary.unconfirmedAppearances.should.equal(2); - summary.unconfirmedBalance.should.equal(-900001); - should.not.exist(summary.txids); - } - bitcoind.getAddressSummary(address, options, function(err, summary) { - checkSummary(summary); - bitcoind.getAddressTxids.callCount.should.equal(1); - bitcoind.getAddressBalance.callCount.should.equal(1); - bitcoind.getAddressSummary(address, options, function(err, summary) { - checkSummary(summary); - bitcoind.getAddressTxids.callCount.should.equal(1); - bitcoind.getAddressBalance.callCount.should.equal(1); - done(); - }); - }); - }); - it('will skip querying the mempool with queryMempool set to false', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getAddressMempool = sinon.stub(); - bitcoind.nodes.push({ - client: { - getAddressMempool: getAddressMempool - } - }); - sinon.spy(bitcoind, '_paginateTxids'); - bitcoind.getAddressTxids = sinon.stub().callsArgWith(2, null, [txid1, txid2, txid3]); - bitcoind.getAddressBalance = sinon.stub().callsArgWith(2, null, { - received: 30 * 1e8, - balance: 20 * 1e8 - }); - var address = '3NbU8XzUgKyuCgYgZEKsBtUvkTm2r7Xgwj'; - var options = { - queryMempool: false - }; - bitcoind.getAddressSummary(address, options, function() { - getAddressMempool.callCount.should.equal(0); - done(); - }); - }); - it('will give error from _paginateTxids', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getAddressMempool = sinon.stub(); - bitcoind.nodes.push({ - client: { - getAddressMempool: getAddressMempool - } - }); - sinon.spy(bitcoind, '_paginateTxids'); - bitcoind.getAddressTxids = sinon.stub().callsArgWith(2, null, [txid1, txid2, txid3]); - bitcoind.getAddressBalance = sinon.stub().callsArgWith(2, null, { - received: 30 * 1e8, - balance: 20 * 1e8 - }); - bitcoind._paginateTxids = sinon.stub().throws(new Error('test')); - var address = '3NbU8XzUgKyuCgYgZEKsBtUvkTm2r7Xgwj'; - var options = { - queryMempool: false - }; - bitcoind.getAddressSummary(address, options, function(err) { - err.should.be.instanceOf(Error); - err.message.should.equal('test'); - done(); - }); - }); - }); - - describe('#getRawBlock', function() { - var blockhash = '00000000050a6d07f583beba2d803296eb1e9d4980c4a20f206c584e89a4f02b'; - var blockhex = '0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000'; - it('will give rcp error from client getblockhash', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.nodes.push({ - client: { - getBlockHash: sinon.stub().callsArgWith(1, {code: -1, message: 'Test error'}) - } - }); - bitcoind.getRawBlock(10, function(err) { - should.exist(err); - err.should.be.instanceof(errors.RPCError); - done(); - }); - }); - it('will give rcp error from client getblock', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.nodes.push({ - client: { - getBlock: sinon.stub().callsArgWith(2, {code: -1, message: 'Test error'}) - } - }); - bitcoind.getRawBlock(blockhash, function(err) { - should.exist(err); - err.should.be.instanceof(errors.RPCError); - done(); - }); - }); - it('will try all nodes for getblock', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getBlockWithError = sinon.stub().callsArgWith(2, {code: -1, message: 'Test error'}); - bitcoind.tryAllInterval = 1; - bitcoind.nodes.push({ - client: { - getBlock: getBlockWithError - } - }); - bitcoind.nodes.push({ - client: { - getBlock: getBlockWithError - } - }); - bitcoind.nodes.push({ - client: { - getBlock: sinon.stub().callsArgWith(2, null, { - result: blockhex - }) - } - }); - bitcoind.getRawBlock(blockhash, function(err, buffer) { - if (err) { - return done(err); - } - buffer.should.be.instanceof(Buffer); - getBlockWithError.callCount.should.equal(2); - done(); - }); - }); - it('will get block from cache', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getBlock = sinon.stub().callsArgWith(2, null, { - result: blockhex - }); - bitcoind.nodes.push({ - client: { - getBlock: getBlock - } - }); - bitcoind.getRawBlock(blockhash, function(err, buffer) { - if (err) { - return done(err); - } - buffer.should.be.instanceof(Buffer); - getBlock.callCount.should.equal(1); - bitcoind.getRawBlock(blockhash, function(err, buffer) { - if (err) { - return done(err); - } - buffer.should.be.instanceof(Buffer); - getBlock.callCount.should.equal(1); - done(); - }); - }); - }); - it('will get block by height', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getBlock = sinon.stub().callsArgWith(2, null, { - result: blockhex - }); - var getBlockHash = sinon.stub().callsArgWith(1, null, { - result: '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f' - }); - bitcoind.nodes.push({ - client: { - getBlock: getBlock, - getBlockHash: getBlockHash - } - }); - bitcoind.getRawBlock(0, function(err, buffer) { - if (err) { - return done(err); - } - buffer.should.be.instanceof(Buffer); - getBlock.callCount.should.equal(1); - getBlockHash.callCount.should.equal(1); - done(); - }); - }); - }); - - describe('#getBlock', function() { - var blockhex = '0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000'; - it('will give an rpc error from client getblock', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getBlock = sinon.stub().callsArgWith(2, {code: -1, message: 'Test error'}); - var getBlockHash = sinon.stub().callsArgWith(1, null, {}); - bitcoind.nodes.push({ - client: { - getBlock: getBlock, - getBlockHash: getBlockHash - } - }); - bitcoind.getBlock(0, function(err) { - err.should.be.instanceof(Error); - done(); - }); - }); - it('will give an rpc error from client getblockhash', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getBlockHash = sinon.stub().callsArgWith(1, {code: -1, message: 'Test error'}); - bitcoind.nodes.push({ - client: { - getBlockHash: getBlockHash - } - }); - bitcoind.getBlock(0, function(err) { - err.should.be.instanceof(Error); - done(); - }); - }); - it('will getblock as bitcore object from height', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getBlock = sinon.stub().callsArgWith(2, null, { - result: blockhex - }); - var getBlockHash = sinon.stub().callsArgWith(1, null, { - result: '00000000050a6d07f583beba2d803296eb1e9d4980c4a20f206c584e89a4f02b' - }); - bitcoind.nodes.push({ - client: { - getBlock: getBlock, - getBlockHash: getBlockHash - } - }); - bitcoind.getBlock(0, function(err, block) { - should.not.exist(err); - getBlock.args[0][0].should.equal('00000000050a6d07f583beba2d803296eb1e9d4980c4a20f206c584e89a4f02b'); - getBlock.args[0][1].should.equal(false); - block.should.be.instanceof(bitcore.Block); - done(); - }); - }); - it('will getblock as bitcore object', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getBlock = sinon.stub().callsArgWith(2, null, { - result: blockhex - }); - var getBlockHash = sinon.stub(); - bitcoind.nodes.push({ - client: { - getBlock: getBlock, - getBlockHash: getBlockHash - } - }); - bitcoind.getBlock('00000000050a6d07f583beba2d803296eb1e9d4980c4a20f206c584e89a4f02b', function(err, block) { - should.not.exist(err); - getBlockHash.callCount.should.equal(0); - getBlock.callCount.should.equal(1); - getBlock.args[0][0].should.equal('00000000050a6d07f583beba2d803296eb1e9d4980c4a20f206c584e89a4f02b'); - getBlock.args[0][1].should.equal(false); - block.should.be.instanceof(bitcore.Block); - done(); - }); - }); - it('will get block from cache', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getBlock = sinon.stub().callsArgWith(2, null, { - result: blockhex - }); - var getBlockHash = sinon.stub(); - bitcoind.nodes.push({ - client: { - getBlock: getBlock, - getBlockHash: getBlockHash - } - }); - var hash = '00000000050a6d07f583beba2d803296eb1e9d4980c4a20f206c584e89a4f02b'; - bitcoind.getBlock(hash, function(err, block) { - should.not.exist(err); - getBlockHash.callCount.should.equal(0); - getBlock.callCount.should.equal(1); - block.should.be.instanceof(bitcore.Block); - bitcoind.getBlock(hash, function(err, block) { - should.not.exist(err); - getBlockHash.callCount.should.equal(0); - getBlock.callCount.should.equal(1); - block.should.be.instanceof(bitcore.Block); - done(); - }); - }); - }); - it('will get block from cache with height (but not height)', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getBlock = sinon.stub().callsArgWith(2, null, { - result: blockhex - }); - var getBlockHash = sinon.stub().callsArgWith(1, null, { - result: '00000000050a6d07f583beba2d803296eb1e9d4980c4a20f206c584e89a4f02b' - }); - bitcoind.nodes.push({ - client: { - getBlock: getBlock, - getBlockHash: getBlockHash - } - }); - bitcoind.getBlock(0, function(err, block) { - should.not.exist(err); - getBlockHash.callCount.should.equal(1); - getBlock.callCount.should.equal(1); - block.should.be.instanceof(bitcore.Block); - bitcoind.getBlock(0, function(err, block) { - should.not.exist(err); - getBlockHash.callCount.should.equal(2); - getBlock.callCount.should.equal(1); - block.should.be.instanceof(bitcore.Block); - done(); - }); - }); - }); - }); - - describe('#getBlockHashesByTimestamp', function() { - it('should give an rpc error', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getBlockHashes = sinon.stub().callsArgWith(3, {message: 'error', code: -1}); - bitcoind.nodes.push({ - client: { - getBlockHashes: getBlockHashes - } - }); - bitcoind.getBlockHashesByTimestamp(1441911000, 1441914000, function(err, hashes) { - should.exist(err); - err.message.should.equal('error'); - done(); - }); - }); - it('should get the correct block hashes', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var block1 = '00000000050a6d07f583beba2d803296eb1e9d4980c4a20f206c584e89a4f02b'; - var block2 = '000000000383752a55a0b2891ce018fd0fdc0b6352502772b034ec282b4a1bf6'; - var getBlockHashes = sinon.stub().callsArgWith(3, null, { - result: [block2, block1] - }); - bitcoind.nodes.push({ - client: { - getBlockHashes: getBlockHashes - } - }); - bitcoind.getBlockHashesByTimestamp(1441914000, 1441911000, function(err, hashes) { - should.not.exist(err); - hashes.should.deep.equal([block2, block1]); - done(); - }); - }); - }); - - describe('#getBlockHeader', function() { - var blockhash = '00000000050a6d07f583beba2d803296eb1e9d4980c4a20f206c584e89a4f02b'; - it('will give error from getBlockHash', function() { - var bitcoind = new BitcoinService(baseConfig); - var getBlockHash = sinon.stub().callsArgWith(1, {code: -1, message: 'Test error'}); - bitcoind.nodes.push({ - client: { - getBlockHash: getBlockHash - } - }); - bitcoind.getBlockHeader(10, function(err) { - err.should.be.instanceof(Error); - }); - }); - it('it will give rpc error from client getblockheader', function() { - var bitcoind = new BitcoinService(baseConfig); - var getBlockHeader = sinon.stub().callsArgWith(1, {code: -1, message: 'Test error'}); - bitcoind.nodes.push({ - client: { - getBlockHeader: getBlockHeader - } - }); - bitcoind.getBlockHeader(blockhash, function(err) { - err.should.be.instanceof(Error); - }); - }); - it('it will give rpc error from client getblockhash', function() { - var bitcoind = new BitcoinService(baseConfig); - var getBlockHeader = sinon.stub(); - var getBlockHash = sinon.stub().callsArgWith(1, {code: -1, message: 'Test error'}); - bitcoind.nodes.push({ - client: { - getBlockHeader: getBlockHeader, - getBlockHash: getBlockHash - } - }); - bitcoind.getBlockHeader(0, function(err) { - err.should.be.instanceof(Error); - }); - }); - it('will give result from client getblockheader (from height)', function() { - var bitcoind = new BitcoinService(baseConfig); - var result = { - hash: '0000000000000a817cd3a74aec2f2246b59eb2cbb1ad730213e6c4a1d68ec2f6', - version: 536870912, - confirmations: 5, - height: 828781, - chainWork: '00000000000000000000000000000000000000000000000ad467352c93bc6a3b', - prevHash: '0000000000000504235b2aff578a48470dbf6b94dafa9b3703bbf0ed554c9dd9', - nextHash: '00000000000000eedd967ec155f237f033686f0924d574b946caf1b0e89551b8', - merkleRoot: '124e0f3fb5aa268f102b0447002dd9700988fc570efcb3e0b5b396ac7db437a9', - time: 1462979126, - medianTime: 1462976771, - nonce: 2981820714, - bits: '1a13ca10', - difficulty: 847779.0710240941 - }; - var getBlockHeader = sinon.stub().callsArgWith(1, null, { - result: { - hash: '0000000000000a817cd3a74aec2f2246b59eb2cbb1ad730213e6c4a1d68ec2f6', - version: 536870912, - confirmations: 5, - height: 828781, - chainwork: '00000000000000000000000000000000000000000000000ad467352c93bc6a3b', - previousblockhash: '0000000000000504235b2aff578a48470dbf6b94dafa9b3703bbf0ed554c9dd9', - nextblockhash: '00000000000000eedd967ec155f237f033686f0924d574b946caf1b0e89551b8', - merkleroot: '124e0f3fb5aa268f102b0447002dd9700988fc570efcb3e0b5b396ac7db437a9', - time: 1462979126, - mediantime: 1462976771, - nonce: 2981820714, - bits: '1a13ca10', - difficulty: 847779.0710240941 - } - }); - var getBlockHash = sinon.stub().callsArgWith(1, null, { - result: blockhash - }); - bitcoind.nodes.push({ - client: { - getBlockHeader: getBlockHeader, - getBlockHash: getBlockHash - } - }); - bitcoind.getBlockHeader(0, function(err, blockHeader) { - should.not.exist(err); - getBlockHeader.args[0][0].should.equal(blockhash); - blockHeader.should.deep.equal(result); - }); - }); - it('will give result from client getblockheader (from hash)', function() { - var bitcoind = new BitcoinService(baseConfig); - var result = { - hash: '0000000000000a817cd3a74aec2f2246b59eb2cbb1ad730213e6c4a1d68ec2f6', - version: 536870912, - confirmations: 5, - height: 828781, - chainWork: '00000000000000000000000000000000000000000000000ad467352c93bc6a3b', - prevHash: '0000000000000504235b2aff578a48470dbf6b94dafa9b3703bbf0ed554c9dd9', - nextHash: '00000000000000eedd967ec155f237f033686f0924d574b946caf1b0e89551b8', - merkleRoot: '124e0f3fb5aa268f102b0447002dd9700988fc570efcb3e0b5b396ac7db437a9', - time: 1462979126, - medianTime: 1462976771, - nonce: 2981820714, - bits: '1a13ca10', - difficulty: 847779.0710240941 - }; - var getBlockHeader = sinon.stub().callsArgWith(1, null, { - result: { - hash: '0000000000000a817cd3a74aec2f2246b59eb2cbb1ad730213e6c4a1d68ec2f6', - version: 536870912, - confirmations: 5, - height: 828781, - chainwork: '00000000000000000000000000000000000000000000000ad467352c93bc6a3b', - previousblockhash: '0000000000000504235b2aff578a48470dbf6b94dafa9b3703bbf0ed554c9dd9', - nextblockhash: '00000000000000eedd967ec155f237f033686f0924d574b946caf1b0e89551b8', - merkleroot: '124e0f3fb5aa268f102b0447002dd9700988fc570efcb3e0b5b396ac7db437a9', - time: 1462979126, - mediantime: 1462976771, - nonce: 2981820714, - bits: '1a13ca10', - difficulty: 847779.0710240941 - } - }); - var getBlockHash = sinon.stub(); - bitcoind.nodes.push({ - client: { - getBlockHeader: getBlockHeader, - getBlockHash: getBlockHash - } - }); - bitcoind.getBlockHeader(blockhash, function(err, blockHeader) { - should.not.exist(err); - getBlockHash.callCount.should.equal(0); - blockHeader.should.deep.equal(result); - }); - }); - }); - - describe('#_maybeGetBlockHash', function() { - it('will not get block hash with an address', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getBlockHash = sinon.stub(); - bitcoind.nodes.push({ - client: { - getBlockHash: getBlockHash - } - }); - bitcoind._maybeGetBlockHash('2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br', function(err, hash) { - if (err) { - return done(err); - } - getBlockHash.callCount.should.equal(0); - hash.should.equal('2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br'); - done(); - }); - }); - it('will not get block hash with non zero-nine numeric string', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getBlockHash = sinon.stub(); - bitcoind.nodes.push({ - client: { - getBlockHash: getBlockHash - } - }); - bitcoind._maybeGetBlockHash('109a', function(err, hash) { - if (err) { - return done(err); - } - getBlockHash.callCount.should.equal(0); - hash.should.equal('109a'); - done(); - }); - }); - it('will get the block hash if argument is a number', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getBlockHash = sinon.stub().callsArgWith(1, null, { - result: 'blockhash' - }); - bitcoind.nodes.push({ - client: { - getBlockHash: getBlockHash - } - }); - bitcoind._maybeGetBlockHash(10, function(err, hash) { - if (err) { - return done(err); - } - hash.should.equal('blockhash'); - getBlockHash.callCount.should.equal(1); - done(); - }); - }); - it('will get the block hash if argument is a number (as string)', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getBlockHash = sinon.stub().callsArgWith(1, null, { - result: 'blockhash' - }); - bitcoind.nodes.push({ - client: { - getBlockHash: getBlockHash - } - }); - bitcoind._maybeGetBlockHash('10', function(err, hash) { - if (err) { - return done(err); - } - hash.should.equal('blockhash'); - getBlockHash.callCount.should.equal(1); - done(); - }); - }); - it('will try multiple nodes if one fails', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getBlockHash = sinon.stub().callsArgWith(1, null, { - result: 'blockhash' - }); - getBlockHash.onCall(0).callsArgWith(1, {code: -1, message: 'test'}); - bitcoind.tryAllInterval = 1; - bitcoind.nodes.push({ - client: { - getBlockHash: getBlockHash - } - }); - bitcoind.nodes.push({ - client: { - getBlockHash: getBlockHash - } - }); - bitcoind._maybeGetBlockHash(10, function(err, hash) { - if (err) { - return done(err); - } - hash.should.equal('blockhash'); - getBlockHash.callCount.should.equal(2); - done(); - }); - }); - it('will give error from getBlockHash', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getBlockHash = sinon.stub().callsArgWith(1, {code: -1, message: 'test'}); - bitcoind.tryAllInterval = 1; - bitcoind.nodes.push({ - client: { - getBlockHash: getBlockHash - } - }); - bitcoind.nodes.push({ - client: { - getBlockHash: getBlockHash - } - }); - bitcoind._maybeGetBlockHash(10, function(err, hash) { - getBlockHash.callCount.should.equal(2); - err.should.be.instanceOf(Error); - err.message.should.equal('test'); - err.code.should.equal(-1); - done(); - }); - }); - }); - - describe('#getBlockOverview', function() { - var blockhash = '00000000050a6d07f583beba2d803296eb1e9d4980c4a20f206c584e89a4f02b'; - it('will handle error from maybeGetBlockHash', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind._maybeGetBlockHash = sinon.stub().callsArgWith(1, new Error('test')); - bitcoind.getBlockOverview(blockhash, function(err) { - err.should.be.instanceOf(Error); - done(); - }); - }); - it('will give error from client.getBlock', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getBlock = sinon.stub().callsArgWith(2, {code: -1, message: 'test'}); - bitcoind.nodes.push({ - client: { - getBlock: getBlock - } - }); - bitcoind.getBlockOverview(blockhash, function(err) { - err.should.be.instanceOf(Error); - err.message.should.equal('test'); - done(); - }); - }); - it('will give expected result', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var blockResult = { - hash: blockhash, - version: 536870912, - confirmations: 5, - height: 828781, - chainwork: '00000000000000000000000000000000000000000000000ad467352c93bc6a3b', - previousblockhash: '0000000000000504235b2aff578a48470dbf6b94dafa9b3703bbf0ed554c9dd9', - nextblockhash: '00000000000000eedd967ec155f237f033686f0924d574b946caf1b0e89551b8', - merkleroot: '124e0f3fb5aa268f102b0447002dd9700988fc570efcb3e0b5b396ac7db437a9', - time: 1462979126, - mediantime: 1462976771, - nonce: 2981820714, - bits: '1a13ca10', - difficulty: 847779.0710240941 - }; - var getBlock = sinon.stub().callsArgWith(2, null, { - result: blockResult - }); - bitcoind.nodes.push({ - client: { - getBlock: getBlock - } - }); - function checkBlock(blockOverview) { - blockOverview.hash.should.equal('00000000050a6d07f583beba2d803296eb1e9d4980c4a20f206c584e89a4f02b'); - blockOverview.version.should.equal(536870912); - blockOverview.confirmations.should.equal(5); - blockOverview.height.should.equal(828781); - blockOverview.chainWork.should.equal('00000000000000000000000000000000000000000000000ad467352c93bc6a3b'); - blockOverview.prevHash.should.equal('0000000000000504235b2aff578a48470dbf6b94dafa9b3703bbf0ed554c9dd9'); - blockOverview.nextHash.should.equal('00000000000000eedd967ec155f237f033686f0924d574b946caf1b0e89551b8'); - blockOverview.merkleRoot.should.equal('124e0f3fb5aa268f102b0447002dd9700988fc570efcb3e0b5b396ac7db437a9'); - blockOverview.time.should.equal(1462979126); - blockOverview.medianTime.should.equal(1462976771); - blockOverview.nonce.should.equal(2981820714); - blockOverview.bits.should.equal('1a13ca10'); - blockOverview.difficulty.should.equal(847779.0710240941); - } - bitcoind.getBlockOverview(blockhash, function(err, blockOverview) { - if (err) { - return done(err); - } - checkBlock(blockOverview); - bitcoind.getBlockOverview(blockhash, function(err, blockOverview) { - checkBlock(blockOverview); - getBlock.callCount.should.equal(1); - done(); - }); - }); - }); - }); - - describe('#estimateFee', function() { - it('will give rpc error', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var estimateFee = sinon.stub().callsArgWith(1, {message: 'error', code: -1}); - bitcoind.nodes.push({ - client: { - estimateFee: estimateFee - } - }); - bitcoind.estimateFee(1, function(err) { - should.exist(err); - err.should.be.an.instanceof(errors.RPCError); - done(); - }); - }); - it('will call client estimateFee and give result', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var estimateFee = sinon.stub().callsArgWith(1, null, { - result: -1 - }); - bitcoind.nodes.push({ - client: { - estimateFee: estimateFee - } - }); - bitcoind.estimateFee(1, function(err, feesPerKb) { - if (err) { - return done(err); - } - feesPerKb.should.equal(-1); - done(); - }); - }); - }); - - describe('#sendTransaction', function(done) { - var tx = bitcore.Transaction(txhex); - it('will give rpc error', function() { - var bitcoind = new BitcoinService(baseConfig); - var sendRawTransaction = sinon.stub().callsArgWith(2, {message: 'error', code: -1}); - bitcoind.nodes.push({ - client: { - sendRawTransaction: sendRawTransaction - } - }); - bitcoind.sendTransaction(txhex, function(err) { - should.exist(err); - err.should.be.an.instanceof(errors.RPCError); - }); - }); - it('will send to client and get hash', function() { - var bitcoind = new BitcoinService(baseConfig); - var sendRawTransaction = sinon.stub().callsArgWith(2, null, { - result: tx.hash - }); - bitcoind.nodes.push({ - client: { - sendRawTransaction: sendRawTransaction - } - }); - bitcoind.sendTransaction(txhex, function(err, hash) { - if (err) { - return done(err); - } - hash.should.equal(tx.hash); - }); - }); - it('will send to client with absurd fees and get hash', function() { - var bitcoind = new BitcoinService(baseConfig); - var sendRawTransaction = sinon.stub().callsArgWith(2, null, { - result: tx.hash - }); - bitcoind.nodes.push({ - client: { - sendRawTransaction: sendRawTransaction - } - }); - bitcoind.sendTransaction(txhex, {allowAbsurdFees: true}, function(err, hash) { - if (err) { - return done(err); - } - hash.should.equal(tx.hash); - }); - }); - it('missing callback will throw error', function() { - var bitcoind = new BitcoinService(baseConfig); - var sendRawTransaction = sinon.stub().callsArgWith(2, null, { - result: tx.hash - }); - bitcoind.nodes.push({ - client: { - sendRawTransaction: sendRawTransaction - } - }); - var transaction = bitcore.Transaction(); - (function() { - bitcoind.sendTransaction(transaction); - }).should.throw(Error); - }); - }); - - describe('#getRawTransaction', function() { - it('will give rpc error', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getRawTransaction = sinon.stub().callsArgWith(1, {message: 'error', code: -1}); - bitcoind.nodes.push({ - client: { - getRawTransaction: getRawTransaction - } - }); - bitcoind.getRawTransaction('txid', function(err) { - should.exist(err); - err.should.be.an.instanceof(errors.RPCError); - done(); - }); - }); - it('will try all nodes', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.tryAllInterval = 1; - var getRawTransactionWithError = sinon.stub().callsArgWith(1, {message: 'error', code: -1}); - var getRawTransaction = sinon.stub().callsArgWith(1, null, { - result: txhex - }); - bitcoind.nodes.push({ - client: { - getRawTransaction: getRawTransactionWithError - } - }); - bitcoind.nodes.push({ - client: { - getRawTransaction: getRawTransactionWithError - } - }); - bitcoind.nodes.push({ - client: { - getRawTransaction: getRawTransaction - } - }); - bitcoind.getRawTransaction('txid', function(err, tx) { - if (err) { - return done(err); - } - should.exist(tx); - tx.should.be.an.instanceof(Buffer); - done(); - }); - }); - it('will get from cache', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getRawTransaction = sinon.stub().callsArgWith(1, null, { - result: txhex - }); - bitcoind.nodes.push({ - client: { - getRawTransaction: getRawTransaction - } - }); - bitcoind.getRawTransaction('txid', function(err, tx) { - if (err) { - return done(err); - } - should.exist(tx); - tx.should.be.an.instanceof(Buffer); - - bitcoind.getRawTransaction('txid', function(err, tx) { - should.exist(tx); - tx.should.be.an.instanceof(Buffer); - getRawTransaction.callCount.should.equal(1); - done(); - }); - }); - }); - }); - - describe('#getTransaction', function() { - it('will give rpc error', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getRawTransaction = sinon.stub().callsArgWith(1, {message: 'error', code: -1}); - bitcoind.nodes.push({ - client: { - getRawTransaction: getRawTransaction - } - }); - bitcoind.getTransaction('txid', function(err) { - should.exist(err); - err.should.be.an.instanceof(errors.RPCError); - done(); - }); - }); - it('will try all nodes', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.tryAllInterval = 1; - var getRawTransactionWithError = sinon.stub().callsArgWith(1, {message: 'error', code: -1}); - var getRawTransaction = sinon.stub().callsArgWith(1, null, { - result: txhex - }); - bitcoind.nodes.push({ - client: { - getRawTransaction: getRawTransactionWithError - } - }); - bitcoind.nodes.push({ - client: { - getRawTransaction: getRawTransactionWithError - } - }); - bitcoind.nodes.push({ - client: { - getRawTransaction: getRawTransaction - } - }); - bitcoind.getTransaction('txid', function(err, tx) { - if (err) { - return done(err); - } - should.exist(tx); - tx.should.be.an.instanceof(bitcore.Transaction); - done(); - }); - }); - it('will get from cache', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getRawTransaction = sinon.stub().callsArgWith(1, null, { - result: txhex - }); - bitcoind.nodes.push({ - client: { - getRawTransaction: getRawTransaction - } - }); - bitcoind.getTransaction('txid', function(err, tx) { - if (err) { - return done(err); - } - should.exist(tx); - tx.should.be.an.instanceof(bitcore.Transaction); - - bitcoind.getTransaction('txid', function(err, tx) { - should.exist(tx); - tx.should.be.an.instanceof(bitcore.Transaction); - getRawTransaction.callCount.should.equal(1); - done(); - }); - - }); - }); - }); - - describe('#getDetailedTransaction', function() { - var txBuffer = new Buffer('01000000016f95980911e01c2c664b3e78299527a47933aac61a515930a8fe0213d1ac9abe01000000da0047304402200e71cda1f71e087c018759ba3427eb968a9ea0b1decd24147f91544629b17b4f0220555ee111ed0fc0f751ffebf097bdf40da0154466eb044e72b6b3dcd5f06807fa01483045022100c86d6c8b417bff6cc3bbf4854c16bba0aaca957e8f73e19f37216e2b06bb7bf802205a37be2f57a83a1b5a8cc511dc61466c11e9ba053c363302e7b99674be6a49fc0147522102632178d046673c9729d828cfee388e121f497707f810c131e0d3fc0fe0bd66d62103a0951ec7d3a9da9de171617026442fcd30f34d66100fab539853b43f508787d452aeffffffff0240420f000000000017a9148a31d53a448c18996e81ce67811e5fb7da21e4468738c9d6f90000000017a9148ce5408cfeaddb7ccb2545ded41ef478109454848700000000', 'hex'); - var info = { - blockHash: '00000000000ec715852ea2ecae4dc8563f62d603c820f81ac284cd5be0a944d6', - height: 530482, - timestamp: 1439559434000, - buffer: txBuffer - }; - var rpcRawTransaction = { - hex: txBuffer.toString('hex'), - blockhash: info.blockHash, - height: info.height, - version: 1, - locktime: 411451, - time: info.timestamp, - vin: [ - { - valueSat: 110, - address: 'mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW', - txid: '3d003413c13eec3fa8ea1fe8bbff6f40718c66facffe2544d7516c9e2900cac2', - sequence: 0xFFFFFFFF, - vout: 0, - scriptSig: { - hex: 'scriptSigHex', - asm: 'scriptSigAsm' - } - } - ], - vout: [ - { - spentTxId: '4316b98e7504073acd19308b4b8c9f4eeb5e811455c54c0ebfe276c0b1eb6315', - spentIndex: 2, - spentHeight: 100, - valueSat: 100, - scriptPubKey: { - hex: '76a9140b2f0a0c31bfe0406b0ccc1381fdbe311946dadc88ac', - asm: 'OP_DUP OP_HASH160 0b2f0a0c31bfe0406b0ccc1381fdbe311946dadc OP_EQUALVERIFY OP_CHECKSIG', - addresses: ['mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW'] - } - } - ] - }; - it('should give a transaction with height and timestamp', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.nodes.push({ - client: { - getRawTransaction: sinon.stub().callsArgWith(2, {code: -1, message: 'Test error'}) - } - }); - var txid = '2d950d00494caf6bfc5fff2a3f839f0eb50f663ae85ce092bc5f9d45296ae91f'; - bitcoind.getDetailedTransaction(txid, function(err) { - should.exist(err); - err.should.be.instanceof(errors.RPCError); - done(); - }); - }); - it('should give a transaction with all properties', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getRawTransaction = sinon.stub().callsArgWith(2, null, { - result: rpcRawTransaction - }); - bitcoind.nodes.push({ - client: { - getRawTransaction: getRawTransaction - } - }); - var txid = '2d950d00494caf6bfc5fff2a3f839f0eb50f663ae85ce092bc5f9d45296ae91f'; - function checkTx(tx) { - /* jshint maxstatements: 30 */ - should.exist(tx); - should.not.exist(tx.coinbase); - should.equal(tx.hex, txBuffer.toString('hex')); - should.equal(tx.blockHash, '00000000000ec715852ea2ecae4dc8563f62d603c820f81ac284cd5be0a944d6'); - should.equal(tx.height, 530482); - should.equal(tx.blockTimestamp, 1439559434000); - should.equal(tx.version, 1); - should.equal(tx.locktime, 411451); - should.equal(tx.feeSatoshis, 10); - should.equal(tx.inputSatoshis, 110); - should.equal(tx.outputSatoshis, 100); - should.equal(tx.hash, txid); - var input = tx.inputs[0]; - should.equal(input.prevTxId, '3d003413c13eec3fa8ea1fe8bbff6f40718c66facffe2544d7516c9e2900cac2'); - should.equal(input.outputIndex, 0); - should.equal(input.satoshis, 110); - should.equal(input.sequence, 0xFFFFFFFF); - should.equal(input.script, 'scriptSigHex'); - should.equal(input.scriptAsm, 'scriptSigAsm'); - should.equal(input.address, 'mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW'); - var output = tx.outputs[0]; - should.equal(output.satoshis, 100); - should.equal(output.script, '76a9140b2f0a0c31bfe0406b0ccc1381fdbe311946dadc88ac'); - should.equal(output.scriptAsm, 'OP_DUP OP_HASH160 0b2f0a0c31bfe0406b0ccc1381fdbe311946dadc OP_EQUALVERIFY OP_CHECKSIG'); - should.equal(output.address, 'mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW'); - should.equal(output.spentTxId, '4316b98e7504073acd19308b4b8c9f4eeb5e811455c54c0ebfe276c0b1eb6315'); - should.equal(output.spentIndex, 2); - should.equal(output.spentHeight, 100); - } - bitcoind.getDetailedTransaction(txid, function(err, tx) { - if (err) { - return done(err); - } - checkTx(tx); - bitcoind.getDetailedTransaction(txid, function(err, tx) { - if (err) { - return done(err); - } - checkTx(tx); - getRawTransaction.callCount.should.equal(1); - done(); - }); - }); - }); - it('should set coinbase to true', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var rawTransaction = JSON.parse((JSON.stringify(rpcRawTransaction))); - delete rawTransaction.vin[0]; - rawTransaction.vin = [ - { - coinbase: 'abcdef' - } - ]; - bitcoind.nodes.push({ - client: { - getRawTransaction: sinon.stub().callsArgWith(2, null, { - result: rawTransaction - }) - } - }); - var txid = '2d950d00494caf6bfc5fff2a3f839f0eb50f663ae85ce092bc5f9d45296ae91f'; - bitcoind.getDetailedTransaction(txid, function(err, tx) { - should.exist(tx); - should.equal(tx.coinbase, true); - done(); - }); - }); - it('will not include address if address length is zero', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var rawTransaction = JSON.parse((JSON.stringify(rpcRawTransaction))); - rawTransaction.vout[0].scriptPubKey.addresses = []; - bitcoind.nodes.push({ - client: { - getRawTransaction: sinon.stub().callsArgWith(2, null, { - result: rawTransaction - }) - } - }); - var txid = '2d950d00494caf6bfc5fff2a3f839f0eb50f663ae85ce092bc5f9d45296ae91f'; - bitcoind.getDetailedTransaction(txid, function(err, tx) { - should.exist(tx); - should.equal(tx.outputs[0].address, null); - done(); - }); - }); - it('will not include address if address length is greater than 1', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var rawTransaction = JSON.parse((JSON.stringify(rpcRawTransaction))); - rawTransaction.vout[0].scriptPubKey.addresses = ['one', 'two']; - bitcoind.nodes.push({ - client: { - getRawTransaction: sinon.stub().callsArgWith(2, null, { - result: rawTransaction - }) - } - }); - var txid = '2d950d00494caf6bfc5fff2a3f839f0eb50f663ae85ce092bc5f9d45296ae91f'; - bitcoind.getDetailedTransaction(txid, function(err, tx) { - should.exist(tx); - should.equal(tx.outputs[0].address, null); - done(); - }); - }); - it('will handle scriptPubKey.addresses not being set', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var rawTransaction = JSON.parse((JSON.stringify(rpcRawTransaction))); - delete rawTransaction.vout[0].scriptPubKey['addresses']; - bitcoind.nodes.push({ - client: { - getRawTransaction: sinon.stub().callsArgWith(2, null, { - result: rawTransaction - }) - } - }); - var txid = '2d950d00494caf6bfc5fff2a3f839f0eb50f663ae85ce092bc5f9d45296ae91f'; - bitcoind.getDetailedTransaction(txid, function(err, tx) { - should.exist(tx); - should.equal(tx.outputs[0].address, null); - done(); - }); - }); - it('will not include script if input missing scriptSig or coinbase', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var rawTransaction = JSON.parse((JSON.stringify(rpcRawTransaction))); - delete rawTransaction.vin[0].scriptSig; - delete rawTransaction.vin[0].coinbase; - bitcoind.nodes.push({ - client: { - getRawTransaction: sinon.stub().callsArgWith(2, null, { - result: rawTransaction - }) - } - }); - var txid = '2d950d00494caf6bfc5fff2a3f839f0eb50f663ae85ce092bc5f9d45296ae91f'; - bitcoind.getDetailedTransaction(txid, function(err, tx) { - should.exist(tx); - should.equal(tx.inputs[0].script, null); - done(); - }); - }); - it('will set height to -1 if missing height', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var rawTransaction = JSON.parse((JSON.stringify(rpcRawTransaction))); - delete rawTransaction.height; - bitcoind.nodes.push({ - client: { - getRawTransaction: sinon.stub().callsArgWith(2, null, { - result: rawTransaction - }) - } - }); - var txid = '2d950d00494caf6bfc5fff2a3f839f0eb50f663ae85ce092bc5f9d45296ae91f'; - bitcoind.getDetailedTransaction(txid, function(err, tx) { - should.exist(tx); - should.equal(tx.height, -1); - done(); - }); - }); - }); - - describe('#getBestBlockHash', function() { - it('will give rpc error', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getBestBlockHash = sinon.stub().callsArgWith(0, {message: 'error', code: -1}); - bitcoind.nodes.push({ - client: { - getBestBlockHash: getBestBlockHash - } - }); - bitcoind.getBestBlockHash(function(err) { - should.exist(err); - err.should.be.an.instanceof(errors.RPCError); - done(); - }); - }); - it('will call client getInfo and give result', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getBestBlockHash = sinon.stub().callsArgWith(0, null, { - result: 'besthash' - }); - bitcoind.nodes.push({ - client: { - getBestBlockHash: getBestBlockHash - } - }); - bitcoind.getBestBlockHash(function(err, hash) { - if (err) { - return done(err); - } - should.exist(hash); - hash.should.equal('besthash'); - done(); - }); - }); - }); - - describe('#getSpentInfo', function() { - it('will give rpc error', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getSpentInfo = sinon.stub().callsArgWith(1, {message: 'error', code: -1}); - bitcoind.nodes.push({ - client: { - getSpentInfo: getSpentInfo - } - }); - bitcoind.getSpentInfo({}, function(err) { - should.exist(err); - err.should.be.an.instanceof(errors.RPCError); - done(); - }); - }); - it('will empty object when not found', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getSpentInfo = sinon.stub().callsArgWith(1, {message: 'test', code: -5}); - bitcoind.nodes.push({ - client: { - getSpentInfo: getSpentInfo - } - }); - bitcoind.getSpentInfo({}, function(err, info) { - should.not.exist(err); - info.should.deep.equal({}); - done(); - }); - }); - it('will call client getSpentInfo and give result', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getSpentInfo = sinon.stub().callsArgWith(1, null, { - result: { - txid: 'txid', - index: 10, - height: 101 - } - }); - bitcoind.nodes.push({ - client: { - getSpentInfo: getSpentInfo - } - }); - bitcoind.getSpentInfo({}, function(err, info) { - if (err) { - return done(err); - } - info.txid.should.equal('txid'); - info.index.should.equal(10); - info.height.should.equal(101); - done(); - }); - }); - }); - - describe('#getInfo', function() { - it('will give rpc error', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var getInfo = sinon.stub().callsArgWith(0, {message: 'error', code: -1}); - bitcoind.nodes.push({ - client: { - getInfo: getInfo - } - }); - bitcoind.getInfo(function(err) { - should.exist(err); - err.should.be.an.instanceof(errors.RPCError); - done(); - }); - }); - it('will call client getInfo and give result', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.node.getNetworkName = sinon.stub().returns('testnet'); - var getInfo = sinon.stub().callsArgWith(0, null, { - result: { - version: 1, - protocolversion: 1, - blocks: 1, - timeoffset: 1, - connections: 1, - proxy: '', - difficulty: 1, - testnet: true, - relayfee: 10, - errors: '' - } - }); - bitcoind.nodes.push({ - client: { - getInfo: getInfo - } - }); - bitcoind.getInfo(function(err, info) { - if (err) { - return done(err); - } - should.exist(info); - should.equal(info.version, 1); - should.equal(info.protocolVersion, 1); - should.equal(info.blocks, 1); - should.equal(info.timeOffset, 1); - should.equal(info.connections, 1); - should.equal(info.proxy, ''); - should.equal(info.difficulty, 1); - should.equal(info.testnet, true); - should.equal(info.relayFee, 10); - should.equal(info.errors, ''); - info.network.should.equal('testnet'); - done(); - }); - }); - }); - - describe('#generateBlock', function() { - it('will give rpc error', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var generate = sinon.stub().callsArgWith(1, {message: 'error', code: -1}); - bitcoind.nodes.push({ - client: { - generate: generate - } - }); - bitcoind.generateBlock(10, function(err) { - should.exist(err); - err.should.be.an.instanceof(errors.RPCError); - done(); - }); - }); - it('will call client generate and give result', function(done) { - var bitcoind = new BitcoinService(baseConfig); - var generate = sinon.stub().callsArgWith(1, null, { - result: ['hash'] - }); - bitcoind.nodes.push({ - client: { - generate: generate - } - }); - bitcoind.generateBlock(10, function(err, hashes) { - if (err) { - return done(err); - } - hashes.length.should.equal(1); - hashes[0].should.equal('hash'); - done(); - }); - }); - }); - - describe('#stop', function() { - it('will callback if spawn is not set', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.stop(done); - }); - it('will exit spawned process', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.spawn = {}; - bitcoind.spawn.process = new EventEmitter(); - bitcoind.spawn.process.kill = sinon.stub(); - bitcoind.stop(done); - bitcoind.spawn.process.kill.callCount.should.equal(1); - bitcoind.spawn.process.kill.args[0][0].should.equal('SIGINT'); - bitcoind.spawn.process.emit('exit', 0); - }); - it('will give error with non-zero exit status code', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.spawn = {}; - bitcoind.spawn.process = new EventEmitter(); - bitcoind.spawn.process.kill = sinon.stub(); - bitcoind.stop(function(err) { - err.should.be.instanceof(Error); - err.code.should.equal(1); - done(); - }); - bitcoind.spawn.process.kill.callCount.should.equal(1); - bitcoind.spawn.process.kill.args[0][0].should.equal('SIGINT'); - bitcoind.spawn.process.emit('exit', 1); - }); - it('will stop after timeout', function(done) { - var bitcoind = new BitcoinService(baseConfig); - bitcoind.shutdownTimeout = 300; - bitcoind.spawn = {}; - bitcoind.spawn.process = new EventEmitter(); - bitcoind.spawn.process.kill = sinon.stub(); - bitcoind.stop(function(err) { - err.should.be.instanceof(Error); - done(); - }); - bitcoind.spawn.process.kill.callCount.should.equal(1); - bitcoind.spawn.process.kill.args[0][0].should.equal('SIGINT'); - }); - }); - -}); diff --git a/test/services/wallet-api/encoding.unit.js b/test/services/wallet-api/encoding.unit.js deleted file mode 100644 index 0d547b8a..00000000 --- a/test/services/wallet-api/encoding.unit.js +++ /dev/null @@ -1,198 +0,0 @@ -'use strict'; - -var should = require('chai').should(); -var bitcore = require('bitcore-lib'); - -var Encoding = require('../../../lib/services/wallet-api/encoding'); - -describe('Wallet-Api service encoding', function() { - - var servicePrefix = new Buffer('0000', 'hex'); - var encoding = new Encoding(servicePrefix); - var walletId = 'abcdef123456'; - var txid = '91b58f19b6eecba94ed0f6e463e8e334ec0bcda7880e2985c82a8f32e4d03add'; - var address = '1EZBqbJSHFKSkVPNKzc5v26HA6nAHiTXq6'; - var height = 1; - var addressSizeBuf = new Buffer(1); - addressSizeBuf.writeUInt8(address.length); - var outputIndex = 5; - var txHex = '0100000001cc3ffe0638792c8b39328bb490caaefe2cf418f2ce0144956e0c22515f29724d010000006a473044022030ce9fa68d1a32abf0cd4adecf90fb998375b64fe887c6987278452b068ae74c022036a7d00d1c8af19e298e04f14294c807ebda51a20389ad751b4ff3c032cf8990012103acfcb348abb526526a9f63214639d79183871311c05b2eebc727adfdd016514fffffffff02f6ae7d04000000001976a9144455183e407ee4d3423858c8a3275918aedcd18e88aca99b9b08010000001976a9140beceae2c29bfde08d2b6d80b33067451c5887be88ac00000000'; - var tx = new bitcore.Transaction(txHex); - var sats = tx.outputs[0].satoshis; - var satsBuf = new Buffer(8); - satsBuf.writeDoubleBE(sats); - - it('should encode wallet transaction key' , function() { - encoding.encodeWalletTransactionKey(walletId, height, txid).should.deep.equal(Buffer.concat([ - servicePrefix, - encoding.subKeyMap.transaction.buffer, - new Buffer('0c', 'hex'), - new Buffer(walletId), - new Buffer('00000001', 'hex'), - new Buffer(txid, 'hex') - ])); - }); - - it('should encode wallet transaction key without a txid (all zeroes) or height (0)' , function() { - encoding.encodeWalletTransactionKey(walletId).should.deep.equal(Buffer.concat([ - servicePrefix, - encoding.subKeyMap.transaction.buffer, - new Buffer('0c', 'hex'), - new Buffer(walletId), - new Buffer('00000000', 'hex'), - new Buffer(new Array(65).join('0'), 'hex') - ])); - }); - - it('should decode wallet transaction key', function() { - var walletTransactionKey = encoding.decodeWalletTransactionKey(Buffer.concat([ - servicePrefix, - encoding.subKeyMap.transaction.buffer, - new Buffer('0c', 'hex'), - new Buffer(walletId), - new Buffer('00000001', 'hex') - ])); - walletTransactionKey.walletId.should.equal(walletId); - walletTransactionKey.height.should.equal(height); - }); - - it('should encode wallet utxo key', function() { - encoding.encodeWalletUtxoKey(walletId, txid, outputIndex).should.deep.equal(Buffer.concat([ - servicePrefix, - encoding.subKeyMap.utxo.buffer, - new Buffer('0c', 'hex'), - new Buffer(walletId), - new Buffer(txid, 'hex'), - new Buffer('00000005', 'hex')])); - }); - - it('should decode wallet utxo key', function() { - var walletUtxoKey = encoding.decodeWalletUtxoKey(Buffer.concat([ - servicePrefix, - encoding.subKeyMap.utxo.buffer, - new Buffer('0c', 'hex'), - new Buffer(walletId), - new Buffer(txid, 'hex'), - new Buffer('00000005', 'hex')])); - walletUtxoKey.walletId.should.equal(walletId); - walletUtxoKey.txid.should.equal(txid); - walletUtxoKey.outputIndex.should.equal(outputIndex); - }); - - it('should encode wallet utxo value', function() { - encoding.encodeWalletUtxoValue(height, sats, tx.outputs[0]._scriptBuffer).should.deep.equal(Buffer.concat([ - new Buffer('00000001', 'hex'), - satsBuf, - tx.outputs[0]._scriptBuffer])); - }); - - it('should decode wallet utxo value', function() { - var walletUtxoValue = encoding.decodeWalletUtxoValue(Buffer.concat([ - new Buffer('00000001', 'hex'), - satsBuf, - tx.outputs[0]._scriptBuffer])); - walletUtxoValue.height.should.equal(height); - walletUtxoValue.satoshis.should.equal(sats); - walletUtxoValue.script.should.deep.equal(tx.outputs[0]._scriptBuffer); - }); - - it('should encode wallet utxo satoshis key', function() { - encoding.encodeWalletUtxoSatoshisKey(walletId, sats, txid, outputIndex).should.deep.equal(Buffer.concat([ - servicePrefix, - encoding.subKeyMap.utxoSat.buffer, - new Buffer('0c', 'hex'), - new Buffer(walletId), - satsBuf, - new Buffer(txid, 'hex'), - new Buffer('00000005', 'hex')])); - }); - - it('should decode wallet utxo satoshis key', function() { - var walletUtxoSatoshisKey = encoding.decodeWalletUtxoSatoshisKey(Buffer.concat([ - servicePrefix, - encoding.subKeyMap.utxoSat.buffer, - new Buffer('0c', 'hex'), - new Buffer(walletId), - satsBuf, - new Buffer(txid, 'hex'), - new Buffer('00000005', 'hex')])); - walletUtxoSatoshisKey.walletId.should.equal(walletId); - walletUtxoSatoshisKey.satoshis.should.equal(sats); - walletUtxoSatoshisKey.txid.should.equal(txid); - walletUtxoSatoshisKey.outputIndex.should.equal(outputIndex); - }); - - it('should encode wallet utxo satoshis value', function() { - encoding.encodeWalletUtxoSatoshisValue(height, tx.outputs[0]._scriptBuffer).should.deep.equal(Buffer.concat([ - new Buffer('00000001', 'hex'), - tx.outputs[0]._scriptBuffer - ])); - }); - - it('should decode wallet utxo satoshis value', function() { - var walletUtxoSatoshisValue = encoding.decodeWalletUtxoSatoshisValue(Buffer.concat([ - new Buffer('00000001', 'hex'), - tx.outputs[0]._scriptBuffer - ])); - walletUtxoSatoshisValue.height.should.equal(height); - walletUtxoSatoshisValue.script.should.deep.equal(tx.outputs[0]._scriptBuffer); - }); - - it('should encode wallet addresses key', function() { - encoding.encodeWalletAddressesKey(walletId).should.deep.equal(Buffer.concat([ - servicePrefix, - encoding.subKeyMap.addresses.buffer, - new Buffer('0c', 'hex'), - new Buffer(walletId) - ])); - }); - - it('should decode wallet addresses key', function() { - encoding.decodeWalletAddressesKey(Buffer.concat([ - servicePrefix, - encoding.subKeyMap.addresses.buffer, - new Buffer('0c', 'hex'), - new Buffer(walletId) - ])).should.equal(walletId); - }); - - it('should encode wallet addresses value', function() { - encoding.encodeWalletAddressesValue(['a']).should.deep.equal(Buffer.concat([ - new Buffer('00000001', 'hex'), - new Buffer('01', 'hex'), - new Buffer('a')])); - }); - - it('should decode wallet addresses value', function() { - encoding.decodeWalletAddressesValue(Buffer.concat([ - new Buffer('00000001', 'hex'), - new Buffer('01', 'hex'), - new Buffer('a')])).should.deep.equal(['a']); - }); - - it('should encode wallet balance key', function() { - encoding.encodeWalletBalanceKey(walletId).should.deep.equal(Buffer.concat([ - servicePrefix, - encoding.subKeyMap.balance.buffer, - new Buffer('0c', 'hex'), - new Buffer(walletId) - ])); - }); - - it('should decode wallet balance key', function() { - encoding.decodeWalletBalanceKey(Buffer.concat([ - servicePrefix, - encoding.subKeyMap.balance.buffer, - new Buffer('0c', 'hex'), - new Buffer(walletId) - ])).should.equal(walletId); - }); - - it('should encode wallet balance value', function() { - encoding.encodeWalletBalanceValue(sats).should.deep.equal(satsBuf); - }); - - it('should decode wallet balance value', function() { - encoding.decodeWalletBalanceValue(satsBuf).should.equal(sats); - }); -}); diff --git a/test/services/wallet-api/utils.js b/test/services/wallet-api/utils.js deleted file mode 100644 index f38ba161..00000000 --- a/test/services/wallet-api/utils.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; - -var should = require('chai').should(); -var utils = require('../../../lib/services/wallet-api/utils'); - -describe('Wallet-Api service utils', function() { - it('should create jsonl from obj', function() { - var obj = { - 1: 'test', - 'foo bar': 'test1', - array: [1,2,'test'] - }; - utils.toJSONL(obj).should.equal('{"1":"test","foo bar":"test1","array":[1,2,"test"]}\n'); - }); -});