Mario Colque
11 years ago
78 changed files with 284 additions and 5547 deletions
@ -1,11 +0,0 @@ |
|||
--extra=+f |
|||
--exclude=*jquery* |
|||
--exclude=node_modules/a* |
|||
--exclude=node_modules/[c-z]* |
|||
--exclude=*grunt* |
|||
--exclude=*bower* |
|||
--exclude=.swp |
|||
--exclude=public |
|||
--links=yes |
|||
--totals=yes |
|||
|
@ -1,21 +0,0 @@ |
|||
# EditorConfig helps developers define and maintain consistent |
|||
# coding styles between different editors and IDEs |
|||
# editorconfig.org |
|||
|
|||
root = true |
|||
|
|||
|
|||
[*] |
|||
|
|||
# Change these settings to your own preference |
|||
indent_style = space |
|||
indent_size = 2 |
|||
|
|||
# We recommend you to keep these unchanged |
|||
end_of_line = lf |
|||
charset = utf-8 |
|||
trim_trailing_whitespace = true |
|||
insert_final_newline = true |
|||
|
|||
[*.md] |
|||
trim_trailing_whitespace = false |
@ -1,7 +0,0 @@ |
|||
language: node_js |
|||
node_js: |
|||
- "0.10" |
|||
env: |
|||
- NODE_ENV=travis |
|||
services: |
|||
- mongodb |
@ -1,53 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
/** |
|||
* Module dependencies. |
|||
*/ |
|||
|
|||
var Address = require('../models/Address'), |
|||
common = require('./common'); |
|||
|
|||
var getAddr = function(req, res, next) { |
|||
var a; |
|||
try { |
|||
var addr = req.param('addr'); |
|||
a = Address.new(addr); |
|||
} catch (e) { |
|||
common.handleErrors({message: 'Invalid address:' + e.message, code: 1}, res, next); |
|||
return null; |
|||
} |
|||
return a; |
|||
}; |
|||
|
|||
|
|||
exports.show = function(req, res, next) { |
|||
var a = getAddr(req, res, next); |
|||
|
|||
if (a) |
|||
a.update(function(err) { |
|||
if (err) { |
|||
return common.handleErrors(err, res); |
|||
} |
|||
else { |
|||
return res.jsonp(a); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
|
|||
|
|||
exports.utxo = function(req, res, next) { |
|||
var a = getAddr(req, res, next); |
|||
|
|||
if (a) |
|||
a.getUtxo(function(err, utxo) { |
|||
if (err) |
|||
return common.handleErrors(err, res); |
|||
else { |
|||
return res.jsonp(utxo); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
|
|||
|
@ -1,156 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
/** |
|||
* Module dependencies. |
|||
*/ |
|||
var common = require('./common'), |
|||
async = require('async'), |
|||
BlockDb = require('../../lib/BlockDb').class(); |
|||
|
|||
var bdb = new BlockDb(); |
|||
|
|||
/** |
|||
* Find block by hash ... |
|||
*/ |
|||
exports.block = function(req, res, next, hash) { |
|||
bdb.fromHashWithInfo(hash, function(err, block) { |
|||
if (err || !block) |
|||
return common.handleErrors(err, res, next); |
|||
else { |
|||
bdb.getPoolInfo(block.info.tx[0], function(info) { |
|||
block.info.poolInfo = info; |
|||
req.block = block.info; |
|||
return next(); |
|||
}); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
|
|||
/** |
|||
* Show block |
|||
*/ |
|||
exports.show = function(req, res) { |
|||
if (req.block) { |
|||
res.jsonp(req.block); |
|||
} |
|||
}; |
|||
|
|||
/** |
|||
* Show block by Height |
|||
*/ |
|||
exports.blockindex = function(req, res, next, height) { |
|||
bdb.blockIndex(height, function(err, hashStr) { |
|||
if (err) { |
|||
console.log(err); |
|||
res.status(400).send('Bad Request'); // TODO
|
|||
} else { |
|||
res.jsonp(hashStr); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
var getBlock = function(blockhash, cb) { |
|||
bdb.fromHashWithInfo(blockhash, function(err, block) { |
|||
if (err) { |
|||
console.log(err); |
|||
return cb(err); |
|||
} |
|||
|
|||
// TODO
|
|||
if (!block.info) { |
|||
console.log('[blocks.js.60]: could not get %s from RPC. Orphan? Error?', blockhash); //TODO
|
|||
// Probably orphan
|
|||
block.info = { |
|||
hash: blockhash, |
|||
isOrphan: 1, |
|||
}; |
|||
} |
|||
|
|||
bdb.getPoolInfo(block.info.tx[0], function(info) { |
|||
block.info.poolInfo = info; |
|||
return cb(err, block.info); |
|||
}); |
|||
|
|||
}); |
|||
}; |
|||
|
|||
/** |
|||
* List of blocks by date |
|||
*/ |
|||
exports.list = function(req, res) { |
|||
var isToday = false; |
|||
|
|||
//helper to convert timestamps to yyyy-mm-dd format
|
|||
var 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
|
|||
}; |
|||
|
|||
var dateStr; |
|||
var todayStr = formatTimestamp(new Date()); |
|||
|
|||
if (req.query.blockDate) { |
|||
// TODO: Validate format yyyy-mm-dd
|
|||
dateStr = req.query.blockDate; |
|||
isToday = dateStr === todayStr; |
|||
} else { |
|||
dateStr = todayStr; |
|||
isToday = true; |
|||
} |
|||
|
|||
var gte = Math.round((new Date(dateStr)).getTime() / 1000); |
|||
|
|||
//pagination
|
|||
var lte = gte + 86400; |
|||
var prev = formatTimestamp(new Date((gte - 86400) * 1000)); |
|||
var next = formatTimestamp(new Date(lte * 1000)); |
|||
|
|||
bdb.getBlocksByDate(gte, lte, function(err, blocks) { |
|||
if (err) { |
|||
res.status(500).send(err); |
|||
} else { |
|||
var blockList = []; |
|||
var l = blocks.length; |
|||
var limit = parseInt(req.query.limit || l); |
|||
if (l < limit) limit = l; |
|||
|
|||
for (var i = 0; i < limit; i++) { |
|||
blockList.push(blocks[i]); |
|||
} |
|||
|
|||
async.mapSeries(blockList, |
|||
function(b, cb) { |
|||
getBlock(b.hash, function(err, info) { |
|||
if (err) { |
|||
console.log(err); |
|||
return cb(err); |
|||
} |
|||
return cb(err, { |
|||
height: info.height, |
|||
size: info.size, |
|||
hash: b.hash, |
|||
time: b.ts || info.time, |
|||
txlength: info.tx.length, |
|||
poolInfo: info.poolInfo |
|||
}); |
|||
}); |
|||
}, function(err, allblocks) { |
|||
res.jsonp({ |
|||
blocks: allblocks, |
|||
length: allblocks.length, |
|||
pagination: { |
|||
next: next, |
|||
prev: prev, |
|||
currentTs: lte - 1, |
|||
current: dateStr, |
|||
isToday: isToday |
|||
} |
|||
}); |
|||
}); |
|||
} |
|||
}); |
|||
}; |
@ -1,16 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
|
|||
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'); |
|||
} |
|||
}; |
@ -1,60 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
var config = require('../../config/config'); |
|||
|
|||
// Set the initial vars
|
|||
var timestamp = +new Date(), |
|||
delay = config.currencyRefresh * 60000, |
|||
bitstampRate = 0; |
|||
|
|||
exports.index = function(req, res) { |
|||
|
|||
var _xhr = function() { |
|||
if (typeof XMLHttpRequest !== 'undefined' && XMLHttpRequest !== null) { |
|||
return new XMLHttpRequest(); |
|||
} else if (typeof require !== 'undefined' && require !== null) { |
|||
var XMLhttprequest = require('xmlhttprequest').XMLHttpRequest; |
|||
return new XMLhttprequest(); |
|||
} |
|||
}; |
|||
|
|||
var _request = function(url, cb) { |
|||
var request; |
|||
request = _xhr(); |
|||
request.open('GET', url, true); |
|||
request.onreadystatechange = function() { |
|||
if (request.readyState === 4) { |
|||
if (request.status === 200) { |
|||
return cb(false, request.responseText); |
|||
} |
|||
|
|||
return cb(true, { |
|||
status: request.status, |
|||
message: 'Request error' |
|||
}); |
|||
} |
|||
}; |
|||
|
|||
return request.send(null); |
|||
}; |
|||
|
|||
// Init
|
|||
var currentTime = +new Date(); |
|||
if (bitstampRate === 0 || currentTime >= (timestamp + delay)) { |
|||
timestamp = currentTime; |
|||
|
|||
_request('https://www.bitstamp.net/api/ticker/', function(err, data) { |
|||
if (!err) bitstampRate = parseFloat(JSON.parse(data).last); |
|||
|
|||
res.jsonp({ |
|||
status: 200, |
|||
data: { bitstamp: bitstampRate } |
|||
}); |
|||
}); |
|||
} else { |
|||
res.jsonp({ |
|||
status: 200, |
|||
data: { bitstamp: bitstampRate } |
|||
}); |
|||
} |
|||
}; |
@ -1,11 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
exports.render = function(req, res) { |
|||
res.render('index'); |
|||
}; |
|||
|
|||
exports.version = function(req, res) { |
|||
var pjson = require('../../package.json'); |
|||
res.json({version: pjson.version}); |
|||
}; |
|||
|
@ -1,57 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
// server-side socket behaviour
|
|||
// io is a variable already taken in express
|
|||
var ios = null; |
|||
var util = require('bitcore/util/util'); |
|||
|
|||
module.exports.init = function(app, io_ext) { |
|||
ios = io_ext; |
|||
ios.set('log level', 1); // reduce logging
|
|||
ios.sockets.on('connection', function(socket) { |
|||
socket.on('subscribe', function(topic) { |
|||
socket.join(topic); |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
module.exports.broadcastTx = function(tx) { |
|||
if (ios) { |
|||
var t; |
|||
if (typeof tx === 'string') { |
|||
t = { |
|||
txid: tx |
|||
}; |
|||
} |
|||
|
|||
else { |
|||
t = { |
|||
txid: tx.txid, |
|||
size: tx.size, |
|||
}; |
|||
// Outputs
|
|||
var valueOut = 0; |
|||
tx.vout.forEach(function(o) { |
|||
valueOut += o.value * util.COIN; |
|||
}); |
|||
|
|||
t.valueOut = parseInt(valueOut) / util.COIN; |
|||
} |
|||
ios.sockets. in ('inv').emit('tx', t); |
|||
} |
|||
}; |
|||
|
|||
module.exports.broadcastBlock = function(block) { |
|||
if (ios) ios.sockets. in ('inv').emit('block', block); |
|||
}; |
|||
|
|||
module.exports.broadcastAddressTx = function(address, tx) { |
|||
if (ios) ios.sockets. in (address).emit(address, tx); |
|||
}; |
|||
|
|||
module.exports.broadcastSyncInfo = function(historicSync) { |
|||
|
|||
if (ios) { |
|||
ios.sockets. in ('sync').emit('status', historicSync); |
|||
} |
|||
}; |
@ -1,59 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
/** |
|||
* Module dependencies. |
|||
*/ |
|||
|
|||
var Status = require('../models/Status'), |
|||
common = require('./common'); |
|||
|
|||
/** |
|||
* Status |
|||
*/ |
|||
exports.show = function(req, res) { |
|||
|
|||
if (! req.query.q) { |
|||
res.status(400).send('Bad Request'); |
|||
} |
|||
else { |
|||
var option = req.query.q; |
|||
var statusObject = Status.new(); |
|||
|
|||
var returnJsonp = function (err) { |
|||
if (err || ! statusObject) |
|||
return common.handleErrors(err, res); |
|||
else { |
|||
res.jsonp(statusObject); |
|||
} |
|||
}; |
|||
|
|||
switch(option) { |
|||
case 'getInfo': |
|||
statusObject.getInfo(returnJsonp); |
|||
break; |
|||
case 'getDifficulty': |
|||
statusObject.getDifficulty(returnJsonp); |
|||
break; |
|||
case 'getTxOutSetInfo': |
|||
statusObject.getTxOutSetInfo(returnJsonp); |
|||
break; |
|||
case 'getLastBlockHash': |
|||
statusObject.getLastBlockHash(returnJsonp); |
|||
break; |
|||
default: |
|||
res.status(400).send('Bad Request'); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
exports.sync = function(req, res) { |
|||
if (req.historicSync) |
|||
res.jsonp(req.historicSync.info()); |
|||
}; |
|||
|
|||
exports.peer = function(req, res) { |
|||
if (req.peerSync) { |
|||
var info = req.peerSync.info(); |
|||
res.jsonp(info); |
|||
} |
|||
}; |
@ -1,145 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
/** |
|||
* Module dependencies. |
|||
*/ |
|||
var Address = require('../models/Address'); |
|||
var async = require('async'); |
|||
var common = require('./common'); |
|||
|
|||
var TransactionDb = require('../../lib/TransactionDb').class(); |
|||
var BlockDb = require('../../lib/BlockDb').class(); |
|||
|
|||
var tDb = new TransactionDb(); |
|||
var bdb = new BlockDb(); |
|||
|
|||
|
|||
/** |
|||
* Find transaction by hash ... |
|||
*/ |
|||
exports.transaction = function(req, res, next, txid) { |
|||
|
|||
tDb.fromIdWithInfo(txid, function(err, tx) { |
|||
if (err || ! tx) |
|||
return common.handleErrors(err, res); |
|||
else { |
|||
req.transaction = tx.info; |
|||
return next(); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
|
|||
/** |
|||
* Show transaction |
|||
*/ |
|||
exports.show = function(req, res) { |
|||
|
|||
if (req.transaction) { |
|||
res.jsonp(req.transaction); |
|||
} |
|||
}; |
|||
|
|||
|
|||
var getTransaction = function(txid, cb) { |
|||
|
|||
tDb.fromIdWithInfo(txid, function(err, tx) { |
|||
if (err) console.log(err); |
|||
|
|||
if (!tx || !tx.info) { |
|||
console.log('[transactions.js.48]:: TXid %s not found in RPC. CHECK THIS.', txid); |
|||
return ({ txid: txid }); |
|||
} |
|||
|
|||
return cb(null, tx.info); |
|||
}); |
|||
}; |
|||
|
|||
|
|||
/** |
|||
* List of transaction |
|||
*/ |
|||
exports.list = function(req, res, next) { |
|||
var bId = req.query.block; |
|||
var addrStr = req.query.address; |
|||
var page = req.query.pageNum; |
|||
var pageLength = 10; |
|||
var pagesTotal = 1; |
|||
var txLength; |
|||
var txs; |
|||
|
|||
if (bId) { |
|||
bdb.fromHashWithInfo(bId, function(err, block) { |
|||
if (err) { |
|||
console.log(err); |
|||
return res.status(500).send('Internal Server Error'); |
|||
} |
|||
|
|||
if (! block) { |
|||
return res.status(404).send('Not found'); |
|||
} |
|||
|
|||
txLength = block.info.tx.length; |
|||
|
|||
if (page) { |
|||
var spliceInit = page * pageLength; |
|||
txs = block.info.tx.splice(spliceInit, pageLength); |
|||
pagesTotal = Math.ceil(txLength / pageLength); |
|||
} |
|||
else { |
|||
txs = block.info.tx; |
|||
} |
|||
|
|||
async.mapSeries(txs, getTransaction, function(err, results) { |
|||
if (err) { |
|||
console.log(err); |
|||
res.status(404).send('TX not found'); |
|||
} |
|||
|
|||
res.jsonp({ |
|||
pagesTotal: pagesTotal, |
|||
txs: results |
|||
}); |
|||
}); |
|||
}); |
|||
} |
|||
else if (addrStr) { |
|||
var a = Address.new(addrStr); |
|||
|
|||
a.update(function(err) { |
|||
if (err && !a.totalReceivedSat) { |
|||
console.log(err); |
|||
res.status(404).send('Invalid address'); |
|||
return next(); |
|||
} |
|||
|
|||
txLength = a.transactions.length; |
|||
|
|||
if (page) { |
|||
var spliceInit = page * pageLength; |
|||
txs = a.transactions.splice(spliceInit, pageLength); |
|||
pagesTotal = Math.ceil(txLength / pageLength); |
|||
} |
|||
else { |
|||
txs = a.transactions; |
|||
} |
|||
|
|||
async.mapSeries(txs, getTransaction, function(err, results) { |
|||
if (err) { |
|||
console.log(err); |
|||
res.status(404).send('TX not found'); |
|||
} |
|||
|
|||
res.jsonp({ |
|||
pagesTotal: pagesTotal, |
|||
txs: results |
|||
}); |
|||
}); |
|||
}); |
|||
} |
|||
else { |
|||
res.jsonp({ |
|||
txs: [] |
|||
}); |
|||
} |
|||
}; |
@ -1,194 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
require('classtool'); |
|||
|
|||
|
|||
function spec() { |
|||
var async = require('async'); |
|||
var BitcoreAddress = require('bitcore/Address').class(); |
|||
var BitcoreUtil = require('bitcore/util/util'); |
|||
var TransactionDb = require('../../lib/TransactionDb').class(); |
|||
var BitcoreTransaction = require('bitcore/Transaction').class(); |
|||
var Parser = require('bitcore/util/BinaryParser').class(); |
|||
var Buffer = require('buffer').Buffer; |
|||
var CONCURRENCY = 5; |
|||
|
|||
function Address(addrStr) { |
|||
this.balanceSat = 0; |
|||
this.totalReceivedSat = 0; |
|||
this.totalSentSat = 0; |
|||
|
|||
this.unconfirmedBalanceSat = 0; |
|||
|
|||
this.txApperances = 0; |
|||
this.unconfirmedTxApperances= 0; |
|||
|
|||
// TODO store only txids? +index? +all?
|
|||
this.transactions = []; |
|||
|
|||
var a = new BitcoreAddress(addrStr); |
|||
a.validate(); |
|||
this.addrStr = addrStr; |
|||
|
|||
Object.defineProperty(this, 'totalSent', { |
|||
get: function() { |
|||
return parseFloat(this.totalSentSat) / parseFloat(BitcoreUtil.COIN); |
|||
}, |
|||
set: function(i) { |
|||
this.totalSentSat = i * BitcoreUtil.COIN; |
|||
}, |
|||
enumerable: 1, |
|||
}); |
|||
|
|||
Object.defineProperty(this, 'balance', { |
|||
get: function() { |
|||
return parseFloat(this.balanceSat) / parseFloat(BitcoreUtil.COIN); |
|||
}, |
|||
set: function(i) { |
|||
this.balance = i * BitcoreUtil.COIN; |
|||
}, |
|||
enumerable: 1, |
|||
}); |
|||
|
|||
Object.defineProperty(this, 'totalReceived', { |
|||
get: function() { |
|||
return parseFloat(this.totalReceivedSat) / parseFloat(BitcoreUtil.COIN); |
|||
}, |
|||
set: function(i) { |
|||
this.totalReceived = i * BitcoreUtil.COIN; |
|||
}, |
|||
enumerable: 1, |
|||
}); |
|||
|
|||
|
|||
Object.defineProperty(this, 'unconfirmedBalance', { |
|||
get: function() { |
|||
return parseFloat(this.unconfirmedBalanceSat) / parseFloat(BitcoreUtil.COIN); |
|||
}, |
|||
set: function(i) { |
|||
this.unconfirmedBalanceSat = i * BitcoreUtil.COIN; |
|||
}, |
|||
enumerable: 1, |
|||
}); |
|||
|
|||
} |
|||
|
|||
Address.prototype._getScriptPubKey = function(hex,n) { |
|||
// ScriptPubKey is not provided by bitcoind RPC, so we parse it from tx hex.
|
|||
|
|||
var parser = new Parser(new Buffer(hex,'hex')); |
|||
var tx = new BitcoreTransaction(); |
|||
tx.parse(parser); |
|||
return (tx.outs[n].s.toString('hex')); |
|||
}; |
|||
|
|||
Address.prototype.getUtxo = function(next) { |
|||
var self = this; |
|||
if (!self.addrStr) return next(); |
|||
|
|||
var ret = []; |
|||
var db = new TransactionDb(); |
|||
|
|||
db.fromAddr(self.addrStr, function(err,txOut){ |
|||
if (err) return next(err); |
|||
|
|||
// Complete utxo info
|
|||
async.eachLimit(txOut,CONCURRENCY,function (txItem, a_c) { |
|||
db.fromIdInfoSimple(txItem.txid, function(err, info) { |
|||
|
|||
var scriptPubKey = self._getScriptPubKey(info.hex, txItem.index); |
|||
|
|||
// we are filtering out even unconfirmed spents!
|
|||
// add || !txItem.spentIsConfirmed
|
|||
if (!txItem.spentTxId) { |
|||
ret.push({ |
|||
address: self.addrStr, |
|||
txid: txItem.txid, |
|||
vout: txItem.index, |
|||
ts: txItem.ts, |
|||
scriptPubKey: scriptPubKey, |
|||
amount: txItem.value_sat / BitcoreUtil.COIN, |
|||
confirmations: txItem.isConfirmed ? info.confirmations : 0, |
|||
}); |
|||
} |
|||
return a_c(err); |
|||
}); |
|||
}, function(err) { |
|||
return next(err,ret); |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
Address.prototype.update = function(next) { |
|||
var self = this; |
|||
if (!self.addrStr) return next(); |
|||
|
|||
var txs = []; |
|||
var db = new TransactionDb(); |
|||
async.series([ |
|||
function (cb) { |
|||
var seen={}; |
|||
db.fromAddr(self.addrStr, function(err,txOut){ |
|||
if (err) return cb(err); |
|||
txOut.forEach(function(txItem){ |
|||
var add=0, addSpend=0; |
|||
var v = txItem.value_sat; |
|||
|
|||
if ( !seen[txItem.txid] ) { |
|||
txs.push({txid: txItem.txid, ts: txItem.ts}); |
|||
seen[txItem.txid]=1; |
|||
add=1; |
|||
} |
|||
|
|||
if (txItem.spentTxId && !seen[txItem.spentTxId] ) { |
|||
txs.push({txid: txItem.spentTxId, ts: txItem.spentTs}); |
|||
seen[txItem.spentTxId]=1; |
|||
addSpend=1; |
|||
} |
|||
|
|||
if (txItem.isConfirmed) { |
|||
self.txApperances += add; |
|||
self.totalReceivedSat += v; |
|||
if (! txItem.spentTxId ) { |
|||
//unspent
|
|||
self.balanceSat += v; |
|||
} |
|||
else if(!txItem.spentIsConfirmed) { |
|||
// unspent
|
|||
self.balanceSat += v; |
|||
self.unconfirmedBalanceSat -= v; |
|||
self.unconfirmedTxApperances += addSpend; |
|||
} |
|||
else { |
|||
// spent
|
|||
self.totalSentSat += v; |
|||
self.txApperances += addSpend; |
|||
} |
|||
} |
|||
else { |
|||
self.unconfirmedBalanceSat += v; |
|||
self.unconfirmedTxApperances += add; |
|||
} |
|||
}); |
|||
return cb(); |
|||
}); |
|||
}, |
|||
], function (err) { |
|||
|
|||
// sort input and outputs togheter
|
|||
txs.sort( |
|||
function compare(a,b) { |
|||
if (a.ts < b.ts) return 1; |
|||
if (a.ts > b.ts) return -1; |
|||
return 0; |
|||
}); |
|||
|
|||
self.transactions = txs.map(function(i) { return i.txid; } ); |
|||
return next(err); |
|||
}); |
|||
}; |
|||
|
|||
return Address; |
|||
} |
|||
module.defineClass(spec); |
|||
|
@ -1,112 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
require('classtool'); |
|||
|
|||
function spec() { |
|||
var async = require('async'); |
|||
var RpcClient = require('bitcore/RpcClient').class(); |
|||
var BlockDb = require('../../lib/BlockDb').class(); |
|||
var config = require('../../config/config'); |
|||
var rpc = new RpcClient(config.bitcoind); |
|||
|
|||
function Status() { |
|||
this.bDb = new BlockDb(); |
|||
} |
|||
|
|||
Status.prototype.getInfo = function(next) { |
|||
var that = this; |
|||
async.series([ |
|||
function (cb) { |
|||
rpc.getInfo(function(err, info){ |
|||
if (err) return cb(err); |
|||
|
|||
that.info = info.result; |
|||
return cb(); |
|||
}); |
|||
}, |
|||
], function (err) { |
|||
return next(err); |
|||
}); |
|||
}; |
|||
|
|||
Status.prototype.getDifficulty = function(next) { |
|||
var that = this; |
|||
async.series([ |
|||
function (cb) { |
|||
rpc.getDifficulty(function(err, df){ |
|||
if (err) return cb(err); |
|||
|
|||
that.difficulty = df.result; |
|||
return cb(); |
|||
}); |
|||
} |
|||
], function (err) { |
|||
return next(err); |
|||
}); |
|||
}; |
|||
|
|||
Status.prototype.getTxOutSetInfo = function(next) { |
|||
var that = this; |
|||
async.series([ |
|||
function (cb) { |
|||
rpc.getTxOutSetInfo(function(err, txout){ |
|||
if (err) return cb(err); |
|||
|
|||
that.txoutsetinfo = txout.result; |
|||
return cb(); |
|||
}); |
|||
} |
|||
], function (err) { |
|||
return next(err); |
|||
}); |
|||
}; |
|||
|
|||
Status.prototype.getBestBlockHash = function(next) { |
|||
var that = this; |
|||
async.series([ |
|||
function (cb) { |
|||
rpc.getBestBlockHash(function(err, bbh){ |
|||
if (err) return cb(err); |
|||
|
|||
that.bestblockhash = bbh.result; |
|||
return cb(); |
|||
}); |
|||
}, |
|||
|
|||
], function (err) { |
|||
return next(err); |
|||
}); |
|||
}; |
|||
|
|||
Status.prototype.getLastBlockHash = function(next) { |
|||
var that = this; |
|||
that.bDb.getTip(function(err,tip) { |
|||
that.syncTipHash = tip; |
|||
async.waterfall( |
|||
[ |
|||
function(callback){ |
|||
rpc.getBlockCount(function(err, bc){ |
|||
if (err) return callback(err); |
|||
callback(null, bc.result); |
|||
}); |
|||
}, |
|||
function(bc, callback){ |
|||
rpc.getBlockHash(bc, function(err, bh){ |
|||
if (err) return callback(err); |
|||
callback(null, bh.result); |
|||
}); |
|||
} |
|||
], |
|||
function (err, result) { |
|||
that.lastblockhash = result; |
|||
return next(); |
|||
} |
|||
); |
|||
}); |
|||
}; |
|||
|
|||
return Status; |
|||
|
|||
} |
|||
module.defineClass(spec); |
|||
|
@ -1,12 +0,0 @@ |
|||
extends layouts/default |
|||
|
|||
block main |
|||
h1 Oops something went wrong |
|||
br |
|||
span 404 |
|||
|
|||
block content |
|||
#error-message-box |
|||
#error-stack-trace |
|||
pre |
|||
code!= error |
@ -1,12 +0,0 @@ |
|||
extends layouts/default |
|||
|
|||
block main |
|||
h1 Oops something went wrong |
|||
br |
|||
span 500 |
|||
|
|||
block content |
|||
#error-message-box |
|||
#error-stack-trace |
|||
pre |
|||
code!= error |
@ -1,7 +0,0 @@ |
|||
#footer(data-ng-include="'/views/includes/footer.html'", role='navigation') |
|||
|
|||
script(type='text/javascript', src='/socket.io/socket.io.js') |
|||
script(type='text/javascript', src='/js/vendors.min.js') |
|||
script(type='text/javascript', src='/js/angularjs-all.min.js') |
|||
script(type='text/javascript', src='/js/main.min.js') |
|||
|
@ -1,22 +0,0 @@ |
|||
head |
|||
meta(charset='utf-8') |
|||
meta(http-equiv='X-UA-Compatible', content='IE=edge,chrome=1') |
|||
meta(name='viewport', content='width=device-width,initial-scale=1.0') |
|||
meta(name="fragment", content="!") |
|||
|
|||
title(data-ng-bind="$root.title + $root.titleDetail + ' | #{appName}'")= appName |
|||
meta(http-equiv='Content-type', content='text/html;charset=UTF-8') |
|||
meta(name="keywords", content="bitcoins, transactions, blocks, address, block chain, best block, mining difficulty, hash serialized") |
|||
meta(name="description", content="Bitcoin Insight. View detailed information on all bitcoin transactions and block. {{ $root.title + $root.titleDetail }}") |
|||
|
|||
link(rel='shortcut icon', href='/img/icons/favicon.ico', type='image/x-icon') |
|||
|
|||
link(rel='stylesheet', href='//fonts.googleapis.com/css?family=Ubuntu:300,400,500,700,400italic') |
|||
link(rel='stylesheet', href='/lib/bootstrap/dist/css/bootstrap.min.css') |
|||
link(rel='stylesheet', href='/css/main.min.css') |
|||
|
|||
if (config.keys.segmentio) |
|||
script(type='text/javascript'). |
|||
window.analytics||(window.analytics=[]),window.analytics.methods=['identify','track','trackLink','trackForm','trackClick','trackSubmit','page','pageview','ab','alias','ready','group','on','once','off'],window.analytics.factory=function(t){return function(){var a=Array.prototype.slice.call(arguments);return a.unshift(t),window.analytics.push(a),window.analytics}};for(var i=0;i<window.analytics.methods.length;i++){var method=window.analytics.methods[i];window.analytics[method]=window.analytics.factory(method)}window.analytics.load=function(t){var a=document.createElement('script');a.type='text/javascript',a.async=!0,a.src=('https:'===document.location.protocol?'https://':'http://')+'d2dq2ahtl5zl1z.cloudfront.net/analytics.js/v1/'+t+'/analytics.min.js';var n=document.getElementsByTagName('script')[0];n.parentNode.insertBefore(a,n)},window.analytics.SNIPPET_VERSION='2.0.8', |
|||
window.analytics.load('#{config.keys.segmentio}'); |
|||
window.analytics.page(); |
@ -1 +0,0 @@ |
|||
.navbar.navbar-default.navbar-fixed-top(data-ng-include="'/views/includes/header.html'", role='navigation') |
@ -1,4 +0,0 @@ |
|||
extends layouts/default |
|||
|
|||
block content |
|||
section.container(data-ng-view) |
@ -1,8 +0,0 @@ |
|||
doctype |
|||
html(lang='en', data-ng-app='insight' data-ng-csp) |
|||
include ../includes/head |
|||
body |
|||
#wrap |
|||
include ../includes/navbar |
|||
block content |
|||
include ../includes/foot |
@ -1,75 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
var path = require('path'), |
|||
rootPath = path.normalize(__dirname + '/..'), |
|||
env, |
|||
db, |
|||
port, |
|||
b_port, |
|||
p2p_port; |
|||
|
|||
if (process.env.INSIGHT_NETWORK === 'livenet') { |
|||
env = 'livenet'; |
|||
db = './db'; |
|||
port = '3000'; |
|||
b_port = '8332'; |
|||
p2p_port = '8333'; |
|||
} |
|||
else { |
|||
env = 'testnet'; |
|||
db = './db/testnet', |
|||
port = '3001', |
|||
b_port = '18332', |
|||
p2p_port = '18333'; |
|||
} |
|||
|
|||
switch(process.env.NODE_ENV) { |
|||
case 'production': |
|||
env += ''; |
|||
break; |
|||
case 'test': |
|||
env += ' - test environment'; |
|||
break; |
|||
default: |
|||
env += ' - development'; |
|||
break; |
|||
} |
|||
|
|||
var dataDir = process.env.BITCOIND_DATADIR; |
|||
var isWin = /^win/.test(process.platform); |
|||
var isMac = /^darwin/.test(process.platform); |
|||
var isLinux = /^linux/.test(process.platform); |
|||
if (!dataDir) { |
|||
if (isWin) dataDir = '%APPDATA%\\Bitcoin\\'; |
|||
if (isMac) dataDir = process.env.HOME + '/Library/Application Support/Bitcoin/'; |
|||
if (isLinux) dataDir = process.env.HOME + '/.bitcoin/'; |
|||
} |
|||
dataDir += ((process.env.INSIGHT_NETWORK || 'testnet')==='testnet'?'testnet3':''); |
|||
|
|||
module.exports = { |
|||
root: rootPath, |
|||
appName: 'Insight ' + env, |
|||
port: port, |
|||
leveldb: db, |
|||
bitcoind: { |
|||
protocol: process.env.BITCOIND_PROTO || 'http', |
|||
user: process.env.BITCOIND_USER || 'user', |
|||
pass: process.env.BITCOIND_PASS || 'pass', |
|||
host: process.env.BITCOIND_HOST || '127.0.0.1', |
|||
port: process.env.BITCOIND_PORT || b_port, |
|||
p2pPort: process.env.BITCOIND_P2P_PORT || p2p_port, |
|||
dataDir: dataDir, |
|||
// DO NOT CHANGE THIS!
|
|||
disableAgent: true |
|||
}, |
|||
network: process.env.INSIGHT_NETWORK || 'testnet', |
|||
disableP2pSync: false, |
|||
disableHistoricSync: false, |
|||
poolMatchFile: './etc/minersPoolStrings.json', |
|||
|
|||
// Time to refresh the currency rate. In minutes
|
|||
currencyRefresh: 10 |
|||
, keys: { |
|||
segmentio: process.env.INSIGHT_SEGMENTIO_KEY |
|||
} |
|||
}; |
@ -1,78 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
/** |
|||
* Module dependencies. |
|||
*/ |
|||
var express = require('express'), |
|||
helpers = require('view-helpers'), |
|||
config = require('./config'); |
|||
|
|||
module.exports = function(app, historicSync, peerSync) { |
|||
|
|||
//custom middleware
|
|||
function setHistoric(req, res, next) { |
|||
req.historicSync = historicSync; |
|||
next(); |
|||
} |
|||
function setPeer(req, res, next) { |
|||
req.peerSync = peerSync; |
|||
next(); |
|||
} |
|||
|
|||
app.set('showStackError', true); |
|||
|
|||
//Set views path, template engine and default layout
|
|||
app.set('views', config.root + '/app/views'); |
|||
app.set('view engine', 'jade'); |
|||
|
|||
// Compress JSON outputs
|
|||
app.set('json spaces', 0); |
|||
|
|||
//Enable jsonp
|
|||
app.enable('jsonp callback'); |
|||
|
|||
app.use('/api/sync', setHistoric); |
|||
app.use('/api/peer', setPeer); |
|||
app.use(express.logger('dev')); |
|||
app.use(express.json()); |
|||
app.use(express.urlencoded()); |
|||
app.use(express.methodOverride()); |
|||
app.use(express.compress()); |
|||
|
|||
// IMPORTANT: for html5mode, this line must to be before app.router
|
|||
app.use(express.static(config.root + '/public')); |
|||
|
|||
//dynamic helpers
|
|||
app.use(helpers(config.appName)); |
|||
|
|||
// manual helpers
|
|||
app.use(function(req, res, next) { |
|||
app.locals.config = config; |
|||
next(); |
|||
}); |
|||
|
|||
//routes should be at the last
|
|||
app.use(app.router); |
|||
|
|||
//Assume "not found" in the error msgs is a 404. this is somewhat silly, but valid, you can do whatever you like, set properties, use instanceof etc.
|
|||
app.use(function(err, req, res, next) { |
|||
//Treat as 404
|
|||
if (~err.message.indexOf('not found')) return next(); |
|||
|
|||
//Log it
|
|||
console.error(err.stack); |
|||
|
|||
//Error page
|
|||
res.status(500).render('500', { |
|||
error: err.stack |
|||
}); |
|||
}); |
|||
|
|||
//Assume 404 since no middleware responded
|
|||
app.use(function(req, res) { |
|||
res.status(404).render('404', { |
|||
url: req.originalUrl, |
|||
error: 'Not found' |
|||
}); |
|||
}); |
|||
}; |
@ -1,42 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
module.exports = function(app) { |
|||
|
|||
//Block routes
|
|||
var blocks = require('../app/controllers/blocks'); |
|||
app.get('/api/blocks', blocks.list); |
|||
|
|||
|
|||
app.get('/api/block/:blockHash', blocks.show); |
|||
app.param('blockHash', blocks.block); |
|||
|
|||
app.get('/api/block-index/:height', blocks.blockindex); |
|||
app.param('height', blocks.blockindex); |
|||
|
|||
// Transaction routes
|
|||
var transactions = require('../app/controllers/transactions'); |
|||
app.get('/api/tx/:txid', transactions.show); |
|||
app.param('txid', transactions.transaction); |
|||
app.get('/api/txs', transactions.list); |
|||
|
|||
// Address routes
|
|||
var addresses = require('../app/controllers/addresses'); |
|||
app.get('/api/addr/:addr', addresses.show); |
|||
app.get('/api/addr/:addr/utxo', addresses.utxo); |
|||
|
|||
// Status route
|
|||
var st = require('../app/controllers/status'); |
|||
app.get('/api/status', st.show); |
|||
|
|||
app.get('/api/sync', st.sync); |
|||
app.get('/api/peer', st.peer); |
|||
|
|||
// Currency
|
|||
var currency = require('../app/controllers/currency'); |
|||
app.get('/api/currency', currency.index); |
|||
|
|||
//Home route
|
|||
var index = require('../app/controllers/index'); |
|||
app.get('/api/version', index.version); |
|||
app.get('*', index.render); |
|||
}; |
@ -1,25 +0,0 @@ |
|||
#!/usr/bin/env node
|
|||
|
|||
var |
|||
config = require('../config/config'), |
|||
levelup = require('levelup'); |
|||
|
|||
|
|||
db = levelup(config.leveldb + '/blocks'); |
|||
|
|||
db.createReadStream({start: 'b-'}) |
|||
.on('data', function (data) { |
|||
console.log('[block-level.js.11:data:]',data); //TODO
|
|||
if (data==false) c++; |
|||
}) |
|||
.on('error', function (err) { |
|||
return cb(err); |
|||
}) |
|||
.on('close', function () { |
|||
return cb(null); |
|||
}) |
|||
.on('end', function () { |
|||
return cb(null); |
|||
}); |
|||
|
|||
|
@ -1,41 +0,0 @@ |
|||
#!/usr/bin/env node
|
|||
'use strict'; |
|||
|
|||
var util = require('util'); |
|||
var mongoose= require('mongoose'), |
|||
config = require('../config/config'); |
|||
|
|||
process.env.NODE_ENV = process.env.NODE_ENV || 'development'; |
|||
|
|||
var T = require('../app/models/TransactionOut'); |
|||
|
|||
|
|||
// var hash = process.argv[2] || '0000000000b6288775bbd326bedf324ca8717a15191da58391535408205aada4';
|
|||
var hash = process.argv[2] || '6749762ae220c10705556799dcec9bb6a54a7b881eb4b961323a3363b00db518'; |
|||
|
|||
|
|||
|
|||
|
|||
mongoose.connect(config.db); |
|||
|
|||
mongoose.connection.on('error', function(err) { console.log(err); }); |
|||
|
|||
|
|||
mongoose.connection.on('open', function() { |
|||
|
|||
var b = new Buffer(hash,'hex'); |
|||
|
|||
T.createFromTxs([hash], function(err, ret) { |
|||
|
|||
console.log('Err:'); |
|||
console.log(err); |
|||
|
|||
|
|||
console.log('Ret:'); |
|||
console.log(util.inspect(ret,{depth:null})); |
|||
mongoose.connection.close(); |
|||
}); |
|||
}); |
|||
|
|||
|
|||
|
@ -1,33 +0,0 @@ |
|||
#!/bin/bash |
|||
|
|||
FIND='find'; |
|||
|
|||
##if [[ "$OSTYPE" =~ "darwin" ]] |
|||
##then |
|||
## FIND='gfind' |
|||
##fi |
|||
|
|||
|
|||
if [ -z "$1" ] |
|||
then |
|||
echo "$0 : find functions references " |
|||
echo "Usage $0 function_name " |
|||
exit; |
|||
fi |
|||
|
|||
EXTRA='' |
|||
|
|||
|
|||
CMD="grep -rnH" |
|||
|
|||
if [ "$2" != '--nocolor' ] |
|||
then |
|||
CMD="$CMD --color=always" |
|||
fi |
|||
|
|||
|
|||
$FIND -L . -name \*.json -not -wholename \*node_modules\* -not -wholename \*public/lib\* -exec $CMD "$1" {} + \ |
|||
-o -name \*.html -not -wholename \*node_modules\* -not -wholename \*public/lib\* -exec $CMD "$1" {} + \ |
|||
-o -name \*.jade -not -wholename \*node_modules\* -not -wholename \*public/lib\* -exec $CMD "$1" {} + \ |
|||
-o -name \*.js -not -wholename \*node_modules\* -not -wholename \*public/lib\* -exec $CMD "$1" {} + |
|||
|
@ -1,31 +0,0 @@ |
|||
#!/usr/bin/env node
|
|||
'use strict'; |
|||
|
|||
var util = require('util'); |
|||
|
|||
process.env.NODE_ENV = process.env.NODE_ENV || 'development'; |
|||
|
|||
var RpcClient = require('../node_modules/bitcore/RpcClient').class(); |
|||
|
|||
var config = require('../config/config'); |
|||
|
|||
|
|||
var hash = process.argv[2] || '0000000000b6288775bbd326bedf324ca8717a15191da58391535408205aada4'; |
|||
//var hash = process.argv[2] || 'f6c2901f39fd07f2f2e503183d76f73ecc1aee9ac9216fde58e867bc29ce674e';
|
|||
|
|||
//hash = 'e2253359458db3e732c82a43fc62f56979ff59928f25a2df34dfa443e9a41160';
|
|||
|
|||
var rpc = new RpcClient(config.bitcoind); |
|||
|
|||
rpc.getBlock( hash, function(err, ret) { |
|||
|
|||
console.log('Err:'); |
|||
console.log(err); |
|||
|
|||
|
|||
console.log('Ret:'); |
|||
console.log(util.inspect(ret, { depth: 10} )); |
|||
}); |
|||
|
|||
|
|||
|
@ -1,41 +0,0 @@ |
|||
#!/usr/bin/env node
|
|||
'use strict'; |
|||
|
|||
var util = require('util'); |
|||
var mongoose= require('mongoose'), |
|||
config = require('../config/config'); |
|||
|
|||
process.env.NODE_ENV = process.env.NODE_ENV || 'development'; |
|||
|
|||
var T = require('../app/models/TransactionOut'); |
|||
|
|||
|
|||
// var hash = process.argv[2] || '0000000000b6288775bbd326bedf324ca8717a15191da58391535408205aada4';
|
|||
var hash = process.argv[2] || 'e2253359458db3e732c82a43fc62f56979ff59928f25a2df34dfa443e9a41160'; |
|||
|
|||
|
|||
|
|||
|
|||
mongoose.connect(config.db); |
|||
|
|||
mongoose.connection.on('error', function(err) { console.log(err); }); |
|||
|
|||
|
|||
mongoose.connection.on('open', function() { |
|||
|
|||
var b = new Buffer(hash,'hex'); |
|||
|
|||
T.find({txidBuf: b}, function(err, ret) { |
|||
|
|||
console.log('Err:'); |
|||
console.log(err); |
|||
|
|||
|
|||
console.log('Ret:'); |
|||
console.log(util.inspect(ret,{depth:null})); |
|||
mongoose.connection.close(); |
|||
}); |
|||
}); |
|||
|
|||
|
|||
|
@ -1,39 +0,0 @@ |
|||
#!/usr/bin/env node
|
|||
'use strict'; |
|||
|
|||
var util = require('util'); |
|||
var mongoose= require('mongoose'), |
|||
config = require('../config/config'); |
|||
|
|||
process.env.NODE_ENV = process.env.NODE_ENV || 'development'; |
|||
|
|||
var T = require('../app/models/TransactionOut'); |
|||
|
|||
|
|||
// var hash = process.argv[2] || '0000000000b6288775bbd326bedf324ca8717a15191da58391535408205aada4';
|
|||
var hash = process.argv[2] || 'mp3Rzxx9s1A21SY3sjJ3CQoa2Xjph7e5eS'; |
|||
|
|||
|
|||
|
|||
|
|||
mongoose.connect(config.db); |
|||
|
|||
mongoose.connection.on('error', function(err) { console.log(err); }); |
|||
|
|||
|
|||
mongoose.connection.on('open', function() { |
|||
|
|||
T.find({addr: hash}, function(err, ret) { |
|||
|
|||
console.log('Err:'); |
|||
console.log(err); |
|||
|
|||
|
|||
console.log('Ret:'); |
|||
console.log(util.inspect(ret,{depth:null})); |
|||
mongoose.connection.close(); |
|||
}); |
|||
}); |
|||
|
|||
|
|||
|
@ -1,27 +0,0 @@ |
|||
#!/usr/bin/env node
|
|||
'use strict'; |
|||
|
|||
var util = require('util'); |
|||
var T = require('../lib/TransactionDb').class(); |
|||
|
|||
process.env.NODE_ENV = process.env.NODE_ENV || 'development'; |
|||
|
|||
|
|||
// var hash = process.argv[2] || '0000000000b6288775bbd326bedf324ca8717a15191da58391535408205aada4';
|
|||
var hash = process.argv[2] || 'e2253359458db3e732c82a43fc62f56979ff59928f25a2df34dfa443e9a41160'; |
|||
|
|||
|
|||
|
|||
var t = new T(); |
|||
t.fromIdWithInfo(hash, function(err, ret) { |
|||
|
|||
console.log('Err:'); |
|||
console.log(err); |
|||
|
|||
|
|||
console.log('Ret:'); |
|||
console.log(util.inspect(ret,{depth:null})); |
|||
}); |
|||
|
|||
|
|||
|
@ -1,34 +0,0 @@ |
|||
#!/usr/bin/env node
|
|||
'use strict'; |
|||
|
|||
var config = require('../config/config'), |
|||
levelup = require('levelup'); |
|||
|
|||
|
|||
|
|||
var k = process.argv[2]; |
|||
var v = process.argv[3]; |
|||
var isBlock = process.argv[4] === '1'; |
|||
|
|||
|
|||
var dbPath = config.leveldb + (isBlock ? '/blocks' : '/txs'); |
|||
console.log('DB: ',dbPath); //TODO
|
|||
|
|||
|
|||
|
|||
var db = levelup(dbPath ); |
|||
|
|||
|
|||
if (v) { |
|||
db.put(k,v,function(err) { |
|||
console.log('[PUT done]',err); //TODO
|
|||
}); |
|||
} |
|||
else { |
|||
db.del(k,function(err) { |
|||
console.log('[DEL done]',err); //TODO
|
|||
}); |
|||
} |
|||
|
|||
|
|||
|
@ -1,30 +0,0 @@ |
|||
#!/usr/bin/env node
|
|||
'use strict'; |
|||
|
|||
var config = require('../config/config'), |
|||
levelup = require('levelup'); |
|||
|
|||
|
|||
|
|||
var s = process.argv[2]; |
|||
var isBlock = process.argv[3] === '1'; |
|||
|
|||
|
|||
var dbPath = config.leveldb + (isBlock ? '/blocks' : '/txs'); |
|||
console.log('DB: ',dbPath); //TODO
|
|||
|
|||
|
|||
|
|||
var db = levelup(dbPath ); |
|||
|
|||
|
|||
db.createReadStream({start: s, end: s+'~'}) |
|||
.on('data', function (data) { |
|||
console.log(data.key + ' => ' + data.value); //TODO
|
|||
}) |
|||
.on('error', function () { |
|||
}) |
|||
.on('end', function () { |
|||
}); |
|||
|
|||
|
@ -1,25 +0,0 @@ |
|||
#!/usr/bin/env node
|
|||
'use strict'; |
|||
|
|||
process.env.NODE_ENV = process.env.NODE_ENV || 'development'; |
|||
|
|||
var assert = require('assert'), |
|||
config = require('../config/config'), |
|||
BlockExtractor = require('../lib/BlockExtractor').class(), |
|||
networks = require('bitcore/networks'), |
|||
util = require('bitcore/util/util'); |
|||
|
|||
var be = new BlockExtractor(config.bitcoind.dataDir, config.network); |
|||
var network = config.network === 'testnet' ? networks.testnet: networks.livenet; |
|||
// console.log('[read_block.js.13]', be.nextFile() );
|
|||
|
|||
var c=0; |
|||
while (c++ < 100) { |
|||
be.getNextBlock(function(err, b) { |
|||
console.log('[read_block.js.14]',err, c, b?util.formatHashAlt(b.hash):''); //TODO
|
|||
}); |
|||
} |
|||
|
|||
|
|||
|
|||
|
@ -1,56 +0,0 @@ |
|||
|
|||
|
|||
first 5% |
|||
|
|||
=> with data + mongo + w/RPC for blocks: 48.8s |
|||
=> with RPC + mongo: 2m26s |
|||
=> with files + mongo + wo/RPC for blocks: 36.7s |
|||
=> with files + mongo + wo/RPC for blocks + wo/mongoIndexes: |
|||
|
|||
|
|||
first 10% |
|||
|
|||
=> sin RPC, sin Tx, sin store block => 0.7s |
|||
=> sin RPC, sin grabar, procesando TX => 8.5s |
|||
=> sin RPC, sin TX processing, sin grabar => 12s28 |
|||
=> con RPC, TX processing, sin Grabar Tx, grabando bloques => 29s |
|||
=> con RPC, sin TX processing, sin Grabar Tx, grabando bloques => 35s |
|||
=> con RPC, TX processing, sin Grabar Tx, grabando bloques => 43s |
|||
|
|||
=> TX processing, sin RPC, sin saves TX, y blocks => 11.6s |
|||
=> TX processing, CON RPC, sin saves TX, y blocks => 35s |
|||
=> con RPC, TX processing, sin saves TX => 45s |
|||
=> con RPC, TX processing, Grabarndo todo => 78s |
|||
=> con RPC, TX processing, Grabarndo todo => 78s |
|||
(18k blocks, 36k txouts) |
|||
|
|||
//LEVEL DB |
|||
=> sin RPC, TX processing, todo en level => 14s |
|||
=> con RPC, TX processing, todo en level => 39.7s |
|||
=> con RPC, TX processing, tx mongo, blocks en level => 64s |
|||
|
|||
|
|||
=> sin RPC, TX processing, todo en level, handling REORGs, more data => 28s |
|||
=> sin RPC, TX processing, todo en level, handling REORGs, more data, tx ts => 34t s |
|||
|
|||
|
|||
//FROM blk00002.dat (more txs), 5% |
|||
|
|||
=> now total : 1m13s |
|||
=> removing block writes => 1m8s |
|||
=> sacando los contenidos adentro de getblock from file de => 4.5s!! |
|||
|
|||
=> con base58 cpp => 21s |
|||
=> toda la testnet => 17m |
|||
|
|||
10% de blk2 |
|||
=> 50s con base58cpp |
|||
=> 41s commentando todo addr |
|||
=> 5s commentando todo get HistoricSync.prototype.getBlockFromFile = function(cb) { |
|||
=> 15s commentando todo get HistoricSync.prototype.getBlockFromFile = function(cb) { |
|||
|
|||
10% de blk 1 |
|||
=> 59s |
|||
=> 15s comentando desde b.getStandardizedObject() |
|||
=> 39s comentando dps b.getStandardizedObject() |
|||
|
@ -1,20 +0,0 @@ |
|||
#!/usr/bin/env node
|
|||
process.env.NODE_ENV = process.env.NODE_ENV || 'development'; |
|||
|
|||
var RpcClient = require('../node_modules/bitcore/RpcClient').class(); |
|||
|
|||
var config = require('../config/config'); |
|||
|
|||
var rpc = new RpcClient(config.bitcoind); |
|||
|
|||
var block = rpc.getInfo(function(err, block) { |
|||
if (err) { |
|||
console.log("Err:"); |
|||
console.log(err); |
|||
} |
|||
|
|||
console.log("Block info:"); |
|||
console.log(block); |
|||
}); |
|||
|
|||
|
@ -1,17 +0,0 @@ |
|||
#!/usr/bin/env node
|
|||
'use strict'; |
|||
|
|||
var Sync = require('../lib/Sync').class(); |
|||
|
|||
|
|||
var s = new Sync(); |
|||
|
|||
|
|||
s.setOrphan( |
|||
'0000000000c2b1e8dab92a72741289e5ef0d4f375fd1b26f729da2ba979c028a', |
|||
'000000000228f9d02654459e09998c7557afa9082784c11226853f5feb805df9', |
|||
function (err) { |
|||
console.log('[sync-level.js.15]',err); //TODO
|
|||
}); |
|||
|
|||
|
@ -1,10 +0,0 @@ |
|||
rpcuser=user |
|||
rpcpassword=pass |
|||
server=1 |
|||
txindex=1 |
|||
|
|||
# Allow connections outsite localhost? |
|||
rpcallowip=192.168.1.* |
|||
|
|||
rpcport=8332 |
|||
|
@ -1,10 +0,0 @@ |
|||
rpcuser=user |
|||
rpcpassword=pass |
|||
server=1 |
|||
txindex=1 |
|||
|
|||
# Allow connections outsite localhost? |
|||
rpcallowip=192.168.1.* |
|||
|
|||
rpcport=18332 |
|||
testnet=3 |
@ -1,178 +0,0 @@ |
|||
[ |
|||
{ |
|||
"poolName":"50BTC", |
|||
"url":"https://50btc.com/", |
|||
"searchStrings":[ |
|||
"50BTC.com", |
|||
"50btc.com" |
|||
] |
|||
}, |
|||
{ |
|||
"poolName":"175btc", |
|||
"url":"http://www.175btc.com/", |
|||
"searchStrings":[ |
|||
"Mined By 175btc.com" |
|||
] |
|||
}, |
|||
{ |
|||
"poolName":"ASICminer", |
|||
"url":"https://bitcointalk.org/index.php?topic=99497.0", |
|||
"searchStrings":[ |
|||
"Mined By ASICMiner" |
|||
] |
|||
}, |
|||
{ |
|||
"poolName":"BitMinter", |
|||
"url":"https://bitminter.com/", |
|||
"searchStrings":[ |
|||
"BitMinter" |
|||
] |
|||
}, |
|||
{ |
|||
"poolName":"Bitparking", |
|||
"url":"http://bitparking.com/", |
|||
"searchStrings":[ |
|||
"bitparking" |
|||
] |
|||
}, |
|||
{ |
|||
"poolName":"BTC Guild", |
|||
"url":"https://www.btcguild.com/", |
|||
"searchStrings":[ |
|||
"Mined by BTC Guild", |
|||
"BTC Guild" |
|||
] |
|||
}, |
|||
{ |
|||
"poolName":"Discus Fish", |
|||
"url":"http://f2pool.com/", |
|||
"searchStrings":[ |
|||
"七彩神仙鱼", |
|||
"Made in China", |
|||
"Mined by user" |
|||
] |
|||
}, |
|||
{ |
|||
"poolName":"Discus Fish Solo", |
|||
"url":"http://f2pool.com/", |
|||
"searchStrings":[ |
|||
"For Pierce and Paul" |
|||
] |
|||
}, |
|||
{ |
|||
"poolName":"Eligius", |
|||
"url":"http://eligius.st/", |
|||
"searchStrings":[ |
|||
"Eligius" |
|||
] |
|||
}, |
|||
{ |
|||
"poolName":"EclipseMC", |
|||
"url":"https://eclipsemc.com/", |
|||
"searchStrings":[ |
|||
"Josh Zerlan was here!", |
|||
"EclipseMC", |
|||
"Aluminum Falcon" |
|||
] |
|||
}, |
|||
{ |
|||
"poolName":"GIVE-ME-COINS", |
|||
"url":"https://give-me-coins.com/", |
|||
"searchStrings":[ |
|||
"Mined at GIVE-ME-COINS.com" |
|||
] |
|||
}, |
|||
{ |
|||
"poolName":"ghash.io", |
|||
"url":"https://ghash.io/", |
|||
"searchStrings":[ |
|||
"ghash.io", |
|||
"GHash.IO" |
|||
] |
|||
}, |
|||
{ |
|||
"poolName":"HHTT", |
|||
"url":"http://hhtt.1209k.com/", |
|||
"searchStrings":[ |
|||
"HHTT" |
|||
] |
|||
}, |
|||
{ |
|||
"poolName":"Megabigpower", |
|||
"url":"http://megabigpower.com/", |
|||
"searchStrings":[ |
|||
"megabigpower.com" |
|||
] |
|||
}, |
|||
{ |
|||
"poolName":"Mt Red", |
|||
"url":"https://mtred.com/", |
|||
"searchStrings":[ |
|||
"/mtred/" |
|||
] |
|||
}, |
|||
{ |
|||
"poolName":"MaxBTC", |
|||
"url":"https://www.maxbtc.com", |
|||
"searchStrings":[ |
|||
"MaxBTC" |
|||
] |
|||
}, |
|||
{ |
|||
"poolName":"NMCbit", |
|||
"url":"http://nmcbit.com/", |
|||
"searchStrings":[ |
|||
"nmcbit.com" |
|||
] |
|||
}, |
|||
{ |
|||
"poolName":"ozcoin", |
|||
"url":"https://ozco.in/", |
|||
"searchStrings":[ |
|||
"ozco.in", |
|||
"ozcoin" |
|||
] |
|||
}, |
|||
{ |
|||
"poolName":"Polmine.pl", |
|||
"url":"https://polmine.pl/", |
|||
"searchStrings":[ |
|||
"by polmine.pl" |
|||
] |
|||
}, |
|||
{ |
|||
"poolName":"simplecoin", |
|||
"url":"http://simplecoin.us/", |
|||
"searchStrings":[ |
|||
"simplecoin.us ftw" |
|||
] |
|||
}, |
|||
{ |
|||
"poolName":"Slush", |
|||
"url":"https://mining.bitcoin.cz/", |
|||
"searchStrings":[ |
|||
"slush" |
|||
] |
|||
}, |
|||
{ |
|||
"poolName":"TripleMining", |
|||
"url":"https://www.triplemining.com/", |
|||
"searchStrings":[ |
|||
"Triplemining.com" |
|||
] |
|||
}, |
|||
{ |
|||
"poolName":"wizkid057", |
|||
"url":"http://wizkid057.com/btc", |
|||
"searchStrings":[ |
|||
"wizkid057" |
|||
] |
|||
}, |
|||
{ |
|||
"poolName":"Yourbtc.net", |
|||
"url":"http://yourbtc.net/", |
|||
"searchStrings":[ |
|||
"yourbtc.net" |
|||
] |
|||
} |
|||
] |
@ -1,92 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
|
|||
//Set the node enviornment variable if not set before
|
|||
process.env.NODE_ENV = process.env.NODE_ENV || 'development'; |
|||
|
|||
/** |
|||
* Module dependencies. |
|||
*/ |
|||
var express = require('express'), |
|||
fs = require('fs'), |
|||
PeerSync = require('./lib/PeerSync').class(), |
|||
HistoricSync = require('./lib/HistoricSync').class(); |
|||
|
|||
//Initializing system variables
|
|||
var config = require('./config/config'); |
|||
|
|||
/** |
|||
* express app |
|||
*/ |
|||
var expressApp = express(); |
|||
|
|||
/** |
|||
* Bootstrap models |
|||
*/ |
|||
var models_path = __dirname + '/app/models'; |
|||
var walk = function(path) { |
|||
fs.readdirSync(path).forEach(function(file) { |
|||
var newPath = path + '/' + file; |
|||
var stat = fs.statSync(newPath); |
|||
if (stat.isFile()) { |
|||
if (/(.*)\.(js$)/.test(file)) { |
|||
require(newPath); |
|||
} |
|||
} |
|||
else if (stat.isDirectory()) { |
|||
walk(newPath); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
walk(models_path); |
|||
|
|||
/** |
|||
* p2pSync process |
|||
*/ |
|||
|
|||
var peerSync = new PeerSync({shouldBroadcast: true}); |
|||
|
|||
if (!config.disableP2pSync) { |
|||
peerSync.run(); |
|||
} |
|||
|
|||
/** |
|||
* historic_sync process |
|||
*/ |
|||
var historicSync = new HistoricSync({ |
|||
shouldBroadcastSync: true |
|||
}); |
|||
peerSync.historicSync = historicSync; |
|||
|
|||
if (!config.disableHistoricSync) { |
|||
historicSync.start({}, function(err){ |
|||
if (err) { |
|||
var txt = 'ABORTED with error: ' + err.message; |
|||
console.log('[historic_sync] ' + txt); |
|||
} |
|||
if (peerSync) peerSync.allowReorgs = true; |
|||
}); |
|||
} |
|||
else |
|||
if (peerSync) peerSync.allowReorgs = true; |
|||
|
|||
|
|||
//express settings
|
|||
require('./config/express')(expressApp, historicSync, peerSync); |
|||
|
|||
//Bootstrap routes
|
|||
require('./config/routes')(expressApp); |
|||
|
|||
// socket.io
|
|||
var server = require('http').createServer(expressApp); |
|||
var ios = require('socket.io').listen(server); |
|||
require('./app/controllers/socket.js').init(expressApp, ios); |
|||
|
|||
//Start the app by listening on <port>
|
|||
server.listen(config.port, function(){ |
|||
console.log('Express server listening on port %d in %s mode', server.address().port, process.env.NODE_ENV); |
|||
}); |
|||
|
|||
//expose app
|
|||
exports = module.exports = expressApp; |
@ -1,235 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
require('classtool'); |
|||
|
|||
|
|||
function spec(b) { |
|||
|
|||
var superclass = b.superclass || require('events').EventEmitter; |
|||
var TIMESTAMP_PREFIX = 'bts-'; // b-ts-<ts> => <hash>
|
|||
var PREV_PREFIX = 'bpr-'; // b-prev-<hash> => <prev_hash>
|
|||
var NEXT_PREFIX = 'bne-'; // b-next-<hash> => <next_hash>
|
|||
var MAIN_PREFIX = 'bma-'; // b-main-<hash> => 1/0
|
|||
var TIP = 'bti-'; // last block on the chain
|
|||
var LAST_FILE_INDEX = 'file-'; // last processed file index
|
|||
|
|||
var MAX_OPEN_FILES = 500; |
|||
|
|||
|
|||
/** |
|||
* Module dependencies. |
|||
*/ |
|||
var levelup = require('levelup'), |
|||
config = require('../config/config'); |
|||
var db = b.db || levelup(config.leveldb + '/blocks',{maxOpenFiles: MAX_OPEN_FILES} ); |
|||
var Rpc = b.rpc || require('./Rpc').class(); |
|||
var PoolMatch = b.poolMatch || require('./PoolMatch').class(config); |
|||
|
|||
var buffertools = require('buffertools'); |
|||
var TransactionDb = require('./TransactionDb.js').class(); |
|||
|
|||
var BlockDb = function() { |
|||
BlockDb.super(this, arguments); |
|||
this.poolMatch = new PoolMatch(); |
|||
}; |
|||
|
|||
BlockDb.superclass = superclass; |
|||
|
|||
BlockDb.prototype.close = function(cb) { |
|||
db.close(cb); |
|||
}; |
|||
|
|||
BlockDb.prototype.drop = function(cb) { |
|||
var path = config.leveldb + '/blocks'; |
|||
db.close(function() { |
|||
require('leveldown').destroy(path, function () { |
|||
db = levelup(path,{maxOpenFiles: MAX_OPEN_FILES} ); |
|||
return cb(); |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
// adds a block. Does not update Next pointer in
|
|||
// the block prev to the new block, nor TIP pointer
|
|||
//
|
|||
BlockDb.prototype.add = function(b, cb) { |
|||
var self = this; |
|||
var time_key = TIMESTAMP_PREFIX + |
|||
( b.time || Math.round(new Date().getTime() / 1000) ); |
|||
|
|||
return db.batch() |
|||
.put(time_key, b.hash) |
|||
.put(MAIN_PREFIX + b.hash, 1) |
|||
.put(PREV_PREFIX + b.hash, b.previousblockhash) |
|||
.write(function(err){ |
|||
if (!err) { |
|||
self.emit('new_block', {blockid: b.hash}); |
|||
} |
|||
cb(err); |
|||
}); |
|||
}; |
|||
|
|||
BlockDb.prototype.getTip = function(cb) { |
|||
db.get(TIP, function(err, val) { |
|||
return cb(err,val); |
|||
}); |
|||
}; |
|||
|
|||
BlockDb.prototype.setTip = function(hash, cb) { |
|||
db.put(TIP, hash, function(err) { |
|||
return cb(err); |
|||
}); |
|||
}; |
|||
|
|||
//mainly for testing
|
|||
BlockDb.prototype.setPrev = function(hash, prevHash, cb) { |
|||
db.put(PREV_PREFIX + hash, prevHash, function(err) { |
|||
return cb(err); |
|||
}); |
|||
}; |
|||
|
|||
BlockDb.prototype.getPrev = function(hash, cb) { |
|||
db.get(PREV_PREFIX + hash, function(err,val) { |
|||
if (err && err.notFound) { err = null; val = null;} |
|||
return cb(err,val); |
|||
}); |
|||
}; |
|||
|
|||
|
|||
BlockDb.prototype.setLastFileIndex = function(idx, cb) { |
|||
var self = this; |
|||
if (this.lastFileIndexSaved === idx) return cb(); |
|||
|
|||
db.put(LAST_FILE_INDEX, idx, function(err) { |
|||
self.lastFileIndexSaved = idx; |
|||
return cb(err); |
|||
}); |
|||
}; |
|||
|
|||
BlockDb.prototype.getLastFileIndex = function(cb) { |
|||
db.get(LAST_FILE_INDEX, function(err,val) { |
|||
if (err && err.notFound) { err = null; val = null;} |
|||
return cb(err,val); |
|||
}); |
|||
}; |
|||
|
|||
BlockDb.prototype.getNext = function(hash, cb) { |
|||
db.get(NEXT_PREFIX + hash, function(err,val) { |
|||
if (err && err.notFound) { err = null; val = null;} |
|||
return cb(err,val); |
|||
}); |
|||
}; |
|||
|
|||
BlockDb.prototype.isMain = function(hash, cb) { |
|||
db.get(MAIN_PREFIX + hash, function(err, val) { |
|||
if (err && err.notFound) { err = null; val = 0;} |
|||
return cb(err,parseInt(val)); |
|||
}); |
|||
}; |
|||
|
|||
BlockDb.prototype.setMain = function(hash, isMain, cb) { |
|||
if (!isMain) console.log('\tNew orphan: %s',hash); |
|||
db.put(MAIN_PREFIX + hash, isMain?1:0, function(err) { |
|||
return cb(err); |
|||
}); |
|||
}; |
|||
BlockDb.prototype.setNext = function(hash, nextHash, cb) { |
|||
db.put(NEXT_PREFIX + hash, nextHash, function(err) { |
|||
return cb(err); |
|||
}); |
|||
}; |
|||
|
|||
BlockDb.prototype.countConnected = function(cb) { |
|||
var c = 0; |
|||
console.log('Counting connected blocks. This could take some minutes'); |
|||
db.createReadStream({start: MAIN_PREFIX, end: MAIN_PREFIX + '~' }) |
|||
.on('data', function (data) { |
|||
if (data.value !== 0) c++; |
|||
}) |
|||
.on('error', function (err) { |
|||
return cb(err); |
|||
}) |
|||
.on('end', function () { |
|||
return cb(null, c); |
|||
}); |
|||
}; |
|||
|
|||
// .has() return true orphans also
|
|||
BlockDb.prototype.has = function(hash, cb) { |
|||
var k = PREV_PREFIX + hash; |
|||
db.get(k, function (err) { |
|||
var ret = true; |
|||
if (err && err.notFound) { |
|||
err = null; |
|||
ret = false; |
|||
} |
|||
return cb(err, ret); |
|||
}); |
|||
}; |
|||
|
|||
BlockDb.prototype.getPoolInfo = function(tx, cb) { |
|||
var tr = new TransactionDb(); |
|||
var self = this; |
|||
|
|||
tr._getInfo(tx, function(e, a) { |
|||
if (e) return cb(false); |
|||
|
|||
if (a.isCoinBase) { |
|||
var coinbaseHexBuffer = new Buffer(a.vin[0].coinbase, 'hex'); |
|||
var a = self.poolMatch.match(coinbaseHexBuffer); |
|||
|
|||
return cb(a); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
BlockDb.prototype.fromHashWithInfo = function(hash, cb) { |
|||
var self = this; |
|||
|
|||
Rpc.getBlock(hash, function(err, info) { |
|||
if (err || !info) return cb(err); |
|||
|
|||
self.isMain(hash, function(err, val) { |
|||
if (err) return cb(err); |
|||
|
|||
info.isMainChain = val ? true : false; |
|||
|
|||
return cb(null, { |
|||
hash: hash, |
|||
info: info, |
|||
}); |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
BlockDb.prototype.getBlocksByDate = function(start_ts, end_ts, cb) { |
|||
var list = []; |
|||
db.createReadStream({ |
|||
start: TIMESTAMP_PREFIX + start_ts, |
|||
end: TIMESTAMP_PREFIX + end_ts, |
|||
fillCache: true |
|||
}) |
|||
.on('data', function (data) { |
|||
var k = data.key.split('-'); |
|||
list.push({ |
|||
ts: k[1], |
|||
hash: data.value, |
|||
}); |
|||
}) |
|||
.on('error', function (err) { |
|||
return cb(err); |
|||
}) |
|||
.on('end', function () { |
|||
return cb(null, list.reverse()); |
|||
}); |
|||
}; |
|||
|
|||
BlockDb.prototype.blockIndex = function(height, cb) { |
|||
return Rpc.blockIndex(height,cb); |
|||
}; |
|||
|
|||
return BlockDb; |
|||
} |
|||
module.defineClass(spec); |
|||
|
|||
|
@ -1,162 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
require('classtool'); |
|||
|
|||
function spec() { |
|||
|
|||
var Block = require('bitcore/Block').class(), |
|||
networks = require('bitcore/networks'), |
|||
Parser = require('bitcore/util/BinaryParser').class(), |
|||
fs = require('fs'), |
|||
Buffer = require('buffer').Buffer, |
|||
glob = require('glob'), |
|||
async = require('async'); |
|||
|
|||
function BlockExtractor(dataDir, network) { |
|||
|
|||
var self = this; |
|||
var path = dataDir + '/blocks/blk*.dat'; |
|||
|
|||
self.dataDir = dataDir; |
|||
self.files = glob.sync(path); |
|||
self.nfiles = self.files.length; |
|||
|
|||
if (self.nfiles === 0) |
|||
throw new Error('Could not find block files at: ' + path); |
|||
|
|||
self.currentFileIndex = 0; |
|||
self.isCurrentRead = false; |
|||
self.currentBuffer = null; |
|||
self.currentParser = null; |
|||
self.network = network === 'testnet' ? networks.testnet: networks.livenet; |
|||
self.magic = self.network.magic.toString('hex'); |
|||
} |
|||
|
|||
BlockExtractor.prototype.currentFile = function() { |
|||
var self = this; |
|||
|
|||
return self.files[self.currentFileIndex]; |
|||
}; |
|||
|
|||
|
|||
BlockExtractor.prototype.nextFile = function() { |
|||
var self = this; |
|||
|
|||
if (self.currentFileIndex < 0) return false; |
|||
|
|||
var ret = true; |
|||
|
|||
self.isCurrentRead = false; |
|||
self.currentBuffer = null; |
|||
self.currentParser = null; |
|||
|
|||
if (self.currentFileIndex < self.nfiles - 1) { |
|||
self.currentFileIndex++; |
|||
} |
|||
else { |
|||
self.currentFileIndex=-1; |
|||
ret = false; |
|||
} |
|||
return ret; |
|||
}; |
|||
|
|||
BlockExtractor.prototype.readCurrentFileSync = function() { |
|||
var self = this; |
|||
|
|||
if (self.currentFileIndex < 0 || self.isCurrentRead) return; |
|||
|
|||
|
|||
self.isCurrentRead = true; |
|||
|
|||
var fname = self.currentFile(); |
|||
if (!fname) return; |
|||
|
|||
|
|||
var stats = fs.statSync(fname); |
|||
|
|||
var size = stats.size; |
|||
|
|||
console.log('Reading Blockfile %s [%d MB]', |
|||
fname, parseInt(size/1024/1024)); |
|||
|
|||
var fd = fs.openSync(fname, 'r'); |
|||
|
|||
var buffer = new Buffer(size); |
|||
|
|||
fs.readSync(fd, buffer, 0, size, 0); |
|||
|
|||
self.currentBuffer = buffer; |
|||
self.currentParser = new Parser(buffer); |
|||
}; |
|||
|
|||
|
|||
|
|||
BlockExtractor.prototype.getNextBlock = function(cb) { |
|||
var self = this; |
|||
|
|||
var b; |
|||
var magic; |
|||
async.series([ |
|||
function (a_cb) { |
|||
|
|||
async.whilst( |
|||
function() { |
|||
return (!magic); |
|||
}, |
|||
function(w_cb) { |
|||
|
|||
self.readCurrentFileSync(); |
|||
|
|||
if (self.currentFileIndex < 0) return cb(); |
|||
|
|||
|
|||
magic = self.currentParser ? self.currentParser.buffer(4).toString('hex') |
|||
: null ; |
|||
|
|||
if (!self.currentParser || self.currentParser.eof() || magic === '00000000') { |
|||
magic = null; |
|||
if (self.nextFile()) { |
|||
console.log('Moving forward to file:' + self.currentFile() ); |
|||
return w_cb(); |
|||
} |
|||
else { |
|||
console.log('Finished all files'); |
|||
magic = null; |
|||
return w_cb(); |
|||
} |
|||
} |
|||
else { |
|||
return w_cb(); |
|||
} |
|||
}, a_cb); |
|||
}, |
|||
function (a_cb) { |
|||
if (!magic) return a_cb(); |
|||
|
|||
if (magic !== self.magic) { |
|||
var e = new Error('CRITICAL ERROR: Magic number mismatch: ' + |
|||
magic + '!=' + self.magic); |
|||
return a_cb(e); |
|||
} |
|||
|
|||
// spacer?
|
|||
self.currentParser.word32le(); |
|||
return a_cb(); |
|||
}, |
|||
function (a_cb) { |
|||
if (!magic) return a_cb(); |
|||
|
|||
b = new Block(); |
|||
b.parse(self.currentParser); |
|||
b.getHash(); |
|||
return a_cb(); |
|||
}, |
|||
], function(err) { |
|||
return cb(err,b); |
|||
}); |
|||
}; |
|||
|
|||
return BlockExtractor; |
|||
} |
|||
module.defineClass(spec); |
|||
|
@ -1,429 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
require('classtool'); |
|||
|
|||
|
|||
|
|||
function spec() { |
|||
var util = require('util'); |
|||
var assert = require('assert'); |
|||
var RpcClient = require('bitcore/RpcClient').class(); |
|||
var Script = require('bitcore/Script').class(); |
|||
var networks = require('bitcore/networks'); |
|||
var async = require('async'); |
|||
var config = require('../config/config'); |
|||
var Sync = require('./Sync').class(); |
|||
var sockets = require('../app/controllers/socket.js'); |
|||
var BlockExtractor = require('./BlockExtractor.js').class(); |
|||
var buffertools = require('buffertools'); |
|||
|
|||
// var bitcoreUtil = require('bitcore/util/util');
|
|||
// var Deserialize = require('bitcore/Deserialize');
|
|||
|
|||
|
|||
var BAD_GEN_ERROR = 'Bad genesis block. Network mismatch between Insight and bitcoind? Insight is configured for:'; |
|||
|
|||
var BAD_GEN_ERROR_DB = 'Bad genesis block. Network mismatch between Insight and levelDB? Insight is configured for:'; |
|||
function HistoricSync(opts) { |
|||
opts = opts || {}; |
|||
|
|||
this.network = config.network === 'testnet' ? networks.testnet: networks.livenet; |
|||
|
|||
var genesisHashReversed = new Buffer(32); |
|||
this.network.genesisBlock.hash.copy(genesisHashReversed); |
|||
buffertools.reverse(genesisHashReversed); |
|||
this.genesis = genesisHashReversed.toString('hex'); |
|||
|
|||
this.rpc = new RpcClient(config.bitcoind); |
|||
this.shouldBroadcast = opts.shouldBroadcastSync; |
|||
this.sync = new Sync(opts); |
|||
} |
|||
|
|||
function p() { |
|||
var args = []; |
|||
Array.prototype.push.apply(args, arguments); |
|||
|
|||
args.unshift('[historic_sync]'); |
|||
/*jshint validthis:true */ |
|||
console.log.apply(this, args); |
|||
} |
|||
|
|||
HistoricSync.prototype.showProgress = function() { |
|||
var self = this; |
|||
|
|||
if ( self.status ==='syncing' && |
|||
( self.syncedBlocks ) % self.step !== 1) return; |
|||
|
|||
if (self.error) { |
|||
p('ERROR: ' + self.error); |
|||
} |
|||
else { |
|||
self.updatePercentage(); |
|||
p(util.format('status: [%d%%]', self.syncPercentage)); |
|||
} |
|||
if (self.shouldBroadcast) { |
|||
sockets.broadcastSyncInfo(self.info()); |
|||
} |
|||
|
|||
// if (self.syncPercentage > 10) {
|
|||
// process.exit(-1);
|
|||
// }
|
|||
}; |
|||
|
|||
|
|||
HistoricSync.prototype.setError = function(err) { |
|||
var self = this; |
|||
self.error = err.message?err.message:err.toString(); |
|||
self.status='error'; |
|||
self.showProgress(); |
|||
return err; |
|||
}; |
|||
|
|||
|
|||
|
|||
HistoricSync.prototype.close = function() { |
|||
this.sync.close(); |
|||
}; |
|||
|
|||
|
|||
HistoricSync.prototype.info = function() { |
|||
this.updatePercentage(); |
|||
return { |
|||
status: this.status, |
|||
blockChainHeight: this.blockChainHeight, |
|||
syncPercentage: this.syncPercentage, |
|||
syncedBlocks: this.syncedBlocks, |
|||
syncTipHash: this.sync.tip, |
|||
error: this.error, |
|||
type: this.type, |
|||
startTs: this.startTs, |
|||
endTs: this.endTs, |
|||
}; |
|||
}; |
|||
|
|||
HistoricSync.prototype.updatePercentage = function() { |
|||
var r = this.syncedBlocks / this.blockChainHeight; |
|||
this.syncPercentage = parseFloat(100 * r).toFixed(3); |
|||
if (this.syncPercentage > 100) this.syncPercentage = 100; |
|||
}; |
|||
|
|||
HistoricSync.prototype.getBlockFromRPC = function(cb) { |
|||
var self = this; |
|||
|
|||
if (!self.currentRpcHash) return cb(); |
|||
|
|||
var blockInfo; |
|||
self.rpc.getBlock(self.currentRpcHash, function(err, ret) { |
|||
if (err) return cb(err); |
|||
if (ret) { |
|||
blockInfo = ret.result; |
|||
// this is to match block retreived from file
|
|||
if (blockInfo.hash === self.genesis) |
|||
blockInfo.previousblockhash = |
|||
self.network.genesisBlock.prev_hash.toString('hex'); |
|||
|
|||
self.currentRpcHash = blockInfo.nextblockhash; |
|||
} |
|||
else { |
|||
blockInfo = null; |
|||
} |
|||
return cb(null, blockInfo); |
|||
}); |
|||
}; |
|||
|
|||
HistoricSync.prototype.getBlockFromFile = function(cb) { |
|||
var self = this; |
|||
|
|||
var blockInfo; |
|||
|
|||
//get Info
|
|||
self.blockExtractor.getNextBlock(function(err, b) { |
|||
if (err || ! b) return cb(err); |
|||
blockInfo = b.getStandardizedObject(b.txs, self.network); |
|||
blockInfo.previousblockhash = blockInfo.prev_block; |
|||
|
|||
var ti=0; |
|||
// Get TX Address
|
|||
b.txs.forEach(function(t) { |
|||
|
|||
|
|||
var objTx = blockInfo.tx[ti++]; |
|||
|
|||
//add time from block
|
|||
objTx.time = blockInfo.time; |
|||
|
|||
var to=0; |
|||
t.outs.forEach( function(o) { |
|||
|
|||
|
|||
var s = new Script(o.s); |
|||
var addrs = self.sync.txDb.getAddrStr(s); |
|||
|
|||
// support only for p2pubkey p2pubkeyhash and p2sh
|
|||
if (addrs.length === 1) { |
|||
objTx.out[to].addrStr = addrs[0]; |
|||
} |
|||
to++; |
|||
}); |
|||
}); |
|||
self.sync.bDb.setLastFileIndex(self.blockExtractor.currentFileIndex, function(err) { |
|||
return cb(err,blockInfo); |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
HistoricSync.prototype.updateConnectedCountDB = function(cb) { |
|||
var self = this; |
|||
self.sync.bDb.countConnected(function(err, count) { |
|||
self.connectedCountDB = count || 0; |
|||
self.syncedBlocks = count || 0; |
|||
return cb(err); |
|||
}); |
|||
}; |
|||
|
|||
|
|||
HistoricSync.prototype.updateBlockChainHeight = function(cb) { |
|||
var self = this; |
|||
|
|||
self.rpc.getBlockCount(function(err, res) { |
|||
self.blockChainHeight = res.result; |
|||
return cb(err); |
|||
}); |
|||
}; |
|||
|
|||
|
|||
HistoricSync.prototype.checkNetworkSettings = function(next) { |
|||
var self = this; |
|||
|
|||
self.hasGenesis = false; |
|||
|
|||
// check network config
|
|||
self.rpc.getBlockHash(0, function(err, res){ |
|||
if (!err && ( res && res.result !== self.genesis)) { |
|||
err = new Error(BAD_GEN_ERROR + config.network); |
|||
} |
|||
if (err) return next(err); |
|||
self.sync.bDb.has(self.genesis, function(err, b) { |
|||
if (!err && ( res && res.result !== self.genesis)) { |
|||
err = new Error(BAD_GEN_ERROR_DB + config.network); |
|||
} |
|||
self.hasGenesis = b?true:false; |
|||
return next(err); |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
HistoricSync.prototype.updateStartBlock = function(next) { |
|||
var self = this; |
|||
|
|||
self.startBlock = self.genesis; |
|||
|
|||
self.sync.bDb.getTip(function(err,tip) { |
|||
if (!tip) return next(); |
|||
|
|||
var blockInfo; |
|||
var oldtip; |
|||
|
|||
//check that the tip is still on the mainchain
|
|||
async.doWhilst( |
|||
function(cb) { |
|||
self.sync.bDb.fromHashWithInfo(tip, function(err, bi) { |
|||
blockInfo = bi ? bi.info : {}; |
|||
if (oldtip) |
|||
self.sync.setBlockMain(oldtip, false, cb); |
|||
else |
|||
return cb(); |
|||
}); |
|||
}, |
|||
function(err) { |
|||
if (err) return next(err); |
|||
var ret = false; |
|||
if ( self.blockChainHeight === blockInfo.height || |
|||
blockInfo.confirmations > 0) { |
|||
ret = false; |
|||
} |
|||
else { |
|||
oldtip = tip; |
|||
tip = blockInfo.previousblockhash; |
|||
assert(tip); |
|||
p('Previous TIP is now orphan. Back to:' + tip); |
|||
ret = true; |
|||
} |
|||
return ret; |
|||
}, |
|||
function(err) { |
|||
self.startBlock = tip; |
|||
p('Resuming sync from block:'+tip); |
|||
return next(err); |
|||
} |
|||
); |
|||
}); |
|||
}; |
|||
|
|||
HistoricSync.prototype.prepareFileSync = function(opts, next) { |
|||
var self = this; |
|||
|
|||
if ( opts.forceRPC || !config.bitcoind.dataDir || |
|||
self.connectedCountDB > self.blockChainHeight * 0.9) return next(); |
|||
|
|||
|
|||
try { |
|||
self.blockExtractor = new BlockExtractor(config.bitcoind.dataDir, config.network); |
|||
} catch (e) { |
|||
p(e.message + '. Disabling file sync.'); |
|||
return next(); |
|||
} |
|||
|
|||
self.getFn = self.getBlockFromFile; |
|||
self.allowReorgs = true; |
|||
self.sync.bDb.getLastFileIndex(function(err, idx) { |
|||
if (opts.forceStartFile) |
|||
self.blockExtractor.currentFileIndex = opts.forceStartFile; |
|||
else if (idx) self.blockExtractor.currentFileIndex = idx; |
|||
|
|||
var h = self.genesis; |
|||
|
|||
p('Seeking file to:' + self.startBlock); |
|||
//forward till startBlock
|
|||
async.whilst( |
|||
function() { |
|||
return h !== self.startBlock; |
|||
}, |
|||
function (w_cb) { |
|||
self.getBlockFromFile(function(err,b) { |
|||
if (!b) return w_cb('Could not find block ' + self.startBlock); |
|||
h=b.hash; |
|||
setImmediate(function(){ |
|||
return w_cb(err); |
|||
}); |
|||
}); |
|||
}, next); |
|||
}); |
|||
}; |
|||
|
|||
//NOP
|
|||
HistoricSync.prototype.prepareRpcSync = function(opts, next) { |
|||
var self = this; |
|||
|
|||
if (self.blockExtractor) return next(); |
|||
self.getFn = self.getBlockFromRPC; |
|||
self.currentRpcHash = self.startBlock; |
|||
self.allowReorgs = false; |
|||
return next(); |
|||
}; |
|||
|
|||
HistoricSync.prototype.showSyncStartMessage = function() { |
|||
var self = this; |
|||
|
|||
p('Got ' + self.connectedCountDB + |
|||
' blocks in current DB, out of ' + self.blockChainHeight + ' block at bitcoind'); |
|||
|
|||
if (self.blockExtractor) { |
|||
p('bitcoind dataDir configured...importing blocks from .dat files'); |
|||
p('First file index: ' + self.blockExtractor.currentFileIndex); |
|||
} |
|||
else { |
|||
p('syncing from RPC (slow)'); |
|||
} |
|||
|
|||
p('Starting from: ', self.startBlock); |
|||
self.showProgress(); |
|||
}; |
|||
|
|||
|
|||
HistoricSync.prototype.setupSyncStatus = function() { |
|||
var self = this; |
|||
|
|||
var step = parseInt( (self.blockChainHeight - self.syncedBlocks) / 1000); |
|||
if (step < 10) step = 10; |
|||
|
|||
self.step = step; |
|||
self.type = self.blockExtractor?'from .dat Files':'from RPC calls'; |
|||
self.status = 'syncing'; |
|||
self.startTs = Date.now(); |
|||
self.endTs = null; |
|||
this.error = null; |
|||
this.syncPercentage = 0; |
|||
}; |
|||
|
|||
HistoricSync.prototype.prepareToSync = function(opts, next) { |
|||
var self = this; |
|||
|
|||
self.status = 'starting'; |
|||
async.series([ |
|||
function(s_c) { |
|||
self.checkNetworkSettings(s_c); |
|||
}, |
|||
function(s_c) { |
|||
self.updateConnectedCountDB(s_c); |
|||
}, |
|||
function(s_c) { |
|||
self.updateBlockChainHeight(s_c); |
|||
}, |
|||
function(s_c) { |
|||
self.updateStartBlock(s_c); |
|||
}, |
|||
function(s_c) { |
|||
self.prepareFileSync(opts, s_c); |
|||
}, |
|||
function(s_c) { |
|||
self.prepareRpcSync(opts, s_c); |
|||
}, |
|||
], |
|||
function(err) { |
|||
if (err) return(self.setError(err)); |
|||
|
|||
self.showSyncStartMessage(); |
|||
self.setupSyncStatus(); |
|||
return next(); |
|||
}); |
|||
}; |
|||
|
|||
|
|||
HistoricSync.prototype.start = function(opts, next) { |
|||
var self = this; |
|||
|
|||
if (self.status==='starting' || self.status==='syncing') { |
|||
p('## Wont start to sync while status is %s', self.status); |
|||
return next(); |
|||
} |
|||
|
|||
self.prepareToSync(opts, function(err) { |
|||
if (err) return next(self.setError(err)); |
|||
|
|||
async.whilst( |
|||
function() { |
|||
self.showProgress(); |
|||
return self.status === 'syncing'; |
|||
}, |
|||
function (w_cb) { |
|||
self.getFn(function(err,blockInfo) { |
|||
if (err) return w_cb(self.setError(err)); |
|||
if (blockInfo && blockInfo.hash) { |
|||
self.syncedBlocks++; |
|||
self.sync.storeTipBlock(blockInfo, self.allowReorgs, function(err) { |
|||
if (err) return w_cb(self.setError(err)); |
|||
|
|||
self.sync.bDb.setTip(blockInfo.hash, function(err) { |
|||
if (err) return w_cb(self.setError(err)); |
|||
|
|||
setImmediate(function(){ |
|||
return w_cb(err); |
|||
}); |
|||
}); |
|||
}); |
|||
} |
|||
else { |
|||
self.endTs = Date.now(); |
|||
self.status = 'finished'; |
|||
console.log('Done Syncing', self.info()); |
|||
return w_cb(err); |
|||
} |
|||
}); |
|||
}, next); |
|||
}); |
|||
}; |
|||
return HistoricSync; |
|||
} |
|||
module.defineClass(spec); |
|||
|
@ -1,127 +0,0 @@ |
|||
'use strict'; |
|||
require('classtool'); |
|||
|
|||
function spec() { |
|||
var fs = require('fs'); |
|||
var bitcoreUtil = require('bitcore/util/util'); |
|||
var Sync = require('./Sync').class(); |
|||
var Peer = require('bitcore/Peer').class(); |
|||
var config = require('../config/config'); |
|||
var networks = require('bitcore/networks'); |
|||
|
|||
var peerdb_fn = 'peerdb.json'; |
|||
|
|||
function PeerSync(opts) { |
|||
this.connected = false; |
|||
this.peerdb = undefined; |
|||
this.allowReorgs = false; |
|||
this.PeerManager = require('bitcore/PeerManager').createClass({ |
|||
network: (config.network === 'testnet' ? networks.testnet : networks.livenet) |
|||
}); |
|||
this.peerman = new this.PeerManager(); |
|||
this.load_peers(); |
|||
this.sync = new Sync(opts); |
|||
} |
|||
|
|||
PeerSync.prototype.load_peers = function() { |
|||
this.peerdb = [{ |
|||
ipv4: config.bitcoind.host, |
|||
port: config.bitcoind.p2pPort |
|||
}]; |
|||
|
|||
fs.writeFileSync(peerdb_fn, JSON.stringify(this.peerdb)); |
|||
}; |
|||
|
|||
PeerSync.prototype.info = function() { |
|||
return { |
|||
connected: this.connected, |
|||
host: this.peerdb[0].ipv4, |
|||
port: this.peerdb[0].port |
|||
}; |
|||
}; |
|||
|
|||
PeerSync.prototype.handleInv = function(info) { |
|||
var invs = info.message.invs; |
|||
info.conn.sendGetData(invs); |
|||
}; |
|||
|
|||
PeerSync.prototype.handleTx = function(info) { |
|||
var tx = info.message.tx.getStandardizedObject(); |
|||
tx.outs = info.message.tx.outs; |
|||
tx.ins = info.message.tx.ins; |
|||
console.log('[p2p_sync] Handle tx: ' + tx.hash); |
|||
tx.time = tx.time || Math.round(new Date().getTime() / 1000); |
|||
|
|||
this.sync.storeTxs([tx], function(err) { |
|||
if (err) { |
|||
console.log('[p2p_sync] Error in handle TX: ' + JSON.stringify(err)); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
PeerSync.prototype.handleBlock = function(info) { |
|||
var self = this; |
|||
var block = info.message.block; |
|||
var blockHash = bitcoreUtil.formatHashFull(block.calcHash()); |
|||
|
|||
console.log('[p2p_sync] Handle block: %s (allowReorgs: %s)', blockHash, self.allowReorgs); |
|||
|
|||
var tx_hashes = block.txs.map(function(tx) { |
|||
return bitcoreUtil.formatHashFull(tx.hash); |
|||
}); |
|||
|
|||
this.sync.storeTipBlock({ |
|||
'hash': blockHash, |
|||
'tx': tx_hashes, |
|||
'previousblockhash': bitcoreUtil.formatHashFull(block.prev_hash), |
|||
}, self.allowReorgs, function(err) { |
|||
if (err && err.message.match(/NEED_SYNC/) && self.historicSync) { |
|||
console.log('[p2p_sync] Orphan block received. Triggering sync'); |
|||
self.historicSync.start({}, function(){ |
|||
console.log('[p2p_sync] Done resync.'); |
|||
}); |
|||
} |
|||
else if (err) { |
|||
console.log('[p2p_sync] Error in handle Block: ' + err); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
PeerSync.prototype.handle_connected = function(data) { |
|||
var peerman = data.pm; |
|||
var peers_n = peerman.peers.length; |
|||
console.log('[p2p_sync] Connected to ' + peers_n + ' peer' + (peers_n !== 1 ? 's' : '')); |
|||
}; |
|||
|
|||
PeerSync.prototype.run = function() { |
|||
var self = this; |
|||
|
|||
this.peerdb.forEach(function(datum) { |
|||
var peer = new Peer(datum.ipv4, datum.port); |
|||
self.peerman.addPeer(peer); |
|||
}); |
|||
|
|||
this.peerman.on('connection', function(conn) { |
|||
self.connected = true; |
|||
conn.on('inv', self.handleInv.bind(self)); |
|||
conn.on('block', self.handleBlock.bind(self)); |
|||
conn.on('tx', self.handleTx.bind(self)); |
|||
}); |
|||
this.peerman.on('connect', self.handle_connected.bind(self)); |
|||
|
|||
this.peerman.on('netDisconnected', function() { |
|||
self.connected = false; |
|||
}); |
|||
|
|||
this.peerman.start(); |
|||
}; |
|||
|
|||
PeerSync.prototype.close = function() { |
|||
this.sync.close(); |
|||
}; |
|||
|
|||
|
|||
return PeerSync; |
|||
|
|||
} |
|||
module.defineClass(spec); |
@ -1,38 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
require('classtool'); |
|||
|
|||
function spec(b) { |
|||
|
|||
var fs = require('fs'); |
|||
var buffertools = require('buffertools'); |
|||
var db = b.db || JSON.parse( fs.readFileSync(b.poolMatchFile || './poolMatchFile.json')); |
|||
|
|||
var PoolMatch = function() { |
|||
var self = this; |
|||
|
|||
self.strings = {}; |
|||
db.forEach(function(pool) { |
|||
pool.searchStrings.forEach(function(s) { |
|||
self.strings[s] = { |
|||
poolName: pool.poolName, |
|||
url: pool.url |
|||
}; |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
|
|||
PoolMatch.prototype.match = function(buffer) { |
|||
var self = this; |
|||
for(var k in self.strings) { |
|||
if (buffertools.indexOf(buffer, k) >= 0) { |
|||
return self.strings[k]; |
|||
} |
|||
}; |
|||
}; |
|||
|
|||
return PoolMatch; |
|||
} |
|||
module.defineClass(spec); |
|||
|
@ -1,103 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
require('classtool'); |
|||
|
|||
|
|||
function spec(b) { |
|||
var RpcClient = require('bitcore/RpcClient').class(), |
|||
BitcoreBlock = require('bitcore/Block').class(), |
|||
bitcoreUtil = require('bitcore/util/util'), |
|||
util = require('util'), |
|||
config = require('../config/config'); |
|||
|
|||
var bitcoreRpc = b.bitcoreRpc || new RpcClient(config.bitcoind); |
|||
|
|||
function Rpc() { |
|||
} |
|||
|
|||
Rpc._parseTxResult = function(info) { |
|||
var b = new Buffer(info.hex,'hex'); |
|||
|
|||
// remove fields we dont need, to speed and adapt the information
|
|||
delete info['hex']; |
|||
|
|||
// Inputs => add index + coinBase flag
|
|||
var n =0; |
|||
info.vin.forEach(function(i) { |
|||
i.n = n++; |
|||
if (i.coinbase) info.isCoinBase = true; |
|||
if (i.scriptSig) delete i.scriptSig['hex']; |
|||
}); |
|||
|
|||
// Outputs => add total
|
|||
var valueOutSat = 0; |
|||
info.vout.forEach( function(o) { |
|||
valueOutSat += o.value * bitcoreUtil.COIN; |
|||
delete o.scriptPubKey['hex']; |
|||
}); |
|||
info.valueOut = parseInt(valueOutSat) / bitcoreUtil.COIN; |
|||
info.size = b.length; |
|||
|
|||
return info; |
|||
}; |
|||
|
|||
|
|||
Rpc.errMsg = function(err) { |
|||
var e = err; |
|||
e.message += util.format(' [Host: %s:%d User:%s Using password:%s]', |
|||
bitcoreRpc.host, |
|||
bitcoreRpc.port, |
|||
bitcoreRpc.user, |
|||
bitcoreRpc.pass?'yes':'no' |
|||
); |
|||
return e; |
|||
}; |
|||
|
|||
Rpc.getTxInfo = function(txid, doNotParse, cb) { |
|||
var self = this; |
|||
|
|||
if (typeof doNotParse === 'function') { |
|||
cb = doNotParse; |
|||
doNotParse = false; |
|||
} |
|||
|
|||
bitcoreRpc.getRawTransaction(txid, 1, function(err, txInfo) { |
|||
// Not found?
|
|||
if (err && err.code === -5) return cb(); |
|||
if (err) return cb(self.errMsg(err)); |
|||
|
|||
var info = doNotParse ? txInfo.result : self._parseTxResult(txInfo.result); |
|||
return cb(null,info); |
|||
}); |
|||
}; |
|||
|
|||
|
|||
Rpc.blockIndex = function(height, cb) { |
|||
var self = this; |
|||
|
|||
bitcoreRpc.getBlockHash(height, function(err, bh){ |
|||
if (err) return cb(self.errMsg(err)); |
|||
cb(null, { blockHash: bh.result }); |
|||
}); |
|||
}; |
|||
|
|||
Rpc.getBlock = function(hash, cb) { |
|||
var self = this; |
|||
|
|||
bitcoreRpc.getBlock(hash, function(err,info) { |
|||
// Not found?
|
|||
if (err && err.code === -5) return cb(); |
|||
if (err) return cb(self.errMsg(err)); |
|||
|
|||
|
|||
if (info.result.height) |
|||
info.result.reward = BitcoreBlock.getBlockValue(info.result.height) / bitcoreUtil.COIN ; |
|||
|
|||
return cb(err,info.result); |
|||
}); |
|||
}; |
|||
return Rpc; |
|||
} |
|||
module.defineClass(spec); |
|||
|
|||
|
@ -1,289 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
require('classtool'); |
|||
|
|||
|
|||
function spec() { |
|||
var sockets = require('../app/controllers/socket.js'); |
|||
var BlockDb = require('./BlockDb').class(); |
|||
|
|||
var TransactionDb = require('./TransactionDb').class(); |
|||
var config = require('../config/config'); |
|||
var networks = require('bitcore/networks'); |
|||
var async = require('async'); |
|||
|
|||
|
|||
function Sync(opts) { |
|||
this.opts = opts || {}; |
|||
this.bDb = new BlockDb(opts); |
|||
this.txDb = new TransactionDb(opts); |
|||
this.txDb.on('tx_for_address', this.handleTxForAddress.bind(this)); |
|||
this.txDb.on('new_tx', this.handleNewTx.bind(this)); |
|||
this.bDb.on('new_block', this.handleNewBlock.bind(this)); |
|||
this.network = config.network === 'testnet' ? networks.testnet : networks.livenet; |
|||
} |
|||
|
|||
Sync.prototype.close = function(cb) { |
|||
var self = this; |
|||
self.txDb.close(function() { |
|||
self.bDb.close(cb); |
|||
}); |
|||
}; |
|||
|
|||
|
|||
Sync.prototype.destroy = function(next) { |
|||
var self = this; |
|||
async.series([ |
|||
|
|||
function(b) { |
|||
self.bDb.drop(b); |
|||
}, |
|||
function(b) { |
|||
self.txDb.drop(b); |
|||
}, |
|||
], next); |
|||
}; |
|||
|
|||
/* |
|||
* Arrives a NEW block, which is the new TIP |
|||
* |
|||
* Case 0) Simple case |
|||
* A-B-C-D-E(TIP)-NEW |
|||
* |
|||
* Case 1) |
|||
* A-B-C-D-E(TIP) |
|||
* \ |
|||
* NEW |
|||
* |
|||
* 1) Declare D-E orphans (and possible invalidate TXs on them) |
|||
* |
|||
* Case 2) |
|||
* A-B-C-D-E(TIP) |
|||
* \ |
|||
* F-G-NEW |
|||
* 1) Set F-G as connected (mark TXs as valid) |
|||
* 2) Declare D-E orphans (and possible invalidate TXs on them) |
|||
* |
|||
* |
|||
* Case 3) |
|||
* |
|||
* A-B-C-D-E(TIP) ... NEW |
|||
* |
|||
* NEW is ignored (if allowReorgs is false) |
|||
* |
|||
* |
|||
*/ |
|||
|
|||
Sync.prototype.storeTipBlock = function(b, allowReorgs, cb) { |
|||
|
|||
if (typeof allowReorgs === 'function') { |
|||
cb = allowReorgs; |
|||
allowReorgs = true; |
|||
} |
|||
if (!b) return cb(); |
|||
|
|||
var self = this; |
|||
var oldTip, oldNext, needReorg = false; |
|||
var newPrev = b.previousblockhash; |
|||
|
|||
async.series([ |
|||
|
|||
function(c) { |
|||
self.bDb.has(b.hash, function(err, val) { |
|||
return c(err || |
|||
(val ? new Error('WARN: Ignoring already existing block:' + b.hash) : null)); |
|||
}); |
|||
}, |
|||
function(c) { |
|||
if (!allowReorgs) return c(); |
|||
self.bDb.has(newPrev, function(err, val) { |
|||
if (!val && newPrev.match(/^0+$/)) return c(); |
|||
|
|||
return c(err || |
|||
(!val ? new Error('NEED_SYNC Ignoring block with non existing prev:' + b.hash) : null)); |
|||
}); |
|||
}, |
|||
function(c) { |
|||
self.txDb.createFromBlock(b, function(err) { |
|||
return c(err); |
|||
}); |
|||
}, |
|||
function(c) { |
|||
if (!allowReorgs) return c(); |
|||
self.bDb.getTip(function(err, val) { |
|||
oldTip = val; |
|||
if (oldTip && newPrev !== oldTip) needReorg = true; |
|||
return c(); |
|||
}); |
|||
}, |
|||
function(c) { |
|||
if (!needReorg) return c(); |
|||
self.bDb.getNext(newPrev, function(err, val) { |
|||
if (err) return c(err); |
|||
oldNext = val; |
|||
return c(); |
|||
}); |
|||
}, |
|||
function(c) { |
|||
self.bDb.add(b, c); |
|||
}, |
|||
function(c) { |
|||
if (!needReorg) return c(); |
|||
console.log('NEW TIP: %s NEED REORG (old tip: %s)', b.hash, oldTip); |
|||
self.processReorg(oldTip, oldNext, newPrev, c); |
|||
}, |
|||
function(c) { |
|||
if (!allowReorgs) return c(); |
|||
self.bDb.setTip(b.hash, function(err) { |
|||
return c(err); |
|||
}); |
|||
}, |
|||
function(c) { |
|||
self.bDb.setNext(newPrev, b.hash, function(err) { |
|||
return c(err); |
|||
}); |
|||
} |
|||
|
|||
], |
|||
function(err) { |
|||
if (err && err.toString().match(/WARN/)) { |
|||
err = null; |
|||
} |
|||
return cb(err); |
|||
}); |
|||
}; |
|||
|
|||
|
|||
|
|||
Sync.prototype.processReorg = function(oldTip, oldNext, newPrev, cb) { |
|||
var self = this; |
|||
|
|||
var orphanizeFrom; |
|||
|
|||
async.series([ |
|||
|
|||
function(c) { |
|||
self.bDb.isMain(newPrev, function(err, val) { |
|||
if (!val) return c(); |
|||
|
|||
console.log('# Reorg Case 1)'); |
|||
// case 1
|
|||
orphanizeFrom = oldNext; |
|||
return c(err); |
|||
}); |
|||
}, |
|||
function(c) { |
|||
if (orphanizeFrom) return c(); |
|||
|
|||
console.log('# Reorg Case 2)'); |
|||
self.setBranchConnectedBackwards(newPrev, function(err, yHash, newYHashNext) { |
|||
if (err) return c(err); |
|||
self.bDb.getNext(yHash, function(err, yHashNext) { |
|||
orphanizeFrom = yHashNext; |
|||
self.bDb.setNext(yHash, newYHashNext, function(err) { |
|||
return c(err); |
|||
}); |
|||
}); |
|||
}); |
|||
}, |
|||
function(c) { |
|||
if (!orphanizeFrom) return c(); |
|||
self.setBranchOrphan(orphanizeFrom, function(err) { |
|||
return c(err); |
|||
}); |
|||
}, |
|||
], |
|||
function(err) { |
|||
return cb(err); |
|||
}); |
|||
}; |
|||
|
|||
Sync.prototype.setBlockMain = function(hash, isMain, cb) { |
|||
var self = this; |
|||
self.bDb.setMain(hash, isMain, function(err) { |
|||
if (err) return cb(err); |
|||
return self.txDb.handleBlockChange(hash, isMain, cb); |
|||
}); |
|||
}; |
|||
|
|||
Sync.prototype.setBranchOrphan = function(fromHash, cb) { |
|||
var self = this, |
|||
hashInterator = fromHash; |
|||
|
|||
async.whilst( |
|||
function() { |
|||
return hashInterator; |
|||
}, |
|||
function(c) { |
|||
self.setBlockMain(hashInterator, false, function(err) { |
|||
if (err) return cb(err); |
|||
self.bDb.getNext(hashInterator, function(err, val) { |
|||
hashInterator = val; |
|||
return c(err); |
|||
}); |
|||
}); |
|||
}, cb); |
|||
}; |
|||
|
|||
Sync.prototype.setBranchConnectedBackwards = function(fromHash, cb) { |
|||
var self = this, |
|||
hashInterator = fromHash, |
|||
lastHash = fromHash, |
|||
isMain; |
|||
|
|||
async.doWhilst( |
|||
function(c) { |
|||
self.setBlockMain(hashInterator, true, function(err) { |
|||
if (err) return c(err); |
|||
self.bDb.getPrev(hashInterator, function(err, val) { |
|||
if (err) return c(err); |
|||
lastHash = hashInterator; |
|||
hashInterator = val; |
|||
self.bDb.isMain(hashInterator, function(err, val) { |
|||
isMain = val; |
|||
return c(); |
|||
}); |
|||
}); |
|||
}); |
|||
}, |
|||
function() { |
|||
return hashInterator && !isMain; |
|||
}, |
|||
function(err) { |
|||
console.log('\tFound yBlock:', hashInterator); |
|||
return cb(err, hashInterator, lastHash); |
|||
} |
|||
); |
|||
}; |
|||
|
|||
|
|||
Sync.prototype.handleTxForAddress = function(data) { |
|||
if (this.opts.shouldBroadcast) { |
|||
sockets.broadcastAddressTx(data.address, data.txid); |
|||
} |
|||
}; |
|||
|
|||
Sync.prototype.handleNewTx = function(data) { |
|||
if (this.opts.shouldBroadcast) { |
|||
sockets.broadcastTx(data.tx); |
|||
} |
|||
}; |
|||
|
|||
Sync.prototype.handleNewBlock = function(data) { |
|||
if (this.opts.shouldBroadcast) { |
|||
sockets.broadcastBlock(data.blockid); |
|||
} |
|||
}; |
|||
|
|||
Sync.prototype.storeTxs = function(txs, cb) { |
|||
var self = this; |
|||
self.txDb.createFromArray(txs, null, function(err) { |
|||
if (err) return cb(err); |
|||
return cb(err); |
|||
}); |
|||
}; |
|||
|
|||
|
|||
return Sync; |
|||
} |
|||
module.defineClass(spec); |
@ -1,733 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
require('classtool'); |
|||
|
|||
|
|||
function spec(b) { |
|||
|
|||
var superclass = b.superclass || require('events').EventEmitter; |
|||
// blockHash -> txid mapping
|
|||
var IN_BLK_PREFIX = 'txb-'; //txb-<txid>-<block> => 1/0 (connected or not)
|
|||
|
|||
// Only for orphan blocks
|
|||
var FROM_BLK_PREFIX = 'tx-'; //tx-<block>-<txid> => 1
|
|||
|
|||
// to show tx outs
|
|||
var OUTS_PREFIX = 'txo-'; //txo-<txid>-<n> => [addr, btc_sat]
|
|||
var SPENT_PREFIX = 'txs-'; //txs-<txid(out)>-<n(out)>-<txid(in)>-<n(in)> = ts
|
|||
|
|||
// to sum up addr balance (only outs, spents are gotten later)
|
|||
var ADDR_PREFIX = 'txa-'; //txa-<addr>-<txid>-<n> => + btc_sat:ts
|
|||
|
|||
// TODO: use bitcore networks module
|
|||
var genesisTXID = '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b'; |
|||
var CONCURRENCY = 10; |
|||
|
|||
var MAX_OPEN_FILES = 500; |
|||
// var CONFIRMATION_NR_TO_NOT_CHECK = 10; //Spend
|
|||
/** |
|||
* Module dependencies. |
|||
*/ |
|||
var Rpc = b.rpc || require('./Rpc').class(), |
|||
util = require('bitcore/util/util'), |
|||
levelup = require('levelup'), |
|||
async = require('async'), |
|||
config = require('../config/config'), |
|||
assert = require('assert'); |
|||
var db = b.db || levelup(config.leveldb + '/txs',{maxOpenFiles: MAX_OPEN_FILES} ); |
|||
var Script = require('bitcore/Script').class(); |
|||
// This is 0.1.2 => c++ version of base57-native
|
|||
var base58 = require('base58-native').base58Check; |
|||
var encodedData = require('bitcore/util/EncodedData').class({ |
|||
base58: base58 |
|||
}); |
|||
var versionedData = require('bitcore/util/VersionedData').class({ |
|||
superclass: encodedData |
|||
}); |
|||
var Address = require('bitcore/Address').class({ |
|||
superclass: versionedData |
|||
}); |
|||
var bitutil = require('bitcore/util/util'); |
|||
var networks = require('bitcore/networks'); |
|||
|
|||
var TransactionDb = function() { |
|||
TransactionDb.super(this, arguments); |
|||
this.network = config.network === 'testnet' ? networks.testnet : networks.livenet; |
|||
}; |
|||
TransactionDb.superclass = superclass; |
|||
|
|||
TransactionDb.prototype.close = function(cb) { |
|||
db.close(cb); |
|||
}; |
|||
|
|||
TransactionDb.prototype.drop = function(cb) { |
|||
var path = config.leveldb + '/txs'; |
|||
db.close(function() { |
|||
require('leveldown').destroy(path, function() { |
|||
db = levelup(path, {maxOpenFiles: 500}); |
|||
return cb(); |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
|
|||
TransactionDb.prototype.has = function(txid, cb) { |
|||
|
|||
var k = OUTS_PREFIX + txid; |
|||
db.get(k, function(err, val) { |
|||
|
|||
var ret; |
|||
|
|||
if (err && err.notFound) { |
|||
err = null; |
|||
ret = false; |
|||
} |
|||
if (typeof val !== undefined) { |
|||
ret = true; |
|||
} |
|||
return cb(err, ret); |
|||
}); |
|||
}; |
|||
|
|||
TransactionDb.prototype._addSpentInfo = function(r, txid, index, ts) { |
|||
if (r.spentTxId) { |
|||
if (!r.multipleSpentAttempts) { |
|||
r.multipleSpentAttempts = [{ |
|||
txid: r.spentTxId, |
|||
index: r.index, |
|||
}]; |
|||
} |
|||
r.multipleSpentAttempts.push({ |
|||
txid: txid, |
|||
index: parseInt(index), |
|||
}); |
|||
} else { |
|||
r.spentTxId = txid; |
|||
r.spentIndex = parseInt(index); |
|||
r.spentTs = parseInt(ts); |
|||
} |
|||
}; |
|||
|
|||
|
|||
// This is not used now
|
|||
TransactionDb.prototype.fromTxId = function(txid, cb) { |
|||
var self = this; |
|||
var k = OUTS_PREFIX + txid; |
|||
var ret = []; |
|||
var idx = {}; |
|||
var i = 0; |
|||
|
|||
// outs.
|
|||
db.createReadStream({ |
|||
start: k, |
|||
end: k + '~' |
|||
}) |
|||
.on('data', function(data) { |
|||
var k = data.key.split('-'); |
|||
var v = data.value.split(':'); |
|||
ret.push({ |
|||
addr: v[0], |
|||
value_sat: parseInt(v[1]), |
|||
index: parseInt(k[2]), |
|||
}); |
|||
idx[parseInt(k[2])] = i++; |
|||
}) |
|||
.on('error', function(err) { |
|||
return cb(err); |
|||
}) |
|||
.on('end', function() { |
|||
|
|||
var k = SPENT_PREFIX + txid + '-'; |
|||
db.createReadStream({ |
|||
start: k, |
|||
end: k + '~' |
|||
}) |
|||
.on('data', function(data) { |
|||
var k = data.key.split('-'); |
|||
var j = idx[parseInt(k[2])]; |
|||
|
|||
assert(typeof j !== 'undefined', 'Spent could not be stored: tx ' + txid + |
|||
'spent in TX:' + k[1] + ',' + k[2] + ' j:' + j); |
|||
|
|||
self._addSpentInfo(ret[j], k[3], k[4], data.value); |
|||
}) |
|||
.on('error', function(err) { |
|||
return cb(err); |
|||
}) |
|||
.on('end', function(err) { |
|||
return cb(err, ret); |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
|
|||
TransactionDb.prototype._fillSpent = function(info, cb) { |
|||
var self = this; |
|||
|
|||
if (!info) return cb(); |
|||
|
|||
var k = SPENT_PREFIX + info.txid + '-'; |
|||
db.createReadStream({ |
|||
start: k, |
|||
end: k + '~' |
|||
}) |
|||
.on('data', function(data) { |
|||
var k = data.key.split('-'); |
|||
self._addSpentInfo(info.vout[k[2]], k[3], k[4], data.value); |
|||
}) |
|||
.on('error', function(err) { |
|||
return cb(err); |
|||
}) |
|||
.on('end', function(err) { |
|||
return cb(err); |
|||
}); |
|||
}; |
|||
|
|||
|
|||
TransactionDb.prototype._fillOutpoints = function(info, cb) { |
|||
var self = this; |
|||
|
|||
if (!info || info.isCoinBase) return cb(); |
|||
|
|||
var valueIn = 0; |
|||
var incompleteInputs = 0; |
|||
|
|||
async.eachLimit(info.vin, CONCURRENCY, function(i, c_in) { |
|||
self.fromTxIdN(i.txid, i.vout, info.confirmations, function(err, ret) { |
|||
//console.log('[TransactionDb.js.154:ret:]',ret); //TODO
|
|||
if (!ret || !ret.addr || !ret.valueSat) { |
|||
console.log('Could not get TXouts in %s,%d from %s ', i.txid, i.vout, info.txid); |
|||
if (ret) i.unconfirmedInput = ret.unconfirmedInput; |
|||
incompleteInputs = 1; |
|||
return c_in(); // error not scalated
|
|||
} |
|||
|
|||
info.firstSeenTs = ret.spentTs; |
|||
i.unconfirmedInput = i.unconfirmedInput; |
|||
i.addr = ret.addr; |
|||
i.valueSat = ret.valueSat; |
|||
i.value = ret.valueSat / util.COIN; |
|||
valueIn += i.valueSat; |
|||
|
|||
/* |
|||
* If confirmed by bitcoind, we could not check for double spents |
|||
* but we prefer to keep the flag of double spent attempt |
|||
* |
|||
if (info.confirmations |
|||
&& info.confirmations >= CONFIRMATION_NR_TO_NOT_CHECK) |
|||
return c_in(); |
|||
isspent |
|||
*/ |
|||
// Double spent?
|
|||
if (ret.multipleSpentAttempt || !ret.spentTxId || |
|||
(ret.spentTxId && ret.spentTxId !== info.txid) |
|||
) { |
|||
if (ret.multipleSpentAttempts) { |
|||
ret.multipleSpentAttempts.each(function(mul) { |
|||
if (mul.spentTxId !== info.txid) { |
|||
i.doubleSpentTxID = ret.spentTxId; |
|||
i.doubleSpentIndex = ret.spentIndex; |
|||
} |
|||
}); |
|||
} else if (!ret.spentTxId) { |
|||
i.dbError = 'Input spent not registered'; |
|||
} else { |
|||
i.doubleSpentTxID = ret.spentTxId; |
|||
i.doubleSpentIndex = ret.spentIndex; |
|||
} |
|||
} else { |
|||
i.doubleSpentTxID = null; |
|||
} |
|||
return c_in(); |
|||
}); |
|||
}, |
|||
function() { |
|||
if (!incompleteInputs) { |
|||
info.valueIn = valueIn / util.COIN; |
|||
info.fees = (valueIn - parseInt(info.valueOut * util.COIN)) / util.COIN; |
|||
} else { |
|||
info.incompleteInputs = 1; |
|||
} |
|||
return cb(); |
|||
}); |
|||
}; |
|||
|
|||
TransactionDb.prototype._getInfo = function(txid, next) { |
|||
var self = this; |
|||
|
|||
Rpc.getTxInfo(txid, function(err, info) { |
|||
if (err) return next(err); |
|||
|
|||
self._fillOutpoints(info, function() { |
|||
self._fillSpent(info, function() { |
|||
return next(null, info); |
|||
}); |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
|
|||
// Simplified / faster Info version: No spent / outpoints info.
|
|||
TransactionDb.prototype.fromIdInfoSimple = function(txid, cb) { |
|||
Rpc.getTxInfo(txid, true, function(err, info) { |
|||
if (err) return cb(err); |
|||
if (!info) return cb(); |
|||
return cb(err, info); |
|||
}); |
|||
}; |
|||
|
|||
TransactionDb.prototype.fromIdWithInfo = function(txid, cb) { |
|||
var self = this; |
|||
|
|||
self._getInfo(txid, function(err, info) { |
|||
if (err) return cb(err); |
|||
if (!info) return cb(); |
|||
return cb(err, { |
|||
txid: txid, |
|||
info: info |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
TransactionDb.prototype.fromTxIdN = function(txid, n, confirmations, cb) { |
|||
var self = this; |
|||
var k = OUTS_PREFIX + txid + '-' + n; |
|||
|
|||
db.get(k, function(err, val) { |
|||
if (!val || (err && err.notFound)) { |
|||
return cb(null, { |
|||
unconfirmedInput: 1 |
|||
}); |
|||
} |
|||
|
|||
var a = val.split(':'); |
|||
var ret = { |
|||
addr: a[0], |
|||
valueSat: parseInt(a[1]), |
|||
}; |
|||
|
|||
/* |
|||
* If this TxID comes from an RPC request |
|||
* the .confirmations value from bitcoind is available |
|||
* so we could avoid checking if the input were double spented |
|||
* |
|||
* This speed up address calculations by ~30% |
|||
* |
|||
if (confirmations >= CONFIRMATION_NR_TO_NOT_CHECK) { |
|||
return cb(null, ret); |
|||
} |
|||
*/ |
|||
|
|||
// spent?
|
|||
var k = SPENT_PREFIX + txid + '-' + n + '-'; |
|||
db.createReadStream({ |
|||
start: k, |
|||
end: k + '~' |
|||
}) |
|||
.on('data', function(data) { |
|||
var k = data.key.split('-'); |
|||
self._addSpentInfo(ret, k[3], k[4], data.value); |
|||
}) |
|||
.on('error', function(error) { |
|||
return cb(error); |
|||
}) |
|||
.on('end', function() { |
|||
return cb(null, ret); |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
TransactionDb.prototype.fillConfirmations = function(o, cb) { |
|||
var self = this; |
|||
|
|||
self.isConfirmed(o.txid, function(err, is) { |
|||
if (err) return cb(err); |
|||
|
|||
o.isConfirmed = is; |
|||
if (!o.spentTxId) return cb(); |
|||
|
|||
if (o.multipleSpentAttempts) { |
|||
|
|||
async.eachLimit(o.multipleSpentAttempts, CONCURRENCY, |
|||
function(oi, e_c) { |
|||
self.isConfirmed(oi.spentTxId, function(err, is) { |
|||
if (err) return; |
|||
if (is) { |
|||
o.spentTxId = oi.spentTxId; |
|||
o.index = oi.index; |
|||
o.spentIsConfirmed = 1; |
|||
} |
|||
return e_c(); |
|||
}); |
|||
}, cb); |
|||
} else { |
|||
self.isConfirmed(o.spentTxId, function(err, is) { |
|||
if (err) return cb(err); |
|||
o.spentIsConfirmed = is; |
|||
return cb(); |
|||
}); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
TransactionDb.prototype.fromAddr = function(addr, cb) { |
|||
var self = this; |
|||
|
|||
var k = ADDR_PREFIX + addr + '-'; |
|||
var ret = []; |
|||
|
|||
db.createReadStream({ |
|||
start: k, |
|||
end: k + '~' |
|||
}) |
|||
.on('data', function(data) { |
|||
var k = data.key.split('-'); |
|||
var v = data.value.split(':'); |
|||
ret.push({ |
|||
txid: k[2], |
|||
index: parseInt(k[3]), |
|||
value_sat: parseInt(v[0]), |
|||
ts: parseInt(v[1]), |
|||
}); |
|||
}) |
|||
.on('error', function(err) { |
|||
return cb(err); |
|||
}) |
|||
.on('end', function() { |
|||
|
|||
async.eachLimit(ret, CONCURRENCY, function(o, e_c) { |
|||
var k = SPENT_PREFIX + o.txid + '-' + o.index + '-'; |
|||
db.createReadStream({ |
|||
start: k, |
|||
end: k + '~' |
|||
}) |
|||
.on('data', function(data) { |
|||
var k = data.key.split('-'); |
|||
self._addSpentInfo(o, k[3], k[4], data.value); |
|||
}) |
|||
.on('error', function(err) { |
|||
return e_c(err); |
|||
}) |
|||
.on('end', function(err) { |
|||
return e_c(err); |
|||
}); |
|||
}, |
|||
function() { |
|||
async.eachLimit(ret, CONCURRENCY, function(o, e_c) { |
|||
self.fillConfirmations(o, e_c); |
|||
}, function(err) { |
|||
return cb(err, ret); |
|||
}); |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
|
|||
TransactionDb.prototype.removeFromTxId = function(txid, cb) { |
|||
|
|||
async.series([ |
|||
|
|||
function(c) { |
|||
db.createReadStream({ |
|||
start: OUTS_PREFIX + txid + '-', |
|||
end: OUTS_PREFIX + txid + '~', |
|||
}).pipe( |
|||
db.createWriteStream({ |
|||
type: 'del' |
|||
}) |
|||
).on('close', c); |
|||
}, |
|||
function(c) { |
|||
db.createReadStream({ |
|||
start: SPENT_PREFIX + txid + '-', |
|||
end: SPENT_PREFIX + txid + '~' |
|||
}) |
|||
.pipe( |
|||
db.createWriteStream({ |
|||
type: 'del' |
|||
}) |
|||
).on('close', c); |
|||
} |
|||
], |
|||
function(err) { |
|||
cb(err); |
|||
}); |
|||
|
|||
}; |
|||
|
|||
|
|||
// TODO. replace with
|
|||
// Script.prototype.getAddrStrs if that one get merged in bitcore
|
|||
TransactionDb.prototype.getAddrStr = function(s) { |
|||
var self = this; |
|||
|
|||
var addrStrs = []; |
|||
var type = s.classify(); |
|||
var addr; |
|||
|
|||
switch (type) { |
|||
case Script.TX_PUBKEY: |
|||
var chunk = s.captureOne(); |
|||
addr = new Address(self.network.addressPubkey, bitutil.sha256ripe160(chunk)); |
|||
addrStrs.push(addr.toString()); |
|||
break; |
|||
case Script.TX_PUBKEYHASH: |
|||
addr = new Address(self.network.addressPubkey, s.captureOne()); |
|||
addrStrs.push(addr.toString()); |
|||
break; |
|||
case Script.TX_SCRIPTHASH: |
|||
addr = new Address(self.network.addressScript, s.captureOne()); |
|||
addrStrs.push(addr.toString()); |
|||
break; |
|||
case Script.TX_MULTISIG: |
|||
var chunks = s.capture(); |
|||
chunks.forEach(function(chunk) { |
|||
var a = new Address(self.network.addressPubkey, bitutil.sha256ripe160(chunk)); |
|||
addrStrs.push(a.toString()); |
|||
}); |
|||
break; |
|||
case Script.TX_UNKNOWN: |
|||
break; |
|||
} |
|||
|
|||
return addrStrs; |
|||
}; |
|||
|
|||
TransactionDb.prototype.adaptTxObject = function(txInfo) { |
|||
var self = this; |
|||
// adapt bitcore TX object to bitcoind JSON response
|
|||
txInfo.txid = txInfo.hash; |
|||
|
|||
|
|||
var to = 0; |
|||
var tx = txInfo; |
|||
if (tx.outs) { |
|||
tx.outs.forEach(function(o) { |
|||
var s = new Script(o.s); |
|||
var addrs = self.getAddrStr(s); |
|||
|
|||
// support only for p2pubkey p2pubkeyhash and p2sh
|
|||
if (addrs.length === 1) { |
|||
tx.out[to].addrStr = addrs[0]; |
|||
tx.out[to].n = to; |
|||
} |
|||
to++; |
|||
}); |
|||
} |
|||
|
|||
var count = 0; |
|||
txInfo.vin = txInfo. in .map(function(txin) { |
|||
var i = {}; |
|||
|
|||
if (txin.coinbase) { |
|||
txInfo.isCoinBase = true; |
|||
} else { |
|||
i.txid = txin.prev_out.hash; |
|||
i.vout = txin.prev_out.n; |
|||
} |
|||
i.n = count++; |
|||
return i; |
|||
}); |
|||
|
|||
|
|||
count = 0; |
|||
txInfo.vout = txInfo.out.map(function(txout) { |
|||
var o = {}; |
|||
|
|||
o.value = txout.value; |
|||
o.n = count++; |
|||
|
|||
if (txout.addrStr) { |
|||
o.scriptPubKey = {}; |
|||
o.scriptPubKey.addresses = [txout.addrStr]; |
|||
} |
|||
return o; |
|||
}); |
|||
}; |
|||
|
|||
|
|||
|
|||
TransactionDb.prototype.add = function(tx, blockhash, cb) { |
|||
var self = this; |
|||
var addrs = []; |
|||
|
|||
if (tx.hash) self.adaptTxObject(tx); |
|||
|
|||
var ts = tx.time; |
|||
|
|||
async.series([ |
|||
// Input Outpoints (mark them as spent)
|
|||
function(p_c) { |
|||
if (tx.isCoinBase) return p_c(); |
|||
async.forEachLimit(tx.vin, CONCURRENCY, |
|||
function(i, next_out) { |
|||
db.batch() |
|||
.put(SPENT_PREFIX + i.txid + '-' + i.vout + '-' + tx.txid + '-' + i.n, |
|||
ts || 0) |
|||
.write(next_out); |
|||
}, |
|||
function(err) { |
|||
return p_c(err); |
|||
}); |
|||
}, |
|||
// Parse Outputs
|
|||
function(p_c) { |
|||
async.forEachLimit(tx.vout, CONCURRENCY, |
|||
function(o, next_out) { |
|||
if (o.value && o.scriptPubKey && |
|||
o.scriptPubKey.addresses && |
|||
o.scriptPubKey.addresses[0] && !o.scriptPubKey.addresses[1] // TODO : not supported
|
|||
) { |
|||
var addr = o.scriptPubKey.addresses[0]; |
|||
var sat = Math.round(o.value * util.COIN); |
|||
|
|||
if (addrs.indexOf(addr) === -1) { |
|||
addrs.push(addr); |
|||
} |
|||
|
|||
// existed?
|
|||
var k = OUTS_PREFIX + tx.txid + '-' + o.n; |
|||
db.get(k, function(err) { |
|||
if (err && err.notFound) { |
|||
db.batch() |
|||
.put(k, addr + ':' + sat) |
|||
.put(ADDR_PREFIX + addr + '-' + tx.txid + '-' + o.n, sat + ':' + ts) |
|||
.write(next_out); |
|||
} else { |
|||
return next_out(); |
|||
} |
|||
}); |
|||
} else { |
|||
return next_out(); |
|||
} |
|||
}, |
|||
function(err) { |
|||
if (err) { |
|||
console.log('ERR at TX %s: %s', tx.txid, err); |
|||
return cb(err); |
|||
} |
|||
return p_c(); |
|||
}); |
|||
}, |
|||
function(p_c) { |
|||
if (!blockhash) { |
|||
return p_c(); |
|||
} |
|||
return self.setConfirmation(tx.txid, blockhash, true, p_c); |
|||
}, |
|||
], function(err) { |
|||
if (addrs.length > 0 && !blockhash) { |
|||
// only emit if we are processing a single tx (not from a block)
|
|||
addrs.forEach(function(addr) { |
|||
self.emit('tx_for_address', { |
|||
address: addr, |
|||
txid: tx.txid |
|||
}); |
|||
}); |
|||
} |
|||
self.emit('new_tx', { |
|||
tx: tx |
|||
}); |
|||
|
|||
return cb(err); |
|||
}); |
|||
}; |
|||
|
|||
|
|||
|
|||
TransactionDb.prototype.setConfirmation = function(txId, blockHash, confirmed, c) { |
|||
if (!blockHash) return c(); |
|||
|
|||
confirmed = confirmed ? 1 : 0; |
|||
|
|||
db.batch() |
|||
.put(IN_BLK_PREFIX + txId + '-' + blockHash, confirmed) |
|||
.put(FROM_BLK_PREFIX + blockHash + '-' + txId, 1) |
|||
.write(c); |
|||
}; |
|||
|
|||
|
|||
// This slowdown addr balance calculation by 100%
|
|||
TransactionDb.prototype.isConfirmed = function(txId, c) { |
|||
var k = IN_BLK_PREFIX + txId; |
|||
var ret = false; |
|||
|
|||
db.createReadStream({ |
|||
start: k, |
|||
end: k + '~' |
|||
}) |
|||
.on('data', function(data) { |
|||
if (data.value === '1') ret = true; |
|||
}) |
|||
.on('error', function(err) { |
|||
return c(err); |
|||
}) |
|||
.on('end', function(err) { |
|||
return c(err, ret); |
|||
}); |
|||
}; |
|||
|
|||
TransactionDb.prototype.handleBlockChange = function(hash, isMain, cb) { |
|||
var toChange = []; |
|||
console.log('\tSearching Txs from block:' + hash); |
|||
|
|||
var k = FROM_BLK_PREFIX + hash; |
|||
var k2 = IN_BLK_PREFIX; |
|||
// This is slow, but prevent us to create a new block->tx index.
|
|||
db.createReadStream({ |
|||
start: k, |
|||
end: k + '~' |
|||
}) |
|||
.on('data', function(data) { |
|||
var ks = data.key.split('-'); |
|||
toChange.push({ |
|||
key: k2 + ks[2] + '-' + ks[1], |
|||
type: 'put', |
|||
value: isMain ? 1 : 0, |
|||
}); |
|||
}) |
|||
.on('error', function(err) { |
|||
return cb(err); |
|||
}) |
|||
.on('end', function(err) { |
|||
if (err) return cb(err); |
|||
console.log('\t%s %d Txs', isMain ? 'Confirming' : 'Invalidating', toChange.length); |
|||
db.batch(toChange, cb); |
|||
}); |
|||
}; |
|||
|
|||
// txs can be a [hashes] or [txObjects]
|
|||
TransactionDb.prototype.createFromArray = function(txs, blockHash, next) { |
|||
var self = this; |
|||
if (!txs) return next(); |
|||
|
|||
async.forEachLimit(txs, CONCURRENCY, function(t, each_cb) { |
|||
if (typeof t === 'string') { |
|||
// TODO: parse it from networks.genesisTX?
|
|||
if (t === genesisTXID) return each_cb(); |
|||
|
|||
Rpc.getTxInfo(t, function(err, inInfo) { |
|||
if (!inInfo) return each_cb(err); |
|||
|
|||
return self.add(inInfo, blockHash, each_cb); |
|||
}); |
|||
} else { |
|||
return self.add(t, blockHash, each_cb); |
|||
} |
|||
}, |
|||
function(err) { |
|||
return next(err); |
|||
}); |
|||
}; |
|||
|
|||
|
|||
TransactionDb.prototype.createFromBlock = function(b, next) { |
|||
var self = this; |
|||
if (!b || !b.tx) return next(); |
|||
|
|||
return self.createFromArray(b.tx, b.hash, next); |
|||
}; |
|||
|
|||
return TransactionDb; |
|||
} |
|||
module.defineClass(spec); |
File diff suppressed because one or more lines are too long
Binary file not shown.
After Width: | Height: | Size: 61 KiB |
Binary file not shown.
Binary file not shown.
@ -0,0 +1,26 @@ |
|||
<!doctype html> |
|||
<html lang="en" data-ng-app="insight" data-ng-csp> |
|||
<head> |
|||
<meta charset="UTF-8"> |
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> |
|||
<meta name="viewport" content="width=device-width,initial-scale=1.0"> |
|||
<meta name="fragment" content="!"> |
|||
<title data-ng-bind="$root.title + $root.titleDetail + ' | Insight'">Insight</title> |
|||
<meta name="keywords" content="bitcoins, transactions, blocks, address, block chain, best block, mining difficulty, hash serialized"> |
|||
<meta name="description" content="Bitcoin Insight. View detailed information on all bitcoin transactions and block. {{ $root.title + $root.titleDetail }}"> |
|||
<link rel="shortcut icon" href="/img/icons/favicon.ico" type="image/x-icon"> |
|||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Ubuntu:300,400,500,700,400italic"> |
|||
<link rel="stylesheet" href="/css/main.min.css"> |
|||
</head> |
|||
<body> |
|||
<div id="wrap"> |
|||
<div class="navbar navbar-default navbar-fixed-top" data-ng-include="'/views/includes/header.html'" role='navigation'></div> |
|||
<section class="container" data-ng-view></section> |
|||
</div> |
|||
<div id="footer" data-ng-include="'/views/includes/footer.html'" role="navigation"></div> |
|||
<script src="/socket.io/socket.io.js"></script> |
|||
<script src="/js/vendors.min.js"></script> |
|||
<script src="/js/angularjs-all.min.js"></script> |
|||
<script src="/js/main.min.js"></script> |
|||
</body> |
|||
</html> |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,170 +0,0 @@ |
|||
#!/usr/bin/env node
|
|||
'use strict'; |
|||
|
|||
process.env.NODE_ENV = process.env.NODE_ENV || 'development'; |
|||
|
|||
|
|||
|
|||
var assert = require('assert'), |
|||
fs = require('fs'), |
|||
util = require('util'), |
|||
TransactionDb = require('../../lib/TransactionDb').class(); |
|||
|
|||
var txItemsValid = JSON.parse(fs.readFileSync('test/integration/txitems.json')); |
|||
var txDb; |
|||
|
|||
describe('TransactionDb fromIdWithInfo', function(){ |
|||
|
|||
before(function(c) { |
|||
txDb = new TransactionDb(); |
|||
return c(); |
|||
}); |
|||
|
|||
|
|||
var txid = '7e621eeb02874ab039a8566fd36f4591e65eca65313875221842c53de6907d6c'; |
|||
it('tx info ' + txid, function(done) { |
|||
txDb.fromIdWithInfo(txid, function(err, tx) { |
|||
|
|||
if (err) done(err); |
|||
assert.equal(tx.txid, txid); |
|||
assert(!tx.info.isCoinBase); |
|||
|
|||
for(var i=0; i<20; i++) |
|||
assert(parseFloat(tx.info.vin[i].value) === parseFloat(50), 'input '+i); |
|||
assert(tx.info.vin[0].addr === 'msGKGCy2i8wbKS5Fo1LbWUTJnf1GoFFG59', 'addr 0'); |
|||
assert(tx.info.vin[1].addr === 'mfye7oHsdrHbydtj4coPXCasKad2eYSv5P', 'addr 1'); |
|||
done(); |
|||
}); |
|||
}); |
|||
|
|||
it('tx info', function(done) { |
|||
var txid = '21798ddc9664ac0ef618f52b151dda82dafaf2e26d2bbef6cdaf55a6957ca237'; |
|||
txDb.fromIdWithInfo(txid, function(err, tx) { |
|||
if (err) done(err); |
|||
assert.equal(tx.txid, txid); |
|||
assert(!tx.info.isCoinBase); |
|||
done(); |
|||
}); |
|||
}); |
|||
|
|||
it('should pool tx\'s info from bitcoind', function(done) { |
|||
var txid = '21798ddc9664ac0ef618f52b151dda82dafaf2e26d2bbef6cdaf55a6957ca237'; |
|||
txDb.fromIdWithInfo(txid, function(err, tx) { |
|||
if (err) done(err); |
|||
assert.equal(tx.info.txid, txid); |
|||
assert.equal(tx.info.blockhash, '000000000185678d3d7ecc9962c96418174431f93fe20bf216d5565272423f74'); |
|||
assert.equal(tx.info.valueOut, 1.66174); |
|||
assert.equal(tx.info.fees, 0.0005 ); |
|||
assert.equal(tx.info.size, 226 ); |
|||
assert(!tx.info.isCoinBase); |
|||
done(); |
|||
}); |
|||
}); |
|||
|
|||
var txid1 = '2a104bab1782e9b6445583296d4a0ecc8af304e4769ceb64b890e8219c562399'; |
|||
it('test a coinbase TX ' + txid1, function(done) { |
|||
txDb.fromIdWithInfo(txid1, function(err, tx) { |
|||
if (err) done(err); |
|||
assert(tx.info.isCoinBase); |
|||
assert.equal(tx.info.txid, txid1); |
|||
assert(!tx.info.feeds); |
|||
done(); |
|||
}); |
|||
}); |
|||
var txid22 = '666'; |
|||
it('test invalid TX ' + txid22, function(done) { |
|||
txDb.fromIdWithInfo(txid22, function(err, tx) { |
|||
if (err && err.message.match(/must.be.hexadecimal/)) { |
|||
return done(); |
|||
} |
|||
else { |
|||
return done(err); |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
var txid23 = '21798ddc9664ac0ef618f52b151dda82dafaf2e26d2bbef6cdaf55a6957ca227'; |
|||
it('test unexisting TX ' + txid23, function(done) { |
|||
|
|||
txDb.fromIdWithInfo(txid23, function(err, tx) { |
|||
assert(!err); |
|||
assert(!tx); |
|||
return done(); |
|||
}); |
|||
}); |
|||
|
|||
|
|||
|
|||
var txid2 = '64496d005faee77ac5a18866f50af6b8dd1f60107d6795df34c402747af98608'; |
|||
it('create TX on the fly ' + txid2, function(done) { |
|||
txDb.fromIdWithInfo(txid2, function(err, tx) { |
|||
if (err) return done(err); |
|||
assert.equal(tx.info.txid, txid2); |
|||
done(); |
|||
}); |
|||
}); |
|||
|
|||
txid2 = '64496d005faee77ac5a18866f50af6b8dd1f60107d6795df34c402747af98608'; |
|||
it('test a broken TX ' + txid2, function(done) { |
|||
txDb.fromIdWithInfo(txid2, function(err, tx) { |
|||
if (err) return done(err); |
|||
assert.equal(tx.info.txid, txid2); |
|||
assert.equal(tx.info.vin[0].addr, 'n1JagbRWBDi6VMvG7HfZmXX74dB9eiHJzU'); |
|||
done(); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
describe('TransactionDb Outs', function(){ |
|||
|
|||
before(function(c) { |
|||
txDb = new TransactionDb(); |
|||
return c(); |
|||
}); |
|||
|
|||
txItemsValid.forEach( function(v) { |
|||
if (v.disabled) return; |
|||
it('test a processing tx ' + v.txid, function(done) { |
|||
this.timeout(60000); |
|||
|
|||
// Remove first
|
|||
txDb.removeFromTxId(v.txid, function() { |
|||
|
|||
txDb.fromTxId( v.txid, function(err, readItems) { |
|||
assert.equal(readItems.length,0); |
|||
|
|||
var unmatch=[]; |
|||
txDb.createFromArray([v.txid], null, function(err) { |
|||
if (err) return done(err); |
|||
|
|||
txDb.fromTxId( v.txid, function(err, readItems) { |
|||
|
|||
v.items.forEach(function(validItem){ |
|||
unmatch[validItem.addr] =1; |
|||
}); |
|||
assert.equal(readItems.length,v.items.length); |
|||
|
|||
v.items.forEach(function(validItem){ |
|||
var readItem = readItems.shift(); |
|||
|
|||
assert.equal(readItem.addr,validItem.addr); |
|||
assert.equal(readItem.value_sat,validItem.value_sat); |
|||
assert.equal(readItem.index,validItem.index); |
|||
assert.equal(readItem.spendIndex, null); |
|||
assert.equal(readItem.spendTxIdBuf, null); |
|||
delete unmatch[validItem.addr]; |
|||
}); |
|||
|
|||
var valid = util.inspect(v.items, { depth: null }); |
|||
assert(!Object.keys(unmatch).length,'\n\tUnmatchs:' + Object.keys(unmatch) + "\n\n" +valid + '\nvs.\n' + readItems); |
|||
return done(); |
|||
|
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
|
@ -1,59 +0,0 @@ |
|||
#!/usr/bin/env node
|
|||
'use strict'; |
|||
|
|||
process.env.NODE_ENV = process.env.NODE_ENV || 'development'; |
|||
|
|||
|
|||
|
|||
var |
|||
assert = require('assert'), |
|||
fs = require('fs'), |
|||
util = require('util'), |
|||
async = require('async'), |
|||
config = require('../../config/config'), |
|||
TransactionDb = require('../../lib/TransactionDb').class(); |
|||
|
|||
var spentValid = JSON.parse(fs.readFileSync('test/integration/spent.json')); |
|||
|
|||
var txDb; |
|||
|
|||
describe('TransactionDb Expenses', function(){ |
|||
|
|||
before(function(c) { |
|||
txDb = new TransactionDb(); |
|||
|
|||
// lets spend!
|
|||
async.each(Object.keys(spentValid), |
|||
function(txid,c_out) { |
|||
async.each(spentValid[txid], |
|||
function(i,c_in) { |
|||
txDb.createFromArray([i.txid], null, function(err) { |
|||
return c_in(); |
|||
}); |
|||
}, |
|||
function(err) { |
|||
return c_out(); |
|||
} |
|||
); |
|||
}, |
|||
function(err) { |
|||
return c(); |
|||
} |
|||
); |
|||
}); |
|||
|
|||
Object.keys(spentValid).forEach( function(txid) { |
|||
it('test result of spending tx ' + txid, function(done) { |
|||
var s = spentValid[txid]; |
|||
var c=0; |
|||
txDb.fromTxId( txid, function(err, readItems) { |
|||
s.forEach( function(v) { |
|||
assert.equal(readItems[c].spentTxId,v.txid); |
|||
assert.equal(readItems[c].spentIndex,v.n); |
|||
c++; |
|||
}); |
|||
done(); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
@ -1,548 +0,0 @@ |
|||
#!/usr/bin/env node |
|||
'use strict'; |
|||
process.env.NODE_ENV = process.env.NODE_ENV || 'development'; |
|||
|
|||
|
|||
var |
|||
assert = require('assert'), |
|||
async = require('async'), |
|||
HistoricSync = require('../../lib/HistoricSync').class(); |
|||
|
|||
|
|||
var s; |
|||
var b = [ |
|||
'00000000c4cbd75af741f3a2b2ff72d9ed4d83a048462c1efe331be31ccf006b', //0 B#16 |
|||
'00000000fe198cce4c8abf9dca0fee1182cb130df966cc428ad2a230df8da743', //1 |
|||
'000000008d55c3e978639f70af1d2bf1fe6f09cb3143e104405a599215c89a48', //2 |
|||
'000000009b3bca4909f38313f2746120129cce4a699a1f552390955da470c5a9', //3 |
|||
'00000000ede57f31cc598dc241d129ccb4d8168ef112afbdc870dc60a85f5dd3', //4 B#20 |
|||
]; |
|||
var t = [ |
|||
'd08582d3711f75d085c618874fb0d049ae09d5ec95ec6f5abd289f4b54712c54', // TX from B#16 |
|||
'1729001087e0cebea8d14de1653d5cf59628d9746bc1ae65f776f1cbaff7ebad', //1 |
|||
'cf53d7ccd83a099acfbc319ee10c1e3b10e3d42ba675b569fdd6b69cb8d2db4e', //2 |
|||
'73a4988adf462b6540cfa59097804174b298cfa439f73c1a072c2c6fbdbe57c7', //3 |
|||
'd45f9da73619799e9d7bd03cc290e70875ea4cbad56b8bffa15135fbbb3df9ea', //4 Tx from B20 |
|||
]; |
|||
|
|||
var test = function(cb) { |
|||
async.each([2,3,4], function(i,c) { |
|||
s.sync.bDb.getPrev(b[i], function(err, p) { |
|||
assert.equal(p,b[i-1]); |
|||
return c(); |
|||
}); |
|||
}, function() { |
|||
async.each([0,1,2,3,4], function(i,c) { |
|||
s.sync.bDb.has(b[i], function(err, p) { |
|||
assert(p); |
|||
return c(); |
|||
}); |
|||
}, function() { |
|||
async.each([0,1,2,3], function(i,c) { |
|||
s.sync.bDb.getNext(b[i], function(err, p) { |
|||
assert.equal(p,b[i+1]); |
|||
return c(); |
|||
}); |
|||
}, cb); |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
/* |
|||
* TEST CASES |
|||
* |
|||
* Blocks: 0-1-2-3-4 |
|||
* case 1) |
|||
* 0-1-2-3-4 |
|||
* \ |
|||
* C1* |
|||
* |
|||
* case 2) |
|||
* 0-1-2---3-4 |
|||
* \ \ |
|||
* C1 C2* |
|||
* |
|||
* case 2b) |
|||
* 0-1-2---3-4 |
|||
* \ \ |
|||
* C1 C2-C2b(TX=C1.TX)* |
|||
* case 2c) |
|||
* 0-1-2---3-4 |
|||
* \ \ |
|||
* C1 C2-C2b(TX=C1.TX) |
|||
* \ |
|||
* C2c(TX=C2.TX)* |
|||
* |
|||
*/ |
|||
|
|||
describe('Sync Reorgs', function(){ |
|||
|
|||
before(function(done) { |
|||
s = new HistoricSync(); |
|||
s.init({}, function(err) { |
|||
if (err) return done(err); |
|||
s.sync.destroy(done); |
|||
}); |
|||
}); |
|||
|
|||
it('simple RPC forward syncing', function(done) { |
|||
s.getPrevNextBlock(s.genesis,b[4], { |
|||
next: true, |
|||
}, function(err) { |
|||
if (err) return done(err); |
|||
test(done); |
|||
}); |
|||
}); |
|||
|
|||
var case1 = { |
|||
hash: '0000000000000000000000000000000000000000000000000000000000000001', |
|||
tx: [ 'f0596531810160d090813673b4a397f4617aab44eb26c7f06c8a766eac984b91' ], |
|||
time: 1296690099, |
|||
previousblockhash: b[2], |
|||
}; |
|||
|
|||
|
|||
it('reorg, case 1', function(done) { |
|||
async.series([ |
|||
function (c) { |
|||
s.sync.txDb.isConfirmed(t[0], function(err,is) { |
|||
assert(!err); |
|||
assert(is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.txDb.isConfirmed(t[3], function(err,is) { |
|||
assert(!err); |
|||
assert(is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.txDb.isConfirmed(t[4], function(err,is) { |
|||
assert(!err); |
|||
assert(is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.storeTipBlock(case1, function(err) { |
|||
assert(!err, 'shouldnt return error' + err); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.bDb.isMain(b[2], function(err,is) { |
|||
assert(!err); |
|||
assert(is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.bDb.isMain(b[3], function(err,is) { |
|||
assert(!err); |
|||
assert(!is, b[3] + 'should not be on main chain'); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.bDb.isMain(b[4], function(err,is) { |
|||
assert(!err); |
|||
assert(!is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.txDb.isConfirmed(t[0], function(err,is) { |
|||
assert(!err); |
|||
assert(is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.txDb.isConfirmed(t[3], function(err,is) { |
|||
assert(!err); |
|||
assert(!is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.txDb.isConfirmed(t[4], function(err,is) { |
|||
assert(!err); |
|||
assert(!is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.txDb.isConfirmed(case1.tx[0], function(err,is) { |
|||
assert(!err); |
|||
assert(is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
], done ); |
|||
}); |
|||
|
|||
it('reorg, case 1 (repeat)', function(done) { |
|||
s.sync.storeTipBlock(case1, function(err) { |
|||
assert(!err, 'shouldnt return error' + err); |
|||
return done(); |
|||
}); |
|||
}); |
|||
|
|||
var case2 = { |
|||
hash: '0000000000000000000000000000000000000000000000000000000000000002', |
|||
tx: [ '99bb359a4b12a588fcb9e59e5e8d92d593ce7a56d2ba42085fe86d9a0b4fde15' ], |
|||
time: 1296690099, |
|||
previousblockhash: b[3], |
|||
}; |
|||
|
|||
|
|||
it('reorg, case 2', function(done) { |
|||
async.series([ |
|||
function (c) { |
|||
s.sync.txDb.isConfirmed(t[0], function(err,is) { |
|||
assert(!err); |
|||
assert(is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.txDb.isConfirmed(case1.tx[0], function(err,is) { |
|||
assert(!err); |
|||
assert(is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.txDb.isConfirmed(t[4], function(err,is) { |
|||
assert(!err); |
|||
assert(!is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.storeTipBlock(case2, function(err) { |
|||
assert(!err, 'shouldnt return error' + err); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.bDb.isMain(b[3], function(err,is) { |
|||
assert(!err); |
|||
assert(is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.bDb.isMain(b[4], function(err,is) { |
|||
assert(!err); |
|||
assert(!is, b[3] + 'should not be on main chain'); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.bDb.isMain(case1.hash, function(err,is) { |
|||
assert(!err); |
|||
assert(!is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.bDb.isMain(case2.hash, function(err,is) { |
|||
assert(!err); |
|||
assert(is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.txDb.isConfirmed(t[3], function(err,is) { |
|||
assert(!err); |
|||
assert(is, 'transaction t[3] should be valid:' + t[3]); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.txDb.isConfirmed(case1.tx[0], function(err,is) { |
|||
assert(!err); |
|||
assert(!is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.txDb.isConfirmed(case2.tx[0], function(err,is) { |
|||
assert(!err); |
|||
assert(is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.txDb.isConfirmed(t[4], function(err,is) { |
|||
assert(!err); |
|||
assert(!is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.bDb.getNext(b[2], function(err, val) { |
|||
assert(!err); |
|||
assert.equal(val,b[3]); |
|||
return c(); |
|||
}); |
|||
}, |
|||
|
|||
|
|||
|
|||
], done ); |
|||
}); |
|||
|
|||
|
|||
var case2b = { |
|||
hash: '0000000000000000000000000000000000000000000000000000000000000003', |
|||
tx: case1.tx, |
|||
time: 1296690099, |
|||
previousblockhash: case2.hash, |
|||
}; |
|||
|
|||
it('reorg, case 2b', function(done) { |
|||
async.series([ |
|||
function (c) { |
|||
s.sync.txDb.isConfirmed(case2b.tx[0], function(err,is) { |
|||
assert(!err); |
|||
assert(!is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.storeTipBlock(case2b, function(err) { |
|||
assert(!err, 'shouldnt return error' + err); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.txDb.isConfirmed(t[3], function(err,is) { |
|||
assert(!err); |
|||
assert(is, 'transaction t[3] should be valid:' + t[3]); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.txDb.isConfirmed(case2b.tx[0], function(err,is) { |
|||
assert(!err); |
|||
assert(is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
], done ); |
|||
}); |
|||
|
|||
|
|||
|
|||
var case2c = { |
|||
hash: '0000000000000000000000000000000000000000000000000000000000000004', |
|||
tx: case2.tx, |
|||
time: 1296690099, |
|||
previousblockhash: case1.hash, |
|||
}; |
|||
|
|||
it('reorg, case 2c', function(done) { |
|||
async.series([ |
|||
function (c) { |
|||
s.sync.txDb.isConfirmed(case1.tx[0], function(err,is) { |
|||
assert(!err); |
|||
assert(is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.bDb.isMain(case1.hash, function(err,is) { |
|||
assert(!err); |
|||
assert(!is, 'case1 block shouldnt be main:' + case1.hash); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.txDb.isConfirmed(case2c.tx[0], function(err,is) { |
|||
assert(!err); |
|||
assert(is); //It was there before (from case2) |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.storeTipBlock(case2c, function(err) { |
|||
assert(!err, 'shouldnt return error' + err); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.txDb.isConfirmed(case1.tx[0], function(err,is) { |
|||
assert(!err); |
|||
assert(is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.bDb.has(case1.hash, function(err,is) { |
|||
assert(!err); |
|||
assert(is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.bDb.has(case2c.hash, function(err,is) { |
|||
assert(!err); |
|||
assert(is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.txDb.isConfirmed(case2c.tx[0], function(err,is) { |
|||
assert(!err); |
|||
assert(is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.txDb.isConfirmed(t[3], function(err,is) { |
|||
assert(!err); |
|||
assert(!is, 'TX t[3]: shouldnt be confirmed:' + t[3] +':'+ is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.txDb.isConfirmed(t[4], function(err,is) { |
|||
assert(!err); |
|||
assert(!is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.txDb.isConfirmed(case2.tx[0], function(err,is) { |
|||
assert(!err); |
|||
assert(is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
|
|||
], done ); |
|||
}); |
|||
|
|||
var case3 = { |
|||
hash: '0000000000000000000000000000000000000000000000000000000000000005', |
|||
tx: case2.tx, |
|||
time: 1296690099, |
|||
previousblockhash: '666', |
|||
}; |
|||
|
|||
it('reorg, case 3)', function(done) { |
|||
async.series([ |
|||
function (c) { |
|||
s.sync.storeTipBlock(case3, function(err) { |
|||
assert(!err, 'shouldnt return error' + err); |
|||
return c(); |
|||
}); |
|||
}, |
|||
|
|||
//shoudnt change anything |
|||
function (c) { |
|||
s.sync.txDb.isConfirmed(case1.tx[0], function(err,is) { |
|||
assert(!err); |
|||
assert(is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.bDb.has(case1.hash, function(err,is) { |
|||
assert(!err); |
|||
assert(is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.bDb.has(case2c.hash, function(err,is) { |
|||
assert(!err); |
|||
assert(is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.txDb.isConfirmed(case2c.tx[0], function(err,is) { |
|||
assert(!err); |
|||
assert(is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.txDb.isConfirmed(t[3], function(err,is) { |
|||
assert(!err); |
|||
assert(!is, 'TX t[3]: shouldnt be confirmed:' + t[3] +':'+ is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.txDb.isConfirmed(t[4], function(err,is) { |
|||
assert(!err); |
|||
assert(!is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.txDb.isConfirmed(case2.tx[0], function(err,is) { |
|||
assert(!err); |
|||
assert(is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
|
|||
], done ); |
|||
}); |
|||
|
|||
var p2p = { |
|||
hash: '0000000000000000000000000000000000000000000000000000000000000006', |
|||
tx: ['f6c2901f39fd07f2f2e503183d76f73ecc1aee9ac9216fde58e867bc29ce674e'], |
|||
time: 1296690099, |
|||
previousblockhash: '111', |
|||
}; |
|||
|
|||
it('p2p, no reorg allowed', function(done) { |
|||
async.series([ |
|||
function (c) { |
|||
s.sync.storeTipBlock(p2p, false, function(err) { |
|||
assert(!err, 'shouldnt return error' + err); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.bDb.has(p2p.hash, function(err,is) { |
|||
assert(!err); |
|||
assert(is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.txDb.isConfirmed(p2p.tx[0], function(err,is) { |
|||
assert(!err); |
|||
assert(is); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.bDb.getNext(p2p.hash, function(err,v) { |
|||
assert(!err); |
|||
assert.equal(v,p2p.nextblockhash); |
|||
return c(); |
|||
}); |
|||
}, |
|||
function (c) { |
|||
s.sync.bDb.getNext(p2p.previousblockhash, function(err,v) { |
|||
assert(!err); |
|||
assert.equal(v,p2p.hash); |
|||
return c(); |
|||
}); |
|||
}, |
|||
|
|||
], done ); |
|||
}); |
|||
}); |
|||
|
|||
|
@ -1,81 +0,0 @@ |
|||
#!/usr/bin/env node
|
|||
|
|||
'use strict'; |
|||
|
|||
process.env.NODE_ENV = process.env.NODE_ENV || 'development'; |
|||
|
|||
var assert = require('assert'), |
|||
fs = require('fs'), |
|||
Address = require('../../app/models/Address').class(), |
|||
TransactionDb = require('../../lib/TransactionDb').class(), |
|||
addrValid = JSON.parse(fs.readFileSync('test/integration/addr.json')), |
|||
utxoValid = JSON.parse(fs.readFileSync('test/integration/utxo.json')); |
|||
|
|||
var txDb; |
|||
describe('Address balances', function() { |
|||
|
|||
before(function(c) { |
|||
txDb = new TransactionDb(); |
|||
return c(); |
|||
}); |
|||
|
|||
addrValid.forEach(function(v) { |
|||
if (v.disabled) { |
|||
console.log(v.addr + ' => disabled in JSON'); |
|||
} else { |
|||
it('Address info for: ' + v.addr, function(done) { |
|||
this.timeout(5000); |
|||
|
|||
var a = new Address(v.addr, txDb); |
|||
|
|||
a.update(function(err) { |
|||
if (err) done(err); |
|||
assert.equal(v.addr, a.addrStr); |
|||
assert.equal(a.unconfirmedTxApperances ,0, 'unconfirmedTxApperances: 0'); |
|||
assert.equal(a.unconfirmedBalanceSat ,0, 'unconfirmedBalanceSat: 0'); |
|||
if (v.txApperances) |
|||
assert.equal(v.txApperances, a.txApperances, 'txApperances: ' + a.txApperances); |
|||
if (v.totalReceived) assert.equal(v.totalReceived, a.totalReceived, 'received: ' + a.totalReceived); |
|||
if (v.totalSent) assert.equal(v.totalSent, a.totalSent, 'send: ' + a.totalSent); |
|||
|
|||
if (v.balance) assert.equal(v.balance, a.balance, 'balance: ' + a.balance); |
|||
|
|||
if (v.transactions) { |
|||
|
|||
v.transactions.forEach(function(tx) { |
|||
assert(a.transactions.indexOf(tx) > -1, 'have tx ' + tx); |
|||
}); |
|||
} |
|||
done(); |
|||
}); |
|||
}); |
|||
} |
|||
}); |
|||
|
|||
}); |
|||
|
|||
|
|||
describe('Address utxo', function() { |
|||
utxoValid.forEach(function(v) { |
|||
if (v.disabled) { |
|||
console.log(v.addr + ' => disabled in JSON'); |
|||
} else { |
|||
it('Address utxo for: ' + v.addr, function(done) { |
|||
this.timeout(50000); |
|||
|
|||
var a = new Address(v.addr, txDb); |
|||
a.getUtxo(function(err, utxo) { |
|||
if (err) done(err); |
|||
assert.equal(v.addr, a.addrStr); |
|||
if (v.length) assert.equal(v.length, utxo.length, 'length: ' + utxo.length); |
|||
if (v.tx0id) assert.equal(v.tx0id, utxo[0].txid, 'have tx: ' + utxo[0].txid); |
|||
if (v.tx0scriptPubKey) |
|||
assert.equal(v.tx0scriptPubKey, utxo[0].scriptPubKey, 'have tx: ' + utxo[0].scriptPubKey); |
|||
if (v.tx0amount) |
|||
assert.equal(v.tx0amount, utxo[0].amount, 'amount: ' + utxo[0].amount); |
|||
done(); |
|||
}); |
|||
}); |
|||
} |
|||
}); |
|||
}); |
@ -1,129 +0,0 @@ |
|||
[ |
|||
{ |
|||
"addr": "mp3Rzxx9s1A21SY3sjJ3CQoa2Xjph7e5eS", |
|||
"balance": 0, |
|||
"totalReceived": 50, |
|||
"totalSent": 50.0, |
|||
"txApperances": 2 |
|||
}, |
|||
{ |
|||
"addr": "muyg1K5WsHkfMVCkUXU2y7Xp5ZD6RGzCeH", |
|||
"balance": 0.38571339, |
|||
"totalReceived": 0.38571339, |
|||
"totalSent": 0, |
|||
"txApperances": 1 |
|||
}, |
|||
{ |
|||
"addr": "mhPEfAmeKVwT7arwMYbhwnL2TfwuWbP4r4", |
|||
"totalReceived": 1069, |
|||
"txApperances": 13, |
|||
"balance": 1065, |
|||
"totalSent": 4 |
|||
}, |
|||
{ |
|||
"addr": "n47CfqnKWdNwqY1UWxTmNJAqYutFxdH3zY", |
|||
"balance": 0, |
|||
"totalReceived":26.4245, |
|||
"totalSent": 26.4245, |
|||
"txApperances": 4 |
|||
}, |
|||
{ |
|||
"addr": "mzSyyXgofoBxpr6gYcU3cV345G8hJpixRd", |
|||
"balance": 0, |
|||
"totalReceived":3.9775, |
|||
"totalSent": 3.9775, |
|||
"txApperances": 2 |
|||
}, |
|||
{ |
|||
"addr": "mgqvRGJMwR9JU5VhJ3x9uX9MTkzTsmmDgQ", |
|||
"txApperances": 27, |
|||
"balance": 0, |
|||
"totalReceived": 54.81284116 |
|||
}, |
|||
{ |
|||
"addr": "mzW2hdZN2um7WBvTDerdahKqRgj3md9C29", |
|||
"balance": 1199.74393853, |
|||
"totalReceived": 1199.74393853, |
|||
"totalSent": 0, |
|||
"txApperances": 6048 |
|||
}, |
|||
{ |
|||
"addr": "mjRmkmYzvZN3cA3aBKJgYJ65epn3WCG84H", |
|||
"txApperances": 7164, |
|||
"balance": 46413.0, |
|||
"totalReceived": 357130.17644359, |
|||
"totalSent": 310717.17644359 |
|||
}, |
|||
{ |
|||
"addr": "mgKY35SXqxFpcKK3Dq9mW9919N7wYXvcFM", |
|||
"txApperances": 1, |
|||
"balance": 0.01979459, |
|||
"totalReceived": 0.01979459, |
|||
"totalSent": 0, |
|||
"transactions": [ "91800d80bb4c69b238c9bfd94eb5155ab821e6b25cae5c79903d12853bbb4ed5" ] |
|||
}, |
|||
{ |
|||
"addr": "mmvP3mTe53qxHdPqXEvdu8WdC7GfQ2vmx5", |
|||
"balance": 10580.50027254, |
|||
"totalReceived": 12157.65075053, |
|||
"totalSent": 1577.15047799, |
|||
"txApperances": 441, |
|||
"transactions": [ |
|||
"91800d80bb4c69b238c9bfd94eb5155ab821e6b25cae5c79903d12853bbb4ed5", |
|||
"f6e80d4fd1a2377406856c67d0cee5ac7e5120993ff97e617ca9aac33b4c6b1e", |
|||
"bc27f31caae86750b126d9b09e969362b85b7c15f41421387d682064544bf7e7", |
|||
"2cd6a1cb26880276fbc9851396f1bd8081cb2b9107ff6921e8fd65ed2df3df79", |
|||
"8bea41f573bccb7b648bc0b1bbfeba8a96da05b1d819ff4a33d39fbcd334ecfd", |
|||
"cb0d55c37acc57f759255193673e13858b5ab3d8fdfa7ee8b25f9964bdaa11e3", |
|||
"7b007aeace2299d27b6bb6c24d0a8040d6a87e4c2601216c34d226462b75f915", |
|||
"a9f40fbaecd2b28a05405e28b95566d7b3bd8ac38a2853debd72517f2994c6fc", |
|||
"4123255b7678e37c168b9e929927760bc5d9363b0c78ec61a7b4a78b2a07adab", |
|||
"cb3760529c2684c32047a2fddf0e2534c9241e5d72011aac4a8982e0c7b46df3", |
|||
"e8d00d8cc744381233dbc95e2d657345084dfb6df785b81285183f4c89b678d4", |
|||
"7a748364255c5b64979d9d3da35ea0fbef0114e0d7f96fccd5bea76f6d19f06b", |
|||
"d0b7e087040f67ef9bd9f21ccf53d1b5410400351d949cabf127caf28a6e7add", |
|||
"209f97873265652b83922921148cad92d7e048c6822e4864e984753e04181470", |
|||
"3a4af7755d3061ecced2f3707c2623534104f08aa73a52ca243d7ddecf5fe86d", |
|||
"4a4b5c8d464a77814ed35a37e2a28e821d467a803761427c057f67823309b725", |
|||
"d85f5265618fb694c3ea3ca6f73eba93df8a644bc1c7286cec2fbc2fbf7d895e", |
|||
"0d2c778ed9976b52792c941cac126bda37d3b1453082022d5e36ac401be3b249", |
|||
"daf03d666047ca0b5340b4a0027f8562b7c5bac87dca3727093b5393176a541a", |
|||
"a0dc03a870e589ea51e3d3a8aed0d34f4f1ae6844acad26dae48fe523b26e764", |
|||
"3df1a50e2e5d8525f04bd21a66bad824364a975449fa24fd5c2537d0f713919b", |
|||
"7bc26c1f3b4ab5ca57677593d28d13bff468a658f4d5efc379c1612554cf668e", |
|||
"ded4cbc9c52fd5599b6a93f89a79cde9aeb5a7f8f56732bb67ae9554325b3666", |
|||
"91224a219196a3f6e6f40ad2137b13fe54109e57aaed7527ea34aa903e6b8313", |
|||
"ee899a182bbb75e98ef14d83489e631dd66a8c5059dc8255692dd8ca9efba01f", |
|||
"0a61590c7548bd4f6a0df1575b268057e5e3e295a44eaeeb1dfbd01332c585ed", |
|||
"d56c22950ad2924f404b5b0baa6e49b0df1aaf09d1947842aed9d0178958eb9d", |
|||
"c6b5368c5a256141894972fbd02377b3894aa0df7c35fab5e0eca90de064fdc1", |
|||
"158e1f9c3f8ec44e88052cadef74e8eb99fbad5697d0b005ba48c933f7d96816", |
|||
"7f6191c0f4e3040901ef0d5d6e76af4f16423061ca1347524c86205e35d904d9", |
|||
"2c2e20f976b98a0ca76c57eca3653699b60c1bd9503cc9cc2fb755164a679a26", |
|||
"59bc81733ff0eaf2b106a70a655e22d2cdeff80ada27b937693993bf0c22e9ea", |
|||
"7da38b66fb5e8582c8be85abecfd744a6de89e738dd5f3aaa0270b218ec424eb", |
|||
"393d51119cdfbf0a308c0bbde2d4c63546c0961022bad1503c4bbaed0638c837", |
|||
"4518868741817ae6757fd98de27693b51fad100e89e5206b9bbf798aeebb804c", |
|||
"c58bce14de1e3016504babd8bbe8175207d75074134a2548a71743fa3e56c58d", |
|||
"6e69ec4a97515a8fd424f123a5fc1fdfd3c3adcd741292cbc09c09a2cc433bea", |
|||
"0e15f2498362050e5ceb6157d0fbf820fdcaf936e447207d433ee7701d7b99c2", |
|||
"a3789e113041db907a1217ddb5c3aaf0eff905cc3d913e68d977e1ab4d19acea", |
|||
"80b460922faf0ad1e8b8a55533654c9a9f3039bfff0fff2bcf8536b8adf95939" |
|||
] |
|||
}, |
|||
{ |
|||
"addr": "mtA6woo1wjCeu1dLkWgpSD3tRnRfrHt3FL", |
|||
"balance": 349.845, |
|||
"totalReceived": 349.845, |
|||
"totalSent": 0, |
|||
"txApperances": 13, |
|||
"transactions": [ |
|||
"794eafc0ad68a3576034eb137d7d20d3bdf1777ecf27e0e20e96e1adcfc66659", |
|||
"0130721f29f50b773858c3c9081678bdddebcd18078c5fa2436d979b54ed5cef", |
|||
"fb1024947b48d90255aedd3f0f1df3673a7e98d06346bb2ac89b116aa19c5db4" |
|||
] |
|||
} |
|||
|
|||
] |
|||
|
|||
|
@ -1,43 +0,0 @@ |
|||
#!/usr/bin/env node
|
|||
|
|||
'use strict'; |
|||
|
|||
process.env.NODE_ENV = process.env.NODE_ENV || 'development'; |
|||
|
|||
|
|||
var TESTING_BLOCK = '000000000185678d3d7ecc9962c96418174431f93fe20bf216d5565272423f74'; |
|||
|
|||
var |
|||
assert = require('assert'), |
|||
// config = require('../../config/config'),
|
|||
BlockDb = require('../../lib/BlockDb').class(); |
|||
|
|||
var bDb; |
|||
|
|||
describe('BlockDb fromHashWithInfo', function() { |
|||
|
|||
before(function(c) { |
|||
bDb = new BlockDb(); |
|||
return c(); |
|||
}); |
|||
|
|||
it('should poll block\'s info from bitcoind', function(done) { |
|||
bDb.fromHashWithInfo(TESTING_BLOCK, function(err, b2) { |
|||
if (err) done(err); |
|||
assert.equal(b2.hash, TESTING_BLOCK, 'hash'); |
|||
assert.equal(b2.info.hash, TESTING_BLOCK, 'info.hash'); |
|||
assert.equal(b2.info.height, 71619); |
|||
assert.equal(b2.info.nonce, 3960980741); |
|||
assert.equal(b2.info.bits, '1c018c14'); |
|||
assert.equal(b2.info.merkleroot, '9a326cb524aa2e5bc926b8c1f6de5b01257929ee02158054b55aae93a55ec9dd'); |
|||
assert.equal(b2.info.nextblockhash, '000000000121941b3b10d76fbe67b35993df91eb3398e9153e140b4f6213cb84'); |
|||
done(); |
|||
}); |
|||
}); |
|||
it('return true in has', function(done) { |
|||
bDb.has(TESTING_BLOCK, function(err, has) { |
|||
assert.equal(has, true); |
|||
done(); |
|||
}); |
|||
}); |
|||
}); |
@ -1,72 +0,0 @@ |
|||
#!/usr/bin/env node
|
|||
'use strict'; |
|||
|
|||
process.env.NODE_ENV = process.env.NODE_ENV || 'development'; |
|||
|
|||
|
|||
|
|||
var assert = require('assert'), |
|||
config = require('../../config/config'), |
|||
BlockExtractor = require('../../lib/BlockExtractor').class(), |
|||
networks = require('bitcore/networks'), |
|||
util = require('bitcore/util/util'); |
|||
|
|||
//var txItemsValid = JSON.parse(fs.readFileSync('test/model/txitems.json'));
|
|||
|
|||
describe('BlockExtractor', function(){ |
|||
|
|||
var be = new BlockExtractor(config.bitcoind.dataDir, config.network); |
|||
|
|||
var network = config.network === 'testnet' ? networks.testnet: networks.livenet; |
|||
|
|||
it('should glob block files ', function(done) { |
|||
assert(be.files.length>0); |
|||
done(); |
|||
}); |
|||
|
|||
var lastTs; |
|||
|
|||
it('should read genesis block ', function(done) { |
|||
be.getNextBlock(function(err,b) { |
|||
assert(!err); |
|||
var genesisHashReversed = new Buffer(32); |
|||
network.genesisBlock.hash.copy(genesisHashReversed); |
|||
var genesis = util.formatHashFull(network.genesisBlock.hash); |
|||
|
|||
assert.equal(util.formatHashFull(b.hash),genesis); |
|||
assert.equal(b.nounce,network.genesisBlock.nounce); |
|||
assert.equal(b.timestamp,network.genesisBlock.timestamp); |
|||
assert.equal(b.merkle_root.toString('hex'),network.genesisBlock.merkle_root.toString('hex')); |
|||
|
|||
lastTs = b.timestamp; |
|||
done(); |
|||
}); |
|||
}); |
|||
|
|||
it('should read next testnet block ', function(done) { |
|||
be.getNextBlock(function(err,b) { |
|||
assert(!err); |
|||
assert(b.timestamp > lastTs, 'timestamp > genesis_ts'); |
|||
done(); |
|||
}); |
|||
}); |
|||
|
|||
it.skip('should read 100000 blocks with no error ', function(done) { |
|||
|
|||
var i=0; |
|||
while(i++<100000) { |
|||
be.getNextBlock(function(err,b) { |
|||
assert(!err,err); |
|||
assert(lastTs < b.timestamp, 'genesisTS < b.timestamp: ' + lastTs + '<' + b.timestamp + ":" + i); |
|||
if(i % 1000 === 1) process.stdout.write('.'); |
|||
if(i === 100000) done(); |
|||
}); |
|||
} |
|||
}); |
|||
|
|||
|
|||
|
|||
}); |
|||
|
|||
|
|||
|
@ -1,36 +0,0 @@ |
|||
#!/usr/bin/env node
|
|||
'use strict'; |
|||
|
|||
process.env.NODE_ENV = process.env.NODE_ENV || 'development'; |
|||
|
|||
var TESTING_BLOCK0 = '00000000b873e79784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206'; |
|||
var TESTING_BLOCK1 = '000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943'; |
|||
var START_TS = 1; |
|||
var END_TS = '1296688928~'; // 2/2/2011 23:23PM
|
|||
|
|||
var assert = require('assert'), |
|||
BlockDb = require('../../lib/BlockDb').class(); |
|||
|
|||
var bDb; |
|||
|
|||
describe('BlockDb getBlocksByDate', function(){ |
|||
|
|||
|
|||
before(function(c) { |
|||
bDb = new BlockDb(); |
|||
return c(); |
|||
}); |
|||
|
|||
it('Get Hash by Date', function(done) { |
|||
|
|||
bDb.getBlocksByDate(START_TS, END_TS, function(err, list) { |
|||
if (err) done(err); |
|||
assert(list, 'returns list'); |
|||
assert.equal(list.length,2, 'list has 2 items'); |
|||
assert.equal(list[0].hash, TESTING_BLOCK0); |
|||
assert.equal(list[1].hash, TESTING_BLOCK1); |
|||
done(); |
|||
}); |
|||
}); |
|||
}); |
|||
|
@ -1,17 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
var BlockDb = require('../../lib/BlockDb').class(); |
|||
var height_needed = 180000; |
|||
var bDb = new BlockDb(); |
|||
|
|||
var expect = require('chai').expect; |
|||
|
|||
describe('Node check', function() { |
|||
it('should contain block ' + height_needed, function(done) { |
|||
bDb.blockIndex(height_needed, function(err, b) { |
|||
expect(err).to.equal(null); |
|||
expect(b).to.not.equal(null); |
|||
done(); |
|||
}); |
|||
}); |
|||
}); |
@ -1,32 +0,0 @@ |
|||
{ |
|||
"21798ddc9664ac0ef618f52b151dda82dafaf2e26d2bbef6cdaf55a6957ca237": [ |
|||
{ |
|||
"txid": "bcd8da8ee847da377f8aaca92502c05e5f914c6a2452753146013b0e642a25a0", |
|||
"n": 0 |
|||
}, |
|||
{ |
|||
"txid": "deb7bddc67e936ae49b97a97885d29e60afc6f6784f6d871f2904614a67250f5", |
|||
"n": 0 |
|||
} |
|||
], |
|||
"b633a6249d4a2bc123e7f8a151cae2d4afd17aa94840009f8697270c7818ceee": [ |
|||
{ |
|||
"txid": "c0c46d6be0183f52c88afe2d649800ecdaa7594ee390c77bafbd06322e6c823d", |
|||
"n": 11 |
|||
}, |
|||
{ |
|||
"txid": "d60e980419c5a8abd629fdea5032d561678b62e23b3fdba62b42f410c5a29560", |
|||
"n": 1 |
|||
} |
|||
], |
|||
"ca2f42e44455b8a84434de139efea1fe2c7d71414a8939e0a20f518849085c3b": [ |
|||
{ |
|||
"txid": "aa21822f1a69bc54e5a4ab60b25c09503702a821379fd2dfbb696b8ada4ce5b9", |
|||
"n": 0 |
|||
}, |
|||
{ |
|||
"txid": "a33bd24a47ab6f23758ed09e05716f809614f2e280e5a05a317ec6d839e81225", |
|||
"n": 1 |
|||
} |
|||
] |
|||
} |
@ -1,43 +0,0 @@ |
|||
#!/usr/bin/env node
|
|||
'use strict'; |
|||
|
|||
process.env.NODE_ENV = process.env.NODE_ENV || 'development'; |
|||
|
|||
var assert = require('assert'), |
|||
Status = require('../../app/models/Status').class(); |
|||
|
|||
describe('Status', function(){ |
|||
|
|||
it('getInfo', function(done) { |
|||
var d = new Status(); |
|||
|
|||
d.getInfo(function(err) { |
|||
if (err) done(err); |
|||
assert.equal('number', typeof d.info.difficulty); |
|||
done(); |
|||
}); |
|||
}); |
|||
|
|||
it('getDifficulty', function(done) { |
|||
var d = new Status(); |
|||
|
|||
d.getDifficulty(function(err) { |
|||
if (err) done(err); |
|||
assert.equal('number', typeof d.difficulty); |
|||
done(); |
|||
}); |
|||
}); |
|||
|
|||
it('getLastBlockHash', function(done) { |
|||
var d = new Status(); |
|||
|
|||
d.getLastBlockHash(function(err) { |
|||
if (err) done(err); |
|||
assert.equal('string', typeof d.lastblockhash); |
|||
done(); |
|||
}); |
|||
}); |
|||
|
|||
|
|||
}); |
|||
|
@ -1,62 +0,0 @@ |
|||
[ |
|||
{ |
|||
"disabled": 1, |
|||
"txid": "75c5ffe6dc2eb0f6bd011a08c041ef115380ccd637d859b379506a0dca4c26fc" |
|||
}, |
|||
{ |
|||
"txid": "21798ddc9664ac0ef618f52b151dda82dafaf2e26d2bbef6cdaf55a6957ca237", |
|||
"toRm": [ |
|||
"txs-86a03cac7d87f596008c6d5a8d3fd8b88842932ea6f0337673eda16f6b472f7f-0", |
|||
"txs-bcd8da8ee847da377f8aaca92502c05e5f914c6a2452753146013b0e642a25a0-0" |
|||
], |
|||
"items": [ |
|||
{ |
|||
"addr": "mzjLe62faUqCSjkwQkwPAL5nYyR8K132fA", |
|||
"value_sat": 134574000, |
|||
"index": 0 |
|||
}, |
|||
{ |
|||
"addr": "n28wb1cRGxPtfmsenYKFfsvnZ6kRapx3jF", |
|||
"value_sat": 31600000, |
|||
"index": 1 |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"txid": "b633a6249d4a2bc123e7f8a151cae2d4afd17aa94840009f8697270c7818ceee", |
|||
"toRm": [ |
|||
"txs-01621403689cb4a95699a3dbae029d7031c5667678ef14e2054793954fb27917-0" |
|||
], |
|||
"items": [ |
|||
{ |
|||
"addr": "mhfQJUSissP6nLM5pz6DxHfctukrrLct2T", |
|||
"value_sat": 19300000, |
|||
"index": 0 |
|||
}, |
|||
{ |
|||
"addr": "mzcDhbL877ES3MGftWnc3EuTSXs3WXDDML", |
|||
"value_sat": 21440667, |
|||
"index": 1 |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"txid": "ca2f42e44455b8a84434de139efea1fe2c7d71414a8939e0a20f518849085c3b", |
|||
"toRm": [ |
|||
"txs-2d7b680fb06e4d7eeb65ca49ac7522276586e0090b7fe662fc708129429c5e6a-0" |
|||
], |
|||
"items": [ |
|||
{ |
|||
"addr": "mhqyL1nDQDo1WLH9qH8sjRjx2WwrnmAaXE", |
|||
"value_sat": 1327746, |
|||
"index": 0 |
|||
}, |
|||
{ |
|||
"addr": "mkGrySSnxcqRbtPCisApj3zXCQVmUUWbf1", |
|||
"value_sat": 1049948, |
|||
"index": 1 |
|||
} |
|||
] |
|||
} |
|||
|
|||
] |
@ -1,16 +0,0 @@ |
|||
[ |
|||
{ |
|||
"addr": "muyg1K5WsHkfMVCkUXU2y7Xp5ZD6RGzCeH", |
|||
"length": 1, |
|||
"tx0id": "eeabc70063d3f266e190e8735bc4599c811d3a79d138da1364e88502069b029c", |
|||
"tx0scriptPubKey": "76a9149e9f6515c70db535abdbbc983c7d8d1bff6c20cd88ac", |
|||
"tx0amount": 0.38571339 |
|||
}, |
|||
{ |
|||
"addr": "mgKY35SXqxFpcKK3Dq9mW9919N7wYXvcFM", |
|||
"length": 1, |
|||
"tx0id": "91800d80bb4c69b238c9bfd94eb5155ab821e6b25cae5c79903d12853bbb4ed5", |
|||
"tx0scriptPubKey": "76a91408cf4ceb2b7278043fcc7f545e6e6e73ef9a644f88ac", |
|||
"tx0amount": 0.01979459 |
|||
} |
|||
] |
@ -1,50 +0,0 @@ |
|||
'use strict'; |
|||
var chai = require('chai'), |
|||
expect = chai.expect, |
|||
sinon = require('sinon'); |
|||
|
|||
var PeerSync = require('../../lib/PeerSync.js').class(); |
|||
describe('PeerSync', function() { |
|||
var ps; |
|||
|
|||
beforeEach(function(done) { |
|||
ps = new PeerSync(); |
|||
done(); |
|||
}); |
|||
afterEach(function() { |
|||
ps.close(); |
|||
}); |
|||
|
|||
|
|||
describe('#handleInv()', function() { |
|||
var inv_info = { |
|||
message: { |
|||
invs: [] |
|||
}, |
|||
conn: { |
|||
sendGetData: sinon.spy() |
|||
} |
|||
}; |
|||
it('should return with no errors', function() { |
|||
expect(function() { |
|||
ps.handleInv(inv_info); |
|||
}).not.to. |
|||
throw (Error); |
|||
}); |
|||
it('should call sendGetData', function() { |
|||
ps.handleInv(inv_info); |
|||
expect(inv_info.conn.sendGetData.calledTwice).to.be.ok; |
|||
}); |
|||
}); |
|||
|
|||
describe('#run()', function() { |
|||
it('should setup peerman', function() { |
|||
var startSpy = sinon.spy(ps.peerman, 'start'); |
|||
var onSpy = sinon.spy(ps.peerman, 'on'); |
|||
ps.run(); |
|||
|
|||
expect(startSpy.called).to.be.ok; |
|||
expect(onSpy.called).to.be.ok; |
|||
}); |
|||
}); |
|||
}); |
@ -1,5 +0,0 @@ |
|||
--require should |
|||
-R spec |
|||
--ui bdd |
|||
--recursive |
|||
|
@ -1,18 +0,0 @@ |
|||
#! /usr/bin/env node
|
|||
'use strict'; |
|||
|
|||
process.env.NODE_ENV = process.env.NODE_ENV || 'development'; |
|||
|
|||
var PeerSync = require('../lib/PeerSync').class(); |
|||
|
|||
var PROGRAM_VERSION = '0.1'; |
|||
var program = require('commander'); |
|||
|
|||
program |
|||
.version(PROGRAM_VERSION) |
|||
.parse(process.argv); |
|||
|
|||
var ps = new PeerSync(); |
|||
ps.run(); |
|||
|
|||
|
@ -1,43 +0,0 @@ |
|||
#!/usr/bin/env node
|
|||
|
|||
|
|||
'use strict'; |
|||
|
|||
process.env.NODE_ENV = process.env.NODE_ENV || 'development'; |
|||
|
|||
var SYNC_VERSION = '0.1'; |
|||
var program = require('commander'); |
|||
var HistoricSync = require('../lib/HistoricSync').class(); |
|||
var async = require('async'); |
|||
|
|||
program |
|||
.version(SYNC_VERSION) |
|||
.option('-D --destroy', 'Remove current DB (and start from there)', 0) |
|||
.option('-S --startfile', 'Number of file from bitcoind to start(default=0)') |
|||
.option('-R --rpc', 'Force sync with RPC') |
|||
.option('-v --verbose', 'Verbose 0/1', 0) |
|||
.parse(process.argv); |
|||
|
|||
var historicSync = new HistoricSync({ |
|||
shouldBroadcastSync: true, |
|||
}); |
|||
|
|||
|
|||
async.series([ |
|||
function(cb) { |
|||
if (!program.destroy) return cb(); |
|||
console.log('Deleting Sync DB...'); |
|||
historicSync.sync.destroy(cb); |
|||
}, |
|||
function(cb) { |
|||
historicSync.start({ |
|||
forceStartFile: program.startfile, |
|||
forceRPC: program.rpc, |
|||
},cb); |
|||
}, |
|||
], |
|||
function(err) { |
|||
historicSync.close(); |
|||
if (err) console.log('CRITICAL ERROR: ', historicSync.info()); |
|||
}); |
|||
|
Loading…
Reference in new issue