206 lines
5.4 KiB
JavaScript
206 lines
5.4 KiB
JavaScript
'use strict';
|
|
|
|
var common = require('./common');
|
|
var async = require('async');
|
|
var bitcore = require('bitcore');
|
|
var BufferUtil = bitcore.util.buffer;
|
|
var BN = bitcore.crypto.BN;
|
|
|
|
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);
|
|
info.isMainChain = self.node.services.bitcoind.isMainChain(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: block.header.getDifficulty(),
|
|
chainwork: info.chainWork,
|
|
previousblockhash: blockObj.header.prevHash,
|
|
nextblockhash: this.node.services.bitcoind.getNextBlockHash(block.hash),
|
|
reward: this.getBlockReward(info.height) / 1e8,
|
|
isMainChain: info.isMainChain
|
|
};
|
|
};
|
|
|
|
/**
|
|
* 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;
|
|
|
|
var dateStr;
|
|
var todayStr = this.formatTimestamp(new Date());
|
|
var isToday;
|
|
|
|
if (req.query.blockDate) {
|
|
dateStr = req.query.blockDate;
|
|
var datePattern = /\d{4}-\d{2}-\d{2}/;
|
|
if(!datePattern.test(dateStr)) {
|
|
return common.handleErrors(new Error('Please use yyyy-mm-dd format'), res);
|
|
}
|
|
|
|
isToday = dateStr === todayStr;
|
|
} else {
|
|
dateStr = todayStr;
|
|
isToday = true;
|
|
}
|
|
|
|
var gte = Math.round((new Date(dateStr)).getTime() / 1000);
|
|
|
|
//pagination
|
|
var lte = parseInt(req.query.startTimestamp) || gte + 86400;
|
|
var prev = this.formatTimestamp(new Date((gte - 86400) * 1000));
|
|
var next = lte ? this.formatTimestamp(new Date(lte * 1000)) : null;
|
|
var limit = parseInt(req.query.limit || BLOCK_LIMIT);
|
|
var more = false;
|
|
var moreTs = lte;
|
|
|
|
self.node.services.db.getBlockHashesByTimestamp(lte, gte, function(err, hashes) {
|
|
if(err) {
|
|
return common.handleErrors(err, res);
|
|
}
|
|
|
|
if(hashes.length > limit) {
|
|
more = true;
|
|
hashes = hashes.slice(0, limit);
|
|
}
|
|
|
|
async.mapSeries(
|
|
hashes,
|
|
function(hash, next) {
|
|
self.node.getBlock(hash, function(err, block) {
|
|
if(err) {
|
|
return next(err);
|
|
}
|
|
|
|
var info = self.node.services.bitcoind.getBlockIndex(hash);
|
|
block.__height = info.height;
|
|
|
|
if(moreTs > block.header.timestamp) {
|
|
moreTs = block.header.timestamp;
|
|
}
|
|
|
|
return next(null, block);
|
|
});
|
|
},
|
|
function(err, blocks) {
|
|
if(err) {
|
|
return common.handleErrors(err, res);
|
|
}
|
|
|
|
blocks.sort(function(a, b) {
|
|
return b.__height - a.__height;
|
|
});
|
|
|
|
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: {
|
|
next: next,
|
|
prev: prev,
|
|
currentTs: lte - 1,
|
|
current: dateStr,
|
|
isToday: isToday,
|
|
more: more
|
|
}
|
|
};
|
|
|
|
if(more) {
|
|
data.pagination.moreTs = moreTs;
|
|
}
|
|
|
|
res.jsonp(data);
|
|
}
|
|
);
|
|
});
|
|
};
|
|
|
|
//helper to convert timestamps to yyyy-mm-dd format
|
|
BlockController.prototype.formatTimestamp = function(date) {
|
|
var yyyy = date.getUTCFullYear().toString();
|
|
var mm = (date.getUTCMonth() + 1).toString(); // getMonth() is zero-based
|
|
var dd = date.getUTCDate().toString();
|
|
|
|
return yyyy + '-' + (mm[1] ? mm : '0' + mm[0]) + '-' + (dd[1] ? dd : '0' + dd[0]); //padding
|
|
};
|
|
|
|
BlockController.prototype.getBlockReward = function(height) {
|
|
var halvings = Math.floor(height / 210000);
|
|
// Force block reward to zero when right shift is undefined.
|
|
if (halvings >= 64) {
|
|
return 0;
|
|
}
|
|
|
|
// Subsidy is cut in half every 210,000 blocks which will occur approximately every 4 years.
|
|
var subsidy = new BN(50 * 1e8);
|
|
subsidy = subsidy.shrn(halvings);
|
|
|
|
return parseInt(subsidy.toString(10));
|
|
};
|
|
|
|
module.exports = BlockController;
|