Last active
May 5, 2017 11:51
-
-
Save cyborgshead/48feb4728f7ef6fc21a0e4b4cb3a47c9 to your computer and use it in GitHub Desktop.
insight-api-blocks
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
var common = require('./common'); | |
var async = require('async'); | |
var bitcore = require('bitcore-lib'); | |
var pools = require('../pools.json'); | |
var BN = bitcore.crypto.BN; | |
var TxController = require('./transactions.js'); | |
function BlockController(node) { | |
var self = this; | |
this.node = node; | |
this.poolStrings = {}; | |
pools.forEach(function(pool) { | |
pool.searchStrings.forEach(function(s) { | |
self.poolStrings[s] = { | |
poolName: pool.poolName, | |
url: pool.url | |
}; | |
}); | |
}); | |
} | |
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, | |
poolInfo: this.getPoolInfo(block) | |
}; | |
}; | |
/** | |
* 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 | |
// }); | |
// }; | |
BlockController.prototype.blockIndex = function(req, res, next, height) { | |
var self = this; | |
var info = this.node.services.bitcoind.getBlockIndex(parseInt(height)); | |
if(!info) { | |
return common.handleErrors(null, res); | |
} | |
self.node.getBlock(info.hash, function(err, block) { | |
if(err && err.message === 'Block not found.') { | |
return common.handleErrors(null, res); | |
} else if(err) { | |
return common.handleErrors(err, res); | |
} | |
var txs = block.transactions; | |
async.mapSeries(txs, function(tx, next) { | |
tx.__blockHash = block.hash; | |
tx.__height = info.height; | |
tx.__timestamp = block.header.time; | |
tx.populateInputs(self.node.services.db, [], function(err) { | |
if(err) { | |
return next(err); | |
} | |
TxController.transformTransaction(tx, next); | |
}); | |
}, function(err, transformed) { | |
if(err) { | |
return common.handleErrors(err, res); | |
} | |
res.jsonp({ | |
block: self.transformBlock(block, info), | |
txs: transformed | |
}); | |
}); | |
}); | |
}; | |
// 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: self.getPoolInfo(block) | |
}; | |
}), | |
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); | |
} | |
); | |
}); | |
}; | |
BlockController.prototype.getPoolInfo = function(block) { | |
var coinbaseBuffer = block.transactions[0].inputs[0]._scriptBuffer; | |
for(var k in this.poolStrings) { | |
if (coinbaseBuffer.toString('utf-8').match(k)) { | |
return this.poolStrings[k]; | |
} | |
} | |
return {}; | |
}; | |
//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; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment