bitcore node service
This commit is contained in:
parent
038eb401db
commit
e4992a6dfb
213
bitcore-node/addresses.js
Normal file
213
bitcore-node/addresses.js
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var common = require('./common');
|
||||||
|
var bitcore = require('bitcore');
|
||||||
|
var TxController = require('./transactions');
|
||||||
|
|
||||||
|
function AddressController(node) {
|
||||||
|
this.node = node;
|
||||||
|
this.txController = new TxController(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
AddressController.prototype.show = function(req, res, next) {
|
||||||
|
this.getAddressHistory(req.addr, function(err, data) {
|
||||||
|
if(err) {
|
||||||
|
return common.handleErrors(err, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.jsonp(data);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
AddressController.prototype.balance = function(req, res, next) {
|
||||||
|
this.addressHistorySubQuery(req, res, next, 'balanceSat');
|
||||||
|
};
|
||||||
|
|
||||||
|
AddressController.prototype.totalReceived = function(req, res, next) {
|
||||||
|
this.addressHistorySubQuery(req, res, next, 'totalReceivedSat');
|
||||||
|
};
|
||||||
|
|
||||||
|
AddressController.prototype.totalSent = function(req, res, next) {
|
||||||
|
this.addressHistorySubQuery(req, res, next, 'totalSentSat');
|
||||||
|
};
|
||||||
|
|
||||||
|
AddressController.prototype.unconfirmedBalance = function(req, res, next) {
|
||||||
|
this.addressHistorySubQuery(req, res, next, 'unconfirmedBalanceSat');
|
||||||
|
};
|
||||||
|
|
||||||
|
AddressController.prototype.addressHistorySubQuery = function(req, res, next, param) {
|
||||||
|
this.getAddressHistory(req.addr, function(err, data) {
|
||||||
|
if(err) {
|
||||||
|
return common.handleErrors(err, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.jsonp(data[param]);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
AddressController.prototype.getAddressHistory = function(address, callback) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.node.getAddressHistory(address, true, function(err, txinfos) {
|
||||||
|
if(err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(null, self.transformAddressHistory(txinfos, address));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
AddressController.prototype.checkAddr = function(req, res, next) {
|
||||||
|
req.addr = req.params.addr;
|
||||||
|
this.check(req, res, next, [req.addr]);
|
||||||
|
};
|
||||||
|
|
||||||
|
AddressController.prototype.checkAddrs = function(req, res, next) {
|
||||||
|
if(req.body.addrs) {
|
||||||
|
req.addrs = req.body.addrs.split(',');
|
||||||
|
} else {
|
||||||
|
req.addrs = req.params.addrs.split(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.check(req, res, next, req.addrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
AddressController.prototype.check = function(req, res, next, addresses) {
|
||||||
|
if(!addresses.length || !addresses[0]) {
|
||||||
|
return common.handleErrors({
|
||||||
|
message: 'Must include address',
|
||||||
|
code: 1
|
||||||
|
}, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(var i = 0; i < addresses.length; i++) {
|
||||||
|
try {
|
||||||
|
var a = new bitcore.Address(addresses[i]);
|
||||||
|
} catch(e) {
|
||||||
|
return common.handleErrors({
|
||||||
|
message: 'Invalid address: ' + e.message,
|
||||||
|
code: 1
|
||||||
|
}, res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
AddressController.prototype.transformAddressHistory = function(txinfos, address) {
|
||||||
|
var transactions = txinfos.map(function(info) {
|
||||||
|
return info.tx.hash;
|
||||||
|
}).filter(function(value, index, self) {
|
||||||
|
return self.indexOf(value) === index;
|
||||||
|
});
|
||||||
|
|
||||||
|
var balance = 0;
|
||||||
|
var appearances = 0;
|
||||||
|
var totalReceived = 0;
|
||||||
|
var totalSent = 0;
|
||||||
|
var unconfirmedBalance = 0;
|
||||||
|
var unconfirmedAppearances = 0;
|
||||||
|
|
||||||
|
for(var i = 0; i < txinfos.length; i++) {
|
||||||
|
if(txinfos[i].satoshis > 0) {
|
||||||
|
totalReceived += txinfos[i].satoshis;
|
||||||
|
} else {
|
||||||
|
totalSent += -txinfos[i].satoshis;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(txinfos[i].confirmations) {
|
||||||
|
balance += txinfos[i].satoshis;
|
||||||
|
unconfirmedBalance += txinfos[i].satoshis;
|
||||||
|
appearances++;
|
||||||
|
} else {
|
||||||
|
unconfirmedBalance += txinfos[i].satoshis;
|
||||||
|
unconfirmedAppearances++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
addrStr: address,
|
||||||
|
balance: balance / 1e8,
|
||||||
|
balanceSat: balance,
|
||||||
|
totalReceived: totalReceived / 1e8,
|
||||||
|
totalReceivedSat: totalReceived,
|
||||||
|
totalSent: totalSent / 1e8,
|
||||||
|
totalSentSat: totalSent,
|
||||||
|
unconfirmedBalance: unconfirmedBalance / 1e8,
|
||||||
|
unconfirmedBalanceSat: unconfirmedBalance,
|
||||||
|
unconfirmedTxApperances: unconfirmedAppearances, // misspelling - ew
|
||||||
|
txApperances: appearances, // yuck
|
||||||
|
transactions: transactions
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
AddressController.prototype.utxo = function(req, res, next) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.node.getUnspentOutputs(req.addr, true, function(err, utxos) {
|
||||||
|
if(err && err instanceof self.node.errors.NoOutputs) {
|
||||||
|
return res.jsonp([]);
|
||||||
|
} else if(err) {
|
||||||
|
return common.handleErrors(err, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.jsonp(utxos.map(self.transformUtxo.bind(self)));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
AddressController.prototype.multiutxo = function(req, res, next) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.node.getUnspentOutputs(req.addrs, true, function(err, utxos) {
|
||||||
|
if(err && err instanceof self.node.errors.NoOutputs) {
|
||||||
|
return res.jsonp([]);
|
||||||
|
} else if(err) {
|
||||||
|
return common.handleErrors(err, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.jsonp(utxos.map(self.transformUtxo.bind(self)));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
AddressController.prototype.transformUtxo = function(utxo) {
|
||||||
|
return {
|
||||||
|
address: utxo.address,
|
||||||
|
txid: utxo.txid,
|
||||||
|
vout: utxo.outputIndex,
|
||||||
|
ts: utxo.timestamp ? Math.round(utxo.timestamp / 1000) : Math.round(Date.now() / 1000),
|
||||||
|
scriptPubKey: utxo.script,
|
||||||
|
amount: utxo.satoshis / 1e8,
|
||||||
|
confirmations: utxo.confirmations
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
AddressController.prototype.multitxs = function(req, res, next) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.node.getAddressHistory(req.addrs, true, function(err, txinfos) {
|
||||||
|
if(err) {
|
||||||
|
return common.handleErrors(err, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.jsonp(self.transformAddressHistoryForMultiTxs(txinfos));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
AddressController.prototype.transformAddressHistoryForMultiTxs = function(txinfos) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
var transformed = {
|
||||||
|
totalItems: txinfos.length,
|
||||||
|
from: 0,
|
||||||
|
to: txinfos.length,
|
||||||
|
items: txinfos.map(function(txinfo) {
|
||||||
|
return self.txController.transformTransaction(txinfo.tx);
|
||||||
|
}).reverse()
|
||||||
|
};
|
||||||
|
|
||||||
|
return transformed;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = AddressController;
|
||||||
128
bitcore-node/blocks.js
Normal file
128
bitcore-node/blocks.js
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var common = require('./common');
|
||||||
|
var async = require('async');
|
||||||
|
var bitcore = require('bitcore');
|
||||||
|
var BufferUtil = bitcore.util.buffer;
|
||||||
|
|
||||||
|
function BlockController(node) {
|
||||||
|
this.node = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
var BLOCK_LIMIT = 200;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find block by hash ...
|
||||||
|
*/
|
||||||
|
BlockController.prototype.block = function(req, res, next, hash) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.node.getBlock(hash, function(err, block) {
|
||||||
|
if(err && err.message === 'Block not found.') {
|
||||||
|
// TODO libbitcoind should pass an instance of errors.Block.NotFound
|
||||||
|
return common.handleErrors(null, res);
|
||||||
|
} else if(err) {
|
||||||
|
return common.handleErrors(err, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
var info = self.node.services.bitcoind.getBlockIndex(hash);
|
||||||
|
|
||||||
|
req.block = self.transformBlock(block, info);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
BlockController.prototype.transformBlock = function(block, info) {
|
||||||
|
var blockObj = block.toObject();
|
||||||
|
var transactionIds = blockObj.transactions.map(function(tx) {
|
||||||
|
return tx.hash
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
hash: block.hash,
|
||||||
|
confirmations: this.node.services.db.tip.__height - info.height + 1,
|
||||||
|
size: block.toBuffer().length,
|
||||||
|
height: info.height,
|
||||||
|
version: blockObj.header.version,
|
||||||
|
merkleroot: blockObj.header.merkleRoot,
|
||||||
|
tx: transactionIds,
|
||||||
|
time: blockObj.header.time,
|
||||||
|
nonce: blockObj.header.nonce,
|
||||||
|
bits: blockObj.header.bits.toString(16),
|
||||||
|
difficulty: 0, // placeholder
|
||||||
|
chainwork: info.chainWork,
|
||||||
|
previousblockhash: blockObj.header.prevHash,
|
||||||
|
nextblockhash: null, // placeholder
|
||||||
|
reward: 0, // First output of first transaction gives us the reward + fees. How to isolate just reward?
|
||||||
|
isMainChain: true // placeholder
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show block
|
||||||
|
*/
|
||||||
|
BlockController.prototype.show = function(req, res) {
|
||||||
|
if (req.block) {
|
||||||
|
res.jsonp(req.block);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BlockController.prototype.blockIndex = function(req, res, next, height) {
|
||||||
|
var info = this.node.services.bitcoind.getBlockIndex(parseInt(height));
|
||||||
|
if(!info) {
|
||||||
|
return common.handleErrors(null, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.jsonp({
|
||||||
|
blockHash: info.hash
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// List blocks by date
|
||||||
|
BlockController.prototype.list = function(req, res) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
// TODO actually get blocks by date instead of just the last blocks
|
||||||
|
// TODO pagination
|
||||||
|
|
||||||
|
var limit = parseInt(req.query.limit || BLOCK_LIMIT);
|
||||||
|
|
||||||
|
var blocks = [];
|
||||||
|
var lastHash = this.node.services.db.tip.hash;
|
||||||
|
|
||||||
|
async.timesSeries(limit, function(n, next) {
|
||||||
|
self.node.getBlock(lastHash, function(err, block) {
|
||||||
|
if(err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
var info = self.node.services.bitcoind.getBlockIndex(block.hash);
|
||||||
|
block.__height = info.height;
|
||||||
|
blocks.push(block);
|
||||||
|
lastHash = BufferUtil.reverse(block.header.prevHash).toString('hex');
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
}, function(err) {
|
||||||
|
if(err) {
|
||||||
|
return common.handleErrors(err, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = {
|
||||||
|
blocks: blocks.map(function(block) {
|
||||||
|
return {
|
||||||
|
height: block.__height,
|
||||||
|
size: block.toBuffer().length,
|
||||||
|
hash: block.hash,
|
||||||
|
time: block.header.time,
|
||||||
|
txlength: block.transactions.length,
|
||||||
|
poolInfo: {}
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
length: blocks.length,
|
||||||
|
pagination: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
res.jsonp(data);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = BlockController;
|
||||||
19
bitcore-node/common.js
Normal file
19
bitcore-node/common.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
exports.notReady = function (err, res, p) {
|
||||||
|
res.status(503).send('Server not yet ready. Sync Percentage:' + p);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.handleErrors = function (err, res) {
|
||||||
|
if (err) {
|
||||||
|
if (err.code) {
|
||||||
|
res.status(400).send(err.message + '. Code:' + err.code);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res.status(503).send(err.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res.status(404).send('Not found');
|
||||||
|
}
|
||||||
|
};
|
||||||
97
bitcore-node/index.js
Normal file
97
bitcore-node/index.js
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var BaseService = require('./service');
|
||||||
|
var inherits = require('util').inherits;
|
||||||
|
var BlockController = require('./blocks');
|
||||||
|
var TxController = require('./transactions');
|
||||||
|
var AddressController = require('./addresses');
|
||||||
|
var StatusController = require('./status');
|
||||||
|
|
||||||
|
var InsightAPI = function(options) {
|
||||||
|
BaseService.call(this, options);
|
||||||
|
};
|
||||||
|
|
||||||
|
InsightAPI.dependencies = ['address', 'web'];
|
||||||
|
|
||||||
|
inherits(InsightAPI, BaseService);
|
||||||
|
|
||||||
|
InsightAPI.prototype.setupRoutes = function(app) {
|
||||||
|
var apiPrefix = '/api';
|
||||||
|
|
||||||
|
//Block routes
|
||||||
|
var blocks = new BlockController(this.node);
|
||||||
|
app.get(apiPrefix + '/blocks', blocks.list.bind(blocks));
|
||||||
|
|
||||||
|
|
||||||
|
app.get(apiPrefix + '/block/:blockHash', blocks.show.bind(blocks));
|
||||||
|
app.param('blockHash', blocks.block.bind(blocks));
|
||||||
|
|
||||||
|
app.get(apiPrefix + '/block-index/:height', blocks.blockIndex.bind(blocks));
|
||||||
|
app.param('height', blocks.blockIndex.bind(blocks));
|
||||||
|
|
||||||
|
|
||||||
|
// Transaction routes
|
||||||
|
var transactions = new TxController(this.node);
|
||||||
|
app.get(apiPrefix + '/tx/:txid', transactions.show.bind(transactions));
|
||||||
|
app.param('txid', transactions.transaction.bind(transactions));
|
||||||
|
app.get(apiPrefix + '/txs', transactions.list.bind(transactions));
|
||||||
|
app.post(apiPrefix + '/tx/send', transactions.send.bind(transactions));
|
||||||
|
|
||||||
|
// Raw Routes
|
||||||
|
app.get(apiPrefix + '/rawtx/:txid', transactions.showRaw.bind(transactions));
|
||||||
|
app.param('txid', transactions.rawTransaction.bind(transactions));
|
||||||
|
|
||||||
|
// Address routes
|
||||||
|
var addresses = new AddressController(this.node);
|
||||||
|
app.get(apiPrefix + '/addr/:addr', addresses.checkAddr.bind(addresses), addresses.show.bind(addresses));
|
||||||
|
app.get(apiPrefix + '/addr/:addr/utxo', addresses.checkAddr.bind(addresses), addresses.utxo.bind(addresses));
|
||||||
|
app.get(apiPrefix + '/addrs/:addrs/utxo', addresses.checkAddrs.bind(addresses), addresses.multiutxo.bind(addresses));
|
||||||
|
app.post(apiPrefix + '/addrs/utxo', addresses.checkAddrs.bind(addresses), addresses.multiutxo.bind(addresses));
|
||||||
|
app.get(apiPrefix + '/addrs/:addrs/txs', addresses.checkAddrs.bind(addresses), addresses.multitxs.bind(addresses));
|
||||||
|
app.post(apiPrefix + '/addrs/txs', addresses.checkAddrs.bind(addresses), addresses.multitxs.bind(addresses));
|
||||||
|
|
||||||
|
// Address property routes
|
||||||
|
app.get(apiPrefix + '/addr/:addr/balance', addresses.checkAddr.bind(addresses), addresses.balance.bind(addresses));
|
||||||
|
app.get(apiPrefix + '/addr/:addr/totalReceived', addresses.checkAddr.bind(addresses), addresses.totalReceived.bind(addresses));
|
||||||
|
app.get(apiPrefix + '/addr/:addr/totalSent', addresses.checkAddr.bind(addresses), addresses.totalSent.bind(addresses));
|
||||||
|
app.get(apiPrefix + '/addr/:addr/unconfirmedBalance', addresses.checkAddr.bind(addresses), addresses.unconfirmedBalance.bind(addresses));
|
||||||
|
|
||||||
|
// Status route
|
||||||
|
var status = new StatusController(this.node);
|
||||||
|
app.get(apiPrefix + '/status', status.show.bind(status));
|
||||||
|
app.get(apiPrefix + '/sync', status.sync.bind(status));
|
||||||
|
app.get(apiPrefix + '/peer', status.peer.bind(status));
|
||||||
|
app.get(apiPrefix + '/version', status.version.bind(status));
|
||||||
|
|
||||||
|
|
||||||
|
// Utils route
|
||||||
|
/*var utils = require('../app/controllers/utils');
|
||||||
|
app.get(apiPrefix + '/utils/estimatefee', utils.estimateFee);
|
||||||
|
|
||||||
|
// Currency
|
||||||
|
var currency = require('../app/controllers/currency');
|
||||||
|
app.get(apiPrefix + '/currency', currency.index);
|
||||||
|
|
||||||
|
// Email store plugin
|
||||||
|
if (config.enableEmailstore) {
|
||||||
|
var emailPlugin = require('../plugins/emailstore');
|
||||||
|
app.get(apiPrefix + '/email/retrieve', emailPlugin.retrieve);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Currency rates plugin
|
||||||
|
if (config.enableCurrencyRates) {
|
||||||
|
var currencyRatesPlugin = require('../plugins/currencyrates');
|
||||||
|
app.get(apiPrefix + '/rates/:code', currencyRatesPlugin.getRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Address routes
|
||||||
|
var messages = require('../app/controllers/messages');
|
||||||
|
app.get(apiPrefix + '/messages/verify', messages.verify);
|
||||||
|
app.post(apiPrefix + '/messages/verify', messages.verify);
|
||||||
|
|
||||||
|
//Home route
|
||||||
|
var index = require('../app/controllers/index');
|
||||||
|
app.get('*', index.render);*/
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = InsightAPI;
|
||||||
91
bitcore-node/service.js
Normal file
91
bitcore-node/service.js
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var util = require('util');
|
||||||
|
var EventEmitter = require('events').EventEmitter;
|
||||||
|
|
||||||
|
var Service = function(options) {
|
||||||
|
EventEmitter.call(this);
|
||||||
|
|
||||||
|
this.node = options.node;
|
||||||
|
this.name = options.name;
|
||||||
|
};
|
||||||
|
|
||||||
|
util.inherits(Service, EventEmitter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the dependencies that should be loaded before this service.
|
||||||
|
*/
|
||||||
|
Service.dependencies = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* blockHandler
|
||||||
|
* @param {Block} block - the block being added or removed from the chain
|
||||||
|
* @param {Boolean} add - whether the block is being added or removed
|
||||||
|
* @param {Function} callback - call with the leveldb database operations to perform
|
||||||
|
*/
|
||||||
|
Service.prototype.blockHandler = function(block, add, callback) {
|
||||||
|
// implement in the child class
|
||||||
|
setImmediate(function() {
|
||||||
|
callback(null, []);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the bus events available for subscription
|
||||||
|
* @return {Array} an array of event info
|
||||||
|
*/
|
||||||
|
Service.prototype.getPublishEvents = function() {
|
||||||
|
// Example:
|
||||||
|
// return [
|
||||||
|
// ['eventname', this, this.subscribeEvent, this.unsubscribeEvent],
|
||||||
|
// ];
|
||||||
|
return [];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the API methods to expose
|
||||||
|
* @return {Array} return array of methods
|
||||||
|
*/
|
||||||
|
Service.prototype.getAPIMethods = function() {
|
||||||
|
// Example:
|
||||||
|
// return [
|
||||||
|
// ['getData', this, this.getData, 1]
|
||||||
|
// ];
|
||||||
|
|
||||||
|
return [];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Example:
|
||||||
|
// Service.prototype.getData = function(arg1, callback) {
|
||||||
|
//
|
||||||
|
// };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function which is called when module is first initialized
|
||||||
|
*/
|
||||||
|
Service.prototype.start = function(done) {
|
||||||
|
setImmediate(done);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to be called when bitcore-node is stopped
|
||||||
|
*/
|
||||||
|
Service.prototype.stop = function(done) {
|
||||||
|
setImmediate(done);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup express routes
|
||||||
|
* @param {Express} app
|
||||||
|
*/
|
||||||
|
Service.prototype.setupRoutes = function(app) {
|
||||||
|
// Setup express routes here
|
||||||
|
};
|
||||||
|
|
||||||
|
Service.prototype.getRoutePrefix = function() {
|
||||||
|
return this.name;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = Service;
|
||||||
75
bitcore-node/status.js
Normal file
75
bitcore-node/status.js
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
function StatusController(node) {
|
||||||
|
this.node = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusController.prototype.show = function(req, res) {
|
||||||
|
|
||||||
|
var option = req.query.q;
|
||||||
|
|
||||||
|
switch(option) {
|
||||||
|
case 'getDifficulty':
|
||||||
|
res.jsonp(this.getDifficulty());
|
||||||
|
break;
|
||||||
|
case 'getTxOutSetInfo':
|
||||||
|
// TODO
|
||||||
|
// break;
|
||||||
|
case 'getLastBlockHash':
|
||||||
|
// TODO
|
||||||
|
// break;
|
||||||
|
case 'getBestBlockHash':
|
||||||
|
// TODO
|
||||||
|
// break;
|
||||||
|
case 'getInfo':
|
||||||
|
default:
|
||||||
|
res.jsonp(this.getInfo());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
StatusController.prototype.getInfo = function() {
|
||||||
|
return this.node.services.bitcoind.getInfo();
|
||||||
|
};
|
||||||
|
|
||||||
|
StatusController.prototype.getDifficulty = function() {
|
||||||
|
var info = this.node.services.bitcoind.getInfo();
|
||||||
|
return {
|
||||||
|
difficulty: info.difficulty
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
StatusController.prototype.sync = function(req, res) {
|
||||||
|
var status = 'syncing';
|
||||||
|
if(this.node.services.bitcoind.isSynced() && this.node.services.db.tip.__height === this.node.services.bitcoind.height) {
|
||||||
|
status = 'finished';
|
||||||
|
}
|
||||||
|
|
||||||
|
var info = {
|
||||||
|
status: status,
|
||||||
|
blockChainHeight: this.node.services.bitcoind.height,
|
||||||
|
syncPercentage: this.node.services.db.tip.__height / this.node.services.bitcoind.height * 100,
|
||||||
|
height: this.node.services.db.tip.__height,
|
||||||
|
error: null,
|
||||||
|
type: 'bitcore node'
|
||||||
|
};
|
||||||
|
|
||||||
|
res.jsonp(info);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hard coded to make insight ui happy, but not applicable
|
||||||
|
StatusController.prototype.peer = function(req, res) {
|
||||||
|
res.jsonp({
|
||||||
|
connected: true,
|
||||||
|
host: '127.0.0.1',
|
||||||
|
port: null
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
StatusController.prototype.version = function(req, res) {
|
||||||
|
var pjson = require('../../../package.json');
|
||||||
|
res.json({
|
||||||
|
version: pjson.version
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = StatusController;
|
||||||
251
bitcore-node/transactions.js
Normal file
251
bitcore-node/transactions.js
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var bitcore = require('bitcore');
|
||||||
|
var common = require('./common');
|
||||||
|
var async = require('async');
|
||||||
|
|
||||||
|
function TxController(node) {
|
||||||
|
this.node = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
TxController.prototype.show = function(req, res) {
|
||||||
|
if (req.transaction) {
|
||||||
|
res.jsonp(req.transaction);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find transaction by hash ...
|
||||||
|
*/
|
||||||
|
TxController.prototype.transaction = function(req, res, next, txid) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.node.getTransactionWithBlockInfo(txid, true, function(err, transaction) {
|
||||||
|
if (err && err instanceof self.node.errors.Transaction.NotFound) {
|
||||||
|
return common.handleErrors(null, res);
|
||||||
|
} else if(err) {
|
||||||
|
return common.handleErrors(err, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction.populateInputs(self.node.services.db, [], function(err) {
|
||||||
|
if(err) {
|
||||||
|
return res.send({
|
||||||
|
error: err.toString()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
req.transaction = self.transformTransaction(transaction);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
TxController.prototype.transformTransaction = function(transaction) {
|
||||||
|
var txObj = transaction.toObject();
|
||||||
|
|
||||||
|
var confirmations = 0;
|
||||||
|
if(transaction.__height >= 0) {
|
||||||
|
confirmations = this.node.services.db.tip.__height - transaction.__height + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var transformed = {
|
||||||
|
txid: txObj.hash,
|
||||||
|
version: txObj.version,
|
||||||
|
locktime: txObj.nLockTime
|
||||||
|
};
|
||||||
|
|
||||||
|
if(transaction.isCoinbase()) {
|
||||||
|
transformed.vin = [
|
||||||
|
{
|
||||||
|
coinbase: txObj.inputs[0].script,
|
||||||
|
sequence: txObj.inputs[0].sequenceNumber,
|
||||||
|
n: 0
|
||||||
|
}
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
transformed.vin = txObj.inputs.map(this.transformInput.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
transformed.vout = txObj.outputs.map(this.transformOutput.bind(this));
|
||||||
|
|
||||||
|
transformed.blockhash = transaction.__blockHash;
|
||||||
|
transformed.confirmations = confirmations;
|
||||||
|
transformed.time = transaction.__timestamp ? transaction.__timestamp : Date.now(); // can we get this from bitcoind?
|
||||||
|
transformed.blocktime = transformed.time;
|
||||||
|
|
||||||
|
if(transaction.isCoinbase()) {
|
||||||
|
transformed.isCoinBase = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
transformed.valueOut = transaction.outputAmount / 1e8;
|
||||||
|
transformed.size = transaction.toBuffer().length;
|
||||||
|
if(transaction.hasAllUtxoInfo()) {
|
||||||
|
transformed.valueIn = transaction.inputAmount / 1e8;
|
||||||
|
transformed.fees = transaction.getFee() / 1e8;
|
||||||
|
}
|
||||||
|
|
||||||
|
return transformed;
|
||||||
|
};
|
||||||
|
|
||||||
|
TxController.prototype.transformInput = function(input, index) {
|
||||||
|
var transformed = {
|
||||||
|
txid: input.prevTxId,
|
||||||
|
vout: input.outputIndex,
|
||||||
|
scriptSig: {
|
||||||
|
asm: null, // TODO
|
||||||
|
hex: input.script
|
||||||
|
},
|
||||||
|
sequence: input.sequenceNumber,
|
||||||
|
n: index
|
||||||
|
};
|
||||||
|
|
||||||
|
if(input.output) {
|
||||||
|
transformed.addr = bitcore.Script(input.output.script).toAddress(this.node.network).toString();
|
||||||
|
transformed.valueSat = input.output.satoshis;
|
||||||
|
transformed.value = input.output.satoshis / 1e8;
|
||||||
|
transformed.doubleSpentTxID = null; // TODO
|
||||||
|
transformed.isConfirmed = null; // TODO
|
||||||
|
transformed.confirmations = null; // TODO
|
||||||
|
transformed.unconfirmedInput = null; // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
return transformed;
|
||||||
|
};
|
||||||
|
|
||||||
|
TxController.prototype.transformOutput = function(output, index) {
|
||||||
|
var transformed = {
|
||||||
|
value: (output.satoshis / 1e8).toFixed(8),
|
||||||
|
n: index,
|
||||||
|
scriptPubKey: {
|
||||||
|
asm: null, // TODO
|
||||||
|
hex: output.script,
|
||||||
|
reqSigs: null, // TODO
|
||||||
|
type: null // TODO
|
||||||
|
},
|
||||||
|
spentTxId: null, // TODO
|
||||||
|
spentIndex: null, // TODO
|
||||||
|
spentTs: null // TODO
|
||||||
|
};
|
||||||
|
|
||||||
|
var address = bitcore.Script(output.script).toAddress(this.node.network).toString();
|
||||||
|
if(address !== 'false') {
|
||||||
|
transformed.scriptPubKey.addresses = [address];
|
||||||
|
}
|
||||||
|
|
||||||
|
return transformed;
|
||||||
|
};
|
||||||
|
|
||||||
|
TxController.prototype.rawTransaction = function(req, res, next, txid) {
|
||||||
|
this.node.getTransaction(txid, true, function(err, transaction) {
|
||||||
|
if (err && err instanceof self.node.errors.Transaction.NotFound) {
|
||||||
|
return common.handleErrors(null, res);
|
||||||
|
} else if(err) {
|
||||||
|
return common.handleErrors(err, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
req.rawTransaction = {
|
||||||
|
'rawtx': transaction.toBuffer().toString('hex')
|
||||||
|
};
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
TxController.prototype.showRaw = function(req, res) {
|
||||||
|
if (req.rawTransaction) {
|
||||||
|
res.jsonp(req.rawTransaction);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TxController.prototype.list = function(req, res) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
var blockHash = req.query.block;
|
||||||
|
var address = req.query.address;
|
||||||
|
var page = req.query.pageNum;
|
||||||
|
var pageLength = 10;
|
||||||
|
var pagesTotal = 1;
|
||||||
|
|
||||||
|
if(blockHash) {
|
||||||
|
self.node.getBlock(blockHash, function(err, block) {
|
||||||
|
if(err && err.message === 'Block not found.') {
|
||||||
|
return common.handleErrors(null, res);
|
||||||
|
} else if(err) {
|
||||||
|
return common.handleErrors(err, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
var blockInfo = self.node.services.bitcoind.getBlockIndex(block.hash);
|
||||||
|
var txs = block.transactions;
|
||||||
|
var totalTxs = txs.length;
|
||||||
|
|
||||||
|
if(page) {
|
||||||
|
txs = txs.splice(page * pageLength, pageLength);
|
||||||
|
pagesTotal = Math.ceil(totalTxs / pageLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
async.mapSeries(txs, function(tx, next) {
|
||||||
|
tx.__blockHash = block.hash;
|
||||||
|
tx.__height = blockInfo.height;
|
||||||
|
tx.__timestamp = block.header.time;
|
||||||
|
|
||||||
|
tx.populateInputs(self.node.services.db, [], function(err) {
|
||||||
|
if(err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
next(null, self.transformTransaction(tx));
|
||||||
|
});
|
||||||
|
}, function(err, transformed) {
|
||||||
|
if(err) {
|
||||||
|
return common.handleErrors(err, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.jsonp({
|
||||||
|
pagesTotal: pagesTotal,
|
||||||
|
txs: transformed
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else if(address) {
|
||||||
|
self.node.getAddressHistory(address, true, function(err, txinfos) {
|
||||||
|
if(err) {
|
||||||
|
return common.handleErrors(err, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
var txs = txinfos.map(function(info) {
|
||||||
|
return info.tx;
|
||||||
|
}).filter(function(value, index, self) {
|
||||||
|
return self.indexOf(value) === index;
|
||||||
|
});
|
||||||
|
|
||||||
|
var totalTxs = txs.length;
|
||||||
|
|
||||||
|
if(page) {
|
||||||
|
txs = txs.splice(page * pageLength, pageLength);
|
||||||
|
pagesTotal = Math.ceil(totalTxs / pageLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
var transformed = txs.map(self.transformTransaction.bind(self));
|
||||||
|
|
||||||
|
res.jsonp({
|
||||||
|
pagesTotal: pagesTotal,
|
||||||
|
txs: transformed
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return common.handleErrors(new Error('Block hash or address expected'), res);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TxController.prototype.send = function(req, res) {
|
||||||
|
this.node.sendTransaction(req.body.rawtx, function(err, txid) {
|
||||||
|
if(err) {
|
||||||
|
// TODO handle specific errors
|
||||||
|
return common.handleErrors(err, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({'txid': txid});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = TxController;
|
||||||
@ -43,6 +43,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node insight.js"
|
"start": "node insight.js"
|
||||||
},
|
},
|
||||||
|
"bitcoreNode": "bitcore-node",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"async": "*",
|
"async": "*",
|
||||||
"base58-native": "0.1.2",
|
"base58-native": "0.1.2",
|
||||||
|
|||||||
165
test/bitcore-node/blocks.js
Normal file
165
test/bitcore-node/blocks.js
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var should = require('should');
|
||||||
|
var sinon = require('sinon');
|
||||||
|
var BlockController = require('../../bitcore-node/blocks');
|
||||||
|
var bitcore = require('bitcore');
|
||||||
|
var _ = require('lodash');
|
||||||
|
|
||||||
|
var blocks = {
|
||||||
|
'0000000000000afa0c3c0afd450c793a1e300ec84cbe9555166e06132f19a8f7': '07000020a491892cca9f143f7f00b8d65bbce0204bb32e17e914325fa5010000000000003e28f0519ecf01f7f01ea8da61084b2e4741a18ce1f3738117b84458353764b06fb9e35567f20c1a78eb626f0301000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2303d6250800feb0aae355fe263600000963676d696e6572343208ae5800000000000000ffffffff01c018824a000000001976a91468bedce8982d25c3b6b03f6238cbad00378b8ead88ac000000000100000002ad5a14ae9d0f3221b790c4fc590fddceea1456e5692d8c4bf1ff7175f2b0c987000000008b4830450221008e5df62719cd92d7b137d00bbd27f153f2909bcad3a300960bc1020ec6d5e961022039df51600ff4fb5da5a794d1648c6b47c1f7d277fd5877fb5e52a730a3595f8c014104eb1e0ccd9afcac42229348dd776e991c69551ae3474340fada12e787e51758397e1d3afdba360d6374261125ea3b6ea079a5f202c150dfd729e1062d9176a307ffffffff9621ac65bc22ea593ca9a61a8d63e461bf3d3f277989df5d3bd33ddfae0aa1d8000000008a4730440220761464d7bab9515d92260762a97af82a9b25d202d8f7197b1aaec81b6fed541f022059f99606de6b06e17b2cd102dceb3807ebdd9e777a5b77c9a0b3672f5eabcb31014104eb1e0ccd9afcac42229348dd776e991c69551ae3474340fada12e787e51758397e1d3afdba360d6374261125ea3b6ea079a5f202c150dfd729e1062d9176a307ffffffff02dc374401000000001976a9144b7b335f978f130269fe661423258ae9642df8a188ac72b3d000000000001976a9146efcf883b4b6f9997be9a0600f6c095fe2bd2d9288ac000000000100000002060d3cb6dfb7ffe85e2908010fea63190c9707e96fc7448128eb895b5e222771030000006b483045022100f67cffc0ae23adb236ff3edb4a9736e277605db30cc7708dfab8cf1e1483bbce022052396aa5d664ec1cb65992c423fd9a17e94dc7af328d2d559e90746dd195ca5901210346134da14907581d8190d3980caaf46d95e4eb9c1ca8e70f1fc6007fefb1909dfeffffff7b2d8a8263cffbdb722e2a5c74166e6f2258634e277c0b08f51b578b667e2fba000000006a473044022077222a91cda23af69179377c62d84a176fb12caff6c5cbf6ae9e5957ff3b1afe0220768edead76819228dcba18cca3c9a5a5d4c32919720f21df21a297ba375bbe5c012103371ea5a4dfe356b3ea4042a537d7ab7ee0faabd43e21b6cc076fda2240629eeefeffffff02209a1d00000000001976a9148e451eec7ca0a1764b4ab119274efdd2727b3c8588ac40420f00000000001976a914d0fce8f064cd1059a6a11501dd66fe42368572b088accb250800',
|
||||||
|
'000000000008fbb2e358e382a6f6948b2da24563bba183af447e6e2542e8efc7': '0300000041c49521c6106debe1221077ca9e71850e3c328037e5e98fbd0600000000000082b3a56da828618b1b7b103763e3c21c38c9536ce185ddd4aebdcd627645a878fb96e355ffff001d192907730101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2803bf25081b4d696e656420627920416e74506f6f6c202bde53072055e396fb010300000099050000ffffffff01807c814a000000001976a9144c5220c58749e5474ec4e5bbb0c2cc0342c6f44488ac00000000',
|
||||||
|
'00000000000006bd8fe9e53780323c0e85719eca771022e1eb6d10c62195c441': '030000004705096ec15ee742e9c14aee22f5f30cb568a0817b4a6c1ec4b9010000000000afb4442dbd55dcc8c86c94dc84ca0d130aeade144623fc0e47849343087067204792e35567f20c1a4b773d0f0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2803be25081b4d696e656420627920416e74506f6f6c202bde53072055e392470100000000aba10000ffffffff01807c814a000000001976a9144c5220c58749e5474ec4e5bbb0c2cc0342c6f44488ac00000000'
|
||||||
|
};
|
||||||
|
|
||||||
|
var blockIndexes = {
|
||||||
|
'0000000000000afa0c3c0afd450c793a1e300ec84cbe9555166e06132f19a8f7': {
|
||||||
|
hash: '0000000000000afa0c3c0afd450c793a1e300ec84cbe9555166e06132f19a8f7',
|
||||||
|
chainWork: '0000000000000000000000000000000000000000000000054626b1839ade284a',
|
||||||
|
prevHash: '00000000000001a55f3214e9172eb34b20e0bc5bd6b8007f3f149fca2c8991a4',
|
||||||
|
height: 533974
|
||||||
|
},
|
||||||
|
'000000000008fbb2e358e382a6f6948b2da24563bba183af447e6e2542e8efc7': {
|
||||||
|
hash: '000000000008fbb2e358e382a6f6948b2da24563bba183af447e6e2542e8efc7',
|
||||||
|
chainWork: '00000000000000000000000000000000000000000000000544ea52e1575ca753',
|
||||||
|
prevHash: '00000000000006bd8fe9e53780323c0e85719eca771022e1eb6d10c62195c441',
|
||||||
|
height: 533951
|
||||||
|
},
|
||||||
|
'00000000000006bd8fe9e53780323c0e85719eca771022e1eb6d10c62195c441': {
|
||||||
|
hash: '00000000000006bd8fe9e53780323c0e85719eca771022e1eb6d10c62195c441',
|
||||||
|
chainWork: '00000000000000000000000000000000000000000000000544ea52e0575ba752',
|
||||||
|
prevHash: '000000000001b9c41e6c4a7b81a068b50cf3f522ee4ac1e942e75ec16e090547',
|
||||||
|
height: 533950
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Blocks', function() {
|
||||||
|
describe('/blocks/:blockHash route', function() {
|
||||||
|
var insight = {
|
||||||
|
"hash": "0000000000000afa0c3c0afd450c793a1e300ec84cbe9555166e06132f19a8f7",
|
||||||
|
"confirmations": 119,
|
||||||
|
"size": 1011,
|
||||||
|
"height": 533974,
|
||||||
|
"version": 536870919,
|
||||||
|
"merkleroot": "b06437355844b8178173f3e18ca141472e4b0861daa81ef0f701cf9e51f0283e",
|
||||||
|
"tx": [
|
||||||
|
"25a988e54b02e0e5df146a0f8fa7b9db56210533a9f04bdfda5f4ceb6f77aadd",
|
||||||
|
"b85334bf2df35c6dd5b294efe92ffc793a78edff75a2ca666fc296ffb04bbba0",
|
||||||
|
"2e01c7a4a0e335112236b711c4aaddd02e8dc59ba2cda416e8f80ff06dddd7e1"
|
||||||
|
],
|
||||||
|
"time": 1440987503,
|
||||||
|
"nonce": 1868753784,
|
||||||
|
"bits": "1a0cf267",
|
||||||
|
"difficulty": 1295829.93087696,
|
||||||
|
"chainwork": "0000000000000000000000000000000000000000000000054626b1839ade284a",
|
||||||
|
"previousblockhash": "00000000000001a55f3214e9172eb34b20e0bc5bd6b8007f3f149fca2c8991a4",
|
||||||
|
"nextblockhash": "000000000001e866a8057cde0c650796cb8a59e0e6038dc31c69d7ca6649627d",
|
||||||
|
"reward": 12.5,
|
||||||
|
"isMainChain": true
|
||||||
|
};
|
||||||
|
|
||||||
|
var bitcoreBlock = bitcore.Block.fromBuffer(new Buffer(blocks['0000000000000afa0c3c0afd450c793a1e300ec84cbe9555166e06132f19a8f7'], 'hex'));
|
||||||
|
|
||||||
|
var todos = {
|
||||||
|
difficulty: 1295829.93087696,
|
||||||
|
nextblockhash: '000000000001e866a8057cde0c650796cb8a59e0e6038dc31c69d7ca6649627d',
|
||||||
|
reward: 12.5
|
||||||
|
};
|
||||||
|
|
||||||
|
var node = {
|
||||||
|
getBlock: sinon.stub().callsArgWith(1, null, bitcoreBlock),
|
||||||
|
services: {
|
||||||
|
bitcoind: {
|
||||||
|
getBlockIndex: sinon.stub().returns(blockIndexes['0000000000000afa0c3c0afd450c793a1e300ec84cbe9555166e06132f19a8f7'])
|
||||||
|
},
|
||||||
|
db: {
|
||||||
|
tip: {
|
||||||
|
__height: 534092
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
it('block data should be correct', function(done) {
|
||||||
|
var blocks = new BlockController(node);
|
||||||
|
var req = {};
|
||||||
|
var res = {};
|
||||||
|
var next = function() {
|
||||||
|
should.exist(req.block);
|
||||||
|
var block = _.extend(req.block, todos);
|
||||||
|
should(block).eql(insight);
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
|
||||||
|
var hash = '0000000000000afa0c3c0afd450c793a1e300ec84cbe9555166e06132f19a8f7';
|
||||||
|
|
||||||
|
blocks.block(req, res, next, hash);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('/blocks route', function() {
|
||||||
|
|
||||||
|
var insight = {
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"height": 533951,
|
||||||
|
"size": 206,
|
||||||
|
"hash": "000000000008fbb2e358e382a6f6948b2da24563bba183af447e6e2542e8efc7",
|
||||||
|
"time": 1440978683,
|
||||||
|
"txlength": 1,
|
||||||
|
"poolInfo": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"height": 533950,
|
||||||
|
"size": 206,
|
||||||
|
"hash": "00000000000006bd8fe9e53780323c0e85719eca771022e1eb6d10c62195c441",
|
||||||
|
"time": 1440977479,
|
||||||
|
"txlength": 1,
|
||||||
|
"poolInfo": {}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"length": 2,
|
||||||
|
"pagination": {}
|
||||||
|
};
|
||||||
|
|
||||||
|
var stub = sinon.stub();
|
||||||
|
stub.onFirstCall().callsArgWith(1, null, bitcore.Block.fromBuffer(blocks['000000000008fbb2e358e382a6f6948b2da24563bba183af447e6e2542e8efc7'], 'hex'));
|
||||||
|
stub.onSecondCall().callsArgWith(1, null, bitcore.Block.fromBuffer(blocks['00000000000006bd8fe9e53780323c0e85719eca771022e1eb6d10c62195c441'], 'hex'))
|
||||||
|
|
||||||
|
var node = {
|
||||||
|
getBlock: stub,
|
||||||
|
services: {
|
||||||
|
bitcoind: {
|
||||||
|
getBlockIndex: function(hash) {
|
||||||
|
return blockIndexes[hash];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
db: {
|
||||||
|
tip: {
|
||||||
|
hash: '000000000008fbb2e358e382a6f6948b2da24563bba183af447e6e2542e8efc7'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should have correct data', function(done) {
|
||||||
|
var blocks = new BlockController(node);
|
||||||
|
|
||||||
|
var req = {
|
||||||
|
query: {
|
||||||
|
limit: 2
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var res = {
|
||||||
|
jsonp: function(data) {
|
||||||
|
should(data).eql(insight);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
blocks.list(req, res);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user