test: update unit tests, refactoring and cleanup
This commit is contained in:
parent
848dc29777
commit
890b38744d
|
@ -14,9 +14,5 @@ node_js:
|
||||||
- "v0.12.7"
|
- "v0.12.7"
|
||||||
- "v4"
|
- "v4"
|
||||||
script:
|
script:
|
||||||
- _mocha -R spec regtest/p2p.js
|
- npm run regtest
|
||||||
- _mocha -R spec regtest/bitcoind.js
|
- npm run test
|
||||||
- _mocha -R spec regtest/cluster.js
|
|
||||||
- _mocha -R spec regtest/node.js
|
|
||||||
- _mocha -R spec --recursive
|
|
||||||
|
|
||||||
|
|
|
@ -3,23 +3,10 @@
|
||||||
var createError = require('errno').create;
|
var createError = require('errno').create;
|
||||||
|
|
||||||
var BitcoreNodeError = createError('BitcoreNodeError');
|
var BitcoreNodeError = createError('BitcoreNodeError');
|
||||||
var NoOutputs = createError('NoOutputs', BitcoreNodeError);
|
|
||||||
var NoOutput = createError('NoOutput', BitcoreNodeError);
|
|
||||||
|
|
||||||
var Wallet = createError('WalletError', BitcoreNodeError);
|
var RPCError = createError('RPCError', BitcoreNodeError);
|
||||||
Wallet.InsufficientFunds = createError('InsufficientFunds', Wallet);
|
|
||||||
|
|
||||||
var Consensus = createError('Consensus', BitcoreNodeError);
|
|
||||||
Consensus.BlockExists = createError('BlockExists', Consensus);
|
|
||||||
|
|
||||||
var Transaction = createError('Transaction', BitcoreNodeError);
|
|
||||||
Transaction.NotFound = createError('NotFound', Transaction);
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Error: BitcoreNodeError,
|
Error: BitcoreNodeError,
|
||||||
NoOutputs: NoOutputs,
|
RPCError: RPCError
|
||||||
NoOutput: NoOutput,
|
|
||||||
Wallet: Wallet,
|
|
||||||
Consensus: Consensus,
|
|
||||||
Transaction: Transaction
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,6 +7,7 @@ var path = require('path');
|
||||||
* or default locations.
|
* or default locations.
|
||||||
* @param {Object} options
|
* @param {Object} options
|
||||||
* @param {String} options.network - "testnet" or "livenet"
|
* @param {String} options.network - "testnet" or "livenet"
|
||||||
|
* @param {String} options.datadir - Absolute path to bitcoin database directory
|
||||||
*/
|
*/
|
||||||
function getDefaultBaseConfig(options) {
|
function getDefaultBaseConfig(options) {
|
||||||
if (!options) {
|
if (!options) {
|
||||||
|
|
|
@ -14,7 +14,9 @@ var $ = bitcore.util.preconditions;
|
||||||
var _ = bitcore.deps._;
|
var _ = bitcore.deps._;
|
||||||
|
|
||||||
var index = require('../');
|
var index = require('../');
|
||||||
|
var errors = index.errors;
|
||||||
var log = index.log;
|
var log = index.log;
|
||||||
|
var utils = require('../utils');
|
||||||
var Service = require('../service');
|
var Service = require('../service');
|
||||||
var Transaction = require('../transaction');
|
var Transaction = require('../transaction');
|
||||||
|
|
||||||
|
@ -45,6 +47,12 @@ function Bitcoin(options) {
|
||||||
this.subscriptions.transaction = [];
|
this.subscriptions.transaction = [];
|
||||||
this.subscriptions.block = [];
|
this.subscriptions.block = [];
|
||||||
|
|
||||||
|
// limits
|
||||||
|
this.maxAddressesQuery = options.maxAddressesQuery || Bitcoin.DEFAULT_MAX_ADDRESSES_QUERY;
|
||||||
|
|
||||||
|
// try all interval
|
||||||
|
this.tryAllInterval = options.tryAllInterval || Bitcoin.DEFAULT_TRY_ALL_INTERVAL;
|
||||||
|
|
||||||
// available bitcoind nodes
|
// available bitcoind nodes
|
||||||
this._initClients();
|
this._initClients();
|
||||||
}
|
}
|
||||||
|
@ -52,7 +60,21 @@ util.inherits(Bitcoin, Service);
|
||||||
|
|
||||||
Bitcoin.dependencies = [];
|
Bitcoin.dependencies = [];
|
||||||
|
|
||||||
Bitcoin.DEFAULT_CONFIG = 'whitelist=127.0.0.1\n' + 'txindex=1\n' + 'addressindex=1\n' + 'server=1\n';
|
Bitcoin.DEFAULT_MAX_ADDRESSES_QUERY = 10000;
|
||||||
|
Bitcoin.DEFAULT_TRY_ALL_INTERVAL = 1000;
|
||||||
|
Bitcoin.DEFAULT_CONFIG_SETTINGS = {
|
||||||
|
server: 1,
|
||||||
|
whitelist: '127.0.0.1',
|
||||||
|
txindex: 1,
|
||||||
|
addressindex: 1,
|
||||||
|
timestampindex: 1,
|
||||||
|
spentindex: 1,
|
||||||
|
zmqpubrawtx: 'tcp://127.0.0.1:28332',
|
||||||
|
zmqpubhashblock: 'tcp://127.0.0.1:28332',
|
||||||
|
rpcallowip: '127.0.0.1',
|
||||||
|
rpcuser: 'bitcoin',
|
||||||
|
rpcpassword: 'local321'
|
||||||
|
};
|
||||||
|
|
||||||
Bitcoin.prototype._initCaches = function() {
|
Bitcoin.prototype._initCaches = function() {
|
||||||
// caches valid until there is a new block
|
// caches valid until there is a new block
|
||||||
|
@ -70,8 +92,8 @@ Bitcoin.prototype._initCaches = function() {
|
||||||
this.blockHeaderCache = LRU(288);
|
this.blockHeaderCache = LRU(288);
|
||||||
this.zmqKnownTransactions = LRU(50);
|
this.zmqKnownTransactions = LRU(50);
|
||||||
this.zmqKnownBlocks = LRU(50);
|
this.zmqKnownBlocks = LRU(50);
|
||||||
this.zmqLastBlock = 0;
|
this.lastTip = 0;
|
||||||
this.zmqUpdateTipTimeout = false;
|
this.lastTipTimeout = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
Bitcoin.prototype._initClients = function() {
|
Bitcoin.prototype._initClients = function() {
|
||||||
|
@ -149,6 +171,15 @@ Bitcoin.prototype.unsubscribe = function(name, emitter) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Bitcoin.prototype._getDefaultConfig = function() {
|
||||||
|
var config = '';
|
||||||
|
var defaults = Bitcoin.DEFAULT_CONFIG_SETTINGS;
|
||||||
|
for(var key in defaults) {
|
||||||
|
config += key + '=' + defaults[key] + '\n';
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
};
|
||||||
|
|
||||||
Bitcoin.prototype._loadSpawnConfiguration = function(node) {
|
Bitcoin.prototype._loadSpawnConfiguration = function(node) {
|
||||||
/* jshint maxstatements: 25 */
|
/* jshint maxstatements: 25 */
|
||||||
|
|
||||||
|
@ -169,6 +200,11 @@ Bitcoin.prototype._loadSpawnConfiguration = function(node) {
|
||||||
mkdirp.sync(spawnOptions.datadir);
|
mkdirp.sync(spawnOptions.datadir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!fs.existsSync(configPath)) {
|
||||||
|
var defaultConfig = this._getDefaultConfig();
|
||||||
|
fs.writeFileSync(configPath, defaultConfig);
|
||||||
|
}
|
||||||
|
|
||||||
var file = fs.readFileSync(configPath);
|
var file = fs.readFileSync(configPath);
|
||||||
var unparsed = file.toString().split('\n');
|
var unparsed = file.toString().split('\n');
|
||||||
for(var i = 0; i < unparsed.length; i++) {
|
for(var i = 0; i < unparsed.length; i++) {
|
||||||
|
@ -187,6 +223,11 @@ Bitcoin.prototype._loadSpawnConfiguration = function(node) {
|
||||||
|
|
||||||
var spawnConfig = this.spawn.config;
|
var spawnConfig = this.spawn.config;
|
||||||
|
|
||||||
|
this._checkConfigIndexes(spawnConfig, node);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
Bitcoin.prototype._checkConfigIndexes = function(spawnConfig, node) {
|
||||||
$.checkState(
|
$.checkState(
|
||||||
spawnConfig.txindex && spawnConfig.txindex === 1,
|
spawnConfig.txindex && spawnConfig.txindex === 1,
|
||||||
'"txindex" option is required in order to use transaction query features of bitcore-node. ' +
|
'"txindex" option is required in order to use transaction query features of bitcore-node. ' +
|
||||||
|
@ -233,7 +274,6 @@ Bitcoin.prototype._loadSpawnConfiguration = function(node) {
|
||||||
'of bitcore-node services will start.');
|
'of bitcore-node services will start.');
|
||||||
node._reindex = true;
|
node._reindex = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Bitcoin.prototype._resetCaches = function() {
|
Bitcoin.prototype._resetCaches = function() {
|
||||||
|
@ -245,11 +285,11 @@ Bitcoin.prototype._resetCaches = function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
Bitcoin.prototype._tryAll = function(func, callback) {
|
Bitcoin.prototype._tryAll = function(func, callback) {
|
||||||
async.retry({times: this.nodes.length, interval: 1000}, func, callback);
|
async.retry({times: this.nodes.length, interval: this.tryAllInterval || 1000}, func, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
Bitcoin.prototype._wrapRPCError = function(errObj) {
|
Bitcoin.prototype._wrapRPCError = function(errObj) {
|
||||||
var err = new Error(errObj.message);
|
var err = new errors.RPCError(errObj.message);
|
||||||
err.code = errObj.code;
|
err.code = errObj.code;
|
||||||
return err;
|
return err;
|
||||||
};
|
};
|
||||||
|
@ -274,11 +314,11 @@ Bitcoin.prototype._initChain = function(callback) {
|
||||||
return callback(self._wrapRPCError(err));
|
return callback(self._wrapRPCError(err));
|
||||||
}
|
}
|
||||||
var blockhash = response.result;
|
var blockhash = response.result;
|
||||||
self.getBlock(blockhash, function(err, block) {
|
self.getRawBlock(blockhash, function(err, blockBuffer) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
self.genesisBuffer = block.toBuffer();
|
self.genesisBuffer = blockBuffer;
|
||||||
self.emit('ready');
|
self.emit('ready');
|
||||||
log.info('Bitcoin Daemon Ready');
|
log.info('Bitcoin Daemon Ready');
|
||||||
callback();
|
callback();
|
||||||
|
@ -303,55 +343,72 @@ Bitcoin.prototype._getNetworkOption = function() {
|
||||||
Bitcoin.prototype._zmqBlockHandler = function(node, message) {
|
Bitcoin.prototype._zmqBlockHandler = function(node, message) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
function updateChain() {
|
// Update the current chain tip
|
||||||
var hex = message.toString('hex');
|
self._rapidProtectedUpdateTip(node, message);
|
||||||
if (hex !== self.tiphash) {
|
|
||||||
self._resetCaches();
|
|
||||||
self.tiphash = message.toString('hex');
|
|
||||||
node.client.getBlock(self.tiphash, function(err, response) {
|
|
||||||
if (err) {
|
|
||||||
return log.error(self._wrapRPCError(err));
|
|
||||||
}
|
|
||||||
self.height = response.result.height;
|
|
||||||
$.checkState(self.height >= 0);
|
|
||||||
self.emit('tip', self.height);
|
|
||||||
});
|
|
||||||
|
|
||||||
if(!self.node.stopping) {
|
|
||||||
self.syncPercentage(function(err, percentage) {
|
|
||||||
if (err) {
|
|
||||||
return log.error(err);
|
|
||||||
}
|
|
||||||
if (Math.round(percentage) >= 100) {
|
|
||||||
self.emit('synced', self.height);
|
|
||||||
}
|
|
||||||
log.info('Bitcoin Height:', self.height, 'Percentage:', percentage.toFixed(2));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prevent a rapid succession of tip updates
|
|
||||||
if (new Date() - self.zmqLastBlock > 1000) {
|
|
||||||
self.zmqLastBlock = new Date();
|
|
||||||
updateChain();
|
|
||||||
} else {
|
|
||||||
clearTimeout(self.zmqUpdateTipTimeout);
|
|
||||||
self.zmqUpdateTipTimeout = setTimeout(function() {
|
|
||||||
updateChain();
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify block subscribers
|
// Notify block subscribers
|
||||||
var id = message.toString('binary');
|
var id = message.toString('binary');
|
||||||
if (!self.zmqKnownBlocks[id]) {
|
if (!self.zmqKnownBlocks.get(id)) {
|
||||||
self.zmqKnownBlocks[id] = true;
|
self.zmqKnownBlocks.set(id, true);
|
||||||
self.emit('block', message);
|
self.emit('block', message);
|
||||||
|
|
||||||
for (var i = 0; i < this.subscriptions.block.length; i++) {
|
for (var i = 0; i < this.subscriptions.block.length; i++) {
|
||||||
this.subscriptions.block[i].emit('bitcoind/block', message.toString('hex'));
|
this.subscriptions.block[i].emit('bitcoind/block', message.toString('hex'));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
Bitcoin.prototype._rapidProtectedUpdateTip = function(node, message) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
// Prevent a rapid succession of tip updates
|
||||||
|
if (new Date() - self.lastTip > 1000) {
|
||||||
|
self.lastTip = new Date();
|
||||||
|
self._updateTip(node, message);
|
||||||
|
} else {
|
||||||
|
clearTimeout(self.lastTipTimeout);
|
||||||
|
self.lastTipTimeout = setTimeout(function() {
|
||||||
|
self._updateTip(node, message);
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Bitcoin.prototype._updateTip = function(node, message) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
var hex = message.toString('hex');
|
||||||
|
if (hex !== self.tiphash) {
|
||||||
|
self.tiphash = message.toString('hex');
|
||||||
|
|
||||||
|
// reset block valid caches
|
||||||
|
self._resetCaches();
|
||||||
|
|
||||||
|
node.client.getBlock(self.tiphash, function(err, response) {
|
||||||
|
if (err) {
|
||||||
|
var error = self._wrapRPCError(err);
|
||||||
|
log.error(error);
|
||||||
|
self.emit('error', error);
|
||||||
|
} else {
|
||||||
|
self.height = response.result.height;
|
||||||
|
$.checkState(self.height >= 0);
|
||||||
|
self.emit('tip', self.height);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if(!self.node.stopping) {
|
||||||
|
self.syncPercentage(function(err, percentage) {
|
||||||
|
if (err) {
|
||||||
|
log.error(err);
|
||||||
|
self.emit('error', err);
|
||||||
|
} else {
|
||||||
|
if (Math.round(percentage) >= 100) {
|
||||||
|
self.emit('synced', self.height);
|
||||||
|
}
|
||||||
|
log.info('Bitcoin Height:', self.height, 'Percentage:', percentage.toFixed(2));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -359,16 +416,15 @@ Bitcoin.prototype._zmqBlockHandler = function(node, message) {
|
||||||
Bitcoin.prototype._zmqTransactionHandler = function(node, message) {
|
Bitcoin.prototype._zmqTransactionHandler = function(node, message) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var id = message.toString('binary');
|
var id = message.toString('binary');
|
||||||
if (!self.zmqKnownTransactions[id]) {
|
if (!self.zmqKnownTransactions.get(id)) {
|
||||||
self.zmqKnownTransactions[id] = true;
|
self.zmqKnownTransactions.set(id, true);
|
||||||
self.emit('tx', message);
|
self.emit('tx', message);
|
||||||
}
|
|
||||||
|
|
||||||
// Notify transaction subscribers
|
// Notify transaction subscribers
|
||||||
for (var i = 0; i < this.subscriptions.transaction.length; i++) {
|
for (var i = 0; i < this.subscriptions.transaction.length; i++) {
|
||||||
this.subscriptions.transaction[i].emit('bitcoind/transaction', message);
|
this.subscriptions.transaction[i].emit('bitcoind/transaction', message.toString('hex'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Bitcoin.prototype._subscribeZmqEvents = function(node) {
|
Bitcoin.prototype._subscribeZmqEvents = function(node) {
|
||||||
|
@ -414,21 +470,24 @@ Bitcoin.prototype._initZmqSubSocket = function(node, zmqUrl) {
|
||||||
|
|
||||||
Bitcoin.prototype._checkReindex = function(node, callback) {
|
Bitcoin.prototype._checkReindex = function(node, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var interval;
|
||||||
|
function finish(err) {
|
||||||
|
clearInterval(interval);
|
||||||
|
callback(err);
|
||||||
|
}
|
||||||
if (node._reindex) {
|
if (node._reindex) {
|
||||||
var interval = setInterval(function() {
|
interval = setInterval(function() {
|
||||||
node.client.syncPercentage(function(err, percentSynced) {
|
node.client.syncPercentage(function(err, percentSynced) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return log.error(self._wrapRPCError(err));
|
return finish(self._wrapRPCError(err));
|
||||||
}
|
}
|
||||||
log.info('Bitcoin Core Daemon Reindex Percentage: ' + percentSynced.toFixed(2));
|
log.info('Bitcoin Core Daemon Reindex Percentage: ' + percentSynced.toFixed(2));
|
||||||
if (Math.round(percentSynced) >= 100) {
|
if (Math.round(percentSynced) >= 100) {
|
||||||
node._reindex = false;
|
node._reindex = false;
|
||||||
callback();
|
finish();
|
||||||
clearInterval(interval);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, self._reindexWait);
|
}, self._reindexWait);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
|
@ -787,11 +846,15 @@ Bitcoin.prototype.getAddressTxids = function(addressArg, options, callback) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Bitcoin.prototype._getConfirmationsDetail = function(transaction) {
|
Bitcoin.prototype._getConfirmationsDetail = function(transaction) {
|
||||||
|
$.checkState(this.height > 0, 'current height is unknown');
|
||||||
var confirmations = 0;
|
var confirmations = 0;
|
||||||
if (transaction.__height >= 0) {
|
if (transaction.__height >= 0) {
|
||||||
confirmations = this.height - transaction.__height + 1;
|
confirmations = this.height - transaction.__height + 1;
|
||||||
}
|
}
|
||||||
return confirmations;
|
if (confirmations < 0) {
|
||||||
|
log.warn('Negative confirmations calculated for transaction:', transaction.hash);
|
||||||
|
}
|
||||||
|
return Math.max(0, confirmations);
|
||||||
};
|
};
|
||||||
|
|
||||||
Bitcoin.prototype._getAddressDetailsForTransaction = function(transaction, addressStrings) {
|
Bitcoin.prototype._getAddressDetailsForTransaction = function(transaction, addressStrings) {
|
||||||
|
@ -904,6 +967,7 @@ Bitcoin.prototype._getAddressStrings = function(addresses) {
|
||||||
Bitcoin.prototype._paginateTxids = function(fullTxids, from, to) {
|
Bitcoin.prototype._paginateTxids = function(fullTxids, from, to) {
|
||||||
var txids;
|
var txids;
|
||||||
if (from >= 0 && to >= 0) {
|
if (from >= 0 && to >= 0) {
|
||||||
|
$.checkState(from < to, '"from" is expected to be less than "to"');
|
||||||
txids = fullTxids.slice(from, to);
|
txids = fullTxids.slice(from, to);
|
||||||
} else {
|
} else {
|
||||||
txids = fullTxids;
|
txids = fullTxids;
|
||||||
|
@ -933,7 +997,11 @@ Bitcoin.prototype.getAddressHistory = function(addressArg, options, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var totalCount = txids.length;
|
var totalCount = txids.length;
|
||||||
txids = self._paginateTxids(txids, options.from, options.to);
|
try {
|
||||||
|
txids = self._paginateTxids(txids, options.from, options.to);
|
||||||
|
} catch(e) {
|
||||||
|
return callback(e);
|
||||||
|
}
|
||||||
|
|
||||||
async.mapSeries(
|
async.mapSeries(
|
||||||
txids,
|
txids,
|
||||||
|
@ -1304,6 +1372,7 @@ Bitcoin.prototype.getTransaction = function(txid, callback) {
|
||||||
* @param {Function} callback
|
* @param {Function} callback
|
||||||
*/
|
*/
|
||||||
Bitcoin.prototype.getTransactionWithBlockInfo = function(txid, callback) {
|
Bitcoin.prototype.getTransactionWithBlockInfo = function(txid, callback) {
|
||||||
|
// TODO give response back as standard js object with bitcore tx
|
||||||
var self = this;
|
var self = this;
|
||||||
var tx = self.transactionInfoCache.get(txid);
|
var tx = self.transactionInfoCache.get(txid);
|
||||||
if (tx) {
|
if (tx) {
|
||||||
|
@ -1408,9 +1477,11 @@ Bitcoin.prototype.generateBlock = function(num, callback) {
|
||||||
*/
|
*/
|
||||||
Bitcoin.prototype.stop = function(callback) {
|
Bitcoin.prototype.stop = function(callback) {
|
||||||
if (this.spawn && this.spawn.process) {
|
if (this.spawn && this.spawn.process) {
|
||||||
this.spawn.process.once('exit', function(err, status) {
|
this.spawn.process.once('exit', function(code) {
|
||||||
if (err) {
|
if (code !== 0) {
|
||||||
return callback(err);
|
var error = new Error('bitcoind spawned process exited with status code: ' + code);
|
||||||
|
error.code = code;
|
||||||
|
return callback(error);
|
||||||
} else {
|
} else {
|
||||||
return callback();
|
return callback();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,6 @@ var async = require('async');
|
||||||
var bitcore = require('bitcore-lib');
|
var bitcore = require('bitcore-lib');
|
||||||
var Transaction = bitcore.Transaction;
|
var Transaction = bitcore.Transaction;
|
||||||
|
|
||||||
var index = require('./');
|
|
||||||
var errors = index.errors;
|
|
||||||
|
|
||||||
var MAX_TRANSACTION_LIMIT = 5;
|
var MAX_TRANSACTION_LIMIT = 5;
|
||||||
|
|
||||||
Transaction.prototype.populateSpentInfo = function(db, options, callback) {
|
Transaction.prototype.populateSpentInfo = function(db, options, callback) {
|
||||||
|
@ -53,11 +50,13 @@ Transaction.prototype.populateInputs = function(db, poolTransactions, callback)
|
||||||
|
|
||||||
Transaction.prototype._populateInput = function(db, input, poolTransactions, callback) {
|
Transaction.prototype._populateInput = function(db, input, poolTransactions, callback) {
|
||||||
if (!input.prevTxId || !Buffer.isBuffer(input.prevTxId)) {
|
if (!input.prevTxId || !Buffer.isBuffer(input.prevTxId)) {
|
||||||
return callback(new Error('Input is expected to have prevTxId as a buffer'));
|
return callback(new TypeError('Input is expected to have prevTxId as a buffer'));
|
||||||
}
|
}
|
||||||
var txid = input.prevTxId.toString('hex');
|
var txid = input.prevTxId.toString('hex');
|
||||||
db.getTransaction(txid, function(err, prevTx) {
|
db.getTransaction(txid, function(err, prevTx) {
|
||||||
if(!prevTx) {
|
if(err) {
|
||||||
|
return callback(err);
|
||||||
|
} else if (!prevTx) {
|
||||||
// Check the pool for transaction
|
// Check the pool for transaction
|
||||||
for(var i = 0; i < poolTransactions.length; i++) {
|
for(var i = 0; i < poolTransactions.length; i++) {
|
||||||
if(txid === poolTransactions[i].hash) {
|
if(txid === poolTransactions[i].hash) {
|
||||||
|
@ -65,25 +64,10 @@ Transaction.prototype._populateInput = function(db, input, poolTransactions, cal
|
||||||
return callback();
|
return callback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return callback(new Error('Previous tx ' + input.prevTxId.toString('hex') + ' not found'));
|
return callback(new Error('Previous tx ' + input.prevTxId.toString('hex') + ' not found'));
|
||||||
} else if(err) {
|
|
||||||
callback(err);
|
|
||||||
} else {
|
|
||||||
input.output = prevTx.outputs[input.outputIndex];
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Transaction.prototype._checkSpent = function(db, input, poolTransactions, callback) {
|
|
||||||
// TODO check and see if another transaction in the pool spent the output
|
|
||||||
db.isSpentDB(input, function(spent) {
|
|
||||||
if(spent) {
|
|
||||||
return callback(new Error('Input already spent'));
|
|
||||||
} else {
|
|
||||||
callback();
|
|
||||||
}
|
}
|
||||||
|
input.output = prevTx.outputs[input.outputIndex];
|
||||||
|
callback();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"install": "./scripts/install",
|
"install": "./scripts/install",
|
||||||
"test": "NODE_ENV=test mocha -R spec --recursive",
|
"test": "NODE_ENV=test mocha -R spec --recursive",
|
||||||
|
"regtest": "./scripts/regtest",
|
||||||
"coverage": "NODE_ENV=test istanbul cover _mocha -- --recursive"
|
"coverage": "NODE_ENV=test istanbul cover _mocha -- --recursive"
|
||||||
},
|
},
|
||||||
"tags": [
|
"tags": [
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
_mocha -R spec regtest/p2p.js
|
||||||
|
_mocha -R spec regtest/bitcoind.js
|
||||||
|
_mocha -R spec regtest/cluster.js
|
||||||
|
_mocha -R spec regtest/node.js
|
|
@ -1,18 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var should = require('chai').should();
|
|
||||||
var path = require('path');
|
|
||||||
var getTarballName = require('../../bin/get-tarball-name');
|
|
||||||
var execSync = require('child_process').execSync;
|
|
||||||
|
|
||||||
describe('#getTarballName', function() {
|
|
||||||
it('will return the expected tarball name', function() {
|
|
||||||
var name = getTarballName();
|
|
||||||
var version = require(path.resolve(__dirname + '../../../package.json')).version;
|
|
||||||
var platform = process.platform;
|
|
||||||
var arch = execSync(path.resolve(__dirname) + '/../../bin/variables.sh arch');
|
|
||||||
var abi = process.versions.modules;
|
|
||||||
var expected = 'libbitcoind-' + version + '-node' + abi + '-' + platform + '-' + arch + '.tgz';
|
|
||||||
name.should.equal(expected);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,17 +1,23 @@
|
||||||
#testnet=1
|
#testnet=1
|
||||||
#irc=0
|
#irc=0
|
||||||
#upnp=0
|
upnp=0
|
||||||
server=1
|
server=1
|
||||||
|
|
||||||
whitelist=127.0.0.1
|
whitelist=127.0.0.1
|
||||||
txindex=1
|
txindex=1
|
||||||
|
addressindex=1
|
||||||
|
timestampindex=1
|
||||||
|
spentindex=1
|
||||||
|
dbcache=8192
|
||||||
|
checkblocks=144
|
||||||
|
maxuploadtarget=1024
|
||||||
|
zmqpubrawtx=tcp://127.0.0.1:28332
|
||||||
|
zmqpubhashblock=tcp://127.0.0.1:28332
|
||||||
|
|
||||||
# listen on different ports
|
|
||||||
port=20000
|
port=20000
|
||||||
|
rpcport=50001
|
||||||
|
|
||||||
rpcallowip=127.0.0.1
|
rpcallowip=127.0.0.1
|
||||||
|
|
||||||
rpcuser=bitcoin
|
rpcuser=bitcoin
|
||||||
rpcpassword=local321
|
rpcpassword=local321
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
server=1
|
||||||
|
whitelist=127.0.0.1
|
||||||
|
txindex=1
|
||||||
|
addressindex=1
|
||||||
|
timestampindex=1
|
||||||
|
spentindex=1
|
||||||
|
zmqpubrawtx=tcp://127.0.0.1:28332
|
||||||
|
zmqpubhashblock=tcp://127.0.0.1:28332
|
||||||
|
rpcallowip=127.0.0.1
|
||||||
|
rpcuser=bitcoin
|
||||||
|
rpcpassword=local321
|
|
@ -1,19 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var should = require('chai').should();
|
|
||||||
var index = require('..');
|
|
||||||
|
|
||||||
describe('Index', function() {
|
|
||||||
describe('#nodeVersionCheck', function() {
|
|
||||||
it('will throw informative error message with incompatible Node.js version 4.1.2', function() {
|
|
||||||
(function() {
|
|
||||||
index.nodeVersionCheck('4.1.2', '>=0.12.0 <1');
|
|
||||||
}).should.throw('Node.js version');
|
|
||||||
});
|
|
||||||
it('will throw informative error message with incompatible Node.js version 0.10.40', function() {
|
|
||||||
(function() {
|
|
||||||
index.nodeVersionCheck('4.1.2', '>=0.12.0 <1');
|
|
||||||
}).should.throw('Node.js version');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,6 +1,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var should = require('chai').should();
|
var should = require('chai').should();
|
||||||
|
var path = require('path');
|
||||||
var defaultBaseConfig = require('../../lib/scaffold/default-base-config');
|
var defaultBaseConfig = require('../../lib/scaffold/default-base-config');
|
||||||
|
|
||||||
describe('#defaultBaseConfig', function() {
|
describe('#defaultBaseConfig', function() {
|
||||||
|
@ -9,29 +10,19 @@ describe('#defaultBaseConfig', function() {
|
||||||
var home = process.env.HOME;
|
var home = process.env.HOME;
|
||||||
var info = defaultBaseConfig();
|
var info = defaultBaseConfig();
|
||||||
info.path.should.equal(cwd);
|
info.path.should.equal(cwd);
|
||||||
info.config.datadir.should.equal(home + '/.bitcoin');
|
|
||||||
info.config.network.should.equal('livenet');
|
info.config.network.should.equal('livenet');
|
||||||
info.config.port.should.equal(3001);
|
info.config.port.should.equal(3001);
|
||||||
info.config.services.should.deep.equal(['bitcoind', 'db', 'address', 'web']);
|
info.config.services.should.deep.equal(['bitcoind', 'web']);
|
||||||
|
var bitcoind = info.config.servicesConfig.bitcoind;
|
||||||
|
bitcoind.spawn.datadir.should.equal(home + '/.bitcoin');
|
||||||
|
bitcoind.spawn.exec.should.equal(path.resolve(__dirname, '../../bin/bitcoind'));
|
||||||
});
|
});
|
||||||
it('be able to specify a network', function() {
|
it('be able to specify a network', function() {
|
||||||
var cwd = process.cwd();
|
|
||||||
var home = process.env.HOME;
|
|
||||||
var info = defaultBaseConfig({network: 'testnet'});
|
var info = defaultBaseConfig({network: 'testnet'});
|
||||||
info.path.should.equal(cwd);
|
|
||||||
info.config.datadir.should.equal(home + '/.bitcoin');
|
|
||||||
info.config.network.should.equal('testnet');
|
info.config.network.should.equal('testnet');
|
||||||
info.config.port.should.equal(3001);
|
|
||||||
info.config.services.should.deep.equal(['bitcoind', 'db', 'address', 'web']);
|
|
||||||
});
|
});
|
||||||
it('be able to specify a datadir', function() {
|
it('be able to specify a datadir', function() {
|
||||||
var cwd = process.cwd();
|
|
||||||
var home = process.env.HOME;
|
|
||||||
var info = defaultBaseConfig({datadir: './data2', network: 'testnet'});
|
var info = defaultBaseConfig({datadir: './data2', network: 'testnet'});
|
||||||
info.path.should.equal(cwd);
|
info.config.servicesConfig.bitcoind.spawn.datadir.should.equal('./data2');
|
||||||
info.config.datadir.should.equal('./data2');
|
|
||||||
info.config.network.should.equal('testnet');
|
|
||||||
info.config.port.should.equal(3001);
|
|
||||||
info.config.services.should.deep.equal(['bitcoind', 'db', 'address', 'web']);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,21 +1,29 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
var path = require('path');
|
||||||
var should = require('chai').should();
|
var should = require('chai').should();
|
||||||
var sinon = require('sinon');
|
var sinon = require('sinon');
|
||||||
var proxyquire = require('proxyquire');
|
var proxyquire = require('proxyquire');
|
||||||
|
|
||||||
describe('#defaultConfig', function() {
|
describe('#defaultConfig', function() {
|
||||||
|
var expectedExecPath = path.resolve(__dirname, '../../bin/bitcoind');
|
||||||
|
|
||||||
it('will return expected configuration', function() {
|
it('will return expected configuration', function() {
|
||||||
var config = JSON.stringify({
|
var config = JSON.stringify({
|
||||||
datadir: process.env.HOME + '/.bitcore/data',
|
|
||||||
network: 'livenet',
|
network: 'livenet',
|
||||||
port: 3001,
|
port: 3001,
|
||||||
services: [
|
services: [
|
||||||
'bitcoind',
|
'bitcoind',
|
||||||
'db',
|
|
||||||
'address',
|
|
||||||
'web'
|
'web'
|
||||||
]
|
],
|
||||||
|
servicesConfig: {
|
||||||
|
bitcoind: {
|
||||||
|
spawn: {
|
||||||
|
datadir: process.env.HOME + '/.bitcore/data',
|
||||||
|
exec: expectedExecPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}, null, 2);
|
}, null, 2);
|
||||||
var defaultConfig = proxyquire('../../lib/scaffold/default-config', {
|
var defaultConfig = proxyquire('../../lib/scaffold/default-config', {
|
||||||
fs: {
|
fs: {
|
||||||
|
@ -32,28 +40,35 @@ describe('#defaultConfig', function() {
|
||||||
sync: sinon.stub()
|
sync: sinon.stub()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
var cwd = process.cwd();
|
|
||||||
var home = process.env.HOME;
|
var home = process.env.HOME;
|
||||||
var info = defaultConfig();
|
var info = defaultConfig();
|
||||||
info.path.should.equal(home + '/.bitcore');
|
info.path.should.equal(home + '/.bitcore');
|
||||||
info.config.datadir.should.equal(home + '/.bitcore/data');
|
|
||||||
info.config.network.should.equal('livenet');
|
info.config.network.should.equal('livenet');
|
||||||
info.config.port.should.equal(3001);
|
info.config.port.should.equal(3001);
|
||||||
info.config.services.should.deep.equal(['bitcoind', 'db', 'address', 'web']);
|
info.config.services.should.deep.equal(['bitcoind', 'web']);
|
||||||
|
var bitcoind = info.config.servicesConfig.bitcoind;
|
||||||
|
should.exist(bitcoind);
|
||||||
|
bitcoind.spawn.datadir.should.equal(home + '/.bitcore/data');
|
||||||
|
bitcoind.spawn.exec.should.equal(expectedExecPath);
|
||||||
});
|
});
|
||||||
it('will include additional services', function() {
|
it('will include additional services', function() {
|
||||||
var config = JSON.stringify({
|
var config = JSON.stringify({
|
||||||
datadir: process.env.HOME + '/.bitcore/data',
|
|
||||||
network: 'livenet',
|
network: 'livenet',
|
||||||
port: 3001,
|
port: 3001,
|
||||||
services: [
|
services: [
|
||||||
'bitcoind',
|
'bitcoind',
|
||||||
'db',
|
|
||||||
'address',
|
|
||||||
'web',
|
'web',
|
||||||
'insight-api',
|
'insight-api',
|
||||||
'insight-ui'
|
'insight-ui'
|
||||||
]
|
],
|
||||||
|
servicesConfig: {
|
||||||
|
bitcoind: {
|
||||||
|
spawn: {
|
||||||
|
datadir: process.env.HOME + '/.bitcore/data',
|
||||||
|
exec: expectedExecPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}, null, 2);
|
}, null, 2);
|
||||||
var defaultConfig = proxyquire('../../lib/scaffold/default-config', {
|
var defaultConfig = proxyquire('../../lib/scaffold/default-config', {
|
||||||
fs: {
|
fs: {
|
||||||
|
@ -75,16 +90,17 @@ describe('#defaultConfig', function() {
|
||||||
additionalServices: ['insight-api', 'insight-ui']
|
additionalServices: ['insight-api', 'insight-ui']
|
||||||
});
|
});
|
||||||
info.path.should.equal(home + '/.bitcore');
|
info.path.should.equal(home + '/.bitcore');
|
||||||
info.config.datadir.should.equal(home + '/.bitcore/data');
|
|
||||||
info.config.network.should.equal('livenet');
|
info.config.network.should.equal('livenet');
|
||||||
info.config.port.should.equal(3001);
|
info.config.port.should.equal(3001);
|
||||||
info.config.services.should.deep.equal([
|
info.config.services.should.deep.equal([
|
||||||
'bitcoind',
|
'bitcoind',
|
||||||
'db',
|
|
||||||
'address',
|
|
||||||
'web',
|
'web',
|
||||||
'insight-api',
|
'insight-api',
|
||||||
'insight-ui'
|
'insight-ui'
|
||||||
]);
|
]);
|
||||||
|
var bitcoind = info.config.servicesConfig.bitcoind;
|
||||||
|
should.exist(bitcoind);
|
||||||
|
bitcoind.spawn.datadir.should.equal(home + '/.bitcore/data');
|
||||||
|
bitcoind.spawn.exec.should.equal(expectedExecPath);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
var should = require('chai').should();
|
var should = require('chai').should();
|
||||||
var sinon = require('sinon');
|
var sinon = require('sinon');
|
||||||
var proxyquire = require('proxyquire');
|
var proxyquire = require('proxyquire');
|
||||||
var AddressService = require('../../lib/services/address');
|
var BitcoinService = require('../../lib/services/bitcoind');
|
||||||
|
|
||||||
describe('#start', function() {
|
describe('#start', function() {
|
||||||
|
|
||||||
|
@ -13,8 +13,8 @@ describe('#start', function() {
|
||||||
var node;
|
var node;
|
||||||
var TestNode = function(options) {
|
var TestNode = function(options) {
|
||||||
options.services[0].should.deep.equal({
|
options.services[0].should.deep.equal({
|
||||||
name: 'address',
|
name: 'bitcoind',
|
||||||
module: AddressService,
|
module: BitcoinService,
|
||||||
config: {}
|
config: {}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -32,7 +32,7 @@ describe('#start', function() {
|
||||||
path: __dirname,
|
path: __dirname,
|
||||||
config: {
|
config: {
|
||||||
services: [
|
services: [
|
||||||
'address'
|
'bitcoind'
|
||||||
],
|
],
|
||||||
datadir: './data'
|
datadir: './data'
|
||||||
}
|
}
|
||||||
|
@ -67,8 +67,8 @@ describe('#start', function() {
|
||||||
var node;
|
var node;
|
||||||
var TestNode = function(options) {
|
var TestNode = function(options) {
|
||||||
options.services[0].should.deep.equal({
|
options.services[0].should.deep.equal({
|
||||||
name: 'address',
|
name: 'bitcoind',
|
||||||
module: AddressService,
|
module: BitcoinService,
|
||||||
config: {
|
config: {
|
||||||
param: 'test'
|
param: 'test'
|
||||||
}
|
}
|
||||||
|
@ -88,10 +88,10 @@ describe('#start', function() {
|
||||||
path: __dirname,
|
path: __dirname,
|
||||||
config: {
|
config: {
|
||||||
services: [
|
services: [
|
||||||
'address'
|
'bitcoind'
|
||||||
],
|
],
|
||||||
servicesConfig: {
|
servicesConfig: {
|
||||||
'address': {
|
'bitcoind': {
|
||||||
param: 'test'
|
param: 'test'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -212,110 +212,6 @@ describe('#start', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('#spawnChildProcess', function() {
|
|
||||||
|
|
||||||
it('should build the appropriate arguments to spawn a child process', function() {
|
|
||||||
var child = {
|
|
||||||
unref: function() {}
|
|
||||||
};
|
|
||||||
var _process = {
|
|
||||||
exit: function() {},
|
|
||||||
env: {
|
|
||||||
__bitcore_node: false
|
|
||||||
},
|
|
||||||
argv: [
|
|
||||||
'node',
|
|
||||||
'bitcore-node'
|
|
||||||
],
|
|
||||||
cwd: function(){return ''},
|
|
||||||
pid: 999,
|
|
||||||
execPath: '/tmp'
|
|
||||||
};
|
|
||||||
var fd = {};
|
|
||||||
var spawn = sinon.stub().returns(child);
|
|
||||||
var openSync = sinon.stub().returns(fd);
|
|
||||||
var spawnChildProcess = proxyquire('../../lib/scaffold/start', {
|
|
||||||
fs: {
|
|
||||||
openSync: openSync
|
|
||||||
},
|
|
||||||
child_process: {
|
|
||||||
spawn: spawn
|
|
||||||
}
|
|
||||||
}).spawnChildProcess;
|
|
||||||
|
|
||||||
spawnChildProcess('/tmp', _process);
|
|
||||||
|
|
||||||
spawn.callCount.should.equal(1);
|
|
||||||
spawn.args[0][0].should.equal(_process.execPath);
|
|
||||||
var expected = [].concat(_process.argv);
|
|
||||||
expected.shift();
|
|
||||||
spawn.args[0][1].should.deep.equal(expected);
|
|
||||||
var cp_opt = {
|
|
||||||
stdio: ['ignore', fd, fd],
|
|
||||||
env: _process.env,
|
|
||||||
cwd: '',
|
|
||||||
detached: true
|
|
||||||
};
|
|
||||||
spawn.args[0][2].should.deep.equal(cp_opt);
|
|
||||||
openSync.callCount.should.equal(1);
|
|
||||||
openSync.args[0][0].should.equal('/tmp/bitcore-node.log');
|
|
||||||
openSync.args[0][1].should.equal('a+');
|
|
||||||
});
|
|
||||||
it('should not spawn a new child process if there is already a daemon running', function() {
|
|
||||||
var _process = {
|
|
||||||
exit: function() {},
|
|
||||||
env: {
|
|
||||||
__bitcore_node: true
|
|
||||||
},
|
|
||||||
argv: [
|
|
||||||
'node',
|
|
||||||
'bitcore-node'
|
|
||||||
],
|
|
||||||
cwd: 'cwd',
|
|
||||||
pid: 999,
|
|
||||||
execPath: '/tmp'
|
|
||||||
};
|
|
||||||
var spawnChildProcess = proxyquire('../../lib/scaffold/start', {}).spawnChildProcess;
|
|
||||||
spawnChildProcess('/tmp', _process).should.equal(999);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe('daemon', function() {
|
|
||||||
var sandbox;
|
|
||||||
var spawn;
|
|
||||||
var setup;
|
|
||||||
var registerSync;
|
|
||||||
var registerExit;
|
|
||||||
var start = require('../../lib/scaffold/start');
|
|
||||||
var options = {
|
|
||||||
config: {
|
|
||||||
datadir: '/tmp',
|
|
||||||
daemon: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
beforeEach(function() {
|
|
||||||
sandbox = sinon.sandbox.create();
|
|
||||||
spawn = sandbox.stub(start, 'spawnChildProcess', function() {});
|
|
||||||
setup = sandbox.stub(start, 'setupServices', function() {});
|
|
||||||
registerSync = sandbox.stub(start, 'registerSyncHandlers', function() {});
|
|
||||||
registerExit = sandbox.stub(start, 'registerExitHandlers', function() {});
|
|
||||||
});
|
|
||||||
afterEach(function() {
|
|
||||||
sandbox.restore();
|
|
||||||
});
|
|
||||||
it('call spawnChildProcess if there is a config option to do so', function() {
|
|
||||||
start(options);
|
|
||||||
registerSync.callCount.should.equal(1);
|
|
||||||
registerExit.callCount.should.equal(1);
|
|
||||||
spawn.callCount.should.equal(1);
|
|
||||||
});
|
|
||||||
it('not call spawnChildProcess if there is not an option to do so', function() {
|
|
||||||
options.config.daemon = false;
|
|
||||||
start(options);
|
|
||||||
registerSync.callCount.should.equal(1);
|
|
||||||
registerExit.callCount.should.equal(1);
|
|
||||||
spawn.callCount.should.equal(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe('#registerExitHandlers', function() {
|
describe('#registerExitHandlers', function() {
|
||||||
var stub;
|
var stub;
|
||||||
var registerExitHandlers = require('../../lib/scaffold/start').registerExitHandlers;
|
var registerExitHandlers = require('../../lib/scaffold/start').registerExitHandlers;
|
||||||
|
|
|
@ -1,103 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var chai = require('chai');
|
|
||||||
var should = chai.should();
|
|
||||||
var sinon = require('sinon');
|
|
||||||
var bitcorenode = require('../../../');
|
|
||||||
var bitcore = require('bitcore-lib');
|
|
||||||
var Address = bitcore.Address;
|
|
||||||
var Script = bitcore.Script;
|
|
||||||
var AddressService = bitcorenode.services.Address;
|
|
||||||
var Networks = bitcore.Networks;
|
|
||||||
var encoding = require('../../../lib/services/address/encoding');
|
|
||||||
|
|
||||||
var mockdb = {
|
|
||||||
};
|
|
||||||
|
|
||||||
var mocknode = {
|
|
||||||
network: Networks.testnet,
|
|
||||||
datadir: 'testdir',
|
|
||||||
db: mockdb,
|
|
||||||
services: {
|
|
||||||
bitcoind: {
|
|
||||||
on: sinon.stub()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('Address Service Encoding', function() {
|
|
||||||
|
|
||||||
describe('#encodeSpentIndexSyncKey', function() {
|
|
||||||
it('will encode to 36 bytes (string)', function() {
|
|
||||||
var txidBuffer = new Buffer('3b6bc2939d1a70ce04bc4f619ee32608fbff5e565c1f9b02e4eaa97959c59ae7', 'hex');
|
|
||||||
var key = encoding.encodeSpentIndexSyncKey(txidBuffer, 12);
|
|
||||||
key.length.should.equal(36);
|
|
||||||
});
|
|
||||||
it('will be able to decode encoded value', function() {
|
|
||||||
var txid = '3b6bc2939d1a70ce04bc4f619ee32608fbff5e565c1f9b02e4eaa97959c59ae7';
|
|
||||||
var txidBuffer = new Buffer(txid, 'hex');
|
|
||||||
var key = encoding.encodeSpentIndexSyncKey(txidBuffer, 12);
|
|
||||||
var keyBuffer = new Buffer(key, 'binary');
|
|
||||||
keyBuffer.slice(0, 32).toString('hex').should.equal(txid);
|
|
||||||
var outputIndex = keyBuffer.readUInt32BE(32);
|
|
||||||
outputIndex.should.equal(12);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#_encodeInputKeyMap/#_decodeInputKeyMap roundtrip', function() {
|
|
||||||
var encoded;
|
|
||||||
var outputTxIdBuffer = new Buffer('3b6bc2939d1a70ce04bc4f619ee32608fbff5e565c1f9b02e4eaa97959c59ae7', 'hex');
|
|
||||||
it('encode key', function() {
|
|
||||||
encoded = encoding.encodeInputKeyMap(outputTxIdBuffer, 13);
|
|
||||||
});
|
|
||||||
it('decode key', function() {
|
|
||||||
var key = encoding.decodeInputKeyMap(encoded);
|
|
||||||
key.outputTxId.toString('hex').should.equal(outputTxIdBuffer.toString('hex'));
|
|
||||||
key.outputIndex.should.equal(13);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#_encodeInputValueMap/#_decodeInputValueMap roundtrip', function() {
|
|
||||||
var encoded;
|
|
||||||
var inputTxIdBuffer = new Buffer('3b6bc2939d1a70ce04bc4f619ee32608fbff5e565c1f9b02e4eaa97959c59ae7', 'hex');
|
|
||||||
it('encode key', function() {
|
|
||||||
encoded = encoding.encodeInputValueMap(inputTxIdBuffer, 7);
|
|
||||||
});
|
|
||||||
it('decode key', function() {
|
|
||||||
var key = encoding.decodeInputValueMap(encoded);
|
|
||||||
key.inputTxId.toString('hex').should.equal(inputTxIdBuffer.toString('hex'));
|
|
||||||
key.inputIndex.should.equal(7);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
describe('#extractAddressInfoFromScript', function() {
|
|
||||||
it('pay-to-publickey', function() {
|
|
||||||
var pubkey = new bitcore.PublicKey('022df8750480ad5b26950b25c7ba79d3e37d75f640f8e5d9bcd5b150a0f85014da');
|
|
||||||
var script = Script.buildPublicKeyOut(pubkey);
|
|
||||||
var info = encoding.extractAddressInfoFromScript(script, Networks.livenet);
|
|
||||||
info.addressType.should.equal(Address.PayToPublicKeyHash);
|
|
||||||
info.hashBuffer.toString('hex').should.equal('9674af7395592ec5d91573aa8d6557de55f60147');
|
|
||||||
});
|
|
||||||
it('pay-to-publickeyhash', function() {
|
|
||||||
var script = Script('OP_DUP OP_HASH160 20 0x0000000000000000000000000000000000000000 OP_EQUALVERIFY OP_CHECKSIG');
|
|
||||||
var info = encoding.extractAddressInfoFromScript(script, Networks.livenet);
|
|
||||||
info.addressType.should.equal(Address.PayToPublicKeyHash);
|
|
||||||
info.hashBuffer.toString('hex').should.equal('0000000000000000000000000000000000000000');
|
|
||||||
});
|
|
||||||
it('pay-to-scripthash', function() {
|
|
||||||
var script = Script('OP_HASH160 20 0x0000000000000000000000000000000000000000 OP_EQUAL');
|
|
||||||
var info = encoding.extractAddressInfoFromScript(script, Networks.livenet);
|
|
||||||
info.addressType.should.equal(Address.PayToScriptHash);
|
|
||||||
info.hashBuffer.toString('hex').should.equal('0000000000000000000000000000000000000000');
|
|
||||||
});
|
|
||||||
it('non-address script type', function() {
|
|
||||||
var buf = new Buffer(40);
|
|
||||||
buf.fill(0);
|
|
||||||
var script = Script('OP_RETURN 40 0x' + buf.toString('hex'));
|
|
||||||
var info = encoding.extractAddressInfoFromScript(script, Networks.livenet);
|
|
||||||
info.should.equal(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
|
@ -1,544 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var should = require('chai').should();
|
|
||||||
var sinon = require('sinon');
|
|
||||||
var bitcore = require('bitcore-lib');
|
|
||||||
var Transaction = require('../../../lib/transaction');
|
|
||||||
var AddressHistory = require('../../../lib/services/address/history');
|
|
||||||
|
|
||||||
describe('Address Service History', function() {
|
|
||||||
|
|
||||||
var address = '12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX';
|
|
||||||
|
|
||||||
describe('@constructor', function() {
|
|
||||||
it('will construct a new instance', function() {
|
|
||||||
var node = {};
|
|
||||||
var options = {};
|
|
||||||
var addresses = [address];
|
|
||||||
var history = new AddressHistory({
|
|
||||||
node: node,
|
|
||||||
options: options,
|
|
||||||
addresses: addresses
|
|
||||||
});
|
|
||||||
history.should.be.instanceof(AddressHistory);
|
|
||||||
history.node.should.equal(node);
|
|
||||||
history.options.should.equal(options);
|
|
||||||
history.addresses.should.equal(addresses);
|
|
||||||
history.detailedArray.should.deep.equal([]);
|
|
||||||
});
|
|
||||||
it('will set addresses an array if only sent a string', function() {
|
|
||||||
var history = new AddressHistory({
|
|
||||||
node: {},
|
|
||||||
options: {},
|
|
||||||
addresses: address
|
|
||||||
});
|
|
||||||
history.addresses.should.deep.equal([address]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#get', function() {
|
|
||||||
it('will give an error if length of addresses is too long', function(done) {
|
|
||||||
var node = {};
|
|
||||||
var options = {};
|
|
||||||
var addresses = [];
|
|
||||||
for (var i = 0; i < 101; i++) {
|
|
||||||
addresses.push(address);
|
|
||||||
}
|
|
||||||
var history = new AddressHistory({
|
|
||||||
node: node,
|
|
||||||
options: options,
|
|
||||||
addresses: addresses
|
|
||||||
});
|
|
||||||
history.maxAddressesQuery = 100;
|
|
||||||
history.get(function(err) {
|
|
||||||
should.exist(err);
|
|
||||||
err.message.match(/Maximum/);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it('give error from getAddressSummary with one address', function(done) {
|
|
||||||
var node = {
|
|
||||||
services: {
|
|
||||||
address: {
|
|
||||||
getAddressSummary: sinon.stub().callsArgWith(2, new Error('test'))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var options = {};
|
|
||||||
var addresses = [address];
|
|
||||||
var history = new AddressHistory({
|
|
||||||
node: node,
|
|
||||||
options: options,
|
|
||||||
addresses: addresses
|
|
||||||
});
|
|
||||||
history.get(function(err) {
|
|
||||||
should.exist(err);
|
|
||||||
err.message.should.equal('test');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it('give error from getAddressSummary with multiple addresses', function(done) {
|
|
||||||
var node = {
|
|
||||||
services: {
|
|
||||||
address: {
|
|
||||||
getAddressSummary: sinon.stub().callsArgWith(2, new Error('test2'))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var options = {};
|
|
||||||
var addresses = [address, address];
|
|
||||||
var history = new AddressHistory({
|
|
||||||
node: node,
|
|
||||||
options: options,
|
|
||||||
addresses: addresses
|
|
||||||
});
|
|
||||||
history.get(function(err) {
|
|
||||||
should.exist(err);
|
|
||||||
err.message.should.equal('test2');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it('will query get address summary directly with one address', function(done) {
|
|
||||||
var txids = [];
|
|
||||||
var summary = {
|
|
||||||
txids: txids
|
|
||||||
};
|
|
||||||
var node = {
|
|
||||||
services: {
|
|
||||||
address: {
|
|
||||||
getAddressSummary: sinon.stub().callsArgWith(2, null, summary)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var options = {};
|
|
||||||
var addresses = [address];
|
|
||||||
var history = new AddressHistory({
|
|
||||||
node: node,
|
|
||||||
options: options,
|
|
||||||
addresses: addresses
|
|
||||||
});
|
|
||||||
history._mergeAndSortTxids = sinon.stub();
|
|
||||||
history._paginateWithDetails = sinon.stub().callsArg(1);
|
|
||||||
history.get(function() {
|
|
||||||
history.node.services.address.getAddressSummary.callCount.should.equal(1);
|
|
||||||
history.node.services.address.getAddressSummary.args[0][0].should.equal(address);
|
|
||||||
history.node.services.address.getAddressSummary.args[0][1].should.deep.equal({
|
|
||||||
noBalance: true
|
|
||||||
});
|
|
||||||
history._paginateWithDetails.callCount.should.equal(1);
|
|
||||||
history._paginateWithDetails.args[0][0].should.equal(txids);
|
|
||||||
history._mergeAndSortTxids.callCount.should.equal(0);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it('will merge multiple summaries with multiple addresses', function(done) {
|
|
||||||
var txids = [];
|
|
||||||
var summary = {
|
|
||||||
txids: txids
|
|
||||||
};
|
|
||||||
var node = {
|
|
||||||
services: {
|
|
||||||
address: {
|
|
||||||
getAddressSummary: sinon.stub().callsArgWith(2, null, summary)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var options = {};
|
|
||||||
var addresses = [address, address];
|
|
||||||
var history = new AddressHistory({
|
|
||||||
node: node,
|
|
||||||
options: options,
|
|
||||||
addresses: addresses
|
|
||||||
});
|
|
||||||
history._mergeAndSortTxids = sinon.stub().returns(txids);
|
|
||||||
history._paginateWithDetails = sinon.stub().callsArg(1);
|
|
||||||
history.get(function() {
|
|
||||||
history.node.services.address.getAddressSummary.callCount.should.equal(2);
|
|
||||||
history.node.services.address.getAddressSummary.args[0][0].should.equal(address);
|
|
||||||
history.node.services.address.getAddressSummary.args[0][1].should.deep.equal({
|
|
||||||
fullTxList: true,
|
|
||||||
noBalance: true
|
|
||||||
});
|
|
||||||
history._paginateWithDetails.callCount.should.equal(1);
|
|
||||||
history._paginateWithDetails.args[0][0].should.equal(txids);
|
|
||||||
history._mergeAndSortTxids.callCount.should.equal(1);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#_paginateWithDetails', function() {
|
|
||||||
it('slice txids based on "from" and "to" (3 to 30)', function() {
|
|
||||||
var node = {};
|
|
||||||
var options = {
|
|
||||||
from: 3,
|
|
||||||
to: 30
|
|
||||||
};
|
|
||||||
var addresses = [address];
|
|
||||||
var history = new AddressHistory({
|
|
||||||
node: node,
|
|
||||||
options: options,
|
|
||||||
addresses: addresses
|
|
||||||
});
|
|
||||||
var txids = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
||||||
sinon.stub(history, 'getDetailedInfo', function(txid, next) {
|
|
||||||
this.detailedArray.push(txid);
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
history._paginateWithDetails(txids, function(err, result) {
|
|
||||||
result.totalCount.should.equal(11);
|
|
||||||
result.items.should.deep.equal([7, 6, 5, 4, 3, 2, 1, 0]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it('slice txids based on "from" and "to" (0 to 3)', function() {
|
|
||||||
var node = {};
|
|
||||||
var options = {
|
|
||||||
from: 0,
|
|
||||||
to: 3
|
|
||||||
};
|
|
||||||
var addresses = [address];
|
|
||||||
var history = new AddressHistory({
|
|
||||||
node: node,
|
|
||||||
options: options,
|
|
||||||
addresses: addresses
|
|
||||||
});
|
|
||||||
var txids = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
||||||
sinon.stub(history, 'getDetailedInfo', function(txid, next) {
|
|
||||||
this.detailedArray.push(txid);
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
history._paginateWithDetails(txids, function(err, result) {
|
|
||||||
result.totalCount.should.equal(11);
|
|
||||||
result.items.should.deep.equal([10, 9, 8]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it('will given an error if the full details is too long', function() {
|
|
||||||
var node = {};
|
|
||||||
var options = {
|
|
||||||
from: 0,
|
|
||||||
to: 3
|
|
||||||
};
|
|
||||||
var addresses = [address];
|
|
||||||
var history = new AddressHistory({
|
|
||||||
node: node,
|
|
||||||
options: options,
|
|
||||||
addresses: addresses
|
|
||||||
});
|
|
||||||
var txids = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
||||||
sinon.stub(history, 'getDetailedInfo', function(txid, next) {
|
|
||||||
this.detailedArray.push(txid);
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
history.maxHistoryQueryLength = 1;
|
|
||||||
history._paginateWithDetails(txids, function(err) {
|
|
||||||
should.exist(err);
|
|
||||||
err.message.match(/Maximum/);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it('will give full result without pagination options', function() {
|
|
||||||
var node = {};
|
|
||||||
var options = {};
|
|
||||||
var addresses = [address];
|
|
||||||
var history = new AddressHistory({
|
|
||||||
node: node,
|
|
||||||
options: options,
|
|
||||||
addresses: addresses
|
|
||||||
});
|
|
||||||
var txids = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
||||||
sinon.stub(history, 'getDetailedInfo', function(txid, next) {
|
|
||||||
this.detailedArray.push(txid);
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
history._paginateWithDetails(txids, function(err, result) {
|
|
||||||
result.totalCount.should.equal(11);
|
|
||||||
result.items.should.deep.equal([10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#_mergeAndSortTxids', function() {
|
|
||||||
it('will merge and sort multiple summaries', function() {
|
|
||||||
var summaries = [
|
|
||||||
{
|
|
||||||
totalReceived: 10000000,
|
|
||||||
totalSpent: 0,
|
|
||||||
balance: 10000000,
|
|
||||||
appearances: 2,
|
|
||||||
unconfirmedBalance: 20000000,
|
|
||||||
unconfirmedAppearances: 2,
|
|
||||||
appearanceIds: {
|
|
||||||
'56fafeb01961831b926558d040c246b97709fd700adcaa916541270583e8e579': 154,
|
|
||||||
'e9dcf22807db77ac0276b03cc2d3a8b03c4837db8ac6650501ef45af1c807cce': 120
|
|
||||||
},
|
|
||||||
unconfirmedAppearanceIds: {
|
|
||||||
'ec94d845c603f292a93b7c829811ac624b76e52b351617ca5a758e9d61a11681': 1452898347406,
|
|
||||||
'ed11a08e3102f9610bda44c80c46781d97936a4290691d87244b1b345b39a693': 1452898331964
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
totalReceived: 59990000,
|
|
||||||
totalSpent: 0,
|
|
||||||
balance: 49990000,
|
|
||||||
appearances: 3,
|
|
||||||
unconfirmedBalance: 1000000,
|
|
||||||
unconfirmedAppearances: 3,
|
|
||||||
appearanceIds: {
|
|
||||||
'bc992ad772eb02864db07ef248d31fb3c6826d25f1153ebf8c79df9b7f70fcf2': 156,
|
|
||||||
'f3c1ba3ef86a0420d6102e40e2cfc8682632ab95d09d86a27f5d466b9fa9da47': 152,
|
|
||||||
'f637384e9f81f18767ea50e00bce58fc9848b6588a1130529eebba22a410155f': 151
|
|
||||||
},
|
|
||||||
unconfirmedAppearanceIds: {
|
|
||||||
'f71bccef3a8f5609c7f016154922adbfe0194a96fb17a798c24077c18d0a9345': 1452897902377,
|
|
||||||
'edc080f2084eed362aa488ccc873a24c378dc0979aa29b05767517b70569414a': 1452897971363,
|
|
||||||
'f35e7e2a2334e845946f3eaca76890d9a68f4393ccc9fe37a0c2fb035f66d2e9': 1452897923107
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
var node = {};
|
|
||||||
var options = {};
|
|
||||||
var addresses = [address];
|
|
||||||
var history = new AddressHistory({
|
|
||||||
node: node,
|
|
||||||
options: options,
|
|
||||||
addresses: addresses
|
|
||||||
});
|
|
||||||
var txids = history._mergeAndSortTxids(summaries);
|
|
||||||
txids.should.deep.equal([
|
|
||||||
'e9dcf22807db77ac0276b03cc2d3a8b03c4837db8ac6650501ef45af1c807cce',
|
|
||||||
'f637384e9f81f18767ea50e00bce58fc9848b6588a1130529eebba22a410155f',
|
|
||||||
'f3c1ba3ef86a0420d6102e40e2cfc8682632ab95d09d86a27f5d466b9fa9da47',
|
|
||||||
'56fafeb01961831b926558d040c246b97709fd700adcaa916541270583e8e579',
|
|
||||||
'bc992ad772eb02864db07ef248d31fb3c6826d25f1153ebf8c79df9b7f70fcf2',
|
|
||||||
'f71bccef3a8f5609c7f016154922adbfe0194a96fb17a798c24077c18d0a9345',
|
|
||||||
'f35e7e2a2334e845946f3eaca76890d9a68f4393ccc9fe37a0c2fb035f66d2e9',
|
|
||||||
'edc080f2084eed362aa488ccc873a24c378dc0979aa29b05767517b70569414a',
|
|
||||||
'ed11a08e3102f9610bda44c80c46781d97936a4290691d87244b1b345b39a693',
|
|
||||||
'ec94d845c603f292a93b7c829811ac624b76e52b351617ca5a758e9d61a11681'
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#getDetailedInfo', function() {
|
|
||||||
it('will add additional information to existing this.transactions', function(done) {
|
|
||||||
var txid = '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0';
|
|
||||||
var tx = {
|
|
||||||
populateInputs: sinon.stub().callsArg(2),
|
|
||||||
__height: 20,
|
|
||||||
__timestamp: 1453134151,
|
|
||||||
isCoinbase: sinon.stub().returns(false),
|
|
||||||
getFee: sinon.stub().returns(1000)
|
|
||||||
};
|
|
||||||
var history = new AddressHistory({
|
|
||||||
node: {
|
|
||||||
services: {
|
|
||||||
db: {
|
|
||||||
getTransactionWithBlockInfo: sinon.stub().callsArgWith(2, null, tx),
|
|
||||||
tip: {
|
|
||||||
__height: 300
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
options: {},
|
|
||||||
addresses: []
|
|
||||||
});
|
|
||||||
history.getAddressDetailsForTransaction = sinon.stub().returns({
|
|
||||||
addresses: {},
|
|
||||||
satoshis: 1000,
|
|
||||||
});
|
|
||||||
history.getDetailedInfo(txid, function(err) {
|
|
||||||
if (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
history.node.services.db.getTransactionWithBlockInfo.callCount.should.equal(1);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it('will handle error from getTransactionFromBlock', function(done) {
|
|
||||||
var txid = '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0';
|
|
||||||
var history = new AddressHistory({
|
|
||||||
node: {
|
|
||||||
services: {
|
|
||||||
db: {
|
|
||||||
getTransactionWithBlockInfo: sinon.stub().callsArgWith(2, new Error('test')),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
options: {},
|
|
||||||
addresses: []
|
|
||||||
});
|
|
||||||
history.getDetailedInfo(txid, function(err) {
|
|
||||||
err.message.should.equal('test');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it('will handle error from populateInputs', function(done) {
|
|
||||||
var txid = '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0';
|
|
||||||
var history = new AddressHistory({
|
|
||||||
node: {
|
|
||||||
services: {
|
|
||||||
db: {
|
|
||||||
getTransactionWithBlockInfo: sinon.stub().callsArgWith(2, null, {
|
|
||||||
populateInputs: sinon.stub().callsArgWith(2, new Error('test'))
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
options: {},
|
|
||||||
addresses: []
|
|
||||||
});
|
|
||||||
history.getDetailedInfo(txid, function(err) {
|
|
||||||
err.message.should.equal('test');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it('will set this.transactions with correct information', function(done) {
|
|
||||||
// block #314159
|
|
||||||
// txid 30169e8bf78bc27c4014a7aba3862c60e2e3cce19e52f1909c8255e4b7b3174e
|
|
||||||
// outputIndex 1
|
|
||||||
var txAddress = '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo';
|
|
||||||
var txString = '0100000001a08ee59fcd5d86fa170abb6d925d62d5c5c476359681b70877c04f270c4ef246000000008a47304402203fb9b476bb0c37c9b9ed5784ebd67ae589492be11d4ae1612be29887e3e4ce750220741ef83781d1b3a5df8c66fa1957ad0398c733005310d7d9b1d8c2310ef4f74c0141046516ad02713e51ecf23ac9378f1069f9ae98e7de2f2edbf46b7836096e5dce95a05455cc87eaa1db64f39b0c63c0a23a3b8df1453dbd1c8317f967c65223cdf8ffffffff02b0a75fac000000001976a91484b45b9bf3add8f7a0f3daad305fdaf6b73441ea88ac20badc02000000001976a914809dc14496f99b6deb722cf46d89d22f4beb8efd88ac00000000';
|
|
||||||
var previousTxString = '010000000155532fad2869bb951b0bd646a546887f6ee668c4c0ee13bf3f1c4bce6d6e3ed9000000008c4930460221008540795f4ef79b1d2549c400c61155ca5abbf3089c84ad280e1ba6db2a31abce022100d7d162175483d51174d40bba722e721542c924202a0c2970b07e680b51f3a0670141046516ad02713e51ecf23ac9378f1069f9ae98e7de2f2edbf46b7836096e5dce95a05455cc87eaa1db64f39b0c63c0a23a3b8df1453dbd1c8317f967c65223cdf8ffffffff02f0af3caf000000001976a91484b45b9bf3add8f7a0f3daad305fdaf6b73441ea88ac80969800000000001976a91421277e65777760d1f3c7c982ba14ed8f934f005888ac00000000';
|
|
||||||
var transaction = new Transaction();
|
|
||||||
var previousTransaction = new Transaction();
|
|
||||||
previousTransaction.fromString(previousTxString);
|
|
||||||
var previousTransactionTxid = '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0';
|
|
||||||
transaction.fromString(txString);
|
|
||||||
var txid = transaction.hash;
|
|
||||||
transaction.__blockHash = '00000000000000001bb82a7f5973618cfd3185ba1ded04dd852a653f92a27c45';
|
|
||||||
transaction.__height = 314159;
|
|
||||||
transaction.__timestamp = 1407292005;
|
|
||||||
var history = new AddressHistory({
|
|
||||||
node: {
|
|
||||||
services: {
|
|
||||||
db: {
|
|
||||||
tip: {
|
|
||||||
__height: 314159
|
|
||||||
},
|
|
||||||
getTransactionWithBlockInfo: sinon.stub().callsArgWith(2, null, transaction),
|
|
||||||
getTransaction: function(prevTxid, queryMempool, callback) {
|
|
||||||
prevTxid.should.equal(previousTransactionTxid);
|
|
||||||
setImmediate(function() {
|
|
||||||
callback(null, previousTransaction);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
options: {},
|
|
||||||
addresses: [txAddress]
|
|
||||||
});
|
|
||||||
var transactionInfo = {
|
|
||||||
addresses: {},
|
|
||||||
txid: txid,
|
|
||||||
timestamp: 1407292005,
|
|
||||||
satoshis: 48020000,
|
|
||||||
address: txAddress
|
|
||||||
};
|
|
||||||
transactionInfo.addresses[txAddress] = {};
|
|
||||||
transactionInfo.addresses[txAddress].outputIndexes = [1];
|
|
||||||
transactionInfo.addresses[txAddress].inputIndexes = [];
|
|
||||||
history.getDetailedInfo(txid, function(err) {
|
|
||||||
if (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
var info = history.detailedArray[0];
|
|
||||||
info.addresses[txAddress].should.deep.equal({
|
|
||||||
outputIndexes: [1],
|
|
||||||
inputIndexes: []
|
|
||||||
});
|
|
||||||
info.satoshis.should.equal(48020000);
|
|
||||||
info.height.should.equal(314159);
|
|
||||||
info.confirmations.should.equal(1);
|
|
||||||
info.timestamp.should.equal(1407292005);
|
|
||||||
info.fees.should.equal(20000);
|
|
||||||
info.tx.should.equal(transaction);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('#getAddressDetailsForTransaction', function() {
|
|
||||||
it('will calculate details for the transaction', function(done) {
|
|
||||||
/* jshint sub:true */
|
|
||||||
var tx = bitcore.Transaction({
|
|
||||||
'hash': 'b12b3ae8489c5a566b629a3c62ce4c51c3870af550fb5dc77d715b669a91343c',
|
|
||||||
'version': 1,
|
|
||||||
'inputs': [
|
|
||||||
{
|
|
||||||
'prevTxId': 'a2b7ea824a92f4a4944686e67ec1001bc8785348b8c111c226f782084077b543',
|
|
||||||
'outputIndex': 0,
|
|
||||||
'sequenceNumber': 4294967295,
|
|
||||||
'script': '47304402201b81c933297241960a57ae1b2952863b965ac8c9ec7466ff0b715712d27548d50220576e115b63864f003889443525f47c7cf0bc1e2b5108398da085b221f267ba2301210229766f1afa25ca499a51f8e01c292b0255a21a41bb6685564a1607a811ffe924',
|
|
||||||
'scriptString': '71 0x304402201b81c933297241960a57ae1b2952863b965ac8c9ec7466ff0b715712d27548d50220576e115b63864f003889443525f47c7cf0bc1e2b5108398da085b221f267ba2301 33 0x0229766f1afa25ca499a51f8e01c292b0255a21a41bb6685564a1607a811ffe924',
|
|
||||||
'output': {
|
|
||||||
'satoshis': 1000000000,
|
|
||||||
'script': '76a9140b2f0a0c31bfe0406b0ccc1381fdbe311946dadc88ac'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'outputs': [
|
|
||||||
{
|
|
||||||
'satoshis': 100000000,
|
|
||||||
'script': '76a9140b2f0a0c31bfe0406b0ccc1381fdbe311946dadc88ac'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'satoshis': 200000000,
|
|
||||||
'script': '76a9140b2f0a0c31bfe0406b0ccc1381fdbe311946dadc88ac'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'satoshis': 50000000,
|
|
||||||
'script': '76a9140b2f0a0c31bfe0406b0ccc1381fdbe311946dadc88ac'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'satoshis': 300000000,
|
|
||||||
'script': '76a9140b2f0a0c31bfe0406b0ccc1381fdbe311946dadc88ac'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'satoshis': 349990000,
|
|
||||||
'script': '76a9140b2f0a0c31bfe0406b0ccc1381fdbe311946dadc88ac'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'nLockTime': 0
|
|
||||||
});
|
|
||||||
var history = new AddressHistory({
|
|
||||||
node: {
|
|
||||||
network: bitcore.Networks.testnet
|
|
||||||
},
|
|
||||||
options: {},
|
|
||||||
addresses: ['mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW']
|
|
||||||
});
|
|
||||||
var details = history.getAddressDetailsForTransaction(tx);
|
|
||||||
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('#getConfirmationsDetail', function() {
|
|
||||||
it('the correct confirmations when included in the tip', function(done) {
|
|
||||||
var history = new AddressHistory({
|
|
||||||
node: {
|
|
||||||
services: {
|
|
||||||
db: {
|
|
||||||
tip: {
|
|
||||||
__height: 100
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
options: {},
|
|
||||||
addresses: []
|
|
||||||
});
|
|
||||||
var transaction = {
|
|
||||||
__height: 100
|
|
||||||
};
|
|
||||||
history.getConfirmationsDetail(transaction).should.equal(1);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -4,9 +4,44 @@ var should = require('chai').should();
|
||||||
var sinon = require('sinon');
|
var sinon = require('sinon');
|
||||||
var bitcoinlib = require('../');
|
var bitcoinlib = require('../');
|
||||||
var Transaction = bitcoinlib.Transaction;
|
var Transaction = bitcoinlib.Transaction;
|
||||||
var levelup = require('levelup');
|
|
||||||
|
|
||||||
describe('Bitcoin Transaction', function() {
|
describe('Bitcoin Transaction', function() {
|
||||||
|
|
||||||
|
describe('#populateSpentInfo', function() {
|
||||||
|
it('will call db.getSpentInfo with correct arguments', function(done) {
|
||||||
|
var tx = new Transaction();
|
||||||
|
tx.to('1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i', 1000);
|
||||||
|
tx.to('3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou', 2000);
|
||||||
|
var expectedHash = tx.hash;
|
||||||
|
var expectedIndex = 2;
|
||||||
|
var expectedHeight = 300000;
|
||||||
|
var db = {
|
||||||
|
getSpentInfo: sinon.stub().callsArgWith(1, null, {
|
||||||
|
txid: expectedHash,
|
||||||
|
index: expectedIndex,
|
||||||
|
height: expectedHeight
|
||||||
|
})
|
||||||
|
};
|
||||||
|
tx.populateSpentInfo(db, {}, function(err) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
db.getSpentInfo.args[0][0].txid.should.equal(tx.hash);
|
||||||
|
db.getSpentInfo.args[0][0].index.should.equal(0);
|
||||||
|
tx.outputs[0].__spentTxId.should.equal(expectedHash);
|
||||||
|
tx.outputs[0].__spentIndex.should.equal(expectedIndex);
|
||||||
|
tx.outputs[0].__spentHeight.should.equal(expectedHeight);
|
||||||
|
|
||||||
|
db.getSpentInfo.args[1][0].txid.should.equal(tx.hash);
|
||||||
|
db.getSpentInfo.args[1][0].index.should.equal(1);
|
||||||
|
tx.outputs[1].__spentTxId.should.equal(expectedHash);
|
||||||
|
tx.outputs[1].__spentIndex.should.equal(expectedIndex);
|
||||||
|
tx.outputs[1].__spentHeight.should.equal(expectedHeight);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('#populateInputs', function() {
|
describe('#populateInputs', function() {
|
||||||
it('will call _populateInput with transactions', function() {
|
it('will call _populateInput with transactions', function() {
|
||||||
var tx = new Transaction();
|
var tx = new Transaction();
|
||||||
|
@ -22,6 +57,17 @@ describe('Bitcoin Transaction', function() {
|
||||||
tx._populateInput.args[0][2].should.equal(transactions);
|
tx._populateInput.args[0][2].should.equal(transactions);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it('will skip coinbase transactions', function() {
|
||||||
|
var tx = new Transaction();
|
||||||
|
tx.isCoinbase = sinon.stub().returns(true);
|
||||||
|
tx._populateInput = sinon.stub().callsArg(3);
|
||||||
|
tx.inputs = ['input'];
|
||||||
|
var transactions = [];
|
||||||
|
var db = {};
|
||||||
|
tx.populateInputs(db, transactions, function(err) {
|
||||||
|
tx._populateInput.callCount.should.equal(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#_populateInput', function() {
|
describe('#_populateInput', function() {
|
||||||
|
@ -29,6 +75,15 @@ describe('Bitcoin Transaction', function() {
|
||||||
prevTxId: new Buffer('d6cffbb343a6a41eeaa199478c985493843bfe6a59d674a5c188787416cbcda3', 'hex'),
|
prevTxId: new Buffer('d6cffbb343a6a41eeaa199478c985493843bfe6a59d674a5c188787416cbcda3', 'hex'),
|
||||||
outputIndex: 0
|
outputIndex: 0
|
||||||
};
|
};
|
||||||
|
it('should give an error if the input does not have a prevTxId', function(done) {
|
||||||
|
var badInput = {};
|
||||||
|
var tx = new Transaction();
|
||||||
|
tx._populateInput({}, badInput, [], function(err) {
|
||||||
|
should.exist(err);
|
||||||
|
err.message.should.equal('Input is expected to have prevTxId as a buffer');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
it('should give an error if the input does not have a valid prevTxId', function(done) {
|
it('should give an error if the input does not have a valid prevTxId', function(done) {
|
||||||
var badInput = {
|
var badInput = {
|
||||||
prevTxId: 'bad'
|
prevTxId: 'bad'
|
||||||
|
@ -43,7 +98,7 @@ describe('Bitcoin Transaction', function() {
|
||||||
it('if an error happened it should pass it along', function(done) {
|
it('if an error happened it should pass it along', function(done) {
|
||||||
var tx = new Transaction();
|
var tx = new Transaction();
|
||||||
var db = {
|
var db = {
|
||||||
getTransaction: sinon.stub().callsArgWith(2, new Error('error'))
|
getTransaction: sinon.stub().callsArgWith(1, new Error('error'))
|
||||||
};
|
};
|
||||||
tx._populateInput(db, input, [], function(err) {
|
tx._populateInput(db, input, [], function(err) {
|
||||||
should.exist(err);
|
should.exist(err);
|
||||||
|
@ -54,7 +109,7 @@ describe('Bitcoin Transaction', function() {
|
||||||
it('should return an error if the transaction for the input does not exist', function(done) {
|
it('should return an error if the transaction for the input does not exist', function(done) {
|
||||||
var tx = new Transaction();
|
var tx = new Transaction();
|
||||||
var db = {
|
var db = {
|
||||||
getTransaction: sinon.stub().callsArgWith(2, new levelup.errors.NotFoundError())
|
getTransaction: sinon.stub().callsArgWith(1, null, null)
|
||||||
};
|
};
|
||||||
tx._populateInput(db, input, [], function(err) {
|
tx._populateInput(db, input, [], function(err) {
|
||||||
should.exist(err);
|
should.exist(err);
|
||||||
|
@ -65,7 +120,7 @@ describe('Bitcoin Transaction', function() {
|
||||||
it('should look through poolTransactions if database does not have transaction', function(done) {
|
it('should look through poolTransactions if database does not have transaction', function(done) {
|
||||||
var tx = new Transaction();
|
var tx = new Transaction();
|
||||||
var db = {
|
var db = {
|
||||||
getTransaction: sinon.stub().callsArgWith(2, new levelup.errors.NotFoundError())
|
getTransaction: sinon.stub().callsArgWith(1, null, null)
|
||||||
};
|
};
|
||||||
var transactions = [
|
var transactions = [
|
||||||
{
|
{
|
||||||
|
@ -79,12 +134,12 @@ describe('Bitcoin Transaction', function() {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('should not return an error if an error did not occur', function(done) {
|
it('should set the output on the input', function(done) {
|
||||||
var prevTx = new Transaction();
|
var prevTx = new Transaction();
|
||||||
prevTx.outputs = ['output'];
|
prevTx.outputs = ['output'];
|
||||||
var tx = new Transaction();
|
var tx = new Transaction();
|
||||||
var db = {
|
var db = {
|
||||||
getTransaction: sinon.stub().callsArgWith(2, null, prevTx)
|
getTransaction: sinon.stub().callsArgWith(1, null, prevTx)
|
||||||
};
|
};
|
||||||
tx._populateInput(db, input, [], function(err) {
|
tx._populateInput(db, input, [], function(err) {
|
||||||
should.not.exist(err);
|
should.not.exist(err);
|
||||||
|
@ -94,27 +149,4 @@ describe('Bitcoin Transaction', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#_checkSpent', function() {
|
|
||||||
it('should return an error if input was spent', function(done) {
|
|
||||||
var tx = new Transaction();
|
|
||||||
var db = {
|
|
||||||
isSpentDB: sinon.stub().callsArgWith(1, true)
|
|
||||||
};
|
|
||||||
tx._checkSpent(db, [], 'input', function(err) {
|
|
||||||
should.exist(err);
|
|
||||||
err.message.should.equal('Input already spent');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it('should not return an error if input was unspent', function(done) {
|
|
||||||
var tx = new Transaction();
|
|
||||||
var db = {
|
|
||||||
isSpentDB: sinon.stub().callsArgWith(1, false)
|
|
||||||
};
|
|
||||||
tx._checkSpent(db, [], 'input', function(err) {
|
|
||||||
should.not.exist(err);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue