diff --git a/README.md b/README.md index 8c1ed487..efc9ae00 100644 --- a/README.md +++ b/README.md @@ -57,36 +57,6 @@ $ npm install -g bower If you get an error, please check the next section "Post-install" -### Post-install (post-dependecies) - - Get bufferput package from Github repository: - - $ git clone git@github.com:gasteve/node-bufferput.git - - Create symbolic link of node-bufferput in your insight folder: - - $ cd /insight/node_modules - $ ln -s /node-bufferput bufferput - - Get bitcore from github repository: - - Get bitcore from github repository: - - $ git clone https://github.com/bitpay/bitcore.git - - $ cd bitcore - - $ npm install - - Then create a symbolic link from this to your insight repository. We need to - use bitcore from github, not with npm for now: - - $ cd insight/node_modules - - $ rm -R bitcore - - $ ln -s /bitcore - ## Syncing old blockchain data Run sync from insight repository (to save old blocks and transactions in MongoDB): diff --git a/app/controllers/blocks.js b/app/controllers/blocks.js index 37324a09..b41b67f3 100644 --- a/app/controllers/blocks.js +++ b/app/controllers/blocks.js @@ -32,6 +32,20 @@ exports.show = function(req, res) { } }; +/** + * Show block by Height + */ +exports.blockindex = function(req, res, next, height) { + Block.fromHeight(height, function(err, hash) { + if (err) { + console.log(err); + res.status(400).send('Bad Request'); // TODO + } + else { + res.jsonp(hash); + } + }); +}; /** * List of blocks by date diff --git a/app/controllers/socket.js b/app/controllers/socket.js index acfa2c9c..25a846ed 100644 --- a/app/controllers/socket.js +++ b/app/controllers/socket.js @@ -26,6 +26,6 @@ module.exports.broadcast_address_tx = function(address, tx) { ios.sockets.in(address).emit('atx', tx); }; -module.exports.broadcastSyncInfo = function(syncInfo) { - ios.sockets.emit('sync', syncInfo); +module.exports.broadcastSyncInfo = function(historicSync) { + ios.sockets.in('sync').emit('status', historicSync); }; diff --git a/app/controllers/status.js b/app/controllers/status.js index 1874605e..c978722f 100644 --- a/app/controllers/status.js +++ b/app/controllers/status.js @@ -10,7 +10,7 @@ var Status = require('../models/Status'), /** * Status */ -exports.show = function(req, res, next) { +exports.show = function(req, res) { if (! req.query.q) { res.status(400).send('Bad Request'); @@ -50,6 +50,6 @@ exports.show = function(req, res, next) { }; exports.sync = function(req, res) { - if (req.syncInfo) - res.jsonp(req.syncInfo); + if (req.historicSync) + res.jsonp(req.historicSync.info()); }; diff --git a/app/controllers/transactions.js b/app/controllers/transactions.js index 5676790e..10128159 100644 --- a/app/controllers/transactions.js +++ b/app/controllers/transactions.js @@ -52,8 +52,12 @@ var getTransaction = function(txid, cb) { */ exports.list = function(req, res, next) { var bId = req.query.block; - var aId = req.query.address; - var limit = req.query.limit || 1000; + var addrStr = req.query.address; + var page = req.query.pageNum; + var pageLength = 20; + var pagesTotal = 1; + var txLength; + var txs; if (bId) { Block.fromHashWithInfo(bId, function(err, block) { @@ -63,14 +67,28 @@ exports.list = function(req, res, next) { return next(); } - async.mapSeries(block.info.tx, getTransaction, + 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) { - res.jsonp(results); + res.jsonp({ + pagesTotal: pagesTotal, + txs: results + }); }); }); } - else if (aId) { - var a = Address.new(aId); + else if (addrStr) { + var a = Address.new(addrStr); a.update(function(err) { if (err && !a.totalReceivedSat) { @@ -79,23 +97,25 @@ exports.list = function(req, res, next) { return next(); } - async.mapSeries(a.transactions, getTransaction, + 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) { - res.jsonp(results); + res.jsonp({ + pagesTotal: pagesTotal, + txs: results + }); }); }); } - else { - Transaction - .find() - .limit(limit) - .sort('-time') - .exec(function(err, txs) { - if (err) { - res.status(500).send(err); - } else { - res.jsonp(txs); - } - }); - } + }; diff --git a/app/models/Address.js b/app/models/Address.js index e054ff19..12a4a284 100644 --- a/app/models/Address.js +++ b/app/models/Address.js @@ -60,7 +60,7 @@ function spec() { // TODO TXout! //T function (cb) { - TransactionItem.find({addr:that.addrStr}).sort({ts:1}).exec(function(err,txItems){ + TransactionItem.find({addr:that.addrStr}).sort({ts:-1}).exec(function(err,txItems){ if (err) return cb(err); txItems.forEach(function(txItem){ diff --git a/app/models/Block.js b/app/models/Block.js index 3f4bcbcf..33bf34bd 100644 --- a/app/models/Block.js +++ b/app/models/Block.js @@ -72,6 +72,15 @@ BlockSchema.statics.load = function(id, cb) { }).exec(cb); }; +BlockSchema.statics.fromHeight = function(height, cb) { + var rpc = new RpcClient(config.bitcoind); + var hash = {}; + rpc.getBlockHash(height, function(err, bh){ + if (err) return cb(err); + hash.blockHash = bh.result; + cb(null, hash); + }); +}; BlockSchema.statics.fromHash = function(hash, cb) { this.findOne({ diff --git a/app/models/Transaction.js b/app/models/Transaction.js index 5e418adf..12b46986 100644 --- a/app/models/Transaction.js +++ b/app/models/Transaction.js @@ -171,10 +171,13 @@ TransactionSchema.statics.explodeTransactionItems = function(txid, time, cb) { TransactionItem.create({ txid : txid, value_sat : o.valueSat, - addr : o.scriptPubKey.addresses[0], + addr : o.scriptPubKey.addresses[0], // TODO: only address 0? index : o.n, ts : time, }, next_out); + if (addrs.indexOf(o.scriptPubKey.addresses[0]) === -1) { + addrs.push(o.scriptPubKey.addresses[0]); + } } else { console.log ('WARN in TX: %s could not parse OUTPUT %d', txid, o.n); @@ -279,7 +282,7 @@ TransactionSchema.statics.queryInfo = function(txid, cb) { else { tx.ins.forEach(function(i) { if (i.value) { - info.vin[c].value = util.formatValue(i.value); + info.vin[c].value = parseFloat(util.formatValue(i.value)); var n = util.valueToBigInt(i.value).toNumber(); info.vin[c].valueSat = n; valueIn = valueIn.add( n ); @@ -319,7 +322,7 @@ TransactionSchema.statics.queryInfo = function(txid, cb) { if ( !tx.isCoinBase() ) { info.valueIn = valueIn / util.COIN; - info.feeds = (valueIn - valueOut) / util.COIN; + info.fees = (valueIn - valueOut) / util.COIN; } else { var reward = BitcoreBlock.getBlockValue(info.height) / util.COIN; @@ -343,8 +346,13 @@ TransactionSchema.methods.fillInfo = function(next) { if (err) return next(err); that.info = info; - that.info.time = that.time; - return next(); + if (! that.info) { + return next(); + } + else { + that.info.time = that.time; + return next(); + } }); }; diff --git a/app/views/includes/foot.jade b/app/views/includes/foot.jade index 735e8c0b..4a14f1ae 100755 --- a/app/views/includes/foot.jade +++ b/app/views/includes/foot.jade @@ -36,7 +36,6 @@ script(type='text/javascript', src='/js/services/socket.js') //Application Controllers script(type='text/javascript', src='/js/controllers/index.js') script(type='text/javascript', src='/js/controllers/header.js') -script(type='text/javascript', src='/js/controllers/footer.js') script(type='text/javascript', src='/js/controllers/blocks.js') script(type='text/javascript', src='/js/controllers/transactions.js') script(type='text/javascript', src='/js/controllers/address.js') diff --git a/config/express.js b/config/express.js index 0e5c58d4..0b9c284d 100644 --- a/config/express.js +++ b/config/express.js @@ -31,7 +31,7 @@ module.exports = function(app, historicSync) { //custom middleware function setHistoric(req, res, next) { - req.syncInfo = historicSync.syncInfo; + req.historicSync = historicSync; next(); } app.use('/api/sync', setHistoric); diff --git a/config/routes.js b/config/routes.js index fc27b789..8125f561 100644 --- a/config/routes.js +++ b/config/routes.js @@ -14,6 +14,9 @@ module.exports = function(app, historicSync) { 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); diff --git a/insight.js b/insight.js index 1f495031..64027d03 100644 --- a/insight.js +++ b/insight.js @@ -24,6 +24,16 @@ var express = require('express'), var config = require('./config/config'); //Bootstrap db connection +// If mongod is running +mongoose.connection.on('open', function () { + console.log('Connected to mongo server.'); +}); +// If mongod is not running +mongoose.connection.on('error', function (err) { + console.log('Could not connect to mongo server!'); + console.log(err); +}); + mongoose.connect(config.db); //Bootstrap models @@ -45,19 +55,29 @@ walk(models_path); // historic_sync process var historicSync = {}; + + if (!config.disableHistoricSync) { historicSync = new HistoricSync(); + historicSync.init({ skipDbConnection: true, shouldBroadcast: true, + progressStep: 2, networkName: config.network - }, function() { - historicSync.smart_import(function(err){ - var txt= 'ended.'; - if (err) txt = 'ABORTED with error: ' + err.message; + }, function(err) { + if (err) { + var txt = 'ABORTED with error: ' + err.message; + console.log('[historic_sync] ' + txt); + } + else { + historicSync.smartImport(function(err){ + var txt= 'ended.'; + if (err) txt = 'ABORTED with error: ' + err.message; - console.log('[historic_sync] ' + txt, historicSync.syncInfo); - }); + console.log('[historic_sync] ' + txt, historicSync.info()); + }); + } }); } diff --git a/lib/HistoricSync.js b/lib/HistoricSync.js index b33b4bd8..7fd0a567 100644 --- a/lib/HistoricSync.js +++ b/lib/HistoricSync.js @@ -3,45 +3,76 @@ require('classtool'); + function spec() { - var util = require('util'); - var RpcClient = require('bitcore/RpcClient').class(); - var networks = require('bitcore/networks'); - var async = require('async'); - var config = require('../config/config'); - var Block = require('../app/models/Block'); - var Sync = require('./Sync').class(); - var sockets = require('../app/controllers/socket.js'); + var util = require('util'); + var RpcClient = require('bitcore/RpcClient').class(); + var networks = require('bitcore/networks'); + var async = require('async'); + var config = require('../config/config'); + var Block = require('../app/models/Block'); + var Sync = require('./Sync').class(); + var sockets = require('../app/controllers/socket.js'); + + + var BAD_GEN_ERROR = 'Bad genesis block. Network mismatch between Insight and bitcoind? Insight is configured for:'; function HistoricSync(opts) { - this.network = config.network === 'testnet' ? networks.testnet: networks.livenet; + this.network = config.network === 'testnet' ? networks.testnet: networks.livenet; var genesisHashReversed = new Buffer(32); this.network.genesisBlock.hash.copy(genesisHashReversed); this.genesis = genesisHashReversed.reverse().toString('hex'); - this.sync = new Sync(opts); - + this.sync = new Sync(opts); //available status: new / syncing / finished / aborted - this.status = 'new'; - this.syncInfo = {}; + this.status = 'new'; + this.error = null; + + this.syncPercentage = 0; + this.syncedBlocks = 0; + this.skippedBlocks = 0; + } function p() { var args = []; - Array.prototype.push.apply( args, arguments ); + Array.prototype.push.apply(args, arguments); - - args.unshift('[historic_sync]'); /*jshint validthis:true */ console.log.apply(this, args); } - HistoricSync.prototype.init = function(opts,cb) { - this.rpc = new RpcClient(config.bitcoind); - this.opts = opts; - this.sync.init(opts, cb); + HistoricSync.prototype.setError = function(err) { + var self = this; + self.error = err.toString(); + self.status='error'; + self.showProgress(); + }; + + HistoricSync.prototype.init = function(opts, cb) { + + var self = this; + self.rpc = new RpcClient(config.bitcoind); + self.opts = opts; + self.sync.init(opts, function(err) { + if (err) { + self.setError(err); + return cb(err); + } + else { + // check testnet? + self.rpc.getBlockHash(0, function(err, res){ + if (!err && ( res && res.result !== self.genesis)) { + err = new Error(BAD_GEN_ERROR + config.network); + self.setError(err); + } + return cb(err); + }); + } + }); + }; HistoricSync.prototype.close = function() { @@ -49,75 +80,93 @@ function spec() { }; + HistoricSync.prototype.info = function() { + return { + status: this.status, + blockChainHeight: this.blockChainHeight, + syncPercentage: this.syncPercentage, + skippedBlocks: this.skippedBlocks, + syncedBlocks: this.syncedBlocks, + }; + }; + HistoricSync.prototype.showProgress = function() { var self = this; - var i = self.syncInfo; - var per = parseInt(100 * i.syncedBlocks / i.blocksToSync); - p(util.format('status: %d/%d [%d%%]', i.syncedBlocks, i.blocksToSync, per)); - if (self.opts.broadcast) { - sockets.broadcastSyncInfo(self.syncInfo); + if (self.error) { + p('ERROR:' + self.error); + } + else { + self.syncPercentage = parseFloat(100 * self.syncedBlocks / self.blockChainHeight).toFixed(3); + if (self.syncPercentage > 100) self.syncPercentage = 100; + + p(util.format('status: [%d%%] skipped: %d', self.syncPercentage, self.skippedBlocks)); + } + if (self.opts.shouldBroadcast) { + sockets.broadcastSyncInfo(self.info()); } }; - HistoricSync.prototype.getPrevNextBlock = function(blockHash, blockEnd, opts, cb) { var self = this; // recursion end. - if (!blockHash ) return cb(); + if (!blockHash) return cb(); - var existed = 0; + var existed = false; var blockInfo; var blockObj; async.series([ - // Already got it? - function(c) { - Block.findOne({hash:blockHash}, function(err,block){ - if (err) { p(err); return c(err); } - if (block) { - existed =1; - blockObj =block; - } - return c(); - }); + // Already got it? + function(c) { + Block.findOne({ + hash: blockHash }, - //show some (inacurate) status - function(c) { - var step = parseInt(self.syncInfo.blocksToSync / 100); - if (step < 10) step = 10; - - if (self.syncInfo.syncedBlocks % step === 1) { - self.showProgress(); + function(err, block) { + if (err) { + p(err); + return c(err); + } + if (block) { + existed = true; + blockObj = block; } return c(); - }, - //get Info from RPC - function(c) { + }); + }, + //show some (inacurate) status + function(c) { + if ( ( self.syncedBlocks + self.skippedBlocks) % self.step === 1) { + self.showProgress(); + } - // TODO: if we store prev/next, no need to go to RPC - // if (blockObj && blockObj.nextBlockHash) return c(); + return c(); + }, + //get Info from RPC + function(c) { - self.rpc.getBlock(blockHash, function(err, ret) { - if (err) return c(err); + // TODO: if we store prev/next, no need to go to RPC + // if (blockObj && blockObj.nextBlockHash) return c(); + self.rpc.getBlock(blockHash, function(err, ret) { + if (err) return c(err); - blockInfo = ret; - return c(); - }); - }, - //store it - function(c) { - if (existed) return c(); - self.sync.storeBlock(blockInfo.result, function(err) { + blockInfo = ret; + return c(); + }); + }, + //store it + function(c) { + if (existed) return c(); + self.sync.storeBlock(blockInfo.result, function(err) { - existed = err && err.toString().match(/E11000/); + existed = err && err.toString().match(/E11000/); - if (err && ! existed) return c(err); - return c(); - }); - }, - /* TODO: Should Start to sync backwards? (this is for partial syncs) + if (err && ! existed) return c(err); + return c(); + }); + }, + /* TODO: Should Start to sync backwards? (this is for partial syncs) function(c) { if (blockInfo.result.prevblockhash != current.blockHash) { @@ -127,176 +176,159 @@ function spec() { return c(); } */ - ], - function (err){ + ], function(err) { - if (err) { - self.err = util.format('ERROR: @%s: %s [count: syncedBlocks: %d]', blockHash, err, self.syncInfo.syncedBlocks); - self.status = 'aborted'; - p(self.err); - } + if (err) { + self.err = util.format('ERROR: @%s: %s [count: syncedBlocks: %d]', blockHash, err, self.syncedBlocks); + self.status = 'aborted'; + self.showProgress(); + p(self.err); + } + else { + self.err = null; + self.status = 'syncing'; + } - else { - self.err = null; - self.status = 'syncing'; - } - - if (opts.upToExisting && existed ) { - var diff = self.syncInfo.blocksToSync - self.syncInfo.syncedBlocks; - if (diff <= 0) { - self.status = 'finished'; - p('DONE. Found existing block: ', blockHash); - return cb(err); - } - else { - self.syncInfo.skipped_blocks = self.syncInfo.skipped_blocks || 1; - if ((self.syncInfo.skipped_blocks++ % 1000) === 1 ) { - p('WARN found target block\n\tbut blockChain Height is still higher that ours. Previous light sync must be interrupted.\n\tWill keep syncing.', self.syncInfo.syncedBlocks, self.syncInfo.blocksToSync, self.syncInfo.skipped_blocks); - } - } - } - - if (blockEnd && blockEnd === blockHash) { + if ( (opts.upToExisting && existed && self.syncedBlocks >= self.blockChainHeight) || + (blockEnd && blockEnd === blockHash)) { self.status = 'finished'; - p('DONE. Found END block: ', blockHash); + p('DONE. Found existing block: ', blockHash); + self.showProgress(); return cb(err); - } + } + // Continue + if (blockInfo && blockInfo.result) { - // Continue - if (blockInfo && blockInfo.result) { - if (! existed) self.syncInfo.syncedBlocks++; - if (opts.prev && blockInfo.result.previousblockhash) { - return self.getPrevNextBlock(blockInfo.result.previousblockhash, blockEnd, opts, cb); - } + if (existed) + self.skippedBlocks++; + else + self.syncedBlocks++; - if (opts.next && blockInfo.result.nextblockhash) - return self.getPrevNextBlock(blockInfo.result.nextblockhash, blockEnd, opts, cb); - } - return cb(err); + // recursion + if (opts.prev && blockInfo.result.previousblockhash) + return self.getPrevNextBlock(blockInfo.result.previousblockhash, blockEnd, opts, cb); + + if (opts.next && blockInfo.result.nextblockhash) + return self.getPrevNextBlock(blockInfo.result.nextblockhash, blockEnd, opts, cb); + } + return cb(err); }); }; - HistoricSync.prototype.import_history = function(opts, next) { + HistoricSync.prototype.importHistory = function(opts, next) { var self = this; - var retry_secs = 2; + var retry_secs = 2; var bestBlock; - var blockChainHeight; async.series([ - function(cb) { - if (opts.destroy) { - p('Deleting DB...'); - return self.sync.destroy(cb); - } + function(cb) { + if (opts.destroy) { + p('Deleting DB...'); + return self.sync.destroy(cb); + } + return cb(); + }, + // We are not using getBestBlockHash, because is not available in all clients + function(cb) { + if (!opts.reverse) return cb(); + + self.rpc.getBlockCount(function(err, res) { + if (err) return cb(err); + self.blockChainHeight = res.result; return cb(); - }, - // We are not using getBestBlockHash, because is not available in all clients - function(cb) { - if (!opts.reverse) return cb(); + }); + }, + function(cb) { + if (!opts.reverse) return cb(); - self.rpc.getBlockCount(function(err, res) { - if (err) return cb(err); - blockChainHeight = res.result; - return cb(); - }); - }, - function(cb) { - if (!opts.reverse) return cb(); + self.rpc.getBlockHash(self.blockChainHeight, function(err, res) { + if (err) return cb(err); - self.rpc.getBlockHash(blockChainHeight, function(err, res) { + bestBlock = res.result; + + return cb(); + }); + }, + function(cb) { + if (opts.upToExisting) { + + // should be isOrphan = true or null to be more accurate. + Block.count({ + isOrphan: null + }, + function(err, count) { if (err) return cb(err); - bestBlock = res.result; - + self.syncedBlocks = count || 0; return cb(); }); - }, - function(cb) { - // This is only to inform progress. - if (!opts.upToExisting) { - self.rpc.getInfo(function(err, res) { - if (err) return cb(err); - self.syncInfo.blocksToSync = res.result.blocks; - return cb(); - }); + } + }, + ], function(err) { + var start, end; + function sync() { + if (opts.reverse) { + start = bestBlock; + end = self.genesis; + opts.prev = true; } else { - // should be isOrphan = true or null to be more accurate. - Block.count({ isOrphan: null}, function(err, count) { - if (err) return cb(err); + start = self.genesis; + end = null; + opts.next = true; + } + p('Starting from: ', start); + p(' to : ', end); + p(' opts: ', JSON.stringify(opts)); - self.syncInfo.blocksToSync = blockChainHeight - count; - if (self.syncInfo.blocksToSync < 1) self.syncInfo.blocksToSync = 1; - return cb(); - }); - } - }, - ], - function(err) { - - - var start, end; - function sync() { - if (opts.reverse) { - start = bestBlock; - end = self.genesis; - opts.prev = true; - } - else { - start = self.genesis; - end = null; - opts.next = true; + self.getPrevNextBlock(start, end, opts, function(err) { + if (err && err.message.match(/ECONNREFUSED/)) { + setTimeout(function() { + p('Retrying in %d secs', retry_secs); + sync(); + }, + retry_secs * 1000); } + else return next(err); + }); + } - self.syncInfo = util._extend(self.syncInfo, { - start: start, - isStartGenesis: start === self.genesis, - end: end, - isEndGenesis: end === self.genesis, - scanningForward: opts.next, - scanningBackward: opts.prev, - upToExisting: opts.upToExisting, - syncedBlocks: 0, - }); - p('Starting from: ', start); - p(' to : ', end); - p(' opts: ', JSON.stringify(opts)); + if (!self.step) { + var step = parseInt( (self.blockChainHeight - self.syncedBlocks) / 1000); - self.getPrevNextBlock( start, end, opts , function(err) { - if (err && err.message.match(/ECONNREFUSED/)){ - setTimeout(function() { - p('Retrying in %d secs', retry_secs); - sync(); - }, retry_secs * 1000); - } - else - return next(err); - }); + if (self.opts.progressStep) { + step = self.opts.progressStep; } - if (err) { - self.syncInfo = util._extend(self.syncInfo, { error: err.message }); - return next(err, 0); - } - else { - sync(); - } + if (step < 10) step = 10; + self.step = step; + } + + if (err) { + self.setError(err); + return next(err, 0); + } + else { + sync(); + } }); }; // upto if we have genesis block? - HistoricSync.prototype.smart_import = function(next) { + HistoricSync.prototype.smartImport = function(next) { var self = this; - Block.findOne({hash:self.genesis}, function(err, b){ + Block.findOne({ + hash: self.genesis + }, + function(err, b) { if (err) return next(err); - if (!b) { p('Could not find Genesis block. Running FULL SYNC'); } @@ -305,15 +337,14 @@ function spec() { } var opts = { - reverse: 1, + reverse: true, upToExisting: b ? true: false, }; - return self.import_history(opts, next); + return self.importHistory(opts, next); }); }; - return HistoricSync; } module.defineClass(spec); diff --git a/package.json b/package.json index 3078ba40..c0fa2e7b 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,6 @@ "mongoose": "~3.8.3", "lodash": "~2.4.1", "bower": "~1.2.8", - "bitcore": "*", "buffertools": "*", "grunt": "~0.4.2", "grunt-cli": "~0.1.11", @@ -75,7 +74,9 @@ "socket.io": "~0.9.16", "moment": "~2.5.0", "sinon": "~1.7.3", - "chai": "~1.8.1" + "chai": "~1.8.1", + "bitcore": "git://github.com/bitpay/bitcore.git", + "bufferput": "git://github.com/bitpay/node-bufferput.git" }, "devDependencies": { "grunt-contrib-watch": "latest", diff --git a/public/css/common.css b/public/css/common.css index 9c9e546d..f65b19e6 100644 --- a/public/css/common.css +++ b/public/css/common.css @@ -29,7 +29,7 @@ h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { min-height: 100%; height: auto; /* Negative indent footer by its height */ - margin: 0 auto -60px; + margin: 0 auto -51px; /* Pad bottom by footer height */ padding: 0 0 60px; } @@ -38,6 +38,7 @@ h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { .m20h {margin: 0 20px;} .m5v {margin: 5px 0;} .m20v {margin: 20px 0;} +.m10v {margin: 10px 0;} .m50v {margin: 50px 0;} .m10b {margin-bottom: 10px;} @@ -87,22 +88,22 @@ h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { font-weight: 100; } -#search::-webkit-input-placeholder { +#search::-webkit-input-placeholder { font-family: Ubuntu, sans-serif; font-weight: 100; font-style: italic; - font-size: 13px; + font-size: 15px; color: #BCDF7E; - line-height: 18px; + line-height: 20px; } -#search::-moz-placeholder { +#search::-moz-placeholder { font-family: Ubuntu, sans-serif; font-weight: 100; - font-size: 13px; + font-size: 15px; color: #BCDF7E; - line-height: 18px; + line-height: 20px; } .status { @@ -119,11 +120,10 @@ h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { } .col-gray { + width: 267px; background-color: #F4F4F4; padding: 15px; margin-top: 21px; - width: 265px; - height: 87%; border-radius: 5px; } @@ -159,9 +159,8 @@ h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { border-radius: :;px; } -.block-id h1 { +.block-id h3 { font-weight: bold; - font-size: 24px; color: #FFFFFF; line-height: 30px; text-align: center; @@ -221,10 +220,29 @@ h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { border: 2px solid #6C0000; } +#status .table { + margin-bottom: 45px; +} + +.progress-bar-info { + background-color: #8DC429; +} + /* Set the fixed height of the footer here */ #footer { - height: 60px; - background-color: #f5f5f5; + height: 51px; + background-color: #373D42; + border-top: 4px solid #656E76; + color: #fff; +} + +#footer .insight { + font-size: 20px; + text-decoration: none; +} + +.line-footer { + border-top: 2px dashed #ccc; } .line-bot { @@ -246,9 +264,14 @@ h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { #wrap > .container { padding: 60px 15px 0; } -/*.container .text-muted { - margin: 20px 0; -}*/ + +.container .text-muted { + margin: 10px 0; +} + +.container .text-muted a { + color: #eee; +} #footer > .container { padding-left: 15px; @@ -265,7 +288,13 @@ h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { #search { width: 400px; } - +.no_matching { + background-color: #FFFFFF; + border: 2px solid #64920F; + padding: 10px 20px; + position: absolute; + top: 46px; +} /*Animations*/ .fader.ng-enter { @@ -334,4 +363,30 @@ h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { background-color: #1a1a1a; } +.expanded-tx { + border-bottom: 1px dashed #444; +} + + +.expanded-tx small { + font-size: 80%; +} + +.status .t { + color: white; + display: inline-block; + padding:0px 5px; +} + +.status .text-danger { + background: red; +} + +.status .text-warning { + background: yellow; + color: black; +} + +.status .text-default { +} diff --git a/public/js/config.js b/public/js/config.js index 65028fd9..9791eb07 100755 --- a/public/js/config.js +++ b/public/js/config.js @@ -7,6 +7,10 @@ angular.module('insight').config(['$routeProvider', when('/block/:blockHash', { templateUrl: 'views/block.html' }). + when('/block-index/:blockHeight', { + controller: 'BlocksController', + template: 'Redirecting...' + }). when('/tx/:txId', { templateUrl: 'views/transaction.html' }). diff --git a/public/js/controllers/address.js b/public/js/controllers/address.js index 664c2ba6..fde4660f 100644 --- a/public/js/controllers/address.js +++ b/public/js/controllers/address.js @@ -1,13 +1,6 @@ 'use strict'; angular.module('insight.address').controller('AddressController', - ['$scope', - '$rootScope', - '$routeParams', - '$location', - 'Global', - 'Address', - 'get_socket', function ($scope, $rootScope, $routeParams, $location, Global, Address, get_socket) { $scope.global = Global; @@ -33,4 +26,4 @@ angular.module('insight.address').controller('AddressController', socket.emit('subscribe', $routeParams.addrStr); $scope.params = $routeParams; -}]); +}); diff --git a/public/js/controllers/blocks.js b/public/js/controllers/blocks.js index e3002999..2013db3b 100644 --- a/public/js/controllers/blocks.js +++ b/public/js/controllers/blocks.js @@ -1,8 +1,20 @@ 'use strict'; -angular.module('insight.blocks').controller('BlocksController', ['$scope', '$rootScope', '$routeParams', '$location', 'Global', 'Block', 'Blocks', function ($scope, $rootScope, $routeParams, $location, Global, Block, Blocks) { +angular.module('insight.blocks').controller('BlocksController', + function ($scope, $rootScope, $routeParams, $location, Global, Block, Blocks, BlockByHeight) { $scope.global = Global; + if ($routeParams.blockHeight) { + BlockByHeight.get({ + blockHeight: $routeParams.blockHeight + }, function(hash) { + $location.path('/block/' + hash.blockHash); + }, function() { + $rootScope.flashMessage = 'Bad Request'; + $location.path('/'); + }); + } + $scope.list = function() { Blocks.get({ blockDate: $routeParams.blockDate @@ -32,4 +44,4 @@ angular.module('insight.blocks').controller('BlocksController', ['$scope', '$roo }; $scope.params = $routeParams; -}]); +}); diff --git a/public/js/controllers/footer.js b/public/js/controllers/footer.js deleted file mode 100644 index 611cfc82..00000000 --- a/public/js/controllers/footer.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; - -angular.module('insight.system').controller('FooterController', - ['$scope', - 'Global', - 'Status', - function ($scope, Global, Status) { - $scope.global = Global; - - $scope.getFooter = function() { - Status.get({ - q: 'getInfo' - }, function(d) { - $scope.info = d.info; - }); - }; - -}]); - diff --git a/public/js/controllers/header.js b/public/js/controllers/header.js index fc9eed3a..8b3b4f7e 100755 --- a/public/js/controllers/header.js +++ b/public/js/controllers/header.js @@ -1,6 +1,7 @@ 'use strict'; -angular.module('insight.system').controller('HeaderController', ['$scope', 'Global', function ($scope, Global) { +angular.module('insight.system').controller('HeaderController', + function ($scope, Global) { $scope.global = Global; $scope.menu = [ @@ -15,4 +16,4 @@ angular.module('insight.system').controller('HeaderController', ['$scope', 'Glob ]; $scope.isCollapsed = false; -}]); +}); diff --git a/public/js/controllers/index.js b/public/js/controllers/index.js index e733d751..cbda7cc0 100755 --- a/public/js/controllers/index.js +++ b/public/js/controllers/index.js @@ -3,13 +3,7 @@ var TRANSACTION_DISPLAYED = 5; var BLOCKS_DISPLAYED = 5; angular.module('insight.system').controller('IndexController', - ['$scope', - '$rootScope', - 'Global', - 'get_socket', - 'Blocks', - 'Transactions', - function($scope, $rootScope, Global, get_socket, Blocks, Transactions) { + function($scope, $rootScope, Global, get_socket, Blocks, Transactions) { $scope.global = Global; var socket = get_socket($scope); @@ -55,4 +49,4 @@ angular.module('insight.system').controller('IndexController', $scope.txs = []; $scope.blocks = []; -}]); +}); diff --git a/public/js/controllers/search.js b/public/js/controllers/search.js index a41b9feb..07af17be 100644 --- a/public/js/controllers/search.js +++ b/public/js/controllers/search.js @@ -1,6 +1,7 @@ 'use strict'; -angular.module('insight.search').controller('SearchController', ['$scope', '$routeParams', '$location', 'Global', 'Block', 'Transaction', 'Address', function ($scope, $routeParams, $location, Global, Block, Transaction, Address) { +angular.module('insight.search').controller('SearchController', + function ($scope, $routeParams, $location, $timeout, Global, Block, Transaction, Address, BlockByHeight) { $scope.global = Global; $scope.search = function() { @@ -9,26 +10,35 @@ angular.module('insight.search').controller('SearchController', ['$scope', '$rou $scope.badQuery = false; $scope.q = ''; - Block.get({ - blockHash: q - }, function() { - $location.path('block/' + q); - }, function () { //block not found, search on TX - Transaction.get({ - txId: q + BlockByHeight.get({ + blockHeight: q + }, function(hash) { + $location.path('/block/' + hash.blockHash); + }, function() { // block by height not found + Block.get({ + blockHash: q }, function() { - $location.path('tx/' + q); - }, function () { //tx not found, search on Address - Address.get({ - addrStr: q + $location.path('block/' + q); + }, function () { //block not found, search on TX + Transaction.get({ + txId: q }, function() { - $location.path('address/' + q); - }, function () { //address not found, fail :( - $scope.badQuery = true; - $scope.q = q; - }); - }); + $location.path('tx/' + q); + }, function () { //tx not found, search on Address + Address.get({ + addrStr: q + }, function() { + $location.path('address/' + q); + }, function () { //address not found, fail :( + $scope.badQuery = true; + $timeout(function() { + $scope.badQuery = false; + }, 2000); + $scope.q = q; + }); + }); + }); }); }; -}]); +}); diff --git a/public/js/controllers/status.js b/public/js/controllers/status.js index 8fa2f24b..56c823a3 100644 --- a/public/js/controllers/status.js +++ b/public/js/controllers/status.js @@ -1,15 +1,18 @@ 'use strict'; -angular.module('insight.status').controller('StatusController', ['$scope', '$routeParams', '$location', '$rootScope', 'Global', 'Status', 'Sync', function ($scope, $routeParams, $location, $rootScope, Global, Status, Sync) { +angular.module('insight.status').controller('StatusController', +function($scope, $routeParams, $location, $rootScope, Global, Status, Sync, get_socket) { $scope.global = Global; $scope.getStatus = function(q) { Status.get({ - q: 'get' + q - }, function(d) { + q: 'get' + q + }, + function(d) { $rootScope.infoError = null; angular.extend($scope, d); - }, function(e) { + }, + function(e) { if (e.status === 503) { $rootScope.infoError = 'Backend Error. ' + e.data; } @@ -19,13 +22,26 @@ angular.module('insight.status').controller('StatusController', ['$scope', '$rou }); }; + var on_sync_update = function(sync) { + $scope.sync = sync; + }; + $scope.getSync = function() { - Sync.get({}, function(sync) { - $rootScope.syncError = null; - $scope.sync = sync; - }, function(e) { - $rootScope.syncError = 'Could not get sync information' + e; + Sync.get({}, + function(sync) { + on_sync_update(sync); + }, + function(e) { + $scope.sync = { error: 'Could not get sync information' + e }; }); }; -}]); + + var socket = get_socket($scope); + socket.emit('subscribe', 'sync'); + socket.on('status', function(sync) { +console.log('[status.js.55::] sync status update received!'); + on_sync_update(sync); + }); + +}); diff --git a/public/js/controllers/transactions.js b/public/js/controllers/transactions.js index 52b12c90..6190c43a 100644 --- a/public/js/controllers/transactions.js +++ b/public/js/controllers/transactions.js @@ -1,28 +1,87 @@ 'use strict'; angular.module('insight.transactions').controller('transactionsController', - ['$scope', - '$rootScope', - '$routeParams', - '$location', - 'Global', - 'Transaction', - 'TransactionsByBlock', - 'TransactionsByAddress', - 'get_socket', - function ($scope, $rootScope, $routeParams, $location, Global, Transaction, TransactionsByBlock, TransactionsByAddress, get_socket) { + function ($scope, $rootScope, $routeParams, $location, Global, Transaction, TransactionsByBlock, TransactionsByAddress, get_socket) { $scope.global = Global; + $scope.loading = false; + $scope.loadedBy = null; + + var pageNum = 0; + var pagesTotal = 1; $scope.findThis = function() { $scope.findTx($routeParams.txId); }; + $scope.aggregateItems = function(items) { + if (!items) return []; + + var l = items.length; + + var ret = []; + var tmp = {}; + var u=0; + // TODO multiple output address + // + for(var i=0; i < l; i++) { + + var notAddr = false; + + // non standard input + if (items[i].scriptSig && !items[i].addr) { + items[i].addr = 'Unparsed address [' + u++ + ']'; + items[i].notAddr = true; + notAddr = true; + } + + // non standard output + if (items[i].scriptPubKey && !items[i].scriptPubKey.addresses) { + items[i].scriptPubKey.addresses = ['Unparsed address [' + u++ + ']']; + items[i].notAddr = true; + notAddr = true; + } + + // multiple addr at output + if (items[i].scriptPubKey && items[i].scriptPubKey.addresses.length > 1) { + items[i].addr = items[i].scriptPubKey.addresses.join(','); + ret.push(items[i]); + continue; + } + + var addr = items[i].addr || (items[i].scriptPubKey && items[i].scriptPubKey.addresses[0] ); + + if (!tmp[addr]) { + tmp[addr] = {}; + tmp[addr].valueSat = 0; + tmp[addr].count = 0; + tmp[addr].addr = addr; + tmp[addr].items = []; + } + tmp[addr].valueSat += items[i].valueSat; + tmp[addr].value = tmp[addr].valueSat / 100000000; + tmp[addr].items.push(items[i]); + tmp[addr].notAddr = notAddr; + tmp[addr].count++; + } + + angular.forEach(tmp, function(v) { + ret.push(v); + }); + return (ret); + }; + + $scope.processTX = function(tx) { + tx.vinSimple = $scope.aggregateItems(tx.vin); + tx.voutSimple = $scope.aggregateItems(tx.vout); + }; + $scope.findTx = function(txid) { Transaction.get({ txId: txid }, function(tx) { $scope.tx = tx; - $scope.txs.push(tx); + $scope.processTX(tx); + $scope.txs.unshift(tx); }, function(e) { if (e.status === 400) { $rootScope.flashMessage = 'Invalid Transaction ID: ' + $routeParams.txId; @@ -37,27 +96,62 @@ angular.module('insight.transactions').controller('transactionsController', }); }; - $scope.byBlock = function(bId) { - TransactionsByBlock.query({ - block: bId - }, function(txs) { - $scope.txs = txs; + $scope.byBlock = function() { + TransactionsByBlock.get({ + block: $routeParams.blockHash, + pageNum: pageNum + }, function(data) { + $scope.paginate(data); }); }; - $scope.byAddress = function(aId) { - TransactionsByAddress.query({ - address: aId - }, function(txs) { - $scope.txs = txs; + $scope.byAddress = function () { + TransactionsByAddress.get({ + address: $routeParams.addrStr, + pageNum: pageNum + }, function(data) { + $scope.paginate(data); }); }; + + $scope.paginate = function (data) { + $scope.loading = false; + + pagesTotal = data.pagesTotal; + pageNum += 1; + + data.txs.forEach(function(tx) { + $scope.processTX(tx); + $scope.txs.push(tx); + }); + }; + + $scope.load = function(from) { + $scope.loadedBy = from; + $scope.loadMore(); + }; + + $scope.loadMore = function() { + if (pageNum < pagesTotal && !$scope.loading) { + $scope.loading = true; + + if ($scope.loadedBy === 'address') { + $scope.byAddress(); + } + else { + $scope.byBlock(); + } + } + }; + var socket = get_socket($scope); socket.on('atx', function(tx) { - console.log('Incoming transaction for address!', tx); + console.log('atx '+tx.txid); + var beep = new Audio('/sound/transaction.mp3'); + beep.play(); $scope.findTx(tx.txid); }); $scope.txs = []; -}]); +}); diff --git a/public/js/directives.js b/public/js/directives.js index a726efc4..d0fe4877 100755 --- a/public/js/directives.js +++ b/public/js/directives.js @@ -1 +1,25 @@ -'use strict'; \ No newline at end of file +'use strict'; + +angular.module('insight.address').directive('whenScrolled', ['$window', function($window) { + return { + link: function(scope, elm, attr) { + var pageHeight, clientHeight, scrollPos; + $window = angular.element($window); + + var handler = function() { + pageHeight = window.document.documentElement.scrollHeight; + clientHeight = window.document.documentElement.clientHeight; + scrollPos = window.pageYOffset; + + if (pageHeight - (scrollPos + clientHeight) === 0) { + scope.$apply(attr.whenScrolled); + } + }; + + $window.on('scroll', handler); + scope.$on('$destroy', function() { + return $window.off('scroll', handler); + }); + } + }; +}]); diff --git a/public/js/services/address.js b/public/js/services/address.js index 6773267b..17d05ce0 100644 --- a/public/js/services/address.js +++ b/public/js/services/address.js @@ -1,6 +1,7 @@ 'use strict'; -angular.module('insight.address').factory('Address', ['$resource', function($resource) { +angular.module('insight.address').factory('Address', + function($resource) { return $resource('/api/addr/:addrStr', { addrStr: '@addStr' }, { @@ -18,5 +19,5 @@ angular.module('insight.address').factory('Address', ['$resource', function($res } } }); -}]); +}); diff --git a/public/js/services/blocks.js b/public/js/services/blocks.js index 346b6508..12d1da16 100644 --- a/public/js/services/blocks.js +++ b/public/js/services/blocks.js @@ -1,6 +1,7 @@ 'use strict'; -angular.module('insight.blocks').factory('Block', ['$resource', function($resource) { +angular.module('insight.blocks').factory('Block', + function($resource) { return $resource('/api/block/:blockHash', { blockHash: '@blockHash' }, { @@ -18,8 +19,15 @@ angular.module('insight.blocks').factory('Block', ['$resource', function($resour } } }); -}]); +}); + +angular.module('insight.blocks').factory('Blocks', + function($resource) { + return $resource('/api/blocks'); +}); + +angular.module('insight.blocks').factory('BlockByHeight', + function($resource) { + return $resource('/api/block-index/:blockHeight'); +}); -angular.module('insight.blocks').factory('Blocks', ['$resource', function($resource) { - return $resource('/api/blocks'); -}]); diff --git a/public/js/services/global.js b/public/js/services/global.js index 73637022..1b1f375f 100755 --- a/public/js/services/global.js +++ b/public/js/services/global.js @@ -1,5 +1,7 @@ 'use strict'; //Global service for global variables -angular.module('insight.system').factory('Global', [function() {}]); +angular.module('insight.system').factory('Global', + function() { +}); diff --git a/public/js/services/socket.js b/public/js/services/socket.js index 98af5e1e..6dc07491 100644 --- a/public/js/services/socket.js +++ b/public/js/services/socket.js @@ -46,7 +46,8 @@ ScopedSocket.prototype.emit = function(event, data, callback) { }); }; -angular.module('insight.socket').factory('get_socket', ['$rootScope', function($rootScope) { +angular.module('insight.socket').factory('get_socket', + function($rootScope) { var socket = io.connect(); return function(scope) { var scopedSocket = new ScopedSocket(socket, $rootScope); @@ -55,5 +56,5 @@ angular.module('insight.socket').factory('get_socket', ['$rootScope', function($ }); return scopedSocket; }; -}]); +}); diff --git a/public/js/services/status.js b/public/js/services/status.js index 67d7004a..eb807620 100644 --- a/public/js/services/status.js +++ b/public/js/services/status.js @@ -1,12 +1,14 @@ 'use strict'; -angular.module('insight.status').factory('Status', ['$resource', function($resource) { +angular.module('insight.status').factory('Status', + function($resource) { return $resource('/api/status', { q: '@q' }); -}]); +}); -angular.module('insight.status').factory('Sync', ['$resource', function($resource) { +angular.module('insight.status').factory('Sync', + function($resource) { return $resource('/api/sync'); -}]); +}); diff --git a/public/js/services/transactions.js b/public/js/services/transactions.js index d1b0b355..25202463 100644 --- a/public/js/services/transactions.js +++ b/public/js/services/transactions.js @@ -1,6 +1,7 @@ 'use strict'; -angular.module('insight.transactions').factory('Transaction', ['$resource', function($resource) { +angular.module('insight.transactions').factory('Transaction', + function($resource) { return $resource('/api/tx/:txId', { txId: '@txId' }, { @@ -18,20 +19,23 @@ angular.module('insight.transactions').factory('Transaction', ['$resource', func } } }); -}]); +}); -angular.module('insight.transactions').factory('TransactionsByBlock', ['$resource', function($resource) { +angular.module('insight.transactions').factory('TransactionsByBlock', + function($resource) { return $resource('/api/txs', { block: '@block' }); -}]); +}); -angular.module('insight.transactions').factory('TransactionsByAddress', ['$resource', function($resource) { +angular.module('insight.transactions').factory('TransactionsByAddress', + function($resource) { return $resource('/api/txs', { address: '@address' }); -}]); +}); -angular.module('insight.transactions').factory('Transactions', ['$resource', function($resource) { +angular.module('insight.transactions').factory('Transactions', + function($resource) { return $resource('/api/txs'); -}]); +}); diff --git a/public/sound/transaction.mp3 b/public/sound/transaction.mp3 new file mode 100644 index 00000000..4841543a Binary files /dev/null and b/public/sound/transaction.mp3 differ diff --git a/public/views/address.html b/public/views/address.html index 1e21aec7..3063a0fa 100644 --- a/public/views/address.html +++ b/public/views/address.html @@ -1,45 +1,44 @@
-
-
- - - - - - - - - - - - - - - - - - - - - - +
+
+
+ +

Address

+ {{address.addrStr}} +
-
-
Address{{address.addrStr}}
Total Received{{address.totalReceived}} BTC
Total Sent{{address.totalSent}} BTC
Final Balance{{address.balance}} BTC
No. Transactions{{address.txApperances}}
-
-
- -
-
+
+

Summary

+ + + + + + + + + + + + + + + + + + + +
Total Received{{address.totalReceived}} BTC
Total Sent{{address.totalSent}} BTC
Final Balance{{address.balance}} BTC
No. Transactions{{address.txApperances}}
+
+ + -
-

Transactions Transactions contained within this block

-
-
+
+
+

Transactions Transactions for this address

+
+
+
+
diff --git a/public/views/block.html b/public/views/block.html index d3db9010..3cf4efb6 100644 --- a/public/views/block.html +++ b/public/views/block.html @@ -1,40 +1,39 @@
-
+
-

Block #{{ block.height }}

+

Block #{{ block.height }}

-

Hashes

- + - + - + - +
Hash Hash {{block.hash}}
Previous Block Previous Block {{block.previousblockhash}}
Next Block Next Block {{block.nextblockhash}}
Merkle RootMerkle Root

{{block.merkleroot}}

-
+
-
+

Summary

@@ -69,7 +68,7 @@ {{block.bits}} - Size + Size (bytes) {{block.size}} @@ -85,9 +84,9 @@
-
+

Transactions Transactions contained within this block

-
+
diff --git a/public/views/blocks/list.html b/public/views/blocks/list.html index e7e0f60b..abdcf856 100644 --- a/public/views/blocks/list.html +++ b/public/views/blocks/list.html @@ -1,27 +1,44 @@
-
diff --git a/public/views/footer.html b/public/views/footer.html index 4e08b207..aac69ff5 100644 --- a/public/views/footer.html +++ b/public/views/footer.html @@ -1,10 +1,3 @@ -
-
-

- Blocks: {{info.blocks}} | - Connections: {{info.connections}} | - Difficulty: {{info.difficulty}} -

-
+ - diff --git a/public/views/header.html b/public/views/header.html index e831eb56..baf6f772 100755 --- a/public/views/header.html +++ b/public/views/header.html @@ -11,29 +11,34 @@
diff --git a/public/views/status.html b/public/views/status.html index 71951199..ab26f957 100644 --- a/public/views/status.html +++ b/public/views/status.html @@ -1,130 +1,42 @@
-
-
-

getInfo

- +
+
+ +

Sync Status

+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Loading... -
{{infoError}} -
Version{{info.version}}
protocolversion{{info.protocolversion}}
walletversion{{info.walletversion}}
balance{{info.balance}}
blocks{{info.blocks}}
timeoffset{{info.timeoffset}}
connections{{info.connections}}
proxy{{info.proxy}}
difficulty{{info.difficulty}}
testnet{{info.testnet}}
keypoololdest{{info.keypoololdest}}
keypoolsize{{info.keypoolsize}}
paytxfee{{info.paytxfee}}
infoErrors{{info.infoErrors}}
-
-
-

sync status

- - - - - - - - + + - - - + + - - - - - - - - - - - - - - - + +
{{ syncError }}
{{ sync.err }} + {{sync.error}}
Sync Progress {{(100 * sync.syncedBlocks/sync.blocksToSync)| number:2}}% + +
+
+ {{sync.syncPercentage}}% Complete +
+
blocksToSync{{sync.blocksToSync}}Initial Block Chain Height{{sync.blockChainHeight}}
syncedBlocks{{sync.syncedBlocks}}
Synced Blocks{{sync.syncedBlocks}}
start{{sync.start}} - (genesisBlock) -
end{{sync.end}} - (genesisBlock) -
Sync Type -
    -
  • Stops at existing block -
  • - scanningBackward - scanningForward -
-
Skipped Blocks (previously synced){{sync.skippedBlocks}}
-

getTxOutSetInfo

- +

Transaction Output Set Information

+
@@ -134,59 +46,37 @@ - + - - + + - - + + - - + + - - + + - - + + - - + +
Loading...
Height{{txoutsetinfo.height}}{{txoutsetinfo.height}}
bestblock{{txoutsetinfo.bestblock}}Best Block{{txoutsetinfo.bestblock}}
transactions{{txoutsetinfo.transactions}}Transactions {{txoutsetinfo.transactions}}
txouts{{txoutsetinfo.txouts}}Transaction Outputs{{txoutsetinfo.txouts}}
bytes_serialized{{txoutsetinfo.bytes_serialized}}Bytes Serialized{{txoutsetinfo.bytes_serialized}}
hash_serialized{{txoutsetinfo.hash_serialized}}Hash Serialized{{txoutsetinfo.hash_serialized}}
total_amount{{txoutsetinfo.total_amount}}Total Amount{{txoutsetinfo.total_amount}}
-
-
- -
-
-

getDifficulty

- - - - - - - - - - - - - - -
Loading...
{{infoError}}
Difficulty{{difficulty}}
-
-
-

getLastBlockHash

- + +

Last Block

+
@@ -196,11 +86,98 @@ - +
Loading...
Last block hash{{lastblockhash}}{{lastblockhash}}
-
+
+ +
+
+

Bitcoin node information

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Loading... +
{{infoError}} +
Version{{info.version}}
Protocol version{{info.protocolversion}}
Wallet version{{info.walletversion}}
Balance (BTC){{info.balance}}
Blocks{{info.blocks}}
Time Offset{{info.timeoffset}}
Connections to other nodes{{info.connections}}
Proxy setting{{info.proxy}}
Mining Difficulty{{info.difficulty}}
Testnet{{info.testnet}}
Keypool Oldest Date{{info.keypoololdest*1000 | date:'medium' }}
Keypool Size{{info.keypoolsize}}
Default Transaction Fee (BTC){{info.paytxfee}}
Info Errors{{info.infoErrors}}
+ +

Difficulty

+ + + + + + + + + + + + + + +
Loading...
{{infoError}}
Mining Difficulty{{difficulty}}
+
+
diff --git a/public/views/transaction.html b/public/views/transaction.html index fe4afeb1..2a688e1e 100644 --- a/public/views/transaction.html +++ b/public/views/transaction.html @@ -5,72 +5,13 @@
- - -
- -
-
    -
  • - - - Address could not be parsed - {{vin.addr}} - {{vin.value}} BTC -
  • - -
-
-
    -
  • - No Inputs (Newly Generated isCoinBasens) - {{vinn.reward}} BTC -
  • -
-
-
- -
- -
- -
-
-
- {{vout.scriptPubKey.type}} -
-
- -
-
-
-
- -
- Feeds: {{tx.feeds}} -
- - - -
-
+

Summary

- +
@@ -81,9 +22,10 @@ - - - +
Size {{tx.time * 1000|date:'medium'}}
Block Block
Block + + + {{tx.blockhash}}
@@ -101,11 +43,12 @@ Fees - {{tx.feeds}} BTC + {{tx.fees}} BTC
+
diff --git a/public/views/transaction/list.html b/public/views/transaction/list.html index f8501c95..8b68db0f 100644 --- a/public/views/transaction/list.html +++ b/public/views/transaction/list.html @@ -1,71 +1,9 @@ -
Loading...
-
There are not transactions
-
-
- {{tx.txid}} - {{tx.time * 1000 | date:'medium'}} -
- -
-
-
-
-

No Inputs (Newly Generated isCoinBasens)

-
-

{{vin.reward}} BTC

-
-
    -
  • - - -
    -
    - Address could not be parsed - {{vin.addr}} -
    -
    -

    {{vin.value}} BTC

    -
  • -
-
- -
-   -
- -
-
-
-
- {{vout.scriptPubKey.type}} -
-
-
-
- -
-
-

{{vout.value}} BTC

-
-
-
-
-
- -
-
-
- - - -
- Feeds: {{tx.feeds}} -
+
There are not transactions
+
+
+
+
+
+ Loading...
diff --git a/public/views/transaction/tx.html b/public/views/transaction/tx.html new file mode 100644 index 00000000..fd505df3 --- /dev/null +++ b/public/views/transaction/tx.html @@ -0,0 +1,98 @@ +
+ {{tx.txid}} + + + + {{tx.time * 1000 | date:'medium'}} +
+ +
+
+
+
+

No Inputs (Newly Generated Coins)

+
+

{{vin.reward}} BTC

+
+ +
+
    +
  • +
    +
    + {{vin.addr}} + {{vin.addr}} +
    +
    +

    {{vin.value}} BTC

    +
+ +
    +
  • + + +
    +
    + {{vin.addr}} + {{vin.addr}} +
    +
    + scriptSig {{vin.scriptSig.asm}} +
    +
    +

    {{vin.value}} BTC

    +
+
+
+ +
+   +
+ +
+
+
+
+
+ {{vout.addr}} + {{address}} +
+
+
+

{{vout.value}} BTC

+
+
+
+
+ + type {{vout.scriptPubKey.type}} +
+ scriptPubKey {{vout.scriptPubKey.asm}} +
+
+
+

{{vout.value}} BTC

+
+
+ +
+
+
+ +
+
+
+ + + +
+ Fees: {{tx.fees}} +
+
+ diff --git a/util/sync.js b/util/sync.js index b7f6e03c..2dd4f15f 100755 --- a/util/sync.js +++ b/util/sync.js @@ -33,10 +33,10 @@ async.series([ }, function(cb) { if (program.smart) { - historicSync.smart_import(cb); + historicSync.smartImport(cb); } else { - historicSync.import_history({ + historicSync.importHistory({ destroy: program.destroy, reverse: program.reverse, upToExisting: program.uptoexisting, @@ -50,7 +50,7 @@ async.series([ console.log('CRITICAL ERROR: ', err); } else { - console.log('Finished.\n Status:\n', historicSync.syncInfo); + console.log('Finished.\n Status:\n', historicSync.info()); } });