From 32155cead7808ff349abc7cf5f377003a884b092 Mon Sep 17 00:00:00 2001 From: Chris Kleeschulte Date: Mon, 28 Aug 2017 14:00:32 -0400 Subject: [PATCH] Added regtests for all routes. --- package-lock.json | 299 +++++++++++++ package.json | 7 +- regtest/address.js | 766 +++++++++++++++++++++++++++++++++ regtest/block.js | 500 +++++++++++++++++++++ regtest/data/blocks.json | 10 + regtest/data/blocks_reorg.json | 4 + regtest/reorg.js | 277 ++++++++++++ regtest/status.js | 549 +++++++++++++++++++++++ regtest/transaction.js | 448 +++++++++++++++++++ test/addresses.js | 4 +- test/transactions.js | 200 ++++----- 11 files changed, 2961 insertions(+), 103 deletions(-) create mode 100644 regtest/address.js create mode 100644 regtest/block.js create mode 100644 regtest/data/blocks.json create mode 100644 regtest/data/blocks_reorg.json create mode 100644 regtest/reorg.js create mode 100644 regtest/status.js create mode 100644 regtest/transaction.js diff --git a/package-lock.json b/package-lock.json index 6036300..6536873 100644 --- a/package-lock.json +++ b/package-lock.json @@ -65,6 +65,12 @@ "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", "optional": true }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, "base64-arraybuffer": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", @@ -143,6 +149,12 @@ "safe-buffer": "5.1.1" } }, + "bitcoind-rpc": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/bitcoind-rpc/-/bitcoind-rpc-0.7.0.tgz", + "integrity": "sha1-2/YkkmfuMovkjeV3hwQpgAksi/I=", + "dev": true + }, "bitcore-lib": { "version": "5.0.0-beta.1", "resolved": "https://registry.npmjs.org/bitcore-lib/-/bitcore-lib-5.0.0-beta.1.tgz", @@ -239,6 +251,95 @@ } } }, + "bitcore-p2p": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/bitcore-p2p/-/bitcore-p2p-1.1.2.tgz", + "integrity": "sha1-F5J8UiE92vDLQGczMrozleQAi1Q=", + "dev": true, + "requires": { + "bitcore-lib": "0.14.0", + "bloom-filter": "0.2.0", + "buffers": "github:bitpay/node-buffers#04f4c4264e0d105db2b99b786843ed64f23230d8", + "socks5-client": "0.3.6" + }, + "dependencies": { + "bitcore-lib": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/bitcore-lib/-/bitcore-lib-0.14.0.tgz", + "integrity": "sha1-IcsjWf57mXo7e3c+t9cnWuN9ZE4=", + "dev": true, + "requires": { + "bn.js": "2.0.4", + "bs58": "2.0.0", + "buffer-compare": "1.0.0", + "elliptic": "3.0.3", + "inherits": "2.0.1", + "lodash": "3.10.1" + }, + "dependencies": { + "bn.js": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-2.0.4.tgz", + "integrity": "sha1-Igp81nf38b+pNif/QZN3b+eBlIA=", + "dev": true + }, + "bs58": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-2.0.0.tgz", + "integrity": "sha1-crcTvtIjoKxRi72g484/SBfznrU=", + "dev": true + }, + "buffer-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-compare/-/buffer-compare-1.0.0.tgz", + "integrity": "sha1-rKp6lm6Y7un64Usxw5pfFY+zxKI=", + "dev": true + }, + "elliptic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-3.0.3.tgz", + "integrity": "sha1-hlybQgv75VAGuflp+XoNLESWZZU=", + "dev": true, + "requires": { + "bn.js": "2.0.4", + "brorand": "1.0.5", + "hash.js": "1.0.3", + "inherits": "2.0.1" + }, + "dependencies": { + "brorand": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.0.5.tgz", + "integrity": "sha1-B7VMowKGq9Fxig4qgwgD79yb+gQ=", + "dev": true + }, + "hash.js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.0.3.tgz", + "integrity": "sha1-EzL/ABVsCg/92CNgE9B7d6BFFXM=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", + "dev": true + } + } + } + } + }, "bl": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.1.tgz", @@ -253,6 +354,12 @@ "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz", "integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE=" }, + "bloom-filter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bloom-filter/-/bloom-filter-0.2.0.tgz", + "integrity": "sha1-hNY7v5Fy2DA+ZMH/FuudvzOpgaM=", + "dev": true + }, "bn.js": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-2.0.4.tgz", @@ -407,6 +514,16 @@ } } }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, "brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", @@ -441,6 +558,10 @@ "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", "optional": true }, + "buffers": { + "version": "github:bitpay/node-buffers#04f4c4264e0d105db2b99b786843ed64f23230d8", + "dev": true + }, "callsite": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", @@ -503,11 +624,37 @@ "safe-buffer": "5.1.1" } }, + "cli": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz", + "integrity": "sha1-ePlIXNFhtWbppsctcXDEJw6B22E=", + "dev": true, + "requires": { + "glob": "7.1.2" + } + }, + "cliff": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/cliff/-/cliff-0.1.10.tgz", + "integrity": "sha1-U74z6p9ZvshWCe4wCsQgdgPlIBM=", + "dev": true, + "requires": { + "colors": "1.0.3", + "eyes": "0.1.8", + "winston": "0.8.3" + } + }, "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, + "colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", + "dev": true + }, "component-bind": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", @@ -616,6 +763,12 @@ } } }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", @@ -657,6 +810,12 @@ "sha.js": "2.4.8" } }, + "cycle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", + "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=", + "dev": true + }, "debug": { "version": "2.6.8", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", @@ -769,12 +928,24 @@ "integrity": "sha1-bDAzIxd6YrGyLAcCefeGEoe2mxo=", "optional": true }, + "eyes": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=", + "dev": true + }, "fast-future": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/fast-future/-/fast-future-1.0.2.tgz", "integrity": "sha1-hDWpqqAteSSNF9cE52JZMB2ZKAo=", "optional": true }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, "gauge": { "version": "2.7.4", "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", @@ -797,6 +968,20 @@ "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=", "optional": true }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.1", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, "has-binary2": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.2.tgz", @@ -862,6 +1047,16 @@ "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, "inherits": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", @@ -873,6 +1068,17 @@ "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=", "optional": true }, + "ipv6": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/ipv6/-/ipv6-3.1.3.tgz", + "integrity": "sha1-TZBk+cLa+g3RC4t9dv/KSq0xs7k=", + "dev": true, + "requires": { + "cli": "0.4.5", + "cliff": "0.1.10", + "sprintf": "0.1.5" + } + }, "is-fullwidth-code-point": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", @@ -886,6 +1092,12 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, "jsonparse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", @@ -972,6 +1184,15 @@ "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } + }, "minimist": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", @@ -1229,6 +1450,12 @@ "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", "optional": true }, + "network-byte-order": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/network-byte-order/-/network-byte-order-0.2.0.tgz", + "integrity": "sha1-asEb9Ev2ENrt2+kKCaXIF8bg0rM=", + "dev": true + }, "node-abi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.1.0.tgz", @@ -1310,6 +1537,18 @@ "better-assert": "1.0.2" } }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "pkginfo": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", + "integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=", + "dev": true + }, "prebuild-install": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-2.2.2.tgz", @@ -1808,6 +2047,15 @@ } } }, + "rimraf": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", + "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", + "dev": true, + "requires": { + "glob": "7.1.2" + } + }, "ripemd160": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", @@ -2055,6 +2303,28 @@ } } }, + "socks5-client": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/socks5-client/-/socks5-client-0.3.6.tgz", + "integrity": "sha1-QgW1eR8t93zwdSciJVj+TkasovE=", + "dev": true, + "requires": { + "ipv6": "3.1.3", + "network-byte-order": "0.2.0" + } + }, + "sprintf": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/sprintf/-/sprintf-0.1.5.tgz", + "integrity": "sha1-j4PjmpMXwaUCy324BQ5Rxnn27c8=", + "dev": true + }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "dev": true + }, "string_decoder": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", @@ -2162,6 +2432,35 @@ "string-width": "1.0.2" } }, + "winston": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/winston/-/winston-0.8.3.tgz", + "integrity": "sha1-ZLar9M0Brcrv1QCTk7HY6L7BnbA=", + "dev": true, + "requires": { + "async": "0.2.10", + "colors": "0.6.2", + "cycle": "1.0.3", + "eyes": "0.1.8", + "isstream": "0.1.2", + "pkginfo": "0.3.1", + "stack-trace": "0.0.10" + }, + "dependencies": { + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", + "dev": true + }, + "colors": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz", + "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=", + "dev": true + } + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/package.json b/package.json index 6d54480..64cac4e 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "node": ">=8.2.0" }, "scripts": { - "test": "NODE_ENV=test mocha -R spec --recursive" + "test": "NODE_ENV=test mocha -R spec --recursive", + "regtest": "mocha -R spec regtest" }, "main": "lib", "bitcoreNode": "lib", @@ -39,9 +40,13 @@ "request": "^2.64.0" }, "devDependencies": { + "bitcoind-rpc": "^0.7.0", + "bitcore-p2p": "^1.1.2", "chai": "^3.5.0", + "mkdirp": "^0.5.1", "mocha": "^2.4.5", "proxyquire": "^1.7.2", + "rimraf": "^2.6.1", "should": "^8.3.1", "sinon": "^1.10.3" } diff --git a/regtest/address.js b/regtest/address.js new file mode 100644 index 0000000..f19298b --- /dev/null +++ b/regtest/address.js @@ -0,0 +1,766 @@ +'use strict'; + +var expect = require('chai').expect; +var spawn = require('child_process').spawn; +var rimraf = require('rimraf'); +var mkdirp = require('mkdirp'); +var fs = require('fs'); +var async = require('async'); +var RPC = require('bitcoind-rpc'); +var http = require('http'); + +var rpc1Address; +var rpc2Address; + +var rpcConfig = { + protocol: 'http', + user: 'local', + pass: 'localtest', + host: '127.0.0.1', + port: 58332, + rejectUnauthorized: false +}; + +var rpc1 = new RPC(rpcConfig); +rpcConfig.port++; +var rpc2 = new RPC(rpcConfig); +var debug = true; +var bitcoreDataDir = '/tmp/bitcore'; +var bitcoinDataDirs = ['/tmp/bitcoin1', '/tmp/bitcoin2']; + +var bitcoin = { + args: { + datadir: null, + listen: 1, + regtest: 1, + server: 1, + rpcuser: 'local', + rpcpassword: 'localtest', + //printtoconsole: 1 + rpcport: 58332, + }, + datadir: null, + exec: 'bitcoind', //if this isn't on your PATH, then provide the absolute path, e.g. /usr/local/bin/bitcoind + processes: [] +}; + +var bitcore = { + configFile: { + file: bitcoreDataDir + '/bitcore-node.json', + conf: { + network: 'regtest', + port: 53001, + datadir: bitcoreDataDir, + services: [ + 'p2p', + 'db', + 'header', + 'block', + 'address', + 'transaction', + 'mempool', + 'web', + 'insight-api', + 'fee', + 'timestamp' + ], + servicesConfig: { + 'p2p': { + 'peers': [ + { 'ip': { 'v4': '127.0.0.1' }, port: 18444 } + ] + }, + 'insight-api': { + 'routePrefix': 'api' + } + } + } + }, + httpOpts: { + protocol: 'http:', + hostname: 'localhost', + port: 53001, + }, + opts: { cwd: bitcoreDataDir }, + datadir: bitcoreDataDir, + exec: 'bitcored', //if this isn't on your PATH, then provide the absolute path, e.g. /usr/local/bin/bitcored + args: ['start'], + process: null +}; + +var startBitcoind = function(count, callback) { + + var listenCount = 0; + async.timesSeries(count, function(n, next) { + + var datadir = bitcoinDataDirs.shift(); + + bitcoin.datadir = datadir; + bitcoin.args.datadir = datadir; + + if (listenCount++ > 0) { + bitcoin.args.listen = 0; + bitcoin.args.rpcport++; + bitcoin.args.connect = '127.0.0.1'; + } + + rimraf(datadir, function(err) { + + if(err) { + return next(err); + } + + mkdirp(datadir, function(err) { + + if(err) { + return next(err); + } + + var args = bitcoin.args; + var argList = Object.keys(args).map(function(key) { + return '-' + key + '=' + args[key]; + }); + + var bitcoinProcess = spawn(bitcoin.exec, argList, bitcoin.opts); + bitcoin.processes.push(bitcoinProcess); + + bitcoinProcess.stdout.on('data', function(data) { + + if (debug) { + process.stdout.write(data.toString()); + } + + }); + + bitcoinProcess.stderr.on('data', function(data) { + + if (debug) { + process.stderr.write(data.toString()); + } + + }); + + next(); + + }); + + }); + }, function(err) { + + if (err) { + return callback(err); + } + + var pids = bitcoin.processes.map(function(process) { + return process.pid; + }); + + console.log(count + ' bitcoind\'s started at pid(s): ' + pids); + callback(); + }); +}; + + +var shutdownBitcoind = function(callback) { + bitcoin.processes.forEach(function(process) { + process.kill(); + }); + setTimeout(callback, 3000); +}; + +var shutdownBitcore = function(callback) { + if (bitcore.process) { + bitcore.process.kill(); + } + callback(); +}; + +var txid; +var buildInitialChain = function(callback) { + async.waterfall([ + function(next) { + console.log('checking to see if bitcoind\'s are connected to each other.'); + rpc1.getinfo(function(err, res) { + if (err || res.result.connections !== 1) { + next(err || new Error('bitcoind\'s not connected to each other.')); + } + next(); + }); + }, + function(next) { + console.log('generating 101 blocks'); + rpc1.generate(101, next); + }, + function(res, next) { + console.log('getting new address from rpc2'); + rpc2.getNewAddress(function(err, res) { + if (err) { + return next(err); + } + rpc2Address = res.result; + console.log(rpc2Address); + next(null, rpc2Address); + }); + }, + function(addr, next) { + rpc1.sendToAddress(rpc2Address, 25, next); + }, + function(res, next) { + console.log('TXID: ' + res.result); + console.log('generating 6 blocks'); + rpc1.generate(7, next); + }, + function(res, next) { + rpc2.getBalance(function(err, res) { + console.log(res); + next(); + }); + }, + function(next) { + console.log('getting new address from rpc1'); + rpc1.getNewAddress(function(err, res) { + if (err) { + return next(err); + } + rpc1Address = res.result; + next(null, rpc1Address); + }); + }, + function(addr, next) { + rpc2.sendToAddress(rpc1Address, 20, next); + }, + function(res, next) { + txid = res.result; + console.log('sending from rpc2Address TXID: ', res); + console.log('generating 6 blocks'); + rpc2.generate(6, next); + } + ], function(err) { + + if (err) { + return callback(err); + } + rpc1.getInfo(function(err, res) { + console.log(res); + callback(); + }); + }); + +}; + +var startBitcore = function(callback) { + + rimraf(bitcoreDataDir, function(err) { + + if(err) { + return callback(err); + } + + mkdirp(bitcoreDataDir, function(err) { + + if(err) { + return callback(err); + } + + fs.writeFileSync(bitcore.configFile.file, JSON.stringify(bitcore.configFile.conf)); + + var args = bitcore.args; + bitcore.process = spawn(bitcore.exec, args, bitcore.opts); + + bitcore.process.stdout.on('data', function(data) { + + if (debug) { + process.stdout.write(data.toString()); + } + + }); + bitcore.process.stderr.on('data', function(data) { + + if (debug) { + process.stderr.write(data.toString()); + } + + }); + + callback(); + }); + + }); + + +}; + +describe('Address', function() { + + this.timeout(60000); + + before(function(done) { + + async.series([ + function(next) { + startBitcoind(2, next); + }, + function(next) { + setTimeout(function() { + buildInitialChain(next); + }, 8000); + }, + function(next) { + setTimeout(function() { + startBitcore(next); + }, 6000); + } + ], function(err) { + if (err) { + return done(err); + } + setTimeout(done, 2000); + }); + + }); + + after(function(done) { + shutdownBitcore(function() { + shutdownBitcoind(done); + }); + }); + + it('should get address info correctly: /addr/:addr', function(done) { + + + var request = http.request('http://localhost:53001/api/addr/' + rpc2Address, function(res) { + + var error; + if (res.statusCode !== 200 && res.statusCode !== 201) { + if (error) { + return; + } + return done('Error from bitcore-node webserver: ' + 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; + } + var data = JSON.parse(resData); + console.log(data); + expect(data.balance).to.equal(0); + expect(data.totalSent).to.equal(25); + done(); + }); + + }); + request.write(''); + request.end(); + }); + + it('should get a utxo: /addr/:addr/utxo', function(done) { + + var request = http.request('http://localhost:53001/api/addr/' + rpc1Address + '/utxo', function(res) { + + var error; + if (res.statusCode !== 200 && res.statusCode !== 201) { + if (error) { + return; + } + return done('Error from bitcore-node webserver: ' + 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; + } + var data = JSON.parse(resData); + console.log(data); + expect(data.length).equal(1); + expect(data[0].amount).equal(20); + expect(data[0].satoshis).equal(2000000000); + expect(data[0].confirmations).equal(6); + done(); + }); + + }); + + request.write(''); + request.end(); + + }); + + it('should get multi-address utxos: /addrs/:addrs/utxo', function(done) { + + var request = http.request('http://localhost:53001/api/addrs/' + rpc2Address + ',' + rpc1Address + '/utxo', function(res) { + + var error; + if (res.statusCode !== 200 && res.statusCode !== 201) { + if (error) { + return; + } + return done('Error from bitcore-node webserver: ' + 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; + } + var data = JSON.parse(resData); + console.log(data); + expect(data.length).to.equal(1); + expect(data[0].amount).to.equal(20); + expect(data[0].satoshis).to.equal(2000000000); + done(); + }); + + }); + + request.write(''); + request.end(); + + }); + + it('should post a utxo: /addrs/:addrs/utxo', function(done) { + + var body = JSON.stringify({ + addrs: [ rpc1Address, rpc2Address ] + }); + + var httpOpts = { + hostname: 'localhost', + port: 53001, + path: '/api/addrs/utxo', + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Content-Length': body.length + } + }; + + var request = http.request(httpOpts, function(res) { + + var error; + if (res.statusCode !== 200 && res.statusCode !== 201) { + if (error) { + return; + } + return done('Error from bitcore-node webserver: ' + 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; + } + var data = JSON.parse(resData); + console.log(data); + expect(data.length).to.equal(1); + expect(data[0].amount).to.equal(20); + expect(data[0].satoshis).to.equal(2000000000); + done(); + }); + + }); + + request.write(body); + request.end(); + + }); + + it('should get txs for a set of addresses: /addrs/:addrs/txs', function(done) { + + var httpOpts = { + hostname: 'localhost', + port: 53001, + path: '/api/addrs/' + rpc1Address + ',' + rpc2Address + '/txs', + method: 'GET' + }; + + var request = http.request(httpOpts, function(res) { + + var error; + if (res.statusCode !== 200 && res.statusCode !== 201) { + if (error) { + return; + } + return done('Error from bitcore-node webserver: ' + 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; + } + var data = JSON.parse(resData); + console.log(resData); + expect(data.items.length).to.equal(3); + expect(data.from).to.equal(0); + expect(data.to).to.equal(3); + done(); + }); + + }); + + request.write(''); + request.end(); + + }); + + it('should post txs for a set of addresses: /addrs/txs', function(done) { + + var body = JSON.stringify({ + addrs: [ rpc1Address, rpc2Address ] + }); + + var httpOpts = { + hostname: 'localhost', + port: 53001, + path: '/api/addrs/txs', + method: 'POST', + headers: { + 'Content-Type': 'application/json' + } + }; + + var request = http.request(httpOpts, function(res) { + + var error; + if (res.statusCode !== 200 && res.statusCode !== 201) { + if (error) { + return; + } + return done('Error from bitcore-node webserver: ' + 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; + } + var data = JSON.parse(resData); + console.log(resData); + expect(data.items.length).to.equal(3); + expect(data.from).to.equal(0); + expect(data.to).to.equal(3); + done(); + }); + + }); + + request.write(body); + request.end(); + + }); + + it('should get totalReceived for an address: /addr/:addr/totalReceived', function(done) { + + var httpOpts = { + hostname: 'localhost', + port: 53001, + path: '/api/addr/' + rpc1Address + '/totalReceived', + method: 'GET', + headers: { + 'Content-Type': 'application/json' + } + }; + + var request = http.request(httpOpts, function(res) { + + var error; + if (res.statusCode !== 200 && res.statusCode !== 201) { + if (error) { + return; + } + return done('Error from bitcore-node webserver: ' + 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; + } + + var data = JSON.parse(resData); + expect(data).to.equal(2000000000); + done(); + }); + + }); + + request.write(''); + request.end(); + + }); + + it('should get totalSent for an address: /addr/:addr/totalSent', function(done) { + + var httpOpts = { + hostname: 'localhost', + port: 53001, + path: '/api/addr/' + rpc1Address + '/totalSent', + method: 'GET', + headers: { + 'Content-Type': 'application/json' + } + }; + + var request = http.request(httpOpts, function(res) { + + var error; + if (res.statusCode !== 200 && res.statusCode !== 201) { + if (error) { + return; + } + return done('Error from bitcore-node webserver: ' + 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; + } + var data = JSON.parse(resData); + expect(data).to.equal(0); + done(); + }); + + }); + + request.write(''); + request.end(); + + }); + + it('should get unconfirmedBalance for an address: /addr/:addr/unconfirmedBalance', function(done) { + + var httpOpts = { + hostname: 'localhost', + port: 53001, + path: '/api/addr/' + rpc1Address + '/unconfirmedBalance', + method: 'GET', + headers: { + 'Content-Type': 'application/json' + } + }; + + var request = http.request(httpOpts, function(res) { + + var error; + if (res.statusCode !== 200 && res.statusCode !== 201) { + if (error) { + return; + } + return done('Error from bitcore-node webserver: ' + 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; + } + var data = JSON.parse(resData); + expect(data).to.equal(0); + done(); + }); + + }); + + request.write(''); + request.end(); + + }); + +}); + + diff --git a/regtest/block.js b/regtest/block.js new file mode 100644 index 0000000..fa6b134 --- /dev/null +++ b/regtest/block.js @@ -0,0 +1,500 @@ +'use strict'; + +var expect = require('chai').expect; +var spawn = require('child_process').spawn; +var rimraf = require('rimraf'); +var mkdirp = require('mkdirp'); +var fs = require('fs'); +var async = require('async'); +var RPC = require('bitcoind-rpc'); +var http = require('http'); + +var rpc1Address; +var rpc2Address; +var tx1; +var tx2; +var block; + +var rpcConfig = { + protocol: 'http', + user: 'local', + pass: 'localtest', + host: '127.0.0.1', + port: 58332, + rejectUnauthorized: false +}; + +var rpc1 = new RPC(rpcConfig); +rpcConfig.port++; +var rpc2 = new RPC(rpcConfig); +var debug = true; +var bitcoreDataDir = '/tmp/bitcore'; +var bitcoinDataDirs = ['/tmp/bitcoin1', '/tmp/bitcoin2']; + +var bitcoin = { + args: { + datadir: null, + listen: 1, + regtest: 1, + server: 1, + rpcuser: 'local', + rpcpassword: 'localtest', + //printtoconsole: 1 + rpcport: 58332, + }, + datadir: null, + exec: 'bitcoind', //if this isn't on your PATH, then provide the absolute path, e.g. /usr/local/bin/bitcoind + processes: [] +}; + +var bitcore = { + configFile: { + file: bitcoreDataDir + '/bitcore-node.json', + conf: { + network: 'regtest', + port: 53001, + datadir: bitcoreDataDir, + services: [ + 'p2p', + 'db', + 'header', + 'block', + 'address', + 'transaction', + 'mempool', + 'web', + 'insight-api', + 'fee', + 'timestamp' + ], + servicesConfig: { + 'p2p': { + 'peers': [ + { 'ip': { 'v4': '127.0.0.1' }, port: 18444 } + ] + }, + 'insight-api': { + 'routePrefix': 'api' + } + } + } + }, + httpOpts: { + protocol: 'http:', + hostname: 'localhost', + port: 53001, + }, + opts: { cwd: bitcoreDataDir }, + datadir: bitcoreDataDir, + exec: 'bitcored', //if this isn't on your PATH, then provide the absolute path, e.g. /usr/local/bin/bitcored + args: ['start'], + process: null +}; + +var startBitcoind = function(count, callback) { + + var listenCount = 0; + async.timesSeries(count, function(n, next) { + + var datadir = bitcoinDataDirs.shift(); + + bitcoin.datadir = datadir; + bitcoin.args.datadir = datadir; + + if (listenCount++ > 0) { + bitcoin.args.listen = 0; + bitcoin.args.rpcport++; + bitcoin.args.connect = '127.0.0.1'; + } + + rimraf(datadir, function(err) { + + if(err) { + return next(err); + } + + mkdirp(datadir, function(err) { + + if(err) { + return next(err); + } + + var args = bitcoin.args; + var argList = Object.keys(args).map(function(key) { + return '-' + key + '=' + args[key]; + }); + + var bitcoinProcess = spawn(bitcoin.exec, argList, bitcoin.opts); + bitcoin.processes.push(bitcoinProcess); + + bitcoinProcess.stdout.on('data', function(data) { + + if (debug) { + process.stdout.write(data.toString()); + } + + }); + + bitcoinProcess.stderr.on('data', function(data) { + + if (debug) { + process.stderr.write(data.toString()); + } + + }); + + next(); + + }); + + }); + }, function(err) { + + if (err) { + return callback(err); + } + + var pids = bitcoin.processes.map(function(process) { + return process.pid; + }); + + console.log(count + ' bitcoind\'s started at pid(s): ' + pids); + callback(); + }); +}; + + +var shutdownBitcoind = function(callback) { + bitcoin.processes.forEach(function(process) { + process.kill(); + }); + setTimeout(callback, 3000); +}; + +var shutdownBitcore = function(callback) { + if (bitcore.process) { + bitcore.process.kill(); + } + callback(); +}; + + +var buildInitialChain = function(callback) { + async.waterfall([ + function(next) { + console.log('checking to see if bitcoind\'s are connected to each other.'); + rpc1.getinfo(function(err, res) { + if (err || res.result.connections !== 1) { + next(err || new Error('bitcoind\'s not connected to each other.')); + } + next(); + }); + }, + function(next) { + console.log('generating 101 blocks'); + rpc1.generate(101, next); + }, + function(res, next) { + console.log('getting new address from rpc2'); + rpc2.getNewAddress(function(err, res) { + if (err) { + return next(err); + } + rpc2Address = res.result; + console.log(rpc2Address); + next(null, rpc2Address); + }); + }, + function(addr, next) { + rpc1.sendToAddress(rpc2Address, 25, next); + }, + function(res, next) { + tx1 = res.result; + console.log('TXID: ' + res.result); + console.log('generating 7 blocks'); + rpc1.generate(7, next); + }, + function(res, next) { + block = res.result[res.result.length - 1]; + rpc2.getBalance(function(err, res) { + console.log(res); + next(); + }); + }, + function(next) { + console.log('getting new address from rpc1'); + rpc1.getNewAddress(function(err, res) { + if (err) { + return next(err); + } + rpc1Address = res.result; + next(null, rpc1Address); + }); + }, + function(addr, next) { + rpc2.sendToAddress(rpc1Address, 20, next); + }, + function(res, next) { + tx2 = res.result; + console.log('sending from rpc2Address TXID: ', res); + console.log('generating 6 blocks'); + rpc2.generate(6, next); + } + ], function(err) { + + if (err) { + return callback(err); + } + rpc1.getInfo(function(err, res) { + console.log(res); + callback(); + }); + }); + +}; + +var startBitcore = function(callback) { + + rimraf(bitcoreDataDir, function(err) { + + if(err) { + return callback(err); + } + + mkdirp(bitcoreDataDir, function(err) { + + if(err) { + return callback(err); + } + + fs.writeFileSync(bitcore.configFile.file, JSON.stringify(bitcore.configFile.conf)); + + var args = bitcore.args; + bitcore.process = spawn(bitcore.exec, args, bitcore.opts); + + bitcore.process.stdout.on('data', function(data) { + + if (debug) { + process.stdout.write(data.toString()); + } + + }); + bitcore.process.stderr.on('data', function(data) { + + if (debug) { + process.stderr.write(data.toString()); + } + + }); + + callback(); + }); + + }); + + +}; + +describe('Block', function() { + + this.timeout(60000); + + before(function(done) { + + async.series([ + function(next) { + startBitcoind(2, next); + }, + function(next) { + setTimeout(function() { + buildInitialChain(next); + }, 8000); + }, + function(next) { + setTimeout(function() { + startBitcore(next); + }, 6000); + } + ], function(err) { + if (err) { + return done(err); + } + setTimeout(done, 2000); + }); + + }); + + after(function(done) { + shutdownBitcore(function() { + shutdownBitcoind(done); + }); + }); + + it('should get blocks: /blocks', function(done) { + + var request = http.request('http://localhost:53001/api/blocks', function(res) { + + var error; + if (res.statusCode !== 200 && res.statusCode !== 201) { + + if (error) { + return; + } + + return done('Error from bitcore-node webserver: ' + 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; + } + var data = JSON.parse(resData); + expect(data.length).to.equal(114); + done(); + }); + + }); + request.write(''); + request.end(); + }); + + it('should get a block: /block/:hash', function(done) { + + var request = http.request('http://localhost:53001/api/block/' + block, function(res) { + + var error; + if (res.statusCode !== 200 && res.statusCode !== 201) { + + if (error) { + return; + } + + return done('Error from bitcore-node webserver: ' + 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; + } + var data = JSON.parse(resData); + expect(data.hash).to.equal(block); + done(); + }); + + }); + request.write(''); + request.end(); + }); + + it('should get a block-index: /block-index/:height', function(done) { + + var request = http.request('http://localhost:53001/api/block-index/' + '108', function(res) { + + var error; + if (res.statusCode !== 200 && res.statusCode !== 201) { + + if (error) { + return; + } + + return done('Error from bitcore-node webserver: ' + 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; + } + var data = JSON.parse(resData); + console.log(resData); + expect(data.blockHash).to.equal(block); + done(); + }); + + }); + + request.write(''); + request.end(); + }); + + it('should get a raw block: /rawblock/:hash', function(done) { + + var request = http.request('http://localhost:53001/api/rawblock/' + block, function(res) { + + var error; + if (res.statusCode !== 200 && res.statusCode !== 201) { + + if (error) { + return; + } + + return done('Error from bitcore-node webserver: ' + 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; + } + var data = JSON.parse(resData); + console.log(resData); + expect(data.rawblock).to.not.be.null; + done(); + }); + + }); + + request.write(''); + request.end(); + }); +}); + + + + diff --git a/regtest/data/blocks.json b/regtest/data/blocks.json new file mode 100644 index 0000000..67a215b --- /dev/null +++ b/regtest/data/blocks.json @@ -0,0 +1,10 @@ +[ + "0000002006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f0520bf1be2e869376908f7c637214f9f0f9f4fd86c819a8c98a3c5f8d70a65f805ba9559ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff03510101ffffffff0200f2052a01000000232103e91a98edf4e457b5d5aa1e50e35ce6afb67bf7a9ff98b5c36dabbe994b080205ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf900000000", + "00000020bd5725c4bc8fb66032523ca18eb2c809ca1935ab35baf5a1bbaf60ef9a616b2b373d5a12492652b1d22c28ce5c6eb6b22f03c69db1eae9a368e1b544147583eb06ba9559ffff7f20030000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff03520101ffffffff0200f2052a01000000232103e91a98edf4e457b5d5aa1e50e35ce6afb67bf7a9ff98b5c36dabbe994b080205ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf900000000", + "00000020af97cfd581a5f0da7925c346c371f6c58131a6b00a95060562f90607aa111e5f97c7b866d2503ee4982569a486f6e25023e72bef588bdef60a06fc740e50001906ba9559ffff7f20020000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff03530101ffffffff0200f2052a01000000232103e91a98edf4e457b5d5aa1e50e35ce6afb67bf7a9ff98b5c36dabbe994b080205ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf900000000", + "000000201c9d9eb8064b9508b3cd10de5927cc47347a6899b66946ecc22d52b04c2f3c5f99435c3a200a2419695565432e3115a5ff0d57ed1975b9efe0c7ad1a709a6a4e07ba9559ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff03540101ffffffff0200f2052a01000000232103e91a98edf4e457b5d5aa1e50e35ce6afb67bf7a9ff98b5c36dabbe994b080205ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf900000000", + "00000020de37a9c9fbb1dc3ea5ca9e148e5310d639649814b45a424afe1d58a18db5d8327b529f7d58238330d55b82228ea14abcab965319de7e59c6534758c91c137fcb07ba9559ffff7f20020000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff03550101ffffffff0200f2052a01000000232103e91a98edf4e457b5d5aa1e50e35ce6afb67bf7a9ff98b5c36dabbe994b080205ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf900000000", + "00000020e3aa93afbba267bec962a7a38bd064478e5c082e9a73e434b20544950ad8395b3b4a71816a4ce7973d0bcc6a583441fca260f71a175bd2887a9e3e40e4badcd355bb9559ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff03560101ffffffff0200f2052a010000002321023886024ea5984e57b35c3b339f5aee097819ac55235e4fd5822a6ad0a4de1b55ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf900000000", + "000000201a3c951a20b5d603144ce060c86e95fed1869524e66acfc46bdf08d96f6642094916aa5b965b0016fb8ba8b58e99f6b3edbe1a844aa7948adaccf7f28f08f914b9cb9559ffff7f20030000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff03570101ffffffff0200f2052a01000000232102a8ef631320be3e6203329acba88fe0a663c19d59fee8240592dc2a32553a4159ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf900000000" + +] diff --git a/regtest/data/blocks_reorg.json b/regtest/data/blocks_reorg.json new file mode 100644 index 0000000..8196842 --- /dev/null +++ b/regtest/data/blocks_reorg.json @@ -0,0 +1,4 @@ +[ + "000000201a3c951a20b5d603144ce060c86e95fed1869524e66acfc46bdf08d96f664209b4b1c32ec485f4ad27c5402a1b16a0b1135364b7c9b0dcf4276f9fa3fd215d1b08cc9559ffff7f20000000000102000000010000000000000000000000000000000000000000000000000000000000000000ffffffff03570101ffffffff0200f2052a01000000232102a5566542d1f0f202541d98755628a41dcd4416b50db820e2b04d5ecb0bd02b73ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf900000000" +] + diff --git a/regtest/reorg.js b/regtest/reorg.js new file mode 100644 index 0000000..28ad447 --- /dev/null +++ b/regtest/reorg.js @@ -0,0 +1,277 @@ +'use strict'; + +var expect = require('chai').expect; +var net = require('net'); +var spawn = require('child_process').spawn; +var rimraf = require('rimraf'); +var mkdirp = require('mkdirp'); +var fs = require('fs'); +var p2p = require('bitcore-p2p'); +var bitcore = require('bitcore-lib'); +var Networks = bitcore.Networks; +var BlockHeader = bitcore.BlockHeader; +var Block = bitcore.Block; +var BcoinBlock = require('bcoin').block; +var http = require('http'); + +Networks.enableRegtest(); +var messages = new p2p.Messages({ network: Networks.get('regtest'), Block: BcoinBlock, BlockHeader: BlockHeader }); +var server; +var rawBlocks = require('./data/blocks.json'); +var rawReorgBlocks = require('./data/blocks_reorg.json')[0]; + +var reorgBlock = BcoinBlock.fromRaw(rawReorgBlocks, 'hex'); + +var blocks = rawBlocks.map(function(rawBlock) { + return new Block(new Buffer(rawBlock, 'hex')); +}); + +var headers = blocks.map(function(block) { + return block.header; +}); + +var debug = true; +var bitcoreDataDir = '/tmp/bitcore'; + +var bitcore = { + configFile: { + file: bitcoreDataDir + '/bitcore-node.json', + conf: { + network: 'regtest', + port: 53001, + datadir: bitcoreDataDir, + services: [ + 'p2p', + 'db', + 'header', + 'block', + 'address', + 'transaction', + 'mempool', + 'web', + 'insight-api', + 'fee', + 'timestamp' + ], + servicesConfig: { + 'p2p': { + 'peers': [ + { 'ip': { 'v4': '127.0.0.1' }, port: 18444 } + ] + }, + 'insight-api': { + 'routePrefix': 'api' + } + } + } + }, + httpOpts: { + protocol: 'http:', + hostname: 'localhost', + port: 53001, + }, + opts: { cwd: bitcoreDataDir }, + datadir: bitcoreDataDir, + exec: 'bitcored', // ensure this on your path or add the full, absolute path. + args: ['start'], + process: null +}; + + +var blockIndex = 0; +var tcpSocket; + +var startFakeNode = function() { + server = net.createServer(function(socket) { + + tcpSocket = socket; + socket.on('end', function() { + console.log('bitcore-node has ended the connection'); + }); + + socket.on('data', function(data) { + + var command = data.slice(4, 16).toString('hex'); + var message; + + if (command === '76657273696f6e0000000000') { //version + message = messages.Version(); + } + + if (command === '76657261636b000000000000') { //verack + message = messages.VerAck(); + } + + if (command === '676574686561646572730000') { //getheaders + message = messages.Headers(headers); + } + + if (command === '676574626c6f636b73000000') { //getblocks + var block = blocks[blockIndex]; + if (!block) { + return; + } + var blockHash = block.hash; + var inv = p2p.Inventory.forBlock(blockHash); + message = messages.Inventory([inv]); + } + + if (command === '676574646174610000000000') { //getdata + var raw = rawBlocks[blockIndex++]; + var blk = BcoinBlock.fromRaw(raw, 'hex'); + message = messages.Block(blk, { Block: BcoinBlock }); + } + + if (message) { + socket.write(message.toBuffer()); + } + + }); + + socket.pipe(socket); + }); + + server.listen(18444, '127.0.0.1'); +}; + + +var shutdownFakeNode = function() { + server.close(); +}; + +var shutdownBitcore = function(callback) { + if (bitcore.process) { + bitcore.process.kill(); + } + callback(); +}; + +var startBitcore = function(callback) { + + rimraf(bitcoreDataDir, function(err) { + + if(err) { + return callback(err); + } + + mkdirp(bitcoreDataDir, function(err) { + + if(err) { + return callback(err); + } + + fs.writeFileSync(bitcore.configFile.file, JSON.stringify(bitcore.configFile.conf)); + + var args = bitcore.args; + bitcore.process = spawn(bitcore.exec, args, bitcore.opts); + + bitcore.process.stdout.on('data', function(data) { + + if (debug) { + process.stdout.write(data.toString()); + } + + }); + bitcore.process.stderr.on('data', function(data) { + + if (debug) { + process.stderr.write(data.toString()); + } + + }); + + callback(); + }); + + }); + + +}; + +describe('Reorg', function() { + // 1. spin up bitcore-node and have it connect to our custom tcp socket + // 2. feed it a few headers + // 3. feed it a few blocks + // 4. feed it a block that reorgs + + this.timeout(60000); + + before(function(done) { + startFakeNode(); + startBitcore(done); + }); + + after(function(done) { + shutdownFakeNode(); + shutdownBitcore(function() { + setTimeout(done, 3000); + }); + }); + + it('should reorg correctly when already synced', function(done) { + + // at this point we have a fully synced chain at height 7.... + // we now want to send a new block number 7 whose prev hash is block 6 (it should be block 7) + // we then should reorg back to block 6 then back up to the new block 7 + + setTimeout(function() { + + console.log('From Test: reorging to block: ' + reorgBlock.rhash()); + + // send the reorg block + rawBlocks.push(rawReorgBlocks); + var blockHash = reorgBlock.rhash(); + var inv = p2p.Inventory.forBlock(blockHash); + var msg = messages.Inventory([inv]); + tcpSocket.write(msg.toBuffer()); + + // wait 2 secs until the reorg happens, if it takes any longer the test ought to fail anyway + setTimeout(function() { + var error; + var request = http.request('http://localhost:53001/api/block/' + reorgBlock.rhash(), function(res) { + + if (res.statusCode !== 200 && res.statusCode !== 201) { + if (error) { + return; + } + return done('Error from bitcore-node webserver: ' + 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; + } + var data = JSON.parse(resData); + expect(data.height).to.equal(7); + expect(data.hash).to.equal(reorgBlock.rhash()); + done(resError, resData); + }); + + }); + + request.on('error', function(e) { + error = e; + done(error); + }); + + request.write(''); + request.end(); + }, 2000); + }, 2000); + + + }); + +}); + diff --git a/regtest/status.js b/regtest/status.js new file mode 100644 index 0000000..637766e --- /dev/null +++ b/regtest/status.js @@ -0,0 +1,549 @@ +'use strict'; + +var expect = require('chai').expect; +var spawn = require('child_process').spawn; +var rimraf = require('rimraf'); +var mkdirp = require('mkdirp'); +var fs = require('fs'); +var async = require('async'); +var RPC = require('bitcoind-rpc'); +var http = require('http'); + +var rpc1Address; +var rpc2Address; +var tx1; +var tx2; +var block; + +var rpcConfig = { + protocol: 'http', + user: 'local', + pass: 'localtest', + host: '127.0.0.1', + port: 58332, + rejectUnauthorized: false +}; + +var rpc1 = new RPC(rpcConfig); +rpcConfig.port++; +var rpc2 = new RPC(rpcConfig); +var debug = true; +var bitcoreDataDir = '/tmp/bitcore'; +var bitcoinDataDirs = ['/tmp/bitcoin1', '/tmp/bitcoin2']; + +var bitcoin = { + args: { + datadir: null, + listen: 1, + regtest: 1, + server: 1, + rpcuser: 'local', + rpcpassword: 'localtest', + //printtoconsole: 1 + rpcport: 58332, + }, + datadir: null, + exec: 'bitcoind', //if this isn't on your PATH, then provide the absolute path, e.g. /usr/local/bin/bitcoind + processes: [] +}; + +var bitcore = { + configFile: { + file: bitcoreDataDir + '/bitcore-node.json', + conf: { + network: 'regtest', + port: 53001, + datadir: bitcoreDataDir, + services: [ + 'p2p', + 'db', + 'header', + 'block', + 'address', + 'transaction', + 'mempool', + 'web', + 'insight-api', + 'fee', + 'timestamp' + ], + servicesConfig: { + 'p2p': { + 'peers': [ + { 'ip': { 'v4': '127.0.0.1' }, port: 18444 } + ] + }, + 'fee': { + 'rpc': { + 'user': 'local', + 'pass': 'localtest', + 'host': 'localhost', + 'protocol': 'http', + 'port': 58332 + } + }, + 'insight-api': { + 'routePrefix': 'api' + } + } + } + }, + httpOpts: { + protocol: 'http:', + hostname: 'localhost', + port: 53001, + }, + opts: { cwd: bitcoreDataDir }, + datadir: bitcoreDataDir, + exec: 'bitcored', + args: ['start'], + process: null +}; + +var startBitcoind = function(count, callback) { + + var listenCount = 0; + async.timesSeries(count, function(n, next) { + + var datadir = bitcoinDataDirs.shift(); + + bitcoin.datadir = datadir; + bitcoin.args.datadir = datadir; + + if (listenCount++ > 0) { + bitcoin.args.listen = 0; + bitcoin.args.rpcport++; + bitcoin.args.connect = '127.0.0.1'; + } + + rimraf(datadir, function(err) { + + if(err) { + return next(err); + } + + mkdirp(datadir, function(err) { + + if(err) { + return next(err); + } + + var args = bitcoin.args; + var argList = Object.keys(args).map(function(key) { + return '-' + key + '=' + args[key]; + }); + + var bitcoinProcess = spawn(bitcoin.exec, argList, bitcoin.opts); + bitcoin.processes.push(bitcoinProcess); + + bitcoinProcess.stdout.on('data', function(data) { + + if (debug) { + process.stdout.write(data.toString()); + } + + }); + + bitcoinProcess.stderr.on('data', function(data) { + + if (debug) { + process.stderr.write(data.toString()); + } + + }); + + next(); + + }); + + }); + }, function(err) { + + if (err) { + return callback(err); + } + + var pids = bitcoin.processes.map(function(process) { + return process.pid; + }); + + console.log(count + ' bitcoind\'s started at pid(s): ' + pids); + callback(); + }); +}; + + +var shutdownBitcoind = function(callback) { + bitcoin.processes.forEach(function(process) { + process.kill(); + }); + setTimeout(callback, 3000); +}; + +var shutdownBitcore = function(callback) { + if (bitcore.process) { + bitcore.process.kill(); + } + callback(); +}; + + +var buildInitialChain = function(callback) { + async.waterfall([ + function(next) { + console.log('checking to see if bitcoind\'s are connected to each other.'); + rpc1.getinfo(function(err, res) { + if (err || res.result.connections !== 1) { + next(err || new Error('bitcoind\'s not connected to each other.')); + } + next(); + }); + }, + function(next) { + console.log('generating 101 blocks'); + rpc1.generate(101, next); + }, + function(res, next) { + console.log('getting new address from rpc2'); + rpc2.getNewAddress(function(err, res) { + if (err) { + return next(err); + } + rpc2Address = res.result; + console.log(rpc2Address); + next(null, rpc2Address); + }); + }, + function(addr, next) { + rpc1.sendToAddress(rpc2Address, 25, next); + }, + function(res, next) { + tx1 = res.result; + console.log('TXID: ' + res.result); + console.log('generating 7 blocks'); + rpc1.generate(7, next); + }, + function(res, next) { + block = res.result[res.result.length - 1]; + rpc2.getBalance(function(err, res) { + console.log(res); + next(); + }); + }, + function(next) { + console.log('getting new address from rpc1'); + rpc1.getNewAddress(function(err, res) { + if (err) { + return next(err); + } + rpc1Address = res.result; + next(null, rpc1Address); + }); + }, + function(addr, next) { + rpc2.sendToAddress(rpc1Address, 20, next); + }, + function(res, next) { + tx2 = res.result; + console.log('sending from rpc2Address TXID: ', res); + console.log('generating 6 blocks'); + rpc2.generate(6, next); + } + ], function(err) { + + if (err) { + return callback(err); + } + rpc1.getInfo(function(err, res) { + console.log(res); + callback(); + }); + }); + +}; + +var startBitcore = function(callback) { + + rimraf(bitcoreDataDir, function(err) { + + if(err) { + return callback(err); + } + + mkdirp(bitcoreDataDir, function(err) { + + if(err) { + return callback(err); + } + + fs.writeFileSync(bitcore.configFile.file, JSON.stringify(bitcore.configFile.conf)); + + var args = bitcore.args; + bitcore.process = spawn(bitcore.exec, args, bitcore.opts); + + bitcore.process.stdout.on('data', function(data) { + + if (debug) { + process.stdout.write(data.toString()); + } + + }); + bitcore.process.stderr.on('data', function(data) { + + if (debug) { + process.stderr.write(data.toString()); + } + + }); + + callback(); + }); + + }); + + +}; + +describe('Status', function() { + + this.timeout(60000); + + before(function(done) { + + async.series([ + function(next) { + startBitcoind(2, next); + }, + function(next) { + setTimeout(function() { + buildInitialChain(next); + }, 8000); + }, + function(next) { + setTimeout(function() { + startBitcore(next); + }, 6000); + } + ], function(err) { + if (err) { + return done(err); + } + setTimeout(done, 2000); + }); + + }); + + after(function(done) { + shutdownBitcore(function() { + shutdownBitcoind(done); + }); + }); + + it('should get status: /status', function(done) { + + var request = http.request('http://localhost:53001/api/status', function(res) { + + var error; + if (res.statusCode !== 200 && res.statusCode !== 201) { + + if (error) { + return; + } + + return done('Error from bitcore-node webserver: ' + 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; + } + var data = JSON.parse(resData); + expect(data.info).to.not.be.null; + done(); + }); + + }); + request.write(''); + request.end(); + }); + + it('should get status: /sync', function(done) { + + var request = http.request('http://localhost:53001/api/sync', function(res) { + + var error; + if (res.statusCode !== 200 && res.statusCode !== 201) { + + if (error) { + return; + } + + return done('Error from bitcore-node webserver: ' + 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; + } + var data = JSON.parse(resData); + console.log(data); + expect(data.status).to.equal('finished'); + done(); + }); + + }); + request.write(''); + request.end(); + }); + + it('should get peer: /peer', function(done) { + + var request = http.request('http://localhost:53001/api/peer', function(res) { + + var error; + if (res.statusCode !== 200 && res.statusCode !== 201) { + + if (error) { + return; + } + + return done('Error from bitcore-node webserver: ' + 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; + } + var data = JSON.parse(resData); + console.log(data); + expect(data.connected).to.be.true; + done(); + }); + + }); + request.write(''); + request.end(); + }); + + it('should get version: /version', function(done) { + + var request = http.request('http://localhost:53001/api/version', function(res) { + + var error; + if (res.statusCode !== 200 && res.statusCode !== 201) { + + if (error) { + return; + } + + return done('Error from bitcore-node webserver: ' + 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; + } + var data = JSON.parse(resData); + console.log(data); + expect(data.version).to.not.be.null; + done(); + }); + + }); + + request.write(''); + request.end(); + + }); + + it('should estimate fee: /estimateFee', function(done) { + + var request = http.request('http://localhost:53001/api/utils/estimateFee', function(res) { + + var error; + if (res.statusCode !== 200 && res.statusCode !== 201) { + + if (error) { + return; + } + + return done('Error from bitcore-node webserver: ' + 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; + } + var data = JSON.parse(resData); + console.log(data); + expect(data['2']).to.not.be.null; + done(); + }); + + }); + + request.write(''); + request.end(); + + }); +}); diff --git a/regtest/transaction.js b/regtest/transaction.js new file mode 100644 index 0000000..4747bcf --- /dev/null +++ b/regtest/transaction.js @@ -0,0 +1,448 @@ +'use strict'; + +var expect = require('chai').expect; +var spawn = require('child_process').spawn; +var rimraf = require('rimraf'); +var mkdirp = require('mkdirp'); +var fs = require('fs'); +var async = require('async'); +var RPC = require('bitcoind-rpc'); +var http = require('http'); + +var rpc1Address; +var rpc2Address; +var tx1; +var tx2; +var block; + +var rpcConfig = { + protocol: 'http', + user: 'local', + pass: 'localtest', + host: '127.0.0.1', + port: 58332, + rejectUnauthorized: false +}; + +var rpc1 = new RPC(rpcConfig); +rpcConfig.port++; +var rpc2 = new RPC(rpcConfig); +var debug = true; +var bitcoreDataDir = '/tmp/bitcore'; +var bitcoinDataDirs = ['/tmp/bitcoin1', '/tmp/bitcoin2']; + +var bitcoin = { + args: { + datadir: null, + listen: 1, + regtest: 1, + server: 1, + rpcuser: 'local', + rpcpassword: 'localtest', + //printtoconsole: 1 + rpcport: 58332, + }, + datadir: null, + exec: 'bitcoind', //if this isn't on your PATH, then provide the absolute path, e.g. /usr/local/bin/bitcoind + processes: [] +}; + +var bitcore = { + configFile: { + file: bitcoreDataDir + '/bitcore-node.json', + conf: { + network: 'regtest', + port: 53001, + datadir: bitcoreDataDir, + services: [ + 'p2p', + 'db', + 'header', + 'block', + 'address', + 'transaction', + 'mempool', + 'web', + 'insight-api', + 'fee', + 'timestamp' + ], + servicesConfig: { + 'p2p': { + 'peers': [ + { 'ip': { 'v4': '127.0.0.1' }, port: 18444 } + ] + }, + 'insight-api': { + 'routePrefix': 'api' + } + } + } + }, + httpOpts: { + protocol: 'http:', + hostname: 'localhost', + port: 53001, + }, + opts: { cwd: bitcoreDataDir }, + datadir: bitcoreDataDir, + exec: 'bitcored', //if this isn't on your PATH, then provide the absolute path, e.g. /usr/local/bin/bitcored + args: ['start'], + process: null +}; + +var startBitcoind = function(count, callback) { + + var listenCount = 0; + async.timesSeries(count, function(n, next) { + + var datadir = bitcoinDataDirs.shift(); + + bitcoin.datadir = datadir; + bitcoin.args.datadir = datadir; + + if (listenCount++ > 0) { + bitcoin.args.listen = 0; + bitcoin.args.rpcport++; + bitcoin.args.connect = '127.0.0.1'; + } + + rimraf(datadir, function(err) { + + if(err) { + return next(err); + } + + mkdirp(datadir, function(err) { + + if(err) { + return next(err); + } + + var args = bitcoin.args; + var argList = Object.keys(args).map(function(key) { + return '-' + key + '=' + args[key]; + }); + + var bitcoinProcess = spawn(bitcoin.exec, argList, bitcoin.opts); + bitcoin.processes.push(bitcoinProcess); + + bitcoinProcess.stdout.on('data', function(data) { + + if (debug) { + process.stdout.write(data.toString()); + } + + }); + + bitcoinProcess.stderr.on('data', function(data) { + + if (debug) { + process.stderr.write(data.toString()); + } + + }); + + next(); + + }); + + }); + }, function(err) { + + if (err) { + return callback(err); + } + + var pids = bitcoin.processes.map(function(process) { + return process.pid; + }); + + console.log(count + ' bitcoind\'s started at pid(s): ' + pids); + callback(); + }); +}; + + +var shutdownBitcoind = function(callback) { + bitcoin.processes.forEach(function(process) { + process.kill(); + }); + setTimeout(callback, 3000); +}; + +var shutdownBitcore = function(callback) { + if (bitcore.process) { + bitcore.process.kill(); + } + callback(); +}; + + +var buildInitialChain = function(callback) { + async.waterfall([ + function(next) { + console.log('checking to see if bitcoind\'s are connected to each other.'); + rpc1.getinfo(function(err, res) { + if (err || res.result.connections !== 1) { + next(err || new Error('bitcoind\'s not connected to each other.')); + } + next(); + }); + }, + function(next) { + console.log('generating 101 blocks'); + rpc1.generate(101, next); + }, + function(res, next) { + console.log('getting new address from rpc2'); + rpc2.getNewAddress(function(err, res) { + if (err) { + return next(err); + } + rpc2Address = res.result; + console.log(rpc2Address); + next(null, rpc2Address); + }); + }, + function(addr, next) { + rpc1.sendToAddress(rpc2Address, 25, next); + }, + function(res, next) { + tx1 = res.result; + console.log('TXID: ' + res.result); + console.log('generating 6 blocks'); + rpc1.generate(7, next); + }, + function(res, next) { + block = res.result[res.result.length - 1]; + rpc2.getBalance(function(err, res) { + console.log(res); + next(); + }); + }, + function(next) { + console.log('getting new address from rpc1'); + rpc1.getNewAddress(function(err, res) { + if (err) { + return next(err); + } + rpc1Address = res.result; + next(null, rpc1Address); + }); + }, + function(addr, next) { + rpc2.sendToAddress(rpc1Address, 20, next); + }, + function(res, next) { + tx2 = res.result; + console.log('sending from rpc2Address TXID: ', res); + console.log('generating 6 blocks'); + rpc2.generate(6, next); + } + ], function(err) { + + if (err) { + return callback(err); + } + rpc1.getInfo(function(err, res) { + console.log(res); + callback(); + }); + }); + +}; + +var startBitcore = function(callback) { + + rimraf(bitcoreDataDir, function(err) { + + if(err) { + return callback(err); + } + + mkdirp(bitcoreDataDir, function(err) { + + if(err) { + return callback(err); + } + + fs.writeFileSync(bitcore.configFile.file, JSON.stringify(bitcore.configFile.conf)); + + var args = bitcore.args; + bitcore.process = spawn(bitcore.exec, args, bitcore.opts); + + bitcore.process.stdout.on('data', function(data) { + + if (debug) { + process.stdout.write(data.toString()); + } + + }); + bitcore.process.stderr.on('data', function(data) { + + if (debug) { + process.stderr.write(data.toString()); + } + + }); + + callback(); + }); + + }); + + +}; + +describe('Transaction', function() { + + this.timeout(60000); + + before(function(done) { + + async.series([ + function(next) { + startBitcoind(2, next); + }, + function(next) { + setTimeout(function() { + buildInitialChain(next); + }, 8000); + }, + function(next) { + setTimeout(function() { + startBitcore(next); + }, 6000); + } + ], function(err) { + if (err) { + return done(err); + } + setTimeout(done, 2000); + }); + + }); + + after(function(done) { + shutdownBitcore(function() { + shutdownBitcoind(done); + }); + }); + + it('should get a transaction: /tx/:txid', function(done) { + + var request = http.request('http://localhost:53001/api/tx/' + tx1, function(res) { + + var error; + if (res.statusCode !== 200 && res.statusCode !== 201) { + if (error) { + return; + } + return done('Error from bitcore-node webserver: ' + 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; + } + var data = JSON.parse(resData); + expect(data.txid).to.equal(tx1); + done(); + }); + + }); + request.write(''); + request.end(); + }); + + it('should get transactions: /txs', function(done) { + + var request = http.request('http://localhost:53001/api/txs?block=' + block, function(res) { + + var error; + if (res.statusCode !== 200 && res.statusCode !== 201) { + if (error) { + return; + } + return done('Error from bitcore-node webserver: ' + 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; + } + var data = JSON.parse(resData); + console.log(resData); + expect(data.txs.length).to.equal(1); + done(); + }); + + }); + request.write(''); + request.end(); + }); + + it('should get a raw transactions: /rawtx/:txid', function(done) { + + var request = http.request('http://localhost:53001/api/rawtx/' + tx2, function(res) { + + var error; + if (res.statusCode !== 200 && res.statusCode !== 201) { + if (error) { + return; + } + return done('Error from bitcore-node webserver: ' + 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; + } + var data = JSON.parse(resData); + console.log(resData); + expect(data.rawtx).to.not.be.null; + done(); + }); + + }); + request.write(''); + request.end(); + }); +}); + + + diff --git a/test/addresses.js b/test/addresses.js index e94fd95..73e642a 100644 --- a/test/addresses.js +++ b/test/addresses.js @@ -184,8 +184,8 @@ var utxos = [ describe('Addresses', function() { var summary = { balance: 0, - totalReceived: 2782729129, - totalSpent: 2782729129, + totalReceivedSat: 2782729129, + totalSent: 2782729129, unconfirmedBalance: 0, appearances: 2, unconfirmedAppearances: 0, diff --git a/test/transactions.js b/test/transactions.js index d0553e6..b324b68 100644 --- a/test/transactions.js +++ b/test/transactions.js @@ -13,60 +13,60 @@ describe('Transactions', function() { it('should have correct data', function(done) { var insight = { - "txid": "eac9723230b8b632117ac3d75288d6f8eb81cf1ea553eb9fd42562d5f767d54a", - "version": 1, - "locktime": 0, - "vin": [ + 'txid': 'eac9723230b8b632117ac3d75288d6f8eb81cf1ea553eb9fd42562d5f767d54a', + 'version': 1, + 'locktime': 0, + 'vin': [ { - "txid": "46e58a68bb9ec9b458a9599dc2bac1e1fa09ad15c29c7f923c8f6f0aa33d6456", - "vout": 1, - "sequence": 4294967295, - "n": 0, - "scriptSig": { - "hex": "47304402203ddb49db43074b421ec6d5604ae91aac37f4715139e0c83ea1145379e8cbf02702207fbc92c4038ad501989b097844ae4e337c9388f0713110620b40e582b85fdff3012102cd90aa18ec8e3b35c0447ffc713c945cb837429d33d075d1b0f050c72ea838d2", - "asm": "304402203ddb49db43074b421ec6d5604ae91aac37f4715139e0c83ea1145379e8cbf02702207fbc92c4038ad501989b097844ae4e337c9388f0713110620b40e582b85fdff301 02cd90aa18ec8e3b35c0447ffc713c945cb837429d33d075d1b0f050c72ea838d2" + 'txid': '46e58a68bb9ec9b458a9599dc2bac1e1fa09ad15c29c7f923c8f6f0aa33d6456', + 'vout': 1, + 'sequence': 4294967295, + 'n': 0, + 'scriptSig': { + 'hex': '47304402203ddb49db43074b421ec6d5604ae91aac37f4715139e0c83ea1145379e8cbf02702207fbc92c4038ad501989b097844ae4e337c9388f0713110620b40e582b85fdff3012102cd90aa18ec8e3b35c0447ffc713c945cb837429d33d075d1b0f050c72ea838d2', + 'asm': '304402203ddb49db43074b421ec6d5604ae91aac37f4715139e0c83ea1145379e8cbf02702207fbc92c4038ad501989b097844ae4e337c9388f0713110620b40e582b85fdff301 02cd90aa18ec8e3b35c0447ffc713c945cb837429d33d075d1b0f050c72ea838d2' }, - "addr": "1NqgMfGUeELP2BfxD4hQuJSRq2d3DVJcCi", - "valueSat": 1546063700, - "value": 15.460637, - "doubleSpentTxID": null + 'addr': '1NqgMfGUeELP2BfxD4hQuJSRq2d3DVJcCi', + 'valueSat': 1546063700, + 'value': 15.460637, + 'doubleSpentTxID': null } ], - "vout": [ + 'vout': [ { - "value": "0.37100000", - "n": 0, - "scriptPubKey": { - "hex": "76a914bc345e0e0e5b0dbddb7b35ef3430fedd528dd1b788ac", - "asm": "OP_DUP OP_HASH160 bc345e0e0e5b0dbddb7b35ef3430fedd528dd1b7 OP_EQUALVERIFY OP_CHECKSIG", - "addresses": [ - "1JA8mcfrBv1YYsASAp9jtohm8x2q7LnUhs" + 'value': '0.37100000', + 'n': 0, + 'scriptPubKey': { + 'hex': '76a914bc345e0e0e5b0dbddb7b35ef3430fedd528dd1b788ac', + 'asm': 'OP_DUP OP_HASH160 bc345e0e0e5b0dbddb7b35ef3430fedd528dd1b7 OP_EQUALVERIFY OP_CHECKSIG', + 'addresses': [ + '1JA8mcfrBv1YYsASAp9jtohm8x2q7LnUhs' ], - "type": "pubkeyhash" + 'type': 'pubkeyhash' } }, { - "value": "15.08763700", - "n": 1, - "scriptPubKey": { - "hex": "76a914c2c74d7519d4425fc1a253f066d980164341554a88ac", - "asm": "OP_DUP OP_HASH160 c2c74d7519d4425fc1a253f066d980164341554a OP_EQUALVERIFY OP_CHECKSIG", - "addresses": [ - "1Jktr121Hm63qtWBMV8dCNJNW2KtWXy4fp" + 'value': '15.08763700', + 'n': 1, + 'scriptPubKey': { + 'hex': '76a914c2c74d7519d4425fc1a253f066d980164341554a88ac', + 'asm': 'OP_DUP OP_HASH160 c2c74d7519d4425fc1a253f066d980164341554a OP_EQUALVERIFY OP_CHECKSIG', + 'addresses': [ + '1Jktr121Hm63qtWBMV8dCNJNW2KtWXy4fp' ], - "type": "pubkeyhash" + 'type': 'pubkeyhash' } } ], - "blockhash": "0000000000000000009d2e9b7a984d55c6c99ba62f98e9bc7dad8b1e779045a3", - "blockheight": 481763, - "confirmations": 4, - "time": 1503507151, - "blocktime": 1503507151, - "valueOut": 15.458637, - "size": 225, - "valueIn": 15.460637, - "fees": 0.002 + 'blockhash': '0000000000000000009d2e9b7a984d55c6c99ba62f98e9bc7dad8b1e779045a3', + 'blockheight': 481763, + 'confirmations': 4, + 'time': 1503507151, + 'blocktime': 1503507151, + 'valueOut': 15.458637, + 'size': 225, + 'valueIn': 15.460637, + 'fees': 0.002 }; var bcoinTx = bcoin.tx.fromRaw('010000000156643da30a6f8f3c927f9cc215ad09fae1c1bac29d59a958b4c99ebb688ae546010000006a47304402203ddb49db43074b421ec6d5604ae91aac37f4715139e0c83ea1145379e8cbf02702207fbc92c4038ad501989b097844ae4e337c9388f0713110620b40e582b85fdff3012102cd90aa18ec8e3b35c0447ffc713c945cb837429d33d075d1b0f050c72ea838d2ffffffff02e0193602000000001976a914bc345e0e0e5b0dbddb7b35ef3430fedd528dd1b788ac34e8ed59000000001976a914c2c74d7519d4425fc1a253f066d980164341554a88ac00000000', 'hex'); @@ -85,7 +85,7 @@ describe('Transactions', function() { getTip: sinon.stub().returns({ height: 481766 }) }, transaction: { - getDetailedTransaction: sinon.stub().callsArgWith(1, null, bcoinTx) + getDetailedTransaction: sinon.stub().callsArgWith(2, null, bcoinTx) } } }; @@ -156,41 +156,41 @@ describe('Transactions', function() { var transactions = new TxController(node); var insight = { - "pagesTotal": 1, - "txs": [ + 'pagesTotal': 1, + 'txs': [ { - "txid": "9b0fc92260312ce44e74ef369f5c66bbb85848f2eddd5a7a1cde251e54ccfdd5", - "version": 1, - "locktime": 0, - "vin": [ + 'txid': '9b0fc92260312ce44e74ef369f5c66bbb85848f2eddd5a7a1cde251e54ccfdd5', + 'version': 1, + 'locktime': 0, + 'vin': [ { - "coinbase": "04ffff001d010b", - "sequence": 4294967295, - "n": 0 + 'coinbase': '04ffff001d010b', + 'sequence': 4294967295, + 'n': 0 } ], - "vout": [ + 'vout': [ { - "value": "50.00000000", - "n": 0, - "scriptPubKey": { - "hex": "41047211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073dee6c89064984f03385237d92167c13e236446b417ab79a0fcae412ae3316b77ac", - "asm": "047211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073dee6c89064984f03385237d92167c13e236446b417ab79a0fcae412ae3316b77 OP_CHECKSIG", - "addresses": [ - "1HLoD9E4SDFFPDiYfNYnkBLQ85Y51J3Zb1" + 'value': '50.00000000', + 'n': 0, + 'scriptPubKey': { + 'hex': '41047211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073dee6c89064984f03385237d92167c13e236446b417ab79a0fcae412ae3316b77ac', + 'asm': '047211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073dee6c89064984f03385237d92167c13e236446b417ab79a0fcae412ae3316b77 OP_CHECKSIG', + 'addresses': [ + '1HLoD9E4SDFFPDiYfNYnkBLQ85Y51J3Zb1' ], - "type": "pubkeyhash" + 'type': 'pubkeyhash' } } ], - "blockhash": "000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd", - "blockheight": 2, - "confirmations": 481772, - "time": 1231469744, - "blocktime": 1231469744, - "isCoinBase": true, - "valueOut": 50, - "size": 134 + 'blockhash': '000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd', + 'blockheight': 2, + 'confirmations': 481772, + 'time': 1231469744, + 'blocktime': 1231469744, + 'isCoinBase': true, + 'valueOut': 50, + 'size': 134 } ] }; @@ -238,49 +238,49 @@ describe('Transactions', function() { }; var insight = { - "pagesTotal": 1, - "txs": [ + 'pagesTotal': 1, + 'txs': [ { - "txid": "1c01a2090db0850e1f1049bea02e4bbf44b6790dfeb8e054f2beb69339ef52d4", - "version": 1, - "locktime": 0, - "vin": [ + 'txid': '1c01a2090db0850e1f1049bea02e4bbf44b6790dfeb8e054f2beb69339ef52d4', + 'version': 1, + 'locktime': 0, + 'vin': [ { - "coinbase": "03835807244d696e656420627920416e74506f6f6c6a2f4542312f4144362f4e59412f1d205999aaa02b1200001fff0200", - "sequence": 4294967295, - "n": 0 + 'coinbase': '03835807244d696e656420627920416e74506f6f6c6a2f4542312f4144362f4e59412f1d205999aaa02b1200001fff0200', + 'sequence': 4294967295, + 'n': 0 } ], - "vout": [ + 'vout': [ { - "value": "12.85927535", - "n": 0, - "scriptPubKey": { - "hex": "76a914ad7309dfc032d7f6b652e0c29ee353e63fffec6688ac", - "asm": "OP_DUP OP_HASH160 ad7309dfc032d7f6b652e0c29ee353e63fffec66 OP_EQUALVERIFY OP_CHECKSIG", - "addresses": [ - "1Gp7iCzDGMZiV55Kt8uKsux6VyoHe1aJaN" + 'value': '12.85927535', + 'n': 0, + 'scriptPubKey': { + 'hex': '76a914ad7309dfc032d7f6b652e0c29ee353e63fffec6688ac', + 'asm': 'OP_DUP OP_HASH160 ad7309dfc032d7f6b652e0c29ee353e63fffec66 OP_EQUALVERIFY OP_CHECKSIG', + 'addresses': [ + '1Gp7iCzDGMZiV55Kt8uKsux6VyoHe1aJaN' ], - "type": "pubkeyhash" + 'type': 'pubkeyhash' } }, { - "value": "0.00000000", - "n": 1, - "scriptPubKey": { - "hex": "6a24aa21a9ed55882e9fed16c5d3b6d77e4160a56f58c70d354d02888a99486125b638231c81", - "asm": "OP_RETURN aa21a9ed55882e9fed16c5d3b6d77e4160a56f58c70d354d02888a99486125b638231c81" + 'value': '0.00000000', + 'n': 1, + 'scriptPubKey': { + 'hex': '6a24aa21a9ed55882e9fed16c5d3b6d77e4160a56f58c70d354d02888a99486125b638231c81', + 'asm': 'OP_RETURN aa21a9ed55882e9fed16c5d3b6d77e4160a56f58c70d354d02888a99486125b638231c81' } } ], - "blockhash": "000000000000000000926a0cd4a05ef116514cbf1852edc306d13eb951ec0b54", - "blockheight": 481411, - "confirmations": 52813, - "time": 1503242912, - "blocktime": 1503242912, - "isCoinBase": true, - "valueOut": 12.85927535, - "size": 181 + 'blockhash': '000000000000000000926a0cd4a05ef116514cbf1852edc306d13eb951ec0b54', + 'blockheight': 481411, + 'confirmations': 52813, + 'time': 1503242912, + 'blocktime': 1503242912, + 'isCoinBase': true, + 'valueOut': 12.85927535, + 'size': 181 } ] }; @@ -321,6 +321,7 @@ describe('Transactions', function() { var transactions = new TxController(node); + var txid = '25a988e54b02e0e5df146a0f8fa7b9db56210533a9f04bdfda5f4ceb6f77aadd'; var res = {}; var req = { params: { @@ -331,7 +332,6 @@ describe('Transactions', function() { should(req.rawTransaction.rawtx).eql(hex); done(); }; - var txid = '25a988e54b02e0e5df146a0f8fa7b9db56210533a9f04bdfda5f4ceb6f77aadd'; transactions.rawTransaction(req, res, next); }); });